blob: 0d151ef8047050d5aac9a7c6c81987bf215605c1 [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 *
7 * Dictionary Abstract Data Type
8 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
9 * Free Software License:
10 * All rights are reserved by the author, with the following exceptions:
11 * Permission is granted to freely reproduce and distribute this software,
12 * possibly in exchange for a fee, provided that this copyright notice appears
13 * intact. Permission is also granted to adapt this software to produce
14 * derivative works, as long as the modified versions carry this copyright
15 * notice and additional notices stating that the work has been modified.
16 * This source code may be translated into executable form and incorporated
17 * into proprietary software; there is no requirement for such software to
18 * contain a copyright notice related to this source.
19 *
20 * linux/fs/recovery and linux/fs/revoke
21 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
22 *
23 * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
24 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000025 * Journal recovery routines for the generic filesystem journaling code;
26 * part of the ext2fs journaling system.
Rob Landley7a260f02006-06-19 03:20:03 +000027 *
28 * Licensed under GPLv2 or later, see file License in this tarball for details.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000029 */
30
31#ifndef _GNU_SOURCE
32#define _GNU_SOURCE 1 /* get strnlen() */
33#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000034
Rob Landley43ac8882006-04-01 00:40:33 +000035#include "e2fsck.h" /*Put all of our defines here to clean things up*/
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000036
Rob Landley15d20a02006-05-29 05:00:44 +000037#define _(x) x
38#define N_(x) x
39
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000040/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000041 * Procedure declarations
42 */
43
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000044static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000045
46/* pass1.c */
47static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000048
49/* pass2.c */
50static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
51 ext2_ino_t ino, char *buf);
52
53/* pass3.c */
54static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
55static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
56 int num, int gauranteed_size);
57static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
58static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
59 int adj);
60
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000061/* rehash.c */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000062static void e2fsck_rehash_directories(e2fsck_t ctx);
63
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000064/* util.c */
65static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
66 const char *description);
67static int ask(e2fsck_t ctx, const char * string, int def);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000068static void e2fsck_read_bitmaps(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000069static void preenhalt(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000070static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
71 struct ext2_inode * inode, const char * proc);
72static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
73 struct ext2_inode * inode, const char * proc);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000074static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
75 const char *name, io_manager manager);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000076
77/* unix.c */
78static void e2fsck_clear_progbar(e2fsck_t ctx);
79static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
80 float percent, unsigned int dpynum);
Rob Landley43ac8882006-04-01 00:40:33 +000081
82
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000083/*
84 * problem.h --- e2fsck problem error codes
85 */
86
87typedef __u32 problem_t;
88
89struct problem_context {
90 errcode_t errcode;
91 ext2_ino_t ino, ino2, dir;
92 struct ext2_inode *inode;
93 struct ext2_dir_entry *dirent;
94 blk_t blk, blk2;
95 e2_blkcnt_t blkcount;
96 int group;
97 __u64 num;
98 const char *str;
99};
100
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000101
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000102/*
103 * Function declarations
104 */
105static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
106static int end_problem_latch(e2fsck_t ctx, int mask);
107static int set_latch_flags(int mask, int setflags, int clearflags);
108static void clear_problem_context(struct problem_context *ctx);
109
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000110/*
111 * Dictionary Abstract Data Type
112 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
113 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000114 * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
115 * kazlib_1_20
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000116 */
117
118#ifndef DICT_H
119#define DICT_H
120
121/*
122 * Blurb for inclusion into C++ translation units
123 */
124
125typedef unsigned long dictcount_t;
126#define DICTCOUNT_T_MAX ULONG_MAX
127
128/*
129 * The dictionary is implemented as a red-black tree
130 */
131
132typedef enum { dnode_red, dnode_black } dnode_color_t;
133
134typedef struct dnode_t {
135 struct dnode_t *dict_left;
136 struct dnode_t *dict_right;
137 struct dnode_t *dict_parent;
138 dnode_color_t dict_color;
139 const void *dict_key;
140 void *dict_data;
141} dnode_t;
142
143typedef int (*dict_comp_t)(const void *, const void *);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000144typedef void (*dnode_free_t)(dnode_t *);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000145
146typedef struct dict_t {
147 dnode_t dict_nilnode;
148 dictcount_t dict_nodecount;
149 dictcount_t dict_maxcount;
150 dict_comp_t dict_compare;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000151 dnode_free_t dict_freenode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000152 int dict_dupes;
153} dict_t;
154
155typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
156
157typedef struct dict_load_t {
158 dict_t *dict_dictptr;
159 dnode_t dict_nilnode;
160} dict_load_t;
161
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000162#define dict_count(D) ((D)->dict_nodecount)
163#define dnode_get(N) ((N)->dict_data)
164#define dnode_getkey(N) ((N)->dict_key)
165
166#endif
167
168/*
169 * Compatibility header file for e2fsck which should be included
170 * instead of linux/jfs.h
171 *
172 * Copyright (C) 2000 Stephen C. Tweedie
173 */
174
175/*
176 * Pull in the definition of the e2fsck context structure
177 */
178
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000179struct buffer_head {
180 char b_data[8192];
181 e2fsck_t b_ctx;
182 io_channel b_io;
183 int b_size;
184 blk_t b_blocknr;
185 int b_dirty;
186 int b_uptodate;
187 int b_err;
188};
189
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000190
191#define K_DEV_FS 1
192#define K_DEV_JOURNAL 2
193
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000194#define lock_buffer(bh) do {} while(0)
195#define unlock_buffer(bh) do {} while(0)
196#define buffer_req(bh) 1
197#define do_readahead(journal, start) do {} while(0)
198
199static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
200
201typedef struct {
202 int object_length;
203} kmem_cache_t;
204
205#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000206
207/*
208 * We use the standard libext2fs portability tricks for inline
209 * functions.
210 */
211
Rob Landley7c94bed2006-05-03 21:58:45 +0000212static kmem_cache_t * do_cache_create(int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000213{
214 kmem_cache_t *new_cache;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000215
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000216 new_cache = malloc(sizeof(*new_cache));
217 if (new_cache)
218 new_cache->object_length = len;
219 return new_cache;
220}
221
Rob Landley7c94bed2006-05-03 21:58:45 +0000222static void do_cache_destroy(kmem_cache_t *cache)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000223{
224 free(cache);
225}
226
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000227
228/*
229 * Dictionary Abstract Data Type
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000230 */
231
232
233/*
234 * These macros provide short convenient names for structure members,
235 * which are embellished with dict_ prefixes so that they are
236 * properly confined to the documented namespace. It's legal for a
237 * program which uses dict to define, for instance, a macro called ``parent''.
238 * Such a macro would interfere with the dnode_t struct definition.
239 * In general, highly portable and reusable C modules which expose their
240 * structures need to confine structure member names to well-defined spaces.
241 * The resulting identifiers aren't necessarily convenient to use, nor
242 * readable, in the implementation, however!
243 */
244
245#define left dict_left
246#define right dict_right
247#define parent dict_parent
248#define color dict_color
249#define key dict_key
250#define data dict_data
251
252#define nilnode dict_nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000253#define maxcount dict_maxcount
254#define compare dict_compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000255#define dupes dict_dupes
256
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000257#define dict_root(D) ((D)->nilnode.left)
258#define dict_nil(D) (&(D)->nilnode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000259
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000260static void dnode_free(dnode_t *node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000261
262/*
263 * Perform a ``left rotation'' adjustment on the tree. The given node P and
264 * its right child C are rearranged so that the P instead becomes the left
265 * child of C. The left subtree of C is inherited as the new right subtree
266 * for P. The ordering of the keys within the tree is thus preserved.
267 */
268
269static void rotate_left(dnode_t *upper)
270{
271 dnode_t *lower, *lowleft, *upparent;
272
273 lower = upper->right;
274 upper->right = lowleft = lower->left;
275 lowleft->parent = upper;
276
277 lower->parent = upparent = upper->parent;
278
279 /* don't need to check for root node here because root->parent is
280 the sentinel nil node, and root->parent->left points back to root */
281
282 if (upper == upparent->left) {
283 upparent->left = lower;
284 } else {
285 assert (upper == upparent->right);
286 upparent->right = lower;
287 }
288
289 lower->left = upper;
290 upper->parent = lower;
291}
292
293/*
294 * This operation is the ``mirror'' image of rotate_left. It is
295 * the same procedure, but with left and right interchanged.
296 */
297
298static void rotate_right(dnode_t *upper)
299{
300 dnode_t *lower, *lowright, *upparent;
301
302 lower = upper->left;
303 upper->left = lowright = lower->right;
304 lowright->parent = upper;
305
306 lower->parent = upparent = upper->parent;
307
308 if (upper == upparent->right) {
309 upparent->right = lower;
310 } else {
311 assert (upper == upparent->left);
312 upparent->left = lower;
313 }
314
315 lower->right = upper;
316 upper->parent = lower;
317}
318
319/*
320 * Do a postorder traversal of the tree rooted at the specified
321 * node and free everything under it. Used by dict_free().
322 */
323
324static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
325{
326 if (node == nil)
327 return;
328 free_nodes(dict, node->left, nil);
329 free_nodes(dict, node->right, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000330 dict->dict_freenode(node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000331}
332
333/*
334 * Verify that the tree contains the given node. This is done by
335 * traversing all of the nodes and comparing their pointers to the
336 * given pointer. Returns 1 if the node is found, otherwise
337 * returns zero. It is intended for debugging purposes.
338 */
339
340static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
341{
342 if (root != nil) {
343 return root == node
344 || verify_dict_has_node(nil, root->left, node)
345 || verify_dict_has_node(nil, root->right, node);
346 }
347 return 0;
348}
349
350
351/*
352 * Select a different set of node allocator routines.
353 */
354
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000355static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000356{
357 assert (dict_count(dict) == 0);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000358 dict->dict_freenode = fr;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000359}
360
361/*
362 * Free all the nodes in the dictionary by using the dictionary's
363 * installed free routine. The dictionary is emptied.
364 */
365
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000366static void dict_free_nodes(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000367{
368 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
369 free_nodes(dict, root, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000370 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000371 dict->nilnode.left = &dict->nilnode;
372 dict->nilnode.right = &dict->nilnode;
373}
374
375/*
376 * Initialize a user-supplied dictionary object.
377 */
378
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000379static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000380{
381 dict->compare = comp;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000382 dict->dict_freenode = dnode_free;
383 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000384 dict->maxcount = maxcount;
385 dict->nilnode.left = &dict->nilnode;
386 dict->nilnode.right = &dict->nilnode;
387 dict->nilnode.parent = &dict->nilnode;
388 dict->nilnode.color = dnode_black;
389 dict->dupes = 0;
390 return dict;
391}
392
393/*
394 * Locate a node in the dictionary having the given key.
395 * If the node is not found, a null a pointer is returned (rather than
396 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
397 * located node is returned.
398 */
399
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000400static dnode_t *dict_lookup(dict_t *dict, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000401{
402 dnode_t *root = dict_root(dict);
403 dnode_t *nil = dict_nil(dict);
404 dnode_t *saved;
405 int result;
406
407 /* simple binary search adapted for trees that contain duplicate keys */
408
409 while (root != nil) {
410 result = dict->compare(key, root->key);
411 if (result < 0)
412 root = root->left;
413 else if (result > 0)
414 root = root->right;
415 else {
416 if (!dict->dupes) { /* no duplicates, return match */
417 return root;
418 } else { /* could be dupes, find leftmost one */
419 do {
420 saved = root;
421 root = root->left;
422 while (root != nil && dict->compare(key, root->key))
423 root = root->right;
424 } while (root != nil);
425 return saved;
426 }
427 }
428 }
429
430 return NULL;
431}
432
433/*
434 * Insert a node into the dictionary. The node should have been
435 * initialized with a data field. All other fields are ignored.
436 * The behavior is undefined if the user attempts to insert into
437 * a dictionary that is already full (for which the dict_isfull()
438 * function returns true).
439 */
440
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000441static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000442{
443 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
444 dnode_t *parent = nil, *uncle, *grandpa;
445 int result = -1;
446
447 node->key = key;
448
449 /* basic binary tree insert */
450
451 while (where != nil) {
452 parent = where;
453 result = dict->compare(key, where->key);
454 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
455 assert (dict->dupes || result != 0);
456 if (result < 0)
457 where = where->left;
458 else
459 where = where->right;
460 }
461
462 assert (where == nil);
463
464 if (result < 0)
465 parent->left = node;
466 else
467 parent->right = node;
468
469 node->parent = parent;
470 node->left = nil;
471 node->right = nil;
472
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000473 dict->dict_nodecount++;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000474
475 /* red black adjustments */
476
477 node->color = dnode_red;
478
479 while (parent->color == dnode_red) {
480 grandpa = parent->parent;
481 if (parent == grandpa->left) {
482 uncle = grandpa->right;
483 if (uncle->color == dnode_red) { /* red parent, red uncle */
484 parent->color = dnode_black;
485 uncle->color = dnode_black;
486 grandpa->color = dnode_red;
487 node = grandpa;
488 parent = grandpa->parent;
489 } else { /* red parent, black uncle */
490 if (node == parent->right) {
491 rotate_left(parent);
492 parent = node;
493 assert (grandpa == parent->parent);
494 /* rotation between parent and child preserves grandpa */
495 }
496 parent->color = dnode_black;
497 grandpa->color = dnode_red;
498 rotate_right(grandpa);
499 break;
500 }
501 } else { /* symmetric cases: parent == parent->parent->right */
502 uncle = grandpa->left;
503 if (uncle->color == dnode_red) {
504 parent->color = dnode_black;
505 uncle->color = dnode_black;
506 grandpa->color = dnode_red;
507 node = grandpa;
508 parent = grandpa->parent;
509 } else {
510 if (node == parent->left) {
511 rotate_right(parent);
512 parent = node;
513 assert (grandpa == parent->parent);
514 }
515 parent->color = dnode_black;
516 grandpa->color = dnode_red;
517 rotate_left(grandpa);
518 break;
519 }
520 }
521 }
522
523 dict_root(dict)->color = dnode_black;
524
525}
526
527/*
528 * Allocate a node using the dictionary's allocator routine, give it
529 * the data item.
530 */
531
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000532static dnode_t *dnode_init(dnode_t *dnode, void *data)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000533{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000534 dnode->data = data;
535 dnode->parent = NULL;
536 dnode->left = NULL;
537 dnode->right = NULL;
538 return dnode;
539}
540
541static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
542{
543 dnode_t *node = malloc(sizeof(dnode_t));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000544
545 if (node) {
546 dnode_init(node, data);
547 dict_insert(dict, node, key);
548 return 1;
549 }
550 return 0;
551}
552
553/*
554 * Return the node with the lowest (leftmost) key. If the dictionary is empty
555 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
556 */
557
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000558static dnode_t *dict_first(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000559{
560 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
561
562 if (root != nil)
563 while ((left = root->left) != nil)
564 root = left;
565
566 return (root == nil) ? NULL : root;
567}
568
569/*
570 * Return the given node's successor node---the node which has the
571 * next key in the the left to right ordering. If the node has
572 * no successor, a null pointer is returned rather than a pointer to
573 * the nil node.
574 */
575
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000576static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000577{
578 dnode_t *nil = dict_nil(dict), *parent, *left;
579
580 if (curr->right != nil) {
581 curr = curr->right;
582 while ((left = curr->left) != nil)
583 curr = left;
584 return curr;
585 }
586
587 parent = curr->parent;
588
589 while (parent != nil && curr == parent->right) {
590 curr = parent;
591 parent = curr->parent;
592 }
593
594 return (parent == nil) ? NULL : parent;
595}
596
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000597
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000598static void dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000599{
600 free(node);
601}
602
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000603
604#undef left
605#undef right
606#undef parent
607#undef color
608#undef key
609#undef data
610
611#undef nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000612#undef maxcount
613#undef compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000614#undef dupes
615
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000616
617/*
618 * dirinfo.c --- maintains the directory information table for e2fsck.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000619 */
620
621/*
622 * This subroutine is called during pass1 to create a directory info
623 * entry. During pass1, the passed-in parent is 0; it will get filled
624 * in during pass2.
625 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000626static 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 +0000627{
628 struct dir_info *dir;
629 int i, j;
630 ext2_ino_t num_dirs;
631 errcode_t retval;
632 unsigned long old_size;
633
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000634 if (!ctx->dir_info) {
635 ctx->dir_info_count = 0;
636 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
637 if (retval)
638 num_dirs = 1024; /* Guess */
639 ctx->dir_info_size = num_dirs + 10;
640 ctx->dir_info = (struct dir_info *)
641 e2fsck_allocate_memory(ctx, ctx->dir_info_size
642 * sizeof (struct dir_info),
643 "directory map");
644 }
645
646 if (ctx->dir_info_count >= ctx->dir_info_size) {
647 old_size = ctx->dir_info_size * sizeof(struct dir_info);
648 ctx->dir_info_size += 10;
649 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
650 sizeof(struct dir_info),
651 &ctx->dir_info);
652 if (retval) {
653 ctx->dir_info_size -= 10;
654 return;
655 }
656 }
657
658 /*
659 * Normally, add_dir_info is called with each inode in
660 * sequential order; but once in a while (like when pass 3
661 * needs to recreate the root directory or lost+found
662 * directory) it is called out of order. In those cases, we
663 * need to move the dir_info entries down to make room, since
664 * the dir_info array needs to be sorted by inode number for
665 * get_dir_info()'s sake.
666 */
667 if (ctx->dir_info_count &&
668 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
669 for (i = ctx->dir_info_count-1; i > 0; i--)
670 if (ctx->dir_info[i-1].ino < ino)
671 break;
672 dir = &ctx->dir_info[i];
673 if (dir->ino != ino)
674 for (j = ctx->dir_info_count++; j > i; j--)
675 ctx->dir_info[j] = ctx->dir_info[j-1];
676 } else
677 dir = &ctx->dir_info[ctx->dir_info_count++];
678
679 dir->ino = ino;
680 dir->dotdot = parent;
681 dir->parent = parent;
682}
683
684/*
685 * get_dir_info() --- given an inode number, try to find the directory
686 * information entry for it.
687 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000688static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000689{
690 int low, high, mid;
691
692 low = 0;
693 high = ctx->dir_info_count-1;
694 if (!ctx->dir_info)
695 return 0;
696 if (ino == ctx->dir_info[low].ino)
697 return &ctx->dir_info[low];
698 if (ino == ctx->dir_info[high].ino)
699 return &ctx->dir_info[high];
700
701 while (low < high) {
702 mid = (low+high)/2;
703 if (mid == low || mid == high)
704 break;
705 if (ino == ctx->dir_info[mid].ino)
706 return &ctx->dir_info[mid];
707 if (ino < ctx->dir_info[mid].ino)
708 high = mid;
709 else
710 low = mid;
711 }
712 return 0;
713}
714
715/*
716 * Free the dir_info structure when it isn't needed any more.
717 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000718static void e2fsck_free_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000719{
Rob Landleye7c43b62006-03-01 16:39:45 +0000720 ext2fs_free_mem(&ctx->dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000721 ctx->dir_info_size = 0;
722 ctx->dir_info_count = 0;
723}
724
725/*
726 * Return the count of number of directories in the dir_info structure
727 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000728static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000729{
730 return ctx->dir_info_count;
731}
732
733/*
734 * A simple interator function
735 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000736static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000737{
738 if (*control >= ctx->dir_info_count)
739 return 0;
740
741 return(ctx->dir_info + (*control)++);
742}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000743
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000744/*
745 * dirinfo.c --- maintains the directory information table for e2fsck.
746 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000747 */
748
749#ifdef ENABLE_HTREE
750
751/*
752 * This subroutine is called during pass1 to create a directory info
753 * entry. During pass1, the passed-in parent is 0; it will get filled
754 * in during pass2.
755 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000756static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000757{
758 struct dx_dir_info *dir;
759 int i, j;
760 errcode_t retval;
761 unsigned long old_size;
762
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000763 if (!ctx->dx_dir_info) {
764 ctx->dx_dir_info_count = 0;
765 ctx->dx_dir_info_size = 100; /* Guess */
766 ctx->dx_dir_info = (struct dx_dir_info *)
767 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
768 * sizeof (struct dx_dir_info),
769 "directory map");
770 }
771
772 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
773 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
774 ctx->dx_dir_info_size += 10;
775 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
776 sizeof(struct dx_dir_info),
777 &ctx->dx_dir_info);
778 if (retval) {
779 ctx->dx_dir_info_size -= 10;
780 return;
781 }
782 }
783
784 /*
785 * Normally, add_dx_dir_info is called with each inode in
786 * sequential order; but once in a while (like when pass 3
787 * needs to recreate the root directory or lost+found
788 * directory) it is called out of order. In those cases, we
789 * need to move the dx_dir_info entries down to make room, since
790 * the dx_dir_info array needs to be sorted by inode number for
791 * get_dx_dir_info()'s sake.
792 */
793 if (ctx->dx_dir_info_count &&
794 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
795 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
796 if (ctx->dx_dir_info[i-1].ino < ino)
797 break;
798 dir = &ctx->dx_dir_info[i];
799 if (dir->ino != ino)
800 for (j = ctx->dx_dir_info_count++; j > i; j--)
801 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
802 } else
803 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
804
805 dir->ino = ino;
806 dir->numblocks = num_blocks;
807 dir->hashversion = 0;
808 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
809 * sizeof (struct dx_dirblock_info),
810 "dx_block info array");
811
812}
813
814/*
815 * get_dx_dir_info() --- given an inode number, try to find the directory
816 * information entry for it.
817 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000818static 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 +0000819{
820 int low, high, mid;
821
822 low = 0;
823 high = ctx->dx_dir_info_count-1;
824 if (!ctx->dx_dir_info)
825 return 0;
826 if (ino == ctx->dx_dir_info[low].ino)
827 return &ctx->dx_dir_info[low];
828 if (ino == ctx->dx_dir_info[high].ino)
829 return &ctx->dx_dir_info[high];
830
831 while (low < high) {
832 mid = (low+high)/2;
833 if (mid == low || mid == high)
834 break;
835 if (ino == ctx->dx_dir_info[mid].ino)
836 return &ctx->dx_dir_info[mid];
837 if (ino < ctx->dx_dir_info[mid].ino)
838 high = mid;
839 else
840 low = mid;
841 }
842 return 0;
843}
844
845/*
846 * Free the dx_dir_info structure when it isn't needed any more.
847 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000848static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000849{
850 int i;
851 struct dx_dir_info *dir;
852
853 if (ctx->dx_dir_info) {
854 dir = ctx->dx_dir_info;
855 for (i=0; i < ctx->dx_dir_info_count; i++) {
Rob Landleye7c43b62006-03-01 16:39:45 +0000856 ext2fs_free_mem(&dir->dx_block);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000857 }
858 ext2fs_free_mem(&ctx->dx_dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000859 }
860 ctx->dx_dir_info_size = 0;
861 ctx->dx_dir_info_count = 0;
862}
863
864/*
865 * A simple interator function
866 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000867static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000868{
869 if (*control >= ctx->dx_dir_info_count)
870 return 0;
871
872 return(ctx->dx_dir_info + (*control)++);
873}
874
875#endif /* ENABLE_HTREE */
876/*
877 * e2fsck.c - a consistency checker for the new extended file system.
878 *
Mike Frysinger51a43b42005-09-24 07:11:16 +0000879 */
880
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000881/*
882 * This function allocates an e2fsck context
883 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000884static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000885{
886 e2fsck_t context;
887 errcode_t retval;
888
889 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
890 if (retval)
891 return retval;
892
893 memset(context, 0, sizeof(struct e2fsck_struct));
894
895 context->process_inode_size = 256;
896 context->ext_attr_ver = 2;
897
898 *ret = context;
899 return 0;
900}
901
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000902struct ea_refcount_el {
903 blk_t ea_blk;
904 int ea_count;
905};
906
907struct ea_refcount {
908 blk_t count;
909 blk_t size;
910 blk_t cursor;
911 struct ea_refcount_el *list;
912};
913
914static void ea_refcount_free(ext2_refcount_t refcount)
915{
916 if (!refcount)
917 return;
918
Rob Landleye7c43b62006-03-01 16:39:45 +0000919 ext2fs_free_mem(&refcount->list);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000920 ext2fs_free_mem(&refcount);
921}
922
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000923/*
924 * This function resets an e2fsck context; it is called when e2fsck
925 * needs to be restarted.
926 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000927static errcode_t e2fsck_reset_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000928{
929 ctx->flags = 0;
930 ctx->lost_and_found = 0;
931 ctx->bad_lost_and_found = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +0000932 ext2fs_free_inode_bitmap(ctx->inode_used_map);
933 ctx->inode_used_map = 0;
934 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
935 ctx->inode_dir_map = 0;
936 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
937 ctx->inode_reg_map = 0;
938 ext2fs_free_block_bitmap(ctx->block_found_map);
939 ctx->block_found_map = 0;
940 ext2fs_free_icount(ctx->inode_link_info);
941 ctx->inode_link_info = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000942 if (ctx->journal_io) {
943 if (ctx->fs && ctx->fs->io != ctx->journal_io)
944 io_channel_close(ctx->journal_io);
945 ctx->journal_io = 0;
946 }
Rob Landleye7c43b62006-03-01 16:39:45 +0000947 if (ctx->fs) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000948 ext2fs_free_dblist(ctx->fs->dblist);
949 ctx->fs->dblist = 0;
950 }
951 e2fsck_free_dir_info(ctx);
952#ifdef ENABLE_HTREE
953 e2fsck_free_dx_dir_info(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +0000954#endif
Rob Landleye7c43b62006-03-01 16:39:45 +0000955 ea_refcount_free(ctx->refcount);
956 ctx->refcount = 0;
957 ea_refcount_free(ctx->refcount_extra);
958 ctx->refcount_extra = 0;
959 ext2fs_free_block_bitmap(ctx->block_dup_map);
960 ctx->block_dup_map = 0;
961 ext2fs_free_block_bitmap(ctx->block_ea_map);
962 ctx->block_ea_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +0000963 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
964 ctx->inode_bad_map = 0;
965 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
966 ctx->inode_imagic_map = 0;
967 ext2fs_u32_list_free(ctx->dirs_to_hash);
968 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000969
970 /*
971 * Clear the array of invalid meta-data flags
972 */
Rob Landleye7c43b62006-03-01 16:39:45 +0000973 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
974 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
975 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000976
977 /* Clear statistic counters */
978 ctx->fs_directory_count = 0;
979 ctx->fs_regular_count = 0;
980 ctx->fs_blockdev_count = 0;
981 ctx->fs_chardev_count = 0;
982 ctx->fs_links_count = 0;
983 ctx->fs_symlinks_count = 0;
984 ctx->fs_fast_symlinks_count = 0;
985 ctx->fs_fifo_count = 0;
986 ctx->fs_total_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000987 ctx->fs_sockets_count = 0;
988 ctx->fs_ind_count = 0;
989 ctx->fs_dind_count = 0;
990 ctx->fs_tind_count = 0;
991 ctx->fs_fragmented = 0;
992 ctx->large_files = 0;
993
994 /* Reset the superblock to the user's requested value */
995 ctx->superblock = ctx->use_superblock;
996
997 return 0;
998}
999
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001000static void e2fsck_free_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001001{
1002 if (!ctx)
1003 return;
1004
1005 e2fsck_reset_context(ctx);
1006 if (ctx->blkid)
1007 blkid_put_cache(ctx->blkid);
1008
1009 ext2fs_free_mem(&ctx);
1010}
1011
1012/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001013 * ea_refcount.c
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001014 */
1015
1016/*
1017 * The strategy we use for keeping track of EA refcounts is as
1018 * follows. We keep a sorted array of first EA blocks and its
1019 * reference counts. Once the refcount has dropped to zero, it is
1020 * removed from the array to save memory space. Once the EA block is
1021 * checked, its bit is set in the block_ea_map bitmap.
1022 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001023
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001024
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001025static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001026{
1027 ext2_refcount_t refcount;
1028 errcode_t retval;
1029 size_t bytes;
1030
1031 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
1032 if (retval)
1033 return retval;
1034 memset(refcount, 0, sizeof(struct ea_refcount));
1035
1036 if (!size)
1037 size = 500;
1038 refcount->size = size;
1039 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
1040#ifdef DEBUG
1041 printf("Refcount allocated %d entries, %d bytes.\n",
1042 refcount->size, bytes);
1043#endif
1044 retval = ext2fs_get_mem(bytes, &refcount->list);
1045 if (retval)
1046 goto errout;
1047 memset(refcount->list, 0, bytes);
1048
1049 refcount->count = 0;
1050 refcount->cursor = 0;
1051
1052 *ret = refcount;
1053 return 0;
1054
1055errout:
1056 ea_refcount_free(refcount);
1057 return(retval);
1058}
1059
1060/*
1061 * collapse_refcount() --- go through the refcount array, and get rid
1062 * of any count == zero entries
1063 */
1064static void refcount_collapse(ext2_refcount_t refcount)
1065{
1066 unsigned int i, j;
1067 struct ea_refcount_el *list;
1068
1069 list = refcount->list;
1070 for (i = 0, j = 0; i < refcount->count; i++) {
1071 if (list[i].ea_count) {
1072 if (i != j)
1073 list[j] = list[i];
1074 j++;
1075 }
1076 }
1077#if defined(DEBUG) || defined(TEST_PROGRAM)
1078 printf("Refcount_collapse: size was %d, now %d\n",
1079 refcount->count, j);
1080#endif
1081 refcount->count = j;
1082}
1083
1084
1085/*
1086 * insert_refcount_el() --- Insert a new entry into the sorted list at a
1087 * specified position.
1088 */
1089static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
1090 blk_t blk, int pos)
1091{
1092 struct ea_refcount_el *el;
1093 errcode_t retval;
1094 blk_t new_size = 0;
1095 int num;
1096
1097 if (refcount->count >= refcount->size) {
1098 new_size = refcount->size + 100;
1099#ifdef DEBUG
1100 printf("Reallocating refcount %d entries...\n", new_size);
1101#endif
1102 retval = ext2fs_resize_mem((size_t) refcount->size *
1103 sizeof(struct ea_refcount_el),
1104 (size_t) new_size *
1105 sizeof(struct ea_refcount_el),
1106 &refcount->list);
1107 if (retval)
1108 return 0;
1109 refcount->size = new_size;
1110 }
1111 num = (int) refcount->count - pos;
1112 if (num < 0)
1113 return 0; /* should never happen */
1114 if (num) {
1115 memmove(&refcount->list[pos+1], &refcount->list[pos],
1116 sizeof(struct ea_refcount_el) * num);
1117 }
1118 refcount->count++;
1119 el = &refcount->list[pos];
1120 el->ea_count = 0;
1121 el->ea_blk = blk;
1122 return el;
1123}
1124
1125
1126/*
1127 * get_refcount_el() --- given an block number, try to find refcount
1128 * information in the sorted list. If the create flag is set,
1129 * and we can't find an entry, create one in the sorted list.
1130 */
1131static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
1132 blk_t blk, int create)
1133{
1134 float range;
1135 int low, high, mid;
1136 blk_t lowval, highval;
1137
1138 if (!refcount || !refcount->list)
1139 return 0;
1140retry:
1141 low = 0;
1142 high = (int) refcount->count-1;
1143 if (create && ((refcount->count == 0) ||
1144 (blk > refcount->list[high].ea_blk))) {
1145 if (refcount->count >= refcount->size)
1146 refcount_collapse(refcount);
1147
1148 return insert_refcount_el(refcount, blk,
1149 (unsigned) refcount->count);
1150 }
1151 if (refcount->count == 0)
1152 return 0;
1153
1154 if (refcount->cursor >= refcount->count)
1155 refcount->cursor = 0;
1156 if (blk == refcount->list[refcount->cursor].ea_blk)
1157 return &refcount->list[refcount->cursor++];
1158#ifdef DEBUG
1159 printf("Non-cursor get_refcount_el: %u\n", blk);
1160#endif
1161 while (low <= high) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001162 if (low == high)
1163 mid = low;
1164 else {
1165 /* Interpolate for efficiency */
1166 lowval = refcount->list[low].ea_blk;
1167 highval = refcount->list[high].ea_blk;
1168
1169 if (blk < lowval)
1170 range = 0;
1171 else if (blk > highval)
1172 range = 1;
1173 else
1174 range = ((float) (blk - lowval)) /
1175 (highval - lowval);
1176 mid = low + ((int) (range * (high-low)));
1177 }
Rob Landley3e72c592006-04-06 22:49:04 +00001178
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001179 if (blk == refcount->list[mid].ea_blk) {
1180 refcount->cursor = mid+1;
1181 return &refcount->list[mid];
1182 }
1183 if (blk < refcount->list[mid].ea_blk)
1184 high = mid-1;
1185 else
1186 low = mid+1;
1187 }
1188 /*
1189 * If we need to create a new entry, it should be right at
1190 * low (where high will be left at low-1).
1191 */
1192 if (create) {
1193 if (refcount->count >= refcount->size) {
1194 refcount_collapse(refcount);
1195 if (refcount->count < refcount->size)
1196 goto retry;
1197 }
1198 return insert_refcount_el(refcount, blk, low);
1199 }
1200 return 0;
1201}
1202
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001203static errcode_t
1204ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001205{
1206 struct ea_refcount_el *el;
1207
1208 el = get_refcount_el(refcount, blk, 1);
1209 if (!el)
1210 return EXT2_ET_NO_MEMORY;
1211 el->ea_count++;
1212
1213 if (ret)
1214 *ret = el->ea_count;
1215 return 0;
1216}
1217
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001218static errcode_t
1219ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001220{
1221 struct ea_refcount_el *el;
1222
1223 el = get_refcount_el(refcount, blk, 0);
1224 if (!el || el->ea_count == 0)
1225 return EXT2_ET_INVALID_ARGUMENT;
1226
1227 el->ea_count--;
1228
1229 if (ret)
1230 *ret = el->ea_count;
1231 return 0;
1232}
1233
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001234static errcode_t
1235ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001236{
1237 struct ea_refcount_el *el;
1238
1239 /*
1240 * Get the refcount element
1241 */
1242 el = get_refcount_el(refcount, blk, count ? 1 : 0);
1243 if (!el)
1244 return count ? EXT2_ET_NO_MEMORY : 0;
1245 el->ea_count = count;
1246 return 0;
1247}
1248
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001249static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001250{
1251 refcount->cursor = 0;
1252}
1253
1254
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001255static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001256{
1257 struct ea_refcount_el *list;
1258
1259 while (1) {
1260 if (refcount->cursor >= refcount->count)
1261 return 0;
1262 list = refcount->list;
1263 if (list[refcount->cursor].ea_count) {
1264 if (ret)
1265 *ret = list[refcount->cursor].ea_count;
1266 return list[refcount->cursor++].ea_blk;
1267 }
1268 refcount->cursor++;
1269 }
1270}
1271
1272
1273/*
1274 * ehandler.c --- handle bad block errors which come up during the
1275 * course of an e2fsck session.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001276 */
1277
1278
1279static const char *operation;
1280
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001281static errcode_t
1282e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001283 void *data, size_t size FSCK_ATTR((unused)),
1284 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001285{
1286 int i;
1287 char *p;
1288 ext2_filsys fs = (ext2_filsys) channel->app_data;
1289 e2fsck_t ctx;
1290
1291 ctx = (e2fsck_t) fs->priv_data;
1292
1293 /*
1294 * If more than one block was read, try reading each block
1295 * separately. We could use the actual bytes read to figure
1296 * out where to start, but we don't bother.
1297 */
1298 if (count > 1) {
1299 p = (char *) data;
1300 for (i=0; i < count; i++, p += channel->block_size, block++) {
1301 error = io_channel_read_blk(channel, block,
1302 1, p);
1303 if (error)
1304 return error;
1305 }
1306 return 0;
1307 }
1308 if (operation)
1309 printf(_("Error reading block %lu (%s) while %s. "), block,
1310 error_message(error), operation);
1311 else
1312 printf(_("Error reading block %lu (%s). "), block,
1313 error_message(error));
1314 preenhalt(ctx);
1315 if (ask(ctx, _("Ignore error"), 1)) {
1316 if (ask(ctx, _("Force rewrite"), 1))
1317 io_channel_write_blk(channel, block, 1, data);
1318 return 0;
1319 }
1320
1321 return error;
1322}
1323
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001324static errcode_t
1325e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001326 const void *data, size_t size FSCK_ATTR((unused)),
1327 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001328{
1329 int i;
1330 const char *p;
1331 ext2_filsys fs = (ext2_filsys) channel->app_data;
1332 e2fsck_t ctx;
1333
1334 ctx = (e2fsck_t) fs->priv_data;
1335
1336 /*
1337 * If more than one block was written, try writing each block
1338 * separately. We could use the actual bytes read to figure
1339 * out where to start, but we don't bother.
1340 */
1341 if (count > 1) {
1342 p = (const char *) data;
1343 for (i=0; i < count; i++, p += channel->block_size, block++) {
1344 error = io_channel_write_blk(channel, block,
1345 1, p);
1346 if (error)
1347 return error;
1348 }
1349 return 0;
1350 }
1351
1352 if (operation)
1353 printf(_("Error writing block %lu (%s) while %s. "), block,
1354 error_message(error), operation);
1355 else
1356 printf(_("Error writing block %lu (%s). "), block,
1357 error_message(error));
1358 preenhalt(ctx);
1359 if (ask(ctx, _("Ignore error"), 1))
1360 return 0;
1361
1362 return error;
1363}
1364
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001365static inline const char *ehandler_operation(const char *op)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001366{
1367 const char *ret = operation;
1368
1369 operation = op;
1370 return ret;
1371}
1372
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001373static void ehandler_init(io_channel channel)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001374{
1375 channel->read_error = e2fsck_handle_read_error;
1376 channel->write_error = e2fsck_handle_write_error;
1377}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001378
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001379/*
1380 * journal.c --- code for handling the "ext3" journal
1381 *
1382 * Copyright (C) 2000 Andreas Dilger
1383 * Copyright (C) 2000 Theodore Ts'o
1384 *
1385 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
1386 * Copyright (C) 1999 Red Hat Software
1387 *
1388 * This file may be redistributed under the terms of the
1389 * GNU General Public License version 2 or at your discretion
1390 * any later version.
1391 */
1392
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001393/*
1394 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
1395 * This creates a larger static binary, and a smaller binary using
1396 * shared libraries. It's also probably slightly less CPU-efficient,
1397 * which is why it's not on by default. But, it's a good way of
1398 * testing the functions in inode_io.c and fileio.c.
1399 */
1400#undef USE_INODE_IO
1401
1402/* Kernel compatibility functions for handling the journal. These allow us
1403 * to use the recovery.c file virtually unchanged from the kernel, so we
1404 * don't have to do much to keep kernel and user recovery in sync.
1405 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001406static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001407{
1408#ifdef USE_INODE_IO
1409 *phys = block;
1410 return 0;
1411#else
1412 struct inode *inode = journal->j_inode;
1413 errcode_t retval;
1414 blk_t pblk;
1415
1416 if (!inode) {
1417 *phys = block;
1418 return 0;
1419 }
1420
1421 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
1422 &inode->i_ext2, NULL, 0, block, &pblk);
1423 *phys = pblk;
1424 return (retval);
1425#endif
1426}
1427
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001428static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001429{
1430 struct buffer_head *bh;
1431
1432 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
1433 if (!bh)
1434 return NULL;
1435
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001436 bh->b_ctx = kdev->k_ctx;
1437 if (kdev->k_dev == K_DEV_FS)
1438 bh->b_io = kdev->k_ctx->fs->io;
1439 else
1440 bh->b_io = kdev->k_ctx->journal_io;
1441 bh->b_size = blocksize;
1442 bh->b_blocknr = blocknr;
1443
1444 return bh;
1445}
1446
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001447static void sync_blockdev(kdev_t kdev)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001448{
1449 io_channel io;
1450
1451 if (kdev->k_dev == K_DEV_FS)
1452 io = kdev->k_ctx->fs->io;
1453 else
1454 io = kdev->k_ctx->journal_io;
1455
1456 io_channel_flush(io);
1457}
1458
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001459static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001460{
1461 int retval;
1462 struct buffer_head *bh;
1463
1464 for (; nr > 0; --nr) {
1465 bh = *bhp++;
1466 if (rw == READ && !bh->b_uptodate) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001467 retval = io_channel_read_blk(bh->b_io,
1468 bh->b_blocknr,
1469 1, bh->b_data);
1470 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001471 bb_error_msg("while reading block %lu\n",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001472 (unsigned long) bh->b_blocknr);
1473 bh->b_err = retval;
1474 continue;
1475 }
1476 bh->b_uptodate = 1;
1477 } else if (rw == WRITE && bh->b_dirty) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001478 retval = io_channel_write_blk(bh->b_io,
1479 bh->b_blocknr,
1480 1, bh->b_data);
1481 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001482 bb_error_msg("while writing block %lu\n",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001483 (unsigned long) bh->b_blocknr);
1484 bh->b_err = retval;
1485 continue;
1486 }
1487 bh->b_dirty = 0;
1488 bh->b_uptodate = 1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001489 }
1490 }
1491}
1492
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001493static inline void mark_buffer_dirty(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001494{
1495 bh->b_dirty = 1;
1496}
1497
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001498static inline void mark_buffer_clean(struct buffer_head * bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001499{
1500 bh->b_dirty = 0;
1501}
1502
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001503static void brelse(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001504{
1505 if (bh->b_dirty)
1506 ll_rw_block(WRITE, 1, &bh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001507 ext2fs_free_mem(&bh);
1508}
1509
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001510static inline int buffer_uptodate(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001511{
1512 return bh->b_uptodate;
1513}
1514
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001515static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001516{
1517 bh->b_uptodate = val;
1518}
1519
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001520static void wait_on_buffer(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001521{
1522 if (!bh->b_uptodate)
1523 ll_rw_block(READ, 1, &bh);
1524}
1525
1526
1527static void e2fsck_clear_recover(e2fsck_t ctx, int error)
1528{
1529 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
1530
1531 /* if we had an error doing journal recovery, we need a full fsck */
1532 if (error)
1533 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
1534 ext2fs_mark_super_dirty(ctx->fs);
1535}
1536
1537static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
1538{
1539 struct ext2_super_block *sb = ctx->fs->super;
1540 struct ext2_super_block jsuper;
1541 struct problem_context pctx;
1542 struct buffer_head *bh;
1543 struct inode *j_inode = NULL;
1544 struct kdev_s *dev_fs = NULL, *dev_journal;
1545 const char *journal_name = 0;
1546 journal_t *journal = NULL;
1547 errcode_t retval = 0;
1548 io_manager io_ptr = 0;
1549 unsigned long start = 0;
1550 blk_t blk;
1551 int ext_journal = 0;
1552 int tried_backup_jnl = 0;
1553 int i;
1554
1555 clear_problem_context(&pctx);
1556
1557 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
1558 if (!journal) {
1559 return EXT2_ET_NO_MEMORY;
1560 }
1561
1562 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
1563 if (!dev_fs) {
1564 retval = EXT2_ET_NO_MEMORY;
1565 goto errout;
1566 }
1567 dev_journal = dev_fs+1;
1568
1569 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
1570 dev_fs->k_dev = K_DEV_FS;
1571 dev_journal->k_dev = K_DEV_JOURNAL;
1572
1573 journal->j_dev = dev_journal;
1574 journal->j_fs_dev = dev_fs;
1575 journal->j_inode = NULL;
1576 journal->j_blocksize = ctx->fs->blocksize;
1577
1578 if (uuid_is_null(sb->s_journal_uuid)) {
1579 if (!sb->s_journal_inum)
1580 return EXT2_ET_BAD_INODE_NUM;
1581 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
1582 "journal inode");
1583 if (!j_inode) {
1584 retval = EXT2_ET_NO_MEMORY;
1585 goto errout;
1586 }
1587
1588 j_inode->i_ctx = ctx;
1589 j_inode->i_ino = sb->s_journal_inum;
1590
1591 if ((retval = ext2fs_read_inode(ctx->fs,
1592 sb->s_journal_inum,
1593 &j_inode->i_ext2))) {
1594 try_backup_journal:
1595 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
1596 tried_backup_jnl)
1597 goto errout;
1598 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
1599 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
1600 EXT2_N_BLOCKS*4);
1601 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
1602 j_inode->i_ext2.i_links_count = 1;
1603 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
1604 tried_backup_jnl++;
1605 }
1606 if (!j_inode->i_ext2.i_links_count ||
1607 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
1608 retval = EXT2_ET_NO_JOURNAL;
1609 goto try_backup_journal;
1610 }
1611 if (j_inode->i_ext2.i_size / journal->j_blocksize <
1612 JFS_MIN_JOURNAL_BLOCKS) {
1613 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1614 goto try_backup_journal;
1615 }
1616 for (i=0; i < EXT2_N_BLOCKS; i++) {
1617 blk = j_inode->i_ext2.i_block[i];
1618 if (!blk) {
1619 if (i < EXT2_NDIR_BLOCKS) {
1620 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1621 goto try_backup_journal;
1622 }
1623 continue;
1624 }
1625 if (blk < sb->s_first_data_block ||
1626 blk >= sb->s_blocks_count) {
1627 retval = EXT2_ET_BAD_BLOCK_NUM;
1628 goto try_backup_journal;
1629 }
1630 }
1631 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
1632
1633#ifdef USE_INODE_IO
1634 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
1635 &j_inode->i_ext2,
1636 &journal_name);
1637 if (retval)
1638 goto errout;
1639
1640 io_ptr = inode_io_manager;
1641#else
1642 journal->j_inode = j_inode;
1643 ctx->journal_io = ctx->fs->io;
1644 if ((retval = journal_bmap(journal, 0, &start)) != 0)
1645 goto errout;
1646#endif
1647 } else {
1648 ext_journal = 1;
1649 if (!ctx->journal_name) {
1650 char uuid[37];
1651
1652 uuid_unparse(sb->s_journal_uuid, uuid);
1653 ctx->journal_name = blkid_get_devname(ctx->blkid,
1654 "UUID", uuid);
1655 if (!ctx->journal_name)
1656 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
1657 }
1658 journal_name = ctx->journal_name;
1659
1660 if (!journal_name) {
1661 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
1662 return EXT2_ET_LOAD_EXT_JOURNAL;
1663 }
1664
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001665 io_ptr = unix_io_manager;
1666 }
1667
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001668#ifndef USE_INODE_IO
1669 if (ext_journal)
1670#endif
1671 retval = io_ptr->open(journal_name, IO_FLAG_RW,
1672 &ctx->journal_io);
1673 if (retval)
1674 goto errout;
1675
1676 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
1677
1678 if (ext_journal) {
1679 if (ctx->fs->blocksize == 1024)
1680 start = 1;
1681 bh = getblk(dev_journal, start, ctx->fs->blocksize);
1682 if (!bh) {
1683 retval = EXT2_ET_NO_MEMORY;
1684 goto errout;
1685 }
1686 ll_rw_block(READ, 1, &bh);
1687 if ((retval = bh->b_err) != 0)
1688 goto errout;
1689 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
1690 sizeof(jsuper));
1691 brelse(bh);
Rob Landley7c94bed2006-05-03 21:58:45 +00001692#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001693 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
1694 ext2fs_swap_super(&jsuper);
1695#endif
1696 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
1697 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
1698 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
1699 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1700 goto errout;
1701 }
1702 /* Make sure the journal UUID is correct */
1703 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
1704 sizeof(jsuper.s_uuid))) {
1705 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
1706 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1707 goto errout;
1708 }
1709
1710 journal->j_maxlen = jsuper.s_blocks_count;
1711 start++;
1712 }
1713
1714 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
1715 retval = EXT2_ET_NO_MEMORY;
1716 goto errout;
1717 }
1718
1719 journal->j_sb_buffer = bh;
1720 journal->j_superblock = (journal_superblock_t *)bh->b_data;
1721
1722#ifdef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001723 ext2fs_free_mem(&j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001724#endif
1725
1726 *ret_journal = journal;
1727 return 0;
1728
1729errout:
Rob Landleye7c43b62006-03-01 16:39:45 +00001730 ext2fs_free_mem(&dev_fs);
1731 ext2fs_free_mem(&j_inode);
1732 ext2fs_free_mem(&journal);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001733 return retval;
1734
1735}
1736
1737static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
1738 struct problem_context *pctx)
1739{
1740 struct ext2_super_block *sb = ctx->fs->super;
1741 int recover = ctx->fs->super->s_feature_incompat &
1742 EXT3_FEATURE_INCOMPAT_RECOVER;
1743 int has_journal = ctx->fs->super->s_feature_compat &
1744 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1745
1746 if (has_journal || sb->s_journal_inum) {
1747 /* The journal inode is bogus, remove and force full fsck */
1748 pctx->ino = sb->s_journal_inum;
1749 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
1750 if (has_journal && sb->s_journal_inum)
1751 printf("*** ext3 journal has been deleted - "
1752 "filesystem is now ext2 only ***\n\n");
1753 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1754 sb->s_journal_inum = 0;
1755 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
1756 e2fsck_clear_recover(ctx, 1);
1757 return 0;
1758 }
1759 return EXT2_ET_BAD_INODE_NUM;
1760 } else if (recover) {
1761 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
1762 e2fsck_clear_recover(ctx, 1);
1763 return 0;
1764 }
1765 return EXT2_ET_UNSUPP_FEATURE;
1766 }
1767 return 0;
1768}
1769
1770#define V1_SB_SIZE 0x0024
1771static void clear_v2_journal_fields(journal_t *journal)
1772{
1773 e2fsck_t ctx = journal->j_dev->k_ctx;
1774 struct problem_context pctx;
1775
1776 clear_problem_context(&pctx);
1777
1778 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
1779 return;
1780
1781 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
1782 ctx->fs->blocksize-V1_SB_SIZE);
1783 mark_buffer_dirty(journal->j_sb_buffer);
1784}
1785
1786
1787static errcode_t e2fsck_journal_load(journal_t *journal)
1788{
1789 e2fsck_t ctx = journal->j_dev->k_ctx;
1790 journal_superblock_t *jsb;
1791 struct buffer_head *jbh = journal->j_sb_buffer;
1792 struct problem_context pctx;
1793
1794 clear_problem_context(&pctx);
1795
1796 ll_rw_block(READ, 1, &jbh);
1797 if (jbh->b_err) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001798 bb_error_msg(_("reading journal superblock\n"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001799 return jbh->b_err;
1800 }
1801
1802 jsb = journal->j_superblock;
1803 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
1804 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
1805 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
1806
1807 switch (ntohl(jsb->s_header.h_blocktype)) {
1808 case JFS_SUPERBLOCK_V1:
1809 journal->j_format_version = 1;
1810 if (jsb->s_feature_compat ||
1811 jsb->s_feature_incompat ||
1812 jsb->s_feature_ro_compat ||
1813 jsb->s_nr_users)
1814 clear_v2_journal_fields(journal);
1815 break;
1816
1817 case JFS_SUPERBLOCK_V2:
1818 journal->j_format_version = 2;
1819 if (ntohl(jsb->s_nr_users) > 1 &&
1820 uuid_is_null(ctx->fs->super->s_journal_uuid))
1821 clear_v2_journal_fields(journal);
1822 if (ntohl(jsb->s_nr_users) > 1) {
1823 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
1824 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1825 }
1826 break;
1827
1828 /*
1829 * These should never appear in a journal super block, so if
1830 * they do, the journal is badly corrupted.
1831 */
1832 case JFS_DESCRIPTOR_BLOCK:
1833 case JFS_COMMIT_BLOCK:
1834 case JFS_REVOKE_BLOCK:
1835 return EXT2_ET_CORRUPT_SUPERBLOCK;
1836
1837 /* If we don't understand the superblock major type, but there
1838 * is a magic number, then it is likely to be a new format we
1839 * just don't understand, so leave it alone. */
1840 default:
1841 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1842 }
1843
1844 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
1845 return EXT2_ET_UNSUPP_FEATURE;
1846
1847 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
1848 return EXT2_ET_RO_UNSUPP_FEATURE;
1849
1850 /* We have now checked whether we know enough about the journal
1851 * format to be able to proceed safely, so any other checks that
1852 * fail we should attempt to recover from. */
1853 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001854 bb_error_msg(_("%s: no valid journal superblock found\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001855 ctx->device_name);
1856 return EXT2_ET_CORRUPT_SUPERBLOCK;
1857 }
1858
1859 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
1860 journal->j_maxlen = ntohl(jsb->s_maxlen);
1861 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001862 bb_error_msg(_("%s: journal too short\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001863 ctx->device_name);
1864 return EXT2_ET_CORRUPT_SUPERBLOCK;
1865 }
1866
1867 journal->j_tail_sequence = ntohl(jsb->s_sequence);
1868 journal->j_transaction_sequence = journal->j_tail_sequence;
1869 journal->j_tail = ntohl(jsb->s_start);
1870 journal->j_first = ntohl(jsb->s_first);
1871 journal->j_last = ntohl(jsb->s_maxlen);
1872
1873 return 0;
1874}
1875
1876static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
1877 journal_t *journal)
1878{
1879 char *p;
1880 union {
1881 uuid_t uuid;
1882 __u32 val[4];
1883 } u;
1884 __u32 new_seq = 0;
1885 int i;
1886
1887 /* Leave a valid existing V1 superblock signature alone.
1888 * Anything unrecognisable we overwrite with a new V2
1889 * signature. */
1890
1891 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
1892 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
1893 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
1894 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
1895 }
1896
1897 /* Zero out everything else beyond the superblock header */
1898
1899 p = ((char *) jsb) + sizeof(journal_header_t);
1900 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
1901
1902 jsb->s_blocksize = htonl(ctx->fs->blocksize);
1903 jsb->s_maxlen = htonl(journal->j_maxlen);
1904 jsb->s_first = htonl(1);
1905
1906 /* Initialize the journal sequence number so that there is "no"
1907 * chance we will find old "valid" transactions in the journal.
1908 * This avoids the need to zero the whole journal (slow to do,
1909 * and risky when we are just recovering the filesystem).
1910 */
1911 uuid_generate(u.uuid);
1912 for (i = 0; i < 4; i ++)
1913 new_seq ^= u.val[i];
1914 jsb->s_sequence = htonl(new_seq);
1915
1916 mark_buffer_dirty(journal->j_sb_buffer);
1917 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
1918}
1919
1920static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
1921 journal_t *journal,
1922 struct problem_context *pctx)
1923{
1924 struct ext2_super_block *sb = ctx->fs->super;
1925 int recover = ctx->fs->super->s_feature_incompat &
1926 EXT3_FEATURE_INCOMPAT_RECOVER;
1927
1928 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
1929 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
1930 e2fsck_journal_reset_super(ctx, journal->j_superblock,
1931 journal);
1932 journal->j_transaction_sequence = 1;
1933 e2fsck_clear_recover(ctx, recover);
1934 return 0;
1935 }
1936 return EXT2_ET_CORRUPT_SUPERBLOCK;
1937 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
1938 return EXT2_ET_CORRUPT_SUPERBLOCK;
1939
1940 return 0;
1941}
1942
1943static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
1944 int reset, int drop)
1945{
1946 journal_superblock_t *jsb;
1947
1948 if (drop)
1949 mark_buffer_clean(journal->j_sb_buffer);
1950 else if (!(ctx->options & E2F_OPT_READONLY)) {
1951 jsb = journal->j_superblock;
1952 jsb->s_sequence = htonl(journal->j_transaction_sequence);
1953 if (reset)
1954 jsb->s_start = 0; /* this marks the journal as empty */
1955 mark_buffer_dirty(journal->j_sb_buffer);
1956 }
1957 brelse(journal->j_sb_buffer);
1958
1959 if (ctx->journal_io) {
1960 if (ctx->fs && ctx->fs->io != ctx->journal_io)
1961 io_channel_close(ctx->journal_io);
1962 ctx->journal_io = 0;
1963 }
1964
1965#ifndef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001966 ext2fs_free_mem(&journal->j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001967#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00001968 ext2fs_free_mem(&journal->j_fs_dev);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001969 ext2fs_free_mem(&journal);
1970}
1971
1972/*
1973 * This function makes sure that the superblock fields regarding the
1974 * journal are consistent.
1975 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001976static int e2fsck_check_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001977{
1978 struct ext2_super_block *sb = ctx->fs->super;
1979 journal_t *journal;
1980 int recover = ctx->fs->super->s_feature_incompat &
1981 EXT3_FEATURE_INCOMPAT_RECOVER;
1982 struct problem_context pctx;
1983 problem_t problem;
1984 int reset = 0, force_fsck = 0;
1985 int retval;
1986
1987 /* If we don't have any journal features, don't do anything more */
1988 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
1989 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
1990 uuid_is_null(sb->s_journal_uuid))
1991 return 0;
1992
1993 clear_problem_context(&pctx);
1994 pctx.num = sb->s_journal_inum;
1995
1996 retval = e2fsck_get_journal(ctx, &journal);
1997 if (retval) {
1998 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
1999 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
2000 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
2001 (retval == EXT2_ET_NO_JOURNAL))
2002 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
2003 return retval;
2004 }
2005
2006 retval = e2fsck_journal_load(journal);
2007 if (retval) {
2008 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
2009 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
2010 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
2011 &pctx))) ||
2012 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
2013 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
2014 &pctx))) ||
2015 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
2016 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
2017 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
2018 &pctx);
2019 e2fsck_journal_release(ctx, journal, 0, 1);
2020 return retval;
2021 }
2022
2023 /*
2024 * We want to make the flags consistent here. We will not leave with
2025 * needs_recovery set but has_journal clear. We can't get in a loop
2026 * with -y, -n, or -p, only if a user isn't making up their mind.
2027 */
2028no_has_journal:
2029 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
2030 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
2031 pctx.str = "inode";
2032 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
2033 if (recover &&
2034 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
2035 goto no_has_journal;
2036 /*
2037 * Need a full fsck if we are releasing a
2038 * journal stored on a reserved inode.
2039 */
2040 force_fsck = recover ||
2041 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
2042 /* Clear all of the journal fields */
2043 sb->s_journal_inum = 0;
2044 sb->s_journal_dev = 0;
2045 memset(sb->s_journal_uuid, 0,
2046 sizeof(sb->s_journal_uuid));
2047 e2fsck_clear_recover(ctx, force_fsck);
2048 } else if (!(ctx->options & E2F_OPT_READONLY)) {
2049 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2050 ext2fs_mark_super_dirty(ctx->fs);
2051 }
2052 }
2053
2054 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
2055 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
2056 journal->j_superblock->s_start != 0) {
2057 /* Print status information */
2058 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
2059 if (ctx->superblock)
2060 problem = PR_0_JOURNAL_RUN_DEFAULT;
2061 else
2062 problem = PR_0_JOURNAL_RUN;
2063 if (fix_problem(ctx, problem, &pctx)) {
2064 ctx->options |= E2F_OPT_FORCE;
2065 sb->s_feature_incompat |=
2066 EXT3_FEATURE_INCOMPAT_RECOVER;
2067 ext2fs_mark_super_dirty(ctx->fs);
2068 } else if (fix_problem(ctx,
2069 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
2070 reset = 1;
2071 sb->s_state &= ~EXT2_VALID_FS;
2072 ext2fs_mark_super_dirty(ctx->fs);
2073 }
2074 /*
2075 * If the user answers no to the above question, we
2076 * ignore the fact that journal apparently has data;
2077 * accidentally replaying over valid data would be far
2078 * worse than skipping a questionable recovery.
2079 *
2080 * XXX should we abort with a fatal error here? What
2081 * will the ext3 kernel code do if a filesystem with
2082 * !NEEDS_RECOVERY but with a non-zero
2083 * journal->j_superblock->s_start is mounted?
2084 */
2085 }
2086
2087 e2fsck_journal_release(ctx, journal, reset, 0);
2088 return retval;
2089}
2090
2091static errcode_t recover_ext3_journal(e2fsck_t ctx)
2092{
2093 journal_t *journal;
2094 int retval;
2095
2096 journal_init_revoke_caches();
2097 retval = e2fsck_get_journal(ctx, &journal);
2098 if (retval)
2099 return retval;
2100
2101 retval = e2fsck_journal_load(journal);
2102 if (retval)
2103 goto errout;
2104
2105 retval = journal_init_revoke(journal, 1024);
2106 if (retval)
2107 goto errout;
2108
2109 retval = -journal_recover(journal);
2110 if (retval)
2111 goto errout;
2112
2113 if (journal->j_superblock->s_errno) {
2114 ctx->fs->super->s_state |= EXT2_ERROR_FS;
2115 ext2fs_mark_super_dirty(ctx->fs);
2116 journal->j_superblock->s_errno = 0;
2117 mark_buffer_dirty(journal->j_sb_buffer);
2118 }
2119
2120errout:
2121 journal_destroy_revoke(journal);
2122 journal_destroy_revoke_caches();
2123 e2fsck_journal_release(ctx, journal, 1, 0);
2124 return retval;
2125}
2126
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002127static int e2fsck_run_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002128{
2129 io_manager io_ptr = ctx->fs->io->manager;
2130 int blocksize = ctx->fs->blocksize;
2131 errcode_t retval, recover_retval;
2132
2133 printf(_("%s: recovering journal\n"), ctx->device_name);
2134 if (ctx->options & E2F_OPT_READONLY) {
2135 printf(_("%s: won't do journal recovery while read-only\n"),
2136 ctx->device_name);
2137 return EXT2_ET_FILE_RO;
2138 }
2139
2140 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
2141 ext2fs_flush(ctx->fs); /* Force out any modifications */
2142
2143 recover_retval = recover_ext3_journal(ctx);
2144
2145 /*
2146 * Reload the filesystem context to get up-to-date data from disk
2147 * because journal recovery will change the filesystem under us.
2148 */
2149 ext2fs_close(ctx->fs);
2150 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
2151 ctx->superblock, blocksize, io_ptr,
2152 &ctx->fs);
2153
2154 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00002155 bb_error_msg(_("while trying to re-open %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002156 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +00002157 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002158 }
2159 ctx->fs->priv_data = ctx;
2160
2161 /* Set the superblock flags */
2162 e2fsck_clear_recover(ctx, recover_retval);
2163 return recover_retval;
2164}
2165
2166/*
2167 * This function will move the journal inode from a visible file in
2168 * the filesystem directory hierarchy to the reserved inode if necessary.
2169 */
2170static const char * const journal_names[] = {
2171 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
2172
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002173static void e2fsck_move_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002174{
2175 struct ext2_super_block *sb = ctx->fs->super;
2176 struct problem_context pctx;
2177 struct ext2_inode inode;
2178 ext2_filsys fs = ctx->fs;
2179 ext2_ino_t ino;
2180 errcode_t retval;
2181 const char * const * cpp;
2182 int group, mount_flags;
2183
2184 clear_problem_context(&pctx);
2185
2186 /*
2187 * If the filesystem is opened read-only, or there is no
2188 * journal, then do nothing.
2189 */
2190 if ((ctx->options & E2F_OPT_READONLY) ||
2191 (sb->s_journal_inum == 0) ||
2192 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
2193 return;
2194
2195 /*
2196 * Read in the journal inode
2197 */
2198 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
2199 return;
2200
2201 /*
2202 * If it's necessary to backup the journal inode, do so.
2203 */
2204 if ((sb->s_jnl_backup_type == 0) ||
2205 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
2206 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
2207 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
2208 memcpy(sb->s_jnl_blocks, inode.i_block,
2209 EXT2_N_BLOCKS*4);
2210 sb->s_jnl_blocks[16] = inode.i_size;
2211 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
2212 ext2fs_mark_super_dirty(fs);
2213 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2214 }
2215 }
2216
2217 /*
2218 * If the journal is already the hidden inode, then do nothing
2219 */
2220 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
2221 return;
2222
2223 /*
2224 * The journal inode had better have only one link and not be readable.
2225 */
2226 if (inode.i_links_count != 1)
2227 return;
2228
2229 /*
2230 * If the filesystem is mounted, or we can't tell whether
2231 * or not it's mounted, do nothing.
2232 */
2233 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
2234 if (retval || (mount_flags & EXT2_MF_MOUNTED))
2235 return;
2236
2237 /*
2238 * If we can't find the name of the journal inode, then do
2239 * nothing.
2240 */
2241 for (cpp = journal_names; *cpp; cpp++) {
2242 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
2243 strlen(*cpp), 0, &ino);
2244 if ((retval == 0) && (ino == sb->s_journal_inum))
2245 break;
2246 }
2247 if (*cpp == 0)
2248 return;
2249
2250 /* We need the inode bitmap to be loaded */
2251 retval = ext2fs_read_bitmaps(fs);
2252 if (retval)
2253 return;
2254
2255 pctx.str = *cpp;
2256 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
2257 return;
2258
2259 /*
2260 * OK, we've done all the checks, let's actually move the
2261 * journal inode. Errors at this point mean we need to force
2262 * an ext2 filesystem check.
2263 */
2264 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
2265 goto err_out;
2266 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
2267 goto err_out;
2268 sb->s_journal_inum = EXT2_JOURNAL_INO;
2269 ext2fs_mark_super_dirty(fs);
2270 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2271 inode.i_links_count = 0;
2272 inode.i_dtime = time(0);
2273 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
2274 goto err_out;
2275
2276 group = ext2fs_group_of_ino(fs, ino);
2277 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
2278 ext2fs_mark_ib_dirty(fs);
2279 fs->group_desc[group].bg_free_inodes_count++;
2280 fs->super->s_free_inodes_count++;
2281 return;
2282
2283err_out:
2284 pctx.errcode = retval;
2285 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
2286 fs->super->s_state &= ~EXT2_VALID_FS;
2287 ext2fs_mark_super_dirty(fs);
2288 return;
2289}
2290
2291/*
2292 * message.c --- print e2fsck messages (with compression)
2293 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002294 * print_e2fsck_message() prints a message to the user, using
2295 * compression techniques and expansions of abbreviations.
2296 *
2297 * The following % expansions are supported:
2298 *
2299 * %b <blk> block number
2300 * %B <blkcount> integer
2301 * %c <blk2> block number
2302 * %Di <dirent>->ino inode number
2303 * %Dn <dirent>->name string
2304 * %Dr <dirent>->rec_len
2305 * %Dl <dirent>->name_len
2306 * %Dt <dirent>->filetype
2307 * %d <dir> inode number
2308 * %g <group> integer
2309 * %i <ino> inode number
2310 * %Is <inode> -> i_size
2311 * %IS <inode> -> i_extra_isize
2312 * %Ib <inode> -> i_blocks
2313 * %Il <inode> -> i_links_count
2314 * %Im <inode> -> i_mode
2315 * %IM <inode> -> i_mtime
2316 * %IF <inode> -> i_faddr
2317 * %If <inode> -> i_file_acl
2318 * %Id <inode> -> i_dir_acl
2319 * %Iu <inode> -> i_uid
2320 * %Ig <inode> -> i_gid
2321 * %j <ino2> inode number
2322 * %m <com_err error message>
2323 * %N <num>
2324 * %p ext2fs_get_pathname of directory <ino>
2325 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
2326 * the containing directory. (If dirent is NULL
2327 * then return the pathname of directory <ino2>)
2328 * %q ext2fs_get_pathname of directory <dir>
2329 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
2330 * the containing directory.
2331 * %s <str> miscellaneous string
2332 * %S backup superblock
2333 * %X <num> hexadecimal format
2334 *
2335 * The following '@' expansions are supported:
2336 *
2337 * @a extended attribute
2338 * @A error allocating
2339 * @b block
2340 * @B bitmap
2341 * @c compress
2342 * @C conflicts with some other fs block
2343 * @D deleted
2344 * @d directory
2345 * @e entry
2346 * @E Entry '%Dn' in %p (%i)
2347 * @f filesystem
2348 * @F for @i %i (%Q) is
2349 * @g group
2350 * @h HTREE directory inode
2351 * @i inode
2352 * @I illegal
2353 * @j journal
2354 * @l lost+found
2355 * @L is a link
Mike Frysinger874af852006-03-08 07:03:27 +00002356 * @m multiply-claimed
2357 * @n invalid
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002358 * @o orphaned
2359 * @p problem in
2360 * @r root inode
2361 * @s should be
2362 * @S superblock
2363 * @u unattached
2364 * @v device
2365 * @z zero-length
2366 */
2367
2368
2369/*
2370 * This structure defines the abbreviations used by the text strings
2371 * below. The first character in the string is the index letter. An
2372 * abbreviation of the form '@<i>' is expanded by looking up the index
2373 * letter <i> in the table below.
2374 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002375static const char * const abbrevs[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002376 N_("aextended attribute"),
2377 N_("Aerror allocating"),
2378 N_("bblock"),
2379 N_("Bbitmap"),
2380 N_("ccompress"),
2381 N_("Cconflicts with some other fs @b"),
2382 N_("iinode"),
2383 N_("Iillegal"),
2384 N_("jjournal"),
2385 N_("Ddeleted"),
2386 N_("ddirectory"),
2387 N_("eentry"),
2388 N_("E@e '%Dn' in %p (%i)"),
2389 N_("ffilesystem"),
2390 N_("Ffor @i %i (%Q) is"),
2391 N_("ggroup"),
2392 N_("hHTREE @d @i"),
2393 N_("llost+found"),
2394 N_("Lis a link"),
Mike Frysinger874af852006-03-08 07:03:27 +00002395 N_("mmultiply-claimed"),
2396 N_("ninvalid"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002397 N_("oorphaned"),
2398 N_("pproblem in"),
2399 N_("rroot @i"),
2400 N_("sshould be"),
2401 N_("Ssuper@b"),
2402 N_("uunattached"),
2403 N_("vdevice"),
2404 N_("zzero-length"),
2405 "@@",
2406 0
2407 };
2408
2409/*
2410 * Give more user friendly names to the "special" inodes.
2411 */
2412#define num_special_inodes 11
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002413static const char * const special_inode_name[] =
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002414{
2415 N_("<The NULL inode>"), /* 0 */
2416 N_("<The bad blocks inode>"), /* 1 */
2417 "/", /* 2 */
2418 N_("<The ACL index inode>"), /* 3 */
2419 N_("<The ACL data inode>"), /* 4 */
2420 N_("<The boot loader inode>"), /* 5 */
2421 N_("<The undelete directory inode>"), /* 6 */
2422 N_("<The group descriptor inode>"), /* 7 */
2423 N_("<The journal inode>"), /* 8 */
2424 N_("<Reserved inode 9>"), /* 9 */
2425 N_("<Reserved inode 10>"), /* 10 */
2426};
2427
2428/*
2429 * This function does "safe" printing. It will convert non-printable
2430 * ASCII characters using '^' and M- notation.
2431 */
2432static void safe_print(const char *cp, int len)
2433{
2434 unsigned char ch;
2435
2436 if (len < 0)
2437 len = strlen(cp);
2438
2439 while (len--) {
2440 ch = *cp++;
2441 if (ch > 128) {
2442 fputs("M-", stdout);
2443 ch -= 128;
2444 }
2445 if ((ch < 32) || (ch == 0x7f)) {
2446 fputc('^', stdout);
2447 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
2448 }
2449 fputc(ch, stdout);
2450 }
2451}
2452
2453
2454/*
2455 * This function prints a pathname, using the ext2fs_get_pathname
2456 * function
2457 */
2458static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
2459{
2460 errcode_t retval;
2461 char *path;
2462
2463 if (!dir && (ino < num_special_inodes)) {
2464 fputs(_(special_inode_name[ino]), stdout);
2465 return;
2466 }
2467
2468 retval = ext2fs_get_pathname(fs, dir, ino, &path);
2469 if (retval)
2470 fputs("???", stdout);
2471 else {
2472 safe_print(path, -1);
2473 ext2fs_free_mem(&path);
2474 }
2475}
2476
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002477static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
2478 struct problem_context *pctx, int first);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002479/*
2480 * This function handles the '@' expansion. We allow recursive
2481 * expansion; an @ expression can contain further '@' and '%'
2482 * expressions.
2483 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002484static void expand_at_expression(e2fsck_t ctx, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002485 struct problem_context *pctx,
2486 int *first)
2487{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002488 const char * const *cpp;
2489 const char *str;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002490
2491 /* Search for the abbreviation */
2492 for (cpp = abbrevs; *cpp; cpp++) {
2493 if (ch == *cpp[0])
2494 break;
2495 }
2496 if (*cpp) {
2497 str = _(*cpp) + 1;
2498 if (*first && islower(*str)) {
2499 *first = 0;
2500 fputc(toupper(*str++), stdout);
2501 }
2502 print_e2fsck_message(ctx, str, pctx, *first);
2503 } else
2504 printf("@%c", ch);
2505}
2506
2507/*
2508 * This function expands '%IX' expressions
2509 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002510static void expand_inode_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002511 struct problem_context *ctx)
2512{
2513 struct ext2_inode *inode;
2514 struct ext2_inode_large *large_inode;
2515 char * time_str;
2516 time_t t;
2517 int do_gmt = -1;
2518
2519 if (!ctx || !ctx->inode)
2520 goto no_inode;
2521
2522 inode = ctx->inode;
2523 large_inode = (struct ext2_inode_large *) inode;
2524
2525 switch (ch) {
2526 case 's':
2527 if (LINUX_S_ISDIR(inode->i_mode))
2528 printf("%u", inode->i_size);
2529 else {
Rob Landley7a260f02006-06-19 03:20:03 +00002530 printf("%"PRIu64, (inode->i_size |
2531 ((uint64_t) inode->i_size_high << 32)));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002532 }
2533 break;
2534 case 'S':
2535 printf("%u", large_inode->i_extra_isize);
2536 break;
2537 case 'b':
2538 printf("%u", inode->i_blocks);
2539 break;
2540 case 'l':
2541 printf("%d", inode->i_links_count);
2542 break;
2543 case 'm':
2544 printf("0%o", inode->i_mode);
2545 break;
2546 case 'M':
2547 /* The diet libc doesn't respect the TZ environemnt variable */
2548 if (do_gmt == -1) {
2549 time_str = getenv("TZ");
2550 if (!time_str)
2551 time_str = "";
2552 do_gmt = !strcmp(time_str, "GMT");
2553 }
2554 t = inode->i_mtime;
2555 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
2556 printf("%.24s", time_str);
2557 break;
2558 case 'F':
2559 printf("%u", inode->i_faddr);
2560 break;
2561 case 'f':
2562 printf("%u", inode->i_file_acl);
2563 break;
2564 case 'd':
2565 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
2566 inode->i_dir_acl : 0));
2567 break;
2568 case 'u':
2569 printf("%d", (inode->i_uid |
2570 (inode->osd2.linux2.l_i_uid_high << 16)));
2571 break;
2572 case 'g':
2573 printf("%d", (inode->i_gid |
2574 (inode->osd2.linux2.l_i_gid_high << 16)));
2575 break;
2576 default:
2577 no_inode:
2578 printf("%%I%c", ch);
2579 break;
2580 }
2581}
2582
2583/*
2584 * This function expands '%dX' expressions
2585 */
Rob Landley7c94bed2006-05-03 21:58:45 +00002586static void expand_dirent_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002587 struct problem_context *ctx)
2588{
2589 struct ext2_dir_entry *dirent;
2590 int len;
2591
2592 if (!ctx || !ctx->dirent)
2593 goto no_dirent;
2594
2595 dirent = ctx->dirent;
2596
2597 switch (ch) {
2598 case 'i':
2599 printf("%u", dirent->inode);
2600 break;
2601 case 'n':
2602 len = dirent->name_len & 0xFF;
2603 if (len > EXT2_NAME_LEN)
2604 len = EXT2_NAME_LEN;
2605 if (len > dirent->rec_len)
2606 len = dirent->rec_len;
2607 safe_print(dirent->name, len);
2608 break;
2609 case 'r':
2610 printf("%u", dirent->rec_len);
2611 break;
2612 case 'l':
2613 printf("%u", dirent->name_len & 0xFF);
2614 break;
2615 case 't':
2616 printf("%u", dirent->name_len >> 8);
2617 break;
2618 default:
2619 no_dirent:
2620 printf("%%D%c", ch);
2621 break;
2622 }
2623}
2624
Rob Landley7c94bed2006-05-03 21:58:45 +00002625static void expand_percent_expression(ext2_filsys fs, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002626 struct problem_context *ctx)
2627{
2628 if (!ctx)
2629 goto no_context;
2630
2631 switch (ch) {
2632 case '%':
2633 fputc('%', stdout);
2634 break;
2635 case 'b':
2636 printf("%u", ctx->blk);
2637 break;
2638 case 'B':
Rob Landley7a260f02006-06-19 03:20:03 +00002639 printf("%"PRIi64, ctx->blkcount);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002640 break;
2641 case 'c':
2642 printf("%u", ctx->blk2);
2643 break;
2644 case 'd':
2645 printf("%u", ctx->dir);
2646 break;
2647 case 'g':
2648 printf("%d", ctx->group);
2649 break;
2650 case 'i':
2651 printf("%u", ctx->ino);
2652 break;
2653 case 'j':
2654 printf("%u", ctx->ino2);
2655 break;
2656 case 'm':
2657 printf("%s", error_message(ctx->errcode));
2658 break;
2659 case 'N':
Rob Landley7a260f02006-06-19 03:20:03 +00002660 printf("%"PRIi64, ctx->num);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002661 break;
2662 case 'p':
2663 print_pathname(fs, ctx->ino, 0);
2664 break;
2665 case 'P':
2666 print_pathname(fs, ctx->ino2,
2667 ctx->dirent ? ctx->dirent->inode : 0);
2668 break;
2669 case 'q':
2670 print_pathname(fs, ctx->dir, 0);
2671 break;
2672 case 'Q':
2673 print_pathname(fs, ctx->dir, ctx->ino);
2674 break;
2675 case 'S':
2676 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
2677 break;
2678 case 's':
2679 printf("%s", ctx->str ? ctx->str : "NULL");
2680 break;
2681 case 'X':
Rob Landley7a260f02006-06-19 03:20:03 +00002682 printf("0x%"PRIi64, ctx->num);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002683 break;
2684 default:
2685 no_context:
2686 printf("%%%c", ch);
2687 break;
2688 }
2689}
2690
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002691
2692static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002693 struct problem_context *pctx, int first)
2694{
2695 ext2_filsys fs = ctx->fs;
2696 const char * cp;
2697 int i;
2698
2699 e2fsck_clear_progbar(ctx);
2700 for (cp = msg; *cp; cp++) {
2701 if (cp[0] == '@') {
2702 cp++;
2703 expand_at_expression(ctx, *cp, pctx, &first);
2704 } else if (cp[0] == '%' && cp[1] == 'I') {
2705 cp += 2;
2706 expand_inode_expression(*cp, pctx);
2707 } else if (cp[0] == '%' && cp[1] == 'D') {
2708 cp += 2;
2709 expand_dirent_expression(*cp, pctx);
2710 } else if ((cp[0] == '%')) {
2711 cp++;
2712 expand_percent_expression(fs, *cp, pctx);
2713 } else {
2714 for (i=0; cp[i]; i++)
2715 if ((cp[i] == '@') || cp[i] == '%')
2716 break;
2717 printf("%.*s", i, cp);
2718 cp += i-1;
2719 }
2720 first = 0;
2721 }
2722}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002723
2724
2725/*
2726 * region.c --- code which manages allocations within a region.
2727 */
2728
2729struct region_el {
2730 region_addr_t start;
2731 region_addr_t end;
2732 struct region_el *next;
2733};
2734
2735struct region_struct {
2736 region_addr_t min;
2737 region_addr_t max;
2738 struct region_el *allocated;
2739};
2740
2741static region_t region_create(region_addr_t min, region_addr_t max)
2742{
2743 region_t region;
2744
2745 region = malloc(sizeof(struct region_struct));
2746 if (!region)
2747 return NULL;
2748 memset(region, 0, sizeof(struct region_struct));
2749 region->min = min;
2750 region->max = max;
2751 return region;
2752}
2753
2754static void region_free(region_t region)
2755{
2756 struct region_el *r, *next;
2757
2758 for (r = region->allocated; r; r = next) {
2759 next = r->next;
2760 free(r);
2761 }
2762 memset(region, 0, sizeof(struct region_struct));
2763 free(region);
2764}
2765
2766static int region_allocate(region_t region, region_addr_t start, int n)
2767{
2768 struct region_el *r, *new_region, *prev, *next;
2769 region_addr_t end;
2770
2771 end = start+n;
2772 if ((start < region->min) || (end > region->max))
2773 return -1;
2774 if (n == 0)
2775 return 1;
2776
2777 /*
2778 * Search through the linked list. If we find that it
2779 * conflicts witih something that's already allocated, return
2780 * 1; if we can find an existing region which we can grow, do
2781 * so. Otherwise, stop when we find the appropriate place
2782 * insert a new region element into the linked list.
2783 */
2784 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
2785 if (((start >= r->start) && (start < r->end)) ||
2786 ((end > r->start) && (end <= r->end)) ||
2787 ((start <= r->start) && (end >= r->end)))
2788 return 1;
2789 if (end == r->start) {
2790 r->start = start;
2791 return 0;
2792 }
2793 if (start == r->end) {
2794 if ((next = r->next)) {
2795 if (end > next->start)
2796 return 1;
2797 if (end == next->start) {
2798 r->end = next->end;
2799 r->next = next->next;
2800 free(next);
2801 return 0;
2802 }
2803 }
2804 r->end = end;
2805 return 0;
2806 }
2807 if (start < r->start)
2808 break;
2809 }
2810 /*
2811 * Insert a new region element structure into the linked list
2812 */
2813 new_region = malloc(sizeof(struct region_el));
2814 if (!new_region)
2815 return -1;
2816 new_region->start = start;
2817 new_region->end = start + n;
2818 new_region->next = r;
2819 if (prev)
2820 prev->next = new_region;
2821 else
2822 region->allocated = new_region;
2823 return 0;
2824}
2825
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002826/*
2827 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
2828 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002829 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
2830 * and applies the following tests to each inode:
2831 *
2832 * - The mode field of the inode must be legal.
2833 * - The size and block count fields of the inode are correct.
2834 * - A data block must not be used by another inode
2835 *
2836 * Pass 1 also gathers the collects the following information:
2837 *
2838 * - A bitmap of which inodes are in use. (inode_used_map)
2839 * - A bitmap of which inodes are directories. (inode_dir_map)
2840 * - A bitmap of which inodes are regular files. (inode_reg_map)
2841 * - A bitmap of which inodes have bad fields. (inode_bad_map)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002842 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
2843 * - A bitmap of which blocks are in use. (block_found_map)
2844 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
2845 * - The data blocks of the directory inodes. (dir_map)
2846 *
2847 * Pass 1 is designed to stash away enough information so that the
2848 * other passes should not need to read in the inode information
2849 * during the normal course of a filesystem check. (Althogh if an
2850 * inconsistency is detected, other passes may need to read in an
2851 * inode to fix it.)
2852 *
2853 * Note that pass 1B will be invoked if there are any duplicate blocks
2854 * found.
2855 */
2856
2857
2858static int process_block(ext2_filsys fs, blk_t *blocknr,
2859 e2_blkcnt_t blockcnt, blk_t ref_blk,
2860 int ref_offset, void *priv_data);
2861static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
2862 e2_blkcnt_t blockcnt, blk_t ref_blk,
2863 int ref_offset, void *priv_data);
2864static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
2865 char *block_buf);
2866static void mark_table_blocks(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002867static void alloc_imagic_map(e2fsck_t ctx);
2868static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
2869static void handle_fs_bad_blocks(e2fsck_t ctx);
2870static void process_inodes(e2fsck_t ctx, char *block_buf);
Rob Landley7c94bed2006-05-03 21:58:45 +00002871static int process_inode_cmp(const void *a, const void *b);
Rob Landley206f7572006-05-19 22:42:23 +00002872static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002873 dgrp_t group, void * priv_data);
2874static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
2875 char *block_buf, int adjust_sign);
2876/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
2877
2878static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
2879 struct ext2_inode * inode, int bufsize,
2880 const char *proc);
2881
2882struct process_block_struct_1 {
2883 ext2_ino_t ino;
2884 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
2885 fragmented:1, compressed:1, bbcheck:1;
2886 blk_t num_blocks;
2887 blk_t max_blocks;
2888 e2_blkcnt_t last_block;
2889 int num_illegal_blocks;
2890 blk_t previous_block;
2891 struct ext2_inode *inode;
2892 struct problem_context *pctx;
2893 ext2fs_block_bitmap fs_meta_blocks;
2894 e2fsck_t ctx;
2895};
2896
2897struct process_inode_block {
2898 ext2_ino_t ino;
2899 struct ext2_inode inode;
2900};
2901
2902struct scan_callback_struct {
2903 e2fsck_t ctx;
2904 char *block_buf;
2905};
2906
2907/*
2908 * For the inodes to process list.
2909 */
2910static struct process_inode_block *inodes_to_process;
2911static int process_inode_count;
2912
2913static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
2914 EXT2_MIN_BLOCK_LOG_SIZE + 1];
2915
2916/*
2917 * Free all memory allocated by pass1 in preparation for restarting
2918 * things.
2919 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002920static void unwind_pass1(void)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002921{
2922 ext2fs_free_mem(&inodes_to_process);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002923}
2924
2925/*
2926 * Check to make sure a device inode is real. Returns 1 if the device
2927 * checks out, 0 if not.
2928 *
2929 * Note: this routine is now also used to check FIFO's and Sockets,
2930 * since they have the same requirement; the i_block fields should be
2931 * zero.
2932 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002933static int
2934e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002935{
2936 int i;
2937
2938 /*
2939 * If i_blocks is non-zero, or the index flag is set, then
2940 * this is a bogus device/fifo/socket
2941 */
2942 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
2943 (inode->i_flags & EXT2_INDEX_FL))
2944 return 0;
2945
2946 /*
2947 * We should be able to do the test below all the time, but
2948 * because the kernel doesn't forcibly clear the device
2949 * inode's additional i_block fields, there are some rare
2950 * occasions when a legitimate device inode will have non-zero
2951 * additional i_block fields. So for now, we only complain
2952 * when the immutable flag is set, which should never happen
2953 * for devices. (And that's when the problem is caused, since
2954 * you can't set or clear immutable flags for devices.) Once
2955 * the kernel has been fixed we can change this...
2956 */
2957 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
2958 for (i=4; i < EXT2_N_BLOCKS; i++)
2959 if (inode->i_block[i])
2960 return 0;
2961 }
2962 return 1;
2963}
2964
2965/*
2966 * Check to make sure a symlink inode is real. Returns 1 if the symlink
2967 * checks out, 0 if not.
2968 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002969static int
2970e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002971{
2972 unsigned int len;
2973 int i;
2974 blk_t blocks;
2975
2976 if ((inode->i_size_high || inode->i_size == 0) ||
2977 (inode->i_flags & EXT2_INDEX_FL))
2978 return 0;
2979
2980 blocks = ext2fs_inode_data_blocks(fs, inode);
2981 if (blocks) {
2982 if ((inode->i_size >= fs->blocksize) ||
2983 (blocks != fs->blocksize >> 9) ||
2984 (inode->i_block[0] < fs->super->s_first_data_block) ||
2985 (inode->i_block[0] >= fs->super->s_blocks_count))
2986 return 0;
2987
2988 for (i = 1; i < EXT2_N_BLOCKS; i++)
2989 if (inode->i_block[i])
2990 return 0;
2991
2992 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
2993 return 0;
2994
2995 len = strnlen(buf, fs->blocksize);
2996 if (len == fs->blocksize)
2997 return 0;
2998 } else {
2999 if (inode->i_size >= sizeof(inode->i_block))
3000 return 0;
3001
3002 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
3003 if (len == sizeof(inode->i_block))
3004 return 0;
3005 }
3006 if (len != inode->i_size)
3007 return 0;
3008 return 1;
3009}
3010
3011/*
3012 * If the immutable (or append-only) flag is set on the inode, offer
3013 * to clear it.
3014 */
3015#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
3016static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
3017{
3018 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
3019 return;
3020
3021 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
3022 return;
3023
3024 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
3025 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3026}
3027
3028/*
3029 * If device, fifo or socket, check size is zero -- if not offer to
3030 * clear it
3031 */
3032static void check_size(e2fsck_t ctx, struct problem_context *pctx)
3033{
3034 struct ext2_inode *inode = pctx->inode;
3035
3036 if ((inode->i_size == 0) && (inode->i_size_high == 0))
3037 return;
3038
3039 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
3040 return;
3041
3042 inode->i_size = 0;
3043 inode->i_size_high = 0;
3044 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3045}
3046
3047static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
3048{
3049 struct ext2_super_block *sb = ctx->fs->super;
3050 struct ext2_inode_large *inode;
3051 struct ext2_ext_attr_entry *entry;
3052 char *start, *end;
3053 int storage_size, remain, offs;
3054 int problem = 0;
3055
3056 inode = (struct ext2_inode_large *) pctx->inode;
3057 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
3058 inode->i_extra_isize;
3059 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3060 inode->i_extra_isize + sizeof(__u32);
3061 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
3062 entry = (struct ext2_ext_attr_entry *) start;
3063
3064 /* scan all entry's headers first */
3065
3066 /* take finish entry 0UL into account */
3067 remain = storage_size - sizeof(__u32);
3068 offs = end - start;
3069
3070 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
3071
3072 /* header eats this space */
3073 remain -= sizeof(struct ext2_ext_attr_entry);
3074
3075 /* is attribute name valid? */
3076 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
3077 pctx->num = entry->e_name_len;
3078 problem = PR_1_ATTR_NAME_LEN;
3079 goto fix;
3080 }
3081
3082 /* attribute len eats this space */
3083 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
3084
3085 /* check value size */
3086 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
3087 pctx->num = entry->e_value_size;
3088 problem = PR_1_ATTR_VALUE_SIZE;
3089 goto fix;
3090 }
3091
3092 /* check value placement */
3093 if (entry->e_value_offs +
3094 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
3095 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
3096 pctx->num = entry->e_value_offs;
3097 problem = PR_1_ATTR_VALUE_OFFSET;
3098 goto fix;
3099 }
3100
3101 /* e_value_block must be 0 in inode's ea */
3102 if (entry->e_value_block != 0) {
3103 pctx->num = entry->e_value_block;
3104 problem = PR_1_ATTR_VALUE_BLOCK;
3105 goto fix;
3106 }
3107
3108 /* e_hash must be 0 in inode's ea */
3109 if (entry->e_hash != 0) {
3110 pctx->num = entry->e_hash;
3111 problem = PR_1_ATTR_HASH;
3112 goto fix;
3113 }
3114
3115 remain -= entry->e_value_size;
3116 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
3117
3118 entry = EXT2_EXT_ATTR_NEXT(entry);
3119 }
3120fix:
3121 /*
3122 * it seems like a corruption. it's very unlikely we could repair
3123 * EA(s) in automatic fashion -bzzz
3124 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003125 if (problem == 0 || !fix_problem(ctx, problem, pctx))
3126 return;
3127
3128 /* simple remove all possible EA(s) */
3129 *((__u32 *)start) = 0UL;
3130 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
3131 EXT2_INODE_SIZE(sb), "pass1");
3132}
3133
3134static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
3135{
3136 struct ext2_super_block *sb = ctx->fs->super;
3137 struct ext2_inode_large *inode;
3138 __u32 *eamagic;
3139 int min, max;
3140
3141 inode = (struct ext2_inode_large *) pctx->inode;
3142 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
3143 /* this isn't large inode. so, nothing to check */
3144 return;
3145 }
3146
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003147 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
3148 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
3149 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
3150 /*
3151 * For now we will allow i_extra_isize to be 0, but really
3152 * implementations should never allow i_extra_isize to be 0
3153 */
3154 if (inode->i_extra_isize &&
3155 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
3156 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
3157 return;
3158 inode->i_extra_isize = min;
3159 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
3160 EXT2_INODE_SIZE(sb), "pass1");
3161 return;
3162 }
3163
3164 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3165 inode->i_extra_isize);
3166 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
3167 /* it seems inode has an extended attribute(s) in body */
3168 check_ea_in_inode(ctx, pctx);
3169 }
3170}
3171
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003172static void e2fsck_pass1(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003173{
3174 int i;
3175 __u64 max_sizes;
3176 ext2_filsys fs = ctx->fs;
3177 ext2_ino_t ino;
3178 struct ext2_inode *inode;
3179 ext2_inode_scan scan;
3180 char *block_buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003181 unsigned char frag, fsize;
3182 struct problem_context pctx;
3183 struct scan_callback_struct scan_struct;
3184 struct ext2_super_block *sb = ctx->fs->super;
3185 int imagic_fs;
3186 int busted_fs_time = 0;
3187 int inode_size;
3188
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003189 clear_problem_context(&pctx);
3190
3191 if (!(ctx->options & E2F_OPT_PREEN))
3192 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
3193
3194 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
3195 !(ctx->options & E2F_OPT_NO)) {
3196 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
3197 ctx->dirs_to_hash = 0;
3198 }
3199
Rob Landley3e72c592006-04-06 22:49:04 +00003200 /* Pass 1 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003201
3202#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
3203
3204 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
3205 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
3206 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
3207 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
3208 max_sizes = (max_sizes * (1UL << i)) - 1;
3209 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
3210 }
3211#undef EXT2_BPP
3212
3213 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
3214
3215 /*
3216 * Allocate bitmaps structures
3217 */
3218 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
3219 &ctx->inode_used_map);
3220 if (pctx.errcode) {
3221 pctx.num = 1;
3222 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3223 ctx->flags |= E2F_FLAG_ABORT;
3224 return;
3225 }
3226 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3227 _("directory inode map"), &ctx->inode_dir_map);
3228 if (pctx.errcode) {
3229 pctx.num = 2;
3230 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3231 ctx->flags |= E2F_FLAG_ABORT;
3232 return;
3233 }
3234 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3235 _("regular file inode map"), &ctx->inode_reg_map);
3236 if (pctx.errcode) {
3237 pctx.num = 6;
3238 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3239 ctx->flags |= E2F_FLAG_ABORT;
3240 return;
3241 }
3242 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
3243 &ctx->block_found_map);
3244 if (pctx.errcode) {
3245 pctx.num = 1;
3246 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3247 ctx->flags |= E2F_FLAG_ABORT;
3248 return;
3249 }
3250 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
3251 &ctx->inode_link_info);
3252 if (pctx.errcode) {
3253 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
3254 ctx->flags |= E2F_FLAG_ABORT;
3255 return;
3256 }
3257 inode_size = EXT2_INODE_SIZE(fs->super);
3258 inode = (struct ext2_inode *)
3259 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
3260
3261 inodes_to_process = (struct process_inode_block *)
3262 e2fsck_allocate_memory(ctx,
3263 (ctx->process_inode_size *
3264 sizeof(struct process_inode_block)),
3265 "array of inodes to process");
3266 process_inode_count = 0;
3267
3268 pctx.errcode = ext2fs_init_dblist(fs, 0);
3269 if (pctx.errcode) {
3270 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
3271 ctx->flags |= E2F_FLAG_ABORT;
3272 return;
3273 }
3274
3275 /*
3276 * If the last orphan field is set, clear it, since the pass1
3277 * processing will automatically find and clear the orphans.
3278 * In the future, we may want to try using the last_orphan
3279 * linked list ourselves, but for now, we clear it so that the
3280 * ext3 mount code won't get confused.
3281 */
3282 if (!(ctx->options & E2F_OPT_READONLY)) {
3283 if (fs->super->s_last_orphan) {
3284 fs->super->s_last_orphan = 0;
3285 ext2fs_mark_super_dirty(fs);
3286 }
3287 }
3288
3289 mark_table_blocks(ctx);
3290 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
3291 "block interate buffer");
3292 e2fsck_use_inode_shortcuts(ctx, 1);
3293 ehandler_operation(_("doing inode scan"));
3294 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
3295 &scan);
3296 if (pctx.errcode) {
3297 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3298 ctx->flags |= E2F_FLAG_ABORT;
3299 return;
3300 }
3301 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
3302 ctx->stashed_inode = inode;
3303 scan_struct.ctx = ctx;
3304 scan_struct.block_buf = block_buf;
3305 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
3306 if (ctx->progress)
3307 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
3308 return;
Mike Frysinger874af852006-03-08 07:03:27 +00003309 if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
3310 (fs->super->s_mtime < fs->super->s_inodes_count))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003311 busted_fs_time = 1;
3312
3313 while (1) {
3314 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
3315 inode, inode_size);
3316 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3317 return;
3318 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003319 continue;
3320 }
3321 if (pctx.errcode) {
3322 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3323 ctx->flags |= E2F_FLAG_ABORT;
3324 return;
3325 }
3326 if (!ino)
3327 break;
3328 pctx.ino = ino;
3329 pctx.inode = inode;
3330 ctx->stashed_ino = ino;
3331 if (inode->i_links_count) {
3332 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
3333 ino, inode->i_links_count);
3334 if (pctx.errcode) {
3335 pctx.num = inode->i_links_count;
3336 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
3337 ctx->flags |= E2F_FLAG_ABORT;
3338 return;
3339 }
3340 }
3341 if (ino == EXT2_BAD_INO) {
3342 struct process_block_struct_1 pb;
3343
3344 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
3345 &pb.fs_meta_blocks);
3346 if (pctx.errcode) {
3347 pctx.num = 4;
3348 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3349 ctx->flags |= E2F_FLAG_ABORT;
3350 return;
3351 }
3352 pb.ino = EXT2_BAD_INO;
3353 pb.num_blocks = pb.last_block = 0;
3354 pb.num_illegal_blocks = 0;
3355 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
3356 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
3357 pb.inode = inode;
3358 pb.pctx = &pctx;
3359 pb.ctx = ctx;
3360 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
3361 block_buf, process_bad_block, &pb);
3362 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
3363 if (pctx.errcode) {
3364 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
3365 ctx->flags |= E2F_FLAG_ABORT;
3366 return;
3367 }
3368 if (pb.bbcheck)
3369 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
3370 ctx->flags |= E2F_FLAG_ABORT;
3371 return;
3372 }
3373 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3374 clear_problem_context(&pctx);
3375 continue;
3376 } else if (ino == EXT2_ROOT_INO) {
3377 /*
3378 * Make sure the root inode is a directory; if
3379 * not, offer to clear it. It will be
3380 * regnerated in pass #3.
3381 */
3382 if (!LINUX_S_ISDIR(inode->i_mode)) {
3383 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
3384 inode->i_dtime = time(0);
3385 inode->i_links_count = 0;
3386 ext2fs_icount_store(ctx->inode_link_info,
3387 ino, 0);
3388 e2fsck_write_inode(ctx, ino, inode,
3389 "pass1");
3390 }
3391
3392 }
3393 /*
3394 * If dtime is set, offer to clear it. mke2fs
3395 * version 0.2b created filesystems with the
3396 * dtime field set for the root and lost+found
3397 * directories. We won't worry about
3398 * /lost+found, since that can be regenerated
3399 * easily. But we will fix the root directory
3400 * as a special case.
3401 */
3402 if (inode->i_dtime && inode->i_links_count) {
3403 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
3404 inode->i_dtime = 0;
3405 e2fsck_write_inode(ctx, ino, inode,
3406 "pass1");
3407 }
3408 }
3409 } else if (ino == EXT2_JOURNAL_INO) {
3410 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3411 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
3412 if (!LINUX_S_ISREG(inode->i_mode) &&
3413 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
3414 &pctx)) {
3415 inode->i_mode = LINUX_S_IFREG;
3416 e2fsck_write_inode(ctx, ino, inode,
3417 "pass1");
3418 }
3419 check_blocks(ctx, &pctx, block_buf);
3420 continue;
3421 }
3422 if ((inode->i_links_count || inode->i_blocks ||
3423 inode->i_blocks || inode->i_block[0]) &&
3424 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
3425 &pctx)) {
3426 memset(inode, 0, inode_size);
3427 ext2fs_icount_store(ctx->inode_link_info,
3428 ino, 0);
3429 e2fsck_write_inode_full(ctx, ino, inode,
3430 inode_size, "pass1");
3431 }
3432 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
3433 int problem = 0;
3434
3435 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3436 if (ino == EXT2_BOOT_LOADER_INO) {
3437 if (LINUX_S_ISDIR(inode->i_mode))
3438 problem = PR_1_RESERVED_BAD_MODE;
3439 } else if (ino == EXT2_RESIZE_INO) {
3440 if (inode->i_mode &&
3441 !LINUX_S_ISREG(inode->i_mode))
3442 problem = PR_1_RESERVED_BAD_MODE;
3443 } else {
3444 if (inode->i_mode != 0)
3445 problem = PR_1_RESERVED_BAD_MODE;
3446 }
3447 if (problem) {
3448 if (fix_problem(ctx, problem, &pctx)) {
3449 inode->i_mode = 0;
3450 e2fsck_write_inode(ctx, ino, inode,
3451 "pass1");
3452 }
3453 }
3454 check_blocks(ctx, &pctx, block_buf);
3455 continue;
3456 }
3457 /*
3458 * Check for inodes who might have been part of the
3459 * orphaned list linked list. They should have gotten
3460 * dealt with by now, unless the list had somehow been
3461 * corrupted.
3462 *
3463 * FIXME: In the future, inodes which are still in use
3464 * (and which are therefore) pending truncation should
3465 * be handled specially. Right now we just clear the
3466 * dtime field, and the normal e2fsck handling of
3467 * inodes where i_size and the inode blocks are
3468 * inconsistent is to fix i_size, instead of releasing
3469 * the extra blocks. This won't catch the inodes that
3470 * was at the end of the orphan list, but it's better
3471 * than nothing. The right answer is that there
3472 * shouldn't be any bugs in the orphan list handling. :-)
3473 */
3474 if (inode->i_dtime && !busted_fs_time &&
3475 inode->i_dtime < ctx->fs->super->s_inodes_count) {
3476 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
3477 inode->i_dtime = inode->i_links_count ?
3478 0 : time(0);
3479 e2fsck_write_inode(ctx, ino, inode,
3480 "pass1");
3481 }
3482 }
3483
3484 /*
3485 * This code assumes that deleted inodes have
3486 * i_links_count set to 0.
3487 */
3488 if (!inode->i_links_count) {
3489 if (!inode->i_dtime && inode->i_mode) {
3490 if (fix_problem(ctx,
3491 PR_1_ZERO_DTIME, &pctx)) {
3492 inode->i_dtime = time(0);
3493 e2fsck_write_inode(ctx, ino, inode,
3494 "pass1");
3495 }
3496 }
3497 continue;
3498 }
3499 /*
3500 * n.b. 0.3c ext2fs code didn't clear i_links_count for
3501 * deleted files. Oops.
3502 *
3503 * Since all new ext2 implementations get this right,
3504 * we now assume that the case of non-zero
3505 * i_links_count and non-zero dtime means that we
3506 * should keep the file, not delete it.
3507 *
3508 */
3509 if (inode->i_dtime) {
3510 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
3511 inode->i_dtime = 0;
3512 e2fsck_write_inode(ctx, ino, inode, "pass1");
3513 }
3514 }
3515
3516 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3517 switch (fs->super->s_creator_os) {
3518 case EXT2_OS_LINUX:
3519 frag = inode->osd2.linux2.l_i_frag;
3520 fsize = inode->osd2.linux2.l_i_fsize;
3521 break;
3522 case EXT2_OS_HURD:
3523 frag = inode->osd2.hurd2.h_i_frag;
3524 fsize = inode->osd2.hurd2.h_i_fsize;
3525 break;
3526 case EXT2_OS_MASIX:
3527 frag = inode->osd2.masix2.m_i_frag;
3528 fsize = inode->osd2.masix2.m_i_fsize;
3529 break;
3530 default:
3531 frag = fsize = 0;
3532 }
3533
3534 if (inode->i_faddr || frag || fsize ||
3535 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
3536 mark_inode_bad(ctx, ino);
3537 if (inode->i_flags & EXT2_IMAGIC_FL) {
3538 if (imagic_fs) {
3539 if (!ctx->inode_imagic_map)
3540 alloc_imagic_map(ctx);
3541 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
3542 ino);
3543 } else {
3544 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
3545 inode->i_flags &= ~EXT2_IMAGIC_FL;
3546 e2fsck_write_inode(ctx, ino,
3547 inode, "pass1");
3548 }
3549 }
3550 }
3551
3552 check_inode_extra_space(ctx, &pctx);
3553
3554 if (LINUX_S_ISDIR(inode->i_mode)) {
3555 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
3556 e2fsck_add_dir_info(ctx, ino, 0);
3557 ctx->fs_directory_count++;
3558 } else if (LINUX_S_ISREG (inode->i_mode)) {
3559 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
3560 ctx->fs_regular_count++;
3561 } else if (LINUX_S_ISCHR (inode->i_mode) &&
3562 e2fsck_pass1_check_device_inode(fs, inode)) {
3563 check_immutable(ctx, &pctx);
3564 check_size(ctx, &pctx);
3565 ctx->fs_chardev_count++;
3566 } else if (LINUX_S_ISBLK (inode->i_mode) &&
3567 e2fsck_pass1_check_device_inode(fs, inode)) {
3568 check_immutable(ctx, &pctx);
3569 check_size(ctx, &pctx);
3570 ctx->fs_blockdev_count++;
3571 } else if (LINUX_S_ISLNK (inode->i_mode) &&
3572 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
3573 check_immutable(ctx, &pctx);
3574 ctx->fs_symlinks_count++;
3575 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
3576 ctx->fs_fast_symlinks_count++;
3577 check_blocks(ctx, &pctx, block_buf);
3578 continue;
3579 }
3580 }
3581 else if (LINUX_S_ISFIFO (inode->i_mode) &&
3582 e2fsck_pass1_check_device_inode(fs, inode)) {
3583 check_immutable(ctx, &pctx);
3584 check_size(ctx, &pctx);
3585 ctx->fs_fifo_count++;
3586 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
3587 e2fsck_pass1_check_device_inode(fs, inode)) {
3588 check_immutable(ctx, &pctx);
3589 check_size(ctx, &pctx);
3590 ctx->fs_sockets_count++;
3591 } else
3592 mark_inode_bad(ctx, ino);
3593 if (inode->i_block[EXT2_IND_BLOCK])
3594 ctx->fs_ind_count++;
3595 if (inode->i_block[EXT2_DIND_BLOCK])
3596 ctx->fs_dind_count++;
3597 if (inode->i_block[EXT2_TIND_BLOCK])
3598 ctx->fs_tind_count++;
3599 if (inode->i_block[EXT2_IND_BLOCK] ||
3600 inode->i_block[EXT2_DIND_BLOCK] ||
3601 inode->i_block[EXT2_TIND_BLOCK] ||
3602 inode->i_file_acl) {
3603 inodes_to_process[process_inode_count].ino = ino;
3604 inodes_to_process[process_inode_count].inode = *inode;
3605 process_inode_count++;
3606 } else
3607 check_blocks(ctx, &pctx, block_buf);
3608
3609 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3610 return;
3611
3612 if (process_inode_count >= ctx->process_inode_size) {
3613 process_inodes(ctx, block_buf);
3614
3615 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3616 return;
3617 }
3618 }
3619 process_inodes(ctx, block_buf);
3620 ext2fs_close_inode_scan(scan);
3621 ehandler_operation(0);
3622
3623 /*
3624 * If any extended attribute blocks' reference counts need to
3625 * be adjusted, either up (ctx->refcount_extra), or down
3626 * (ctx->refcount), then fix them.
3627 */
3628 if (ctx->refcount) {
3629 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
3630 ea_refcount_free(ctx->refcount);
3631 ctx->refcount = 0;
3632 }
3633 if (ctx->refcount_extra) {
3634 adjust_extattr_refcount(ctx, ctx->refcount_extra,
3635 block_buf, +1);
3636 ea_refcount_free(ctx->refcount_extra);
3637 ctx->refcount_extra = 0;
3638 }
3639
3640 if (ctx->invalid_bitmaps)
3641 handle_fs_bad_blocks(ctx);
3642
3643 /* We don't need the block_ea_map any more */
Rob Landleye7c43b62006-03-01 16:39:45 +00003644 ext2fs_free_block_bitmap(ctx->block_ea_map);
3645 ctx->block_ea_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003646
3647 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
3648 ext2fs_block_bitmap save_bmap;
3649
3650 save_bmap = fs->block_map;
3651 fs->block_map = ctx->block_found_map;
3652 clear_problem_context(&pctx);
3653 pctx.errcode = ext2fs_create_resize_inode(fs);
3654 if (pctx.errcode) {
3655 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
3656 /* Should never get here */
3657 ctx->flags |= E2F_FLAG_ABORT;
3658 return;
3659 }
Mike Frysinger874af852006-03-08 07:03:27 +00003660 e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
3661 "recreate inode");
3662 inode->i_mtime = time(0);
3663 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
3664 "recreate inode");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003665 fs->block_map = save_bmap;
3666 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
3667 }
3668
3669 if (ctx->flags & E2F_FLAG_RESTART) {
3670 /*
3671 * Only the master copy of the superblock and block
3672 * group descriptors are going to be written during a
3673 * restart, so set the superblock to be used to be the
3674 * master superblock.
3675 */
3676 ctx->use_superblock = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003677 unwind_pass1();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003678 goto endit;
3679 }
3680
3681 if (ctx->block_dup_map) {
3682 if (ctx->options & E2F_OPT_PREEN) {
3683 clear_problem_context(&pctx);
3684 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
3685 }
3686 e2fsck_pass1_dupblocks(ctx, block_buf);
3687 }
3688 ext2fs_free_mem(&inodes_to_process);
3689endit:
3690 e2fsck_use_inode_shortcuts(ctx, 0);
3691
3692 ext2fs_free_mem(&block_buf);
3693 ext2fs_free_mem(&inode);
3694
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003695}
3696
3697/*
3698 * When the inode_scan routines call this callback at the end of the
3699 * glock group, call process_inodes.
3700 */
3701static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003702 dgrp_t group, void * priv_data)
3703{
3704 struct scan_callback_struct *scan_struct;
3705 e2fsck_t ctx;
3706
3707 scan_struct = (struct scan_callback_struct *) priv_data;
3708 ctx = scan_struct->ctx;
3709
3710 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
3711
3712 if (ctx->progress)
3713 if ((ctx->progress)(ctx, 1, group+1,
3714 ctx->fs->group_desc_count))
3715 return EXT2_ET_CANCEL_REQUESTED;
3716
3717 return 0;
3718}
3719
3720/*
3721 * Process the inodes in the "inodes to process" list.
3722 */
3723static void process_inodes(e2fsck_t ctx, char *block_buf)
3724{
3725 int i;
3726 struct ext2_inode *old_stashed_inode;
3727 ext2_ino_t old_stashed_ino;
3728 const char *old_operation;
3729 char buf[80];
3730 struct problem_context pctx;
3731
Rob Landley3e72c592006-04-06 22:49:04 +00003732 /* begin process_inodes */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003733 if (process_inode_count == 0)
3734 return;
3735 old_operation = ehandler_operation(0);
3736 old_stashed_inode = ctx->stashed_inode;
3737 old_stashed_ino = ctx->stashed_ino;
3738 qsort(inodes_to_process, process_inode_count,
3739 sizeof(struct process_inode_block), process_inode_cmp);
3740 clear_problem_context(&pctx);
3741 for (i=0; i < process_inode_count; i++) {
3742 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
3743 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003744 sprintf(buf, _("reading indirect blocks of inode %u"),
3745 pctx.ino);
3746 ehandler_operation(buf);
3747 check_blocks(ctx, &pctx, block_buf);
3748 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3749 break;
3750 }
3751 ctx->stashed_inode = old_stashed_inode;
3752 ctx->stashed_ino = old_stashed_ino;
3753 process_inode_count = 0;
Rob Landley3e72c592006-04-06 22:49:04 +00003754 /* end process inodes */
3755
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003756 ehandler_operation(old_operation);
3757}
3758
Rob Landley7c94bed2006-05-03 21:58:45 +00003759static int process_inode_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003760{
3761 const struct process_inode_block *ib_a =
3762 (const struct process_inode_block *) a;
3763 const struct process_inode_block *ib_b =
3764 (const struct process_inode_block *) b;
3765 int ret;
3766
3767 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
3768 ib_b->inode.i_block[EXT2_IND_BLOCK]);
3769 if (ret == 0)
3770 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
3771 return ret;
3772}
3773
3774/*
3775 * Mark an inode as being bad in some what
3776 */
3777static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
3778{
3779 struct problem_context pctx;
3780
3781 if (!ctx->inode_bad_map) {
3782 clear_problem_context(&pctx);
3783
3784 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3785 _("bad inode map"), &ctx->inode_bad_map);
3786 if (pctx.errcode) {
3787 pctx.num = 3;
3788 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3789 /* Should never get here */
3790 ctx->flags |= E2F_FLAG_ABORT;
3791 return;
3792 }
3793 }
3794 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
3795}
3796
3797
3798/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003799 * This procedure will allocate the inode imagic table
3800 */
3801static void alloc_imagic_map(e2fsck_t ctx)
3802{
3803 struct problem_context pctx;
3804
3805 clear_problem_context(&pctx);
3806 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3807 _("imagic inode map"),
3808 &ctx->inode_imagic_map);
3809 if (pctx.errcode) {
3810 pctx.num = 5;
3811 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3812 /* Should never get here */
3813 ctx->flags |= E2F_FLAG_ABORT;
3814 return;
3815 }
3816}
3817
3818/*
3819 * Marks a block as in use, setting the dup_map if it's been set
3820 * already. Called by process_block and process_bad_block.
3821 *
3822 * WARNING: Assumes checks have already been done to make sure block
3823 * is valid. This is true in both process_block and process_bad_block.
3824 */
Rob Landley7c94bed2006-05-03 21:58:45 +00003825static void mark_block_used(e2fsck_t ctx, blk_t block)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003826{
3827 struct problem_context pctx;
3828
3829 clear_problem_context(&pctx);
3830
3831 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
3832 if (!ctx->block_dup_map) {
3833 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
3834 _("multiply claimed block map"),
3835 &ctx->block_dup_map);
3836 if (pctx.errcode) {
3837 pctx.num = 3;
3838 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
3839 &pctx);
3840 /* Should never get here */
3841 ctx->flags |= E2F_FLAG_ABORT;
3842 return;
3843 }
3844 }
3845 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
3846 } else {
3847 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
3848 }
3849}
3850
3851/*
3852 * Adjust the extended attribute block's reference counts at the end
3853 * of pass 1, either by subtracting out references for EA blocks that
3854 * are still referenced in ctx->refcount, or by adding references for
3855 * EA blocks that had extra references as accounted for in
3856 * ctx->refcount_extra.
3857 */
3858static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3859 char *block_buf, int adjust_sign)
3860{
3861 struct ext2_ext_attr_header *header;
3862 struct problem_context pctx;
3863 ext2_filsys fs = ctx->fs;
3864 blk_t blk;
3865 __u32 should_be;
3866 int count;
3867
3868 clear_problem_context(&pctx);
3869
3870 ea_refcount_intr_begin(refcount);
3871 while (1) {
3872 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
3873 break;
3874 pctx.blk = blk;
3875 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
3876 if (pctx.errcode) {
3877 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
3878 return;
3879 }
3880 header = (struct ext2_ext_attr_header *) block_buf;
3881 pctx.blkcount = header->h_refcount;
3882 should_be = header->h_refcount + adjust_sign * count;
3883 pctx.num = should_be;
3884 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
3885 header->h_refcount = should_be;
3886 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
3887 block_buf);
3888 if (pctx.errcode) {
3889 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
3890 continue;
3891 }
3892 }
3893 }
3894}
3895
3896/*
3897 * Handle processing the extended attribute blocks
3898 */
3899static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
3900 char *block_buf)
3901{
3902 ext2_filsys fs = ctx->fs;
3903 ext2_ino_t ino = pctx->ino;
3904 struct ext2_inode *inode = pctx->inode;
3905 blk_t blk;
3906 char * end;
3907 struct ext2_ext_attr_header *header;
3908 struct ext2_ext_attr_entry *entry;
3909 int count;
3910 region_t region;
3911
3912 blk = inode->i_file_acl;
3913 if (blk == 0)
3914 return 0;
3915
3916 /*
3917 * If the Extended attribute flag isn't set, then a non-zero
3918 * file acl means that the inode is corrupted.
3919 *
3920 * Or if the extended attribute block is an invalid block,
3921 * then the inode is also corrupted.
3922 */
3923 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
3924 (blk < fs->super->s_first_data_block) ||
3925 (blk >= fs->super->s_blocks_count)) {
3926 mark_inode_bad(ctx, ino);
3927 return 0;
3928 }
3929
3930 /* If ea bitmap hasn't been allocated, create it */
3931 if (!ctx->block_ea_map) {
3932 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
3933 _("ext attr block map"),
3934 &ctx->block_ea_map);
3935 if (pctx->errcode) {
3936 pctx->num = 2;
3937 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
3938 ctx->flags |= E2F_FLAG_ABORT;
3939 return 0;
3940 }
3941 }
3942
3943 /* Create the EA refcount structure if necessary */
3944 if (!ctx->refcount) {
3945 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
3946 if (pctx->errcode) {
3947 pctx->num = 1;
3948 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3949 ctx->flags |= E2F_FLAG_ABORT;
3950 return 0;
3951 }
3952 }
3953
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003954 /* Have we seen this EA block before? */
3955 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
3956 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
3957 return 1;
3958 /* Ooops, this EA was referenced more than it stated */
3959 if (!ctx->refcount_extra) {
3960 pctx->errcode = ea_refcount_create(0,
3961 &ctx->refcount_extra);
3962 if (pctx->errcode) {
3963 pctx->num = 2;
3964 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3965 ctx->flags |= E2F_FLAG_ABORT;
3966 return 0;
3967 }
3968 }
3969 ea_refcount_increment(ctx->refcount_extra, blk, 0);
3970 return 1;
3971 }
3972
3973 /*
3974 * OK, we haven't seen this EA block yet. So we need to
3975 * validate it
3976 */
3977 pctx->blk = blk;
3978 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
3979 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
3980 goto clear_extattr;
3981 header = (struct ext2_ext_attr_header *) block_buf;
3982 pctx->blk = inode->i_file_acl;
3983 if (((ctx->ext_attr_ver == 1) &&
3984 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
3985 ((ctx->ext_attr_ver == 2) &&
3986 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
3987 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
3988 goto clear_extattr;
3989 }
3990
3991 if (header->h_blocks != 1) {
3992 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
3993 goto clear_extattr;
3994 }
3995
3996 region = region_create(0, fs->blocksize);
3997 if (!region) {
3998 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
3999 ctx->flags |= E2F_FLAG_ABORT;
4000 return 0;
4001 }
4002 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
4003 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4004 goto clear_extattr;
4005 }
4006
4007 entry = (struct ext2_ext_attr_entry *)(header+1);
4008 end = block_buf + fs->blocksize;
4009 while ((char *)entry < end && *(__u32 *)entry) {
4010 if (region_allocate(region, (char *)entry - (char *)header,
4011 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
4012 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4013 goto clear_extattr;
4014 }
4015 if ((ctx->ext_attr_ver == 1 &&
4016 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
4017 (ctx->ext_attr_ver == 2 &&
4018 entry->e_name_index == 0)) {
4019 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
4020 goto clear_extattr;
4021 }
4022 if (entry->e_value_block != 0) {
4023 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
4024 goto clear_extattr;
4025 }
4026 if (entry->e_value_size &&
4027 region_allocate(region, entry->e_value_offs,
4028 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
4029 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4030 goto clear_extattr;
4031 }
4032 entry = EXT2_EXT_ATTR_NEXT(entry);
4033 }
4034 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
4035 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4036 goto clear_extattr;
4037 }
4038 region_free(region);
4039
4040 count = header->h_refcount - 1;
4041 if (count)
4042 ea_refcount_store(ctx->refcount, blk, count);
4043 mark_block_used(ctx, blk);
4044 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
4045
4046 return 1;
4047
4048clear_extattr:
4049 inode->i_file_acl = 0;
4050 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
4051 return 0;
4052}
4053
4054/* Returns 1 if bad htree, 0 if OK */
4055static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004056 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004057 struct ext2_inode *inode,
4058 char *block_buf)
4059{
4060 struct ext2_dx_root_info *root;
4061 ext2_filsys fs = ctx->fs;
4062 errcode_t retval;
4063 blk_t blk;
4064
4065 if ((!LINUX_S_ISDIR(inode->i_mode) &&
4066 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
4067 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4068 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
4069 return 1;
4070
4071 blk = inode->i_block[0];
4072 if (((blk == 0) ||
4073 (blk < fs->super->s_first_data_block) ||
4074 (blk >= fs->super->s_blocks_count)) &&
4075 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4076 return 1;
4077
4078 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
4079 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4080 return 1;
4081
4082 /* XXX should check that beginning matches a directory */
4083 root = (struct ext2_dx_root_info *) (block_buf + 24);
4084
4085 if ((root->reserved_zero || root->info_length < 8) &&
4086 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4087 return 1;
4088
4089 pctx->num = root->hash_version;
4090 if ((root->hash_version != EXT2_HASH_LEGACY) &&
4091 (root->hash_version != EXT2_HASH_HALF_MD4) &&
4092 (root->hash_version != EXT2_HASH_TEA) &&
4093 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
4094 return 1;
4095
4096 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
4097 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
4098 return 1;
4099
4100 pctx->num = root->indirect_levels;
4101 if ((root->indirect_levels > 1) &&
4102 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
4103 return 1;
4104
4105 return 0;
4106}
4107
4108/*
4109 * This subroutine is called on each inode to account for all of the
4110 * blocks used by that inode.
4111 */
4112static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
4113 char *block_buf)
4114{
4115 ext2_filsys fs = ctx->fs;
4116 struct process_block_struct_1 pb;
4117 ext2_ino_t ino = pctx->ino;
4118 struct ext2_inode *inode = pctx->inode;
4119 int bad_size = 0;
4120 int dirty_inode = 0;
4121 __u64 size;
4122
4123 pb.ino = ino;
4124 pb.num_blocks = 0;
4125 pb.last_block = -1;
4126 pb.num_illegal_blocks = 0;
4127 pb.suppress = 0; pb.clear = 0;
4128 pb.fragmented = 0;
4129 pb.compressed = 0;
4130 pb.previous_block = 0;
4131 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
4132 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
4133 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
4134 pb.inode = inode;
4135 pb.pctx = pctx;
4136 pb.ctx = ctx;
4137 pctx->ino = ino;
4138 pctx->errcode = 0;
4139
4140 if (inode->i_flags & EXT2_COMPRBLK_FL) {
4141 if (fs->super->s_feature_incompat &
4142 EXT2_FEATURE_INCOMPAT_COMPRESSION)
4143 pb.compressed = 1;
4144 else {
4145 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
4146 inode->i_flags &= ~EXT2_COMPRBLK_FL;
4147 dirty_inode++;
4148 }
4149 }
4150 }
4151
4152 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
4153 pb.num_blocks++;
4154
4155 if (ext2fs_inode_has_valid_blocks(inode))
4156 pctx->errcode = ext2fs_block_iterate2(fs, ino,
4157 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
4158 block_buf, process_block, &pb);
4159 end_problem_latch(ctx, PR_LATCH_BLOCK);
4160 end_problem_latch(ctx, PR_LATCH_TOOBIG);
4161 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4162 goto out;
4163 if (pctx->errcode)
4164 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
4165
4166 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
4167 ctx->fs_fragmented++;
4168
4169 if (pb.clear) {
4170 inode->i_links_count = 0;
4171 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4172 inode->i_dtime = time(0);
4173 dirty_inode++;
4174 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4175 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4176 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4177 /*
4178 * The inode was probably partially accounted for
4179 * before processing was aborted, so we need to
4180 * restart the pass 1 scan.
4181 */
4182 ctx->flags |= E2F_FLAG_RESTART;
4183 goto out;
4184 }
4185
4186 if (inode->i_flags & EXT2_INDEX_FL) {
4187 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
4188 inode->i_flags &= ~EXT2_INDEX_FL;
4189 dirty_inode++;
4190 } else {
4191#ifdef ENABLE_HTREE
4192 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
4193#endif
4194 }
4195 }
4196 if (ctx->dirs_to_hash && pb.is_dir &&
4197 !(inode->i_flags & EXT2_INDEX_FL) &&
4198 ((inode->i_size / fs->blocksize) >= 3))
4199 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
4200
4201 if (!pb.num_blocks && pb.is_dir) {
4202 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
4203 inode->i_links_count = 0;
4204 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4205 inode->i_dtime = time(0);
4206 dirty_inode++;
4207 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4208 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4209 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4210 ctx->fs_directory_count--;
4211 goto out;
4212 }
4213 }
4214
4215 pb.num_blocks *= (fs->blocksize / 512);
Rob Landley3e72c592006-04-06 22:49:04 +00004216
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004217 if (pb.is_dir) {
4218 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
4219 if (nblock > (pb.last_block + 1))
4220 bad_size = 1;
4221 else if (nblock < (pb.last_block + 1)) {
4222 if (((pb.last_block + 1) - nblock) >
4223 fs->super->s_prealloc_dir_blocks)
4224 bad_size = 2;
4225 }
4226 } else {
4227 size = EXT2_I_SIZE(inode);
4228 if ((pb.last_block >= 0) &&
4229 (size < (__u64) pb.last_block * fs->blocksize))
4230 bad_size = 3;
4231 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
4232 bad_size = 4;
4233 }
4234 /* i_size for symlinks is checked elsewhere */
4235 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
4236 pctx->num = (pb.last_block+1) * fs->blocksize;
4237 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
4238 inode->i_size = pctx->num;
4239 if (!LINUX_S_ISDIR(inode->i_mode))
4240 inode->i_size_high = pctx->num >> 32;
4241 dirty_inode++;
4242 }
4243 pctx->num = 0;
4244 }
4245 if (LINUX_S_ISREG(inode->i_mode) &&
4246 (inode->i_size_high || inode->i_size & 0x80000000UL))
4247 ctx->large_files++;
4248 if (pb.num_blocks != inode->i_blocks) {
4249 pctx->num = pb.num_blocks;
4250 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
4251 inode->i_blocks = pb.num_blocks;
4252 dirty_inode++;
4253 }
4254 pctx->num = 0;
4255 }
4256out:
4257 if (dirty_inode)
4258 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
4259}
4260
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004261
4262/*
4263 * This is a helper function for check_blocks().
4264 */
4265static int process_block(ext2_filsys fs,
4266 blk_t *block_nr,
4267 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004268 blk_t ref_block FSCK_ATTR((unused)),
4269 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004270 void *priv_data)
4271{
4272 struct process_block_struct_1 *p;
4273 struct problem_context *pctx;
4274 blk_t blk = *block_nr;
4275 int ret_code = 0;
4276 int problem = 0;
4277 e2fsck_t ctx;
4278
4279 p = (struct process_block_struct_1 *) priv_data;
4280 pctx = p->pctx;
4281 ctx = p->ctx;
4282
4283 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
4284 /* todo: Check that the comprblk_fl is high, that the
4285 blkaddr pattern looks right (all non-holes up to
4286 first EXT2FS_COMPRESSED_BLKADDR, then all
4287 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
4288 that the feature_incompat bit is high, and that the
4289 inode is a regular file. If we're doing a "full
4290 check" (a concept introduced to e2fsck by e2compr,
4291 meaning that we look at data blocks as well as
4292 metadata) then call some library routine that
4293 checks the compressed data. I'll have to think
4294 about this, because one particularly important
4295 problem to be able to fix is to recalculate the
4296 cluster size if necessary. I think that perhaps
4297 we'd better do most/all e2compr-specific checks
4298 separately, after the non-e2compr checks. If not
4299 doing a full check, it may be useful to test that
4300 the personality is linux; e.g. if it isn't then
4301 perhaps this really is just an illegal block. */
4302 return 0;
4303 }
4304
4305 if (blk == 0) {
4306 if (p->is_dir == 0) {
4307 /*
4308 * Should never happen, since only directories
4309 * get called with BLOCK_FLAG_HOLE
4310 */
4311#if DEBUG_E2FSCK
4312 printf("process_block() called with blk == 0, "
4313 "blockcnt=%d, inode %lu???\n",
4314 blockcnt, p->ino);
4315#endif
4316 return 0;
4317 }
4318 if (blockcnt < 0)
4319 return 0;
4320 if (blockcnt * fs->blocksize < p->inode->i_size) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004321 goto mark_dir;
4322 }
4323 return 0;
4324 }
4325
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004326 /*
4327 * Simplistic fragmentation check. We merely require that the
4328 * file be contiguous. (Which can never be true for really
4329 * big files that are greater than a block group.)
4330 */
4331 if (!HOLE_BLKADDR(p->previous_block)) {
4332 if (p->previous_block+1 != blk)
4333 p->fragmented = 1;
4334 }
4335 p->previous_block = blk;
4336
4337 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
4338 problem = PR_1_TOOBIG_DIR;
4339 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
4340 problem = PR_1_TOOBIG_REG;
4341 if (!p->is_dir && !p->is_reg && blockcnt > 0)
4342 problem = PR_1_TOOBIG_SYMLINK;
4343
4344 if (blk < fs->super->s_first_data_block ||
4345 blk >= fs->super->s_blocks_count)
4346 problem = PR_1_ILLEGAL_BLOCK_NUM;
4347
4348 if (problem) {
4349 p->num_illegal_blocks++;
4350 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
4351 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
4352 p->clear = 1;
4353 return BLOCK_ABORT;
4354 }
4355 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
4356 p->suppress = 1;
4357 set_latch_flags(PR_LATCH_BLOCK,
4358 PRL_SUPPRESS, 0);
4359 }
4360 }
4361 pctx->blk = blk;
4362 pctx->blkcount = blockcnt;
4363 if (fix_problem(ctx, problem, pctx)) {
4364 blk = *block_nr = 0;
4365 ret_code = BLOCK_CHANGED;
4366 goto mark_dir;
4367 } else
4368 return 0;
4369 }
4370
4371 if (p->ino == EXT2_RESIZE_INO) {
4372 /*
4373 * The resize inode has already be sanity checked
4374 * during pass #0 (the superblock checks). All we
4375 * have to do is mark the double indirect block as
4376 * being in use; all of the other blocks are handled
4377 * by mark_table_blocks()).
4378 */
4379 if (blockcnt == BLOCK_COUNT_DIND)
4380 mark_block_used(ctx, blk);
4381 } else
4382 mark_block_used(ctx, blk);
4383 p->num_blocks++;
4384 if (blockcnt >= 0)
4385 p->last_block = blockcnt;
4386mark_dir:
4387 if (p->is_dir && (blockcnt >= 0)) {
4388 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
4389 blk, blockcnt);
4390 if (pctx->errcode) {
4391 pctx->blk = blk;
4392 pctx->num = blockcnt;
4393 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
4394 /* Should never get here */
4395 ctx->flags |= E2F_FLAG_ABORT;
4396 return BLOCK_ABORT;
4397 }
4398 }
4399 return ret_code;
4400}
4401
Rob Landleyd8f66012006-05-05 17:29:09 +00004402static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004403 blk_t *block_nr,
4404 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004405 blk_t ref_block FSCK_ATTR((unused)),
4406 int ref_offset FSCK_ATTR((unused)),
Rob Landleyd8f66012006-05-05 17:29:09 +00004407 void *priv_data EXT2FS_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004408{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004409 /*
4410 * Note: This function processes blocks for the bad blocks
4411 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
4412 */
4413
Rob Landley7a260f02006-06-19 03:20:03 +00004414 printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr);
Rob Landleyd8f66012006-05-05 17:29:09 +00004415 return BLOCK_ERROR;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004416}
4417
4418/*
4419 * This routine gets called at the end of pass 1 if bad blocks are
4420 * detected in the superblock, group descriptors, inode_bitmaps, or
4421 * block bitmaps. At this point, all of the blocks have been mapped
4422 * out, so we can try to allocate new block(s) to replace the bad
4423 * blocks.
4424 */
4425static void handle_fs_bad_blocks(e2fsck_t ctx)
4426{
Rob Landleyd8f66012006-05-05 17:29:09 +00004427 printf("Bad blocks detected on your filesystem\n"
4428 "You should get your data off as the device will soon die\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004429}
4430
4431/*
4432 * This routine marks all blocks which are used by the superblock,
4433 * group descriptors, inode bitmaps, and block bitmaps.
4434 */
4435static void mark_table_blocks(e2fsck_t ctx)
4436{
4437 ext2_filsys fs = ctx->fs;
4438 blk_t block, b;
4439 dgrp_t i;
4440 int j;
4441 struct problem_context pctx;
4442
4443 clear_problem_context(&pctx);
4444
4445 block = fs->super->s_first_data_block;
4446 for (i = 0; i < fs->group_desc_count; i++) {
4447 pctx.group = i;
4448
4449 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
4450
4451 /*
4452 * Mark the blocks used for the inode table
4453 */
4454 if (fs->group_desc[i].bg_inode_table) {
4455 for (j = 0, b = fs->group_desc[i].bg_inode_table;
4456 j < fs->inode_blocks_per_group;
4457 j++, b++) {
4458 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4459 b)) {
4460 pctx.blk = b;
4461 if (fix_problem(ctx,
4462 PR_1_ITABLE_CONFLICT, &pctx)) {
4463 ctx->invalid_inode_table_flag[i]++;
4464 ctx->invalid_bitmaps++;
4465 }
4466 } else {
4467 ext2fs_mark_block_bitmap(ctx->block_found_map,
4468 b);
4469 }
4470 }
4471 }
4472
4473 /*
4474 * Mark block used for the block bitmap
4475 */
4476 if (fs->group_desc[i].bg_block_bitmap) {
4477 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4478 fs->group_desc[i].bg_block_bitmap)) {
4479 pctx.blk = fs->group_desc[i].bg_block_bitmap;
4480 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
4481 ctx->invalid_block_bitmap_flag[i]++;
4482 ctx->invalid_bitmaps++;
4483 }
4484 } else {
4485 ext2fs_mark_block_bitmap(ctx->block_found_map,
4486 fs->group_desc[i].bg_block_bitmap);
4487 }
4488
4489 }
4490 /*
4491 * Mark block used for the inode bitmap
4492 */
4493 if (fs->group_desc[i].bg_inode_bitmap) {
4494 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4495 fs->group_desc[i].bg_inode_bitmap)) {
4496 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
4497 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
4498 ctx->invalid_inode_bitmap_flag[i]++;
4499 ctx->invalid_bitmaps++;
4500 }
4501 } else {
4502 ext2fs_mark_block_bitmap(ctx->block_found_map,
4503 fs->group_desc[i].bg_inode_bitmap);
4504 }
4505 }
4506 block += fs->super->s_blocks_per_group;
4507 }
4508}
4509
4510/*
4511 * Thes subroutines short circuits ext2fs_get_blocks and
4512 * ext2fs_check_directory; we use them since we already have the inode
4513 * structure, so there's no point in letting the ext2fs library read
4514 * the inode again.
4515 */
4516static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
4517 blk_t *blocks)
4518{
4519 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4520 int i;
4521
4522 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4523 return EXT2_ET_CALLBACK_NOTHANDLED;
4524
4525 for (i=0; i < EXT2_N_BLOCKS; i++)
4526 blocks[i] = ctx->stashed_inode->i_block[i];
4527 return 0;
4528}
4529
4530static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
4531 struct ext2_inode *inode)
4532{
4533 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4534
4535 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4536 return EXT2_ET_CALLBACK_NOTHANDLED;
4537 *inode = *ctx->stashed_inode;
4538 return 0;
4539}
4540
4541static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
4542 struct ext2_inode *inode)
4543{
4544 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4545
4546 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
4547 *ctx->stashed_inode = *inode;
4548 return EXT2_ET_CALLBACK_NOTHANDLED;
4549}
4550
4551static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
4552{
4553 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4554
4555 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4556 return EXT2_ET_CALLBACK_NOTHANDLED;
4557
4558 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
4559 return EXT2_ET_NO_DIRECTORY;
4560 return 0;
4561}
4562
4563void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
4564{
4565 ext2_filsys fs = ctx->fs;
4566
4567 if (bool) {
4568 fs->get_blocks = pass1_get_blocks;
4569 fs->check_directory = pass1_check_directory;
4570 fs->read_inode = pass1_read_inode;
4571 fs->write_inode = pass1_write_inode;
4572 ctx->stashed_ino = 0;
4573 } else {
4574 fs->get_blocks = 0;
4575 fs->check_directory = 0;
4576 fs->read_inode = 0;
4577 fs->write_inode = 0;
4578 }
4579}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004580
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004581/*
4582 * pass1b.c --- Pass #1b of e2fsck
4583 *
4584 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
4585 * only invoked if pass 1 discovered blocks which are in use by more
4586 * than one inode.
4587 *
4588 * Pass1B scans the data blocks of all the inodes again, generating a
4589 * complete list of duplicate blocks and which inodes have claimed
4590 * them.
4591 *
4592 * Pass1C does a tree-traversal of the filesystem, to determine the
4593 * parent directories of these inodes. This step is necessary so that
4594 * e2fsck can print out the pathnames of affected inodes.
4595 *
4596 * Pass1D is a reconciliation pass. For each inode with duplicate
4597 * blocks, the user is prompted if s/he would like to clone the file
4598 * (so that the file gets a fresh copy of the duplicated blocks) or
4599 * simply to delete the file.
4600 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004601 */
4602
4603
4604/* Needed for architectures where sizeof(int) != sizeof(void *) */
4605#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
4606#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
4607
4608/* Define an extension to the ext2 library's block count information */
4609#define BLOCK_COUNT_EXTATTR (-5)
4610
4611struct block_el {
4612 blk_t block;
4613 struct block_el *next;
4614};
4615
4616struct inode_el {
4617 ext2_ino_t inode;
4618 struct inode_el *next;
4619};
4620
4621struct dup_block {
4622 int num_bad;
4623 struct inode_el *inode_list;
4624};
4625
4626/*
4627 * This structure stores information about a particular inode which
4628 * is sharing blocks with other inodes. This information is collected
4629 * to display to the user, so that the user knows what files he or she
4630 * is dealing with, when trying to decide how to resolve the conflict
4631 * of multiply-claimed blocks.
4632 */
4633struct dup_inode {
4634 ext2_ino_t dir;
4635 int num_dupblocks;
4636 struct ext2_inode inode;
4637 struct block_el *block_list;
4638};
4639
4640static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
4641 e2_blkcnt_t blockcnt, blk_t ref_blk,
4642 int ref_offset, void *priv_data);
4643static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
4644 struct dup_inode *dp, char *block_buf);
4645static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
4646 struct dup_inode *dp, char* block_buf);
4647static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
4648
4649static void pass1b(e2fsck_t ctx, char *block_buf);
4650static void pass1c(e2fsck_t ctx, char *block_buf);
4651static void pass1d(e2fsck_t ctx, char *block_buf);
4652
4653static int dup_inode_count = 0;
4654
4655static dict_t blk_dict, ino_dict;
4656
4657static ext2fs_inode_bitmap inode_dup_map;
4658
4659static int dict_int_cmp(const void *a, const void *b)
4660{
4661 intptr_t ia, ib;
4662
4663 ia = (intptr_t)a;
4664 ib = (intptr_t)b;
4665
4666 return (ia-ib);
4667}
4668
4669/*
4670 * Add a duplicate block record
4671 */
4672static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
4673 struct ext2_inode *inode)
4674{
4675 dnode_t *n;
4676 struct dup_block *db;
4677 struct dup_inode *di;
4678 struct block_el *blk_el;
4679 struct inode_el *ino_el;
4680
4681 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
4682 if (n)
4683 db = (struct dup_block *) dnode_get(n);
4684 else {
4685 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
4686 sizeof(struct dup_block), "duplicate block header");
4687 db->num_bad = 0;
4688 db->inode_list = 0;
4689 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
4690 }
4691 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
4692 sizeof(struct inode_el), "inode element");
4693 ino_el->inode = ino;
4694 ino_el->next = db->inode_list;
4695 db->inode_list = ino_el;
4696 db->num_bad++;
4697
4698 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
4699 if (n)
4700 di = (struct dup_inode *) dnode_get(n);
4701 else {
4702 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
4703 sizeof(struct dup_inode), "duplicate inode header");
4704 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
4705 di->num_dupblocks = 0;
4706 di->block_list = 0;
4707 di->inode = *inode;
4708 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
4709 }
4710 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
4711 sizeof(struct block_el), "block element");
4712 blk_el->block = blk;
4713 blk_el->next = di->block_list;
4714 di->block_list = blk_el;
4715 di->num_dupblocks++;
4716}
4717
4718/*
4719 * Free a duplicate inode record
4720 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004721static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004722{
4723 struct dup_inode *di;
4724 struct block_el *p, *next;
4725
4726 di = (struct dup_inode *) dnode_get(node);
4727 for (p = di->block_list; p; p = next) {
4728 next = p->next;
4729 free(p);
4730 }
4731 free(node);
4732}
4733
4734/*
4735 * Free a duplicate block record
4736 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004737static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004738{
4739 struct dup_block *db;
4740 struct inode_el *p, *next;
4741
4742 db = (struct dup_block *) dnode_get(node);
4743 for (p = db->inode_list; p; p = next) {
4744 next = p->next;
4745 free(p);
4746 }
4747 free(node);
4748}
4749
4750
4751/*
4752 * Main procedure for handling duplicate blocks
4753 */
4754void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
4755{
4756 ext2_filsys fs = ctx->fs;
4757 struct problem_context pctx;
4758
4759 clear_problem_context(&pctx);
4760
4761 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4762 _("multiply claimed inode map"), &inode_dup_map);
4763 if (pctx.errcode) {
4764 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
4765 ctx->flags |= E2F_FLAG_ABORT;
4766 return;
4767 }
4768
4769 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
4770 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004771 dict_set_allocator(&ino_dict, inode_dnode_free);
4772 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004773
4774 pass1b(ctx, block_buf);
4775 pass1c(ctx, block_buf);
4776 pass1d(ctx, block_buf);
4777
4778 /*
4779 * Time to free all of the accumulated data structures that we
4780 * don't need anymore.
4781 */
4782 dict_free_nodes(&ino_dict);
4783 dict_free_nodes(&blk_dict);
4784}
4785
4786/*
4787 * Scan the inodes looking for inodes that contain duplicate blocks.
4788 */
4789struct process_block_struct_1b {
4790 e2fsck_t ctx;
4791 ext2_ino_t ino;
4792 int dup_blocks;
4793 struct ext2_inode *inode;
4794 struct problem_context *pctx;
4795};
4796
4797static void pass1b(e2fsck_t ctx, char *block_buf)
4798{
4799 ext2_filsys fs = ctx->fs;
4800 ext2_ino_t ino;
4801 struct ext2_inode inode;
4802 ext2_inode_scan scan;
4803 struct process_block_struct_1b pb;
4804 struct problem_context pctx;
4805
4806 clear_problem_context(&pctx);
4807
4808 if (!(ctx->options & E2F_OPT_PREEN))
4809 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
4810 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
4811 &scan);
4812 if (pctx.errcode) {
4813 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4814 ctx->flags |= E2F_FLAG_ABORT;
4815 return;
4816 }
4817 ctx->stashed_inode = &inode;
4818 pb.ctx = ctx;
4819 pb.pctx = &pctx;
4820 pctx.str = "pass1b";
4821 while (1) {
4822 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
4823 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
4824 continue;
4825 if (pctx.errcode) {
4826 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4827 ctx->flags |= E2F_FLAG_ABORT;
4828 return;
4829 }
4830 if (!ino)
4831 break;
4832 pctx.ino = ctx->stashed_ino = ino;
4833 if ((ino != EXT2_BAD_INO) &&
4834 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
4835 continue;
4836
4837 pb.ino = ino;
4838 pb.dup_blocks = 0;
4839 pb.inode = &inode;
4840
4841 if (ext2fs_inode_has_valid_blocks(&inode) ||
4842 (ino == EXT2_BAD_INO))
4843 pctx.errcode = ext2fs_block_iterate2(fs, ino,
4844 0, block_buf, process_pass1b_block, &pb);
4845 if (inode.i_file_acl)
4846 process_pass1b_block(fs, &inode.i_file_acl,
4847 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
4848 if (pb.dup_blocks) {
4849 end_problem_latch(ctx, PR_LATCH_DBLOCK);
4850 if (ino >= EXT2_FIRST_INODE(fs->super) ||
4851 ino == EXT2_ROOT_INO)
4852 dup_inode_count++;
4853 }
4854 if (pctx.errcode)
4855 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
4856 }
4857 ext2fs_close_inode_scan(scan);
4858 e2fsck_use_inode_shortcuts(ctx, 0);
4859}
4860
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004861static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004862 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004863 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
4864 blk_t ref_blk FSCK_ATTR((unused)),
4865 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004866 void *priv_data)
4867{
4868 struct process_block_struct_1b *p;
4869 e2fsck_t ctx;
4870
4871 if (HOLE_BLKADDR(*block_nr))
4872 return 0;
4873 p = (struct process_block_struct_1b *) priv_data;
4874 ctx = p->ctx;
4875
4876 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
4877 return 0;
4878
4879 /* OK, this is a duplicate block */
4880 if (p->ino != EXT2_BAD_INO) {
4881 p->pctx->blk = *block_nr;
4882 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
4883 }
4884 p->dup_blocks++;
4885 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
4886
4887 add_dupe(ctx, p->ino, *block_nr, p->inode);
4888
4889 return 0;
4890}
4891
4892/*
4893 * Pass 1c: Scan directories for inodes with duplicate blocks. This
4894 * is used so that we can print pathnames when prompting the user for
4895 * what to do.
4896 */
4897struct search_dir_struct {
4898 int count;
4899 ext2_ino_t first_inode;
4900 ext2_ino_t max_inode;
4901};
4902
4903static int search_dirent_proc(ext2_ino_t dir, int entry,
4904 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004905 int offset FSCK_ATTR((unused)),
4906 int blocksize FSCK_ATTR((unused)),
4907 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004908 void *priv_data)
4909{
4910 struct search_dir_struct *sd;
4911 struct dup_inode *p;
4912 dnode_t *n;
4913
4914 sd = (struct search_dir_struct *) priv_data;
4915
4916 if (dirent->inode > sd->max_inode)
4917 /* Should abort this inode, but not everything */
4918 return 0;
4919
4920 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
4921 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
4922 return 0;
4923
4924 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
4925 if (!n)
4926 return 0;
4927 p = (struct dup_inode *) dnode_get(n);
4928 p->dir = dir;
4929 sd->count--;
4930
4931 return(sd->count ? 0 : DIRENT_ABORT);
4932}
4933
4934
4935static void pass1c(e2fsck_t ctx, char *block_buf)
4936{
4937 ext2_filsys fs = ctx->fs;
4938 struct search_dir_struct sd;
4939 struct problem_context pctx;
4940
4941 clear_problem_context(&pctx);
4942
4943 if (!(ctx->options & E2F_OPT_PREEN))
4944 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
4945
4946 /*
4947 * Search through all directories to translate inodes to names
4948 * (by searching for the containing directory for that inode.)
4949 */
4950 sd.count = dup_inode_count;
4951 sd.first_inode = EXT2_FIRST_INODE(fs->super);
4952 sd.max_inode = fs->super->s_inodes_count;
4953 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
4954 search_dirent_proc, &sd);
4955}
4956
4957static void pass1d(e2fsck_t ctx, char *block_buf)
4958{
4959 ext2_filsys fs = ctx->fs;
4960 struct dup_inode *p, *t;
4961 struct dup_block *q;
4962 ext2_ino_t *shared, ino;
4963 int shared_len;
4964 int i;
4965 int file_ok;
4966 int meta_data = 0;
4967 struct problem_context pctx;
4968 dnode_t *n, *m;
4969 struct block_el *s;
4970 struct inode_el *r;
4971
4972 clear_problem_context(&pctx);
4973
4974 if (!(ctx->options & E2F_OPT_PREEN))
4975 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
4976 e2fsck_read_bitmaps(ctx);
4977
4978 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
4979 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
4980 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
4981 sizeof(ext2_ino_t) * dict_count(&ino_dict),
4982 "Shared inode list");
4983 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
4984 p = (struct dup_inode *) dnode_get(n);
4985 shared_len = 0;
4986 file_ok = 1;
4987 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00004988 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004989 continue;
4990
4991 /*
4992 * Find all of the inodes which share blocks with this
4993 * one. First we find all of the duplicate blocks
4994 * belonging to this inode, and then search each block
4995 * get the list of inodes, and merge them together.
4996 */
4997 for (s = p->block_list; s; s = s->next) {
4998 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
4999 if (!m)
5000 continue; /* Should never happen... */
5001 q = (struct dup_block *) dnode_get(m);
5002 if (q->num_bad > 1)
5003 file_ok = 0;
5004 if (check_if_fs_block(ctx, s->block)) {
5005 file_ok = 0;
5006 meta_data = 1;
5007 }
5008
5009 /*
5010 * Add all inodes used by this block to the
5011 * shared[] --- which is a unique list, so
5012 * if an inode is already in shared[], don't
5013 * add it again.
5014 */
5015 for (r = q->inode_list; r; r = r->next) {
5016 if (r->inode == ino)
5017 continue;
5018 for (i = 0; i < shared_len; i++)
5019 if (shared[i] == r->inode)
5020 break;
5021 if (i == shared_len) {
5022 shared[shared_len++] = r->inode;
5023 }
5024 }
5025 }
5026
5027 /*
5028 * Report the inode that we are working on
5029 */
5030 pctx.inode = &p->inode;
5031 pctx.ino = ino;
5032 pctx.dir = p->dir;
5033 pctx.blkcount = p->num_dupblocks;
5034 pctx.num = meta_data ? shared_len+1 : shared_len;
5035 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
5036 pctx.blkcount = 0;
5037 pctx.num = 0;
5038
5039 if (meta_data)
5040 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
5041
5042 for (i = 0; i < shared_len; i++) {
5043 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
5044 if (!m)
5045 continue; /* should never happen */
5046 t = (struct dup_inode *) dnode_get(m);
5047 /*
5048 * Report the inode that we are sharing with
5049 */
5050 pctx.inode = &t->inode;
5051 pctx.ino = shared[i];
5052 pctx.dir = t->dir;
5053 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
5054 }
5055 if (file_ok) {
5056 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
5057 continue;
5058 }
5059 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
5060 pctx.errcode = clone_file(ctx, ino, p, block_buf);
5061 if (pctx.errcode)
5062 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
5063 else
5064 continue;
5065 }
5066 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
5067 delete_file(ctx, ino, p, block_buf);
5068 else
5069 ext2fs_unmark_valid(fs);
5070 }
5071 ext2fs_free_mem(&shared);
5072}
5073
5074/*
5075 * Drop the refcount on the dup_block structure, and clear the entry
5076 * in the block_dup_map if appropriate.
5077 */
5078static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
5079{
5080 p->num_bad--;
5081 if (p->num_bad <= 0 ||
5082 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
5083 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
5084}
5085
5086static int delete_file_block(ext2_filsys fs,
5087 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005088 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
5089 blk_t ref_block FSCK_ATTR((unused)),
5090 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005091 void *priv_data)
5092{
5093 struct process_block_struct_1b *pb;
5094 struct dup_block *p;
5095 dnode_t *n;
5096 e2fsck_t ctx;
5097
5098 pb = (struct process_block_struct_1b *) priv_data;
5099 ctx = pb->ctx;
5100
5101 if (HOLE_BLKADDR(*block_nr))
5102 return 0;
5103
5104 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5105 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5106 if (n) {
5107 p = (struct dup_block *) dnode_get(n);
5108 decrement_badcount(ctx, *block_nr, p);
5109 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005110 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005111 *block_nr);
5112 } else {
5113 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
5114 ext2fs_block_alloc_stats(fs, *block_nr, -1);
5115 }
5116
5117 return 0;
5118}
5119
5120static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5121 struct dup_inode *dp, char* block_buf)
5122{
5123 ext2_filsys fs = ctx->fs;
5124 struct process_block_struct_1b pb;
5125 struct ext2_inode inode;
5126 struct problem_context pctx;
5127 unsigned int count;
5128
5129 clear_problem_context(&pctx);
5130 pctx.ino = pb.ino = ino;
5131 pb.dup_blocks = dp->num_dupblocks;
5132 pb.ctx = ctx;
5133 pctx.str = "delete_file";
5134
5135 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5136 if (ext2fs_inode_has_valid_blocks(&inode))
5137 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5138 delete_file_block, &pb);
5139 if (pctx.errcode)
5140 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5141 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5142 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5143 if (ctx->inode_bad_map)
5144 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
5145 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
5146
5147 /* Inode may have changed by block_iterate, so reread it */
5148 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5149 inode.i_links_count = 0;
5150 inode.i_dtime = time(0);
5151 if (inode.i_file_acl &&
5152 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
5153 count = 1;
5154 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
5155 block_buf, -1, &count);
5156 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
5157 pctx.errcode = 0;
5158 count = 1;
5159 }
5160 if (pctx.errcode) {
5161 pctx.blk = inode.i_file_acl;
5162 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
5163 }
5164 /*
5165 * If the count is zero, then arrange to have the
5166 * block deleted. If the block is in the block_dup_map,
5167 * also call delete_file_block since it will take care
5168 * of keeping the accounting straight.
5169 */
5170 if ((count == 0) ||
5171 ext2fs_test_block_bitmap(ctx->block_dup_map,
5172 inode.i_file_acl))
5173 delete_file_block(fs, &inode.i_file_acl,
5174 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
5175 }
5176 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
5177}
5178
5179struct clone_struct {
5180 errcode_t errcode;
5181 ext2_ino_t dir;
5182 char *buf;
5183 e2fsck_t ctx;
5184};
5185
5186static int clone_file_block(ext2_filsys fs,
5187 blk_t *block_nr,
5188 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005189 blk_t ref_block FSCK_ATTR((unused)),
5190 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005191 void *priv_data)
5192{
5193 struct dup_block *p;
5194 blk_t new_block;
5195 errcode_t retval;
5196 struct clone_struct *cs = (struct clone_struct *) priv_data;
5197 dnode_t *n;
5198 e2fsck_t ctx;
5199
5200 ctx = cs->ctx;
5201
5202 if (HOLE_BLKADDR(*block_nr))
5203 return 0;
5204
5205 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5206 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5207 if (n) {
5208 p = (struct dup_block *) dnode_get(n);
5209 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
5210 &new_block);
5211 if (retval) {
5212 cs->errcode = retval;
5213 return BLOCK_ABORT;
5214 }
5215 if (cs->dir && (blockcnt >= 0)) {
5216 retval = ext2fs_set_dir_block(fs->dblist,
5217 cs->dir, new_block, blockcnt);
5218 if (retval) {
5219 cs->errcode = retval;
5220 return BLOCK_ABORT;
5221 }
5222 }
Rob Landley3e72c592006-04-06 22:49:04 +00005223
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005224 retval = io_channel_read_blk(fs->io, *block_nr, 1,
5225 cs->buf);
5226 if (retval) {
5227 cs->errcode = retval;
5228 return BLOCK_ABORT;
5229 }
5230 retval = io_channel_write_blk(fs->io, new_block, 1,
5231 cs->buf);
5232 if (retval) {
5233 cs->errcode = retval;
5234 return BLOCK_ABORT;
5235 }
5236 decrement_badcount(ctx, *block_nr, p);
5237 *block_nr = new_block;
5238 ext2fs_mark_block_bitmap(ctx->block_found_map,
5239 new_block);
5240 ext2fs_mark_block_bitmap(fs->block_map, new_block);
5241 return BLOCK_CHANGED;
5242 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005243 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005244 *block_nr);
5245 }
5246 return 0;
5247}
5248
5249static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5250 struct dup_inode *dp, char* block_buf)
5251{
5252 ext2_filsys fs = ctx->fs;
5253 errcode_t retval;
5254 struct clone_struct cs;
5255 struct problem_context pctx;
5256 blk_t blk;
5257 dnode_t *n;
5258 struct inode_el *ino_el;
5259 struct dup_block *db;
5260 struct dup_inode *di;
5261
5262 clear_problem_context(&pctx);
5263 cs.errcode = 0;
5264 cs.dir = 0;
5265 cs.ctx = ctx;
5266 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
5267 if (retval)
5268 return retval;
5269
5270 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
5271 cs.dir = ino;
5272
5273 pctx.ino = ino;
5274 pctx.str = "clone_file";
5275 if (ext2fs_inode_has_valid_blocks(&dp->inode))
5276 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5277 clone_file_block, &cs);
5278 ext2fs_mark_bb_dirty(fs);
5279 if (pctx.errcode) {
5280 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5281 retval = pctx.errcode;
5282 goto errout;
5283 }
5284 if (cs.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +00005285 bb_error_msg(_("returned from clone_file_block"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005286 retval = cs.errcode;
5287 goto errout;
5288 }
5289 /* The inode may have changed on disk, so we have to re-read it */
5290 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
5291 blk = dp->inode.i_file_acl;
5292 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
5293 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
5294 BLOCK_CHANGED)) {
5295 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
5296 /*
5297 * If we cloned the EA block, find all other inodes
5298 * which refered to that EA block, and modify
5299 * them to point to the new EA block.
5300 */
5301 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5302 db = (struct dup_block *) dnode_get(n);
5303 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
5304 if (ino_el->inode == ino)
5305 continue;
5306 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
5307 di = (struct dup_inode *) dnode_get(n);
5308 if (di->inode.i_file_acl == blk) {
5309 di->inode.i_file_acl = dp->inode.i_file_acl;
5310 e2fsck_write_inode(ctx, ino_el->inode,
5311 &di->inode, "clone file EA");
5312 decrement_badcount(ctx, blk, db);
5313 }
5314 }
5315 }
5316 retval = 0;
5317errout:
5318 ext2fs_free_mem(&cs.buf);
5319 return retval;
5320}
5321
5322/*
5323 * This routine returns 1 if a block overlaps with one of the superblocks,
5324 * group descriptors, inode bitmaps, or block bitmaps.
5325 */
5326static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
5327{
5328 ext2_filsys fs = ctx->fs;
5329 blk_t block;
5330 dgrp_t i;
5331
5332 block = fs->super->s_first_data_block;
5333 for (i = 0; i < fs->group_desc_count; i++) {
5334
5335 /* Check superblocks/block group descriptros */
5336 if (ext2fs_bg_has_super(fs, i)) {
5337 if (test_block >= block &&
5338 (test_block <= block + fs->desc_blocks))
5339 return 1;
5340 }
5341
5342 /* Check the inode table */
5343 if ((fs->group_desc[i].bg_inode_table) &&
5344 (test_block >= fs->group_desc[i].bg_inode_table) &&
5345 (test_block < (fs->group_desc[i].bg_inode_table +
5346 fs->inode_blocks_per_group)))
5347 return 1;
5348
5349 /* Check the bitmap blocks */
5350 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
5351 (test_block == fs->group_desc[i].bg_inode_bitmap))
5352 return 1;
5353
5354 block += fs->super->s_blocks_per_group;
5355 }
5356 return 0;
5357}
5358/*
5359 * pass2.c --- check directory structure
5360 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005361 * Pass 2 of e2fsck iterates through all active directory inodes, and
5362 * applies to following tests to each directory entry in the directory
5363 * blocks in the inodes:
5364 *
5365 * - The length of the directory entry (rec_len) should be at
5366 * least 8 bytes, and no more than the remaining space
5367 * left in the directory block.
5368 * - The length of the name in the directory entry (name_len)
5369 * should be less than (rec_len - 8).
5370 * - The inode number in the directory entry should be within
5371 * legal bounds.
5372 * - The inode number should refer to a in-use inode.
5373 * - The first entry should be '.', and its inode should be
5374 * the inode of the directory.
5375 * - The second entry should be '..'.
5376 *
5377 * To minimize disk seek time, the directory blocks are processed in
5378 * sorted order of block numbers.
5379 *
5380 * Pass 2 also collects the following information:
5381 * - The inode numbers of the subdirectories for each directory.
5382 *
5383 * Pass 2 relies on the following information from previous passes:
5384 * - The directory information collected in pass 1.
5385 * - The inode_used_map bitmap
5386 * - The inode_bad_map bitmap
5387 * - The inode_dir_map bitmap
5388 *
5389 * Pass 2 frees the following data structures
5390 * - The inode_bad_map bitmap
5391 * - The inode_reg_map bitmap
5392 */
5393
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005394/*
5395 * Keeps track of how many times an inode is referenced.
5396 */
5397static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
5398static int check_dir_block(ext2_filsys fs,
5399 struct ext2_db_entry *dir_blocks_info,
5400 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005401static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
5402 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005403static int update_dir_block(ext2_filsys fs,
5404 blk_t *block_nr,
5405 e2_blkcnt_t blockcnt,
5406 blk_t ref_block,
5407 int ref_offset,
5408 void *priv_data);
5409static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
5410static int htree_depth(struct dx_dir_info *dx_dir,
5411 struct dx_dirblock_info *dx_db);
Rob Landley7c94bed2006-05-03 21:58:45 +00005412static int special_dir_block_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005413
5414struct check_dir_struct {
5415 char *buf;
5416 struct problem_context pctx;
5417 int count, max;
5418 e2fsck_t ctx;
5419};
5420
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005421static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005422{
5423 struct ext2_super_block *sb = ctx->fs->super;
5424 struct problem_context pctx;
5425 ext2_filsys fs = ctx->fs;
5426 char *buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005427 struct dir_info *dir;
5428 struct check_dir_struct cd;
5429 struct dx_dir_info *dx_dir;
5430 struct dx_dirblock_info *dx_db, *dx_parent;
5431 int b;
5432 int i, depth;
5433 problem_t code;
5434 int bad_dir;
5435
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005436 clear_problem_context(&cd.pctx);
5437
Rob Landley3e72c592006-04-06 22:49:04 +00005438 /* Pass 2 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005439
5440 if (!(ctx->options & E2F_OPT_PREEN))
5441 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
5442
5443 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
5444 0, ctx->inode_link_info,
5445 &ctx->inode_count);
5446 if (cd.pctx.errcode) {
5447 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
5448 ctx->flags |= E2F_FLAG_ABORT;
5449 return;
5450 }
5451 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
5452 "directory scan buffer");
5453
5454 /*
5455 * Set up the parent pointer for the root directory, if
5456 * present. (If the root directory is not present, we will
5457 * create it in pass 3.)
5458 */
5459 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
5460 if (dir)
5461 dir->parent = EXT2_ROOT_INO;
5462
5463 cd.buf = buf;
5464 cd.ctx = ctx;
5465 cd.count = 1;
5466 cd.max = ext2fs_dblist_count(fs->dblist);
5467
5468 if (ctx->progress)
5469 (void) (ctx->progress)(ctx, 2, 0, cd.max);
5470
5471 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
5472 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
5473
5474 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
5475 &cd);
5476 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5477 return;
5478 if (cd.pctx.errcode) {
5479 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
5480 ctx->flags |= E2F_FLAG_ABORT;
5481 return;
5482 }
5483
5484#ifdef ENABLE_HTREE
5485 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
5486 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5487 return;
5488 if (dx_dir->numblocks == 0)
5489 continue;
5490 clear_problem_context(&pctx);
5491 bad_dir = 0;
5492 pctx.dir = dx_dir->ino;
5493 dx_db = dx_dir->dx_block;
5494 if (dx_db->flags & DX_FLAG_REFERENCED)
5495 dx_db->flags |= DX_FLAG_DUP_REF;
5496 else
5497 dx_db->flags |= DX_FLAG_REFERENCED;
5498 /*
5499 * Find all of the first and last leaf blocks, and
5500 * update their parent's min and max hash values
5501 */
5502 for (b=0, dx_db = dx_dir->dx_block;
5503 b < dx_dir->numblocks;
5504 b++, dx_db++) {
5505 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
5506 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
5507 continue;
5508 dx_parent = &dx_dir->dx_block[dx_db->parent];
5509 /*
5510 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
5511 */
5512 if (dx_db->flags & DX_FLAG_FIRST)
5513 dx_parent->min_hash = dx_db->min_hash;
5514 /*
5515 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
5516 */
5517 if (dx_db->flags & DX_FLAG_LAST)
5518 dx_parent->max_hash = dx_db->max_hash;
5519 }
5520
5521 for (b=0, dx_db = dx_dir->dx_block;
5522 b < dx_dir->numblocks;
5523 b++, dx_db++) {
5524 pctx.blkcount = b;
5525 pctx.group = dx_db->parent;
5526 code = 0;
5527 if (!(dx_db->flags & DX_FLAG_FIRST) &&
5528 (dx_db->min_hash < dx_db->node_min_hash)) {
5529 pctx.blk = dx_db->min_hash;
5530 pctx.blk2 = dx_db->node_min_hash;
5531 code = PR_2_HTREE_MIN_HASH;
5532 fix_problem(ctx, code, &pctx);
5533 bad_dir++;
5534 }
5535 if (dx_db->type == DX_DIRBLOCK_LEAF) {
5536 depth = htree_depth(dx_dir, dx_db);
5537 if (depth != dx_dir->depth) {
5538 code = PR_2_HTREE_BAD_DEPTH;
5539 fix_problem(ctx, code, &pctx);
5540 bad_dir++;
5541 }
5542 }
5543 /*
5544 * This test doesn't apply for the root block
5545 * at block #0
5546 */
5547 if (b &&
5548 (dx_db->max_hash > dx_db->node_max_hash)) {
5549 pctx.blk = dx_db->max_hash;
5550 pctx.blk2 = dx_db->node_max_hash;
5551 code = PR_2_HTREE_MAX_HASH;
5552 fix_problem(ctx, code, &pctx);
5553 bad_dir++;
5554 }
5555 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
5556 code = PR_2_HTREE_NOTREF;
5557 fix_problem(ctx, code, &pctx);
5558 bad_dir++;
5559 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
5560 code = PR_2_HTREE_DUPREF;
5561 fix_problem(ctx, code, &pctx);
5562 bad_dir++;
5563 }
5564 if (code == 0)
5565 continue;
5566 }
5567 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
5568 clear_htree(ctx, dx_dir->ino);
5569 dx_dir->numblocks = 0;
5570 }
5571 }
5572#endif
5573 ext2fs_free_mem(&buf);
5574 ext2fs_free_dblist(fs->dblist);
5575
Rob Landleye7c43b62006-03-01 16:39:45 +00005576 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
5577 ctx->inode_bad_map = 0;
5578 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
5579 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005580
5581 clear_problem_context(&pctx);
5582 if (ctx->large_files) {
5583 if (!(sb->s_feature_ro_compat &
5584 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
5585 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
5586 sb->s_feature_ro_compat |=
5587 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5588 ext2fs_mark_super_dirty(fs);
5589 }
5590 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
5591 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
5592 ext2fs_update_dynamic_rev(fs);
5593 ext2fs_mark_super_dirty(fs);
5594 }
5595 } else if (!ctx->large_files &&
5596 (sb->s_feature_ro_compat &
5597 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
5598 if (fs->flags & EXT2_FLAG_RW) {
5599 sb->s_feature_ro_compat &=
5600 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5601 ext2fs_mark_super_dirty(fs);
5602 }
5603 }
5604
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005605}
5606
5607#define MAX_DEPTH 32000
5608static int htree_depth(struct dx_dir_info *dx_dir,
5609 struct dx_dirblock_info *dx_db)
5610{
5611 int depth = 0;
5612
5613 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
5614 dx_db = &dx_dir->dx_block[dx_db->parent];
5615 depth++;
5616 }
5617 return depth;
5618}
5619
5620static int dict_de_cmp(const void *a, const void *b)
5621{
5622 const struct ext2_dir_entry *de_a, *de_b;
5623 int a_len, b_len;
5624
5625 de_a = (const struct ext2_dir_entry *) a;
5626 a_len = de_a->name_len & 0xFF;
5627 de_b = (const struct ext2_dir_entry *) b;
5628 b_len = de_b->name_len & 0xFF;
5629
5630 if (a_len != b_len)
5631 return (a_len - b_len);
5632
5633 return strncmp(de_a->name, de_b->name, a_len);
5634}
5635
5636/*
5637 * This is special sort function that makes sure that directory blocks
5638 * with a dirblock of zero are sorted to the beginning of the list.
5639 * This guarantees that the root node of the htree directories are
5640 * processed first, so we know what hash version to use.
5641 */
Rob Landley7c94bed2006-05-03 21:58:45 +00005642static int special_dir_block_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005643{
5644 const struct ext2_db_entry *db_a =
5645 (const struct ext2_db_entry *) a;
5646 const struct ext2_db_entry *db_b =
5647 (const struct ext2_db_entry *) b;
5648
5649 if (db_a->blockcnt && !db_b->blockcnt)
5650 return 1;
5651
5652 if (!db_a->blockcnt && db_b->blockcnt)
5653 return -1;
5654
5655 if (db_a->blk != db_b->blk)
5656 return (int) (db_a->blk - db_b->blk);
5657
5658 if (db_a->ino != db_b->ino)
5659 return (int) (db_a->ino - db_b->ino);
5660
5661 return (int) (db_a->blockcnt - db_b->blockcnt);
5662}
5663
5664
5665/*
5666 * Make sure the first entry in the directory is '.', and that the
5667 * directory entry is sane.
5668 */
5669static int check_dot(e2fsck_t ctx,
5670 struct ext2_dir_entry *dirent,
5671 ext2_ino_t ino, struct problem_context *pctx)
5672{
5673 struct ext2_dir_entry *nextdir;
5674 int status = 0;
5675 int created = 0;
5676 int new_len;
5677 int problem = 0;
5678
5679 if (!dirent->inode)
5680 problem = PR_2_MISSING_DOT;
5681 else if (((dirent->name_len & 0xFF) != 1) ||
5682 (dirent->name[0] != '.'))
5683 problem = PR_2_1ST_NOT_DOT;
5684 else if (dirent->name[1] != '\0')
5685 problem = PR_2_DOT_NULL_TERM;
5686
5687 if (problem) {
5688 if (fix_problem(ctx, problem, pctx)) {
5689 if (dirent->rec_len < 12)
5690 dirent->rec_len = 12;
5691 dirent->inode = ino;
5692 dirent->name_len = 1;
5693 dirent->name[0] = '.';
5694 dirent->name[1] = '\0';
5695 status = 1;
5696 created = 1;
5697 }
5698 }
5699 if (dirent->inode != ino) {
5700 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
5701 dirent->inode = ino;
5702 status = 1;
5703 }
5704 }
5705 if (dirent->rec_len > 12) {
5706 new_len = dirent->rec_len - 12;
5707 if (new_len > 12) {
5708 if (created ||
5709 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
5710 nextdir = (struct ext2_dir_entry *)
5711 ((char *) dirent + 12);
5712 dirent->rec_len = 12;
5713 nextdir->rec_len = new_len;
5714 nextdir->inode = 0;
5715 nextdir->name_len = 0;
5716 status = 1;
5717 }
5718 }
5719 }
5720 return status;
5721}
5722
5723/*
5724 * Make sure the second entry in the directory is '..', and that the
5725 * directory entry is sane. We do not check the inode number of '..'
5726 * here; this gets done in pass 3.
5727 */
5728static int check_dotdot(e2fsck_t ctx,
5729 struct ext2_dir_entry *dirent,
5730 struct dir_info *dir, struct problem_context *pctx)
5731{
5732 int problem = 0;
5733
5734 if (!dirent->inode)
5735 problem = PR_2_MISSING_DOT_DOT;
5736 else if (((dirent->name_len & 0xFF) != 2) ||
5737 (dirent->name[0] != '.') ||
5738 (dirent->name[1] != '.'))
5739 problem = PR_2_2ND_NOT_DOT_DOT;
5740 else if (dirent->name[2] != '\0')
5741 problem = PR_2_DOT_DOT_NULL_TERM;
5742
5743 if (problem) {
5744 if (fix_problem(ctx, problem, pctx)) {
5745 if (dirent->rec_len < 12)
5746 dirent->rec_len = 12;
5747 /*
5748 * Note: we don't have the parent inode just
5749 * yet, so we will fill it in with the root
5750 * inode. This will get fixed in pass 3.
5751 */
5752 dirent->inode = EXT2_ROOT_INO;
5753 dirent->name_len = 2;
5754 dirent->name[0] = '.';
5755 dirent->name[1] = '.';
5756 dirent->name[2] = '\0';
5757 return 1;
5758 }
5759 return 0;
5760 }
5761 dir->dotdot = dirent->inode;
5762 return 0;
5763}
5764
5765/*
5766 * Check to make sure a directory entry doesn't contain any illegal
5767 * characters.
5768 */
5769static int check_name(e2fsck_t ctx,
5770 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005771 struct problem_context *pctx)
5772{
5773 int i;
5774 int fixup = -1;
5775 int ret = 0;
5776
5777 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
5778 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
5779 if (fixup < 0) {
5780 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
5781 }
5782 if (fixup) {
5783 dirent->name[i] = '.';
5784 ret = 1;
5785 }
5786 }
5787 }
5788 return ret;
5789}
5790
5791/*
5792 * Check the directory filetype (if present)
5793 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005794
5795/*
5796 * Given a mode, return the ext2 file type
5797 */
5798static int ext2_file_type(unsigned int mode)
5799{
5800 if (LINUX_S_ISREG(mode))
5801 return EXT2_FT_REG_FILE;
5802
5803 if (LINUX_S_ISDIR(mode))
5804 return EXT2_FT_DIR;
5805
5806 if (LINUX_S_ISCHR(mode))
5807 return EXT2_FT_CHRDEV;
5808
5809 if (LINUX_S_ISBLK(mode))
5810 return EXT2_FT_BLKDEV;
5811
5812 if (LINUX_S_ISLNK(mode))
5813 return EXT2_FT_SYMLINK;
5814
5815 if (LINUX_S_ISFIFO(mode))
5816 return EXT2_FT_FIFO;
5817
5818 if (LINUX_S_ISSOCK(mode))
5819 return EXT2_FT_SOCK;
5820
5821 return 0;
5822}
5823
Rob Landley7c94bed2006-05-03 21:58:45 +00005824static int check_filetype(e2fsck_t ctx,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005825 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005826 struct problem_context *pctx)
5827{
5828 int filetype = dirent->name_len >> 8;
5829 int should_be = EXT2_FT_UNKNOWN;
5830 struct ext2_inode inode;
5831
5832 if (!(ctx->fs->super->s_feature_incompat &
5833 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
5834 if (filetype == 0 ||
5835 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
5836 return 0;
5837 dirent->name_len = dirent->name_len & 0xFF;
5838 return 1;
5839 }
5840
5841 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
5842 should_be = EXT2_FT_DIR;
5843 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
5844 dirent->inode)) {
5845 should_be = EXT2_FT_REG_FILE;
5846 } else if (ctx->inode_bad_map &&
5847 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
5848 dirent->inode))
5849 should_be = 0;
5850 else {
5851 e2fsck_read_inode(ctx, dirent->inode, &inode,
5852 "check_filetype");
5853 should_be = ext2_file_type(inode.i_mode);
5854 }
5855 if (filetype == should_be)
5856 return 0;
5857 pctx->num = should_be;
5858
5859 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
5860 pctx) == 0)
5861 return 0;
5862
5863 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
5864 return 1;
5865}
5866
5867#ifdef ENABLE_HTREE
5868static void parse_int_node(ext2_filsys fs,
5869 struct ext2_db_entry *db,
5870 struct check_dir_struct *cd,
5871 struct dx_dir_info *dx_dir,
5872 char *block_buf)
5873{
5874 struct ext2_dx_root_info *root;
5875 struct ext2_dx_entry *ent;
5876 struct ext2_dx_countlimit *limit;
5877 struct dx_dirblock_info *dx_db;
5878 int i, expect_limit, count;
5879 blk_t blk;
5880 ext2_dirhash_t min_hash = 0xffffffff;
5881 ext2_dirhash_t max_hash = 0;
5882 ext2_dirhash_t hash = 0, prev_hash;
5883
5884 if (db->blockcnt == 0) {
5885 root = (struct ext2_dx_root_info *) (block_buf + 24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005886 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
5887 } else {
5888 ent = (struct ext2_dx_entry *) (block_buf+8);
5889 }
5890 limit = (struct ext2_dx_countlimit *) ent;
5891
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005892 count = ext2fs_le16_to_cpu(limit->count);
5893 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
5894 sizeof(struct ext2_dx_entry);
5895 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
5896 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
5897 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
5898 goto clear_and_exit;
5899 }
5900 if (count > expect_limit) {
5901 cd->pctx.num = count;
5902 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
5903 goto clear_and_exit;
5904 count = expect_limit;
5905 }
5906
5907 for (i=0; i < count; i++) {
5908 prev_hash = hash;
5909 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005910 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
5911 /* Check to make sure the block is valid */
5912 if (blk > (blk_t) dx_dir->numblocks) {
5913 cd->pctx.blk = blk;
5914 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
5915 &cd->pctx))
5916 goto clear_and_exit;
5917 }
5918 if (hash < prev_hash &&
5919 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
5920 goto clear_and_exit;
5921 dx_db = &dx_dir->dx_block[blk];
5922 if (dx_db->flags & DX_FLAG_REFERENCED) {
5923 dx_db->flags |= DX_FLAG_DUP_REF;
5924 } else {
5925 dx_db->flags |= DX_FLAG_REFERENCED;
5926 dx_db->parent = db->blockcnt;
5927 }
5928 if (hash < min_hash)
5929 min_hash = hash;
5930 if (hash > max_hash)
5931 max_hash = hash;
5932 dx_db->node_min_hash = hash;
5933 if ((i+1) < count)
5934 dx_db->node_max_hash =
5935 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
5936 else {
5937 dx_db->node_max_hash = 0xfffffffe;
5938 dx_db->flags |= DX_FLAG_LAST;
5939 }
5940 if (i == 0)
5941 dx_db->flags |= DX_FLAG_FIRST;
5942 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005943 dx_db = &dx_dir->dx_block[db->blockcnt];
5944 dx_db->min_hash = min_hash;
5945 dx_db->max_hash = max_hash;
5946 return;
5947
5948clear_and_exit:
5949 clear_htree(cd->ctx, cd->pctx.ino);
5950 dx_dir->numblocks = 0;
5951}
5952#endif /* ENABLE_HTREE */
5953
5954/*
5955 * Given a busted directory, try to salvage it somehow.
5956 *
5957 */
5958static void salvage_directory(ext2_filsys fs,
5959 struct ext2_dir_entry *dirent,
5960 struct ext2_dir_entry *prev,
5961 unsigned int *offset)
5962{
5963 char *cp = (char *) dirent;
5964 int left = fs->blocksize - *offset - dirent->rec_len;
5965 int name_len = dirent->name_len & 0xFF;
5966
5967 /*
5968 * Special case of directory entry of size 8: copy what's left
5969 * of the directory block up to cover up the invalid hole.
5970 */
5971 if ((left >= 12) && (dirent->rec_len == 8)) {
5972 memmove(cp, cp+8, left);
5973 memset(cp + left, 0, 8);
5974 return;
5975 }
5976 /*
5977 * If the directory entry overruns the end of the directory
5978 * block, and the name is small enough to fit, then adjust the
5979 * record length.
5980 */
5981 if ((left < 0) &&
5982 (name_len + 8 <= dirent->rec_len + left) &&
5983 dirent->inode <= fs->super->s_inodes_count &&
5984 strnlen(dirent->name, name_len) == name_len) {
5985 dirent->rec_len += left;
5986 return;
5987 }
5988 /*
5989 * If the directory entry is a multiple of four, so it is
5990 * valid, let the previous directory entry absorb the invalid
5991 * one.
5992 */
5993 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
5994 prev->rec_len += dirent->rec_len;
5995 *offset += dirent->rec_len;
5996 return;
5997 }
5998 /*
5999 * Default salvage method --- kill all of the directory
6000 * entries for the rest of the block. We will either try to
6001 * absorb it into the previous directory entry, or create a
6002 * new empty directory entry the rest of the directory block.
6003 */
6004 if (prev) {
6005 prev->rec_len += fs->blocksize - *offset;
6006 *offset = fs->blocksize;
6007 } else {
6008 dirent->rec_len = fs->blocksize - *offset;
6009 dirent->name_len = 0;
6010 dirent->inode = 0;
6011 }
6012}
6013
6014static int check_dir_block(ext2_filsys fs,
6015 struct ext2_db_entry *db,
6016 void *priv_data)
6017{
6018 struct dir_info *subdir, *dir;
6019 struct dx_dir_info *dx_dir;
6020#ifdef ENABLE_HTREE
6021 struct dx_dirblock_info *dx_db = 0;
6022#endif /* ENABLE_HTREE */
6023 struct ext2_dir_entry *dirent, *prev;
6024 ext2_dirhash_t hash;
6025 unsigned int offset = 0;
6026 int dir_modified = 0;
6027 int dot_state;
6028 blk_t block_nr = db->blk;
6029 ext2_ino_t ino = db->ino;
6030 __u16 links;
6031 struct check_dir_struct *cd;
6032 char *buf;
6033 e2fsck_t ctx;
6034 int problem;
6035 struct ext2_dx_root_info *root;
6036 struct ext2_dx_countlimit *limit;
6037 static dict_t de_dict;
6038 struct problem_context pctx;
6039 int dups_found = 0;
6040
6041 cd = (struct check_dir_struct *) priv_data;
6042 buf = cd->buf;
6043 ctx = cd->ctx;
6044
6045 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6046 return DIRENT_ABORT;
6047
6048 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
6049 return DIRENT_ABORT;
6050
6051 /*
6052 * Make sure the inode is still in use (could have been
6053 * deleted in the duplicate/bad blocks pass.
6054 */
6055 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
6056 return 0;
6057
6058 cd->pctx.ino = ino;
6059 cd->pctx.blk = block_nr;
6060 cd->pctx.blkcount = db->blockcnt;
6061 cd->pctx.ino2 = 0;
6062 cd->pctx.dirent = 0;
6063 cd->pctx.num = 0;
6064
6065 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006066 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006067 return 0;
6068 block_nr = db->blk;
6069 }
6070
6071 if (db->blockcnt)
6072 dot_state = 2;
6073 else
6074 dot_state = 0;
6075
6076 if (ctx->dirs_to_hash &&
6077 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
6078 dups_found++;
6079
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006080 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
6081 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
6082 cd->pctx.errcode = 0; /* We'll handle this ourselves */
6083 if (cd->pctx.errcode) {
6084 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
6085 ctx->flags |= E2F_FLAG_ABORT;
6086 return DIRENT_ABORT;
6087 }
6088 memset(buf, 0, fs->blocksize);
6089 }
6090#ifdef ENABLE_HTREE
6091 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
6092 if (dx_dir && dx_dir->numblocks) {
6093 if (db->blockcnt >= dx_dir->numblocks) {
6094 printf("XXX should never happen!!!\n");
6095 abort();
6096 }
6097 dx_db = &dx_dir->dx_block[db->blockcnt];
6098 dx_db->type = DX_DIRBLOCK_LEAF;
6099 dx_db->phys = block_nr;
6100 dx_db->min_hash = ~0;
6101 dx_db->max_hash = 0;
6102
6103 dirent = (struct ext2_dir_entry *) buf;
6104 limit = (struct ext2_dx_countlimit *) (buf+8);
6105 if (db->blockcnt == 0) {
6106 root = (struct ext2_dx_root_info *) (buf + 24);
6107 dx_db->type = DX_DIRBLOCK_ROOT;
6108 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
6109 if ((root->reserved_zero ||
6110 root->info_length < 8 ||
6111 root->indirect_levels > 1) &&
6112 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
6113 clear_htree(ctx, ino);
6114 dx_dir->numblocks = 0;
6115 dx_db = 0;
6116 }
6117 dx_dir->hashversion = root->hash_version;
6118 dx_dir->depth = root->indirect_levels + 1;
6119 } else if ((dirent->inode == 0) &&
6120 (dirent->rec_len == fs->blocksize) &&
6121 (dirent->name_len == 0) &&
6122 (ext2fs_le16_to_cpu(limit->limit) ==
6123 ((fs->blocksize-8) /
6124 sizeof(struct ext2_dx_entry))))
6125 dx_db->type = DX_DIRBLOCK_NODE;
6126 }
6127#endif /* ENABLE_HTREE */
6128
6129 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
6130 prev = 0;
6131 do {
6132 problem = 0;
6133 dirent = (struct ext2_dir_entry *) (buf + offset);
6134 cd->pctx.dirent = dirent;
6135 cd->pctx.num = offset;
6136 if (((offset + dirent->rec_len) > fs->blocksize) ||
6137 (dirent->rec_len < 12) ||
6138 ((dirent->rec_len % 4) != 0) ||
6139 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
6140 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
6141 salvage_directory(fs, dirent, prev, &offset);
6142 dir_modified++;
6143 continue;
6144 } else
6145 goto abort_free_dict;
6146 }
6147 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
6148 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
6149 dirent->name_len = EXT2_NAME_LEN;
6150 dir_modified++;
6151 }
6152 }
6153
6154 if (dot_state == 0) {
6155 if (check_dot(ctx, dirent, ino, &cd->pctx))
6156 dir_modified++;
6157 } else if (dot_state == 1) {
6158 dir = e2fsck_get_dir_info(ctx, ino);
6159 if (!dir) {
6160 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6161 goto abort_free_dict;
6162 }
6163 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
6164 dir_modified++;
6165 } else if (dirent->inode == ino) {
6166 problem = PR_2_LINK_DOT;
6167 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
6168 dirent->inode = 0;
6169 dir_modified++;
6170 goto next;
6171 }
6172 }
6173 if (!dirent->inode)
6174 goto next;
6175
6176 /*
6177 * Make sure the inode listed is a legal one.
6178 */
6179 if (((dirent->inode != EXT2_ROOT_INO) &&
6180 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
6181 (dirent->inode > fs->super->s_inodes_count)) {
6182 problem = PR_2_BAD_INO;
6183 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
6184 dirent->inode))) {
6185 /*
6186 * If the inode is unused, offer to clear it.
6187 */
6188 problem = PR_2_UNUSED_INODE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006189 } else if ((dot_state > 1) &&
6190 ((dirent->name_len & 0xFF) == 1) &&
6191 (dirent->name[0] == '.')) {
6192 /*
6193 * If there's a '.' entry in anything other
6194 * than the first directory entry, it's a
6195 * duplicate entry that should be removed.
6196 */
6197 problem = PR_2_DUP_DOT;
6198 } else if ((dot_state > 1) &&
6199 ((dirent->name_len & 0xFF) == 2) &&
6200 (dirent->name[0] == '.') &&
6201 (dirent->name[1] == '.')) {
6202 /*
6203 * If there's a '..' entry in anything other
6204 * than the second directory entry, it's a
6205 * duplicate entry that should be removed.
6206 */
6207 problem = PR_2_DUP_DOT_DOT;
6208 } else if ((dot_state > 1) &&
6209 (dirent->inode == EXT2_ROOT_INO)) {
6210 /*
6211 * Don't allow links to the root directory.
6212 * We check this specially to make sure we
6213 * catch this error case even if the root
6214 * directory hasn't been created yet.
6215 */
6216 problem = PR_2_LINK_ROOT;
6217 } else if ((dot_state > 1) &&
6218 (dirent->name_len & 0xFF) == 0) {
6219 /*
6220 * Don't allow zero-length directory names.
6221 */
6222 problem = PR_2_NULL_NAME;
6223 }
6224
6225 if (problem) {
6226 if (fix_problem(ctx, problem, &cd->pctx)) {
6227 dirent->inode = 0;
6228 dir_modified++;
6229 goto next;
6230 } else {
6231 ext2fs_unmark_valid(fs);
6232 if (problem == PR_2_BAD_INO)
6233 goto next;
6234 }
6235 }
6236
6237 /*
6238 * If the inode was marked as having bad fields in
6239 * pass1, process it and offer to fix/clear it.
6240 * (We wait until now so that we can display the
6241 * pathname to the user.)
6242 */
6243 if (ctx->inode_bad_map &&
6244 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
6245 dirent->inode)) {
6246 if (e2fsck_process_bad_inode(ctx, ino,
6247 dirent->inode,
6248 buf + fs->blocksize)) {
6249 dirent->inode = 0;
6250 dir_modified++;
6251 goto next;
6252 }
6253 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6254 return DIRENT_ABORT;
6255 }
6256
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006257 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006258 dir_modified++;
6259
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006260 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006261 dir_modified++;
6262
6263#ifdef ENABLE_HTREE
6264 if (dx_db) {
6265 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
6266 (dirent->name_len & 0xFF),
6267 fs->super->s_hash_seed, &hash, 0);
6268 if (hash < dx_db->min_hash)
6269 dx_db->min_hash = hash;
6270 if (hash > dx_db->max_hash)
6271 dx_db->max_hash = hash;
6272 }
6273#endif
6274
6275 /*
6276 * If this is a directory, then mark its parent in its
6277 * dir_info structure. If the parent field is already
6278 * filled in, then this directory has more than one
6279 * hard link. We assume the first link is correct,
6280 * and ask the user if he/she wants to clear this one.
6281 */
6282 if ((dot_state > 1) &&
6283 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6284 dirent->inode))) {
6285 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
6286 if (!subdir) {
6287 cd->pctx.ino = dirent->inode;
6288 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6289 goto abort_free_dict;
6290 }
6291 if (subdir->parent) {
6292 cd->pctx.ino2 = subdir->parent;
6293 if (fix_problem(ctx, PR_2_LINK_DIR,
6294 &cd->pctx)) {
6295 dirent->inode = 0;
6296 dir_modified++;
6297 goto next;
6298 }
6299 cd->pctx.ino2 = 0;
6300 } else
6301 subdir->parent = ino;
6302 }
6303
6304 if (dups_found) {
6305 ;
6306 } else if (dict_lookup(&de_dict, dirent)) {
6307 clear_problem_context(&pctx);
6308 pctx.ino = ino;
6309 pctx.dirent = dirent;
6310 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
6311 if (!ctx->dirs_to_hash)
6312 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
6313 if (ctx->dirs_to_hash)
6314 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6315 dups_found++;
6316 } else
6317 dict_alloc_insert(&de_dict, dirent, dirent);
6318
6319 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
6320 &links);
6321 if (links > 1)
6322 ctx->fs_links_count++;
6323 ctx->fs_total_count++;
6324 next:
6325 prev = dirent;
6326 offset += dirent->rec_len;
6327 dot_state++;
6328 } while (offset < fs->blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006329#ifdef ENABLE_HTREE
6330 if (dx_db) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006331 cd->pctx.dir = cd->pctx.ino;
6332 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
6333 (dx_db->type == DX_DIRBLOCK_NODE))
6334 parse_int_node(fs, db, cd, dx_dir, buf);
6335 }
6336#endif /* ENABLE_HTREE */
6337 if (offset != fs->blocksize) {
6338 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
6339 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
6340 dirent->rec_len = cd->pctx.num;
6341 dir_modified++;
6342 }
6343 }
6344 if (dir_modified) {
6345 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
6346 if (cd->pctx.errcode) {
6347 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
6348 &cd->pctx))
6349 goto abort_free_dict;
6350 }
6351 ext2fs_mark_changed(fs);
6352 }
6353 dict_free_nodes(&de_dict);
6354 return 0;
6355abort_free_dict:
6356 dict_free_nodes(&de_dict);
6357 ctx->flags |= E2F_FLAG_ABORT;
6358 return DIRENT_ABORT;
6359}
6360
6361/*
6362 * This function is called to deallocate a block, and is an interator
6363 * functioned called by deallocate inode via ext2fs_iterate_block().
6364 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006365static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006366 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6367 blk_t ref_block FSCK_ATTR((unused)),
6368 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006369 void *priv_data)
6370{
6371 e2fsck_t ctx = (e2fsck_t) priv_data;
6372
6373 if (HOLE_BLKADDR(*block_nr))
6374 return 0;
6375 if ((*block_nr < fs->super->s_first_data_block) ||
6376 (*block_nr >= fs->super->s_blocks_count))
6377 return 0;
6378 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6379 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6380 return 0;
6381}
6382
6383/*
6384 * This fuction deallocates an inode
6385 */
6386static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
6387{
6388 ext2_filsys fs = ctx->fs;
6389 struct ext2_inode inode;
6390 struct problem_context pctx;
6391 __u32 count;
6392
6393 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
6394 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
6395 inode.i_links_count = 0;
6396 inode.i_dtime = time(0);
6397 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
6398 clear_problem_context(&pctx);
6399 pctx.ino = ino;
6400
6401 /*
6402 * Fix up the bitmaps...
6403 */
6404 e2fsck_read_bitmaps(ctx);
6405 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6406 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6407 if (ctx->inode_bad_map)
6408 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6409 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6410
6411 if (inode.i_file_acl &&
6412 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6413 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6414 block_buf, -1, &count);
6415 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6416 pctx.errcode = 0;
6417 count = 1;
6418 }
6419 if (pctx.errcode) {
6420 pctx.blk = inode.i_file_acl;
6421 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
6422 ctx->flags |= E2F_FLAG_ABORT;
6423 return;
6424 }
6425 if (count == 0) {
6426 ext2fs_unmark_block_bitmap(ctx->block_found_map,
6427 inode.i_file_acl);
6428 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
6429 }
6430 inode.i_file_acl = 0;
6431 }
6432
6433 if (!ext2fs_inode_has_valid_blocks(&inode))
6434 return;
6435
6436 if (LINUX_S_ISREG(inode.i_mode) &&
6437 (inode.i_size_high || inode.i_size & 0x80000000UL))
6438 ctx->large_files--;
6439
6440 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6441 deallocate_inode_block, ctx);
6442 if (pctx.errcode) {
6443 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
6444 ctx->flags |= E2F_FLAG_ABORT;
6445 return;
6446 }
6447}
6448
6449/*
6450 * This fuction clears the htree flag on an inode
6451 */
6452static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
6453{
6454 struct ext2_inode inode;
6455
6456 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
6457 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
6458 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
6459 if (ctx->dirs_to_hash)
6460 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6461}
6462
6463
6464static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
6465 ext2_ino_t ino, char *buf)
6466{
6467 ext2_filsys fs = ctx->fs;
6468 struct ext2_inode inode;
6469 int inode_modified = 0;
6470 int not_fixed = 0;
6471 unsigned char *frag, *fsize;
6472 struct problem_context pctx;
6473 int problem = 0;
6474
6475 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
6476
6477 clear_problem_context(&pctx);
6478 pctx.ino = ino;
6479 pctx.dir = dir;
6480 pctx.inode = &inode;
6481
6482 if (inode.i_file_acl &&
6483 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
6484 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
6485 inode.i_file_acl = 0;
Rob Landley7c94bed2006-05-03 21:58:45 +00006486#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006487 /*
6488 * This is a special kludge to deal with long symlinks
6489 * on big endian systems. i_blocks had already been
6490 * decremented earlier in pass 1, but since i_file_acl
6491 * hadn't yet been cleared, ext2fs_read_inode()
6492 * assumed that the file was short symlink and would
6493 * not have byte swapped i_block[0]. Hence, we have
6494 * to byte-swap it here.
6495 */
6496 if (LINUX_S_ISLNK(inode.i_mode) &&
6497 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
6498 (inode.i_blocks == fs->blocksize >> 9))
6499 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
6500#endif
6501 inode_modified++;
6502 } else
6503 not_fixed++;
6504
6505 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
6506 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
6507 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
6508 !(LINUX_S_ISSOCK(inode.i_mode)))
6509 problem = PR_2_BAD_MODE;
6510 else if (LINUX_S_ISCHR(inode.i_mode)
6511 && !e2fsck_pass1_check_device_inode(fs, &inode))
6512 problem = PR_2_BAD_CHAR_DEV;
6513 else if (LINUX_S_ISBLK(inode.i_mode)
6514 && !e2fsck_pass1_check_device_inode(fs, &inode))
6515 problem = PR_2_BAD_BLOCK_DEV;
6516 else if (LINUX_S_ISFIFO(inode.i_mode)
6517 && !e2fsck_pass1_check_device_inode(fs, &inode))
6518 problem = PR_2_BAD_FIFO;
6519 else if (LINUX_S_ISSOCK(inode.i_mode)
6520 && !e2fsck_pass1_check_device_inode(fs, &inode))
6521 problem = PR_2_BAD_SOCKET;
6522 else if (LINUX_S_ISLNK(inode.i_mode)
6523 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
6524 problem = PR_2_INVALID_SYMLINK;
6525 }
6526
6527 if (problem) {
6528 if (fix_problem(ctx, problem, &pctx)) {
6529 deallocate_inode(ctx, ino, 0);
6530 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6531 return 0;
6532 return 1;
6533 } else
6534 not_fixed++;
6535 problem = 0;
6536 }
6537
6538 if (inode.i_faddr) {
6539 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
6540 inode.i_faddr = 0;
6541 inode_modified++;
6542 } else
6543 not_fixed++;
6544 }
6545
6546 switch (fs->super->s_creator_os) {
6547 case EXT2_OS_LINUX:
6548 frag = &inode.osd2.linux2.l_i_frag;
6549 fsize = &inode.osd2.linux2.l_i_fsize;
6550 break;
6551 case EXT2_OS_HURD:
6552 frag = &inode.osd2.hurd2.h_i_frag;
6553 fsize = &inode.osd2.hurd2.h_i_fsize;
6554 break;
6555 case EXT2_OS_MASIX:
6556 frag = &inode.osd2.masix2.m_i_frag;
6557 fsize = &inode.osd2.masix2.m_i_fsize;
6558 break;
6559 default:
6560 frag = fsize = 0;
6561 }
6562 if (frag && *frag) {
6563 pctx.num = *frag;
6564 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
6565 *frag = 0;
6566 inode_modified++;
6567 } else
6568 not_fixed++;
6569 pctx.num = 0;
6570 }
6571 if (fsize && *fsize) {
6572 pctx.num = *fsize;
6573 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
6574 *fsize = 0;
6575 inode_modified++;
6576 } else
6577 not_fixed++;
6578 pctx.num = 0;
6579 }
6580
6581 if (inode.i_file_acl &&
6582 ((inode.i_file_acl < fs->super->s_first_data_block) ||
6583 (inode.i_file_acl >= fs->super->s_blocks_count))) {
6584 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
6585 inode.i_file_acl = 0;
6586 inode_modified++;
6587 } else
6588 not_fixed++;
6589 }
6590 if (inode.i_dir_acl &&
6591 LINUX_S_ISDIR(inode.i_mode)) {
6592 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
6593 inode.i_dir_acl = 0;
6594 inode_modified++;
6595 } else
6596 not_fixed++;
6597 }
6598
6599 if (inode_modified)
6600 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
6601 if (!not_fixed)
6602 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6603 return 0;
6604}
6605
6606
6607/*
6608 * allocate_dir_block --- this function allocates a new directory
6609 * block for a particular inode; this is done if a directory has
6610 * a "hole" in it, or if a directory has a illegal block number
6611 * that was zeroed out and now needs to be replaced.
6612 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006613static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006614 struct problem_context *pctx)
6615{
6616 ext2_filsys fs = ctx->fs;
6617 blk_t blk;
6618 char *block;
6619 struct ext2_inode inode;
6620
6621 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
6622 return 1;
6623
6624 /*
6625 * Read the inode and block bitmaps in; we'll be messing with
6626 * them.
6627 */
6628 e2fsck_read_bitmaps(ctx);
6629
6630 /*
6631 * First, find a free block
6632 */
6633 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6634 if (pctx->errcode) {
6635 pctx->str = "ext2fs_new_block";
6636 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6637 return 1;
6638 }
6639 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6640 ext2fs_mark_block_bitmap(fs->block_map, blk);
6641 ext2fs_mark_bb_dirty(fs);
6642
6643 /*
6644 * Now let's create the actual data block for the inode
6645 */
6646 if (db->blockcnt)
6647 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
6648 else
6649 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
6650 EXT2_ROOT_INO, &block);
6651
6652 if (pctx->errcode) {
6653 pctx->str = "ext2fs_new_dir_block";
6654 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6655 return 1;
6656 }
6657
6658 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
6659 ext2fs_free_mem(&block);
6660 if (pctx->errcode) {
6661 pctx->str = "ext2fs_write_dir_block";
6662 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6663 return 1;
6664 }
6665
6666 /*
6667 * Update the inode block count
6668 */
6669 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
6670 inode.i_blocks += fs->blocksize / 512;
6671 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
6672 inode.i_size = (db->blockcnt+1) * fs->blocksize;
6673 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
6674
6675 /*
6676 * Finally, update the block pointers for the inode
6677 */
6678 db->blk = blk;
6679 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
6680 0, update_dir_block, db);
6681 if (pctx->errcode) {
6682 pctx->str = "ext2fs_block_iterate";
6683 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6684 return 1;
6685 }
6686
6687 return 0;
6688}
6689
6690/*
6691 * This is a helper function for allocate_dir_block().
6692 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006693static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006694 blk_t *block_nr,
6695 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006696 blk_t ref_block FSCK_ATTR((unused)),
6697 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006698 void *priv_data)
6699{
6700 struct ext2_db_entry *db;
6701
6702 db = (struct ext2_db_entry *) priv_data;
6703 if (db->blockcnt == (int) blockcnt) {
6704 *block_nr = db->blk;
6705 return BLOCK_CHANGED;
6706 }
6707 return 0;
6708}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006709
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006710/*
6711 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
6712 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006713 * Pass #3 assures that all directories are connected to the
6714 * filesystem tree, using the following algorithm:
6715 *
6716 * First, the root directory is checked to make sure it exists; if
6717 * not, e2fsck will offer to create a new one. It is then marked as
6718 * "done".
6719 *
6720 * Then, pass3 interates over all directory inodes; for each directory
6721 * it attempts to trace up the filesystem tree, using dirinfo.parent
6722 * until it reaches a directory which has been marked "done". If it
6723 * can not do so, then the directory must be disconnected, and e2fsck
6724 * will offer to reconnect it to /lost+found. While it is chasing
6725 * parent pointers up the filesystem tree, if pass3 sees a directory
6726 * twice, then it has detected a filesystem loop, and it will again
6727 * offer to reconnect the directory to /lost+found in to break the
6728 * filesystem loop.
6729 *
6730 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
6731 * reconnect inodes to /lost+found; this subroutine is also used by
6732 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
6733 * is responsible for creating /lost+found if it does not exist.
6734 *
6735 * Pass 3 frees the following data structures:
6736 * - The dirinfo directory information cache.
6737 */
6738
6739static void check_root(e2fsck_t ctx);
6740static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6741 struct problem_context *pctx);
6742static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
6743
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006744static ext2fs_inode_bitmap inode_loop_detect;
6745static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006746
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006747static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006748{
6749 ext2_filsys fs = ctx->fs;
6750 int i;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006751 struct problem_context pctx;
6752 struct dir_info *dir;
6753 unsigned long maxdirs, count;
6754
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006755 clear_problem_context(&pctx);
6756
Rob Landley3e72c592006-04-06 22:49:04 +00006757 /* Pass 3 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006758
6759 if (!(ctx->options & E2F_OPT_PREEN))
6760 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
6761
6762 /*
6763 * Allocate some bitmaps to do loop detection.
6764 */
6765 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
6766 &inode_done_map);
6767 if (pctx.errcode) {
6768 pctx.num = 2;
6769 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
6770 ctx->flags |= E2F_FLAG_ABORT;
6771 goto abort_exit;
6772 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006773 check_root(ctx);
6774 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6775 goto abort_exit;
6776
6777 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
6778
6779 maxdirs = e2fsck_get_num_dirinfo(ctx);
6780 count = 1;
6781
6782 if (ctx->progress)
6783 if ((ctx->progress)(ctx, 3, 0, maxdirs))
6784 goto abort_exit;
6785
6786 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
6787 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6788 goto abort_exit;
6789 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
6790 goto abort_exit;
6791 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
6792 if (check_directory(ctx, dir, &pctx))
6793 goto abort_exit;
6794 }
6795
6796 /*
6797 * Force the creation of /lost+found if not present
6798 */
6799 if ((ctx->flags & E2F_OPT_READONLY) == 0)
6800 e2fsck_get_lost_and_found(ctx, 1);
6801
6802 /*
6803 * If there are any directories that need to be indexed or
6804 * optimized, do it here.
6805 */
6806 e2fsck_rehash_directories(ctx);
6807
6808abort_exit:
6809 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00006810 ext2fs_free_inode_bitmap(inode_loop_detect);
6811 inode_loop_detect = 0;
6812 ext2fs_free_inode_bitmap(inode_done_map);
6813 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006814}
6815
6816/*
6817 * This makes sure the root inode is present; if not, we ask if the
6818 * user wants us to create it. Not creating it is a fatal error.
6819 */
6820static void check_root(e2fsck_t ctx)
6821{
6822 ext2_filsys fs = ctx->fs;
6823 blk_t blk;
6824 struct ext2_inode inode;
6825 char * block;
6826 struct problem_context pctx;
6827
6828 clear_problem_context(&pctx);
6829
6830 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
6831 /*
6832 * If the root inode is not a directory, die here. The
6833 * user must have answered 'no' in pass1 when we
6834 * offered to clear it.
6835 */
6836 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6837 EXT2_ROOT_INO))) {
6838 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
6839 ctx->flags |= E2F_FLAG_ABORT;
6840 }
6841 return;
6842 }
6843
6844 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
6845 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
6846 ctx->flags |= E2F_FLAG_ABORT;
6847 return;
6848 }
6849
6850 e2fsck_read_bitmaps(ctx);
6851
6852 /*
6853 * First, find a free block
6854 */
6855 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6856 if (pctx.errcode) {
6857 pctx.str = "ext2fs_new_block";
6858 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6859 ctx->flags |= E2F_FLAG_ABORT;
6860 return;
6861 }
6862 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6863 ext2fs_mark_block_bitmap(fs->block_map, blk);
6864 ext2fs_mark_bb_dirty(fs);
6865
6866 /*
6867 * Now let's create the actual data block for the inode
6868 */
6869 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
6870 &block);
6871 if (pctx.errcode) {
6872 pctx.str = "ext2fs_new_dir_block";
6873 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6874 ctx->flags |= E2F_FLAG_ABORT;
6875 return;
6876 }
6877
6878 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
6879 if (pctx.errcode) {
6880 pctx.str = "ext2fs_write_dir_block";
6881 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6882 ctx->flags |= E2F_FLAG_ABORT;
6883 return;
6884 }
6885 ext2fs_free_mem(&block);
6886
6887 /*
6888 * Set up the inode structure
6889 */
6890 memset(&inode, 0, sizeof(inode));
6891 inode.i_mode = 040755;
6892 inode.i_size = fs->blocksize;
6893 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
6894 inode.i_links_count = 2;
6895 inode.i_blocks = fs->blocksize / 512;
6896 inode.i_block[0] = blk;
6897
6898 /*
6899 * Write out the inode.
6900 */
6901 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
6902 if (pctx.errcode) {
6903 pctx.str = "ext2fs_write_inode";
6904 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6905 ctx->flags |= E2F_FLAG_ABORT;
6906 return;
6907 }
6908
6909 /*
6910 * Miscellaneous bookkeeping...
6911 */
6912 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
6913 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
6914 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
6915
6916 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
6917 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
6918 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
6919 ext2fs_mark_ib_dirty(fs);
6920}
6921
6922/*
6923 * This subroutine is responsible for making sure that a particular
6924 * directory is connected to the root; if it isn't we trace it up as
6925 * far as we can go, and then offer to connect the resulting parent to
6926 * the lost+found. We have to do loop detection; if we ever discover
6927 * a loop, we treat that as a disconnected directory and offer to
6928 * reparent it to lost+found.
6929 *
6930 * However, loop detection is expensive, because for very large
6931 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
6932 * is non-trivial. Loops in filesystems are also a rare error case,
6933 * and we shouldn't optimize for error cases. So we try two passes of
6934 * the algorithm. The first time, we ignore loop detection and merely
6935 * increment a counter; if the counter exceeds some extreme threshold,
6936 * then we try again with the loop detection bitmap enabled.
6937 */
6938static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6939 struct problem_context *pctx)
6940{
6941 ext2_filsys fs = ctx->fs;
6942 struct dir_info *p = dir;
6943 int loop_pass = 0, parent_count = 0;
6944
6945 if (!p)
6946 return 0;
6947
6948 while (1) {
6949 /*
6950 * Mark this inode as being "done"; by the time we
6951 * return from this function, the inode we either be
6952 * verified as being connected to the directory tree,
6953 * or we will have offered to reconnect this to
6954 * lost+found.
6955 *
6956 * If it was marked done already, then we've reached a
6957 * parent we've already checked.
6958 */
6959 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
6960 break;
6961
6962 /*
6963 * If this directory doesn't have a parent, or we've
6964 * seen the parent once already, then offer to
6965 * reparent it to lost+found
6966 */
6967 if (!p->parent ||
6968 (loop_pass &&
6969 (ext2fs_test_inode_bitmap(inode_loop_detect,
6970 p->parent)))) {
6971 pctx->ino = p->ino;
6972 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
6973 if (e2fsck_reconnect_file(ctx, pctx->ino))
6974 ext2fs_unmark_valid(fs);
6975 else {
6976 p = e2fsck_get_dir_info(ctx, pctx->ino);
6977 p->parent = ctx->lost_and_found;
6978 fix_dotdot(ctx, p, ctx->lost_and_found);
6979 }
6980 }
6981 break;
6982 }
6983 p = e2fsck_get_dir_info(ctx, p->parent);
6984 if (!p) {
6985 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
6986 return 0;
6987 }
6988 if (loop_pass) {
6989 ext2fs_mark_inode_bitmap(inode_loop_detect,
6990 p->ino);
6991 } else if (parent_count++ > 2048) {
6992 /*
6993 * If we've run into a path depth that's
6994 * greater than 2048, try again with the inode
6995 * loop bitmap turned on and start from the
6996 * top.
6997 */
6998 loop_pass = 1;
6999 if (inode_loop_detect)
7000 ext2fs_clear_inode_bitmap(inode_loop_detect);
7001 else {
7002 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
7003 if (pctx->errcode) {
7004 pctx->num = 1;
7005 fix_problem(ctx,
7006 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
7007 ctx->flags |= E2F_FLAG_ABORT;
7008 return -1;
7009 }
7010 }
7011 p = dir;
7012 }
7013 }
7014
7015 /*
7016 * Make sure that .. and the parent directory are the same;
7017 * offer to fix it if not.
7018 */
7019 if (dir->parent != dir->dotdot) {
7020 pctx->ino = dir->ino;
7021 pctx->ino2 = dir->dotdot;
7022 pctx->dir = dir->parent;
7023 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
7024 fix_dotdot(ctx, dir, dir->parent);
7025 }
7026 return 0;
7027}
7028
7029/*
7030 * This routine gets the lost_and_found inode, making it a directory
7031 * if necessary
7032 */
7033ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
7034{
7035 ext2_filsys fs = ctx->fs;
7036 ext2_ino_t ino;
7037 blk_t blk;
7038 errcode_t retval;
7039 struct ext2_inode inode;
7040 char * block;
7041 static const char name[] = "lost+found";
7042 struct problem_context pctx;
7043 struct dir_info *dirinfo;
7044
7045 if (ctx->lost_and_found)
7046 return ctx->lost_and_found;
7047
7048 clear_problem_context(&pctx);
7049
7050 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
7051 sizeof(name)-1, 0, &ino);
7052 if (retval && !fix)
7053 return 0;
7054 if (!retval) {
7055 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
7056 ctx->lost_and_found = ino;
7057 return ino;
7058 }
7059
7060 /* Lost+found isn't a directory! */
7061 if (!fix)
7062 return 0;
7063 pctx.ino = ino;
7064 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
7065 return 0;
7066
7067 /* OK, unlink the old /lost+found file. */
7068 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
7069 if (pctx.errcode) {
7070 pctx.str = "ext2fs_unlink";
7071 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7072 return 0;
7073 }
7074 dirinfo = e2fsck_get_dir_info(ctx, ino);
7075 if (dirinfo)
7076 dirinfo->parent = 0;
7077 e2fsck_adjust_inode_count(ctx, ino, -1);
7078 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
7079 pctx.errcode = retval;
7080 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
7081 }
7082 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
7083 return 0;
7084
7085 /*
7086 * Read the inode and block bitmaps in; we'll be messing with
7087 * them.
7088 */
7089 e2fsck_read_bitmaps(ctx);
7090
7091 /*
7092 * First, find a free block
7093 */
7094 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7095 if (retval) {
7096 pctx.errcode = retval;
7097 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
7098 return 0;
7099 }
7100 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7101 ext2fs_block_alloc_stats(fs, blk, +1);
7102
7103 /*
7104 * Next find a free inode.
7105 */
7106 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
7107 ctx->inode_used_map, &ino);
7108 if (retval) {
7109 pctx.errcode = retval;
7110 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
7111 return 0;
7112 }
7113 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
7114 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
7115 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
7116
7117 /*
7118 * Now let's create the actual data block for the inode
7119 */
7120 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
7121 if (retval) {
7122 pctx.errcode = retval;
7123 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
7124 return 0;
7125 }
7126
7127 retval = ext2fs_write_dir_block(fs, blk, block);
7128 ext2fs_free_mem(&block);
7129 if (retval) {
7130 pctx.errcode = retval;
7131 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
7132 return 0;
7133 }
7134
7135 /*
7136 * Set up the inode structure
7137 */
7138 memset(&inode, 0, sizeof(inode));
7139 inode.i_mode = 040700;
7140 inode.i_size = fs->blocksize;
7141 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
7142 inode.i_links_count = 2;
7143 inode.i_blocks = fs->blocksize / 512;
7144 inode.i_block[0] = blk;
7145
7146 /*
7147 * Next, write out the inode.
7148 */
7149 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
7150 if (pctx.errcode) {
7151 pctx.str = "ext2fs_write_inode";
7152 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7153 return 0;
7154 }
7155 /*
7156 * Finally, create the directory link
7157 */
7158 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
7159 if (pctx.errcode) {
7160 pctx.str = "ext2fs_link";
7161 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7162 return 0;
7163 }
7164
7165 /*
7166 * Miscellaneous bookkeeping that needs to be kept straight.
7167 */
7168 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
7169 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
7170 ext2fs_icount_store(ctx->inode_count, ino, 2);
7171 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
7172 ctx->lost_and_found = ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007173 return ino;
7174}
7175
7176/*
7177 * This routine will connect a file to lost+found
7178 */
7179int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
7180{
7181 ext2_filsys fs = ctx->fs;
7182 errcode_t retval;
7183 char name[80];
7184 struct problem_context pctx;
7185 struct ext2_inode inode;
7186 int file_type = 0;
7187
7188 clear_problem_context(&pctx);
7189 pctx.ino = ino;
7190
7191 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
7192 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
7193 ctx->bad_lost_and_found++;
7194 }
7195 if (ctx->bad_lost_and_found) {
7196 fix_problem(ctx, PR_3_NO_LPF, &pctx);
7197 return 1;
7198 }
7199
7200 sprintf(name, "#%u", ino);
7201 if (ext2fs_read_inode(fs, ino, &inode) == 0)
7202 file_type = ext2_file_type(inode.i_mode);
7203 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
7204 if (retval == EXT2_ET_DIR_NO_SPACE) {
7205 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
7206 return 1;
7207 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
7208 1, 0);
7209 if (retval) {
7210 pctx.errcode = retval;
7211 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
7212 return 1;
7213 }
7214 retval = ext2fs_link(fs, ctx->lost_and_found, name,
7215 ino, file_type);
7216 }
7217 if (retval) {
7218 pctx.errcode = retval;
7219 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
7220 return 1;
7221 }
7222 e2fsck_adjust_inode_count(ctx, ino, 1);
7223
7224 return 0;
7225}
7226
7227/*
7228 * Utility routine to adjust the inode counts on an inode.
7229 */
7230errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
7231{
7232 ext2_filsys fs = ctx->fs;
7233 errcode_t retval;
7234 struct ext2_inode inode;
7235
7236 if (!ino)
7237 return 0;
7238
7239 retval = ext2fs_read_inode(fs, ino, &inode);
7240 if (retval)
7241 return retval;
7242
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007243 if (adj == 1) {
7244 ext2fs_icount_increment(ctx->inode_count, ino, 0);
7245 if (inode.i_links_count == (__u16) ~0)
7246 return 0;
7247 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
7248 inode.i_links_count++;
7249 } else if (adj == -1) {
7250 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
7251 if (inode.i_links_count == 0)
7252 return 0;
7253 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
7254 inode.i_links_count--;
7255 }
7256
7257 retval = ext2fs_write_inode(fs, ino, &inode);
7258 if (retval)
7259 return retval;
7260
7261 return 0;
7262}
7263
7264/*
7265 * Fix parent --- this routine fixes up the parent of a directory.
7266 */
7267struct fix_dotdot_struct {
7268 ext2_filsys fs;
7269 ext2_ino_t parent;
7270 int done;
7271 e2fsck_t ctx;
7272};
7273
7274static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007275 int offset FSCK_ATTR((unused)),
7276 int blocksize FSCK_ATTR((unused)),
7277 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007278 void *priv_data)
7279{
7280 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
7281 errcode_t retval;
7282 struct problem_context pctx;
7283
7284 if ((dirent->name_len & 0xFF) != 2)
7285 return 0;
7286 if (strncmp(dirent->name, "..", 2))
7287 return 0;
7288
7289 clear_problem_context(&pctx);
7290
7291 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
7292 if (retval) {
7293 pctx.errcode = retval;
7294 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7295 }
7296 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
7297 if (retval) {
7298 pctx.errcode = retval;
7299 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7300 }
7301 dirent->inode = fp->parent;
7302
7303 fp->done++;
7304 return DIRENT_ABORT | DIRENT_CHANGED;
7305}
7306
7307static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
7308{
7309 ext2_filsys fs = ctx->fs;
7310 errcode_t retval;
7311 struct fix_dotdot_struct fp;
7312 struct problem_context pctx;
7313
7314 fp.fs = fs;
7315 fp.parent = parent;
7316 fp.done = 0;
7317 fp.ctx = ctx;
7318
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007319 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
7320 0, fix_dotdot_proc, &fp);
7321 if (retval || !fp.done) {
7322 clear_problem_context(&pctx);
7323 pctx.ino = dir->ino;
7324 pctx.errcode = retval;
7325 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
7326 PR_3_FIX_PARENT_NOFIND, &pctx);
7327 ext2fs_unmark_valid(fs);
7328 }
7329 dir->dotdot = parent;
7330
7331 return;
7332}
7333
7334/*
7335 * These routines are responsible for expanding a /lost+found if it is
7336 * too small.
7337 */
7338
7339struct expand_dir_struct {
7340 int num;
7341 int guaranteed_size;
7342 int newblocks;
7343 int last_block;
7344 errcode_t err;
7345 e2fsck_t ctx;
7346};
7347
7348static int expand_dir_proc(ext2_filsys fs,
7349 blk_t *blocknr,
7350 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007351 blk_t ref_block FSCK_ATTR((unused)),
7352 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007353 void *priv_data)
7354{
7355 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
7356 blk_t new_blk;
7357 static blk_t last_blk = 0;
7358 char *block;
7359 errcode_t retval;
7360 e2fsck_t ctx;
7361
7362 ctx = es->ctx;
7363
7364 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
7365 return BLOCK_ABORT;
7366
7367 if (blockcnt > 0)
7368 es->last_block = blockcnt;
7369 if (*blocknr) {
7370 last_blk = *blocknr;
7371 return 0;
7372 }
7373 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
7374 &new_blk);
7375 if (retval) {
7376 es->err = retval;
7377 return BLOCK_ABORT;
7378 }
7379 if (blockcnt > 0) {
7380 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
7381 if (retval) {
7382 es->err = retval;
7383 return BLOCK_ABORT;
7384 }
7385 es->num--;
7386 retval = ext2fs_write_dir_block(fs, new_blk, block);
7387 } else {
7388 retval = ext2fs_get_mem(fs->blocksize, &block);
7389 if (retval) {
7390 es->err = retval;
7391 return BLOCK_ABORT;
7392 }
7393 memset(block, 0, fs->blocksize);
7394 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
7395 }
7396 if (retval) {
7397 es->err = retval;
7398 return BLOCK_ABORT;
7399 }
7400 ext2fs_free_mem(&block);
7401 *blocknr = new_blk;
7402 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
7403 ext2fs_block_alloc_stats(fs, new_blk, +1);
7404 es->newblocks++;
7405
7406 if (es->num == 0)
7407 return (BLOCK_CHANGED | BLOCK_ABORT);
7408 else
7409 return BLOCK_CHANGED;
7410}
7411
7412errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
7413 int num, int guaranteed_size)
7414{
7415 ext2_filsys fs = ctx->fs;
7416 errcode_t retval;
7417 struct expand_dir_struct es;
7418 struct ext2_inode inode;
7419
7420 if (!(fs->flags & EXT2_FLAG_RW))
7421 return EXT2_ET_RO_FILSYS;
7422
7423 /*
7424 * Read the inode and block bitmaps in; we'll be messing with
7425 * them.
7426 */
7427 e2fsck_read_bitmaps(ctx);
7428
7429 retval = ext2fs_check_directory(fs, dir);
7430 if (retval)
7431 return retval;
7432
7433 es.num = num;
7434 es.guaranteed_size = guaranteed_size;
7435 es.last_block = 0;
7436 es.err = 0;
7437 es.newblocks = 0;
7438 es.ctx = ctx;
7439
7440 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
7441 0, expand_dir_proc, &es);
7442
7443 if (es.err)
7444 return es.err;
7445
7446 /*
7447 * Update the size and block count fields in the inode.
7448 */
7449 retval = ext2fs_read_inode(fs, dir, &inode);
7450 if (retval)
7451 return retval;
7452
7453 inode.i_size = (es.last_block + 1) * fs->blocksize;
7454 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
7455
7456 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
7457
7458 return 0;
7459}
7460
7461/*
7462 * pass4.c -- pass #4 of e2fsck: Check reference counts
7463 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007464 * Pass 4 frees the following data structures:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007465 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
7466 */
7467
7468/*
7469 * This routine is called when an inode is not connected to the
7470 * directory tree.
7471 *
7472 * This subroutine returns 1 then the caller shouldn't bother with the
7473 * rest of the pass 4 tests.
7474 */
7475static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
7476{
7477 ext2_filsys fs = ctx->fs;
7478 struct ext2_inode inode;
7479 struct problem_context pctx;
7480
7481 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
7482 clear_problem_context(&pctx);
7483 pctx.ino = i;
7484 pctx.inode = &inode;
7485
7486 /*
7487 * Offer to delete any zero-length files that does not have
7488 * blocks. If there is an EA block, it might have useful
7489 * information, so we won't prompt to delete it, but let it be
7490 * reconnected to lost+found.
7491 */
7492 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
7493 LINUX_S_ISDIR(inode.i_mode))) {
7494 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
7495 ext2fs_icount_store(ctx->inode_link_info, i, 0);
7496 inode.i_links_count = 0;
7497 inode.i_dtime = time(0);
7498 e2fsck_write_inode(ctx, i, &inode,
7499 "disconnect_inode");
7500 /*
7501 * Fix up the bitmaps...
7502 */
7503 e2fsck_read_bitmaps(ctx);
7504 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
7505 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
7506 ext2fs_inode_alloc_stats2(fs, i, -1,
7507 LINUX_S_ISDIR(inode.i_mode));
7508 return 0;
7509 }
7510 }
7511
7512 /*
7513 * Prompt to reconnect.
7514 */
7515 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
7516 if (e2fsck_reconnect_file(ctx, i))
7517 ext2fs_unmark_valid(fs);
7518 } else {
7519 /*
7520 * If we don't attach the inode, then skip the
7521 * i_links_test since there's no point in trying to
7522 * force i_links_count to zero.
7523 */
7524 ext2fs_unmark_valid(fs);
7525 return 1;
7526 }
7527 return 0;
7528}
7529
7530
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007531static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007532{
7533 ext2_filsys fs = ctx->fs;
7534 ext2_ino_t i;
7535 struct ext2_inode inode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007536 struct problem_context pctx;
7537 __u16 link_count, link_counted;
7538 char *buf = 0;
7539 int group, maxgroup;
7540
Rob Landley3e72c592006-04-06 22:49:04 +00007541 /* Pass 4 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007542
7543 clear_problem_context(&pctx);
7544
7545 if (!(ctx->options & E2F_OPT_PREEN))
7546 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
7547
7548 group = 0;
7549 maxgroup = fs->group_desc_count;
7550 if (ctx->progress)
7551 if ((ctx->progress)(ctx, 4, 0, maxgroup))
7552 return;
7553
7554 for (i=1; i <= fs->super->s_inodes_count; i++) {
7555 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7556 return;
7557 if ((i % fs->super->s_inodes_per_group) == 0) {
7558 group++;
7559 if (ctx->progress)
7560 if ((ctx->progress)(ctx, 4, group, maxgroup))
7561 return;
7562 }
7563 if (i == EXT2_BAD_INO ||
7564 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
7565 continue;
7566 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
7567 (ctx->inode_imagic_map &&
Rob Landleyd8f66012006-05-05 17:29:09 +00007568 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007569 continue;
7570 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
7571 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
7572 if (link_counted == 0) {
7573 if (!buf)
7574 buf = e2fsck_allocate_memory(ctx,
7575 fs->blocksize, "bad_inode buffer");
7576 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
7577 continue;
7578 if (disconnect_inode(ctx, i))
7579 continue;
7580 ext2fs_icount_fetch(ctx->inode_link_info, i,
7581 &link_count);
7582 ext2fs_icount_fetch(ctx->inode_count, i,
7583 &link_counted);
7584 }
7585 if (link_counted != link_count) {
7586 e2fsck_read_inode(ctx, i, &inode, "pass4");
7587 pctx.ino = i;
7588 pctx.inode = &inode;
7589 if (link_count != inode.i_links_count) {
7590 pctx.num = link_count;
7591 fix_problem(ctx,
7592 PR_4_INCONSISTENT_COUNT, &pctx);
7593 }
7594 pctx.num = link_counted;
7595 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
7596 inode.i_links_count = link_counted;
7597 e2fsck_write_inode(ctx, i, &inode, "pass4");
7598 }
7599 }
7600 }
7601 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
7602 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007603 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
7604 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00007605 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007606}
7607
7608/*
7609 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007610 */
7611
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007612#define NO_BLK ((blk_t) -1)
7613
7614static void print_bitmap_problem(e2fsck_t ctx, int problem,
7615 struct problem_context *pctx)
7616{
7617 switch (problem) {
7618 case PR_5_BLOCK_UNUSED:
7619 if (pctx->blk == pctx->blk2)
7620 pctx->blk2 = 0;
7621 else
7622 problem = PR_5_BLOCK_RANGE_UNUSED;
7623 break;
7624 case PR_5_BLOCK_USED:
7625 if (pctx->blk == pctx->blk2)
7626 pctx->blk2 = 0;
7627 else
7628 problem = PR_5_BLOCK_RANGE_USED;
7629 break;
7630 case PR_5_INODE_UNUSED:
7631 if (pctx->ino == pctx->ino2)
7632 pctx->ino2 = 0;
7633 else
7634 problem = PR_5_INODE_RANGE_UNUSED;
7635 break;
7636 case PR_5_INODE_USED:
7637 if (pctx->ino == pctx->ino2)
7638 pctx->ino2 = 0;
7639 else
7640 problem = PR_5_INODE_RANGE_USED;
7641 break;
7642 }
7643 fix_problem(ctx, problem, pctx);
7644 pctx->blk = pctx->blk2 = NO_BLK;
7645 pctx->ino = pctx->ino2 = 0;
7646}
7647
7648static void check_block_bitmaps(e2fsck_t ctx)
7649{
7650 ext2_filsys fs = ctx->fs;
7651 blk_t i;
7652 int *free_array;
7653 int group = 0;
7654 unsigned int blocks = 0;
7655 unsigned int free_blocks = 0;
7656 int group_free = 0;
7657 int actual, bitmap;
7658 struct problem_context pctx;
7659 int problem, save_problem, fixit, had_problem;
7660 errcode_t retval;
7661
7662 clear_problem_context(&pctx);
7663 free_array = (int *) e2fsck_allocate_memory(ctx,
7664 fs->group_desc_count * sizeof(int), "free block count array");
7665
7666 if ((fs->super->s_first_data_block <
7667 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
7668 (fs->super->s_blocks_count-1 >
7669 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
7670 pctx.num = 1;
7671 pctx.blk = fs->super->s_first_data_block;
7672 pctx.blk2 = fs->super->s_blocks_count -1;
7673 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
7674 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
7675 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7676
7677 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7678 return;
7679 }
7680
7681 if ((fs->super->s_first_data_block <
7682 ext2fs_get_block_bitmap_start(fs->block_map)) ||
7683 (fs->super->s_blocks_count-1 >
7684 ext2fs_get_block_bitmap_end(fs->block_map))) {
7685 pctx.num = 2;
7686 pctx.blk = fs->super->s_first_data_block;
7687 pctx.blk2 = fs->super->s_blocks_count -1;
7688 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
7689 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
7690 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7691
7692 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7693 return;
7694 }
7695
7696redo_counts:
7697 had_problem = 0;
7698 save_problem = 0;
7699 pctx.blk = pctx.blk2 = NO_BLK;
7700 for (i = fs->super->s_first_data_block;
7701 i < fs->super->s_blocks_count;
7702 i++) {
7703 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
7704 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
7705
7706 if (actual == bitmap)
7707 goto do_counts;
7708
7709 if (!actual && bitmap) {
7710 /*
7711 * Block not used, but marked in use in the bitmap.
7712 */
7713 problem = PR_5_BLOCK_UNUSED;
7714 } else {
7715 /*
7716 * Block used, but not marked in use in the bitmap.
7717 */
7718 problem = PR_5_BLOCK_USED;
7719 }
7720 if (pctx.blk == NO_BLK) {
7721 pctx.blk = pctx.blk2 = i;
7722 save_problem = problem;
7723 } else {
7724 if ((problem == save_problem) &&
7725 (pctx.blk2 == i-1))
7726 pctx.blk2++;
7727 else {
7728 print_bitmap_problem(ctx, save_problem, &pctx);
7729 pctx.blk = pctx.blk2 = i;
7730 save_problem = problem;
7731 }
7732 }
7733 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7734 had_problem++;
7735
7736 do_counts:
7737 if (!bitmap) {
7738 group_free++;
7739 free_blocks++;
7740 }
7741 blocks ++;
7742 if ((blocks == fs->super->s_blocks_per_group) ||
7743 (i == fs->super->s_blocks_count-1)) {
7744 free_array[group] = group_free;
7745 group ++;
7746 blocks = 0;
7747 group_free = 0;
7748 if (ctx->progress)
7749 if ((ctx->progress)(ctx, 5, group,
7750 fs->group_desc_count*2))
7751 return;
7752 }
7753 }
7754 if (pctx.blk != NO_BLK)
7755 print_bitmap_problem(ctx, save_problem, &pctx);
7756 if (had_problem)
7757 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
7758 else
7759 fixit = -1;
7760 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7761
7762 if (fixit == 1) {
7763 ext2fs_free_block_bitmap(fs->block_map);
7764 retval = ext2fs_copy_bitmap(ctx->block_found_map,
7765 &fs->block_map);
7766 if (retval) {
7767 clear_problem_context(&pctx);
7768 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
7769 ctx->flags |= E2F_FLAG_ABORT;
7770 return;
7771 }
7772 ext2fs_set_bitmap_padding(fs->block_map);
7773 ext2fs_mark_bb_dirty(fs);
7774
7775 /* Redo the counts */
7776 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
7777 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7778 goto redo_counts;
7779 } else if (fixit == 0)
7780 ext2fs_unmark_valid(fs);
7781
7782 for (i = 0; i < fs->group_desc_count; i++) {
7783 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
7784 pctx.group = i;
7785 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
7786 pctx.blk2 = free_array[i];
7787
7788 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
7789 &pctx)) {
7790 fs->group_desc[i].bg_free_blocks_count =
7791 free_array[i];
7792 ext2fs_mark_super_dirty(fs);
7793 } else
7794 ext2fs_unmark_valid(fs);
7795 }
7796 }
7797 if (free_blocks != fs->super->s_free_blocks_count) {
7798 pctx.group = 0;
7799 pctx.blk = fs->super->s_free_blocks_count;
7800 pctx.blk2 = free_blocks;
7801
7802 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
7803 fs->super->s_free_blocks_count = free_blocks;
7804 ext2fs_mark_super_dirty(fs);
7805 } else
7806 ext2fs_unmark_valid(fs);
7807 }
7808 ext2fs_free_mem(&free_array);
7809}
7810
7811static void check_inode_bitmaps(e2fsck_t ctx)
7812{
7813 ext2_filsys fs = ctx->fs;
7814 ext2_ino_t i;
7815 unsigned int free_inodes = 0;
7816 int group_free = 0;
7817 int dirs_count = 0;
7818 int group = 0;
7819 unsigned int inodes = 0;
7820 int *free_array;
7821 int *dir_array;
7822 int actual, bitmap;
7823 errcode_t retval;
7824 struct problem_context pctx;
7825 int problem, save_problem, fixit, had_problem;
7826
7827 clear_problem_context(&pctx);
7828 free_array = (int *) e2fsck_allocate_memory(ctx,
7829 fs->group_desc_count * sizeof(int), "free inode count array");
7830
7831 dir_array = (int *) e2fsck_allocate_memory(ctx,
7832 fs->group_desc_count * sizeof(int), "directory count array");
7833
7834 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
7835 (fs->super->s_inodes_count >
7836 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
7837 pctx.num = 3;
7838 pctx.blk = 1;
7839 pctx.blk2 = fs->super->s_inodes_count;
7840 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
7841 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
7842 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7843
7844 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7845 return;
7846 }
7847 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
7848 (fs->super->s_inodes_count >
7849 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
7850 pctx.num = 4;
7851 pctx.blk = 1;
7852 pctx.blk2 = fs->super->s_inodes_count;
7853 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
7854 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
7855 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7856
7857 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7858 return;
7859 }
7860
7861redo_counts:
7862 had_problem = 0;
7863 save_problem = 0;
7864 pctx.ino = pctx.ino2 = 0;
7865 for (i = 1; i <= fs->super->s_inodes_count; i++) {
7866 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
7867 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
7868
7869 if (actual == bitmap)
7870 goto do_counts;
7871
7872 if (!actual && bitmap) {
7873 /*
7874 * Inode wasn't used, but marked in bitmap
7875 */
7876 problem = PR_5_INODE_UNUSED;
7877 } else /* if (actual && !bitmap) */ {
7878 /*
7879 * Inode used, but not in bitmap
7880 */
7881 problem = PR_5_INODE_USED;
7882 }
7883 if (pctx.ino == 0) {
7884 pctx.ino = pctx.ino2 = i;
7885 save_problem = problem;
7886 } else {
7887 if ((problem == save_problem) &&
7888 (pctx.ino2 == i-1))
7889 pctx.ino2++;
7890 else {
7891 print_bitmap_problem(ctx, save_problem, &pctx);
7892 pctx.ino = pctx.ino2 = i;
7893 save_problem = problem;
7894 }
7895 }
7896 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7897 had_problem++;
7898
7899do_counts:
7900 if (!bitmap) {
7901 group_free++;
7902 free_inodes++;
7903 } else {
7904 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
7905 dirs_count++;
7906 }
7907 inodes++;
7908 if ((inodes == fs->super->s_inodes_per_group) ||
7909 (i == fs->super->s_inodes_count)) {
7910 free_array[group] = group_free;
7911 dir_array[group] = dirs_count;
7912 group ++;
7913 inodes = 0;
7914 group_free = 0;
7915 dirs_count = 0;
7916 if (ctx->progress)
7917 if ((ctx->progress)(ctx, 5,
7918 group + fs->group_desc_count,
7919 fs->group_desc_count*2))
7920 return;
7921 }
7922 }
7923 if (pctx.ino)
7924 print_bitmap_problem(ctx, save_problem, &pctx);
7925
7926 if (had_problem)
7927 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
7928 else
7929 fixit = -1;
7930 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7931
7932 if (fixit == 1) {
7933 ext2fs_free_inode_bitmap(fs->inode_map);
7934 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
7935 &fs->inode_map);
7936 if (retval) {
7937 clear_problem_context(&pctx);
7938 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
7939 ctx->flags |= E2F_FLAG_ABORT;
7940 return;
7941 }
7942 ext2fs_set_bitmap_padding(fs->inode_map);
7943 ext2fs_mark_ib_dirty(fs);
7944
7945 /* redo counts */
7946 inodes = 0; free_inodes = 0; group_free = 0;
7947 dirs_count = 0; group = 0;
7948 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7949 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
7950 goto redo_counts;
7951 } else if (fixit == 0)
7952 ext2fs_unmark_valid(fs);
7953
7954 for (i = 0; i < fs->group_desc_count; i++) {
7955 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
7956 pctx.group = i;
7957 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
7958 pctx.ino2 = free_array[i];
7959 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
7960 &pctx)) {
7961 fs->group_desc[i].bg_free_inodes_count =
7962 free_array[i];
7963 ext2fs_mark_super_dirty(fs);
7964 } else
7965 ext2fs_unmark_valid(fs);
7966 }
7967 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
7968 pctx.group = i;
7969 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
7970 pctx.ino2 = dir_array[i];
7971
7972 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
7973 &pctx)) {
7974 fs->group_desc[i].bg_used_dirs_count =
7975 dir_array[i];
7976 ext2fs_mark_super_dirty(fs);
7977 } else
7978 ext2fs_unmark_valid(fs);
7979 }
7980 }
7981 if (free_inodes != fs->super->s_free_inodes_count) {
7982 pctx.group = -1;
7983 pctx.ino = fs->super->s_free_inodes_count;
7984 pctx.ino2 = free_inodes;
7985
7986 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
7987 fs->super->s_free_inodes_count = free_inodes;
7988 ext2fs_mark_super_dirty(fs);
7989 } else
7990 ext2fs_unmark_valid(fs);
7991 }
7992 ext2fs_free_mem(&free_array);
7993 ext2fs_free_mem(&dir_array);
7994}
7995
7996static void check_inode_end(e2fsck_t ctx)
7997{
7998 ext2_filsys fs = ctx->fs;
7999 ext2_ino_t end, save_inodes_count, i;
8000 struct problem_context pctx;
8001
8002 clear_problem_context(&pctx);
8003
8004 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
8005 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
8006 &save_inodes_count);
8007 if (pctx.errcode) {
8008 pctx.num = 1;
8009 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8010 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8011 return;
8012 }
8013 if (save_inodes_count == end)
8014 return;
8015
8016 for (i = save_inodes_count + 1; i <= end; i++) {
8017 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
8018 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
8019 for (i = save_inodes_count + 1; i <= end; i++)
8020 ext2fs_mark_inode_bitmap(fs->inode_map,
8021 i);
8022 ext2fs_mark_ib_dirty(fs);
8023 } else
8024 ext2fs_unmark_valid(fs);
8025 break;
8026 }
8027 }
8028
8029 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
8030 save_inodes_count, 0);
8031 if (pctx.errcode) {
8032 pctx.num = 2;
8033 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8034 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8035 return;
8036 }
8037}
8038
8039static void check_block_end(e2fsck_t ctx)
8040{
8041 ext2_filsys fs = ctx->fs;
8042 blk_t end, save_blocks_count, i;
8043 struct problem_context pctx;
8044
8045 clear_problem_context(&pctx);
8046
8047 end = fs->block_map->start +
8048 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
8049 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
8050 &save_blocks_count);
8051 if (pctx.errcode) {
8052 pctx.num = 3;
8053 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8054 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8055 return;
8056 }
8057 if (save_blocks_count == end)
8058 return;
8059
8060 for (i = save_blocks_count + 1; i <= end; i++) {
8061 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
8062 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
8063 for (i = save_blocks_count + 1; i <= end; i++)
8064 ext2fs_mark_block_bitmap(fs->block_map,
8065 i);
8066 ext2fs_mark_bb_dirty(fs);
8067 } else
8068 ext2fs_unmark_valid(fs);
8069 break;
8070 }
8071 }
8072
8073 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
8074 save_blocks_count, 0);
8075 if (pctx.errcode) {
8076 pctx.num = 4;
8077 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8078 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8079 return;
8080 }
8081}
8082
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008083static void e2fsck_pass5(e2fsck_t ctx)
8084{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008085 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008086
Rob Landley3e72c592006-04-06 22:49:04 +00008087 /* Pass 5 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008088
8089 clear_problem_context(&pctx);
8090
8091 if (!(ctx->options & E2F_OPT_PREEN))
8092 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
8093
8094 if (ctx->progress)
8095 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
8096 return;
8097
8098 e2fsck_read_bitmaps(ctx);
8099
8100 check_block_bitmaps(ctx);
8101 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8102 return;
8103 check_inode_bitmaps(ctx);
8104 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8105 return;
8106 check_inode_end(ctx);
8107 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8108 return;
8109 check_block_end(ctx);
8110 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8111 return;
8112
8113 ext2fs_free_inode_bitmap(ctx->inode_used_map);
8114 ctx->inode_used_map = 0;
8115 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
8116 ctx->inode_dir_map = 0;
8117 ext2fs_free_block_bitmap(ctx->block_found_map);
8118 ctx->block_found_map = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008119}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008120
8121/*
8122 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008123 */
8124
8125#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
8126#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
8127#define PR_NO_DEFAULT 0x000004 /* Default to no */
8128#define PR_MSG_ONLY 0x000008 /* Print message only */
8129
8130/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
8131
8132#define PR_FATAL 0x001000 /* Fatal error */
8133#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
8134 /* ask another */
8135#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
8136#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
8137#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
8138#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
8139#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
8140
8141
8142#define PROMPT_NONE 0
8143#define PROMPT_FIX 1
8144#define PROMPT_CLEAR 2
8145#define PROMPT_RELOCATE 3
8146#define PROMPT_ALLOCATE 4
8147#define PROMPT_EXPAND 5
8148#define PROMPT_CONNECT 6
8149#define PROMPT_CREATE 7
8150#define PROMPT_SALVAGE 8
8151#define PROMPT_TRUNCATE 9
8152#define PROMPT_CLEAR_INODE 10
8153#define PROMPT_ABORT 11
8154#define PROMPT_SPLIT 12
8155#define PROMPT_CONTINUE 13
8156#define PROMPT_CLONE 14
8157#define PROMPT_DELETE 15
8158#define PROMPT_SUPPRESS 16
8159#define PROMPT_UNLINK 17
8160#define PROMPT_CLEAR_HTREE 18
8161#define PROMPT_RECREATE 19
8162#define PROMPT_NULL 20
8163
8164struct e2fsck_problem {
8165 problem_t e2p_code;
8166 const char * e2p_description;
8167 char prompt;
8168 int flags;
8169 problem_t second_code;
8170};
8171
8172struct latch_descr {
8173 int latch_code;
8174 problem_t question;
8175 problem_t end_message;
8176 int flags;
8177};
8178
8179/*
8180 * These are the prompts which are used to ask the user if they want
8181 * to fix a problem.
8182 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008183static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008184 N_("(no prompt)"), /* 0 */
8185 N_("Fix"), /* 1 */
8186 N_("Clear"), /* 2 */
8187 N_("Relocate"), /* 3 */
8188 N_("Allocate"), /* 4 */
8189 N_("Expand"), /* 5 */
8190 N_("Connect to /lost+found"), /* 6 */
8191 N_("Create"), /* 7 */
8192 N_("Salvage"), /* 8 */
8193 N_("Truncate"), /* 9 */
8194 N_("Clear inode"), /* 10 */
8195 N_("Abort"), /* 11 */
8196 N_("Split"), /* 12 */
8197 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008198 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008199 N_("Delete file"), /* 15 */
8200 N_("Suppress messages"),/* 16 */
8201 N_("Unlink"), /* 17 */
8202 N_("Clear HTree index"),/* 18 */
8203 N_("Recreate"), /* 19 */
8204 "", /* 20 */
8205};
8206
8207/*
8208 * These messages are printed when we are preen mode and we will be
8209 * automatically fixing the problem.
8210 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008211static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008212 N_("(NONE)"), /* 0 */
8213 N_("FIXED"), /* 1 */
8214 N_("CLEARED"), /* 2 */
8215 N_("RELOCATED"), /* 3 */
8216 N_("ALLOCATED"), /* 4 */
8217 N_("EXPANDED"), /* 5 */
8218 N_("RECONNECTED"), /* 6 */
8219 N_("CREATED"), /* 7 */
8220 N_("SALVAGED"), /* 8 */
8221 N_("TRUNCATED"), /* 9 */
8222 N_("INODE CLEARED"), /* 10 */
8223 N_("ABORTED"), /* 11 */
8224 N_("SPLIT"), /* 12 */
8225 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008226 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008227 N_("FILE DELETED"), /* 15 */
8228 N_("SUPPRESSED"), /* 16 */
8229 N_("UNLINKED"), /* 17 */
8230 N_("HTREE INDEX CLEARED"),/* 18 */
8231 N_("WILL RECREATE"), /* 19 */
8232 "", /* 20 */
8233};
8234
8235static const struct e2fsck_problem problem_table[] = {
8236
8237 /* Pre-Pass 1 errors */
8238
8239 /* Block bitmap not in group */
8240 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
8241 PROMPT_RELOCATE, PR_LATCH_RELOC },
8242
8243 /* Inode bitmap not in group */
8244 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
8245 PROMPT_RELOCATE, PR_LATCH_RELOC },
8246
8247 /* Inode table not in group */
8248 { PR_0_ITABLE_NOT_GROUP,
8249 N_("@i table for @g %g is not in @g. (@b %b)\n"
8250 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
8251 PROMPT_RELOCATE, PR_LATCH_RELOC },
8252
8253 /* Superblock corrupt */
8254 { PR_0_SB_CORRUPT,
8255 N_("\nThe @S could not be read or does not describe a correct ext2\n"
8256 "@f. If the @v is valid and it really contains an ext2\n"
8257 "@f (and not swap or ufs or something else), then the @S\n"
8258 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
8259 " e2fsck -b %S <@v>\n\n"),
8260 PROMPT_NONE, PR_FATAL },
8261
8262 /* Filesystem size is wrong */
8263 { PR_0_FS_SIZE_WRONG,
8264 N_("The @f size (according to the @S) is %b @bs\n"
8265 "The physical size of the @v is %c @bs\n"
8266 "Either the @S or the partition table is likely to be corrupt!\n"),
8267 PROMPT_ABORT, 0 },
8268
8269 /* Fragments not supported */
8270 { PR_0_NO_FRAGMENTS,
8271 N_("@S @b_size = %b, fragsize = %c.\n"
8272 "This version of e2fsck does not support fragment sizes different\n"
8273 "from the @b size.\n"),
8274 PROMPT_NONE, PR_FATAL },
8275
8276 /* Bad blocks_per_group */
8277 { PR_0_BLOCKS_PER_GROUP,
8278 N_("@S @bs_per_group = %b, should have been %c\n"),
8279 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8280
8281 /* Bad first_data_block */
8282 { PR_0_FIRST_DATA_BLOCK,
8283 N_("@S first_data_@b = %b, should have been %c\n"),
8284 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8285
8286 /* Adding UUID to filesystem */
8287 { PR_0_ADD_UUID,
8288 N_("@f did not have a UUID; generating one.\n\n"),
8289 PROMPT_NONE, 0 },
8290
8291 /* Relocate hint */
8292 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00008293 N_("Note: if several inode or block bitmap blocks or part\n"
8294 "of the inode table require relocation, you may wish to try\n"
8295 "running e2fsck with the '-b %S' option first. The problem\n"
8296 "may lie only with the primary block group descriptors, and\n"
8297 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008298 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
8299
8300 /* Miscellaneous superblock corruption */
8301 { PR_0_MISC_CORRUPT_SUPER,
8302 N_("Corruption found in @S. (%s = %N).\n"),
8303 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8304
8305 /* Error determing physical device size of filesystem */
8306 { PR_0_GETSIZE_ERROR,
8307 N_("Error determining size of the physical @v: %m\n"),
8308 PROMPT_NONE, PR_FATAL },
8309
8310 /* Inode count in superblock is incorrect */
8311 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00008312 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008313 PROMPT_FIX, 0 },
8314
8315 { PR_0_HURD_CLEAR_FILETYPE,
8316 N_("The Hurd does not support the filetype feature.\n"),
8317 PROMPT_CLEAR, 0 },
8318
8319 /* Journal inode is invalid */
8320 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008321 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008322 PROMPT_CLEAR, PR_PREEN_OK },
8323
8324 /* The external journal has (unsupported) multiple filesystems */
8325 { PR_0_JOURNAL_UNSUPP_MULTIFS,
8326 N_("External @j has multiple @f users (unsupported).\n"),
8327 PROMPT_NONE, PR_FATAL },
8328
8329 /* Can't find external journal */
8330 { PR_0_CANT_FIND_JOURNAL,
8331 N_("Can't find external @j\n"),
8332 PROMPT_NONE, PR_FATAL },
8333
8334 /* External journal has bad superblock */
8335 { PR_0_EXT_JOURNAL_BAD_SUPER,
8336 N_("External @j has bad @S\n"),
8337 PROMPT_NONE, PR_FATAL },
8338
8339 /* Superblock has a bad journal UUID */
8340 { PR_0_JOURNAL_BAD_UUID,
8341 N_("External @j does not support this @f\n"),
8342 PROMPT_NONE, PR_FATAL },
8343
8344 /* Journal has an unknown superblock type */
8345 { PR_0_JOURNAL_UNSUPP_SUPER,
8346 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
8347 "It is likely that your copy of e2fsck is old and/or doesn't "
8348 "support this @j format.\n"
8349 "It is also possible the @j @S is corrupt.\n"),
8350 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
8351
8352 /* Journal superblock is corrupt */
8353 { PR_0_JOURNAL_BAD_SUPER,
8354 N_("Ext3 @j @S is corrupt.\n"),
8355 PROMPT_FIX, PR_PREEN_OK },
8356
8357 /* Superblock flag should be cleared */
8358 { PR_0_JOURNAL_HAS_JOURNAL,
8359 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
8360 PROMPT_CLEAR, PR_PREEN_OK },
8361
8362 /* Superblock flag is incorrect */
8363 { PR_0_JOURNAL_RECOVER_SET,
8364 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
8365 PROMPT_CLEAR, PR_PREEN_OK },
8366
8367 /* Journal has data, but recovery flag is clear */
8368 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008369 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008370 PROMPT_NONE, 0 },
8371
8372 /* Ask if we should clear the journal */
8373 { PR_0_JOURNAL_RESET_JOURNAL,
8374 N_("Clear @j"),
8375 PROMPT_NULL, PR_PREEN_NOMSG },
8376
8377 /* Ask if we should run the journal anyway */
8378 { PR_0_JOURNAL_RUN,
8379 N_("Run @j anyway"),
8380 PROMPT_NULL, 0 },
8381
8382 /* Run the journal by default */
8383 { PR_0_JOURNAL_RUN_DEFAULT,
8384 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
8385 PROMPT_NONE, 0 },
8386
8387 /* Clearing orphan inode */
8388 { PR_0_ORPHAN_CLEAR_INODE,
8389 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
8390 PROMPT_NONE, 0 },
8391
8392 /* Illegal block found in orphaned inode */
8393 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
8394 N_("@I @b #%B (%b) found in @o @i %i.\n"),
8395 PROMPT_NONE, 0 },
8396
8397 /* Already cleared block found in orphaned inode */
8398 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
8399 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
8400 PROMPT_NONE, 0 },
8401
8402 /* Illegal orphan inode in superblock */
8403 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
8404 N_("@I @o @i %i in @S.\n"),
8405 PROMPT_NONE, 0 },
8406
8407 /* Illegal inode in orphaned inode list */
8408 { PR_0_ORPHAN_ILLEGAL_INODE,
8409 N_("@I @i %i in @o @i list.\n"),
8410 PROMPT_NONE, 0 },
8411
8412 /* Filesystem revision is 0, but feature flags are set */
8413 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008414 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008415 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8416
8417 /* Journal superblock has an unknown read-only feature flag set */
8418 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
8419 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
8420 PROMPT_ABORT, 0 },
8421
8422 /* Journal superblock has an unknown incompatible feature flag set */
8423 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
8424 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
8425 PROMPT_ABORT, 0 },
8426
8427 /* Journal has unsupported version number */
8428 { PR_0_JOURNAL_UNSUPP_VERSION,
8429 N_("@j version not supported by this e2fsck.\n"),
8430 PROMPT_ABORT, 0 },
8431
8432 /* Moving journal to hidden file */
8433 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008434 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008435 PROMPT_NONE, 0 },
8436
8437 /* Error moving journal to hidden file */
8438 { PR_0_ERR_MOVE_JOURNAL,
8439 N_("Error moving @j: %m\n\n"),
8440 PROMPT_NONE, 0 },
8441
8442 /* Clearing V2 journal superblock */
8443 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008444 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008445 "Clearing fields beyond the V1 @j @S...\n\n"),
8446 PROMPT_NONE, 0 },
8447
8448 /* Backup journal inode blocks */
8449 { PR_0_BACKUP_JNL,
8450 N_("Backing up @j @i @b information.\n\n"),
8451 PROMPT_NONE, 0 },
8452
8453 /* Reserved blocks w/o resize_inode */
8454 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
8455 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
8456 "is %N; @s zero. "),
8457 PROMPT_FIX, 0 },
8458
8459 /* Resize_inode not enabled, but resize inode is non-zero */
8460 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008461 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008462 PROMPT_CLEAR, 0 },
8463
8464 /* Resize inode invalid */
8465 { PR_0_RESIZE_INODE_INVALID,
8466 N_("Resize @i not valid. "),
8467 PROMPT_RECREATE, 0 },
8468
8469 /* Pass 1 errors */
8470
8471 /* Pass 1: Checking inodes, blocks, and sizes */
8472 { PR_1_PASS_HEADER,
8473 N_("Pass 1: Checking @is, @bs, and sizes\n"),
8474 PROMPT_NONE, 0 },
8475
8476 /* Root directory is not an inode */
8477 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
8478 PROMPT_CLEAR, 0 },
8479
8480 /* Root directory has dtime set */
8481 { PR_1_ROOT_DTIME,
8482 N_("@r has dtime set (probably due to old mke2fs). "),
8483 PROMPT_FIX, PR_PREEN_OK },
8484
8485 /* Reserved inode has bad mode */
8486 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008487 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008488 PROMPT_CLEAR, PR_PREEN_OK },
8489
8490 /* Deleted inode has zero dtime */
8491 { PR_1_ZERO_DTIME,
8492 N_("@D @i %i has zero dtime. "),
8493 PROMPT_FIX, PR_PREEN_OK },
8494
8495 /* Inode in use, but dtime set */
8496 { PR_1_SET_DTIME,
8497 N_("@i %i is in use, but has dtime set. "),
8498 PROMPT_FIX, PR_PREEN_OK },
8499
8500 /* Zero-length directory */
8501 { PR_1_ZERO_LENGTH_DIR,
8502 N_("@i %i is a @z @d. "),
8503 PROMPT_CLEAR, PR_PREEN_OK },
8504
8505 /* Block bitmap conflicts with some other fs block */
8506 { PR_1_BB_CONFLICT,
8507 N_("@g %g's @b @B at %b @C.\n"),
8508 PROMPT_RELOCATE, 0 },
8509
8510 /* Inode bitmap conflicts with some other fs block */
8511 { PR_1_IB_CONFLICT,
8512 N_("@g %g's @i @B at %b @C.\n"),
8513 PROMPT_RELOCATE, 0 },
8514
8515 /* Inode table conflicts with some other fs block */
8516 { PR_1_ITABLE_CONFLICT,
8517 N_("@g %g's @i table at %b @C.\n"),
8518 PROMPT_RELOCATE, 0 },
8519
8520 /* Block bitmap is on a bad block */
8521 { PR_1_BB_BAD_BLOCK,
8522 N_("@g %g's @b @B (%b) is bad. "),
8523 PROMPT_RELOCATE, 0 },
8524
8525 /* Inode bitmap is on a bad block */
8526 { PR_1_IB_BAD_BLOCK,
8527 N_("@g %g's @i @B (%b) is bad. "),
8528 PROMPT_RELOCATE, 0 },
8529
8530 /* Inode has incorrect i_size */
8531 { PR_1_BAD_I_SIZE,
8532 N_("@i %i, i_size is %Is, @s %N. "),
8533 PROMPT_FIX, PR_PREEN_OK },
8534
8535 /* Inode has incorrect i_blocks */
8536 { PR_1_BAD_I_BLOCKS,
8537 N_("@i %i, i_@bs is %Ib, @s %N. "),
8538 PROMPT_FIX, PR_PREEN_OK },
8539
8540 /* Illegal blocknumber in inode */
8541 { PR_1_ILLEGAL_BLOCK_NUM,
8542 N_("@I @b #%B (%b) in @i %i. "),
8543 PROMPT_CLEAR, PR_LATCH_BLOCK },
8544
8545 /* Block number overlaps fs metadata */
8546 { PR_1_BLOCK_OVERLAPS_METADATA,
8547 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
8548 PROMPT_CLEAR, PR_LATCH_BLOCK },
8549
8550 /* Inode has illegal blocks (latch question) */
8551 { PR_1_INODE_BLOCK_LATCH,
8552 N_("@i %i has illegal @b(s). "),
8553 PROMPT_CLEAR, 0 },
8554
8555 /* Too many bad blocks in inode */
8556 { PR_1_TOO_MANY_BAD_BLOCKS,
8557 N_("Too many illegal @bs in @i %i.\n"),
8558 PROMPT_CLEAR_INODE, PR_NO_OK },
8559
8560 /* Illegal block number in bad block inode */
8561 { PR_1_BB_ILLEGAL_BLOCK_NUM,
8562 N_("@I @b #%B (%b) in bad @b @i. "),
8563 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8564
8565 /* Bad block inode has illegal blocks (latch question) */
8566 { PR_1_INODE_BBLOCK_LATCH,
8567 N_("Bad @b @i has illegal @b(s). "),
8568 PROMPT_CLEAR, 0 },
8569
8570 /* Duplicate or bad blocks in use! */
8571 { PR_1_DUP_BLOCKS_PREENSTOP,
8572 N_("Duplicate or bad @b in use!\n"),
8573 PROMPT_NONE, 0 },
8574
8575 /* Bad block used as bad block indirect block */
8576 { PR_1_BBINODE_BAD_METABLOCK,
8577 N_("Bad @b %b used as bad @b @i indirect @b. "),
8578 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8579
8580 /* Inconsistency can't be fixed prompt */
8581 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
8582 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
8583 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
8584 "in the @f.\n"),
8585 PROMPT_CONTINUE, PR_PREEN_NOMSG },
8586
8587 /* Bad primary block */
8588 { PR_1_BAD_PRIMARY_BLOCK,
8589 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
8590 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
8591
8592 /* Bad primary block prompt */
8593 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +00008594 N_("You can remove this @b from the bad @b list and hope\n"
8595 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008596 PROMPT_CLEAR, PR_PREEN_NOMSG },
8597
8598 /* Bad primary superblock */
8599 { PR_1_BAD_PRIMARY_SUPERBLOCK,
8600 N_("The primary @S (%b) is on the bad @b list.\n"),
8601 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8602
8603 /* Bad primary block group descriptors */
8604 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
8605 N_("Block %b in the primary @g descriptors "
8606 "is on the bad @b list\n"),
8607 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8608
8609 /* Bad superblock in group */
8610 { PR_1_BAD_SUPERBLOCK,
8611 N_("Warning: Group %g's @S (%b) is bad.\n"),
8612 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8613
8614 /* Bad block group descriptors in group */
8615 { PR_1_BAD_GROUP_DESCRIPTORS,
8616 N_("Warning: Group %g's copy of the @g descriptors has a bad "
8617 "@b (%b).\n"),
8618 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8619
8620 /* Block claimed for no reason */
8621 { PR_1_PROGERR_CLAIMED_BLOCK,
8622 N_("Programming error? @b #%b claimed for no reason in "
8623 "process_bad_@b.\n"),
8624 PROMPT_NONE, PR_PREEN_OK },
8625
8626 /* Error allocating blocks for relocating metadata */
8627 { PR_1_RELOC_BLOCK_ALLOCATE,
8628 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
8629 PROMPT_NONE, PR_PREEN_OK },
8630
8631 /* Error allocating block buffer during relocation process */
8632 { PR_1_RELOC_MEMORY_ALLOCATE,
8633 N_("@A @b buffer for relocating %s\n"),
8634 PROMPT_NONE, PR_PREEN_OK },
8635
8636 /* Relocating metadata group information from X to Y */
8637 { PR_1_RELOC_FROM_TO,
8638 N_("Relocating @g %g's %s from %b to %c...\n"),
8639 PROMPT_NONE, PR_PREEN_OK },
8640
8641 /* Relocating metatdata group information to X */
8642 { PR_1_RELOC_TO,
8643 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
8644 PROMPT_NONE, PR_PREEN_OK },
8645
8646 /* Block read error during relocation process */
8647 { PR_1_RELOC_READ_ERR,
8648 N_("Warning: could not read @b %b of %s: %m\n"),
8649 PROMPT_NONE, PR_PREEN_OK },
8650
8651 /* Block write error during relocation process */
8652 { PR_1_RELOC_WRITE_ERR,
8653 N_("Warning: could not write @b %b for %s: %m\n"),
8654 PROMPT_NONE, PR_PREEN_OK },
8655
8656 /* Error allocating inode bitmap */
8657 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008658 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008659 PROMPT_NONE, PR_FATAL },
8660
8661 /* Error allocating block bitmap */
8662 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008663 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008664 PROMPT_NONE, PR_FATAL },
8665
8666 /* Error allocating icount structure */
8667 { PR_1_ALLOCATE_ICOUNT,
8668 N_("@A icount link information: %m\n"),
8669 PROMPT_NONE, PR_FATAL },
8670
8671 /* Error allocating dbcount */
8672 { PR_1_ALLOCATE_DBCOUNT,
8673 N_("@A @d @b array: %m\n"),
8674 PROMPT_NONE, PR_FATAL },
8675
8676 /* Error while scanning inodes */
8677 { PR_1_ISCAN_ERROR,
8678 N_("Error while scanning @is (%i): %m\n"),
8679 PROMPT_NONE, PR_FATAL },
8680
8681 /* Error while iterating over blocks */
8682 { PR_1_BLOCK_ITERATE,
8683 N_("Error while iterating over @bs in @i %i: %m\n"),
8684 PROMPT_NONE, PR_FATAL },
8685
8686 /* Error while storing inode count information */
8687 { PR_1_ICOUNT_STORE,
8688 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
8689 PROMPT_NONE, PR_FATAL },
8690
8691 /* Error while storing directory block information */
8692 { PR_1_ADD_DBLOCK,
8693 N_("Error storing @d @b information "
8694 "(@i=%i, @b=%b, num=%N): %m\n"),
8695 PROMPT_NONE, PR_FATAL },
8696
8697 /* Error while reading inode (for clearing) */
8698 { PR_1_READ_INODE,
8699 N_("Error reading @i %i: %m\n"),
8700 PROMPT_NONE, PR_FATAL },
8701
8702 /* Suppress messages prompt */
8703 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
8704
8705 /* Imagic flag set on an inode when filesystem doesn't support it */
8706 { PR_1_SET_IMAGIC,
8707 N_("@i %i has imagic flag set. "),
8708 PROMPT_CLEAR, 0 },
8709
8710 /* Immutable flag set on a device or socket inode */
8711 { PR_1_SET_IMMUTABLE,
8712 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
8713 "or append-only flag set. "),
8714 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
8715
8716 /* Compression flag set on an inode when filesystem doesn't support it */
8717 { PR_1_COMPR_SET,
8718 N_("@i %i has @cion flag set on @f without @cion support. "),
8719 PROMPT_CLEAR, 0 },
8720
8721 /* Non-zero size for device, fifo or socket inode */
8722 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008723 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008724 PROMPT_FIX, PR_PREEN_OK },
8725
8726 /* Filesystem revision is 0, but feature flags are set */
8727 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008728 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008729 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8730
8731 /* Journal inode is not in use, but contains data */
8732 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008733 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008734 PROMPT_CLEAR, PR_PREEN_OK },
8735
8736 /* Journal has bad mode */
8737 { PR_1_JOURNAL_BAD_MODE,
8738 N_("@j is not regular file. "),
8739 PROMPT_FIX, PR_PREEN_OK },
8740
8741 /* Deal with inodes that were part of orphan linked list */
8742 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +00008743 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008744 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
8745
8746 /* Deal with inodes that were part of corrupted orphan linked
8747 list (latch question) */
8748 { PR_1_ORPHAN_LIST_REFUGEES,
8749 N_("@is that were part of a corrupted orphan linked list found. "),
8750 PROMPT_FIX, 0 },
8751
8752 /* Error allocating refcount structure */
8753 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008754 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008755 PROMPT_NONE, PR_FATAL },
8756
8757 /* Error reading extended attribute block */
8758 { PR_1_READ_EA_BLOCK,
8759 N_("Error reading @a @b %b for @i %i. "),
8760 PROMPT_CLEAR, 0 },
8761
8762 /* Invalid extended attribute block */
8763 { PR_1_BAD_EA_BLOCK,
8764 N_("@i %i has a bad @a @b %b. "),
8765 PROMPT_CLEAR, 0 },
8766
8767 /* Error reading Extended Attribute block while fixing refcount */
8768 { PR_1_EXTATTR_READ_ABORT,
8769 N_("Error reading @a @b %b (%m). "),
8770 PROMPT_ABORT, 0 },
8771
8772 /* Extended attribute reference count incorrect */
8773 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008774 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008775 PROMPT_FIX, 0 },
8776
8777 /* Error writing Extended Attribute block while fixing refcount */
8778 { PR_1_EXTATTR_WRITE,
8779 N_("Error writing @a @b %b (%m). "),
8780 PROMPT_ABORT, 0 },
8781
8782 /* Multiple EA blocks not supported */
8783 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008784 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008785 PROMPT_CLEAR, 0},
8786
8787 /* Error allocating EA region allocation structure */
8788 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +00008789 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008790 PROMPT_ABORT, 0},
8791
8792 /* Error EA allocation collision */
8793 { PR_1_EA_ALLOC_COLLISION,
8794 N_("@a @b %b is corrupt (allocation collision). "),
8795 PROMPT_CLEAR, 0},
8796
8797 /* Bad extended attribute name */
8798 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00008799 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008800 PROMPT_CLEAR, 0},
8801
8802 /* Bad extended attribute value */
8803 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +00008804 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008805 PROMPT_CLEAR, 0},
8806
8807 /* Inode too big (latch question) */
8808 { PR_1_INODE_TOOBIG,
8809 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
8810
8811 /* Directory too big */
8812 { PR_1_TOOBIG_DIR,
8813 N_("@b #%B (%b) causes @d to be too big. "),
8814 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8815
8816 /* Regular file too big */
8817 { PR_1_TOOBIG_REG,
8818 N_("@b #%B (%b) causes file to be too big. "),
8819 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8820
8821 /* Symlink too big */
8822 { PR_1_TOOBIG_SYMLINK,
8823 N_("@b #%B (%b) causes symlink to be too big. "),
8824 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8825
8826 /* INDEX_FL flag set on a non-HTREE filesystem */
8827 { PR_1_HTREE_SET,
8828 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
8829 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8830
8831 /* INDEX_FL flag set on a non-directory */
8832 { PR_1_HTREE_NODIR,
8833 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
8834 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8835
8836 /* Invalid root node in HTREE directory */
8837 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00008838 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008839 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8840
8841 /* Unsupported hash version in HTREE directory */
8842 { PR_1_HTREE_HASHV,
8843 N_("@h %i has an unsupported hash version (%N)\n"),
8844 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8845
8846 /* Incompatible flag in HTREE root node */
8847 { PR_1_HTREE_INCOMPAT,
8848 N_("@h %i uses an incompatible htree root node flag.\n"),
8849 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8850
8851 /* HTREE too deep */
8852 { PR_1_HTREE_DEPTH,
8853 N_("@h %i has a tree depth (%N) which is too big\n"),
8854 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8855
8856 /* Bad block has indirect block that conflicts with filesystem block */
8857 { PR_1_BB_FS_BLOCK,
8858 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
8859 "@f metadata. "),
8860 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8861
8862 /* Resize inode failed */
8863 { PR_1_RESIZE_INODE_CREATE,
8864 N_("Resize @i (re)creation failed: %m."),
8865 PROMPT_ABORT, 0 },
8866
8867 /* invalid inode->i_extra_isize */
8868 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008869 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008870 PROMPT_FIX, PR_PREEN_OK },
8871
8872 /* invalid ea entry->e_name_len */
8873 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +00008874 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008875 PROMPT_CLEAR, PR_PREEN_OK },
8876
8877 /* invalid ea entry->e_value_size */
8878 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008879 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008880 PROMPT_CLEAR, PR_PREEN_OK },
8881
8882 /* invalid ea entry->e_value_offs */
8883 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +00008884 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008885 PROMPT_CLEAR, PR_PREEN_OK },
8886
8887 /* invalid ea entry->e_value_block */
8888 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008889 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 +00008890 PROMPT_CLEAR, PR_PREEN_OK },
8891
8892 /* invalid ea entry->e_hash */
8893 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +00008894 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 +00008895 PROMPT_CLEAR, PR_PREEN_OK },
8896
8897 /* Pass 1b errors */
8898
8899 /* Pass 1B: Rescan for duplicate/bad blocks */
8900 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008901 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
8902 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008903 PROMPT_NONE, 0 },
8904
8905 /* Duplicate/bad block(s) header */
8906 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008907 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008908 PROMPT_NONE, 0 },
8909
8910 /* Duplicate/bad block(s) in inode */
8911 { PR_1B_DUP_BLOCK,
8912 " %b",
8913 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
8914
8915 /* Duplicate/bad block(s) end */
8916 { PR_1B_DUP_BLOCK_END,
8917 "\n",
8918 PROMPT_NONE, PR_PREEN_NOHDR },
8919
8920 /* Error while scanning inodes */
8921 { PR_1B_ISCAN_ERROR,
8922 N_("Error while scanning inodes (%i): %m\n"),
8923 PROMPT_NONE, PR_FATAL },
8924
8925 /* Error allocating inode bitmap */
8926 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008927 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008928 PROMPT_NONE, PR_FATAL },
8929
8930 /* Error while iterating over blocks */
8931 { PR_1B_BLOCK_ITERATE,
8932 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
8933 PROMPT_NONE, 0 },
8934
8935 /* Error adjusting EA refcount */
8936 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008937 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008938 PROMPT_NONE, 0 },
8939
8940
Mike Frysinger874af852006-03-08 07:03:27 +00008941 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008942 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008943 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008944 PROMPT_NONE, 0 },
8945
8946
Mike Frysinger874af852006-03-08 07:03:27 +00008947 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008948 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008949 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008950 PROMPT_NONE, 0 },
8951
8952 /* File has duplicate blocks */
8953 { PR_1D_DUP_FILE,
8954 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +00008955 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008956 PROMPT_NONE, 0 },
8957
8958 /* List of files sharing duplicate blocks */
8959 { PR_1D_DUP_FILE_LIST,
8960 N_("\t%Q (@i #%i, mod time %IM)\n"),
8961 PROMPT_NONE, 0 },
8962
8963 /* File sharing blocks with filesystem metadata */
8964 { PR_1D_SHARE_METADATA,
8965 N_("\t<@f metadata>\n"),
8966 PROMPT_NONE, 0 },
8967
8968 /* Report of how many duplicate/bad inodes */
8969 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +00008970 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008971 PROMPT_NONE, 0 },
8972
8973 /* Duplicated blocks already reassigned or cloned. */
8974 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +00008975 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008976 PROMPT_NONE, 0 },
8977
8978 /* Clone duplicate/bad blocks? */
8979 { PR_1D_CLONE_QUESTION,
8980 "", PROMPT_CLONE, PR_NO_OK },
8981
8982 /* Delete file? */
8983 { PR_1D_DELETE_QUESTION,
8984 "", PROMPT_DELETE, 0 },
8985
8986 /* Couldn't clone file (error) */
8987 { PR_1D_CLONE_ERROR,
8988 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
8989
8990 /* Pass 2 errors */
8991
8992 /* Pass 2: Checking directory structure */
8993 { PR_2_PASS_HEADER,
8994 N_("Pass 2: Checking @d structure\n"),
8995 PROMPT_NONE, 0 },
8996
8997 /* Bad inode number for '.' */
8998 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00008999 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009000 PROMPT_FIX, 0 },
9001
9002 /* Directory entry has bad inode number */
9003 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +00009004 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009005 PROMPT_CLEAR, 0 },
9006
9007 /* Directory entry has deleted or unused inode */
9008 { PR_2_UNUSED_INODE,
9009 N_("@E has @D/unused @i %Di. "),
9010 PROMPT_CLEAR, PR_PREEN_OK },
9011
9012 /* Directry entry is link to '.' */
9013 { PR_2_LINK_DOT,
9014 N_("@E @L to '.' "),
9015 PROMPT_CLEAR, 0 },
9016
9017 /* Directory entry points to inode now located in a bad block */
9018 { PR_2_BB_INODE,
9019 N_("@E points to @i (%Di) located in a bad @b.\n"),
9020 PROMPT_CLEAR, 0 },
9021
9022 /* Directory entry contains a link to a directory */
9023 { PR_2_LINK_DIR,
9024 N_("@E @L to @d %P (%Di).\n"),
9025 PROMPT_CLEAR, 0 },
9026
9027 /* Directory entry contains a link to the root directry */
9028 { PR_2_LINK_ROOT,
9029 N_("@E @L to the @r.\n"),
9030 PROMPT_CLEAR, 0 },
9031
9032 /* Directory entry has illegal characters in its name */
9033 { PR_2_BAD_NAME,
9034 N_("@E has illegal characters in its name.\n"),
9035 PROMPT_FIX, 0 },
9036
9037 /* Missing '.' in directory inode */
9038 { PR_2_MISSING_DOT,
9039 N_("Missing '.' in @d @i %i.\n"),
9040 PROMPT_FIX, 0 },
9041
9042 /* Missing '..' in directory inode */
9043 { PR_2_MISSING_DOT_DOT,
9044 N_("Missing '..' in @d @i %i.\n"),
9045 PROMPT_FIX, 0 },
9046
9047 /* First entry in directory inode doesn't contain '.' */
9048 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009049 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009050 PROMPT_FIX, 0 },
9051
9052 /* Second entry in directory inode doesn't contain '..' */
9053 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009054 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009055 PROMPT_FIX, 0 },
9056
9057 /* i_faddr should be zero */
9058 { PR_2_FADDR_ZERO,
9059 N_("i_faddr @F %IF, @s zero.\n"),
9060 PROMPT_CLEAR, 0 },
9061
9062 /* i_file_acl should be zero */
9063 { PR_2_FILE_ACL_ZERO,
9064 N_("i_file_acl @F %If, @s zero.\n"),
9065 PROMPT_CLEAR, 0 },
9066
9067 /* i_dir_acl should be zero */
9068 { PR_2_DIR_ACL_ZERO,
9069 N_("i_dir_acl @F %Id, @s zero.\n"),
9070 PROMPT_CLEAR, 0 },
9071
9072 /* i_frag should be zero */
9073 { PR_2_FRAG_ZERO,
9074 N_("i_frag @F %N, @s zero.\n"),
9075 PROMPT_CLEAR, 0 },
9076
9077 /* i_fsize should be zero */
9078 { PR_2_FSIZE_ZERO,
9079 N_("i_fsize @F %N, @s zero.\n"),
9080 PROMPT_CLEAR, 0 },
9081
9082 /* inode has bad mode */
9083 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009084 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009085 PROMPT_CLEAR, 0 },
9086
9087 /* directory corrupted */
9088 { PR_2_DIR_CORRUPTED,
9089 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
9090 PROMPT_SALVAGE, 0 },
9091
9092 /* filename too long */
9093 { PR_2_FILENAME_LONG,
9094 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
9095 PROMPT_TRUNCATE, 0 },
9096
9097 /* Directory inode has a missing block (hole) */
9098 { PR_2_DIRECTORY_HOLE,
9099 N_("@d @i %i has an unallocated @b #%B. "),
9100 PROMPT_ALLOCATE, 0 },
9101
9102 /* '.' is not NULL terminated */
9103 { PR_2_DOT_NULL_TERM,
9104 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
9105 PROMPT_FIX, 0 },
9106
9107 /* '..' is not NULL terminated */
9108 { PR_2_DOT_DOT_NULL_TERM,
9109 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
9110 PROMPT_FIX, 0 },
9111
9112 /* Illegal character device inode */
9113 { PR_2_BAD_CHAR_DEV,
9114 N_("@i %i (%Q) is an @I character @v.\n"),
9115 PROMPT_CLEAR, 0 },
9116
9117 /* Illegal block device inode */
9118 { PR_2_BAD_BLOCK_DEV,
9119 N_("@i %i (%Q) is an @I @b @v.\n"),
9120 PROMPT_CLEAR, 0 },
9121
9122 /* Duplicate '.' entry */
9123 { PR_2_DUP_DOT,
9124 N_("@E is duplicate '.' @e.\n"),
9125 PROMPT_FIX, 0 },
9126
9127 /* Duplicate '..' entry */
9128 { PR_2_DUP_DOT_DOT,
9129 N_("@E is duplicate '..' @e.\n"),
9130 PROMPT_FIX, 0 },
9131
9132 /* Internal error: couldn't find dir_info */
9133 { PR_2_NO_DIRINFO,
9134 N_("Internal error: couldn't find dir_info for %i.\n"),
9135 PROMPT_NONE, PR_FATAL },
9136
9137 /* Final rec_len is wrong */
9138 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +00009139 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009140 PROMPT_FIX, 0 },
9141
9142 /* Error allocating icount structure */
9143 { PR_2_ALLOCATE_ICOUNT,
9144 N_("@A icount structure: %m\n"),
9145 PROMPT_NONE, PR_FATAL },
9146
9147 /* Error iterating over directory blocks */
9148 { PR_2_DBLIST_ITERATE,
9149 N_("Error iterating over @d @bs: %m\n"),
9150 PROMPT_NONE, PR_FATAL },
9151
9152 /* Error reading directory block */
9153 { PR_2_READ_DIRBLOCK,
9154 N_("Error reading @d @b %b (@i %i): %m\n"),
9155 PROMPT_CONTINUE, 0 },
9156
9157 /* Error writing directory block */
9158 { PR_2_WRITE_DIRBLOCK,
9159 N_("Error writing @d @b %b (@i %i): %m\n"),
9160 PROMPT_CONTINUE, 0 },
9161
9162 /* Error allocating new directory block */
9163 { PR_2_ALLOC_DIRBOCK,
9164 N_("@A new @d @b for @i %i (%s): %m\n"),
9165 PROMPT_NONE, 0 },
9166
9167 /* Error deallocating inode */
9168 { PR_2_DEALLOC_INODE,
9169 N_("Error deallocating @i %i: %m\n"),
9170 PROMPT_NONE, PR_FATAL },
9171
9172 /* Directory entry for '.' is big. Split? */
9173 { PR_2_SPLIT_DOT,
9174 N_("@d @e for '.' is big. "),
9175 PROMPT_SPLIT, PR_NO_OK },
9176
9177 /* Illegal FIFO inode */
9178 { PR_2_BAD_FIFO,
9179 N_("@i %i (%Q) is an @I FIFO.\n"),
9180 PROMPT_CLEAR, 0 },
9181
9182 /* Illegal socket inode */
9183 { PR_2_BAD_SOCKET,
9184 N_("@i %i (%Q) is an @I socket.\n"),
9185 PROMPT_CLEAR, 0 },
9186
9187 /* Directory filetype not set */
9188 { PR_2_SET_FILETYPE,
9189 N_("Setting filetype for @E to %N.\n"),
9190 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
9191
9192 /* Directory filetype incorrect */
9193 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +00009194 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009195 PROMPT_FIX, 0 },
9196
9197 /* Directory filetype set on filesystem */
9198 { PR_2_CLEAR_FILETYPE,
9199 N_("@E has filetype set.\n"),
9200 PROMPT_CLEAR, PR_PREEN_OK },
9201
9202 /* Directory filename is null */
9203 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00009204 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009205 PROMPT_CLEAR, 0 },
9206
9207 /* Invalid symlink */
9208 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +00009209 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009210 PROMPT_CLEAR, 0 },
9211
9212 /* i_file_acl (extended attribute block) is bad */
9213 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +00009214 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009215 PROMPT_CLEAR, 0 },
9216
9217 /* Filesystem contains large files, but has no such flag in sb */
9218 { PR_2_FEATURE_LARGE_FILES,
9219 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
9220 PROMPT_FIX, 0 },
9221
9222 /* Node in HTREE directory not referenced */
9223 { PR_2_HTREE_NOTREF,
9224 N_("@p @h %d: node (%B) not referenced\n"),
9225 PROMPT_NONE, 0 },
9226
9227 /* Node in HTREE directory referenced twice */
9228 { PR_2_HTREE_DUPREF,
9229 N_("@p @h %d: node (%B) referenced twice\n"),
9230 PROMPT_NONE, 0 },
9231
9232 /* Node in HTREE directory has bad min hash */
9233 { PR_2_HTREE_MIN_HASH,
9234 N_("@p @h %d: node (%B) has bad min hash\n"),
9235 PROMPT_NONE, 0 },
9236
9237 /* Node in HTREE directory has bad max hash */
9238 { PR_2_HTREE_MAX_HASH,
9239 N_("@p @h %d: node (%B) has bad max hash\n"),
9240 PROMPT_NONE, 0 },
9241
9242 /* Clear invalid HTREE directory */
9243 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009244 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009245
9246 /* Bad block in htree interior node */
9247 { PR_2_HTREE_BADBLK,
9248 N_("@p @h %d (%q): bad @b number %b.\n"),
9249 PROMPT_CLEAR_HTREE, 0 },
9250
9251 /* Error adjusting EA refcount */
9252 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009253 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009254 PROMPT_NONE, PR_FATAL },
9255
9256 /* Invalid HTREE root node */
9257 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009258 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009259 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9260
9261 /* Invalid HTREE limit */
9262 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +00009263 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009264 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9265
9266 /* Invalid HTREE count */
9267 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009268 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009269 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9270
9271 /* HTREE interior node has out-of-order hashes in table */
9272 { PR_2_HTREE_HASH_ORDER,
9273 N_("@p @h %d: node (%B) has an unordered hash table\n"),
9274 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9275
Mike Frysinger874af852006-03-08 07:03:27 +00009276 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009277 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +00009278 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009279 PROMPT_NONE, 0 },
9280
9281 /* Duplicate directory entry found */
9282 { PR_2_DUPLICATE_DIRENT,
9283 N_("Duplicate @E found. "),
9284 PROMPT_CLEAR, 0 },
9285
9286 /* Non-unique filename found */
9287 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
9288 N_("@E has a non-unique filename.\nRename to %s"),
9289 PROMPT_NULL, 0 },
9290
9291 /* Duplicate directory entry found */
9292 { PR_2_REPORT_DUP_DIRENT,
9293 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
9294 PROMPT_NONE, 0 },
9295
9296 /* Pass 3 errors */
9297
9298 /* Pass 3: Checking directory connectivity */
9299 { PR_3_PASS_HEADER,
9300 N_("Pass 3: Checking @d connectivity\n"),
9301 PROMPT_NONE, 0 },
9302
9303 /* Root inode not allocated */
9304 { PR_3_NO_ROOT_INODE,
9305 N_("@r not allocated. "),
9306 PROMPT_ALLOCATE, 0 },
9307
9308 /* No room in lost+found */
9309 { PR_3_EXPAND_LF_DIR,
9310 N_("No room in @l @d. "),
9311 PROMPT_EXPAND, 0 },
9312
9313 /* Unconnected directory inode */
9314 { PR_3_UNCONNECTED_DIR,
9315 N_("Unconnected @d @i %i (%p)\n"),
9316 PROMPT_CONNECT, 0 },
9317
9318 /* /lost+found not found */
9319 { PR_3_NO_LF_DIR,
9320 N_("/@l not found. "),
9321 PROMPT_CREATE, PR_PREEN_OK },
9322
9323 /* .. entry is incorrect */
9324 { PR_3_BAD_DOT_DOT,
9325 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
9326 PROMPT_FIX, 0 },
9327
9328 /* Bad or non-existent /lost+found. Cannot reconnect */
9329 { PR_3_NO_LPF,
9330 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
9331 PROMPT_NONE, 0 },
9332
9333 /* Could not expand /lost+found */
9334 { PR_3_CANT_EXPAND_LPF,
9335 N_("Could not expand /@l: %m\n"),
9336 PROMPT_NONE, 0 },
9337
9338 /* Could not reconnect inode */
9339 { PR_3_CANT_RECONNECT,
9340 N_("Could not reconnect %i: %m\n"),
9341 PROMPT_NONE, 0 },
9342
9343 /* Error while trying to find /lost+found */
9344 { PR_3_ERR_FIND_LPF,
9345 N_("Error while trying to find /@l: %m\n"),
9346 PROMPT_NONE, 0 },
9347
9348 /* Error in ext2fs_new_block while creating /lost+found */
9349 { PR_3_ERR_LPF_NEW_BLOCK,
9350 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
9351 PROMPT_NONE, 0 },
9352
9353 /* Error in ext2fs_new_inode while creating /lost+found */
9354 { PR_3_ERR_LPF_NEW_INODE,
9355 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
9356 PROMPT_NONE, 0 },
9357
9358 /* Error in ext2fs_new_dir_block while creating /lost+found */
9359 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
9360 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
9361 PROMPT_NONE, 0 },
9362
9363 /* Error while writing directory block for /lost+found */
9364 { PR_3_ERR_LPF_WRITE_BLOCK,
9365 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
9366 PROMPT_NONE, 0 },
9367
9368 /* Error while adjusting inode count */
9369 { PR_3_ADJUST_INODE,
9370 N_("Error while adjusting @i count on @i %i\n"),
9371 PROMPT_NONE, 0 },
9372
9373 /* Couldn't fix parent directory -- error */
9374 { PR_3_FIX_PARENT_ERR,
9375 N_("Couldn't fix parent of @i %i: %m\n\n"),
9376 PROMPT_NONE, 0 },
9377
9378 /* Couldn't fix parent directory -- couldn't find it */
9379 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +00009380 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 +00009381 PROMPT_NONE, 0 },
9382
9383 /* Error allocating inode bitmap */
9384 { PR_3_ALLOCATE_IBITMAP_ERROR,
9385 N_("@A @i @B (%N): %m\n"),
9386 PROMPT_NONE, PR_FATAL },
9387
9388 /* Error creating root directory */
9389 { PR_3_CREATE_ROOT_ERROR,
9390 N_("Error creating root @d (%s): %m\n"),
9391 PROMPT_NONE, PR_FATAL },
9392
9393 /* Error creating lost and found directory */
9394 { PR_3_CREATE_LPF_ERROR,
9395 N_("Error creating /@l @d (%s): %m\n"),
9396 PROMPT_NONE, PR_FATAL },
9397
9398 /* Root inode is not directory; aborting */
9399 { PR_3_ROOT_NOT_DIR_ABORT,
9400 N_("@r is not a @d; aborting.\n"),
9401 PROMPT_NONE, PR_FATAL },
9402
9403 /* Cannot proceed without a root inode. */
9404 { PR_3_NO_ROOT_INODE_ABORT,
9405 N_("Cannot proceed without a @r.\n"),
9406 PROMPT_NONE, PR_FATAL },
9407
9408 /* Internal error: couldn't find dir_info */
9409 { PR_3_NO_DIRINFO,
9410 N_("Internal error: couldn't find dir_info for %i.\n"),
9411 PROMPT_NONE, PR_FATAL },
9412
9413 /* Lost+found not a directory */
9414 { PR_3_LPF_NOTDIR,
9415 N_("/@l is not a @d (ino=%i)\n"),
9416 PROMPT_UNLINK, 0 },
9417
9418 /* Pass 3A Directory Optimization */
9419
9420 /* Pass 3A: Optimizing directories */
9421 { PR_3A_PASS_HEADER,
9422 N_("Pass 3A: Optimizing directories\n"),
9423 PROMPT_NONE, PR_PREEN_NOMSG },
9424
9425 /* Error iterating over directories */
9426 { PR_3A_OPTIMIZE_ITER,
9427 N_("Failed to create dirs_to_hash iterator: %m"),
9428 PROMPT_NONE, 0 },
9429
9430 /* Error rehash directory */
9431 { PR_3A_OPTIMIZE_DIR_ERR,
9432 N_("Failed to optimize directory %q (%d): %m"),
9433 PROMPT_NONE, 0 },
9434
9435 /* Rehashing dir header */
9436 { PR_3A_OPTIMIZE_DIR_HEADER,
9437 N_("Optimizing directories: "),
9438 PROMPT_NONE, PR_MSG_ONLY },
9439
9440 /* Rehashing directory %d */
9441 { PR_3A_OPTIMIZE_DIR,
9442 " %d",
9443 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
9444
9445 /* Rehashing dir end */
9446 { PR_3A_OPTIMIZE_DIR_END,
9447 "\n",
9448 PROMPT_NONE, PR_PREEN_NOHDR },
9449
9450 /* Pass 4 errors */
9451
9452 /* Pass 4: Checking reference counts */
9453 { PR_4_PASS_HEADER,
9454 N_("Pass 4: Checking reference counts\n"),
9455 PROMPT_NONE, 0 },
9456
9457 /* Unattached zero-length inode */
9458 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009459 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009460 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
9461
9462 /* Unattached inode */
9463 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009464 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009465 PROMPT_CONNECT, 0 },
9466
9467 /* Inode ref count wrong */
9468 { PR_4_BAD_REF_COUNT,
9469 N_("@i %i ref count is %Il, @s %N. "),
9470 PROMPT_FIX, PR_PREEN_OK },
9471
9472 { PR_4_INCONSISTENT_COUNT,
9473 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
9474 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
9475 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +00009476 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009477 PROMPT_NONE, 0 },
9478
9479 /* Pass 5 errors */
9480
9481 /* Pass 5: Checking group summary information */
9482 { PR_5_PASS_HEADER,
9483 N_("Pass 5: Checking @g summary information\n"),
9484 PROMPT_NONE, 0 },
9485
9486 /* Padding at end of inode bitmap is not set. */
9487 { PR_5_INODE_BMAP_PADDING,
9488 N_("Padding at end of @i @B is not set. "),
9489 PROMPT_FIX, PR_PREEN_OK },
9490
9491 /* Padding at end of block bitmap is not set. */
9492 { PR_5_BLOCK_BMAP_PADDING,
9493 N_("Padding at end of @b @B is not set. "),
9494 PROMPT_FIX, PR_PREEN_OK },
9495
9496 /* Block bitmap differences header */
9497 { PR_5_BLOCK_BITMAP_HEADER,
9498 N_("@b @B differences: "),
9499 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
9500
9501 /* Block not used, but marked in bitmap */
9502 { PR_5_BLOCK_UNUSED,
9503 " -%b",
9504 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9505
9506 /* Block used, but not marked used in bitmap */
9507 { PR_5_BLOCK_USED,
9508 " +%b",
9509 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9510
9511 /* Block bitmap differences end */
9512 { PR_5_BLOCK_BITMAP_END,
9513 "\n",
9514 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9515
9516 /* Inode bitmap differences header */
9517 { PR_5_INODE_BITMAP_HEADER,
9518 N_("@i @B differences: "),
9519 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9520
9521 /* Inode not used, but marked in bitmap */
9522 { PR_5_INODE_UNUSED,
9523 " -%i",
9524 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9525
9526 /* Inode used, but not marked used in bitmap */
9527 { PR_5_INODE_USED,
9528 " +%i",
9529 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9530
9531 /* Inode bitmap differences end */
9532 { PR_5_INODE_BITMAP_END,
9533 "\n",
9534 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9535
9536 /* Free inodes count for group wrong */
9537 { PR_5_FREE_INODE_COUNT_GROUP,
9538 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
9539 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9540
9541 /* Directories count for group wrong */
9542 { PR_5_FREE_DIR_COUNT_GROUP,
9543 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
9544 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9545
9546 /* Free inodes count wrong */
9547 { PR_5_FREE_INODE_COUNT,
9548 N_("Free @is count wrong (%i, counted=%j).\n"),
9549 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9550
9551 /* Free blocks count for group wrong */
9552 { PR_5_FREE_BLOCK_COUNT_GROUP,
9553 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
9554 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9555
9556 /* Free blocks count wrong */
9557 { PR_5_FREE_BLOCK_COUNT,
9558 N_("Free @bs count wrong (%b, counted=%c).\n"),
9559 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9560
9561 /* Programming error: bitmap endpoints don't match */
9562 { PR_5_BMAP_ENDPOINTS,
9563 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
9564 "match calculated @B endpoints (%i, %j)\n"),
9565 PROMPT_NONE, PR_FATAL },
9566
9567 /* Internal error: fudging end of bitmap */
9568 { PR_5_FUDGE_BITMAP_ERROR,
9569 N_("Internal error: fudging end of bitmap (%N)\n"),
9570 PROMPT_NONE, PR_FATAL },
9571
9572 /* Error copying in replacement inode bitmap */
9573 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009574 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009575 PROMPT_NONE, PR_FATAL },
9576
9577 /* Error copying in replacement block bitmap */
9578 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009579 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009580 PROMPT_NONE, PR_FATAL },
9581
9582 /* Block range not used, but marked in bitmap */
9583 { PR_5_BLOCK_RANGE_UNUSED,
9584 " -(%b--%c)",
9585 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9586
9587 /* Block range used, but not marked used in bitmap */
9588 { PR_5_BLOCK_RANGE_USED,
9589 " +(%b--%c)",
9590 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9591
9592 /* Inode range not used, but marked in bitmap */
9593 { PR_5_INODE_RANGE_UNUSED,
9594 " -(%i--%j)",
9595 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9596
9597 /* Inode range used, but not marked used in bitmap */
9598 { PR_5_INODE_RANGE_USED,
9599 " +(%i--%j)",
9600 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9601
9602 { 0 }
9603};
9604
9605/*
9606 * This is the latch flags register. It allows several problems to be
9607 * "latched" together. This means that the user has to answer but one
9608 * question for the set of problems, and all of the associated
9609 * problems will be either fixed or not fixed.
9610 */
9611static struct latch_descr pr_latch_info[] = {
9612 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
9613 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
9614 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
9615 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
9616 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
9617 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
9618 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
9619 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
9620 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
9621 { -1, 0, 0 },
9622};
9623
9624static const struct e2fsck_problem *find_problem(problem_t code)
9625{
9626 int i;
9627
9628 for (i=0; problem_table[i].e2p_code; i++) {
9629 if (problem_table[i].e2p_code == code)
9630 return &problem_table[i];
9631 }
9632 return 0;
9633}
9634
9635static struct latch_descr *find_latch(int code)
9636{
9637 int i;
9638
9639 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
9640 if (pr_latch_info[i].latch_code == code)
9641 return &pr_latch_info[i];
9642 }
9643 return 0;
9644}
9645
9646int end_problem_latch(e2fsck_t ctx, int mask)
9647{
9648 struct latch_descr *ldesc;
9649 struct problem_context pctx;
9650 int answer = -1;
9651
9652 ldesc = find_latch(mask);
9653 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
9654 clear_problem_context(&pctx);
9655 answer = fix_problem(ctx, ldesc->end_message, &pctx);
9656 }
9657 ldesc->flags &= ~(PRL_VARIABLE);
9658 return answer;
9659}
9660
9661int set_latch_flags(int mask, int setflags, int clearflags)
9662{
9663 struct latch_descr *ldesc;
9664
9665 ldesc = find_latch(mask);
9666 if (!ldesc)
9667 return -1;
9668 ldesc->flags |= setflags;
9669 ldesc->flags &= ~clearflags;
9670 return 0;
9671}
9672
9673void clear_problem_context(struct problem_context *ctx)
9674{
9675 memset(ctx, 0, sizeof(struct problem_context));
9676 ctx->blkcount = -1;
9677 ctx->group = -1;
9678}
9679
9680int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
9681{
9682 ext2_filsys fs = ctx->fs;
9683 const struct e2fsck_problem *ptr;
9684 struct latch_descr *ldesc = 0;
9685 const char *message;
9686 int def_yn, answer, ans;
9687 int print_answer = 0;
9688 int suppress = 0;
9689
9690 ptr = find_problem(code);
9691 if (!ptr) {
9692 printf(_("Unhandled error code (0x%x)!\n"), code);
9693 return 0;
9694 }
9695 def_yn = 1;
9696 if ((ptr->flags & PR_NO_DEFAULT) ||
9697 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
9698 (ctx->options & E2F_OPT_NO))
9699 def_yn= 0;
9700
9701 /*
9702 * Do special latch processing. This is where we ask the
9703 * latch question, if it exists
9704 */
9705 if (ptr->flags & PR_LATCH_MASK) {
9706 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
9707 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
9708 ans = fix_problem(ctx, ldesc->question, pctx);
9709 if (ans == 1)
9710 ldesc->flags |= PRL_YES;
9711 if (ans == 0)
9712 ldesc->flags |= PRL_NO;
9713 ldesc->flags |= PRL_LATCHED;
9714 }
9715 if (ldesc->flags & PRL_SUPPRESS)
9716 suppress++;
9717 }
9718 if ((ptr->flags & PR_PREEN_NOMSG) &&
9719 (ctx->options & E2F_OPT_PREEN))
9720 suppress++;
9721 if ((ptr->flags & PR_NO_NOMSG) &&
9722 (ctx->options & E2F_OPT_NO))
9723 suppress++;
9724 if (!suppress) {
9725 message = ptr->e2p_description;
9726 if ((ctx->options & E2F_OPT_PREEN) &&
9727 !(ptr->flags & PR_PREEN_NOHDR)) {
9728 printf("%s: ", ctx->device_name ?
9729 ctx->device_name : ctx->filesystem_name);
9730 }
9731 if (*message)
9732 print_e2fsck_message(ctx, _(message), pctx, 1);
9733 }
9734 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
9735 preenhalt(ctx);
9736
9737 if (ptr->flags & PR_FATAL)
Rob Landley7c94bed2006-05-03 21:58:45 +00009738 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009739
9740 if (ptr->prompt == PROMPT_NONE) {
9741 if (ptr->flags & PR_NOCOLLATE)
9742 answer = -1;
9743 else
9744 answer = def_yn;
9745 } else {
9746 if (ctx->options & E2F_OPT_PREEN) {
9747 answer = def_yn;
9748 if (!(ptr->flags & PR_PREEN_NOMSG))
9749 print_answer = 1;
9750 } else if ((ptr->flags & PR_LATCH_MASK) &&
9751 (ldesc->flags & (PRL_YES | PRL_NO))) {
9752 if (!suppress)
9753 print_answer = 1;
9754 if (ldesc->flags & PRL_YES)
9755 answer = 1;
9756 else
9757 answer = 0;
9758 } else
9759 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
9760 if (!answer && !(ptr->flags & PR_NO_OK))
9761 ext2fs_unmark_valid(fs);
9762
9763 if (print_answer)
9764 printf("%s.\n", answer ?
9765 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
9766
9767 }
9768
9769 if ((ptr->prompt == PROMPT_ABORT) && answer)
Rob Landley7c94bed2006-05-03 21:58:45 +00009770 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009771
9772 if (ptr->flags & PR_AFTER_CODE)
9773 answer = fix_problem(ctx, ptr->second_code, pctx);
9774
9775 return answer;
9776}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009777
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009778/*
9779 * linux/fs/recovery.c
9780 *
9781 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009782 */
9783
9784/*
9785 * Maintain information about the progress of the recovery job, so that
9786 * the different passes can carry information between them.
9787 */
9788struct recovery_info
9789{
9790 tid_t start_transaction;
9791 tid_t end_transaction;
9792
9793 int nr_replays;
9794 int nr_revokes;
9795 int nr_revoke_hits;
9796};
9797
9798enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
9799static int do_one_pass(journal_t *journal,
9800 struct recovery_info *info, enum passtype pass);
9801static int scan_revoke_records(journal_t *, struct buffer_head *,
9802 tid_t, struct recovery_info *);
9803
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009804/*
9805 * Read a block from the journal
9806 */
9807
9808static int jread(struct buffer_head **bhp, journal_t *journal,
9809 unsigned int offset)
9810{
9811 int err;
9812 unsigned long blocknr;
9813 struct buffer_head *bh;
9814
9815 *bhp = NULL;
9816
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009817 err = journal_bmap(journal, offset, &blocknr);
9818
9819 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +00009820 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009821 return err;
9822 }
9823
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009824 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009825 if (!bh)
9826 return -ENOMEM;
9827
9828 if (!buffer_uptodate(bh)) {
9829 /* If this is a brand new buffer, start readahead.
9830 Otherwise, we assume we are already reading it. */
9831 if (!buffer_req(bh))
9832 do_readahead(journal, offset);
9833 wait_on_buffer(bh);
9834 }
9835
9836 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +00009837 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009838 brelse(bh);
9839 return -EIO;
9840 }
9841
9842 *bhp = bh;
9843 return 0;
9844}
9845
9846
9847/*
9848 * Count the number of in-use tags in a journal descriptor block.
9849 */
9850
9851static int count_tags(struct buffer_head *bh, int size)
9852{
9853 char * tagp;
9854 journal_block_tag_t * tag;
9855 int nr = 0;
9856
9857 tagp = &bh->b_data[sizeof(journal_header_t)];
9858
9859 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
9860 tag = (journal_block_tag_t *) tagp;
9861
9862 nr++;
9863 tagp += sizeof(journal_block_tag_t);
9864 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
9865 tagp += 16;
9866
9867 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
9868 break;
9869 }
9870
9871 return nr;
9872}
9873
9874
9875/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00009876#define wrap(journal, var) \
9877do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009878 if (var >= (journal)->j_last) \
9879 var -= ((journal)->j_last - (journal)->j_first); \
9880} while (0)
9881
9882/**
9883 * int journal_recover(journal_t *journal) - recovers a on-disk journal
9884 * @journal: the journal to recover
9885 *
9886 * The primary function for recovering the log contents when mounting a
9887 * journaled device.
9888 *
9889 * Recovery is done in three passes. In the first pass, we look for the
9890 * end of the log. In the second, we assemble the list of revoke
9891 * blocks. In the third and final pass, we replay any un-revoked blocks
9892 * in the log.
9893 */
9894int journal_recover(journal_t *journal)
9895{
9896 int err;
9897 journal_superblock_t * sb;
9898
9899 struct recovery_info info;
9900
9901 memset(&info, 0, sizeof(info));
9902 sb = journal->j_superblock;
9903
9904 /*
9905 * The journal superblock's s_start field (the current log head)
9906 * is always zero if, and only if, the journal was cleanly
9907 * unmounted.
9908 */
9909
9910 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009911 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
9912 return 0;
9913 }
9914
9915 err = do_one_pass(journal, &info, PASS_SCAN);
9916 if (!err)
9917 err = do_one_pass(journal, &info, PASS_REVOKE);
9918 if (!err)
9919 err = do_one_pass(journal, &info, PASS_REPLAY);
9920
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009921 /* Restart the log at the next transaction ID, thus invalidating
9922 * any existing commit records in the log. */
9923 journal->j_transaction_sequence = ++info.end_transaction;
9924
9925 journal_clear_revoke(journal);
9926 sync_blockdev(journal->j_fs_dev);
9927 return err;
9928}
9929
9930static int do_one_pass(journal_t *journal,
9931 struct recovery_info *info, enum passtype pass)
9932{
9933 unsigned int first_commit_ID, next_commit_ID;
9934 unsigned long next_log_block;
9935 int err, success = 0;
9936 journal_superblock_t * sb;
9937 journal_header_t * tmp;
9938 struct buffer_head * bh;
9939 unsigned int sequence;
9940 int blocktype;
9941
9942 /* Precompute the maximum metadata descriptors in a descriptor block */
9943 int MAX_BLOCKS_PER_DESC;
9944 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
9945 / sizeof(journal_block_tag_t));
9946
9947 /*
9948 * First thing is to establish what we expect to find in the log
9949 * (in terms of transaction IDs), and where (in terms of log
9950 * block offsets): query the superblock.
9951 */
9952
9953 sb = journal->j_superblock;
9954 next_commit_ID = ntohl(sb->s_sequence);
9955 next_log_block = ntohl(sb->s_start);
9956
9957 first_commit_ID = next_commit_ID;
9958 if (pass == PASS_SCAN)
9959 info->start_transaction = first_commit_ID;
9960
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009961 /*
9962 * Now we walk through the log, transaction by transaction,
9963 * making sure that each transaction has a commit block in the
9964 * expected place. Each complete transaction gets replayed back
9965 * into the main filesystem.
9966 */
9967
9968 while (1) {
9969 int flags;
9970 char * tagp;
9971 journal_block_tag_t * tag;
9972 struct buffer_head * obh;
9973 struct buffer_head * nbh;
9974
9975 /* If we already know where to stop the log traversal,
9976 * check right now that we haven't gone past the end of
9977 * the log. */
9978
9979 if (pass != PASS_SCAN)
9980 if (tid_geq(next_commit_ID, info->end_transaction))
9981 break;
9982
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009983 /* Skip over each chunk of the transaction looking
9984 * either the next descriptor block or the final commit
9985 * record. */
9986
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009987 err = jread(&bh, journal, next_log_block);
9988 if (err)
9989 goto failed;
9990
9991 next_log_block++;
9992 wrap(journal, next_log_block);
9993
9994 /* What kind of buffer is it?
9995 *
9996 * If it is a descriptor block, check that it has the
9997 * expected sequence number. Otherwise, we're all done
9998 * here. */
9999
10000 tmp = (journal_header_t *)bh->b_data;
10001
10002 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
10003 brelse(bh);
10004 break;
10005 }
10006
10007 blocktype = ntohl(tmp->h_blocktype);
10008 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010009
10010 if (sequence != next_commit_ID) {
10011 brelse(bh);
10012 break;
10013 }
10014
10015 /* OK, we have a valid descriptor block which matches
10016 * all of the sequence number checks. What are we going
10017 * to do with it? That depends on the pass... */
10018
10019 switch(blocktype) {
10020 case JFS_DESCRIPTOR_BLOCK:
10021 /* If it is a valid descriptor block, replay it
10022 * in pass REPLAY; otherwise, just skip over the
10023 * blocks it describes. */
10024 if (pass != PASS_REPLAY) {
10025 next_log_block +=
10026 count_tags(bh, journal->j_blocksize);
10027 wrap(journal, next_log_block);
10028 brelse(bh);
10029 continue;
10030 }
10031
10032 /* A descriptor block: we can now write all of
10033 * the data blocks. Yay, useful work is finally
10034 * getting done here! */
10035
10036 tagp = &bh->b_data[sizeof(journal_header_t)];
10037 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
10038 <= journal->j_blocksize) {
10039 unsigned long io_block;
10040
10041 tag = (journal_block_tag_t *) tagp;
10042 flags = ntohl(tag->t_flags);
10043
10044 io_block = next_log_block++;
10045 wrap(journal, next_log_block);
10046 err = jread(&obh, journal, io_block);
10047 if (err) {
10048 /* Recover what we can, but
10049 * report failure at the end. */
10050 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000010051 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010052 "block %ld in log\n",
10053 err, io_block);
10054 } else {
10055 unsigned long blocknr;
10056
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010057 blocknr = ntohl(tag->t_blocknr);
10058
10059 /* If the block has been
10060 * revoked, then we're all done
10061 * here. */
10062 if (journal_test_revoke
10063 (journal, blocknr,
10064 next_commit_ID)) {
10065 brelse(obh);
10066 ++info->nr_revoke_hits;
10067 goto skip_write;
10068 }
10069
10070 /* Find a buffer for the new
10071 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010072 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010073 blocknr,
10074 journal->j_blocksize);
10075 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000010076 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010077 "during recovery.\n");
10078 err = -ENOMEM;
10079 brelse(bh);
10080 brelse(obh);
10081 goto failed;
10082 }
10083
10084 lock_buffer(nbh);
10085 memcpy(nbh->b_data, obh->b_data,
10086 journal->j_blocksize);
10087 if (flags & JFS_FLAG_ESCAPE) {
10088 *((unsigned int *)bh->b_data) =
10089 htonl(JFS_MAGIC_NUMBER);
10090 }
10091
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010092 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010093 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010094 ++info->nr_replays;
10095 /* ll_rw_block(WRITE, 1, &nbh); */
10096 unlock_buffer(nbh);
10097 brelse(obh);
10098 brelse(nbh);
10099 }
10100
10101 skip_write:
10102 tagp += sizeof(journal_block_tag_t);
10103 if (!(flags & JFS_FLAG_SAME_UUID))
10104 tagp += 16;
10105
10106 if (flags & JFS_FLAG_LAST_TAG)
10107 break;
10108 }
10109
10110 brelse(bh);
10111 continue;
10112
10113 case JFS_COMMIT_BLOCK:
10114 /* Found an expected commit block: not much to
10115 * do other than move on to the next sequence
10116 * number. */
10117 brelse(bh);
10118 next_commit_ID++;
10119 continue;
10120
10121 case JFS_REVOKE_BLOCK:
10122 /* If we aren't in the REVOKE pass, then we can
10123 * just skip over this block. */
10124 if (pass != PASS_REVOKE) {
10125 brelse(bh);
10126 continue;
10127 }
10128
10129 err = scan_revoke_records(journal, bh,
10130 next_commit_ID, info);
10131 brelse(bh);
10132 if (err)
10133 goto failed;
10134 continue;
10135
10136 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010137 goto done;
10138 }
10139 }
10140
10141 done:
10142 /*
10143 * We broke out of the log scan loop: either we came to the
10144 * known end of the log or we found an unexpected block in the
10145 * log. If the latter happened, then we know that the "current"
10146 * transaction marks the end of the valid log.
10147 */
10148
10149 if (pass == PASS_SCAN)
10150 info->end_transaction = next_commit_ID;
10151 else {
10152 /* It's really bad news if different passes end up at
10153 * different places (but possible due to IO errors). */
10154 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000010155 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010156 "transaction %u, expected %u\n",
10157 pass, next_commit_ID, info->end_transaction);
10158 if (!success)
10159 success = -EIO;
10160 }
10161 }
10162
10163 return success;
10164
10165 failed:
10166 return err;
10167}
10168
10169
10170/* Scan a revoke record, marking all blocks mentioned as revoked. */
10171
10172static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
10173 tid_t sequence, struct recovery_info *info)
10174{
10175 journal_revoke_header_t *header;
10176 int offset, max;
10177
10178 header = (journal_revoke_header_t *) bh->b_data;
10179 offset = sizeof(journal_revoke_header_t);
10180 max = ntohl(header->r_count);
10181
10182 while (offset < max) {
10183 unsigned long blocknr;
10184 int err;
10185
10186 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
10187 offset += 4;
10188 err = journal_set_revoke(journal, blocknr, sequence);
10189 if (err)
10190 return err;
10191 ++info->nr_revokes;
10192 }
10193 return 0;
10194}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010195
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010196
10197/*
10198 * rehash.c --- rebuild hash tree directories
10199 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010200 * This algorithm is designed for simplicity of implementation and to
10201 * pack the directory as much as possible. It however requires twice
10202 * as much memory as the size of the directory. The maximum size
10203 * directory supported using a 4k blocksize is roughly a gigabyte, and
10204 * so there may very well be problems with machines that don't have
10205 * virtual memory, and obscenely large directories.
10206 *
10207 * An alternate algorithm which is much more disk intensive could be
10208 * written, and probably will need to be written in the future. The
10209 * design goals of such an algorithm are: (a) use (roughly) constant
10210 * amounts of memory, no matter how large the directory, (b) the
10211 * directory must be safe at all times, even if e2fsck is interrupted
10212 * in the middle, (c) we must use minimal amounts of extra disk
10213 * blocks. This pretty much requires an incremental approach, where
10214 * we are reading from one part of the directory, and inserting into
10215 * the front half. So the algorithm will have to keep track of a
10216 * moving block boundary between the new tree and the old tree, and
10217 * files will need to be moved from the old directory and inserted
10218 * into the new tree. If the new directory requires space which isn't
10219 * yet available, blocks from the beginning part of the old directory
10220 * may need to be moved to the end of the directory to make room for
10221 * the new tree:
10222 *
10223 * --------------------------------------------------------
10224 * | new tree | | old tree |
10225 * --------------------------------------------------------
10226 * ^ ptr ^ptr
10227 * tail new head old
10228 *
10229 * This is going to be a pain in the tuckus to implement, and will
10230 * require a lot more disk accesses. So I'm going to skip it for now;
10231 * it's only really going to be an issue for really, really big
10232 * filesystems (when we reach the level of tens of millions of files
10233 * in a single directory). It will probably be easier to simply
10234 * require that e2fsck use VM first.
10235 */
10236
10237struct fill_dir_struct {
10238 char *buf;
10239 struct ext2_inode *inode;
10240 int err;
10241 e2fsck_t ctx;
10242 struct hash_entry *harray;
10243 int max_array, num_array;
10244 int dir_size;
10245 int compress;
10246 ino_t parent;
10247};
10248
10249struct hash_entry {
10250 ext2_dirhash_t hash;
10251 ext2_dirhash_t minor_hash;
10252 struct ext2_dir_entry *dir;
10253};
10254
10255struct out_dir {
10256 int num;
10257 int max;
10258 char *buf;
10259 ext2_dirhash_t *hashes;
10260};
10261
10262static int fill_dir_block(ext2_filsys fs,
10263 blk_t *block_nr,
10264 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010265 blk_t ref_block FSCK_ATTR((unused)),
10266 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010267 void *priv_data)
10268{
10269 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
10270 struct hash_entry *new_array, *ent;
10271 struct ext2_dir_entry *dirent;
10272 char *dir;
10273 unsigned int offset, dir_offset;
10274
10275 if (blockcnt < 0)
10276 return 0;
10277
10278 offset = blockcnt * fs->blocksize;
10279 if (offset + fs->blocksize > fd->inode->i_size) {
10280 fd->err = EXT2_ET_DIR_CORRUPTED;
10281 return BLOCK_ABORT;
10282 }
10283 dir = (fd->buf+offset);
10284 if (HOLE_BLKADDR(*block_nr)) {
10285 memset(dir, 0, fs->blocksize);
10286 dirent = (struct ext2_dir_entry *) dir;
10287 dirent->rec_len = fs->blocksize;
10288 } else {
10289 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
10290 if (fd->err)
10291 return BLOCK_ABORT;
10292 }
10293 /* While the directory block is "hot", index it. */
10294 dir_offset = 0;
10295 while (dir_offset < fs->blocksize) {
10296 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
10297 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
10298 (dirent->rec_len < 8) ||
10299 ((dirent->rec_len % 4) != 0) ||
10300 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
10301 fd->err = EXT2_ET_DIR_CORRUPTED;
10302 return BLOCK_ABORT;
10303 }
10304 dir_offset += dirent->rec_len;
10305 if (dirent->inode == 0)
10306 continue;
10307 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
10308 (dirent->name[0] == '.'))
10309 continue;
10310 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
10311 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
10312 fd->parent = dirent->inode;
10313 continue;
10314 }
10315 if (fd->num_array >= fd->max_array) {
10316 new_array = realloc(fd->harray,
10317 sizeof(struct hash_entry) * (fd->max_array+500));
10318 if (!new_array) {
10319 fd->err = ENOMEM;
10320 return BLOCK_ABORT;
10321 }
10322 fd->harray = new_array;
10323 fd->max_array += 500;
10324 }
10325 ent = fd->harray + fd->num_array++;
10326 ent->dir = dirent;
10327 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
10328 if (fd->compress)
10329 ent->hash = ent->minor_hash = 0;
10330 else {
10331 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
10332 dirent->name,
10333 dirent->name_len & 0xFF,
10334 fs->super->s_hash_seed,
10335 &ent->hash, &ent->minor_hash);
10336 if (fd->err)
10337 return BLOCK_ABORT;
10338 }
10339 }
10340
10341 return 0;
10342}
10343
10344/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010345static int name_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010346{
10347 const struct hash_entry *he_a = (const struct hash_entry *) a;
10348 const struct hash_entry *he_b = (const struct hash_entry *) b;
10349 int ret;
10350 int min_len;
10351
10352 min_len = he_a->dir->name_len;
10353 if (min_len > he_b->dir->name_len)
10354 min_len = he_b->dir->name_len;
10355
10356 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
10357 if (ret == 0) {
10358 if (he_a->dir->name_len > he_b->dir->name_len)
10359 ret = 1;
10360 else if (he_a->dir->name_len < he_b->dir->name_len)
10361 ret = -1;
10362 else
10363 ret = he_b->dir->inode - he_a->dir->inode;
10364 }
10365 return ret;
10366}
10367
10368/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010369static int hash_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010370{
10371 const struct hash_entry *he_a = (const struct hash_entry *) a;
10372 const struct hash_entry *he_b = (const struct hash_entry *) b;
10373 int ret;
10374
10375 if (he_a->hash > he_b->hash)
10376 ret = 1;
10377 else if (he_a->hash < he_b->hash)
10378 ret = -1;
10379 else {
10380 if (he_a->minor_hash > he_b->minor_hash)
10381 ret = 1;
10382 else if (he_a->minor_hash < he_b->minor_hash)
10383 ret = -1;
10384 else
10385 ret = name_cmp(a, b);
10386 }
10387 return ret;
10388}
10389
10390static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
10391 int blocks)
10392{
10393 void *new_mem;
10394
10395 if (outdir->max) {
10396 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
10397 if (!new_mem)
10398 return ENOMEM;
10399 outdir->buf = new_mem;
10400 new_mem = realloc(outdir->hashes,
10401 blocks * sizeof(ext2_dirhash_t));
10402 if (!new_mem)
10403 return ENOMEM;
10404 outdir->hashes = new_mem;
10405 } else {
10406 outdir->buf = malloc(blocks * fs->blocksize);
10407 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
10408 outdir->num = 0;
10409 }
10410 outdir->max = blocks;
10411 return 0;
10412}
10413
10414static void free_out_dir(struct out_dir *outdir)
10415{
Rob Landleye7c43b62006-03-01 16:39:45 +000010416 free(outdir->buf);
10417 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010418 outdir->max = 0;
10419 outdir->num =0;
10420}
10421
10422static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
10423 char ** ret)
10424{
10425 errcode_t retval;
10426
10427 if (outdir->num >= outdir->max) {
10428 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
10429 if (retval)
10430 return retval;
10431 }
10432 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
10433 memset(*ret, 0, fs->blocksize);
10434 return 0;
10435}
10436
10437/*
10438 * This function is used to make a unique filename. We do this by
10439 * appending ~0, and then incrementing the number. However, we cannot
10440 * expand the length of the filename beyond the padding available in
10441 * the directory entry.
10442 */
10443static void mutate_name(char *str, __u16 *len)
10444{
10445 int i;
10446 __u16 l = *len & 0xFF, h = *len & 0xff00;
10447
10448 /*
10449 * First check to see if it looks the name has been mutated
10450 * already
10451 */
10452 for (i = l-1; i > 0; i--) {
10453 if (!isdigit(str[i]))
10454 break;
10455 }
10456 if ((i == l-1) || (str[i] != '~')) {
10457 if (((l-1) & 3) < 2)
10458 l += 2;
10459 else
10460 l = (l+3) & ~3;
10461 str[l-2] = '~';
10462 str[l-1] = '0';
10463 *len = l | h;
10464 return;
10465 }
10466 for (i = l-1; i >= 0; i--) {
10467 if (isdigit(str[i])) {
10468 if (str[i] == '9')
10469 str[i] = '0';
10470 else {
10471 str[i]++;
10472 return;
10473 }
10474 continue;
10475 }
10476 if (i == 1) {
10477 if (str[0] == 'z')
10478 str[0] = 'A';
10479 else if (str[0] == 'Z') {
10480 str[0] = '~';
10481 str[1] = '0';
10482 } else
10483 str[0]++;
10484 } else if (i > 0) {
10485 str[i] = '1';
10486 str[i-1] = '~';
10487 } else {
10488 if (str[0] == '~')
10489 str[0] = 'a';
10490 else
10491 str[0]++;
10492 }
10493 break;
10494 }
10495}
10496
10497static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
10498 ext2_ino_t ino,
10499 struct fill_dir_struct *fd)
10500{
10501 struct problem_context pctx;
10502 struct hash_entry *ent, *prev;
10503 int i, j;
10504 int fixed = 0;
10505 char new_name[256];
10506 __u16 new_len;
10507
10508 clear_problem_context(&pctx);
10509 pctx.ino = ino;
10510
10511 for (i=1; i < fd->num_array; i++) {
10512 ent = fd->harray + i;
10513 prev = ent - 1;
10514 if (!ent->dir->inode ||
10515 ((ent->dir->name_len & 0xFF) !=
10516 (prev->dir->name_len & 0xFF)) ||
10517 (strncmp(ent->dir->name, prev->dir->name,
10518 ent->dir->name_len & 0xFF)))
10519 continue;
10520 pctx.dirent = ent->dir;
10521 if ((ent->dir->inode == prev->dir->inode) &&
10522 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
10523 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
10524 ent->dir->inode = 0;
10525 fixed++;
10526 continue;
10527 }
10528 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
10529 new_len = ent->dir->name_len;
10530 mutate_name(new_name, &new_len);
10531 for (j=0; j < fd->num_array; j++) {
10532 if ((i==j) ||
10533 ((ent->dir->name_len & 0xFF) !=
10534 (fd->harray[j].dir->name_len & 0xFF)) ||
10535 (strncmp(new_name, fd->harray[j].dir->name,
10536 new_len & 0xFF)))
10537 continue;
10538 mutate_name(new_name, &new_len);
10539
10540 j = -1;
10541 }
10542 new_name[new_len & 0xFF] = 0;
10543 pctx.str = new_name;
10544 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
10545 memcpy(ent->dir->name, new_name, new_len & 0xFF);
10546 ent->dir->name_len = new_len;
10547 ext2fs_dirhash(fs->super->s_def_hash_version,
10548 ent->dir->name,
10549 ent->dir->name_len & 0xFF,
10550 fs->super->s_hash_seed,
10551 &ent->hash, &ent->minor_hash);
10552 fixed++;
10553 }
10554 }
10555 return fixed;
10556}
10557
10558
10559static errcode_t copy_dir_entries(ext2_filsys fs,
10560 struct fill_dir_struct *fd,
10561 struct out_dir *outdir)
10562{
10563 errcode_t retval;
10564 char *block_start;
10565 struct hash_entry *ent;
10566 struct ext2_dir_entry *dirent;
10567 int i, rec_len, left;
10568 ext2_dirhash_t prev_hash;
10569 int offset;
10570
10571 outdir->max = 0;
10572 retval = alloc_size_dir(fs, outdir,
10573 (fd->dir_size / fs->blocksize) + 2);
10574 if (retval)
10575 return retval;
10576 outdir->num = fd->compress ? 0 : 1;
10577 offset = 0;
10578 outdir->hashes[0] = 0;
10579 prev_hash = 1;
10580 if ((retval = get_next_block(fs, outdir, &block_start)))
10581 return retval;
10582 dirent = (struct ext2_dir_entry *) block_start;
10583 left = fs->blocksize;
10584 for (i=0; i < fd->num_array; i++) {
10585 ent = fd->harray + i;
10586 if (ent->dir->inode == 0)
10587 continue;
10588 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
10589 if (rec_len > left) {
10590 if (left)
10591 dirent->rec_len += left;
10592 if ((retval = get_next_block(fs, outdir,
10593 &block_start)))
10594 return retval;
10595 offset = 0;
10596 }
10597 left = fs->blocksize - offset;
10598 dirent = (struct ext2_dir_entry *) (block_start + offset);
10599 if (offset == 0) {
10600 if (ent->hash == prev_hash)
10601 outdir->hashes[outdir->num-1] = ent->hash | 1;
10602 else
10603 outdir->hashes[outdir->num-1] = ent->hash;
10604 }
10605 dirent->inode = ent->dir->inode;
10606 dirent->name_len = ent->dir->name_len;
10607 dirent->rec_len = rec_len;
10608 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
10609 offset += rec_len;
10610 left -= rec_len;
10611 if (left < 12) {
10612 dirent->rec_len += left;
10613 offset += left;
10614 left = 0;
10615 }
10616 prev_hash = ent->hash;
10617 }
10618 if (left)
10619 dirent->rec_len += left;
10620
10621 return 0;
10622}
10623
10624
10625static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
10626 ext2_ino_t ino, ext2_ino_t parent)
10627{
10628 struct ext2_dir_entry *dir;
10629 struct ext2_dx_root_info *root;
10630 struct ext2_dx_countlimit *limits;
10631 int filetype = 0;
10632
10633 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
10634 filetype = EXT2_FT_DIR << 8;
10635
10636 memset(buf, 0, fs->blocksize);
10637 dir = (struct ext2_dir_entry *) buf;
10638 dir->inode = ino;
10639 dir->name[0] = '.';
10640 dir->name_len = 1 | filetype;
10641 dir->rec_len = 12;
10642 dir = (struct ext2_dir_entry *) (buf + 12);
10643 dir->inode = parent;
10644 dir->name[0] = '.';
10645 dir->name[1] = '.';
10646 dir->name_len = 2 | filetype;
10647 dir->rec_len = fs->blocksize - 12;
10648
10649 root = (struct ext2_dx_root_info *) (buf+24);
10650 root->reserved_zero = 0;
10651 root->hash_version = fs->super->s_def_hash_version;
10652 root->info_length = 8;
10653 root->indirect_levels = 0;
10654 root->unused_flags = 0;
10655
10656 limits = (struct ext2_dx_countlimit *) (buf+32);
10657 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
10658 limits->count = 0;
10659
10660 return root;
10661}
10662
10663
10664static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
10665{
10666 struct ext2_dir_entry *dir;
10667 struct ext2_dx_countlimit *limits;
10668
10669 memset(buf, 0, fs->blocksize);
10670 dir = (struct ext2_dir_entry *) buf;
10671 dir->inode = 0;
10672 dir->rec_len = fs->blocksize;
10673
10674 limits = (struct ext2_dx_countlimit *) (buf+8);
10675 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
10676 limits->count = 0;
10677
10678 return (struct ext2_dx_entry *) limits;
10679}
10680
10681/*
10682 * This function takes the leaf nodes which have been written in
10683 * outdir, and populates the root node and any necessary interior nodes.
10684 */
10685static errcode_t calculate_tree(ext2_filsys fs,
10686 struct out_dir *outdir,
10687 ext2_ino_t ino,
10688 ext2_ino_t parent)
10689{
10690 struct ext2_dx_root_info *root_info;
10691 struct ext2_dx_entry *root, *dx_ent = 0;
10692 struct ext2_dx_countlimit *root_limit, *limit;
10693 errcode_t retval;
10694 char * block_start;
10695 int i, c1, c2, nblks;
10696 int limit_offset, root_offset;
10697
10698 root_info = set_root_node(fs, outdir->buf, ino, parent);
10699 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
10700 root_info->info_length;
10701 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10702 c1 = root_limit->limit;
10703 nblks = outdir->num;
10704
10705 /* Write out the pointer blocks */
10706 if (nblks-1 <= c1) {
10707 /* Just write out the root block, and we're done */
10708 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
10709 for (i=1; i < nblks; i++) {
10710 root->block = ext2fs_cpu_to_le32(i);
10711 if (i != 1)
10712 root->hash =
10713 ext2fs_cpu_to_le32(outdir->hashes[i]);
10714 root++;
10715 c1--;
10716 }
10717 } else {
10718 c2 = 0;
10719 limit = 0;
10720 root_info->indirect_levels = 1;
10721 for (i=1; i < nblks; i++) {
10722 if (c1 == 0)
10723 return ENOSPC;
10724 if (c2 == 0) {
10725 if (limit)
10726 limit->limit = limit->count =
10727 ext2fs_cpu_to_le16(limit->limit);
10728 root = (struct ext2_dx_entry *)
10729 (outdir->buf + root_offset);
10730 root->block = ext2fs_cpu_to_le32(outdir->num);
10731 if (i != 1)
10732 root->hash =
10733 ext2fs_cpu_to_le32(outdir->hashes[i]);
10734 if ((retval = get_next_block(fs, outdir,
10735 &block_start)))
10736 return retval;
10737 dx_ent = set_int_node(fs, block_start);
10738 limit = (struct ext2_dx_countlimit *) dx_ent;
10739 c2 = limit->limit;
10740 root_offset += sizeof(struct ext2_dx_entry);
10741 c1--;
10742 }
10743 dx_ent->block = ext2fs_cpu_to_le32(i);
10744 if (c2 != limit->limit)
10745 dx_ent->hash =
10746 ext2fs_cpu_to_le32(outdir->hashes[i]);
10747 dx_ent++;
10748 c2--;
10749 }
10750 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
10751 limit->limit = ext2fs_cpu_to_le16(limit->limit);
10752 }
10753 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10754 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
10755 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
10756
10757 return 0;
10758}
10759
10760struct write_dir_struct {
10761 struct out_dir *outdir;
10762 errcode_t err;
10763 e2fsck_t ctx;
10764 int cleared;
10765};
10766
10767/*
10768 * Helper function which writes out a directory block.
10769 */
10770static int write_dir_block(ext2_filsys fs,
10771 blk_t *block_nr,
10772 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010773 blk_t ref_block FSCK_ATTR((unused)),
10774 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010775 void *priv_data)
10776{
10777 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
10778 blk_t blk;
10779 char *dir;
10780
10781 if (*block_nr == 0)
10782 return 0;
10783 if (blockcnt >= wd->outdir->num) {
10784 e2fsck_read_bitmaps(wd->ctx);
10785 blk = *block_nr;
10786 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
10787 ext2fs_block_alloc_stats(fs, blk, -1);
10788 *block_nr = 0;
10789 wd->cleared++;
10790 return BLOCK_CHANGED;
10791 }
10792 if (blockcnt < 0)
10793 return 0;
10794
10795 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
10796 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
10797 if (wd->err)
10798 return BLOCK_ABORT;
10799 return 0;
10800}
10801
10802static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
10803 struct out_dir *outdir,
10804 ext2_ino_t ino, int compress)
10805{
10806 struct write_dir_struct wd;
10807 errcode_t retval;
10808 struct ext2_inode inode;
10809
10810 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
10811 if (retval)
10812 return retval;
10813
10814 wd.outdir = outdir;
10815 wd.err = 0;
10816 wd.ctx = ctx;
10817 wd.cleared = 0;
10818
10819 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10820 write_dir_block, &wd);
10821 if (retval)
10822 return retval;
10823 if (wd.err)
10824 return wd.err;
10825
10826 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10827 if (compress)
10828 inode.i_flags &= ~EXT2_INDEX_FL;
10829 else
10830 inode.i_flags |= EXT2_INDEX_FL;
10831 inode.i_size = outdir->num * fs->blocksize;
10832 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
10833 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
10834
10835 return 0;
10836}
10837
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010838static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010839{
10840 ext2_filsys fs = ctx->fs;
10841 errcode_t retval;
10842 struct ext2_inode inode;
10843 char *dir_buf = 0;
10844 struct fill_dir_struct fd;
10845 struct out_dir outdir;
10846
10847 outdir.max = outdir.num = 0;
10848 outdir.buf = 0;
10849 outdir.hashes = 0;
10850 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10851
10852 retval = ENOMEM;
10853 fd.harray = 0;
10854 dir_buf = malloc(inode.i_size);
10855 if (!dir_buf)
10856 goto errout;
10857
10858 fd.max_array = inode.i_size / 32;
10859 fd.num_array = 0;
10860 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
10861 if (!fd.harray)
10862 goto errout;
10863
10864 fd.ctx = ctx;
10865 fd.buf = dir_buf;
10866 fd.inode = &inode;
10867 fd.err = 0;
10868 fd.dir_size = 0;
10869 fd.compress = 0;
10870 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
10871 (inode.i_size / fs->blocksize) < 2)
10872 fd.compress = 1;
10873 fd.parent = 0;
10874
10875 /* Read in the entire directory into memory */
10876 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10877 fill_dir_block, &fd);
10878 if (fd.err) {
10879 retval = fd.err;
10880 goto errout;
10881 }
10882
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010883 /* Sort the list */
10884resort:
10885 if (fd.compress)
10886 qsort(fd.harray+2, fd.num_array-2,
10887 sizeof(struct hash_entry), name_cmp);
10888 else
10889 qsort(fd.harray, fd.num_array,
10890 sizeof(struct hash_entry), hash_cmp);
10891
10892 /*
10893 * Look for duplicates
10894 */
10895 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
10896 goto resort;
10897
10898 if (ctx->options & E2F_OPT_NO) {
10899 retval = 0;
10900 goto errout;
10901 }
10902
10903 /*
10904 * Copy the directory entries. In a htree directory these
10905 * will become the leaf nodes.
10906 */
10907 retval = copy_dir_entries(fs, &fd, &outdir);
10908 if (retval)
10909 goto errout;
10910
10911 free(dir_buf); dir_buf = 0;
10912
10913 if (!fd.compress) {
10914 /* Calculate the interior nodes */
10915 retval = calculate_tree(fs, &outdir, ino, fd.parent);
10916 if (retval)
10917 goto errout;
10918 }
10919
10920 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010921
10922errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000010923 free(dir_buf);
10924 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010925
10926 free_out_dir(&outdir);
10927 return retval;
10928}
10929
10930void e2fsck_rehash_directories(e2fsck_t ctx)
10931{
10932 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010933 struct dir_info *dir;
10934 ext2_u32_iterate iter;
10935 ext2_ino_t ino;
10936 errcode_t retval;
10937 int i, cur, max, all_dirs, dir_index, first = 1;
10938
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010939 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
10940
10941 if (!ctx->dirs_to_hash && !all_dirs)
10942 return;
10943
10944 e2fsck_get_lost_and_found(ctx, 0);
10945
10946 clear_problem_context(&pctx);
10947
10948 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
10949 cur = 0;
10950 if (all_dirs) {
10951 i = 0;
10952 max = e2fsck_get_num_dirinfo(ctx);
10953 } else {
10954 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
10955 &iter);
10956 if (retval) {
10957 pctx.errcode = retval;
10958 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
10959 return;
10960 }
10961 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
10962 }
10963 while (1) {
10964 if (all_dirs) {
10965 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
10966 break;
10967 ino = dir->ino;
10968 } else {
10969 if (!ext2fs_u32_list_iterate(iter, &ino))
10970 break;
10971 }
10972 if (ino == ctx->lost_and_found)
10973 continue;
10974 pctx.dir = ino;
10975 if (first) {
10976 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
10977 first = 0;
10978 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010979 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
10980 if (pctx.errcode) {
10981 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
10982 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
10983 }
10984 if (ctx->progress && !ctx->progress_fd)
10985 e2fsck_simple_progress(ctx, "Rebuilding directory",
10986 100.0 * (float) (++cur) / (float) max, ino);
10987 }
10988 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
10989 if (!all_dirs)
10990 ext2fs_u32_list_iterate_end(iter);
10991
Rob Landleye7c43b62006-03-01 16:39:45 +000010992 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010993 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010994}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010995
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010996/*
10997 * linux/fs/revoke.c
10998 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010999 * Journal revoke routines for the generic filesystem journaling code;
11000 * part of the ext2fs journaling system.
11001 *
11002 * Revoke is the mechanism used to prevent old log records for deleted
11003 * metadata from being replayed on top of newer data using the same
11004 * blocks. The revoke mechanism is used in two separate places:
11005 *
11006 * + Commit: during commit we write the entire list of the current
11007 * transaction's revoked blocks to the journal
11008 *
11009 * + Recovery: during recovery we record the transaction ID of all
11010 * revoked blocks. If there are multiple revoke records in the log
11011 * for a single block, only the last one counts, and if there is a log
11012 * entry for a block beyond the last revoke, then that log entry still
11013 * gets replayed.
11014 *
11015 * We can get interactions between revokes and new log data within a
11016 * single transaction:
11017 *
11018 * Block is revoked and then journaled:
11019 * The desired end result is the journaling of the new block, so we
11020 * cancel the revoke before the transaction commits.
11021 *
11022 * Block is journaled and then revoked:
11023 * The revoke must take precedence over the write of the block, so we
11024 * need either to cancel the journal entry or to write the revoke
11025 * later in the log than the log block. In this case, we choose the
11026 * latter: journaling a block cancels any revoke record for that block
11027 * in the current transaction, so any revoke for that block in the
11028 * transaction must have happened after the block was journaled and so
11029 * the revoke must take precedence.
11030 *
11031 * Block is revoked and then written as data:
11032 * The data write is allowed to succeed, but the revoke is _not_
11033 * cancelled. We still need to prevent old log records from
11034 * overwriting the new data. We don't even need to clear the revoke
11035 * bit here.
11036 *
11037 * Revoke information on buffers is a tri-state value:
11038 *
11039 * RevokeValid clear: no cached revoke status, need to look it up
11040 * RevokeValid set, Revoked clear:
11041 * buffer has not been revoked, and cancel_revoke
11042 * need do nothing.
11043 * RevokeValid set, Revoked set:
11044 * buffer has been revoked.
11045 */
11046
11047static kmem_cache_t *revoke_record_cache;
11048static kmem_cache_t *revoke_table_cache;
11049
11050/* Each revoke record represents one single revoked block. During
11051 journal replay, this involves recording the transaction ID of the
11052 last transaction to revoke this block. */
11053
11054struct jbd_revoke_record_s
11055{
11056 struct list_head hash;
11057 tid_t sequence; /* Used for recovery only */
11058 unsigned long blocknr;
11059};
11060
11061
11062/* The revoke table is just a simple hash table of revoke records. */
11063struct jbd_revoke_table_s
11064{
11065 /* It is conceivable that we might want a larger hash table
11066 * for recovery. Must be a power of two. */
11067 int hash_size;
11068 int hash_shift;
11069 struct list_head *hash_table;
11070};
11071
11072
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011073/* Utility functions to maintain the revoke table */
11074
11075/* Borrowed from buffer.c: this is a tried and tested block hash function */
11076static inline int hash(journal_t *journal, unsigned long block)
11077{
11078 struct jbd_revoke_table_s *table = journal->j_revoke;
11079 int hash_shift = table->hash_shift;
11080
11081 return ((block << (hash_shift - 6)) ^
11082 (block >> 13) ^
11083 (block << (hash_shift - 12))) & (table->hash_size - 1);
11084}
11085
11086static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
11087 tid_t seq)
11088{
11089 struct list_head *hash_list;
11090 struct jbd_revoke_record_s *record;
11091
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011092 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
11093 if (!record)
11094 goto oom;
11095
11096 record->sequence = seq;
11097 record->blocknr = blocknr;
11098 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11099 list_add(&record->hash, hash_list);
11100 return 0;
11101
11102oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011103 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011104}
11105
11106/* Find a revoke record in the journal's hash table. */
11107
11108static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
11109 unsigned long blocknr)
11110{
11111 struct list_head *hash_list;
11112 struct jbd_revoke_record_s *record;
11113
11114 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11115
11116 record = (struct jbd_revoke_record_s *) hash_list->next;
11117 while (&(record->hash) != hash_list) {
11118 if (record->blocknr == blocknr)
11119 return record;
11120 record = (struct jbd_revoke_record_s *) record->hash.next;
11121 }
11122 return NULL;
11123}
11124
11125int journal_init_revoke_caches(void)
11126{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011127 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011128 if (revoke_record_cache == 0)
11129 return -ENOMEM;
11130
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011131 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011132 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011133 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011134 revoke_record_cache = NULL;
11135 return -ENOMEM;
11136 }
11137 return 0;
11138}
11139
11140void journal_destroy_revoke_caches(void)
11141{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011142 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011143 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011144 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011145 revoke_table_cache = 0;
11146}
11147
11148/* Initialise the revoke table for a given journal to a given size. */
11149
11150int journal_init_revoke(journal_t *journal, int hash_size)
11151{
11152 int shift, tmp;
11153
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011154 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
11155 if (!journal->j_revoke)
11156 return -ENOMEM;
11157
11158 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011159 journal->j_revoke->hash_size = hash_size;
11160
11161 shift = 0;
11162 tmp = hash_size;
11163 while((tmp >>= 1UL) != 0UL)
11164 shift++;
11165 journal->j_revoke->hash_shift = shift;
11166
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011167 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011168 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011169 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011170 journal->j_revoke = NULL;
11171 return -ENOMEM;
11172 }
11173
11174 for (tmp = 0; tmp < hash_size; tmp++)
11175 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
11176
11177 return 0;
11178}
11179
11180/* Destoy a journal's revoke table. The table must already be empty! */
11181
11182void journal_destroy_revoke(journal_t *journal)
11183{
11184 struct jbd_revoke_table_s *table;
11185 struct list_head *hash_list;
11186 int i;
11187
11188 table = journal->j_revoke;
11189 if (!table)
11190 return;
11191
11192 for (i=0; i<table->hash_size; i++) {
11193 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011194 }
11195
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011196 free(table->hash_table);
11197 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011198 journal->j_revoke = NULL;
11199}
11200
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011201/*
11202 * Revoke support for recovery.
11203 *
11204 * Recovery needs to be able to:
11205 *
11206 * record all revoke records, including the tid of the latest instance
11207 * of each revoke in the journal
11208 *
11209 * check whether a given block in a given transaction should be replayed
11210 * (ie. has not been revoked by a revoke record in that or a subsequent
11211 * transaction)
11212 *
11213 * empty the revoke table after recovery.
11214 */
11215
11216/*
11217 * First, setting revoke records. We create a new revoke record for
11218 * every block ever revoked in the log as we scan it for recovery, and
11219 * we update the existing records if we find multiple revokes for a
11220 * single block.
11221 */
11222
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011223int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011224 tid_t sequence)
11225{
11226 struct jbd_revoke_record_s *record;
11227
11228 record = find_revoke_record(journal, blocknr);
11229 if (record) {
11230 /* If we have multiple occurences, only record the
11231 * latest sequence number in the hashed record */
11232 if (tid_gt(sequence, record->sequence))
11233 record->sequence = sequence;
11234 return 0;
11235 }
11236 return insert_revoke_hash(journal, blocknr, sequence);
11237}
11238
11239/*
11240 * Test revoke records. For a given block referenced in the log, has
11241 * that block been revoked? A revoke record with a given transaction
11242 * sequence number revokes all blocks in that transaction and earlier
11243 * ones, but later transactions still need replayed.
11244 */
11245
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011246int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011247 tid_t sequence)
11248{
11249 struct jbd_revoke_record_s *record;
11250
11251 record = find_revoke_record(journal, blocknr);
11252 if (!record)
11253 return 0;
11254 if (tid_gt(sequence, record->sequence))
11255 return 0;
11256 return 1;
11257}
11258
11259/*
11260 * Finally, once recovery is over, we need to clear the revoke table so
11261 * that it can be reused by the running filesystem.
11262 */
11263
11264void journal_clear_revoke(journal_t *journal)
11265{
11266 int i;
11267 struct list_head *hash_list;
11268 struct jbd_revoke_record_s *record;
11269 struct jbd_revoke_table_s *revoke_var;
11270
11271 revoke_var = journal->j_revoke;
11272
11273 for (i = 0; i < revoke_var->hash_size; i++) {
11274 hash_list = &revoke_var->hash_table[i];
11275 while (!list_empty(hash_list)) {
11276 record = (struct jbd_revoke_record_s*) hash_list->next;
11277 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011278 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011279 }
11280 }
11281}
11282
11283/*
11284 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011285 */
11286
11287#define MIN_CHECK 1
11288#define MAX_CHECK 2
11289
11290static void check_super_value(e2fsck_t ctx, const char *descr,
11291 unsigned long value, int flags,
11292 unsigned long min_val, unsigned long max_val)
11293{
11294 struct problem_context pctx;
11295
11296 if (((flags & MIN_CHECK) && (value < min_val)) ||
11297 ((flags & MAX_CHECK) && (value > max_val))) {
11298 clear_problem_context(&pctx);
11299 pctx.num = value;
11300 pctx.str = descr;
11301 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11302 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11303 }
11304}
11305
11306/*
11307 * This routine may get stubbed out in special compilations of the
11308 * e2fsck code..
11309 */
11310#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011311static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011312{
11313 return (ext2fs_get_device_size(ctx->filesystem_name,
11314 EXT2_BLOCK_SIZE(ctx->fs->super),
11315 &ctx->num_blocks));
11316}
11317#endif
11318
11319/*
11320 * helper function to release an inode
11321 */
11322struct process_block_struct {
11323 e2fsck_t ctx;
11324 char *buf;
11325 struct problem_context *pctx;
11326 int truncating;
11327 int truncate_offset;
11328 e2_blkcnt_t truncate_block;
11329 int truncated_blocks;
11330 int abort;
11331 errcode_t errcode;
11332};
11333
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011334static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011335 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011336 blk_t ref_blk FSCK_ATTR((unused)),
11337 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011338 void *priv_data)
11339{
11340 struct process_block_struct *pb;
11341 e2fsck_t ctx;
11342 struct problem_context *pctx;
11343 blk_t blk = *block_nr;
11344 int retval = 0;
11345
11346 pb = (struct process_block_struct *) priv_data;
11347 ctx = pb->ctx;
11348 pctx = pb->pctx;
11349
11350 pctx->blk = blk;
11351 pctx->blkcount = blockcnt;
11352
11353 if (HOLE_BLKADDR(blk))
11354 return 0;
11355
11356 if ((blk < fs->super->s_first_data_block) ||
11357 (blk >= fs->super->s_blocks_count)) {
11358 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
11359 return_abort:
11360 pb->abort = 1;
11361 return BLOCK_ABORT;
11362 }
11363
11364 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
11365 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
11366 goto return_abort;
11367 }
11368
11369 /*
11370 * If we are deleting an orphan, then we leave the fields alone.
11371 * If we are truncating an orphan, then update the inode fields
11372 * and clean up any partial block data.
11373 */
11374 if (pb->truncating) {
11375 /*
11376 * We only remove indirect blocks if they are
11377 * completely empty.
11378 */
11379 if (blockcnt < 0) {
11380 int i, limit;
11381 blk_t *bp;
11382
11383 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11384 pb->buf);
11385 if (pb->errcode)
11386 goto return_abort;
11387
11388 limit = fs->blocksize >> 2;
11389 for (i = 0, bp = (blk_t *) pb->buf;
11390 i < limit; i++, bp++)
11391 if (*bp)
11392 return 0;
11393 }
11394 /*
11395 * We don't remove direct blocks until we've reached
11396 * the truncation block.
11397 */
11398 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
11399 return 0;
11400 /*
11401 * If part of the last block needs truncating, we do
11402 * it here.
11403 */
11404 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
11405 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11406 pb->buf);
11407 if (pb->errcode)
11408 goto return_abort;
11409 memset(pb->buf + pb->truncate_offset, 0,
11410 fs->blocksize - pb->truncate_offset);
11411 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
11412 pb->buf);
11413 if (pb->errcode)
11414 goto return_abort;
11415 }
11416 pb->truncated_blocks++;
11417 *block_nr = 0;
11418 retval |= BLOCK_CHANGED;
11419 }
11420
11421 ext2fs_block_alloc_stats(fs, blk, -1);
11422 return retval;
11423}
11424
11425/*
11426 * This function releases an inode. Returns 1 if an inconsistency was
11427 * found. If the inode has a link count, then it is being truncated and
11428 * not deleted.
11429 */
11430static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
11431 struct ext2_inode *inode, char *block_buf,
11432 struct problem_context *pctx)
11433{
11434 struct process_block_struct pb;
11435 ext2_filsys fs = ctx->fs;
11436 errcode_t retval;
11437 __u32 count;
11438
11439 if (!ext2fs_inode_has_valid_blocks(inode))
11440 return 0;
11441
11442 pb.buf = block_buf + 3 * ctx->fs->blocksize;
11443 pb.ctx = ctx;
11444 pb.abort = 0;
11445 pb.errcode = 0;
11446 pb.pctx = pctx;
11447 if (inode->i_links_count) {
11448 pb.truncating = 1;
11449 pb.truncate_block = (e2_blkcnt_t)
11450 ((((long long)inode->i_size_high << 32) +
11451 inode->i_size + fs->blocksize - 1) /
11452 fs->blocksize);
11453 pb.truncate_offset = inode->i_size % fs->blocksize;
11454 } else {
11455 pb.truncating = 0;
11456 pb.truncate_block = 0;
11457 pb.truncate_offset = 0;
11458 }
11459 pb.truncated_blocks = 0;
11460 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
11461 block_buf, release_inode_block, &pb);
11462 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011463 bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011464 ino);
11465 return 1;
11466 }
11467 if (pb.abort)
11468 return 1;
11469
11470 /* Refresh the inode since ext2fs_block_iterate may have changed it */
11471 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
11472
11473 if (pb.truncated_blocks)
11474 inode->i_blocks -= pb.truncated_blocks *
11475 (fs->blocksize / 512);
11476
11477 if (inode->i_file_acl) {
11478 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
11479 block_buf, -1, &count);
11480 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
11481 retval = 0;
11482 count = 1;
11483 }
11484 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011485 bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011486 ino);
11487 return 1;
11488 }
11489 if (count == 0)
11490 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
11491 inode->i_file_acl = 0;
11492 }
11493 return 0;
11494}
11495
11496/*
11497 * This function releases all of the orphan inodes. It returns 1 if
11498 * it hit some error, and 0 on success.
11499 */
11500static int release_orphan_inodes(e2fsck_t ctx)
11501{
11502 ext2_filsys fs = ctx->fs;
11503 ext2_ino_t ino, next_ino;
11504 struct ext2_inode inode;
11505 struct problem_context pctx;
11506 char *block_buf;
11507
11508 if ((ino = fs->super->s_last_orphan) == 0)
11509 return 0;
11510
11511 /*
11512 * Win or lose, we won't be using the head of the orphan inode
11513 * list again.
11514 */
11515 fs->super->s_last_orphan = 0;
11516 ext2fs_mark_super_dirty(fs);
11517
11518 /*
11519 * If the filesystem contains errors, don't run the orphan
11520 * list, since the orphan list can't be trusted; and we're
11521 * going to be running a full e2fsck run anyway...
11522 */
11523 if (fs->super->s_state & EXT2_ERROR_FS)
11524 return 0;
11525
11526 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
11527 (ino > fs->super->s_inodes_count)) {
11528 clear_problem_context(&pctx);
11529 pctx.ino = ino;
11530 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
11531 return 1;
11532 }
11533
11534 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
11535 "block iterate buffer");
11536 e2fsck_read_bitmaps(ctx);
11537
11538 while (ino) {
11539 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
11540 clear_problem_context(&pctx);
11541 pctx.ino = ino;
11542 pctx.inode = &inode;
11543 pctx.str = inode.i_links_count ? _("Truncating") :
11544 _("Clearing");
11545
11546 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
11547
11548 next_ino = inode.i_dtime;
11549 if (next_ino &&
11550 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
11551 (next_ino > fs->super->s_inodes_count))) {
11552 pctx.ino = next_ino;
11553 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
11554 goto return_abort;
11555 }
11556
11557 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
11558 goto return_abort;
11559
11560 if (!inode.i_links_count) {
11561 ext2fs_inode_alloc_stats2(fs, ino, -1,
11562 LINUX_S_ISDIR(inode.i_mode));
11563 inode.i_dtime = time(0);
11564 } else {
11565 inode.i_dtime = 0;
11566 }
11567 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
11568 ino = next_ino;
11569 }
11570 ext2fs_free_mem(&block_buf);
11571 return 0;
11572return_abort:
11573 ext2fs_free_mem(&block_buf);
11574 return 1;
11575}
11576
11577/*
11578 * Check the resize inode to make sure it is sane. We check both for
11579 * the case where on-line resizing is not enabled (in which case the
11580 * resize inode should be cleared) as well as the case where on-line
11581 * resizing is enabled.
11582 */
11583static void check_resize_inode(e2fsck_t ctx)
11584{
11585 ext2_filsys fs = ctx->fs;
11586 struct ext2_inode inode;
11587 struct problem_context pctx;
11588 int i, j, gdt_off, ind_off;
11589 blk_t blk, pblk, expect;
11590 __u32 *dind_buf = 0, *ind_buf;
11591 errcode_t retval;
11592
11593 clear_problem_context(&pctx);
11594
11595 /*
11596 * If the resize inode feature isn't set, then
11597 * s_reserved_gdt_blocks must be zero.
11598 */
11599 if (!(fs->super->s_feature_compat &
11600 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11601 if (fs->super->s_reserved_gdt_blocks) {
11602 pctx.num = fs->super->s_reserved_gdt_blocks;
11603 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
11604 &pctx)) {
11605 fs->super->s_reserved_gdt_blocks = 0;
11606 ext2fs_mark_super_dirty(fs);
11607 }
11608 }
11609 }
11610
Mike Frysinger874af852006-03-08 07:03:27 +000011611 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011612 pctx.ino = EXT2_RESIZE_INO;
11613 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
11614 if (retval) {
11615 if (fs->super->s_feature_compat &
11616 EXT2_FEATURE_COMPAT_RESIZE_INODE)
11617 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11618 return;
11619 }
11620
11621 /*
11622 * If the resize inode feature isn't set, check to make sure
11623 * the resize inode is cleared; then we're done.
11624 */
11625 if (!(fs->super->s_feature_compat &
11626 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11627 for (i=0; i < EXT2_N_BLOCKS; i++) {
11628 if (inode.i_block[i])
11629 break;
11630 }
11631 if ((i < EXT2_N_BLOCKS) &&
11632 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
11633 memset(&inode, 0, sizeof(inode));
11634 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11635 "clear_resize");
11636 }
11637 return;
11638 }
11639
11640 /*
11641 * The resize inode feature is enabled; check to make sure the
11642 * only block in use is the double indirect block
11643 */
11644 blk = inode.i_block[EXT2_DIND_BLOCK];
11645 for (i=0; i < EXT2_N_BLOCKS; i++) {
11646 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
11647 break;
11648 }
11649 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
11650 !(inode.i_mode & LINUX_S_IFREG) ||
11651 (blk < fs->super->s_first_data_block ||
11652 blk >= fs->super->s_blocks_count)) {
11653 resize_inode_invalid:
11654 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
11655 memset(&inode, 0, sizeof(inode));
11656 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11657 "clear_resize");
11658 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11659 }
11660 if (!(ctx->options & E2F_OPT_READONLY)) {
11661 fs->super->s_state &= ~EXT2_VALID_FS;
11662 ext2fs_mark_super_dirty(fs);
11663 }
11664 goto cleanup;
11665 }
11666 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
11667 "resize dind buffer");
11668 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
11669
11670 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
11671 if (retval)
11672 goto resize_inode_invalid;
11673
11674 gdt_off = fs->desc_blocks;
11675 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
11676 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
11677 i++, gdt_off++, pblk++) {
11678 gdt_off %= fs->blocksize/4;
11679 if (dind_buf[gdt_off] != pblk)
11680 goto resize_inode_invalid;
11681 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
11682 if (retval)
11683 goto resize_inode_invalid;
11684 ind_off = 0;
11685 for (j = 1; j < fs->group_desc_count; j++) {
11686 if (!ext2fs_bg_has_super(fs, j))
11687 continue;
11688 expect = pblk + (j * fs->super->s_blocks_per_group);
11689 if (ind_buf[ind_off] != expect)
11690 goto resize_inode_invalid;
11691 ind_off++;
11692 }
11693 }
11694
11695cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000011696 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011697
11698 }
11699
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011700static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011701{
11702 ext2_filsys fs = ctx->fs;
11703 blk_t first_block, last_block;
11704 struct ext2_super_block *sb = fs->super;
11705 struct ext2_group_desc *gd;
11706 blk_t blocks_per_group = fs->super->s_blocks_per_group;
11707 blk_t bpg_max;
11708 int inodes_per_block;
11709 int ipg_max;
11710 int inode_size;
11711 dgrp_t i;
11712 blk_t should_be;
11713 struct problem_context pctx;
11714 __u32 free_blocks = 0, free_inodes = 0;
11715
11716 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
11717 ipg_max = inodes_per_block * (blocks_per_group - 4);
11718 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
11719 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
11720 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
11721 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
11722 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
11723
11724 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11725 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
11726 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11727 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
11728 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
11729 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
11730
11731 clear_problem_context(&pctx);
11732
11733 /*
11734 * Verify the super block constants...
11735 */
11736 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
11737 MIN_CHECK, 1, 0);
11738 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
11739 MIN_CHECK, 1, 0);
11740 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
11741 MAX_CHECK, 0, sb->s_blocks_count);
11742 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
11743 MIN_CHECK | MAX_CHECK, 0,
11744 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
11745 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
11746 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
11747 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
11748 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
11749 bpg_max);
11750 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
11751 MIN_CHECK | MAX_CHECK, 8, bpg_max);
11752 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
11753 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
11754 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
11755 MAX_CHECK, 0, sb->s_blocks_count / 2);
11756 check_super_value(ctx, "reserved_gdt_blocks",
11757 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
11758 fs->blocksize/4);
11759 inode_size = EXT2_INODE_SIZE(sb);
11760 check_super_value(ctx, "inode_size",
11761 inode_size, MIN_CHECK | MAX_CHECK,
11762 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
11763 if (inode_size & (inode_size - 1)) {
11764 pctx.num = inode_size;
11765 pctx.str = "inode_size";
11766 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11767 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11768 return;
11769 }
11770
11771 if (!ctx->num_blocks) {
11772 pctx.errcode = e2fsck_get_device_size(ctx);
11773 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
11774 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
11775 ctx->flags |= E2F_FLAG_ABORT;
11776 return;
11777 }
11778 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
11779 (ctx->num_blocks < sb->s_blocks_count)) {
11780 pctx.blk = sb->s_blocks_count;
11781 pctx.blk2 = ctx->num_blocks;
11782 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
11783 ctx->flags |= E2F_FLAG_ABORT;
11784 return;
11785 }
11786 }
11787 }
11788
11789 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
11790 pctx.blk = EXT2_BLOCK_SIZE(sb);
11791 pctx.blk2 = EXT2_FRAG_SIZE(sb);
11792 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
11793 ctx->flags |= E2F_FLAG_ABORT;
11794 return;
11795 }
11796
11797 should_be = sb->s_frags_per_group >>
11798 (sb->s_log_block_size - sb->s_log_frag_size);
11799 if (sb->s_blocks_per_group != should_be) {
11800 pctx.blk = sb->s_blocks_per_group;
11801 pctx.blk2 = should_be;
11802 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
11803 ctx->flags |= E2F_FLAG_ABORT;
11804 return;
11805 }
11806
11807 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
11808 if (sb->s_first_data_block != should_be) {
11809 pctx.blk = sb->s_first_data_block;
11810 pctx.blk2 = should_be;
11811 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
11812 ctx->flags |= E2F_FLAG_ABORT;
11813 return;
11814 }
11815
11816 should_be = sb->s_inodes_per_group * fs->group_desc_count;
11817 if (sb->s_inodes_count != should_be) {
11818 pctx.ino = sb->s_inodes_count;
11819 pctx.ino2 = should_be;
11820 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
11821 sb->s_inodes_count = should_be;
11822 ext2fs_mark_super_dirty(fs);
11823 }
11824 }
11825
11826 /*
11827 * Verify the group descriptors....
11828 */
11829 first_block = sb->s_first_data_block;
11830 last_block = first_block + blocks_per_group;
11831
11832 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
11833 pctx.group = i;
11834
11835 if (i == fs->group_desc_count - 1)
11836 last_block = sb->s_blocks_count;
11837 if ((gd->bg_block_bitmap < first_block) ||
11838 (gd->bg_block_bitmap >= last_block)) {
11839 pctx.blk = gd->bg_block_bitmap;
11840 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
11841 gd->bg_block_bitmap = 0;
11842 }
11843 if (gd->bg_block_bitmap == 0) {
11844 ctx->invalid_block_bitmap_flag[i]++;
11845 ctx->invalid_bitmaps++;
11846 }
11847 if ((gd->bg_inode_bitmap < first_block) ||
11848 (gd->bg_inode_bitmap >= last_block)) {
11849 pctx.blk = gd->bg_inode_bitmap;
11850 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
11851 gd->bg_inode_bitmap = 0;
11852 }
11853 if (gd->bg_inode_bitmap == 0) {
11854 ctx->invalid_inode_bitmap_flag[i]++;
11855 ctx->invalid_bitmaps++;
11856 }
11857 if ((gd->bg_inode_table < first_block) ||
11858 ((gd->bg_inode_table +
11859 fs->inode_blocks_per_group - 1) >= last_block)) {
11860 pctx.blk = gd->bg_inode_table;
11861 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
11862 gd->bg_inode_table = 0;
11863 }
11864 if (gd->bg_inode_table == 0) {
11865 ctx->invalid_inode_table_flag[i]++;
11866 ctx->invalid_bitmaps++;
11867 }
11868 free_blocks += gd->bg_free_blocks_count;
11869 free_inodes += gd->bg_free_inodes_count;
11870 first_block += sb->s_blocks_per_group;
11871 last_block += sb->s_blocks_per_group;
11872
11873 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
11874 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
11875 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
11876 ext2fs_unmark_valid(fs);
11877
11878 }
11879
11880 /*
11881 * Update the global counts from the block group counts. This
11882 * is needed for an experimental patch which eliminates
11883 * locking the entire filesystem when allocating blocks or
11884 * inodes; if the filesystem is not unmounted cleanly, the
11885 * global counts may not be accurate.
11886 */
11887 if ((free_blocks != sb->s_free_blocks_count) ||
11888 (free_inodes != sb->s_free_inodes_count)) {
11889 if (ctx->options & E2F_OPT_READONLY)
11890 ext2fs_unmark_valid(fs);
11891 else {
11892 sb->s_free_blocks_count = free_blocks;
11893 sb->s_free_inodes_count = free_inodes;
11894 ext2fs_mark_super_dirty(fs);
11895 }
11896 }
11897
11898 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
11899 (sb->s_free_inodes_count > sb->s_inodes_count))
11900 ext2fs_unmark_valid(fs);
11901
11902
11903 /*
11904 * If we have invalid bitmaps, set the error state of the
11905 * filesystem.
11906 */
11907 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
11908 sb->s_state &= ~EXT2_VALID_FS;
11909 ext2fs_mark_super_dirty(fs);
11910 }
11911
11912 clear_problem_context(&pctx);
11913
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011914 /*
11915 * If the UUID field isn't assigned, assign it.
11916 */
11917 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
11918 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
11919 uuid_generate(sb->s_uuid);
11920 ext2fs_mark_super_dirty(fs);
11921 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
11922 }
11923 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011924
Rob Landley3e72c592006-04-06 22:49:04 +000011925 /* FIXME - HURD support?
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011926 * For the Hurd, check to see if the filetype option is set,
11927 * since it doesn't support it.
11928 */
11929 if (!(ctx->options & E2F_OPT_READONLY) &&
11930 fs->super->s_creator_os == EXT2_OS_HURD &&
11931 (fs->super->s_feature_incompat &
11932 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
11933 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
11934 fs->super->s_feature_incompat &=
11935 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
11936 ext2fs_mark_super_dirty(fs);
11937
11938 }
11939 }
11940
11941 /*
11942 * If we have any of the compatibility flags set, we need to have a
11943 * revision 1 filesystem. Most kernels will not check the flags on
11944 * a rev 0 filesystem and we may have corruption issues because of
11945 * the incompatible changes to the filesystem.
11946 */
11947 if (!(ctx->options & E2F_OPT_READONLY) &&
11948 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
11949 (fs->super->s_feature_compat ||
11950 fs->super->s_feature_ro_compat ||
11951 fs->super->s_feature_incompat) &&
11952 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
11953 ext2fs_update_dynamic_rev(fs);
11954 ext2fs_mark_super_dirty(fs);
11955 }
11956
11957 check_resize_inode(ctx);
11958
11959 /*
11960 * Clean up any orphan inodes, if present.
11961 */
11962 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
11963 fs->super->s_state &= ~EXT2_VALID_FS;
11964 ext2fs_mark_super_dirty(fs);
11965 }
11966
11967 /*
11968 * Move the ext3 journal file, if necessary.
11969 */
11970 e2fsck_move_ext3_journal(ctx);
11971 return;
11972}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011973
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011974/*
11975 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011976 */
11977
11978#ifdef ENABLE_SWAPFS
11979
11980struct swap_block_struct {
11981 ext2_ino_t ino;
11982 int isdir;
11983 errcode_t errcode;
11984 char *dir_buf;
11985 struct ext2_inode *inode;
11986};
11987
11988/*
11989 * This is a helper function for block_iterate. We mark all of the
11990 * indirect and direct blocks as changed, so that block_iterate will
11991 * write them out.
11992 */
11993static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
11994 void *priv_data)
11995{
11996 errcode_t retval;
11997
11998 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
11999
12000 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
12001 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
12002 if (retval) {
12003 sb->errcode = retval;
12004 return BLOCK_ABORT;
12005 }
12006 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
12007 if (retval) {
12008 sb->errcode = retval;
12009 return BLOCK_ABORT;
12010 }
12011 }
12012 if (blockcnt >= 0) {
12013 if (blockcnt < EXT2_NDIR_BLOCKS)
12014 return 0;
12015 return BLOCK_CHANGED;
12016 }
12017 if (blockcnt == BLOCK_COUNT_IND) {
12018 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
12019 return 0;
12020 return BLOCK_CHANGED;
12021 }
12022 if (blockcnt == BLOCK_COUNT_DIND) {
12023 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
12024 return 0;
12025 return BLOCK_CHANGED;
12026 }
12027 if (blockcnt == BLOCK_COUNT_TIND) {
12028 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
12029 return 0;
12030 return BLOCK_CHANGED;
12031 }
12032 return BLOCK_CHANGED;
12033}
12034
12035/*
12036 * This function is responsible for byte-swapping all of the indirect,
12037 * block pointers. It is also responsible for byte-swapping directories.
12038 */
12039static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
12040 struct ext2_inode *inode)
12041{
12042 errcode_t retval;
12043 struct swap_block_struct sb;
12044
12045 sb.ino = ino;
12046 sb.inode = inode;
12047 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
12048 sb.errcode = 0;
12049 sb.isdir = 0;
12050 if (LINUX_S_ISDIR(inode->i_mode))
12051 sb.isdir = 1;
12052
12053 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
12054 swap_block, &sb);
12055 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012056 bb_error_msg(_("while calling ext2fs_block_iterate"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012057 ctx->flags |= E2F_FLAG_ABORT;
12058 return;
12059 }
12060 if (sb.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012061 bb_error_msg(_("while calling iterator function"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012062 ctx->flags |= E2F_FLAG_ABORT;
12063 return;
12064 }
12065}
12066
12067static void swap_inodes(e2fsck_t ctx)
12068{
12069 ext2_filsys fs = ctx->fs;
12070 dgrp_t group;
12071 unsigned int i;
12072 ext2_ino_t ino = 1;
12073 char *buf, *block_buf;
12074 errcode_t retval;
12075 struct ext2_inode * inode;
12076
12077 e2fsck_use_inode_shortcuts(ctx, 1);
12078
12079 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
12080 &buf);
12081 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012082 bb_error_msg(_("while allocating inode buffer"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012083 ctx->flags |= E2F_FLAG_ABORT;
12084 return;
12085 }
12086 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
12087 "block interate buffer");
12088 for (group = 0; group < fs->group_desc_count; group++) {
12089 retval = io_channel_read_blk(fs->io,
12090 fs->group_desc[group].bg_inode_table,
12091 fs->inode_blocks_per_group, buf);
12092 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012093 bb_error_msg(_("while reading inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012094 group);
12095 ctx->flags |= E2F_FLAG_ABORT;
12096 return;
12097 }
12098 inode = (struct ext2_inode *) buf;
12099 for (i=0; i < fs->super->s_inodes_per_group;
12100 i++, ino++, inode++) {
12101 ctx->stashed_ino = ino;
12102 ctx->stashed_inode = inode;
12103
12104 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
12105 ext2fs_swap_inode(fs, inode, inode, 0);
12106
12107 /*
12108 * Skip deleted files.
12109 */
12110 if (inode->i_links_count == 0)
12111 continue;
12112
12113 if (LINUX_S_ISDIR(inode->i_mode) ||
12114 ((inode->i_block[EXT2_IND_BLOCK] ||
12115 inode->i_block[EXT2_DIND_BLOCK] ||
12116 inode->i_block[EXT2_TIND_BLOCK]) &&
12117 ext2fs_inode_has_valid_blocks(inode)))
12118 swap_inode_blocks(ctx, ino, block_buf, inode);
12119
12120 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12121 return;
12122
12123 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12124 ext2fs_swap_inode(fs, inode, inode, 1);
12125 }
12126 retval = io_channel_write_blk(fs->io,
12127 fs->group_desc[group].bg_inode_table,
12128 fs->inode_blocks_per_group, buf);
12129 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012130 bb_error_msg(_("while writing inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012131 group);
12132 ctx->flags |= E2F_FLAG_ABORT;
12133 return;
12134 }
12135 }
12136 ext2fs_free_mem(&buf);
12137 ext2fs_free_mem(&block_buf);
12138 e2fsck_use_inode_shortcuts(ctx, 0);
12139 ext2fs_flush_icache(fs);
12140}
12141
Rob Landley7c94bed2006-05-03 21:58:45 +000012142#if defined(__powerpc__) && BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012143/*
12144 * On the PowerPC, the big-endian variant of the ext2 filesystem
12145 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
12146 * of each word. Thus a bitmap with only bit 0 set would be, as
12147 * a string of bytes, 00 00 00 01 00 ...
12148 * To cope with this, we byte-reverse each word of a bitmap if
12149 * we have a big-endian filesystem, that is, if we are *not*
12150 * byte-swapping other word-sized numbers.
12151 */
12152#define EXT2_BIG_ENDIAN_BITMAPS
12153#endif
12154
12155#ifdef EXT2_BIG_ENDIAN_BITMAPS
12156static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
12157{
12158 __u32 *p = (__u32 *) bmap->bitmap;
12159 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
12160
12161 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
12162 *p = ext2fs_swab32(*p);
12163}
12164#endif
12165
12166
12167#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012168static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012169{
12170 ext2_filsys fs = ctx->fs;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012171 if (!(ctx->options & E2F_OPT_PREEN))
12172 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
12173
Rob Landley3e72c592006-04-06 22:49:04 +000012174 /* Byte swap */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012175
12176 if (fs->super->s_mnt_count) {
12177 fprintf(stderr, _("%s: the filesystem must be freshly "
12178 "checked using fsck\n"
12179 "and not mounted before trying to "
12180 "byte-swap it.\n"), ctx->device_name);
12181 ctx->flags |= E2F_FLAG_ABORT;
12182 return;
12183 }
12184 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
12185 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
12186 EXT2_FLAG_SWAP_BYTES_WRITE);
12187 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
12188 } else {
12189 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
12190 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
12191 }
12192 swap_inodes(ctx);
12193 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12194 return;
12195 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12196 fs->flags |= EXT2_FLAG_SWAP_BYTES;
12197 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
12198 EXT2_FLAG_SWAP_BYTES_WRITE);
12199
12200#ifdef EXT2_BIG_ENDIAN_BITMAPS
12201 e2fsck_read_bitmaps(ctx);
12202 ext2fs_swap_bitmap(fs->inode_map);
12203 ext2fs_swap_bitmap(fs->block_map);
12204 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
12205#endif
12206 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
12207 ext2fs_flush(fs);
12208 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012209}
12210#endif /* ENABLE_SWAPFS */
12211
12212#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012213
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012214/*
12215 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012216 */
12217
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012218
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012219void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
12220 const char *description)
12221{
12222 void *ret;
12223 char buf[256];
12224
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012225 ret = malloc(size);
12226 if (!ret) {
12227 sprintf(buf, "Can't allocate %s\n", description);
Rob Landley7c94bed2006-05-03 21:58:45 +000012228 bb_error_msg_and_die(buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012229 }
12230 memset(ret, 0, size);
12231 return ret;
12232}
12233
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012234static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012235{
12236 char *ret;
12237
12238 if (!str)
12239 return NULL;
12240 if (!len)
12241 len = strlen(str);
12242 ret = malloc(len+1);
12243 if (ret) {
12244 strncpy(ret, str, len);
12245 ret[len] = 0;
12246 }
12247 return ret;
12248}
12249
12250#ifndef HAVE_CONIO_H
12251static int read_a_char(void)
12252{
12253 char c;
12254 int r;
12255 int fail = 0;
12256
12257 while(1) {
12258 if (e2fsck_global_ctx &&
12259 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
12260 return 3;
12261 }
12262 r = read(0, &c, 1);
12263 if (r == 1)
12264 return c;
12265 if (fail++ > 100)
12266 break;
12267 }
12268 return EOF;
12269}
12270#endif
12271
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012272static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012273{
12274 int c;
12275 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012276 static const char short_yes[] = "yY";
12277 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012278
12279#ifdef HAVE_TERMIOS_H
12280 struct termios termios, tmp;
12281
12282 tcgetattr (0, &termios);
12283 tmp = termios;
12284 tmp.c_lflag &= ~(ICANON | ECHO);
12285 tmp.c_cc[VMIN] = 1;
12286 tmp.c_cc[VTIME] = 0;
12287 tcsetattr (0, TCSANOW, &tmp);
12288#endif
12289
12290 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012291 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012292 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012293 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012294 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012295 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012296 printf("%s%s? ", string, defstr);
12297 while (1) {
12298 fflush (stdout);
12299 if ((c = read_a_char()) == EOF)
12300 break;
12301 if (c == 3) {
12302#ifdef HAVE_TERMIOS_H
12303 tcsetattr (0, TCSANOW, &termios);
12304#endif
12305 if (e2fsck_global_ctx &&
12306 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
12307 puts("\n");
12308 longjmp(e2fsck_global_ctx->abort_loc, 1);
12309 }
12310 puts(_("cancelled!\n"));
12311 return 0;
12312 }
12313 if (strchr(short_yes, (char) c)) {
12314 def = 1;
12315 break;
12316 }
12317 else if (strchr(short_no, (char) c)) {
12318 def = 0;
12319 break;
12320 }
12321 else if ((c == ' ' || c == '\n') && (def != -1))
12322 break;
12323 }
12324 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012325 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012326 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012327 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012328#ifdef HAVE_TERMIOS_H
12329 tcsetattr (0, TCSANOW, &termios);
12330#endif
12331 return def;
12332}
12333
12334int ask (e2fsck_t ctx, const char * string, int def)
12335{
12336 if (ctx->options & E2F_OPT_NO) {
12337 printf (_("%s? no\n\n"), string);
12338 return 0;
12339 }
12340 if (ctx->options & E2F_OPT_YES) {
12341 printf (_("%s? yes\n\n"), string);
12342 return 1;
12343 }
12344 if (ctx->options & E2F_OPT_PREEN) {
12345 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
12346 return def;
12347 }
12348 return ask_yn(string, def);
12349}
12350
12351void e2fsck_read_bitmaps(e2fsck_t ctx)
12352{
12353 ext2_filsys fs = ctx->fs;
12354 errcode_t retval;
12355
12356 if (ctx->invalid_bitmaps) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012357 bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012358 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012359 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012360 }
12361
12362 ehandler_operation(_("reading inode and block bitmaps"));
12363 retval = ext2fs_read_bitmaps(fs);
12364 ehandler_operation(0);
12365 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012366 bb_error_msg(_("while retrying to read bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012367 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012368 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012369 }
12370}
12371
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012372static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012373{
12374 ext2_filsys fs = ctx->fs;
12375 errcode_t retval;
12376
12377 if (ext2fs_test_bb_dirty(fs)) {
12378 ehandler_operation(_("writing block bitmaps"));
12379 retval = ext2fs_write_block_bitmap(fs);
12380 ehandler_operation(0);
12381 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012382 bb_error_msg(_("while retrying to write block bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012383 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012384 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012385 }
12386 }
12387
12388 if (ext2fs_test_ib_dirty(fs)) {
12389 ehandler_operation(_("writing inode bitmaps"));
12390 retval = ext2fs_write_inode_bitmap(fs);
12391 ehandler_operation(0);
12392 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012393 bb_error_msg(_("while retrying to write inode bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012394 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012395 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012396 }
12397 }
12398}
12399
12400void preenhalt(e2fsck_t ctx)
12401{
12402 ext2_filsys fs = ctx->fs;
12403
12404 if (!(ctx->options & E2F_OPT_PREEN))
12405 return;
12406 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
12407 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
12408 ctx->device_name);
12409 if (fs != NULL) {
12410 fs->super->s_state |= EXT2_ERROR_FS;
12411 ext2fs_mark_super_dirty(fs);
12412 ext2fs_close(fs);
12413 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012414 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012415}
12416
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012417void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
12418 struct ext2_inode * inode, const char *proc)
12419{
12420 int retval;
12421
12422 retval = ext2fs_read_inode(ctx->fs, ino, inode);
12423 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012424 bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
12425 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012426 }
12427}
12428
12429extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
12430 struct ext2_inode * inode, int bufsize,
12431 const char *proc)
12432{
12433 int retval;
12434
12435 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
12436 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012437 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12438 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012439 }
12440}
12441
12442extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
12443 struct ext2_inode * inode, const char *proc)
12444{
12445 int retval;
12446
12447 retval = ext2fs_write_inode(ctx->fs, ino, inode);
12448 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012449 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12450 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012451 }
12452}
12453
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012454blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
12455 io_manager manager)
12456{
12457 struct ext2_super_block *sb;
12458 io_channel io = NULL;
12459 void *buf = NULL;
12460 int blocksize;
12461 blk_t superblock, ret_sb = 8193;
12462
12463 if (fs && fs->super) {
12464 ret_sb = (fs->super->s_blocks_per_group +
12465 fs->super->s_first_data_block);
12466 if (ctx) {
12467 ctx->superblock = ret_sb;
12468 ctx->blocksize = fs->blocksize;
12469 }
12470 return ret_sb;
12471 }
12472
12473 if (ctx) {
12474 if (ctx->blocksize) {
12475 ret_sb = ctx->blocksize * 8;
12476 if (ctx->blocksize == 1024)
12477 ret_sb++;
12478 ctx->superblock = ret_sb;
12479 return ret_sb;
12480 }
12481 ctx->superblock = ret_sb;
12482 ctx->blocksize = 1024;
12483 }
12484
12485 if (!name || !manager)
12486 goto cleanup;
12487
12488 if (manager->open(name, 0, &io) != 0)
12489 goto cleanup;
12490
12491 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
12492 goto cleanup;
12493 sb = (struct ext2_super_block *) buf;
12494
12495 for (blocksize = EXT2_MIN_BLOCK_SIZE;
12496 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
12497 superblock = blocksize*8;
12498 if (blocksize == 1024)
12499 superblock++;
12500 io_channel_set_blksize(io, blocksize);
12501 if (io_channel_read_blk(io, superblock,
12502 -SUPERBLOCK_SIZE, buf))
12503 continue;
Rob Landley7c94bed2006-05-03 21:58:45 +000012504#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012505 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
12506 ext2fs_swap_super(sb);
12507#endif
12508 if (sb->s_magic == EXT2_SUPER_MAGIC) {
12509 ret_sb = superblock;
12510 if (ctx) {
12511 ctx->superblock = superblock;
12512 ctx->blocksize = blocksize;
12513 }
12514 break;
12515 }
12516 }
12517
12518cleanup:
12519 if (io)
12520 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000012521 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012522 return (ret_sb);
12523}
12524
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012525
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012526/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012527 * This function runs through the e2fsck passes and calls them all,
12528 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012529 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012530typedef void (*pass_t)(e2fsck_t ctx);
12531
12532static const pass_t e2fsck_passes[] = {
12533 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
12534 e2fsck_pass5, 0 };
12535
12536#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
12537
12538static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012539{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012540 int i;
12541 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012542
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012543 if (setjmp(ctx->abort_loc)) {
12544 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
12545 return (ctx->flags & E2F_FLAG_RUN_RETURN);
12546 }
12547 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012548
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012549 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
12550 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12551 break;
12552 e2fsck_pass(ctx);
12553 if (ctx->progress)
12554 (void) (ctx->progress)(ctx, 0, 0, 0);
12555 }
12556 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012557
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012558 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12559 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012560 return 0;
12561}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012562
12563
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012564/*
12565 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012566 */
12567
12568
Mike Frysinger51a43b42005-09-24 07:11:16 +000012569/* Command line options */
12570static int swapfs;
12571#ifdef ENABLE_SWAPFS
12572static int normalize_swapfs;
12573#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012574static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000012575static int show_version_only;
12576static int verbose;
12577
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012578#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
12579
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012580static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012581{
12582 ext2_filsys fs = ctx->fs;
12583 int inodes, inodes_used, blocks, blocks_used;
12584 int dir_links;
12585 int num_files, num_links;
12586 int frag_percent;
12587
12588 dir_links = 2 * ctx->fs_directory_count - 1;
12589 num_files = ctx->fs_total_count - dir_links;
12590 num_links = ctx->fs_links_count - dir_links;
12591 inodes = fs->super->s_inodes_count;
12592 inodes_used = (fs->super->s_inodes_count -
12593 fs->super->s_free_inodes_count);
12594 blocks = fs->super->s_blocks_count;
12595 blocks_used = (fs->super->s_blocks_count -
12596 fs->super->s_free_blocks_count);
12597
12598 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
12599 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012600
Mike Frysinger51a43b42005-09-24 07:11:16 +000012601 if (!verbose) {
12602 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
12603 ctx->device_name, inodes_used, inodes,
12604 frag_percent / 10, frag_percent % 10,
12605 blocks_used, blocks);
12606 return;
12607 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012608 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
12609 100 * inodes_used / inodes);
12610 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
12611 P_E2("", "s", ctx->fs_fragmented),
12612 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012613 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
12614 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012615 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
12616 (int) ((long long) 100 * blocks_used / blocks));
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012617 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
12618 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
12619 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
12620 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
12621 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
12622 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
12623 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
12624 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
12625 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
12626 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
12627 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012628}
12629
12630static void check_mount(e2fsck_t ctx)
12631{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012632 errcode_t retval;
12633 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012634
12635 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
12636 &ctx->mount_flags);
12637 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012638 bb_error_msg(_("while determining whether %s is mounted."),
Mike Frysinger51a43b42005-09-24 07:11:16 +000012639 ctx->filesystem_name);
12640 return;
12641 }
12642
12643 /*
12644 * If the filesystem isn't mounted, or it's the root filesystem
12645 * and it's mounted read-only, then everything's fine.
12646 */
12647 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
12648 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
12649 (ctx->mount_flags & EXT2_MF_READONLY)))
12650 return;
12651
12652 if (ctx->options & E2F_OPT_READONLY) {
12653 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
12654 return;
12655 }
12656
12657 printf(_("%s is mounted. "), ctx->filesystem_name);
12658 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000012659 bb_error_msg_and_die(_("Cannot continue, aborting.\n\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012660 printf(_("\n\n\007\007\007\007WARNING!!! "
12661 "Running e2fsck on a mounted filesystem may cause\n"
12662 "SEVERE filesystem damage.\007\007\007\n\n"));
12663 cont = ask_yn(_("Do you really want to continue"), -1);
12664 if (!cont) {
12665 printf (_("check aborted.\n"));
12666 exit (0);
12667 }
12668 return;
12669}
12670
12671static int is_on_batt(void)
12672{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012673 FILE *f;
12674 DIR *d;
12675 char tmp[80], tmp2[80], fname[80];
12676 unsigned int acflag;
12677 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012678
12679 f = fopen("/proc/apm", "r");
12680 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012681 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012682 acflag = 1;
12683 fclose(f);
12684 return (acflag != 1);
12685 }
12686 d = opendir("/proc/acpi/ac_adapter");
12687 if (d) {
12688 while ((de=readdir(d)) != NULL) {
12689 if (!strncmp(".", de->d_name, 1))
12690 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012691 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000012692 de->d_name);
12693 f = fopen(fname, "r");
12694 if (!f)
12695 continue;
12696 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
12697 tmp[0] = 0;
12698 fclose(f);
12699 if (strncmp(tmp, "off-line", 8) == 0) {
12700 closedir(d);
12701 return 1;
12702 }
12703 }
12704 closedir(d);
12705 }
12706 return 0;
12707}
12708
12709/*
12710 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012711 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000012712 * message explaining why a check is being forced.
12713 */
12714static void check_if_skip(e2fsck_t ctx)
12715{
12716 ext2_filsys fs = ctx->fs;
12717 const char *reason = NULL;
12718 unsigned int reason_arg = 0;
12719 long next_check;
12720 int batt = is_on_batt();
12721 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012722
Rob Landleyd8f66012006-05-05 17:29:09 +000012723 if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012724 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012725
Mike Frysinger51a43b42005-09-24 07:11:16 +000012726 if ((fs->super->s_state & EXT2_ERROR_FS) ||
12727 !ext2fs_test_valid(fs))
12728 reason = _(" contains a file system with errors");
12729 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
12730 reason = _(" was not cleanly unmounted");
12731 else if ((fs->super->s_max_mnt_count > 0) &&
12732 (fs->super->s_mnt_count >=
12733 (unsigned) fs->super->s_max_mnt_count)) {
12734 reason = _(" has been mounted %u times without being checked");
12735 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012736 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012737 (unsigned) fs->super->s_max_mnt_count*2))
12738 reason = 0;
12739 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012740 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000012741 fs->super->s_checkinterval)) {
12742 reason = _(" has gone %u days without being checked");
12743 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012744 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012745 fs->super->s_checkinterval*2))
12746 reason = 0;
12747 }
12748 if (reason) {
12749 fputs(ctx->device_name, stdout);
12750 printf(reason, reason_arg);
12751 fputs(_(", check forced.\n"), stdout);
12752 return;
12753 }
12754 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
12755 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
12756 fs->super->s_inodes_count,
12757 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
12758 fs->super->s_blocks_count);
12759 next_check = 100000;
12760 if (fs->super->s_max_mnt_count > 0) {
12761 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012762 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012763 next_check = 1;
12764 }
12765 if (fs->super->s_checkinterval &&
12766 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
12767 next_check = 1;
12768 if (next_check <= 5) {
12769 if (next_check == 1)
12770 fputs(_(" (check after next mount)"), stdout);
12771 else
12772 printf(_(" (check in %ld mounts)"), next_check);
12773 }
12774 fputc('\n', stdout);
12775 ext2fs_close(fs);
12776 ctx->fs = NULL;
12777 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012778 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012779}
12780
12781/*
12782 * For completion notice
12783 */
12784struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012785 int max_pass;
12786 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000012787};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012788static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000012789 5, { 0, 70, 90, 92, 95, 100 }
12790};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012791
Mike Frysinger51a43b42005-09-24 07:11:16 +000012792static char bar[128], spaces[128];
12793
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012794static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000012795 int max)
12796{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012797 float percent;
12798
Mike Frysinger51a43b42005-09-24 07:11:16 +000012799 if (pass <= 0)
12800 return 0.0;
12801 if (pass > tbl->max_pass || max == 0)
12802 return 100.0;
12803 percent = ((float) curr) / ((float) max);
12804 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
12805 + tbl->table[pass-1]);
12806}
12807
Rob Landleydfba7412006-03-06 20:47:33 +000012808void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012809{
12810 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
12811 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012812
Mike Frysinger51a43b42005-09-24 07:11:16 +000012813 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
12814 ctx->stop_meta);
12815 fflush(stdout);
12816 ctx->flags &= ~E2F_FLAG_PROG_BAR;
12817}
12818
12819int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
12820 unsigned int dpynum)
12821{
12822 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012823 int i;
12824 unsigned int tick;
12825 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012826 int dpywidth;
12827 int fixed_percent;
12828
12829 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
12830 return 0;
12831
12832 /*
12833 * Calculate the new progress position. If the
12834 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012835 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000012836 */
12837 fixed_percent = (int) ((10 * percent) + 0.5);
12838 if (ctx->progress_last_percent == fixed_percent)
12839 return 0;
12840 ctx->progress_last_percent = fixed_percent;
12841
12842 /*
12843 * If we've already updated the spinner once within
12844 * the last 1/8th of a second, no point doing it
12845 * again.
12846 */
12847 gettimeofday(&tv, NULL);
12848 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
12849 if ((tick == ctx->progress_last_time) &&
12850 (fixed_percent != 0) && (fixed_percent != 1000))
12851 return 0;
12852 ctx->progress_last_time = tick;
12853
12854 /*
12855 * Advance the spinner, and note that the progress bar
12856 * will be on the screen
12857 */
12858 ctx->progress_pos = (ctx->progress_pos+1) & 3;
12859 ctx->flags |= E2F_FLAG_PROG_BAR;
12860
12861 dpywidth = 66 - strlen(label);
12862 dpywidth = 8 * (dpywidth / 8);
12863 if (dpynum)
12864 dpywidth -= 8;
12865
12866 i = ((percent * dpywidth) + 50) / 100;
12867 printf("%s%s: |%s%s", ctx->start_meta, label,
12868 bar + (sizeof(bar) - (i+1)),
12869 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
12870 if (fixed_percent == 1000)
12871 fputc('|', stdout);
12872 else
12873 fputc(spinner[ctx->progress_pos & 3], stdout);
12874 printf(" %4.1f%% ", percent);
12875 if (dpynum)
12876 printf("%u\r", dpynum);
12877 else
12878 fputs(" \r", stdout);
12879 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012880
Mike Frysinger51a43b42005-09-24 07:11:16 +000012881 if (fixed_percent == 1000)
12882 e2fsck_clear_progbar(ctx);
12883 fflush(stdout);
12884
12885 return 0;
12886}
12887
12888static int e2fsck_update_progress(e2fsck_t ctx, int pass,
12889 unsigned long cur, unsigned long max)
12890{
12891 char buf[80];
12892 float percent;
12893
12894 if (pass == 0)
12895 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012896
Mike Frysinger51a43b42005-09-24 07:11:16 +000012897 if (ctx->progress_fd) {
12898 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
12899 write(ctx->progress_fd, buf, strlen(buf));
12900 } else {
12901 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
12902 e2fsck_simple_progress(ctx, ctx->device_name,
12903 percent, 0);
12904 }
12905 return 0;
12906}
12907
Mike Frysinger51a43b42005-09-24 07:11:16 +000012908static void reserve_stdio_fds(void)
12909{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012910 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012911
12912 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000012913 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012914 if (fd > 2)
12915 break;
12916 if (fd < 0) {
12917 fprintf(stderr, _("ERROR: Couldn't open "
12918 "/dev/null (%s)\n"),
12919 strerror(errno));
12920 break;
12921 }
12922 }
12923 close(fd);
12924}
12925
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012926static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012927{
12928 e2fsck_t ctx = e2fsck_global_ctx;
12929
12930 if (!ctx)
12931 return;
12932
12933 ctx->progress = e2fsck_update_progress;
12934 ctx->progress_fd = 0;
12935}
12936
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012937static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012938{
12939 e2fsck_t ctx = e2fsck_global_ctx;
12940
12941 if (!ctx)
12942 return;
12943
12944 e2fsck_clear_progbar(ctx);
12945 ctx->progress = 0;
12946}
12947
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012948static void signal_cancel(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 exit(FSCK_CANCELED);
12954
12955 ctx->flags |= E2F_FLAG_CANCEL;
12956}
Mike Frysinger51a43b42005-09-24 07:11:16 +000012957
12958static void parse_extended_opts(e2fsck_t ctx, const char *opts)
12959{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012960 char *buf, *token, *next, *p, *arg;
12961 int ea_ver;
12962 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012963
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012964 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012965 for (token = buf; token && *token; token = next) {
12966 p = strchr(token, ',');
12967 next = 0;
12968 if (p) {
12969 *p = 0;
12970 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012971 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000012972 arg = strchr(token, '=');
12973 if (arg) {
12974 *arg = 0;
12975 arg++;
12976 }
12977 if (strcmp(token, "ea_ver") == 0) {
12978 if (!arg) {
12979 extended_usage++;
12980 continue;
12981 }
12982 ea_ver = strtoul(arg, &p, 0);
12983 if (*p ||
12984 ((ea_ver != 1) && (ea_ver != 2))) {
12985 fprintf(stderr,
12986 _("Invalid EA version.\n"));
12987 extended_usage++;
12988 continue;
12989 }
12990 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000012991 } else {
12992 fprintf(stderr, _("Unknown extended option: %s\n"),
12993 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012994 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000012995 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000012996 }
12997 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012998 bb_error_msg_and_die(
12999 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000013000 "and may take an argument which\n"
13001 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000013002 "Valid extended options are:\n"
13003 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000013004 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013005}
Mike Frysinger51a43b42005-09-24 07:11:16 +000013006
13007
13008static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
13009{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013010 int flush = 0;
13011 int c, fd;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013012 e2fsck_t ctx;
13013 errcode_t retval;
13014 struct sigaction sa;
13015 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013016
13017 retval = e2fsck_allocate_context(&ctx);
13018 if (retval)
13019 return retval;
13020
13021 *ret_ctx = ctx;
13022
13023 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
13024 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
13025 if (isatty(0) && isatty(1)) {
13026 ctx->interactive = 1;
13027 } else {
13028 ctx->start_meta[0] = '\001';
13029 ctx->stop_meta[0] = '\002';
13030 }
13031 memset(bar, '=', sizeof(bar)-1);
13032 memset(spaces, ' ', sizeof(spaces)-1);
13033 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013034
Mike Frysinger51a43b42005-09-24 07:11:16 +000013035 if (argc && *argv)
13036 ctx->program_name = *argv;
13037 else
13038 ctx->program_name = "e2fsck";
13039 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
13040 switch (c) {
13041 case 'C':
13042 ctx->progress = e2fsck_update_progress;
13043 ctx->progress_fd = atoi(optarg);
13044 if (!ctx->progress_fd)
13045 break;
13046 /* Validate the file descriptor to avoid disasters */
13047 fd = dup(ctx->progress_fd);
13048 if (fd < 0) {
13049 fprintf(stderr,
13050 _("Error validating file descriptor %d: %s\n"),
13051 ctx->progress_fd,
13052 error_message(errno));
Rob Landley7c94bed2006-05-03 21:58:45 +000013053 bb_error_msg_and_die(_("Invalid completion information file descriptor"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013054 } else
13055 close(fd);
13056 break;
13057 case 'D':
13058 ctx->options |= E2F_OPT_COMPRESS_DIRS;
13059 break;
13060 case 'E':
13061 extended_opts = optarg;
13062 break;
13063 case 'p':
13064 case 'a':
13065 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
13066 conflict_opt:
Rob Landley7c94bed2006-05-03 21:58:45 +000013067 bb_error_msg_and_die(_("Only one the options -p/-a, -n or -y may be specified."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013068 }
13069 ctx->options |= E2F_OPT_PREEN;
13070 break;
13071 case 'n':
13072 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
13073 goto conflict_opt;
13074 ctx->options |= E2F_OPT_NO;
13075 break;
13076 case 'y':
13077 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
13078 goto conflict_opt;
13079 ctx->options |= E2F_OPT_YES;
13080 break;
13081 case 't':
Rob Landley3e72c592006-04-06 22:49:04 +000013082 /* FIXME - This needs to go away in a future path - will change binary */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013083 fprintf(stderr, _("The -t option is not "
13084 "supported on this version of e2fsck.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013085 break;
13086 case 'c':
13087 if (cflag++)
13088 ctx->options |= E2F_OPT_WRITECHECK;
13089 ctx->options |= E2F_OPT_CHECKBLOCKS;
13090 break;
13091 case 'r':
13092 /* What we do by default, anyway! */
13093 break;
13094 case 'b':
13095 ctx->use_superblock = atoi(optarg);
13096 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
13097 break;
13098 case 'B':
13099 ctx->blocksize = atoi(optarg);
13100 break;
13101 case 'I':
13102 ctx->inode_buffer_blocks = atoi(optarg);
13103 break;
13104 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013105 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013106 break;
13107 case 'P':
13108 ctx->process_inode_size = atoi(optarg);
13109 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013110 case 'd':
13111 ctx->options |= E2F_OPT_DEBUG;
13112 break;
13113 case 'f':
13114 ctx->options |= E2F_OPT_FORCE;
13115 break;
13116 case 'F':
13117 flush = 1;
13118 break;
13119 case 'v':
13120 verbose = 1;
13121 break;
13122 case 'V':
13123 show_version_only = 1;
13124 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013125 case 'N':
13126 ctx->device_name = optarg;
13127 break;
13128#ifdef ENABLE_SWAPFS
13129 case 's':
13130 normalize_swapfs = 1;
13131 case 'S':
13132 swapfs = 1;
13133 break;
13134#else
13135 case 's':
13136 case 'S':
13137 fprintf(stderr, _("Byte-swapping filesystems "
13138 "not compiled in this version "
13139 "of e2fsck\n"));
13140 exit(1);
13141#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013142 default:
Rob Landley7c94bed2006-05-03 21:58:45 +000013143 bb_show_usage();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013144 }
13145 if (show_version_only)
13146 return 0;
13147 if (optind != argc - 1)
Rob Landley7c94bed2006-05-03 21:58:45 +000013148 bb_show_usage();
Rob Landleyd8f66012006-05-05 17:29:09 +000013149 if ((ctx->options & E2F_OPT_NO) &&
Mike Frysinger51a43b42005-09-24 07:11:16 +000013150 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
13151 ctx->options |= E2F_OPT_READONLY;
13152 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013153 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013154 *ctx->io_options++ = 0;
13155 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
13156 if (!ctx->filesystem_name) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013157 bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
13158 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013159 }
13160 if (extended_opts)
13161 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013162
Mike Frysinger51a43b42005-09-24 07:11:16 +000013163 if (flush) {
13164 fd = open(ctx->filesystem_name, O_RDONLY, 0);
13165 if (fd < 0) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013166 bb_error_msg(_("while opening %s for flushing"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013167 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013168 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013169 }
13170 if ((retval = ext2fs_sync_device(fd, 1))) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013171 bb_error_msg(_("while trying to flush %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013172 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013173 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013174 }
13175 close(fd);
13176 }
13177#ifdef ENABLE_SWAPFS
Rob Landleyd8f66012006-05-05 17:29:09 +000013178 if (swapfs && cflag) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013179 fprintf(stderr, _("Incompatible options not "
13180 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013181 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013182 }
13183#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013184 /*
13185 * Set up signal action
13186 */
13187 memset(&sa, 0, sizeof(struct sigaction));
13188 sa.sa_handler = signal_cancel;
13189 sigaction(SIGINT, &sa, 0);
13190 sigaction(SIGTERM, &sa, 0);
13191#ifdef SA_RESTART
13192 sa.sa_flags = SA_RESTART;
13193#endif
13194 e2fsck_global_ctx = ctx;
13195 sa.sa_handler = signal_progress_on;
13196 sigaction(SIGUSR1, &sa, 0);
13197 sa.sa_handler = signal_progress_off;
13198 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013199
13200 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013201 if (cflag)
13202 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013203 return 0;
13204}
13205
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013206static const char my_ver_string[] = E2FSPROGS_VERSION;
13207static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013208
Mike Frysinger51a43b42005-09-24 07:11:16 +000013209int e2fsck_main (int argc, char *argv[])
13210{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013211 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013212 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013213 ext2_filsys fs = 0;
13214 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013215 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013216 const char *lib_ver_date;
13217 int my_ver, lib_ver;
13218 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013219 struct problem_context pctx;
13220 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013221
Mike Frysinger51a43b42005-09-24 07:11:16 +000013222 clear_problem_context(&pctx);
Rob Landley3e72c592006-04-06 22:49:04 +000013223
Mike Frysinger51a43b42005-09-24 07:11:16 +000013224 my_ver = ext2fs_parse_version_string(my_ver_string);
13225 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
13226 if (my_ver > lib_ver) {
13227 fprintf( stderr, _("Error: ext2fs library version "
13228 "out of date!\n"));
13229 show_version_only++;
13230 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013231
Mike Frysinger51a43b42005-09-24 07:11:16 +000013232 retval = PRS(argc, argv, &ctx);
13233 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013234 bb_error_msg(_("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013235 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013236 }
13237 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013238
Mike Frysinger51a43b42005-09-24 07:11:16 +000013239 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
13240 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
13241 my_ver_date);
13242
13243 if (show_version_only) {
13244 fprintf(stderr, _("\tUsing %s, %s\n"),
13245 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013246 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013247 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013248
Mike Frysinger51a43b42005-09-24 07:11:16 +000013249 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013250
Mike Frysinger51a43b42005-09-24 07:11:16 +000013251 if (!(ctx->options & E2F_OPT_PREEN) &&
13252 !(ctx->options & E2F_OPT_NO) &&
13253 !(ctx->options & E2F_OPT_YES)) {
13254 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000013255 bb_error_msg_and_die(_("need terminal for interactive repairs"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013256 }
13257 ctx->superblock = ctx->use_superblock;
13258restart:
13259#ifdef CONFIG_TESTIO_DEBUG
13260 io_ptr = test_io_manager;
13261 test_io_backing_manager = unix_io_manager;
13262#else
13263 io_ptr = unix_io_manager;
13264#endif
13265 flags = 0;
13266 if ((ctx->options & E2F_OPT_READONLY) == 0)
13267 flags |= EXT2_FLAG_RW;
13268
13269 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013270 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013271 flags, ctx->superblock, ctx->blocksize,
13272 io_ptr, &fs);
13273 } else if (ctx->superblock) {
13274 int blocksize;
13275 for (blocksize = EXT2_MIN_BLOCK_SIZE;
13276 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013277 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013278 ctx->io_options, flags,
13279 ctx->superblock, blocksize,
13280 io_ptr, &fs);
13281 if (!retval)
13282 break;
13283 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013284 } else
13285 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013286 flags, 0, 0, io_ptr, &fs);
13287 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
13288 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
13289 ((retval == EXT2_ET_BAD_MAGIC) ||
13290 ((retval == 0) && ext2fs_check_desc(fs)))) {
13291 if (!fs || (fs->group_desc_count > 1)) {
13292 printf(_("%s trying backup blocks...\n"),
13293 retval ? _("Couldn't find ext2 superblock,") :
13294 _("Group descriptors look bad..."));
13295 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
13296 if (fs)
13297 ext2fs_close(fs);
13298 goto restart;
13299 }
13300 }
13301 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013302 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013303 ctx->filesystem_name);
13304 if (retval == EXT2_ET_REV_TOO_HIGH) {
13305 printf(_("The filesystem revision is apparently "
13306 "too high for this version of e2fsck.\n"
13307 "(Or the filesystem superblock "
13308 "is corrupt)\n\n"));
13309 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
13310 } else if (retval == EXT2_ET_SHORT_READ)
13311 printf(_("Could this be a zero-length partition?\n"));
13312 else if ((retval == EPERM) || (retval == EACCES))
13313 printf(_("You must have %s access to the "
13314 "filesystem or be root\n"),
13315 (ctx->options & E2F_OPT_READONLY) ?
13316 "r/o" : "r/w");
13317 else if (retval == ENXIO)
13318 printf(_("Possibly non-existent or swap device?\n"));
13319#ifdef EROFS
13320 else if (retval == EROFS)
13321 printf(_("Disk write-protected; use the -n option "
13322 "to do a read-only\n"
13323 "check of the device.\n"));
13324#endif
13325 else
13326 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
Rob Landley7c94bed2006-05-03 21:58:45 +000013327 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013328 }
13329 ctx->fs = fs;
13330 fs->priv_data = ctx;
13331 sb = fs->super;
13332 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013333 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013334 ctx->filesystem_name);
13335 get_newer:
Rob Landley7c94bed2006-05-03 21:58:45 +000013336 bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013337 }
13338
13339 /*
13340 * Set the device name, which is used whenever we print error
13341 * or informational messages to the user.
13342 */
13343 if (ctx->device_name == 0 &&
13344 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013345 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013346 sizeof(sb->s_volume_name));
13347 }
13348 if (ctx->device_name == 0)
13349 ctx->device_name = ctx->filesystem_name;
13350
13351 /*
13352 * Make sure the ext3 superblock fields are consistent.
13353 */
13354 retval = e2fsck_check_ext3_journal(ctx);
13355 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013356 bb_error_msg(_("while checking ext3 journal for %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013357 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013358 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013359 }
13360
13361 /*
13362 * Check to see if we need to do ext3-style recovery. If so,
13363 * do it, and then restart the fsck.
13364 */
13365 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
13366 if (ctx->options & E2F_OPT_READONLY) {
13367 printf(_("Warning: skipping journal recovery "
13368 "because doing a read-only filesystem "
13369 "check.\n"));
13370 io_channel_flush(ctx->fs->io);
13371 } else {
13372 if (ctx->flags & E2F_FLAG_RESTARTED) {
13373 /*
13374 * Whoops, we attempted to run the
13375 * journal twice. This should never
13376 * happen, unless the hardware or
13377 * device driver is being bogus.
13378 */
Rob Landley7c94bed2006-05-03 21:58:45 +000013379 bb_error_msg(_("unable to set superblock flags on %s\n"), ctx->device_name);
13380 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013381 }
13382 retval = e2fsck_run_ext3_journal(ctx);
13383 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013384 bb_error_msg(_("while recovering ext3 journal of %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013385 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013386 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013387 }
13388 ext2fs_close(ctx->fs);
13389 ctx->fs = 0;
13390 ctx->flags |= E2F_FLAG_RESTARTED;
13391 goto restart;
13392 }
13393 }
13394
13395 /*
13396 * Check for compatibility with the feature sets. We need to
13397 * be more stringent than ext2fs_open().
13398 */
13399 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
13400 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013401 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013402 goto get_newer;
13403 }
13404 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013405 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013406 goto get_newer;
13407 }
13408#ifdef ENABLE_COMPRESSION
Rob Landley3e72c592006-04-06 22:49:04 +000013409 /* FIXME - do we support this at all? */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013410 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
Rob Landley7c94bed2006-05-03 21:58:45 +000013411 bb_error_msg(_("Warning: compression support is experimental.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013412#endif
13413#ifndef ENABLE_HTREE
13414 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013415 bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
Mike Frysinger51a43b42005-09-24 07:11:16 +000013416 "but filesystem %s has HTREE directories.\n"),
13417 ctx->device_name);
13418 goto get_newer;
13419 }
13420#endif
13421
13422 /*
13423 * If the user specified a specific superblock, presumably the
13424 * master superblock has been trashed. So we mark the
13425 * superblock as dirty, so it can be written out.
13426 */
13427 if (ctx->superblock &&
13428 !(ctx->options & E2F_OPT_READONLY))
13429 ext2fs_mark_super_dirty(fs);
13430
13431 /*
13432 * We only update the master superblock because (a) paranoia;
13433 * we don't want to corrupt the backup superblocks, and (b) we
13434 * don't need to update the mount count and last checked
13435 * fields in the backup superblock (the kernel doesn't
13436 * update the backup superblocks anyway).
13437 */
13438 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
13439
13440 ehandler_init(fs->io);
13441
13442 if (ctx->superblock)
13443 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
13444 ext2fs_mark_valid(fs);
13445 check_super_block(ctx);
13446 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013447 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013448 check_if_skip(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013449 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013450 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013451#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000013452
13453#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000013454#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000013455#else
Rob Landley8b606342006-01-24 02:38:28 +000013456#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000013457#endif
13458
13459
Mike Frysinger51a43b42005-09-24 07:11:16 +000013460 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000013461 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013462 fprintf(stderr, _("%s: Filesystem byte order "
13463 "already normalized.\n"), ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013464 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013465 }
13466 }
13467 if (swapfs) {
13468 swap_filesys(ctx);
13469 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013470 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013471 }
13472#endif
13473
13474 /*
13475 * Mark the system as valid, 'til proven otherwise
13476 */
13477 ext2fs_mark_valid(fs);
13478
13479 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
13480 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013481 bb_error_msg(_("while reading bad blocks inode"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013482 preenhalt(ctx);
13483 printf(_("This doesn't bode well,"
13484 " but we'll try to go on...\n"));
13485 }
13486
13487 run_result = e2fsck_run(ctx);
13488 e2fsck_clear_progbar(ctx);
13489 if (run_result == E2F_FLAG_RESTART) {
13490 printf(_("Restarting e2fsck from the beginning...\n"));
13491 retval = e2fsck_reset_context(ctx);
13492 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013493 bb_error_msg(_("while resetting context"));
13494 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013495 }
13496 ext2fs_close(fs);
13497 goto restart;
13498 }
13499 if (run_result & E2F_FLAG_CANCEL) {
13500 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
13501 ctx->device_name : ctx->filesystem_name);
13502 exit_value |= FSCK_CANCELED;
13503 }
13504 if (run_result & E2F_FLAG_ABORT)
Rob Landley7c94bed2006-05-03 21:58:45 +000013505 bb_error_msg_and_die(_("aborted"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013506
Rob Landley3e72c592006-04-06 22:49:04 +000013507 /* Cleanup */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013508 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013509 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013510 if (!(ctx->options & E2F_OPT_PREEN))
13511 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
13512 ctx->device_name);
13513 if (ctx->mount_flags & EXT2_MF_ISROOT) {
13514 printf(_("%s: ***** REBOOT LINUX *****\n"),
13515 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013516 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013517 }
13518 }
13519 if (!ext2fs_test_valid(fs)) {
13520 printf(_("\n%s: ********** WARNING: Filesystem still has "
13521 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013522 exit_value |= EXIT_UNCORRECTED;
13523 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013524 }
13525 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013526 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013527 else {
13528 show_stats(ctx);
13529 if (!(ctx->options & E2F_OPT_READONLY)) {
13530 if (ext2fs_test_valid(fs)) {
13531 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013532 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013533 sb->s_state = EXT2_VALID_FS;
13534 } else
13535 sb->s_state &= ~EXT2_VALID_FS;
13536 sb->s_mnt_count = 0;
13537 sb->s_lastcheck = time(NULL);
13538 ext2fs_mark_super_dirty(fs);
13539 }
13540 }
13541
13542 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013543
Mike Frysinger51a43b42005-09-24 07:11:16 +000013544 ext2fs_close(fs);
13545 ctx->fs = NULL;
13546 free(ctx->filesystem_name);
13547 free(ctx->journal_name);
13548 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013549
Mike Frysinger51a43b42005-09-24 07:11:16 +000013550 return exit_value;
13551}