blob: 4c0614addac754b6b82e29a0ede3319842a20d45 [file] [log] [blame]
"Robert P. J. Day"63fc1a92006-07-02 19:47:05 +00001/* vi: set sw=4 ts=4: */
Mike Frysinger51a43b42005-09-24 07:11:16 +00002/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003 * e2fsck
4 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
Rob Landley7c94bed2006-05-03 21:58:45 +00006 * Copyright (C) 2006 Garrett Kajmowicz
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007 *
8 * Dictionary Abstract Data Type
9 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
10 * Free Software License:
11 * All rights are reserved by the author, with the following exceptions:
12 * Permission is granted to freely reproduce and distribute this software,
13 * possibly in exchange for a fee, provided that this copyright notice appears
14 * intact. Permission is also granted to adapt this software to produce
15 * derivative works, as long as the modified versions carry this copyright
16 * notice and additional notices stating that the work has been modified.
17 * This source code may be translated into executable form and incorporated
18 * into proprietary software; there is no requirement for such software to
19 * contain a copyright notice related to this source.
20 *
21 * linux/fs/recovery and linux/fs/revoke
22 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
23 *
24 * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
25 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000026 * Journal recovery routines for the generic filesystem journaling code;
27 * part of the ext2fs journaling system.
Rob Landley7a260f02006-06-19 03:20:03 +000028 *
29 * Licensed under GPLv2 or later, see file License in this tarball for details.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000030 */
31
32#ifndef _GNU_SOURCE
33#define _GNU_SOURCE 1 /* get strnlen() */
34#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000035
Rob Landley43ac8882006-04-01 00:40:33 +000036#include "e2fsck.h" /*Put all of our defines here to clean things up*/
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000037
Rob Landley15d20a02006-05-29 05:00:44 +000038#define _(x) x
39#define N_(x) x
40
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000041/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000042 * Procedure declarations
43 */
44
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000045static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000046
47/* pass1.c */
48static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000049
50/* pass2.c */
51static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
52 ext2_ino_t ino, char *buf);
53
54/* pass3.c */
55static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
56static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
57 int num, int gauranteed_size);
58static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
59static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
60 int adj);
61
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000062/* rehash.c */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000063static void e2fsck_rehash_directories(e2fsck_t ctx);
64
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000065/* util.c */
66static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
67 const char *description);
68static int ask(e2fsck_t ctx, const char * string, int def);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000069static void e2fsck_read_bitmaps(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000070static void preenhalt(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000071static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
72 struct ext2_inode * inode, const char * proc);
73static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
74 struct ext2_inode * inode, const char * proc);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000075static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
76 const char *name, io_manager manager);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000077
78/* unix.c */
79static void e2fsck_clear_progbar(e2fsck_t ctx);
80static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
81 float percent, unsigned int dpynum);
Rob Landley43ac8882006-04-01 00:40:33 +000082
83
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000084/*
85 * problem.h --- e2fsck problem error codes
86 */
87
88typedef __u32 problem_t;
89
90struct problem_context {
91 errcode_t errcode;
92 ext2_ino_t ino, ino2, dir;
93 struct ext2_inode *inode;
94 struct ext2_dir_entry *dirent;
95 blk_t blk, blk2;
96 e2_blkcnt_t blkcount;
97 int group;
98 __u64 num;
99 const char *str;
100};
101
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000102
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000103/*
104 * Function declarations
105 */
106static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
107static int end_problem_latch(e2fsck_t ctx, int mask);
108static int set_latch_flags(int mask, int setflags, int clearflags);
109static void clear_problem_context(struct problem_context *ctx);
110
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000111/*
112 * Dictionary Abstract Data Type
113 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
114 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000115 * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
116 * kazlib_1_20
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000117 */
118
119#ifndef DICT_H
120#define DICT_H
121
122/*
123 * Blurb for inclusion into C++ translation units
124 */
125
126typedef unsigned long dictcount_t;
127#define DICTCOUNT_T_MAX ULONG_MAX
128
129/*
130 * The dictionary is implemented as a red-black tree
131 */
132
133typedef enum { dnode_red, dnode_black } dnode_color_t;
134
135typedef struct dnode_t {
136 struct dnode_t *dict_left;
137 struct dnode_t *dict_right;
138 struct dnode_t *dict_parent;
139 dnode_color_t dict_color;
140 const void *dict_key;
141 void *dict_data;
142} dnode_t;
143
144typedef int (*dict_comp_t)(const void *, const void *);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000145typedef void (*dnode_free_t)(dnode_t *);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000146
147typedef struct dict_t {
148 dnode_t dict_nilnode;
149 dictcount_t dict_nodecount;
150 dictcount_t dict_maxcount;
151 dict_comp_t dict_compare;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000152 dnode_free_t dict_freenode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000153 int dict_dupes;
154} dict_t;
155
156typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
157
158typedef struct dict_load_t {
159 dict_t *dict_dictptr;
160 dnode_t dict_nilnode;
161} dict_load_t;
162
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000163#define dict_count(D) ((D)->dict_nodecount)
164#define dnode_get(N) ((N)->dict_data)
165#define dnode_getkey(N) ((N)->dict_key)
166
167#endif
168
169/*
170 * Compatibility header file for e2fsck which should be included
171 * instead of linux/jfs.h
172 *
173 * Copyright (C) 2000 Stephen C. Tweedie
174 */
175
176/*
177 * Pull in the definition of the e2fsck context structure
178 */
179
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000180struct buffer_head {
181 char b_data[8192];
182 e2fsck_t b_ctx;
183 io_channel b_io;
184 int b_size;
185 blk_t b_blocknr;
186 int b_dirty;
187 int b_uptodate;
188 int b_err;
189};
190
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000191
192#define K_DEV_FS 1
193#define K_DEV_JOURNAL 2
194
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000195#define lock_buffer(bh) do {} while(0)
196#define unlock_buffer(bh) do {} while(0)
197#define buffer_req(bh) 1
198#define do_readahead(journal, start) do {} while(0)
199
200static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
201
202typedef struct {
203 int object_length;
204} kmem_cache_t;
205
206#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000207
208/*
209 * We use the standard libext2fs portability tricks for inline
210 * functions.
211 */
212
Rob Landley7c94bed2006-05-03 21:58:45 +0000213static kmem_cache_t * do_cache_create(int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000214{
215 kmem_cache_t *new_cache;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000216
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000217 new_cache = malloc(sizeof(*new_cache));
218 if (new_cache)
219 new_cache->object_length = len;
220 return new_cache;
221}
222
Rob Landley7c94bed2006-05-03 21:58:45 +0000223static void do_cache_destroy(kmem_cache_t *cache)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000224{
225 free(cache);
226}
227
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000228
229/*
230 * Dictionary Abstract Data Type
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000231 */
232
233
234/*
235 * These macros provide short convenient names for structure members,
236 * which are embellished with dict_ prefixes so that they are
237 * properly confined to the documented namespace. It's legal for a
238 * program which uses dict to define, for instance, a macro called ``parent''.
239 * Such a macro would interfere with the dnode_t struct definition.
240 * In general, highly portable and reusable C modules which expose their
241 * structures need to confine structure member names to well-defined spaces.
242 * The resulting identifiers aren't necessarily convenient to use, nor
243 * readable, in the implementation, however!
244 */
245
246#define left dict_left
247#define right dict_right
248#define parent dict_parent
249#define color dict_color
250#define key dict_key
251#define data dict_data
252
253#define nilnode dict_nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000254#define maxcount dict_maxcount
255#define compare dict_compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000256#define dupes dict_dupes
257
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000258#define dict_root(D) ((D)->nilnode.left)
259#define dict_nil(D) (&(D)->nilnode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000260
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000261static void dnode_free(dnode_t *node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000262
263/*
264 * Perform a ``left rotation'' adjustment on the tree. The given node P and
265 * its right child C are rearranged so that the P instead becomes the left
266 * child of C. The left subtree of C is inherited as the new right subtree
267 * for P. The ordering of the keys within the tree is thus preserved.
268 */
269
270static void rotate_left(dnode_t *upper)
271{
272 dnode_t *lower, *lowleft, *upparent;
273
274 lower = upper->right;
275 upper->right = lowleft = lower->left;
276 lowleft->parent = upper;
277
278 lower->parent = upparent = upper->parent;
279
280 /* don't need to check for root node here because root->parent is
281 the sentinel nil node, and root->parent->left points back to root */
282
283 if (upper == upparent->left) {
284 upparent->left = lower;
285 } else {
286 assert (upper == upparent->right);
287 upparent->right = lower;
288 }
289
290 lower->left = upper;
291 upper->parent = lower;
292}
293
294/*
295 * This operation is the ``mirror'' image of rotate_left. It is
296 * the same procedure, but with left and right interchanged.
297 */
298
299static void rotate_right(dnode_t *upper)
300{
301 dnode_t *lower, *lowright, *upparent;
302
303 lower = upper->left;
304 upper->left = lowright = lower->right;
305 lowright->parent = upper;
306
307 lower->parent = upparent = upper->parent;
308
309 if (upper == upparent->right) {
310 upparent->right = lower;
311 } else {
312 assert (upper == upparent->left);
313 upparent->left = lower;
314 }
315
316 lower->right = upper;
317 upper->parent = lower;
318}
319
320/*
321 * Do a postorder traversal of the tree rooted at the specified
322 * node and free everything under it. Used by dict_free().
323 */
324
325static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
326{
327 if (node == nil)
328 return;
329 free_nodes(dict, node->left, nil);
330 free_nodes(dict, node->right, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000331 dict->dict_freenode(node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000332}
333
334/*
335 * Verify that the tree contains the given node. This is done by
336 * traversing all of the nodes and comparing their pointers to the
337 * given pointer. Returns 1 if the node is found, otherwise
338 * returns zero. It is intended for debugging purposes.
339 */
340
341static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
342{
343 if (root != nil) {
344 return root == node
345 || verify_dict_has_node(nil, root->left, node)
346 || verify_dict_has_node(nil, root->right, node);
347 }
348 return 0;
349}
350
351
352/*
353 * Select a different set of node allocator routines.
354 */
355
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000356static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000357{
358 assert (dict_count(dict) == 0);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000359 dict->dict_freenode = fr;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000360}
361
362/*
363 * Free all the nodes in the dictionary by using the dictionary's
364 * installed free routine. The dictionary is emptied.
365 */
366
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000367static void dict_free_nodes(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000368{
369 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
370 free_nodes(dict, root, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000371 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000372 dict->nilnode.left = &dict->nilnode;
373 dict->nilnode.right = &dict->nilnode;
374}
375
376/*
377 * Initialize a user-supplied dictionary object.
378 */
379
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000380static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000381{
382 dict->compare = comp;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000383 dict->dict_freenode = dnode_free;
384 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000385 dict->maxcount = maxcount;
386 dict->nilnode.left = &dict->nilnode;
387 dict->nilnode.right = &dict->nilnode;
388 dict->nilnode.parent = &dict->nilnode;
389 dict->nilnode.color = dnode_black;
390 dict->dupes = 0;
391 return dict;
392}
393
394/*
395 * Locate a node in the dictionary having the given key.
396 * If the node is not found, a null a pointer is returned (rather than
397 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
398 * located node is returned.
399 */
400
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000401static dnode_t *dict_lookup(dict_t *dict, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000402{
403 dnode_t *root = dict_root(dict);
404 dnode_t *nil = dict_nil(dict);
405 dnode_t *saved;
406 int result;
407
408 /* simple binary search adapted for trees that contain duplicate keys */
409
410 while (root != nil) {
411 result = dict->compare(key, root->key);
412 if (result < 0)
413 root = root->left;
414 else if (result > 0)
415 root = root->right;
416 else {
417 if (!dict->dupes) { /* no duplicates, return match */
418 return root;
419 } else { /* could be dupes, find leftmost one */
420 do {
421 saved = root;
422 root = root->left;
423 while (root != nil && dict->compare(key, root->key))
424 root = root->right;
425 } while (root != nil);
426 return saved;
427 }
428 }
429 }
430
431 return NULL;
432}
433
434/*
435 * Insert a node into the dictionary. The node should have been
436 * initialized with a data field. All other fields are ignored.
437 * The behavior is undefined if the user attempts to insert into
438 * a dictionary that is already full (for which the dict_isfull()
439 * function returns true).
440 */
441
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000442static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000443{
444 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
445 dnode_t *parent = nil, *uncle, *grandpa;
446 int result = -1;
447
448 node->key = key;
449
450 /* basic binary tree insert */
451
452 while (where != nil) {
453 parent = where;
454 result = dict->compare(key, where->key);
455 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
456 assert (dict->dupes || result != 0);
457 if (result < 0)
458 where = where->left;
459 else
460 where = where->right;
461 }
462
463 assert (where == nil);
464
465 if (result < 0)
466 parent->left = node;
467 else
468 parent->right = node;
469
470 node->parent = parent;
471 node->left = nil;
472 node->right = nil;
473
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000474 dict->dict_nodecount++;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000475
476 /* red black adjustments */
477
478 node->color = dnode_red;
479
480 while (parent->color == dnode_red) {
481 grandpa = parent->parent;
482 if (parent == grandpa->left) {
483 uncle = grandpa->right;
484 if (uncle->color == dnode_red) { /* red parent, red uncle */
485 parent->color = dnode_black;
486 uncle->color = dnode_black;
487 grandpa->color = dnode_red;
488 node = grandpa;
489 parent = grandpa->parent;
490 } else { /* red parent, black uncle */
491 if (node == parent->right) {
492 rotate_left(parent);
493 parent = node;
494 assert (grandpa == parent->parent);
495 /* rotation between parent and child preserves grandpa */
496 }
497 parent->color = dnode_black;
498 grandpa->color = dnode_red;
499 rotate_right(grandpa);
500 break;
501 }
502 } else { /* symmetric cases: parent == parent->parent->right */
503 uncle = grandpa->left;
504 if (uncle->color == dnode_red) {
505 parent->color = dnode_black;
506 uncle->color = dnode_black;
507 grandpa->color = dnode_red;
508 node = grandpa;
509 parent = grandpa->parent;
510 } else {
511 if (node == parent->left) {
512 rotate_right(parent);
513 parent = node;
514 assert (grandpa == parent->parent);
515 }
516 parent->color = dnode_black;
517 grandpa->color = dnode_red;
518 rotate_left(grandpa);
519 break;
520 }
521 }
522 }
523
524 dict_root(dict)->color = dnode_black;
525
526}
527
528/*
529 * Allocate a node using the dictionary's allocator routine, give it
530 * the data item.
531 */
532
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000533static dnode_t *dnode_init(dnode_t *dnode, void *data)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000534{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000535 dnode->data = data;
536 dnode->parent = NULL;
537 dnode->left = NULL;
538 dnode->right = NULL;
539 return dnode;
540}
541
542static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
543{
544 dnode_t *node = malloc(sizeof(dnode_t));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000545
546 if (node) {
547 dnode_init(node, data);
548 dict_insert(dict, node, key);
549 return 1;
550 }
551 return 0;
552}
553
554/*
555 * Return the node with the lowest (leftmost) key. If the dictionary is empty
556 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
557 */
558
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000559static dnode_t *dict_first(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000560{
561 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
562
563 if (root != nil)
564 while ((left = root->left) != nil)
565 root = left;
566
567 return (root == nil) ? NULL : root;
568}
569
570/*
571 * Return the given node's successor node---the node which has the
572 * next key in the the left to right ordering. If the node has
573 * no successor, a null pointer is returned rather than a pointer to
574 * the nil node.
575 */
576
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000577static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000578{
579 dnode_t *nil = dict_nil(dict), *parent, *left;
580
581 if (curr->right != nil) {
582 curr = curr->right;
583 while ((left = curr->left) != nil)
584 curr = left;
585 return curr;
586 }
587
588 parent = curr->parent;
589
590 while (parent != nil && curr == parent->right) {
591 curr = parent;
592 parent = curr->parent;
593 }
594
595 return (parent == nil) ? NULL : parent;
596}
597
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000598
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000599static void dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000600{
601 free(node);
602}
603
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000604
605#undef left
606#undef right
607#undef parent
608#undef color
609#undef key
610#undef data
611
612#undef nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000613#undef maxcount
614#undef compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000615#undef dupes
616
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000617
618/*
619 * dirinfo.c --- maintains the directory information table for e2fsck.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000620 */
621
622/*
623 * This subroutine is called during pass1 to create a directory info
624 * entry. During pass1, the passed-in parent is 0; it will get filled
625 * in during pass2.
626 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000627static 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 +0000628{
629 struct dir_info *dir;
630 int i, j;
631 ext2_ino_t num_dirs;
632 errcode_t retval;
633 unsigned long old_size;
634
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000635 if (!ctx->dir_info) {
636 ctx->dir_info_count = 0;
637 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
638 if (retval)
639 num_dirs = 1024; /* Guess */
640 ctx->dir_info_size = num_dirs + 10;
641 ctx->dir_info = (struct dir_info *)
642 e2fsck_allocate_memory(ctx, ctx->dir_info_size
643 * sizeof (struct dir_info),
644 "directory map");
645 }
646
647 if (ctx->dir_info_count >= ctx->dir_info_size) {
648 old_size = ctx->dir_info_size * sizeof(struct dir_info);
649 ctx->dir_info_size += 10;
650 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
651 sizeof(struct dir_info),
652 &ctx->dir_info);
653 if (retval) {
654 ctx->dir_info_size -= 10;
655 return;
656 }
657 }
658
659 /*
660 * Normally, add_dir_info is called with each inode in
661 * sequential order; but once in a while (like when pass 3
662 * needs to recreate the root directory or lost+found
663 * directory) it is called out of order. In those cases, we
664 * need to move the dir_info entries down to make room, since
665 * the dir_info array needs to be sorted by inode number for
666 * get_dir_info()'s sake.
667 */
668 if (ctx->dir_info_count &&
669 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
670 for (i = ctx->dir_info_count-1; i > 0; i--)
671 if (ctx->dir_info[i-1].ino < ino)
672 break;
673 dir = &ctx->dir_info[i];
674 if (dir->ino != ino)
675 for (j = ctx->dir_info_count++; j > i; j--)
676 ctx->dir_info[j] = ctx->dir_info[j-1];
677 } else
678 dir = &ctx->dir_info[ctx->dir_info_count++];
679
680 dir->ino = ino;
681 dir->dotdot = parent;
682 dir->parent = parent;
683}
684
685/*
686 * get_dir_info() --- given an inode number, try to find the directory
687 * information entry for it.
688 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000689static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000690{
691 int low, high, mid;
692
693 low = 0;
694 high = ctx->dir_info_count-1;
695 if (!ctx->dir_info)
696 return 0;
697 if (ino == ctx->dir_info[low].ino)
698 return &ctx->dir_info[low];
699 if (ino == ctx->dir_info[high].ino)
700 return &ctx->dir_info[high];
701
702 while (low < high) {
703 mid = (low+high)/2;
704 if (mid == low || mid == high)
705 break;
706 if (ino == ctx->dir_info[mid].ino)
707 return &ctx->dir_info[mid];
708 if (ino < ctx->dir_info[mid].ino)
709 high = mid;
710 else
711 low = mid;
712 }
713 return 0;
714}
715
716/*
717 * Free the dir_info structure when it isn't needed any more.
718 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000719static void e2fsck_free_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000720{
Rob Landleye7c43b62006-03-01 16:39:45 +0000721 ext2fs_free_mem(&ctx->dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000722 ctx->dir_info_size = 0;
723 ctx->dir_info_count = 0;
724}
725
726/*
727 * Return the count of number of directories in the dir_info structure
728 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000729static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000730{
731 return ctx->dir_info_count;
732}
733
734/*
735 * A simple interator function
736 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000737static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000738{
739 if (*control >= ctx->dir_info_count)
740 return 0;
741
742 return(ctx->dir_info + (*control)++);
743}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000744
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000745/*
746 * dirinfo.c --- maintains the directory information table for e2fsck.
747 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000748 */
749
750#ifdef ENABLE_HTREE
751
752/*
753 * This subroutine is called during pass1 to create a directory info
754 * entry. During pass1, the passed-in parent is 0; it will get filled
755 * in during pass2.
756 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000757static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000758{
759 struct dx_dir_info *dir;
760 int i, j;
761 errcode_t retval;
762 unsigned long old_size;
763
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000764 if (!ctx->dx_dir_info) {
765 ctx->dx_dir_info_count = 0;
766 ctx->dx_dir_info_size = 100; /* Guess */
767 ctx->dx_dir_info = (struct dx_dir_info *)
768 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
769 * sizeof (struct dx_dir_info),
770 "directory map");
771 }
772
773 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
774 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
775 ctx->dx_dir_info_size += 10;
776 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
777 sizeof(struct dx_dir_info),
778 &ctx->dx_dir_info);
779 if (retval) {
780 ctx->dx_dir_info_size -= 10;
781 return;
782 }
783 }
784
785 /*
786 * Normally, add_dx_dir_info is called with each inode in
787 * sequential order; but once in a while (like when pass 3
788 * needs to recreate the root directory or lost+found
789 * directory) it is called out of order. In those cases, we
790 * need to move the dx_dir_info entries down to make room, since
791 * the dx_dir_info array needs to be sorted by inode number for
792 * get_dx_dir_info()'s sake.
793 */
794 if (ctx->dx_dir_info_count &&
795 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
796 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
797 if (ctx->dx_dir_info[i-1].ino < ino)
798 break;
799 dir = &ctx->dx_dir_info[i];
800 if (dir->ino != ino)
801 for (j = ctx->dx_dir_info_count++; j > i; j--)
802 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
803 } else
804 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
805
806 dir->ino = ino;
807 dir->numblocks = num_blocks;
808 dir->hashversion = 0;
809 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
810 * sizeof (struct dx_dirblock_info),
811 "dx_block info array");
812
813}
814
815/*
816 * get_dx_dir_info() --- given an inode number, try to find the directory
817 * information entry for it.
818 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000819static 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 +0000820{
821 int low, high, mid;
822
823 low = 0;
824 high = ctx->dx_dir_info_count-1;
825 if (!ctx->dx_dir_info)
826 return 0;
827 if (ino == ctx->dx_dir_info[low].ino)
828 return &ctx->dx_dir_info[low];
829 if (ino == ctx->dx_dir_info[high].ino)
830 return &ctx->dx_dir_info[high];
831
832 while (low < high) {
833 mid = (low+high)/2;
834 if (mid == low || mid == high)
835 break;
836 if (ino == ctx->dx_dir_info[mid].ino)
837 return &ctx->dx_dir_info[mid];
838 if (ino < ctx->dx_dir_info[mid].ino)
839 high = mid;
840 else
841 low = mid;
842 }
843 return 0;
844}
845
846/*
847 * Free the dx_dir_info structure when it isn't needed any more.
848 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000849static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000850{
851 int i;
852 struct dx_dir_info *dir;
853
854 if (ctx->dx_dir_info) {
855 dir = ctx->dx_dir_info;
856 for (i=0; i < ctx->dx_dir_info_count; i++) {
Rob Landleye7c43b62006-03-01 16:39:45 +0000857 ext2fs_free_mem(&dir->dx_block);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000858 }
859 ext2fs_free_mem(&ctx->dx_dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000860 }
861 ctx->dx_dir_info_size = 0;
862 ctx->dx_dir_info_count = 0;
863}
864
865/*
866 * A simple interator function
867 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000868static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000869{
870 if (*control >= ctx->dx_dir_info_count)
871 return 0;
872
873 return(ctx->dx_dir_info + (*control)++);
874}
875
876#endif /* ENABLE_HTREE */
877/*
878 * e2fsck.c - a consistency checker for the new extended file system.
879 *
Mike Frysinger51a43b42005-09-24 07:11:16 +0000880 */
881
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000882/*
883 * This function allocates an e2fsck context
884 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000885static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000886{
887 e2fsck_t context;
888 errcode_t retval;
889
890 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
891 if (retval)
892 return retval;
893
894 memset(context, 0, sizeof(struct e2fsck_struct));
895
896 context->process_inode_size = 256;
897 context->ext_attr_ver = 2;
898
899 *ret = context;
900 return 0;
901}
902
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000903struct ea_refcount_el {
904 blk_t ea_blk;
905 int ea_count;
906};
907
908struct ea_refcount {
909 blk_t count;
910 blk_t size;
911 blk_t cursor;
912 struct ea_refcount_el *list;
913};
914
915static void ea_refcount_free(ext2_refcount_t refcount)
916{
917 if (!refcount)
918 return;
919
Rob Landleye7c43b62006-03-01 16:39:45 +0000920 ext2fs_free_mem(&refcount->list);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000921 ext2fs_free_mem(&refcount);
922}
923
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000924/*
925 * This function resets an e2fsck context; it is called when e2fsck
926 * needs to be restarted.
927 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000928static errcode_t e2fsck_reset_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000929{
930 ctx->flags = 0;
931 ctx->lost_and_found = 0;
932 ctx->bad_lost_and_found = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +0000933 ext2fs_free_inode_bitmap(ctx->inode_used_map);
934 ctx->inode_used_map = 0;
935 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
936 ctx->inode_dir_map = 0;
937 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
938 ctx->inode_reg_map = 0;
939 ext2fs_free_block_bitmap(ctx->block_found_map);
940 ctx->block_found_map = 0;
941 ext2fs_free_icount(ctx->inode_link_info);
942 ctx->inode_link_info = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000943 if (ctx->journal_io) {
944 if (ctx->fs && ctx->fs->io != ctx->journal_io)
945 io_channel_close(ctx->journal_io);
946 ctx->journal_io = 0;
947 }
Rob Landleye7c43b62006-03-01 16:39:45 +0000948 if (ctx->fs) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000949 ext2fs_free_dblist(ctx->fs->dblist);
950 ctx->fs->dblist = 0;
951 }
952 e2fsck_free_dir_info(ctx);
953#ifdef ENABLE_HTREE
954 e2fsck_free_dx_dir_info(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +0000955#endif
Rob Landleye7c43b62006-03-01 16:39:45 +0000956 ea_refcount_free(ctx->refcount);
957 ctx->refcount = 0;
958 ea_refcount_free(ctx->refcount_extra);
959 ctx->refcount_extra = 0;
960 ext2fs_free_block_bitmap(ctx->block_dup_map);
961 ctx->block_dup_map = 0;
962 ext2fs_free_block_bitmap(ctx->block_ea_map);
963 ctx->block_ea_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +0000964 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
965 ctx->inode_bad_map = 0;
966 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
967 ctx->inode_imagic_map = 0;
968 ext2fs_u32_list_free(ctx->dirs_to_hash);
969 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000970
971 /*
972 * Clear the array of invalid meta-data flags
973 */
Rob Landleye7c43b62006-03-01 16:39:45 +0000974 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
975 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
976 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000977
978 /* Clear statistic counters */
979 ctx->fs_directory_count = 0;
980 ctx->fs_regular_count = 0;
981 ctx->fs_blockdev_count = 0;
982 ctx->fs_chardev_count = 0;
983 ctx->fs_links_count = 0;
984 ctx->fs_symlinks_count = 0;
985 ctx->fs_fast_symlinks_count = 0;
986 ctx->fs_fifo_count = 0;
987 ctx->fs_total_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000988 ctx->fs_sockets_count = 0;
989 ctx->fs_ind_count = 0;
990 ctx->fs_dind_count = 0;
991 ctx->fs_tind_count = 0;
992 ctx->fs_fragmented = 0;
993 ctx->large_files = 0;
994
995 /* Reset the superblock to the user's requested value */
996 ctx->superblock = ctx->use_superblock;
997
998 return 0;
999}
1000
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001001static void e2fsck_free_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001002{
1003 if (!ctx)
1004 return;
1005
1006 e2fsck_reset_context(ctx);
1007 if (ctx->blkid)
1008 blkid_put_cache(ctx->blkid);
1009
1010 ext2fs_free_mem(&ctx);
1011}
1012
1013/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001014 * ea_refcount.c
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001015 */
1016
1017/*
1018 * The strategy we use for keeping track of EA refcounts is as
1019 * follows. We keep a sorted array of first EA blocks and its
1020 * reference counts. Once the refcount has dropped to zero, it is
1021 * removed from the array to save memory space. Once the EA block is
1022 * checked, its bit is set in the block_ea_map bitmap.
1023 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001024
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001025
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001026static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001027{
1028 ext2_refcount_t refcount;
1029 errcode_t retval;
1030 size_t bytes;
1031
1032 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
1033 if (retval)
1034 return retval;
1035 memset(refcount, 0, sizeof(struct ea_refcount));
1036
1037 if (!size)
1038 size = 500;
1039 refcount->size = size;
1040 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
1041#ifdef DEBUG
1042 printf("Refcount allocated %d entries, %d bytes.\n",
1043 refcount->size, bytes);
1044#endif
1045 retval = ext2fs_get_mem(bytes, &refcount->list);
1046 if (retval)
1047 goto errout;
1048 memset(refcount->list, 0, bytes);
1049
1050 refcount->count = 0;
1051 refcount->cursor = 0;
1052
1053 *ret = refcount;
1054 return 0;
1055
1056errout:
1057 ea_refcount_free(refcount);
1058 return(retval);
1059}
1060
1061/*
1062 * collapse_refcount() --- go through the refcount array, and get rid
1063 * of any count == zero entries
1064 */
1065static void refcount_collapse(ext2_refcount_t refcount)
1066{
1067 unsigned int i, j;
1068 struct ea_refcount_el *list;
1069
1070 list = refcount->list;
1071 for (i = 0, j = 0; i < refcount->count; i++) {
1072 if (list[i].ea_count) {
1073 if (i != j)
1074 list[j] = list[i];
1075 j++;
1076 }
1077 }
1078#if defined(DEBUG) || defined(TEST_PROGRAM)
1079 printf("Refcount_collapse: size was %d, now %d\n",
1080 refcount->count, j);
1081#endif
1082 refcount->count = j;
1083}
1084
1085
1086/*
1087 * insert_refcount_el() --- Insert a new entry into the sorted list at a
1088 * specified position.
1089 */
1090static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
1091 blk_t blk, int pos)
1092{
1093 struct ea_refcount_el *el;
1094 errcode_t retval;
1095 blk_t new_size = 0;
1096 int num;
1097
1098 if (refcount->count >= refcount->size) {
1099 new_size = refcount->size + 100;
1100#ifdef DEBUG
1101 printf("Reallocating refcount %d entries...\n", new_size);
1102#endif
1103 retval = ext2fs_resize_mem((size_t) refcount->size *
1104 sizeof(struct ea_refcount_el),
1105 (size_t) new_size *
1106 sizeof(struct ea_refcount_el),
1107 &refcount->list);
1108 if (retval)
1109 return 0;
1110 refcount->size = new_size;
1111 }
1112 num = (int) refcount->count - pos;
1113 if (num < 0)
1114 return 0; /* should never happen */
1115 if (num) {
1116 memmove(&refcount->list[pos+1], &refcount->list[pos],
1117 sizeof(struct ea_refcount_el) * num);
1118 }
1119 refcount->count++;
1120 el = &refcount->list[pos];
1121 el->ea_count = 0;
1122 el->ea_blk = blk;
1123 return el;
1124}
1125
1126
1127/*
1128 * get_refcount_el() --- given an block number, try to find refcount
1129 * information in the sorted list. If the create flag is set,
1130 * and we can't find an entry, create one in the sorted list.
1131 */
1132static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
1133 blk_t blk, int create)
1134{
1135 float range;
1136 int low, high, mid;
1137 blk_t lowval, highval;
1138
1139 if (!refcount || !refcount->list)
1140 return 0;
1141retry:
1142 low = 0;
1143 high = (int) refcount->count-1;
1144 if (create && ((refcount->count == 0) ||
1145 (blk > refcount->list[high].ea_blk))) {
1146 if (refcount->count >= refcount->size)
1147 refcount_collapse(refcount);
1148
1149 return insert_refcount_el(refcount, blk,
1150 (unsigned) refcount->count);
1151 }
1152 if (refcount->count == 0)
1153 return 0;
1154
1155 if (refcount->cursor >= refcount->count)
1156 refcount->cursor = 0;
1157 if (blk == refcount->list[refcount->cursor].ea_blk)
1158 return &refcount->list[refcount->cursor++];
1159#ifdef DEBUG
1160 printf("Non-cursor get_refcount_el: %u\n", blk);
1161#endif
1162 while (low <= high) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001163 if (low == high)
1164 mid = low;
1165 else {
1166 /* Interpolate for efficiency */
1167 lowval = refcount->list[low].ea_blk;
1168 highval = refcount->list[high].ea_blk;
1169
1170 if (blk < lowval)
1171 range = 0;
1172 else if (blk > highval)
1173 range = 1;
1174 else
1175 range = ((float) (blk - lowval)) /
1176 (highval - lowval);
1177 mid = low + ((int) (range * (high-low)));
1178 }
Rob Landley3e72c592006-04-06 22:49:04 +00001179
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001180 if (blk == refcount->list[mid].ea_blk) {
1181 refcount->cursor = mid+1;
1182 return &refcount->list[mid];
1183 }
1184 if (blk < refcount->list[mid].ea_blk)
1185 high = mid-1;
1186 else
1187 low = mid+1;
1188 }
1189 /*
1190 * If we need to create a new entry, it should be right at
1191 * low (where high will be left at low-1).
1192 */
1193 if (create) {
1194 if (refcount->count >= refcount->size) {
1195 refcount_collapse(refcount);
1196 if (refcount->count < refcount->size)
1197 goto retry;
1198 }
1199 return insert_refcount_el(refcount, blk, low);
1200 }
1201 return 0;
1202}
1203
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001204static errcode_t
1205ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001206{
1207 struct ea_refcount_el *el;
1208
1209 el = get_refcount_el(refcount, blk, 1);
1210 if (!el)
1211 return EXT2_ET_NO_MEMORY;
1212 el->ea_count++;
1213
1214 if (ret)
1215 *ret = el->ea_count;
1216 return 0;
1217}
1218
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001219static errcode_t
1220ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001221{
1222 struct ea_refcount_el *el;
1223
1224 el = get_refcount_el(refcount, blk, 0);
1225 if (!el || el->ea_count == 0)
1226 return EXT2_ET_INVALID_ARGUMENT;
1227
1228 el->ea_count--;
1229
1230 if (ret)
1231 *ret = el->ea_count;
1232 return 0;
1233}
1234
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001235static errcode_t
1236ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001237{
1238 struct ea_refcount_el *el;
1239
1240 /*
1241 * Get the refcount element
1242 */
1243 el = get_refcount_el(refcount, blk, count ? 1 : 0);
1244 if (!el)
1245 return count ? EXT2_ET_NO_MEMORY : 0;
1246 el->ea_count = count;
1247 return 0;
1248}
1249
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001250static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001251{
1252 refcount->cursor = 0;
1253}
1254
1255
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001256static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001257{
1258 struct ea_refcount_el *list;
1259
1260 while (1) {
1261 if (refcount->cursor >= refcount->count)
1262 return 0;
1263 list = refcount->list;
1264 if (list[refcount->cursor].ea_count) {
1265 if (ret)
1266 *ret = list[refcount->cursor].ea_count;
1267 return list[refcount->cursor++].ea_blk;
1268 }
1269 refcount->cursor++;
1270 }
1271}
1272
1273
1274/*
1275 * ehandler.c --- handle bad block errors which come up during the
1276 * course of an e2fsck session.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001277 */
1278
1279
1280static const char *operation;
1281
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001282static errcode_t
1283e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001284 void *data, size_t size FSCK_ATTR((unused)),
1285 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001286{
1287 int i;
1288 char *p;
1289 ext2_filsys fs = (ext2_filsys) channel->app_data;
1290 e2fsck_t ctx;
1291
1292 ctx = (e2fsck_t) fs->priv_data;
1293
1294 /*
1295 * If more than one block was read, try reading each block
1296 * separately. We could use the actual bytes read to figure
1297 * out where to start, but we don't bother.
1298 */
1299 if (count > 1) {
1300 p = (char *) data;
1301 for (i=0; i < count; i++, p += channel->block_size, block++) {
1302 error = io_channel_read_blk(channel, block,
1303 1, p);
1304 if (error)
1305 return error;
1306 }
1307 return 0;
1308 }
1309 if (operation)
1310 printf(_("Error reading block %lu (%s) while %s. "), block,
1311 error_message(error), operation);
1312 else
1313 printf(_("Error reading block %lu (%s). "), block,
1314 error_message(error));
1315 preenhalt(ctx);
1316 if (ask(ctx, _("Ignore error"), 1)) {
1317 if (ask(ctx, _("Force rewrite"), 1))
1318 io_channel_write_blk(channel, block, 1, data);
1319 return 0;
1320 }
1321
1322 return error;
1323}
1324
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001325static errcode_t
1326e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001327 const void *data, size_t size FSCK_ATTR((unused)),
1328 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001329{
1330 int i;
1331 const char *p;
1332 ext2_filsys fs = (ext2_filsys) channel->app_data;
1333 e2fsck_t ctx;
1334
1335 ctx = (e2fsck_t) fs->priv_data;
1336
1337 /*
1338 * If more than one block was written, try writing each block
1339 * separately. We could use the actual bytes read to figure
1340 * out where to start, but we don't bother.
1341 */
1342 if (count > 1) {
1343 p = (const char *) data;
1344 for (i=0; i < count; i++, p += channel->block_size, block++) {
1345 error = io_channel_write_blk(channel, block,
1346 1, p);
1347 if (error)
1348 return error;
1349 }
1350 return 0;
1351 }
1352
1353 if (operation)
1354 printf(_("Error writing block %lu (%s) while %s. "), block,
1355 error_message(error), operation);
1356 else
1357 printf(_("Error writing block %lu (%s). "), block,
1358 error_message(error));
1359 preenhalt(ctx);
1360 if (ask(ctx, _("Ignore error"), 1))
1361 return 0;
1362
1363 return error;
1364}
1365
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001366static inline const char *ehandler_operation(const char *op)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001367{
1368 const char *ret = operation;
1369
1370 operation = op;
1371 return ret;
1372}
1373
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001374static void ehandler_init(io_channel channel)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001375{
1376 channel->read_error = e2fsck_handle_read_error;
1377 channel->write_error = e2fsck_handle_write_error;
1378}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001379
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001380/*
1381 * journal.c --- code for handling the "ext3" journal
1382 *
1383 * Copyright (C) 2000 Andreas Dilger
1384 * Copyright (C) 2000 Theodore Ts'o
1385 *
1386 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
1387 * Copyright (C) 1999 Red Hat Software
1388 *
1389 * This file may be redistributed under the terms of the
1390 * GNU General Public License version 2 or at your discretion
1391 * any later version.
1392 */
1393
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001394/*
1395 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
1396 * This creates a larger static binary, and a smaller binary using
1397 * shared libraries. It's also probably slightly less CPU-efficient,
1398 * which is why it's not on by default. But, it's a good way of
1399 * testing the functions in inode_io.c and fileio.c.
1400 */
1401#undef USE_INODE_IO
1402
1403/* Kernel compatibility functions for handling the journal. These allow us
1404 * to use the recovery.c file virtually unchanged from the kernel, so we
1405 * don't have to do much to keep kernel and user recovery in sync.
1406 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001407static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001408{
1409#ifdef USE_INODE_IO
1410 *phys = block;
1411 return 0;
1412#else
1413 struct inode *inode = journal->j_inode;
1414 errcode_t retval;
1415 blk_t pblk;
1416
1417 if (!inode) {
1418 *phys = block;
1419 return 0;
1420 }
1421
1422 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
1423 &inode->i_ext2, NULL, 0, block, &pblk);
1424 *phys = pblk;
1425 return (retval);
1426#endif
1427}
1428
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001429static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001430{
1431 struct buffer_head *bh;
1432
1433 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
1434 if (!bh)
1435 return NULL;
1436
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001437 bh->b_ctx = kdev->k_ctx;
1438 if (kdev->k_dev == K_DEV_FS)
1439 bh->b_io = kdev->k_ctx->fs->io;
1440 else
1441 bh->b_io = kdev->k_ctx->journal_io;
1442 bh->b_size = blocksize;
1443 bh->b_blocknr = blocknr;
1444
1445 return bh;
1446}
1447
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001448static void sync_blockdev(kdev_t kdev)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001449{
1450 io_channel io;
1451
1452 if (kdev->k_dev == K_DEV_FS)
1453 io = kdev->k_ctx->fs->io;
1454 else
1455 io = kdev->k_ctx->journal_io;
1456
1457 io_channel_flush(io);
1458}
1459
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001460static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001461{
1462 int retval;
1463 struct buffer_head *bh;
1464
1465 for (; nr > 0; --nr) {
1466 bh = *bhp++;
1467 if (rw == READ && !bh->b_uptodate) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001468 retval = io_channel_read_blk(bh->b_io,
1469 bh->b_blocknr,
1470 1, bh->b_data);
1471 if (retval) {
Denis Vlasenko6d655be2006-09-06 19:02:46 +00001472 bb_error_msg("while reading block %lu",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001473 (unsigned long) bh->b_blocknr);
1474 bh->b_err = retval;
1475 continue;
1476 }
1477 bh->b_uptodate = 1;
1478 } else if (rw == WRITE && bh->b_dirty) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001479 retval = io_channel_write_blk(bh->b_io,
1480 bh->b_blocknr,
1481 1, bh->b_data);
1482 if (retval) {
Denis Vlasenko6d655be2006-09-06 19:02:46 +00001483 bb_error_msg("while writing block %lu",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001484 (unsigned long) bh->b_blocknr);
1485 bh->b_err = retval;
1486 continue;
1487 }
1488 bh->b_dirty = 0;
1489 bh->b_uptodate = 1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001490 }
1491 }
1492}
1493
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001494static inline void mark_buffer_dirty(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001495{
1496 bh->b_dirty = 1;
1497}
1498
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001499static inline void mark_buffer_clean(struct buffer_head * bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001500{
1501 bh->b_dirty = 0;
1502}
1503
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001504static void brelse(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001505{
1506 if (bh->b_dirty)
1507 ll_rw_block(WRITE, 1, &bh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001508 ext2fs_free_mem(&bh);
1509}
1510
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001511static inline int buffer_uptodate(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001512{
1513 return bh->b_uptodate;
1514}
1515
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001516static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001517{
1518 bh->b_uptodate = val;
1519}
1520
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001521static void wait_on_buffer(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001522{
1523 if (!bh->b_uptodate)
1524 ll_rw_block(READ, 1, &bh);
1525}
1526
1527
1528static void e2fsck_clear_recover(e2fsck_t ctx, int error)
1529{
1530 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
1531
1532 /* if we had an error doing journal recovery, we need a full fsck */
1533 if (error)
1534 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
1535 ext2fs_mark_super_dirty(ctx->fs);
1536}
1537
1538static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
1539{
1540 struct ext2_super_block *sb = ctx->fs->super;
1541 struct ext2_super_block jsuper;
1542 struct problem_context pctx;
1543 struct buffer_head *bh;
1544 struct inode *j_inode = NULL;
1545 struct kdev_s *dev_fs = NULL, *dev_journal;
1546 const char *journal_name = 0;
1547 journal_t *journal = NULL;
1548 errcode_t retval = 0;
1549 io_manager io_ptr = 0;
1550 unsigned long start = 0;
1551 blk_t blk;
1552 int ext_journal = 0;
1553 int tried_backup_jnl = 0;
1554 int i;
1555
1556 clear_problem_context(&pctx);
1557
1558 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
1559 if (!journal) {
1560 return EXT2_ET_NO_MEMORY;
1561 }
1562
1563 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
1564 if (!dev_fs) {
1565 retval = EXT2_ET_NO_MEMORY;
1566 goto errout;
1567 }
1568 dev_journal = dev_fs+1;
1569
1570 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
1571 dev_fs->k_dev = K_DEV_FS;
1572 dev_journal->k_dev = K_DEV_JOURNAL;
1573
1574 journal->j_dev = dev_journal;
1575 journal->j_fs_dev = dev_fs;
1576 journal->j_inode = NULL;
1577 journal->j_blocksize = ctx->fs->blocksize;
1578
1579 if (uuid_is_null(sb->s_journal_uuid)) {
1580 if (!sb->s_journal_inum)
1581 return EXT2_ET_BAD_INODE_NUM;
1582 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
1583 "journal inode");
1584 if (!j_inode) {
1585 retval = EXT2_ET_NO_MEMORY;
1586 goto errout;
1587 }
1588
1589 j_inode->i_ctx = ctx;
1590 j_inode->i_ino = sb->s_journal_inum;
1591
1592 if ((retval = ext2fs_read_inode(ctx->fs,
1593 sb->s_journal_inum,
1594 &j_inode->i_ext2))) {
1595 try_backup_journal:
1596 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
1597 tried_backup_jnl)
1598 goto errout;
1599 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
1600 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
1601 EXT2_N_BLOCKS*4);
1602 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
1603 j_inode->i_ext2.i_links_count = 1;
1604 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
1605 tried_backup_jnl++;
1606 }
1607 if (!j_inode->i_ext2.i_links_count ||
1608 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
1609 retval = EXT2_ET_NO_JOURNAL;
1610 goto try_backup_journal;
1611 }
1612 if (j_inode->i_ext2.i_size / journal->j_blocksize <
1613 JFS_MIN_JOURNAL_BLOCKS) {
1614 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1615 goto try_backup_journal;
1616 }
1617 for (i=0; i < EXT2_N_BLOCKS; i++) {
1618 blk = j_inode->i_ext2.i_block[i];
1619 if (!blk) {
1620 if (i < EXT2_NDIR_BLOCKS) {
1621 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1622 goto try_backup_journal;
1623 }
1624 continue;
1625 }
1626 if (blk < sb->s_first_data_block ||
1627 blk >= sb->s_blocks_count) {
1628 retval = EXT2_ET_BAD_BLOCK_NUM;
1629 goto try_backup_journal;
1630 }
1631 }
1632 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
1633
1634#ifdef USE_INODE_IO
1635 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
1636 &j_inode->i_ext2,
1637 &journal_name);
1638 if (retval)
1639 goto errout;
1640
1641 io_ptr = inode_io_manager;
1642#else
1643 journal->j_inode = j_inode;
1644 ctx->journal_io = ctx->fs->io;
1645 if ((retval = journal_bmap(journal, 0, &start)) != 0)
1646 goto errout;
1647#endif
1648 } else {
1649 ext_journal = 1;
1650 if (!ctx->journal_name) {
1651 char uuid[37];
1652
1653 uuid_unparse(sb->s_journal_uuid, uuid);
1654 ctx->journal_name = blkid_get_devname(ctx->blkid,
1655 "UUID", uuid);
1656 if (!ctx->journal_name)
1657 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
1658 }
1659 journal_name = ctx->journal_name;
1660
1661 if (!journal_name) {
1662 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
1663 return EXT2_ET_LOAD_EXT_JOURNAL;
1664 }
1665
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001666 io_ptr = unix_io_manager;
1667 }
1668
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001669#ifndef USE_INODE_IO
1670 if (ext_journal)
1671#endif
1672 retval = io_ptr->open(journal_name, IO_FLAG_RW,
1673 &ctx->journal_io);
1674 if (retval)
1675 goto errout;
1676
1677 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
1678
1679 if (ext_journal) {
1680 if (ctx->fs->blocksize == 1024)
1681 start = 1;
1682 bh = getblk(dev_journal, start, ctx->fs->blocksize);
1683 if (!bh) {
1684 retval = EXT2_ET_NO_MEMORY;
1685 goto errout;
1686 }
1687 ll_rw_block(READ, 1, &bh);
1688 if ((retval = bh->b_err) != 0)
1689 goto errout;
1690 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
1691 sizeof(jsuper));
1692 brelse(bh);
Rob Landley7c94bed2006-05-03 21:58:45 +00001693#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001694 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
1695 ext2fs_swap_super(&jsuper);
1696#endif
1697 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
1698 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
1699 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
1700 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1701 goto errout;
1702 }
1703 /* Make sure the journal UUID is correct */
1704 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
1705 sizeof(jsuper.s_uuid))) {
1706 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
1707 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1708 goto errout;
1709 }
1710
1711 journal->j_maxlen = jsuper.s_blocks_count;
1712 start++;
1713 }
1714
1715 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
1716 retval = EXT2_ET_NO_MEMORY;
1717 goto errout;
1718 }
1719
1720 journal->j_sb_buffer = bh;
1721 journal->j_superblock = (journal_superblock_t *)bh->b_data;
1722
1723#ifdef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001724 ext2fs_free_mem(&j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001725#endif
1726
1727 *ret_journal = journal;
1728 return 0;
1729
1730errout:
Rob Landleye7c43b62006-03-01 16:39:45 +00001731 ext2fs_free_mem(&dev_fs);
1732 ext2fs_free_mem(&j_inode);
1733 ext2fs_free_mem(&journal);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001734 return retval;
1735
1736}
1737
1738static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
1739 struct problem_context *pctx)
1740{
1741 struct ext2_super_block *sb = ctx->fs->super;
1742 int recover = ctx->fs->super->s_feature_incompat &
1743 EXT3_FEATURE_INCOMPAT_RECOVER;
1744 int has_journal = ctx->fs->super->s_feature_compat &
1745 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1746
1747 if (has_journal || sb->s_journal_inum) {
1748 /* The journal inode is bogus, remove and force full fsck */
1749 pctx->ino = sb->s_journal_inum;
1750 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
1751 if (has_journal && sb->s_journal_inum)
1752 printf("*** ext3 journal has been deleted - "
1753 "filesystem is now ext2 only ***\n\n");
1754 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1755 sb->s_journal_inum = 0;
1756 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
1757 e2fsck_clear_recover(ctx, 1);
1758 return 0;
1759 }
1760 return EXT2_ET_BAD_INODE_NUM;
1761 } else if (recover) {
1762 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
1763 e2fsck_clear_recover(ctx, 1);
1764 return 0;
1765 }
1766 return EXT2_ET_UNSUPP_FEATURE;
1767 }
1768 return 0;
1769}
1770
1771#define V1_SB_SIZE 0x0024
1772static void clear_v2_journal_fields(journal_t *journal)
1773{
1774 e2fsck_t ctx = journal->j_dev->k_ctx;
1775 struct problem_context pctx;
1776
1777 clear_problem_context(&pctx);
1778
1779 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
1780 return;
1781
1782 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
1783 ctx->fs->blocksize-V1_SB_SIZE);
1784 mark_buffer_dirty(journal->j_sb_buffer);
1785}
1786
1787
1788static errcode_t e2fsck_journal_load(journal_t *journal)
1789{
1790 e2fsck_t ctx = journal->j_dev->k_ctx;
1791 journal_superblock_t *jsb;
1792 struct buffer_head *jbh = journal->j_sb_buffer;
1793 struct problem_context pctx;
1794
1795 clear_problem_context(&pctx);
1796
1797 ll_rw_block(READ, 1, &jbh);
1798 if (jbh->b_err) {
Denis Vlasenko6d655be2006-09-06 19:02:46 +00001799 bb_error_msg(_("reading journal superblock"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001800 return jbh->b_err;
1801 }
1802
1803 jsb = journal->j_superblock;
1804 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
1805 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
1806 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
1807
1808 switch (ntohl(jsb->s_header.h_blocktype)) {
1809 case JFS_SUPERBLOCK_V1:
1810 journal->j_format_version = 1;
1811 if (jsb->s_feature_compat ||
1812 jsb->s_feature_incompat ||
1813 jsb->s_feature_ro_compat ||
1814 jsb->s_nr_users)
1815 clear_v2_journal_fields(journal);
1816 break;
1817
1818 case JFS_SUPERBLOCK_V2:
1819 journal->j_format_version = 2;
1820 if (ntohl(jsb->s_nr_users) > 1 &&
1821 uuid_is_null(ctx->fs->super->s_journal_uuid))
1822 clear_v2_journal_fields(journal);
1823 if (ntohl(jsb->s_nr_users) > 1) {
1824 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
1825 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1826 }
1827 break;
1828
1829 /*
1830 * These should never appear in a journal super block, so if
1831 * they do, the journal is badly corrupted.
1832 */
1833 case JFS_DESCRIPTOR_BLOCK:
1834 case JFS_COMMIT_BLOCK:
1835 case JFS_REVOKE_BLOCK:
1836 return EXT2_ET_CORRUPT_SUPERBLOCK;
1837
1838 /* If we don't understand the superblock major type, but there
1839 * is a magic number, then it is likely to be a new format we
1840 * just don't understand, so leave it alone. */
1841 default:
1842 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1843 }
1844
1845 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
1846 return EXT2_ET_UNSUPP_FEATURE;
1847
1848 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
1849 return EXT2_ET_RO_UNSUPP_FEATURE;
1850
1851 /* We have now checked whether we know enough about the journal
1852 * format to be able to proceed safely, so any other checks that
1853 * fail we should attempt to recover from. */
1854 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
Denis Vlasenko6d655be2006-09-06 19:02:46 +00001855 bb_error_msg(_("%s: no valid journal superblock found"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001856 ctx->device_name);
1857 return EXT2_ET_CORRUPT_SUPERBLOCK;
1858 }
1859
1860 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
1861 journal->j_maxlen = ntohl(jsb->s_maxlen);
1862 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
Denis Vlasenko6d655be2006-09-06 19:02:46 +00001863 bb_error_msg(_("%s: journal too short"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001864 ctx->device_name);
1865 return EXT2_ET_CORRUPT_SUPERBLOCK;
1866 }
1867
1868 journal->j_tail_sequence = ntohl(jsb->s_sequence);
1869 journal->j_transaction_sequence = journal->j_tail_sequence;
1870 journal->j_tail = ntohl(jsb->s_start);
1871 journal->j_first = ntohl(jsb->s_first);
1872 journal->j_last = ntohl(jsb->s_maxlen);
1873
1874 return 0;
1875}
1876
1877static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
1878 journal_t *journal)
1879{
1880 char *p;
1881 union {
1882 uuid_t uuid;
1883 __u32 val[4];
1884 } u;
1885 __u32 new_seq = 0;
1886 int i;
1887
1888 /* Leave a valid existing V1 superblock signature alone.
1889 * Anything unrecognisable we overwrite with a new V2
1890 * signature. */
1891
1892 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
1893 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
1894 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
1895 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
1896 }
1897
1898 /* Zero out everything else beyond the superblock header */
1899
1900 p = ((char *) jsb) + sizeof(journal_header_t);
1901 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
1902
1903 jsb->s_blocksize = htonl(ctx->fs->blocksize);
1904 jsb->s_maxlen = htonl(journal->j_maxlen);
1905 jsb->s_first = htonl(1);
1906
1907 /* Initialize the journal sequence number so that there is "no"
1908 * chance we will find old "valid" transactions in the journal.
1909 * This avoids the need to zero the whole journal (slow to do,
1910 * and risky when we are just recovering the filesystem).
1911 */
1912 uuid_generate(u.uuid);
1913 for (i = 0; i < 4; i ++)
1914 new_seq ^= u.val[i];
1915 jsb->s_sequence = htonl(new_seq);
1916
1917 mark_buffer_dirty(journal->j_sb_buffer);
1918 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
1919}
1920
1921static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
1922 journal_t *journal,
1923 struct problem_context *pctx)
1924{
1925 struct ext2_super_block *sb = ctx->fs->super;
1926 int recover = ctx->fs->super->s_feature_incompat &
1927 EXT3_FEATURE_INCOMPAT_RECOVER;
1928
1929 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
1930 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
1931 e2fsck_journal_reset_super(ctx, journal->j_superblock,
1932 journal);
1933 journal->j_transaction_sequence = 1;
1934 e2fsck_clear_recover(ctx, recover);
1935 return 0;
1936 }
1937 return EXT2_ET_CORRUPT_SUPERBLOCK;
1938 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
1939 return EXT2_ET_CORRUPT_SUPERBLOCK;
1940
1941 return 0;
1942}
1943
1944static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
1945 int reset, int drop)
1946{
1947 journal_superblock_t *jsb;
1948
1949 if (drop)
1950 mark_buffer_clean(journal->j_sb_buffer);
1951 else if (!(ctx->options & E2F_OPT_READONLY)) {
1952 jsb = journal->j_superblock;
1953 jsb->s_sequence = htonl(journal->j_transaction_sequence);
1954 if (reset)
1955 jsb->s_start = 0; /* this marks the journal as empty */
1956 mark_buffer_dirty(journal->j_sb_buffer);
1957 }
1958 brelse(journal->j_sb_buffer);
1959
1960 if (ctx->journal_io) {
1961 if (ctx->fs && ctx->fs->io != ctx->journal_io)
1962 io_channel_close(ctx->journal_io);
1963 ctx->journal_io = 0;
1964 }
1965
1966#ifndef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001967 ext2fs_free_mem(&journal->j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001968#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00001969 ext2fs_free_mem(&journal->j_fs_dev);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001970 ext2fs_free_mem(&journal);
1971}
1972
1973/*
1974 * This function makes sure that the superblock fields regarding the
1975 * journal are consistent.
1976 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001977static int e2fsck_check_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001978{
1979 struct ext2_super_block *sb = ctx->fs->super;
1980 journal_t *journal;
1981 int recover = ctx->fs->super->s_feature_incompat &
1982 EXT3_FEATURE_INCOMPAT_RECOVER;
1983 struct problem_context pctx;
1984 problem_t problem;
1985 int reset = 0, force_fsck = 0;
1986 int retval;
1987
1988 /* If we don't have any journal features, don't do anything more */
1989 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
1990 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
1991 uuid_is_null(sb->s_journal_uuid))
1992 return 0;
1993
1994 clear_problem_context(&pctx);
1995 pctx.num = sb->s_journal_inum;
1996
1997 retval = e2fsck_get_journal(ctx, &journal);
1998 if (retval) {
1999 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
2000 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
2001 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
2002 (retval == EXT2_ET_NO_JOURNAL))
2003 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
2004 return retval;
2005 }
2006
2007 retval = e2fsck_journal_load(journal);
2008 if (retval) {
2009 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
2010 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
2011 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
2012 &pctx))) ||
2013 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
2014 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
2015 &pctx))) ||
2016 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
2017 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
2018 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
2019 &pctx);
2020 e2fsck_journal_release(ctx, journal, 0, 1);
2021 return retval;
2022 }
2023
2024 /*
2025 * We want to make the flags consistent here. We will not leave with
2026 * needs_recovery set but has_journal clear. We can't get in a loop
2027 * with -y, -n, or -p, only if a user isn't making up their mind.
2028 */
2029no_has_journal:
2030 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
2031 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
2032 pctx.str = "inode";
2033 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
2034 if (recover &&
2035 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
2036 goto no_has_journal;
2037 /*
2038 * Need a full fsck if we are releasing a
2039 * journal stored on a reserved inode.
2040 */
2041 force_fsck = recover ||
2042 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
2043 /* Clear all of the journal fields */
2044 sb->s_journal_inum = 0;
2045 sb->s_journal_dev = 0;
2046 memset(sb->s_journal_uuid, 0,
2047 sizeof(sb->s_journal_uuid));
2048 e2fsck_clear_recover(ctx, force_fsck);
2049 } else if (!(ctx->options & E2F_OPT_READONLY)) {
2050 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2051 ext2fs_mark_super_dirty(ctx->fs);
2052 }
2053 }
2054
2055 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
2056 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
2057 journal->j_superblock->s_start != 0) {
2058 /* Print status information */
2059 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
2060 if (ctx->superblock)
2061 problem = PR_0_JOURNAL_RUN_DEFAULT;
2062 else
2063 problem = PR_0_JOURNAL_RUN;
2064 if (fix_problem(ctx, problem, &pctx)) {
2065 ctx->options |= E2F_OPT_FORCE;
2066 sb->s_feature_incompat |=
2067 EXT3_FEATURE_INCOMPAT_RECOVER;
2068 ext2fs_mark_super_dirty(ctx->fs);
2069 } else if (fix_problem(ctx,
2070 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
2071 reset = 1;
2072 sb->s_state &= ~EXT2_VALID_FS;
2073 ext2fs_mark_super_dirty(ctx->fs);
2074 }
2075 /*
2076 * If the user answers no to the above question, we
2077 * ignore the fact that journal apparently has data;
2078 * accidentally replaying over valid data would be far
2079 * worse than skipping a questionable recovery.
2080 *
2081 * XXX should we abort with a fatal error here? What
2082 * will the ext3 kernel code do if a filesystem with
2083 * !NEEDS_RECOVERY but with a non-zero
2084 * journal->j_superblock->s_start is mounted?
2085 */
2086 }
2087
2088 e2fsck_journal_release(ctx, journal, reset, 0);
2089 return retval;
2090}
2091
2092static errcode_t recover_ext3_journal(e2fsck_t ctx)
2093{
2094 journal_t *journal;
2095 int retval;
2096
2097 journal_init_revoke_caches();
2098 retval = e2fsck_get_journal(ctx, &journal);
2099 if (retval)
2100 return retval;
2101
2102 retval = e2fsck_journal_load(journal);
2103 if (retval)
2104 goto errout;
2105
2106 retval = journal_init_revoke(journal, 1024);
2107 if (retval)
2108 goto errout;
2109
2110 retval = -journal_recover(journal);
2111 if (retval)
2112 goto errout;
2113
2114 if (journal->j_superblock->s_errno) {
2115 ctx->fs->super->s_state |= EXT2_ERROR_FS;
2116 ext2fs_mark_super_dirty(ctx->fs);
2117 journal->j_superblock->s_errno = 0;
2118 mark_buffer_dirty(journal->j_sb_buffer);
2119 }
2120
2121errout:
2122 journal_destroy_revoke(journal);
2123 journal_destroy_revoke_caches();
2124 e2fsck_journal_release(ctx, journal, 1, 0);
2125 return retval;
2126}
2127
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002128static int e2fsck_run_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002129{
2130 io_manager io_ptr = ctx->fs->io->manager;
2131 int blocksize = ctx->fs->blocksize;
2132 errcode_t retval, recover_retval;
2133
2134 printf(_("%s: recovering journal\n"), ctx->device_name);
2135 if (ctx->options & E2F_OPT_READONLY) {
2136 printf(_("%s: won't do journal recovery while read-only\n"),
2137 ctx->device_name);
2138 return EXT2_ET_FILE_RO;
2139 }
2140
2141 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
2142 ext2fs_flush(ctx->fs); /* Force out any modifications */
2143
2144 recover_retval = recover_ext3_journal(ctx);
2145
2146 /*
2147 * Reload the filesystem context to get up-to-date data from disk
2148 * because journal recovery will change the filesystem under us.
2149 */
2150 ext2fs_close(ctx->fs);
2151 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
2152 ctx->superblock, blocksize, io_ptr,
2153 &ctx->fs);
2154
2155 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00002156 bb_error_msg(_("while trying to re-open %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002157 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +00002158 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002159 }
2160 ctx->fs->priv_data = ctx;
2161
2162 /* Set the superblock flags */
2163 e2fsck_clear_recover(ctx, recover_retval);
2164 return recover_retval;
2165}
2166
2167/*
2168 * This function will move the journal inode from a visible file in
2169 * the filesystem directory hierarchy to the reserved inode if necessary.
2170 */
2171static const char * const journal_names[] = {
2172 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
2173
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002174static void e2fsck_move_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002175{
2176 struct ext2_super_block *sb = ctx->fs->super;
2177 struct problem_context pctx;
2178 struct ext2_inode inode;
2179 ext2_filsys fs = ctx->fs;
2180 ext2_ino_t ino;
2181 errcode_t retval;
2182 const char * const * cpp;
2183 int group, mount_flags;
2184
2185 clear_problem_context(&pctx);
2186
2187 /*
2188 * If the filesystem is opened read-only, or there is no
2189 * journal, then do nothing.
2190 */
2191 if ((ctx->options & E2F_OPT_READONLY) ||
2192 (sb->s_journal_inum == 0) ||
2193 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
2194 return;
2195
2196 /*
2197 * Read in the journal inode
2198 */
2199 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
2200 return;
2201
2202 /*
2203 * If it's necessary to backup the journal inode, do so.
2204 */
2205 if ((sb->s_jnl_backup_type == 0) ||
2206 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
2207 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
2208 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
2209 memcpy(sb->s_jnl_blocks, inode.i_block,
2210 EXT2_N_BLOCKS*4);
2211 sb->s_jnl_blocks[16] = inode.i_size;
2212 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
2213 ext2fs_mark_super_dirty(fs);
2214 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2215 }
2216 }
2217
2218 /*
2219 * If the journal is already the hidden inode, then do nothing
2220 */
2221 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
2222 return;
2223
2224 /*
2225 * The journal inode had better have only one link and not be readable.
2226 */
2227 if (inode.i_links_count != 1)
2228 return;
2229
2230 /*
2231 * If the filesystem is mounted, or we can't tell whether
2232 * or not it's mounted, do nothing.
2233 */
2234 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
2235 if (retval || (mount_flags & EXT2_MF_MOUNTED))
2236 return;
2237
2238 /*
2239 * If we can't find the name of the journal inode, then do
2240 * nothing.
2241 */
2242 for (cpp = journal_names; *cpp; cpp++) {
2243 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
2244 strlen(*cpp), 0, &ino);
2245 if ((retval == 0) && (ino == sb->s_journal_inum))
2246 break;
2247 }
2248 if (*cpp == 0)
2249 return;
2250
2251 /* We need the inode bitmap to be loaded */
2252 retval = ext2fs_read_bitmaps(fs);
2253 if (retval)
2254 return;
2255
2256 pctx.str = *cpp;
2257 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
2258 return;
2259
2260 /*
2261 * OK, we've done all the checks, let's actually move the
2262 * journal inode. Errors at this point mean we need to force
2263 * an ext2 filesystem check.
2264 */
2265 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
2266 goto err_out;
2267 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
2268 goto err_out;
2269 sb->s_journal_inum = EXT2_JOURNAL_INO;
2270 ext2fs_mark_super_dirty(fs);
2271 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2272 inode.i_links_count = 0;
2273 inode.i_dtime = time(0);
2274 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
2275 goto err_out;
2276
2277 group = ext2fs_group_of_ino(fs, ino);
2278 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
2279 ext2fs_mark_ib_dirty(fs);
2280 fs->group_desc[group].bg_free_inodes_count++;
2281 fs->super->s_free_inodes_count++;
2282 return;
2283
2284err_out:
2285 pctx.errcode = retval;
2286 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
2287 fs->super->s_state &= ~EXT2_VALID_FS;
2288 ext2fs_mark_super_dirty(fs);
2289 return;
2290}
2291
2292/*
2293 * message.c --- print e2fsck messages (with compression)
2294 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002295 * print_e2fsck_message() prints a message to the user, using
2296 * compression techniques and expansions of abbreviations.
2297 *
2298 * The following % expansions are supported:
2299 *
2300 * %b <blk> block number
2301 * %B <blkcount> integer
2302 * %c <blk2> block number
2303 * %Di <dirent>->ino inode number
2304 * %Dn <dirent>->name string
2305 * %Dr <dirent>->rec_len
2306 * %Dl <dirent>->name_len
2307 * %Dt <dirent>->filetype
2308 * %d <dir> inode number
2309 * %g <group> integer
2310 * %i <ino> inode number
2311 * %Is <inode> -> i_size
2312 * %IS <inode> -> i_extra_isize
2313 * %Ib <inode> -> i_blocks
2314 * %Il <inode> -> i_links_count
2315 * %Im <inode> -> i_mode
2316 * %IM <inode> -> i_mtime
2317 * %IF <inode> -> i_faddr
2318 * %If <inode> -> i_file_acl
2319 * %Id <inode> -> i_dir_acl
2320 * %Iu <inode> -> i_uid
2321 * %Ig <inode> -> i_gid
2322 * %j <ino2> inode number
2323 * %m <com_err error message>
2324 * %N <num>
2325 * %p ext2fs_get_pathname of directory <ino>
2326 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
2327 * the containing directory. (If dirent is NULL
2328 * then return the pathname of directory <ino2>)
2329 * %q ext2fs_get_pathname of directory <dir>
2330 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
2331 * the containing directory.
2332 * %s <str> miscellaneous string
2333 * %S backup superblock
2334 * %X <num> hexadecimal format
2335 *
2336 * The following '@' expansions are supported:
2337 *
2338 * @a extended attribute
2339 * @A error allocating
2340 * @b block
2341 * @B bitmap
2342 * @c compress
2343 * @C conflicts with some other fs block
2344 * @D deleted
2345 * @d directory
2346 * @e entry
2347 * @E Entry '%Dn' in %p (%i)
2348 * @f filesystem
2349 * @F for @i %i (%Q) is
2350 * @g group
2351 * @h HTREE directory inode
2352 * @i inode
2353 * @I illegal
2354 * @j journal
2355 * @l lost+found
2356 * @L is a link
Mike Frysinger874af852006-03-08 07:03:27 +00002357 * @m multiply-claimed
2358 * @n invalid
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002359 * @o orphaned
2360 * @p problem in
2361 * @r root inode
2362 * @s should be
2363 * @S superblock
2364 * @u unattached
2365 * @v device
2366 * @z zero-length
2367 */
2368
2369
2370/*
2371 * This structure defines the abbreviations used by the text strings
2372 * below. The first character in the string is the index letter. An
2373 * abbreviation of the form '@<i>' is expanded by looking up the index
2374 * letter <i> in the table below.
2375 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002376static const char * const abbrevs[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002377 N_("aextended attribute"),
2378 N_("Aerror allocating"),
2379 N_("bblock"),
2380 N_("Bbitmap"),
2381 N_("ccompress"),
2382 N_("Cconflicts with some other fs @b"),
2383 N_("iinode"),
2384 N_("Iillegal"),
2385 N_("jjournal"),
2386 N_("Ddeleted"),
2387 N_("ddirectory"),
2388 N_("eentry"),
2389 N_("E@e '%Dn' in %p (%i)"),
2390 N_("ffilesystem"),
2391 N_("Ffor @i %i (%Q) is"),
2392 N_("ggroup"),
2393 N_("hHTREE @d @i"),
2394 N_("llost+found"),
2395 N_("Lis a link"),
Mike Frysinger874af852006-03-08 07:03:27 +00002396 N_("mmultiply-claimed"),
2397 N_("ninvalid"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002398 N_("oorphaned"),
2399 N_("pproblem in"),
2400 N_("rroot @i"),
2401 N_("sshould be"),
2402 N_("Ssuper@b"),
2403 N_("uunattached"),
2404 N_("vdevice"),
2405 N_("zzero-length"),
2406 "@@",
2407 0
2408 };
2409
2410/*
2411 * Give more user friendly names to the "special" inodes.
2412 */
2413#define num_special_inodes 11
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002414static const char * const special_inode_name[] =
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002415{
2416 N_("<The NULL inode>"), /* 0 */
2417 N_("<The bad blocks inode>"), /* 1 */
2418 "/", /* 2 */
2419 N_("<The ACL index inode>"), /* 3 */
2420 N_("<The ACL data inode>"), /* 4 */
2421 N_("<The boot loader inode>"), /* 5 */
2422 N_("<The undelete directory inode>"), /* 6 */
2423 N_("<The group descriptor inode>"), /* 7 */
2424 N_("<The journal inode>"), /* 8 */
2425 N_("<Reserved inode 9>"), /* 9 */
2426 N_("<Reserved inode 10>"), /* 10 */
2427};
2428
2429/*
2430 * This function does "safe" printing. It will convert non-printable
2431 * ASCII characters using '^' and M- notation.
2432 */
2433static void safe_print(const char *cp, int len)
2434{
2435 unsigned char ch;
2436
2437 if (len < 0)
2438 len = strlen(cp);
2439
2440 while (len--) {
2441 ch = *cp++;
2442 if (ch > 128) {
2443 fputs("M-", stdout);
2444 ch -= 128;
2445 }
2446 if ((ch < 32) || (ch == 0x7f)) {
2447 fputc('^', stdout);
2448 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
2449 }
2450 fputc(ch, stdout);
2451 }
2452}
2453
2454
2455/*
2456 * This function prints a pathname, using the ext2fs_get_pathname
2457 * function
2458 */
2459static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
2460{
2461 errcode_t retval;
2462 char *path;
2463
2464 if (!dir && (ino < num_special_inodes)) {
2465 fputs(_(special_inode_name[ino]), stdout);
2466 return;
2467 }
2468
2469 retval = ext2fs_get_pathname(fs, dir, ino, &path);
2470 if (retval)
2471 fputs("???", stdout);
2472 else {
2473 safe_print(path, -1);
2474 ext2fs_free_mem(&path);
2475 }
2476}
2477
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002478static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
2479 struct problem_context *pctx, int first);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002480/*
2481 * This function handles the '@' expansion. We allow recursive
2482 * expansion; an @ expression can contain further '@' and '%'
2483 * expressions.
2484 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002485static void expand_at_expression(e2fsck_t ctx, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002486 struct problem_context *pctx,
2487 int *first)
2488{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002489 const char * const *cpp;
2490 const char *str;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002491
2492 /* Search for the abbreviation */
2493 for (cpp = abbrevs; *cpp; cpp++) {
2494 if (ch == *cpp[0])
2495 break;
2496 }
2497 if (*cpp) {
2498 str = _(*cpp) + 1;
2499 if (*first && islower(*str)) {
2500 *first = 0;
2501 fputc(toupper(*str++), stdout);
2502 }
2503 print_e2fsck_message(ctx, str, pctx, *first);
2504 } else
2505 printf("@%c", ch);
2506}
2507
2508/*
2509 * This function expands '%IX' expressions
2510 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002511static void expand_inode_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002512 struct problem_context *ctx)
2513{
2514 struct ext2_inode *inode;
2515 struct ext2_inode_large *large_inode;
2516 char * time_str;
2517 time_t t;
2518 int do_gmt = -1;
2519
2520 if (!ctx || !ctx->inode)
2521 goto no_inode;
2522
2523 inode = ctx->inode;
2524 large_inode = (struct ext2_inode_large *) inode;
2525
2526 switch (ch) {
2527 case 's':
2528 if (LINUX_S_ISDIR(inode->i_mode))
2529 printf("%u", inode->i_size);
2530 else {
Rob Landley7a260f02006-06-19 03:20:03 +00002531 printf("%"PRIu64, (inode->i_size |
2532 ((uint64_t) inode->i_size_high << 32)));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002533 }
2534 break;
2535 case 'S':
2536 printf("%u", large_inode->i_extra_isize);
2537 break;
2538 case 'b':
2539 printf("%u", inode->i_blocks);
2540 break;
2541 case 'l':
2542 printf("%d", inode->i_links_count);
2543 break;
2544 case 'm':
2545 printf("0%o", inode->i_mode);
2546 break;
2547 case 'M':
2548 /* The diet libc doesn't respect the TZ environemnt variable */
2549 if (do_gmt == -1) {
2550 time_str = getenv("TZ");
2551 if (!time_str)
2552 time_str = "";
2553 do_gmt = !strcmp(time_str, "GMT");
2554 }
2555 t = inode->i_mtime;
2556 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
2557 printf("%.24s", time_str);
2558 break;
2559 case 'F':
2560 printf("%u", inode->i_faddr);
2561 break;
2562 case 'f':
2563 printf("%u", inode->i_file_acl);
2564 break;
2565 case 'd':
2566 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
2567 inode->i_dir_acl : 0));
2568 break;
2569 case 'u':
2570 printf("%d", (inode->i_uid |
2571 (inode->osd2.linux2.l_i_uid_high << 16)));
2572 break;
2573 case 'g':
2574 printf("%d", (inode->i_gid |
2575 (inode->osd2.linux2.l_i_gid_high << 16)));
2576 break;
2577 default:
2578 no_inode:
2579 printf("%%I%c", ch);
2580 break;
2581 }
2582}
2583
2584/*
2585 * This function expands '%dX' expressions
2586 */
Rob Landley7c94bed2006-05-03 21:58:45 +00002587static void expand_dirent_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002588 struct problem_context *ctx)
2589{
2590 struct ext2_dir_entry *dirent;
2591 int len;
2592
2593 if (!ctx || !ctx->dirent)
2594 goto no_dirent;
2595
2596 dirent = ctx->dirent;
2597
2598 switch (ch) {
2599 case 'i':
2600 printf("%u", dirent->inode);
2601 break;
2602 case 'n':
2603 len = dirent->name_len & 0xFF;
2604 if (len > EXT2_NAME_LEN)
2605 len = EXT2_NAME_LEN;
2606 if (len > dirent->rec_len)
2607 len = dirent->rec_len;
2608 safe_print(dirent->name, len);
2609 break;
2610 case 'r':
2611 printf("%u", dirent->rec_len);
2612 break;
2613 case 'l':
2614 printf("%u", dirent->name_len & 0xFF);
2615 break;
2616 case 't':
2617 printf("%u", dirent->name_len >> 8);
2618 break;
2619 default:
2620 no_dirent:
2621 printf("%%D%c", ch);
2622 break;
2623 }
2624}
2625
Rob Landley7c94bed2006-05-03 21:58:45 +00002626static void expand_percent_expression(ext2_filsys fs, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002627 struct problem_context *ctx)
2628{
2629 if (!ctx)
2630 goto no_context;
2631
2632 switch (ch) {
2633 case '%':
2634 fputc('%', stdout);
2635 break;
2636 case 'b':
2637 printf("%u", ctx->blk);
2638 break;
2639 case 'B':
Rob Landley7a260f02006-06-19 03:20:03 +00002640 printf("%"PRIi64, ctx->blkcount);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002641 break;
2642 case 'c':
2643 printf("%u", ctx->blk2);
2644 break;
2645 case 'd':
2646 printf("%u", ctx->dir);
2647 break;
2648 case 'g':
2649 printf("%d", ctx->group);
2650 break;
2651 case 'i':
2652 printf("%u", ctx->ino);
2653 break;
2654 case 'j':
2655 printf("%u", ctx->ino2);
2656 break;
2657 case 'm':
2658 printf("%s", error_message(ctx->errcode));
2659 break;
2660 case 'N':
Rob Landley7a260f02006-06-19 03:20:03 +00002661 printf("%"PRIi64, ctx->num);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002662 break;
2663 case 'p':
2664 print_pathname(fs, ctx->ino, 0);
2665 break;
2666 case 'P':
2667 print_pathname(fs, ctx->ino2,
2668 ctx->dirent ? ctx->dirent->inode : 0);
2669 break;
2670 case 'q':
2671 print_pathname(fs, ctx->dir, 0);
2672 break;
2673 case 'Q':
2674 print_pathname(fs, ctx->dir, ctx->ino);
2675 break;
2676 case 'S':
2677 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
2678 break;
2679 case 's':
2680 printf("%s", ctx->str ? ctx->str : "NULL");
2681 break;
2682 case 'X':
Rob Landley7a260f02006-06-19 03:20:03 +00002683 printf("0x%"PRIi64, ctx->num);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002684 break;
2685 default:
2686 no_context:
2687 printf("%%%c", ch);
2688 break;
2689 }
2690}
2691
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002692
2693static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002694 struct problem_context *pctx, int first)
2695{
2696 ext2_filsys fs = ctx->fs;
2697 const char * cp;
2698 int i;
2699
2700 e2fsck_clear_progbar(ctx);
2701 for (cp = msg; *cp; cp++) {
2702 if (cp[0] == '@') {
2703 cp++;
2704 expand_at_expression(ctx, *cp, pctx, &first);
2705 } else if (cp[0] == '%' && cp[1] == 'I') {
2706 cp += 2;
2707 expand_inode_expression(*cp, pctx);
2708 } else if (cp[0] == '%' && cp[1] == 'D') {
2709 cp += 2;
2710 expand_dirent_expression(*cp, pctx);
2711 } else if ((cp[0] == '%')) {
2712 cp++;
2713 expand_percent_expression(fs, *cp, pctx);
2714 } else {
2715 for (i=0; cp[i]; i++)
2716 if ((cp[i] == '@') || cp[i] == '%')
2717 break;
2718 printf("%.*s", i, cp);
2719 cp += i-1;
2720 }
2721 first = 0;
2722 }
2723}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002724
2725
2726/*
2727 * region.c --- code which manages allocations within a region.
2728 */
2729
2730struct region_el {
2731 region_addr_t start;
2732 region_addr_t end;
2733 struct region_el *next;
2734};
2735
2736struct region_struct {
2737 region_addr_t min;
2738 region_addr_t max;
2739 struct region_el *allocated;
2740};
2741
2742static region_t region_create(region_addr_t min, region_addr_t max)
2743{
2744 region_t region;
2745
2746 region = malloc(sizeof(struct region_struct));
2747 if (!region)
2748 return NULL;
2749 memset(region, 0, sizeof(struct region_struct));
2750 region->min = min;
2751 region->max = max;
2752 return region;
2753}
2754
2755static void region_free(region_t region)
2756{
2757 struct region_el *r, *next;
2758
2759 for (r = region->allocated; r; r = next) {
2760 next = r->next;
2761 free(r);
2762 }
2763 memset(region, 0, sizeof(struct region_struct));
2764 free(region);
2765}
2766
2767static int region_allocate(region_t region, region_addr_t start, int n)
2768{
2769 struct region_el *r, *new_region, *prev, *next;
2770 region_addr_t end;
2771
2772 end = start+n;
2773 if ((start < region->min) || (end > region->max))
2774 return -1;
2775 if (n == 0)
2776 return 1;
2777
2778 /*
2779 * Search through the linked list. If we find that it
2780 * conflicts witih something that's already allocated, return
2781 * 1; if we can find an existing region which we can grow, do
2782 * so. Otherwise, stop when we find the appropriate place
2783 * insert a new region element into the linked list.
2784 */
2785 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
2786 if (((start >= r->start) && (start < r->end)) ||
2787 ((end > r->start) && (end <= r->end)) ||
2788 ((start <= r->start) && (end >= r->end)))
2789 return 1;
2790 if (end == r->start) {
2791 r->start = start;
2792 return 0;
2793 }
2794 if (start == r->end) {
2795 if ((next = r->next)) {
2796 if (end > next->start)
2797 return 1;
2798 if (end == next->start) {
2799 r->end = next->end;
2800 r->next = next->next;
2801 free(next);
2802 return 0;
2803 }
2804 }
2805 r->end = end;
2806 return 0;
2807 }
2808 if (start < r->start)
2809 break;
2810 }
2811 /*
2812 * Insert a new region element structure into the linked list
2813 */
2814 new_region = malloc(sizeof(struct region_el));
2815 if (!new_region)
2816 return -1;
2817 new_region->start = start;
2818 new_region->end = start + n;
2819 new_region->next = r;
2820 if (prev)
2821 prev->next = new_region;
2822 else
2823 region->allocated = new_region;
2824 return 0;
2825}
2826
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002827/*
2828 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
2829 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002830 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
2831 * and applies the following tests to each inode:
2832 *
2833 * - The mode field of the inode must be legal.
2834 * - The size and block count fields of the inode are correct.
2835 * - A data block must not be used by another inode
2836 *
2837 * Pass 1 also gathers the collects the following information:
2838 *
2839 * - A bitmap of which inodes are in use. (inode_used_map)
2840 * - A bitmap of which inodes are directories. (inode_dir_map)
2841 * - A bitmap of which inodes are regular files. (inode_reg_map)
2842 * - A bitmap of which inodes have bad fields. (inode_bad_map)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002843 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
2844 * - A bitmap of which blocks are in use. (block_found_map)
2845 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
2846 * - The data blocks of the directory inodes. (dir_map)
2847 *
2848 * Pass 1 is designed to stash away enough information so that the
2849 * other passes should not need to read in the inode information
2850 * during the normal course of a filesystem check. (Althogh if an
2851 * inconsistency is detected, other passes may need to read in an
2852 * inode to fix it.)
2853 *
2854 * Note that pass 1B will be invoked if there are any duplicate blocks
2855 * found.
2856 */
2857
2858
2859static int process_block(ext2_filsys fs, blk_t *blocknr,
2860 e2_blkcnt_t blockcnt, blk_t ref_blk,
2861 int ref_offset, void *priv_data);
2862static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
2863 e2_blkcnt_t blockcnt, blk_t ref_blk,
2864 int ref_offset, void *priv_data);
2865static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
2866 char *block_buf);
2867static void mark_table_blocks(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002868static void alloc_imagic_map(e2fsck_t ctx);
2869static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
2870static void handle_fs_bad_blocks(e2fsck_t ctx);
2871static void process_inodes(e2fsck_t ctx, char *block_buf);
Rob Landley7c94bed2006-05-03 21:58:45 +00002872static int process_inode_cmp(const void *a, const void *b);
Rob Landley206f7572006-05-19 22:42:23 +00002873static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002874 dgrp_t group, void * priv_data);
2875static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
2876 char *block_buf, int adjust_sign);
2877/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
2878
2879static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
2880 struct ext2_inode * inode, int bufsize,
2881 const char *proc);
2882
2883struct process_block_struct_1 {
2884 ext2_ino_t ino;
2885 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
2886 fragmented:1, compressed:1, bbcheck:1;
2887 blk_t num_blocks;
2888 blk_t max_blocks;
2889 e2_blkcnt_t last_block;
2890 int num_illegal_blocks;
2891 blk_t previous_block;
2892 struct ext2_inode *inode;
2893 struct problem_context *pctx;
2894 ext2fs_block_bitmap fs_meta_blocks;
2895 e2fsck_t ctx;
2896};
2897
2898struct process_inode_block {
2899 ext2_ino_t ino;
2900 struct ext2_inode inode;
2901};
2902
2903struct scan_callback_struct {
2904 e2fsck_t ctx;
2905 char *block_buf;
2906};
2907
2908/*
2909 * For the inodes to process list.
2910 */
2911static struct process_inode_block *inodes_to_process;
2912static int process_inode_count;
2913
2914static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
2915 EXT2_MIN_BLOCK_LOG_SIZE + 1];
2916
2917/*
2918 * Free all memory allocated by pass1 in preparation for restarting
2919 * things.
2920 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002921static void unwind_pass1(void)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002922{
2923 ext2fs_free_mem(&inodes_to_process);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002924}
2925
2926/*
2927 * Check to make sure a device inode is real. Returns 1 if the device
2928 * checks out, 0 if not.
2929 *
2930 * Note: this routine is now also used to check FIFO's and Sockets,
2931 * since they have the same requirement; the i_block fields should be
2932 * zero.
2933 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002934static int
2935e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002936{
2937 int i;
2938
2939 /*
2940 * If i_blocks is non-zero, or the index flag is set, then
2941 * this is a bogus device/fifo/socket
2942 */
2943 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
2944 (inode->i_flags & EXT2_INDEX_FL))
2945 return 0;
2946
2947 /*
2948 * We should be able to do the test below all the time, but
2949 * because the kernel doesn't forcibly clear the device
2950 * inode's additional i_block fields, there are some rare
2951 * occasions when a legitimate device inode will have non-zero
2952 * additional i_block fields. So for now, we only complain
2953 * when the immutable flag is set, which should never happen
2954 * for devices. (And that's when the problem is caused, since
2955 * you can't set or clear immutable flags for devices.) Once
2956 * the kernel has been fixed we can change this...
2957 */
2958 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
2959 for (i=4; i < EXT2_N_BLOCKS; i++)
2960 if (inode->i_block[i])
2961 return 0;
2962 }
2963 return 1;
2964}
2965
2966/*
2967 * Check to make sure a symlink inode is real. Returns 1 if the symlink
2968 * checks out, 0 if not.
2969 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002970static int
2971e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002972{
2973 unsigned int len;
2974 int i;
2975 blk_t blocks;
2976
2977 if ((inode->i_size_high || inode->i_size == 0) ||
2978 (inode->i_flags & EXT2_INDEX_FL))
2979 return 0;
2980
2981 blocks = ext2fs_inode_data_blocks(fs, inode);
2982 if (blocks) {
2983 if ((inode->i_size >= fs->blocksize) ||
2984 (blocks != fs->blocksize >> 9) ||
2985 (inode->i_block[0] < fs->super->s_first_data_block) ||
2986 (inode->i_block[0] >= fs->super->s_blocks_count))
2987 return 0;
2988
2989 for (i = 1; i < EXT2_N_BLOCKS; i++)
2990 if (inode->i_block[i])
2991 return 0;
2992
2993 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
2994 return 0;
2995
2996 len = strnlen(buf, fs->blocksize);
2997 if (len == fs->blocksize)
2998 return 0;
2999 } else {
3000 if (inode->i_size >= sizeof(inode->i_block))
3001 return 0;
3002
3003 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
3004 if (len == sizeof(inode->i_block))
3005 return 0;
3006 }
3007 if (len != inode->i_size)
3008 return 0;
3009 return 1;
3010}
3011
3012/*
3013 * If the immutable (or append-only) flag is set on the inode, offer
3014 * to clear it.
3015 */
3016#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
3017static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
3018{
3019 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
3020 return;
3021
3022 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
3023 return;
3024
3025 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
3026 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3027}
3028
3029/*
3030 * If device, fifo or socket, check size is zero -- if not offer to
3031 * clear it
3032 */
3033static void check_size(e2fsck_t ctx, struct problem_context *pctx)
3034{
3035 struct ext2_inode *inode = pctx->inode;
3036
3037 if ((inode->i_size == 0) && (inode->i_size_high == 0))
3038 return;
3039
3040 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
3041 return;
3042
3043 inode->i_size = 0;
3044 inode->i_size_high = 0;
3045 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3046}
3047
3048static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
3049{
3050 struct ext2_super_block *sb = ctx->fs->super;
3051 struct ext2_inode_large *inode;
3052 struct ext2_ext_attr_entry *entry;
3053 char *start, *end;
3054 int storage_size, remain, offs;
3055 int problem = 0;
3056
3057 inode = (struct ext2_inode_large *) pctx->inode;
3058 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
3059 inode->i_extra_isize;
3060 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3061 inode->i_extra_isize + sizeof(__u32);
3062 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
3063 entry = (struct ext2_ext_attr_entry *) start;
3064
3065 /* scan all entry's headers first */
3066
3067 /* take finish entry 0UL into account */
3068 remain = storage_size - sizeof(__u32);
3069 offs = end - start;
3070
3071 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
3072
3073 /* header eats this space */
3074 remain -= sizeof(struct ext2_ext_attr_entry);
3075
3076 /* is attribute name valid? */
3077 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
3078 pctx->num = entry->e_name_len;
3079 problem = PR_1_ATTR_NAME_LEN;
3080 goto fix;
3081 }
3082
3083 /* attribute len eats this space */
3084 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
3085
3086 /* check value size */
3087 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
3088 pctx->num = entry->e_value_size;
3089 problem = PR_1_ATTR_VALUE_SIZE;
3090 goto fix;
3091 }
3092
3093 /* check value placement */
3094 if (entry->e_value_offs +
3095 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
3096 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
3097 pctx->num = entry->e_value_offs;
3098 problem = PR_1_ATTR_VALUE_OFFSET;
3099 goto fix;
3100 }
3101
3102 /* e_value_block must be 0 in inode's ea */
3103 if (entry->e_value_block != 0) {
3104 pctx->num = entry->e_value_block;
3105 problem = PR_1_ATTR_VALUE_BLOCK;
3106 goto fix;
3107 }
3108
3109 /* e_hash must be 0 in inode's ea */
3110 if (entry->e_hash != 0) {
3111 pctx->num = entry->e_hash;
3112 problem = PR_1_ATTR_HASH;
3113 goto fix;
3114 }
3115
3116 remain -= entry->e_value_size;
3117 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
3118
3119 entry = EXT2_EXT_ATTR_NEXT(entry);
3120 }
3121fix:
3122 /*
3123 * it seems like a corruption. it's very unlikely we could repair
3124 * EA(s) in automatic fashion -bzzz
3125 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003126 if (problem == 0 || !fix_problem(ctx, problem, pctx))
3127 return;
3128
3129 /* simple remove all possible EA(s) */
3130 *((__u32 *)start) = 0UL;
3131 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
3132 EXT2_INODE_SIZE(sb), "pass1");
3133}
3134
3135static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
3136{
3137 struct ext2_super_block *sb = ctx->fs->super;
3138 struct ext2_inode_large *inode;
3139 __u32 *eamagic;
3140 int min, max;
3141
3142 inode = (struct ext2_inode_large *) pctx->inode;
3143 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
3144 /* this isn't large inode. so, nothing to check */
3145 return;
3146 }
3147
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003148 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
3149 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
3150 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
3151 /*
3152 * For now we will allow i_extra_isize to be 0, but really
3153 * implementations should never allow i_extra_isize to be 0
3154 */
3155 if (inode->i_extra_isize &&
3156 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
3157 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
3158 return;
3159 inode->i_extra_isize = min;
3160 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
3161 EXT2_INODE_SIZE(sb), "pass1");
3162 return;
3163 }
3164
3165 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3166 inode->i_extra_isize);
3167 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
3168 /* it seems inode has an extended attribute(s) in body */
3169 check_ea_in_inode(ctx, pctx);
3170 }
3171}
3172
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003173static void e2fsck_pass1(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003174{
3175 int i;
3176 __u64 max_sizes;
3177 ext2_filsys fs = ctx->fs;
3178 ext2_ino_t ino;
3179 struct ext2_inode *inode;
3180 ext2_inode_scan scan;
3181 char *block_buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003182 unsigned char frag, fsize;
3183 struct problem_context pctx;
3184 struct scan_callback_struct scan_struct;
3185 struct ext2_super_block *sb = ctx->fs->super;
3186 int imagic_fs;
3187 int busted_fs_time = 0;
3188 int inode_size;
3189
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003190 clear_problem_context(&pctx);
3191
3192 if (!(ctx->options & E2F_OPT_PREEN))
3193 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
3194
3195 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
3196 !(ctx->options & E2F_OPT_NO)) {
3197 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
3198 ctx->dirs_to_hash = 0;
3199 }
3200
Rob Landley3e72c592006-04-06 22:49:04 +00003201 /* Pass 1 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003202
3203#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
3204
3205 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
3206 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
3207 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
3208 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
3209 max_sizes = (max_sizes * (1UL << i)) - 1;
3210 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
3211 }
3212#undef EXT2_BPP
3213
3214 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
3215
3216 /*
3217 * Allocate bitmaps structures
3218 */
3219 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
3220 &ctx->inode_used_map);
3221 if (pctx.errcode) {
3222 pctx.num = 1;
3223 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3224 ctx->flags |= E2F_FLAG_ABORT;
3225 return;
3226 }
3227 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3228 _("directory inode map"), &ctx->inode_dir_map);
3229 if (pctx.errcode) {
3230 pctx.num = 2;
3231 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3232 ctx->flags |= E2F_FLAG_ABORT;
3233 return;
3234 }
3235 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3236 _("regular file inode map"), &ctx->inode_reg_map);
3237 if (pctx.errcode) {
3238 pctx.num = 6;
3239 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3240 ctx->flags |= E2F_FLAG_ABORT;
3241 return;
3242 }
3243 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
3244 &ctx->block_found_map);
3245 if (pctx.errcode) {
3246 pctx.num = 1;
3247 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3248 ctx->flags |= E2F_FLAG_ABORT;
3249 return;
3250 }
3251 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
3252 &ctx->inode_link_info);
3253 if (pctx.errcode) {
3254 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
3255 ctx->flags |= E2F_FLAG_ABORT;
3256 return;
3257 }
3258 inode_size = EXT2_INODE_SIZE(fs->super);
3259 inode = (struct ext2_inode *)
3260 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
3261
3262 inodes_to_process = (struct process_inode_block *)
3263 e2fsck_allocate_memory(ctx,
3264 (ctx->process_inode_size *
3265 sizeof(struct process_inode_block)),
3266 "array of inodes to process");
3267 process_inode_count = 0;
3268
3269 pctx.errcode = ext2fs_init_dblist(fs, 0);
3270 if (pctx.errcode) {
3271 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
3272 ctx->flags |= E2F_FLAG_ABORT;
3273 return;
3274 }
3275
3276 /*
3277 * If the last orphan field is set, clear it, since the pass1
3278 * processing will automatically find and clear the orphans.
3279 * In the future, we may want to try using the last_orphan
3280 * linked list ourselves, but for now, we clear it so that the
3281 * ext3 mount code won't get confused.
3282 */
3283 if (!(ctx->options & E2F_OPT_READONLY)) {
3284 if (fs->super->s_last_orphan) {
3285 fs->super->s_last_orphan = 0;
3286 ext2fs_mark_super_dirty(fs);
3287 }
3288 }
3289
3290 mark_table_blocks(ctx);
3291 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
3292 "block interate buffer");
3293 e2fsck_use_inode_shortcuts(ctx, 1);
3294 ehandler_operation(_("doing inode scan"));
3295 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
3296 &scan);
3297 if (pctx.errcode) {
3298 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3299 ctx->flags |= E2F_FLAG_ABORT;
3300 return;
3301 }
3302 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
3303 ctx->stashed_inode = inode;
3304 scan_struct.ctx = ctx;
3305 scan_struct.block_buf = block_buf;
3306 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
3307 if (ctx->progress)
3308 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
3309 return;
Mike Frysinger874af852006-03-08 07:03:27 +00003310 if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
3311 (fs->super->s_mtime < fs->super->s_inodes_count))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003312 busted_fs_time = 1;
3313
3314 while (1) {
3315 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
3316 inode, inode_size);
3317 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3318 return;
3319 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003320 continue;
3321 }
3322 if (pctx.errcode) {
3323 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3324 ctx->flags |= E2F_FLAG_ABORT;
3325 return;
3326 }
3327 if (!ino)
3328 break;
3329 pctx.ino = ino;
3330 pctx.inode = inode;
3331 ctx->stashed_ino = ino;
3332 if (inode->i_links_count) {
3333 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
3334 ino, inode->i_links_count);
3335 if (pctx.errcode) {
3336 pctx.num = inode->i_links_count;
3337 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
3338 ctx->flags |= E2F_FLAG_ABORT;
3339 return;
3340 }
3341 }
3342 if (ino == EXT2_BAD_INO) {
3343 struct process_block_struct_1 pb;
3344
3345 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
3346 &pb.fs_meta_blocks);
3347 if (pctx.errcode) {
3348 pctx.num = 4;
3349 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3350 ctx->flags |= E2F_FLAG_ABORT;
3351 return;
3352 }
3353 pb.ino = EXT2_BAD_INO;
3354 pb.num_blocks = pb.last_block = 0;
3355 pb.num_illegal_blocks = 0;
3356 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
3357 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
3358 pb.inode = inode;
3359 pb.pctx = &pctx;
3360 pb.ctx = ctx;
3361 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
3362 block_buf, process_bad_block, &pb);
3363 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
3364 if (pctx.errcode) {
3365 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
3366 ctx->flags |= E2F_FLAG_ABORT;
3367 return;
3368 }
3369 if (pb.bbcheck)
3370 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
3371 ctx->flags |= E2F_FLAG_ABORT;
3372 return;
3373 }
3374 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3375 clear_problem_context(&pctx);
3376 continue;
3377 } else if (ino == EXT2_ROOT_INO) {
3378 /*
3379 * Make sure the root inode is a directory; if
3380 * not, offer to clear it. It will be
3381 * regnerated in pass #3.
3382 */
3383 if (!LINUX_S_ISDIR(inode->i_mode)) {
3384 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
3385 inode->i_dtime = time(0);
3386 inode->i_links_count = 0;
3387 ext2fs_icount_store(ctx->inode_link_info,
3388 ino, 0);
3389 e2fsck_write_inode(ctx, ino, inode,
3390 "pass1");
3391 }
3392
3393 }
3394 /*
3395 * If dtime is set, offer to clear it. mke2fs
3396 * version 0.2b created filesystems with the
3397 * dtime field set for the root and lost+found
3398 * directories. We won't worry about
3399 * /lost+found, since that can be regenerated
3400 * easily. But we will fix the root directory
3401 * as a special case.
3402 */
3403 if (inode->i_dtime && inode->i_links_count) {
3404 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
3405 inode->i_dtime = 0;
3406 e2fsck_write_inode(ctx, ino, inode,
3407 "pass1");
3408 }
3409 }
3410 } else if (ino == EXT2_JOURNAL_INO) {
3411 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3412 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
3413 if (!LINUX_S_ISREG(inode->i_mode) &&
3414 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
3415 &pctx)) {
3416 inode->i_mode = LINUX_S_IFREG;
3417 e2fsck_write_inode(ctx, ino, inode,
3418 "pass1");
3419 }
3420 check_blocks(ctx, &pctx, block_buf);
3421 continue;
3422 }
3423 if ((inode->i_links_count || inode->i_blocks ||
3424 inode->i_blocks || inode->i_block[0]) &&
3425 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
3426 &pctx)) {
3427 memset(inode, 0, inode_size);
3428 ext2fs_icount_store(ctx->inode_link_info,
3429 ino, 0);
3430 e2fsck_write_inode_full(ctx, ino, inode,
3431 inode_size, "pass1");
3432 }
3433 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
3434 int problem = 0;
3435
3436 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3437 if (ino == EXT2_BOOT_LOADER_INO) {
3438 if (LINUX_S_ISDIR(inode->i_mode))
3439 problem = PR_1_RESERVED_BAD_MODE;
3440 } else if (ino == EXT2_RESIZE_INO) {
3441 if (inode->i_mode &&
3442 !LINUX_S_ISREG(inode->i_mode))
3443 problem = PR_1_RESERVED_BAD_MODE;
3444 } else {
3445 if (inode->i_mode != 0)
3446 problem = PR_1_RESERVED_BAD_MODE;
3447 }
3448 if (problem) {
3449 if (fix_problem(ctx, problem, &pctx)) {
3450 inode->i_mode = 0;
3451 e2fsck_write_inode(ctx, ino, inode,
3452 "pass1");
3453 }
3454 }
3455 check_blocks(ctx, &pctx, block_buf);
3456 continue;
3457 }
3458 /*
3459 * Check for inodes who might have been part of the
3460 * orphaned list linked list. They should have gotten
3461 * dealt with by now, unless the list had somehow been
3462 * corrupted.
3463 *
3464 * FIXME: In the future, inodes which are still in use
3465 * (and which are therefore) pending truncation should
3466 * be handled specially. Right now we just clear the
3467 * dtime field, and the normal e2fsck handling of
3468 * inodes where i_size and the inode blocks are
3469 * inconsistent is to fix i_size, instead of releasing
3470 * the extra blocks. This won't catch the inodes that
3471 * was at the end of the orphan list, but it's better
3472 * than nothing. The right answer is that there
3473 * shouldn't be any bugs in the orphan list handling. :-)
3474 */
3475 if (inode->i_dtime && !busted_fs_time &&
3476 inode->i_dtime < ctx->fs->super->s_inodes_count) {
3477 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
3478 inode->i_dtime = inode->i_links_count ?
3479 0 : time(0);
3480 e2fsck_write_inode(ctx, ino, inode,
3481 "pass1");
3482 }
3483 }
3484
3485 /*
3486 * This code assumes that deleted inodes have
3487 * i_links_count set to 0.
3488 */
3489 if (!inode->i_links_count) {
3490 if (!inode->i_dtime && inode->i_mode) {
3491 if (fix_problem(ctx,
3492 PR_1_ZERO_DTIME, &pctx)) {
3493 inode->i_dtime = time(0);
3494 e2fsck_write_inode(ctx, ino, inode,
3495 "pass1");
3496 }
3497 }
3498 continue;
3499 }
3500 /*
3501 * n.b. 0.3c ext2fs code didn't clear i_links_count for
3502 * deleted files. Oops.
3503 *
3504 * Since all new ext2 implementations get this right,
3505 * we now assume that the case of non-zero
3506 * i_links_count and non-zero dtime means that we
3507 * should keep the file, not delete it.
3508 *
3509 */
3510 if (inode->i_dtime) {
3511 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
3512 inode->i_dtime = 0;
3513 e2fsck_write_inode(ctx, ino, inode, "pass1");
3514 }
3515 }
3516
3517 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3518 switch (fs->super->s_creator_os) {
3519 case EXT2_OS_LINUX:
3520 frag = inode->osd2.linux2.l_i_frag;
3521 fsize = inode->osd2.linux2.l_i_fsize;
3522 break;
3523 case EXT2_OS_HURD:
3524 frag = inode->osd2.hurd2.h_i_frag;
3525 fsize = inode->osd2.hurd2.h_i_fsize;
3526 break;
3527 case EXT2_OS_MASIX:
3528 frag = inode->osd2.masix2.m_i_frag;
3529 fsize = inode->osd2.masix2.m_i_fsize;
3530 break;
3531 default:
3532 frag = fsize = 0;
3533 }
3534
3535 if (inode->i_faddr || frag || fsize ||
3536 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
3537 mark_inode_bad(ctx, ino);
3538 if (inode->i_flags & EXT2_IMAGIC_FL) {
3539 if (imagic_fs) {
3540 if (!ctx->inode_imagic_map)
3541 alloc_imagic_map(ctx);
3542 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
3543 ino);
3544 } else {
3545 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
3546 inode->i_flags &= ~EXT2_IMAGIC_FL;
3547 e2fsck_write_inode(ctx, ino,
3548 inode, "pass1");
3549 }
3550 }
3551 }
3552
3553 check_inode_extra_space(ctx, &pctx);
3554
3555 if (LINUX_S_ISDIR(inode->i_mode)) {
3556 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
3557 e2fsck_add_dir_info(ctx, ino, 0);
3558 ctx->fs_directory_count++;
3559 } else if (LINUX_S_ISREG (inode->i_mode)) {
3560 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
3561 ctx->fs_regular_count++;
3562 } else if (LINUX_S_ISCHR (inode->i_mode) &&
3563 e2fsck_pass1_check_device_inode(fs, inode)) {
3564 check_immutable(ctx, &pctx);
3565 check_size(ctx, &pctx);
3566 ctx->fs_chardev_count++;
3567 } else if (LINUX_S_ISBLK (inode->i_mode) &&
3568 e2fsck_pass1_check_device_inode(fs, inode)) {
3569 check_immutable(ctx, &pctx);
3570 check_size(ctx, &pctx);
3571 ctx->fs_blockdev_count++;
3572 } else if (LINUX_S_ISLNK (inode->i_mode) &&
3573 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
3574 check_immutable(ctx, &pctx);
3575 ctx->fs_symlinks_count++;
3576 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
3577 ctx->fs_fast_symlinks_count++;
3578 check_blocks(ctx, &pctx, block_buf);
3579 continue;
3580 }
3581 }
3582 else if (LINUX_S_ISFIFO (inode->i_mode) &&
3583 e2fsck_pass1_check_device_inode(fs, inode)) {
3584 check_immutable(ctx, &pctx);
3585 check_size(ctx, &pctx);
3586 ctx->fs_fifo_count++;
3587 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
3588 e2fsck_pass1_check_device_inode(fs, inode)) {
3589 check_immutable(ctx, &pctx);
3590 check_size(ctx, &pctx);
3591 ctx->fs_sockets_count++;
3592 } else
3593 mark_inode_bad(ctx, ino);
3594 if (inode->i_block[EXT2_IND_BLOCK])
3595 ctx->fs_ind_count++;
3596 if (inode->i_block[EXT2_DIND_BLOCK])
3597 ctx->fs_dind_count++;
3598 if (inode->i_block[EXT2_TIND_BLOCK])
3599 ctx->fs_tind_count++;
3600 if (inode->i_block[EXT2_IND_BLOCK] ||
3601 inode->i_block[EXT2_DIND_BLOCK] ||
3602 inode->i_block[EXT2_TIND_BLOCK] ||
3603 inode->i_file_acl) {
3604 inodes_to_process[process_inode_count].ino = ino;
3605 inodes_to_process[process_inode_count].inode = *inode;
3606 process_inode_count++;
3607 } else
3608 check_blocks(ctx, &pctx, block_buf);
3609
3610 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3611 return;
3612
3613 if (process_inode_count >= ctx->process_inode_size) {
3614 process_inodes(ctx, block_buf);
3615
3616 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3617 return;
3618 }
3619 }
3620 process_inodes(ctx, block_buf);
3621 ext2fs_close_inode_scan(scan);
3622 ehandler_operation(0);
3623
3624 /*
3625 * If any extended attribute blocks' reference counts need to
3626 * be adjusted, either up (ctx->refcount_extra), or down
3627 * (ctx->refcount), then fix them.
3628 */
3629 if (ctx->refcount) {
3630 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
3631 ea_refcount_free(ctx->refcount);
3632 ctx->refcount = 0;
3633 }
3634 if (ctx->refcount_extra) {
3635 adjust_extattr_refcount(ctx, ctx->refcount_extra,
3636 block_buf, +1);
3637 ea_refcount_free(ctx->refcount_extra);
3638 ctx->refcount_extra = 0;
3639 }
3640
3641 if (ctx->invalid_bitmaps)
3642 handle_fs_bad_blocks(ctx);
3643
3644 /* We don't need the block_ea_map any more */
Rob Landleye7c43b62006-03-01 16:39:45 +00003645 ext2fs_free_block_bitmap(ctx->block_ea_map);
3646 ctx->block_ea_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003647
3648 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
3649 ext2fs_block_bitmap save_bmap;
3650
3651 save_bmap = fs->block_map;
3652 fs->block_map = ctx->block_found_map;
3653 clear_problem_context(&pctx);
3654 pctx.errcode = ext2fs_create_resize_inode(fs);
3655 if (pctx.errcode) {
3656 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
3657 /* Should never get here */
3658 ctx->flags |= E2F_FLAG_ABORT;
3659 return;
3660 }
Mike Frysinger874af852006-03-08 07:03:27 +00003661 e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
3662 "recreate inode");
3663 inode->i_mtime = time(0);
3664 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
3665 "recreate inode");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003666 fs->block_map = save_bmap;
3667 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
3668 }
3669
3670 if (ctx->flags & E2F_FLAG_RESTART) {
3671 /*
3672 * Only the master copy of the superblock and block
3673 * group descriptors are going to be written during a
3674 * restart, so set the superblock to be used to be the
3675 * master superblock.
3676 */
3677 ctx->use_superblock = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003678 unwind_pass1();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003679 goto endit;
3680 }
3681
3682 if (ctx->block_dup_map) {
3683 if (ctx->options & E2F_OPT_PREEN) {
3684 clear_problem_context(&pctx);
3685 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
3686 }
3687 e2fsck_pass1_dupblocks(ctx, block_buf);
3688 }
3689 ext2fs_free_mem(&inodes_to_process);
3690endit:
3691 e2fsck_use_inode_shortcuts(ctx, 0);
3692
3693 ext2fs_free_mem(&block_buf);
3694 ext2fs_free_mem(&inode);
3695
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003696}
3697
3698/*
3699 * When the inode_scan routines call this callback at the end of the
3700 * glock group, call process_inodes.
3701 */
3702static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003703 dgrp_t group, void * priv_data)
3704{
3705 struct scan_callback_struct *scan_struct;
3706 e2fsck_t ctx;
3707
3708 scan_struct = (struct scan_callback_struct *) priv_data;
3709 ctx = scan_struct->ctx;
3710
3711 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
3712
3713 if (ctx->progress)
3714 if ((ctx->progress)(ctx, 1, group+1,
3715 ctx->fs->group_desc_count))
3716 return EXT2_ET_CANCEL_REQUESTED;
3717
3718 return 0;
3719}
3720
3721/*
3722 * Process the inodes in the "inodes to process" list.
3723 */
3724static void process_inodes(e2fsck_t ctx, char *block_buf)
3725{
3726 int i;
3727 struct ext2_inode *old_stashed_inode;
3728 ext2_ino_t old_stashed_ino;
3729 const char *old_operation;
3730 char buf[80];
3731 struct problem_context pctx;
3732
Rob Landley3e72c592006-04-06 22:49:04 +00003733 /* begin process_inodes */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003734 if (process_inode_count == 0)
3735 return;
3736 old_operation = ehandler_operation(0);
3737 old_stashed_inode = ctx->stashed_inode;
3738 old_stashed_ino = ctx->stashed_ino;
3739 qsort(inodes_to_process, process_inode_count,
3740 sizeof(struct process_inode_block), process_inode_cmp);
3741 clear_problem_context(&pctx);
3742 for (i=0; i < process_inode_count; i++) {
3743 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
3744 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003745 sprintf(buf, _("reading indirect blocks of inode %u"),
3746 pctx.ino);
3747 ehandler_operation(buf);
3748 check_blocks(ctx, &pctx, block_buf);
3749 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3750 break;
3751 }
3752 ctx->stashed_inode = old_stashed_inode;
3753 ctx->stashed_ino = old_stashed_ino;
3754 process_inode_count = 0;
Rob Landley3e72c592006-04-06 22:49:04 +00003755 /* end process inodes */
3756
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003757 ehandler_operation(old_operation);
3758}
3759
Rob Landley7c94bed2006-05-03 21:58:45 +00003760static int process_inode_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003761{
3762 const struct process_inode_block *ib_a =
3763 (const struct process_inode_block *) a;
3764 const struct process_inode_block *ib_b =
3765 (const struct process_inode_block *) b;
3766 int ret;
3767
3768 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
3769 ib_b->inode.i_block[EXT2_IND_BLOCK]);
3770 if (ret == 0)
3771 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
3772 return ret;
3773}
3774
3775/*
3776 * Mark an inode as being bad in some what
3777 */
3778static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
3779{
3780 struct problem_context pctx;
3781
3782 if (!ctx->inode_bad_map) {
3783 clear_problem_context(&pctx);
3784
3785 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3786 _("bad inode map"), &ctx->inode_bad_map);
3787 if (pctx.errcode) {
3788 pctx.num = 3;
3789 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3790 /* Should never get here */
3791 ctx->flags |= E2F_FLAG_ABORT;
3792 return;
3793 }
3794 }
3795 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
3796}
3797
3798
3799/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003800 * This procedure will allocate the inode imagic table
3801 */
3802static void alloc_imagic_map(e2fsck_t ctx)
3803{
3804 struct problem_context pctx;
3805
3806 clear_problem_context(&pctx);
3807 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3808 _("imagic inode map"),
3809 &ctx->inode_imagic_map);
3810 if (pctx.errcode) {
3811 pctx.num = 5;
3812 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3813 /* Should never get here */
3814 ctx->flags |= E2F_FLAG_ABORT;
3815 return;
3816 }
3817}
3818
3819/*
3820 * Marks a block as in use, setting the dup_map if it's been set
3821 * already. Called by process_block and process_bad_block.
3822 *
3823 * WARNING: Assumes checks have already been done to make sure block
3824 * is valid. This is true in both process_block and process_bad_block.
3825 */
Rob Landley7c94bed2006-05-03 21:58:45 +00003826static void mark_block_used(e2fsck_t ctx, blk_t block)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003827{
3828 struct problem_context pctx;
3829
3830 clear_problem_context(&pctx);
3831
3832 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
3833 if (!ctx->block_dup_map) {
3834 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
3835 _("multiply claimed block map"),
3836 &ctx->block_dup_map);
3837 if (pctx.errcode) {
3838 pctx.num = 3;
3839 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
3840 &pctx);
3841 /* Should never get here */
3842 ctx->flags |= E2F_FLAG_ABORT;
3843 return;
3844 }
3845 }
3846 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
3847 } else {
3848 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
3849 }
3850}
3851
3852/*
3853 * Adjust the extended attribute block's reference counts at the end
3854 * of pass 1, either by subtracting out references for EA blocks that
3855 * are still referenced in ctx->refcount, or by adding references for
3856 * EA blocks that had extra references as accounted for in
3857 * ctx->refcount_extra.
3858 */
3859static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3860 char *block_buf, int adjust_sign)
3861{
3862 struct ext2_ext_attr_header *header;
3863 struct problem_context pctx;
3864 ext2_filsys fs = ctx->fs;
3865 blk_t blk;
3866 __u32 should_be;
3867 int count;
3868
3869 clear_problem_context(&pctx);
3870
3871 ea_refcount_intr_begin(refcount);
3872 while (1) {
3873 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
3874 break;
3875 pctx.blk = blk;
3876 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
3877 if (pctx.errcode) {
3878 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
3879 return;
3880 }
3881 header = (struct ext2_ext_attr_header *) block_buf;
3882 pctx.blkcount = header->h_refcount;
3883 should_be = header->h_refcount + adjust_sign * count;
3884 pctx.num = should_be;
3885 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
3886 header->h_refcount = should_be;
3887 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
3888 block_buf);
3889 if (pctx.errcode) {
3890 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
3891 continue;
3892 }
3893 }
3894 }
3895}
3896
3897/*
3898 * Handle processing the extended attribute blocks
3899 */
3900static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
3901 char *block_buf)
3902{
3903 ext2_filsys fs = ctx->fs;
3904 ext2_ino_t ino = pctx->ino;
3905 struct ext2_inode *inode = pctx->inode;
3906 blk_t blk;
3907 char * end;
3908 struct ext2_ext_attr_header *header;
3909 struct ext2_ext_attr_entry *entry;
3910 int count;
3911 region_t region;
3912
3913 blk = inode->i_file_acl;
3914 if (blk == 0)
3915 return 0;
3916
3917 /*
3918 * If the Extended attribute flag isn't set, then a non-zero
3919 * file acl means that the inode is corrupted.
3920 *
3921 * Or if the extended attribute block is an invalid block,
3922 * then the inode is also corrupted.
3923 */
3924 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
3925 (blk < fs->super->s_first_data_block) ||
3926 (blk >= fs->super->s_blocks_count)) {
3927 mark_inode_bad(ctx, ino);
3928 return 0;
3929 }
3930
3931 /* If ea bitmap hasn't been allocated, create it */
3932 if (!ctx->block_ea_map) {
3933 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
3934 _("ext attr block map"),
3935 &ctx->block_ea_map);
3936 if (pctx->errcode) {
3937 pctx->num = 2;
3938 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
3939 ctx->flags |= E2F_FLAG_ABORT;
3940 return 0;
3941 }
3942 }
3943
3944 /* Create the EA refcount structure if necessary */
3945 if (!ctx->refcount) {
3946 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
3947 if (pctx->errcode) {
3948 pctx->num = 1;
3949 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3950 ctx->flags |= E2F_FLAG_ABORT;
3951 return 0;
3952 }
3953 }
3954
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003955 /* Have we seen this EA block before? */
3956 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
3957 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
3958 return 1;
3959 /* Ooops, this EA was referenced more than it stated */
3960 if (!ctx->refcount_extra) {
3961 pctx->errcode = ea_refcount_create(0,
3962 &ctx->refcount_extra);
3963 if (pctx->errcode) {
3964 pctx->num = 2;
3965 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3966 ctx->flags |= E2F_FLAG_ABORT;
3967 return 0;
3968 }
3969 }
3970 ea_refcount_increment(ctx->refcount_extra, blk, 0);
3971 return 1;
3972 }
3973
3974 /*
3975 * OK, we haven't seen this EA block yet. So we need to
3976 * validate it
3977 */
3978 pctx->blk = blk;
3979 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
3980 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
3981 goto clear_extattr;
3982 header = (struct ext2_ext_attr_header *) block_buf;
3983 pctx->blk = inode->i_file_acl;
3984 if (((ctx->ext_attr_ver == 1) &&
3985 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
3986 ((ctx->ext_attr_ver == 2) &&
3987 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
3988 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
3989 goto clear_extattr;
3990 }
3991
3992 if (header->h_blocks != 1) {
3993 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
3994 goto clear_extattr;
3995 }
3996
3997 region = region_create(0, fs->blocksize);
3998 if (!region) {
3999 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
4000 ctx->flags |= E2F_FLAG_ABORT;
4001 return 0;
4002 }
4003 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
4004 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4005 goto clear_extattr;
4006 }
4007
4008 entry = (struct ext2_ext_attr_entry *)(header+1);
4009 end = block_buf + fs->blocksize;
4010 while ((char *)entry < end && *(__u32 *)entry) {
4011 if (region_allocate(region, (char *)entry - (char *)header,
4012 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
4013 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4014 goto clear_extattr;
4015 }
4016 if ((ctx->ext_attr_ver == 1 &&
4017 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
4018 (ctx->ext_attr_ver == 2 &&
4019 entry->e_name_index == 0)) {
4020 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
4021 goto clear_extattr;
4022 }
4023 if (entry->e_value_block != 0) {
4024 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
4025 goto clear_extattr;
4026 }
4027 if (entry->e_value_size &&
4028 region_allocate(region, entry->e_value_offs,
4029 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
4030 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4031 goto clear_extattr;
4032 }
4033 entry = EXT2_EXT_ATTR_NEXT(entry);
4034 }
4035 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
4036 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4037 goto clear_extattr;
4038 }
4039 region_free(region);
4040
4041 count = header->h_refcount - 1;
4042 if (count)
4043 ea_refcount_store(ctx->refcount, blk, count);
4044 mark_block_used(ctx, blk);
4045 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
4046
4047 return 1;
4048
4049clear_extattr:
4050 inode->i_file_acl = 0;
4051 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
4052 return 0;
4053}
4054
4055/* Returns 1 if bad htree, 0 if OK */
4056static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004057 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004058 struct ext2_inode *inode,
4059 char *block_buf)
4060{
4061 struct ext2_dx_root_info *root;
4062 ext2_filsys fs = ctx->fs;
4063 errcode_t retval;
4064 blk_t blk;
4065
4066 if ((!LINUX_S_ISDIR(inode->i_mode) &&
4067 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
4068 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4069 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
4070 return 1;
4071
4072 blk = inode->i_block[0];
4073 if (((blk == 0) ||
4074 (blk < fs->super->s_first_data_block) ||
4075 (blk >= fs->super->s_blocks_count)) &&
4076 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4077 return 1;
4078
4079 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
4080 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4081 return 1;
4082
4083 /* XXX should check that beginning matches a directory */
4084 root = (struct ext2_dx_root_info *) (block_buf + 24);
4085
4086 if ((root->reserved_zero || root->info_length < 8) &&
4087 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4088 return 1;
4089
4090 pctx->num = root->hash_version;
4091 if ((root->hash_version != EXT2_HASH_LEGACY) &&
4092 (root->hash_version != EXT2_HASH_HALF_MD4) &&
4093 (root->hash_version != EXT2_HASH_TEA) &&
4094 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
4095 return 1;
4096
4097 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
4098 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
4099 return 1;
4100
4101 pctx->num = root->indirect_levels;
4102 if ((root->indirect_levels > 1) &&
4103 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
4104 return 1;
4105
4106 return 0;
4107}
4108
4109/*
4110 * This subroutine is called on each inode to account for all of the
4111 * blocks used by that inode.
4112 */
4113static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
4114 char *block_buf)
4115{
4116 ext2_filsys fs = ctx->fs;
4117 struct process_block_struct_1 pb;
4118 ext2_ino_t ino = pctx->ino;
4119 struct ext2_inode *inode = pctx->inode;
4120 int bad_size = 0;
4121 int dirty_inode = 0;
4122 __u64 size;
4123
4124 pb.ino = ino;
4125 pb.num_blocks = 0;
4126 pb.last_block = -1;
4127 pb.num_illegal_blocks = 0;
4128 pb.suppress = 0; pb.clear = 0;
4129 pb.fragmented = 0;
4130 pb.compressed = 0;
4131 pb.previous_block = 0;
4132 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
4133 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
4134 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
4135 pb.inode = inode;
4136 pb.pctx = pctx;
4137 pb.ctx = ctx;
4138 pctx->ino = ino;
4139 pctx->errcode = 0;
4140
4141 if (inode->i_flags & EXT2_COMPRBLK_FL) {
4142 if (fs->super->s_feature_incompat &
4143 EXT2_FEATURE_INCOMPAT_COMPRESSION)
4144 pb.compressed = 1;
4145 else {
4146 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
4147 inode->i_flags &= ~EXT2_COMPRBLK_FL;
4148 dirty_inode++;
4149 }
4150 }
4151 }
4152
4153 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
4154 pb.num_blocks++;
4155
4156 if (ext2fs_inode_has_valid_blocks(inode))
4157 pctx->errcode = ext2fs_block_iterate2(fs, ino,
4158 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
4159 block_buf, process_block, &pb);
4160 end_problem_latch(ctx, PR_LATCH_BLOCK);
4161 end_problem_latch(ctx, PR_LATCH_TOOBIG);
4162 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4163 goto out;
4164 if (pctx->errcode)
4165 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
4166
4167 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
4168 ctx->fs_fragmented++;
4169
4170 if (pb.clear) {
4171 inode->i_links_count = 0;
4172 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4173 inode->i_dtime = time(0);
4174 dirty_inode++;
4175 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4176 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4177 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4178 /*
4179 * The inode was probably partially accounted for
4180 * before processing was aborted, so we need to
4181 * restart the pass 1 scan.
4182 */
4183 ctx->flags |= E2F_FLAG_RESTART;
4184 goto out;
4185 }
4186
4187 if (inode->i_flags & EXT2_INDEX_FL) {
4188 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
4189 inode->i_flags &= ~EXT2_INDEX_FL;
4190 dirty_inode++;
4191 } else {
4192#ifdef ENABLE_HTREE
4193 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
4194#endif
4195 }
4196 }
4197 if (ctx->dirs_to_hash && pb.is_dir &&
4198 !(inode->i_flags & EXT2_INDEX_FL) &&
4199 ((inode->i_size / fs->blocksize) >= 3))
4200 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
4201
4202 if (!pb.num_blocks && pb.is_dir) {
4203 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
4204 inode->i_links_count = 0;
4205 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4206 inode->i_dtime = time(0);
4207 dirty_inode++;
4208 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4209 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4210 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4211 ctx->fs_directory_count--;
4212 goto out;
4213 }
4214 }
4215
4216 pb.num_blocks *= (fs->blocksize / 512);
Rob Landley3e72c592006-04-06 22:49:04 +00004217
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004218 if (pb.is_dir) {
4219 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
4220 if (nblock > (pb.last_block + 1))
4221 bad_size = 1;
4222 else if (nblock < (pb.last_block + 1)) {
4223 if (((pb.last_block + 1) - nblock) >
4224 fs->super->s_prealloc_dir_blocks)
4225 bad_size = 2;
4226 }
4227 } else {
4228 size = EXT2_I_SIZE(inode);
4229 if ((pb.last_block >= 0) &&
4230 (size < (__u64) pb.last_block * fs->blocksize))
4231 bad_size = 3;
4232 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
4233 bad_size = 4;
4234 }
4235 /* i_size for symlinks is checked elsewhere */
4236 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
4237 pctx->num = (pb.last_block+1) * fs->blocksize;
4238 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
4239 inode->i_size = pctx->num;
4240 if (!LINUX_S_ISDIR(inode->i_mode))
4241 inode->i_size_high = pctx->num >> 32;
4242 dirty_inode++;
4243 }
4244 pctx->num = 0;
4245 }
4246 if (LINUX_S_ISREG(inode->i_mode) &&
4247 (inode->i_size_high || inode->i_size & 0x80000000UL))
4248 ctx->large_files++;
4249 if (pb.num_blocks != inode->i_blocks) {
4250 pctx->num = pb.num_blocks;
4251 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
4252 inode->i_blocks = pb.num_blocks;
4253 dirty_inode++;
4254 }
4255 pctx->num = 0;
4256 }
4257out:
4258 if (dirty_inode)
4259 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
4260}
4261
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004262
4263/*
4264 * This is a helper function for check_blocks().
4265 */
4266static int process_block(ext2_filsys fs,
4267 blk_t *block_nr,
4268 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004269 blk_t ref_block FSCK_ATTR((unused)),
4270 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004271 void *priv_data)
4272{
4273 struct process_block_struct_1 *p;
4274 struct problem_context *pctx;
4275 blk_t blk = *block_nr;
4276 int ret_code = 0;
4277 int problem = 0;
4278 e2fsck_t ctx;
4279
4280 p = (struct process_block_struct_1 *) priv_data;
4281 pctx = p->pctx;
4282 ctx = p->ctx;
4283
4284 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
4285 /* todo: Check that the comprblk_fl is high, that the
4286 blkaddr pattern looks right (all non-holes up to
4287 first EXT2FS_COMPRESSED_BLKADDR, then all
4288 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
4289 that the feature_incompat bit is high, and that the
4290 inode is a regular file. If we're doing a "full
4291 check" (a concept introduced to e2fsck by e2compr,
4292 meaning that we look at data blocks as well as
4293 metadata) then call some library routine that
4294 checks the compressed data. I'll have to think
4295 about this, because one particularly important
4296 problem to be able to fix is to recalculate the
4297 cluster size if necessary. I think that perhaps
4298 we'd better do most/all e2compr-specific checks
4299 separately, after the non-e2compr checks. If not
4300 doing a full check, it may be useful to test that
4301 the personality is linux; e.g. if it isn't then
4302 perhaps this really is just an illegal block. */
4303 return 0;
4304 }
4305
4306 if (blk == 0) {
4307 if (p->is_dir == 0) {
4308 /*
4309 * Should never happen, since only directories
4310 * get called with BLOCK_FLAG_HOLE
4311 */
4312#if DEBUG_E2FSCK
4313 printf("process_block() called with blk == 0, "
4314 "blockcnt=%d, inode %lu???\n",
4315 blockcnt, p->ino);
4316#endif
4317 return 0;
4318 }
4319 if (blockcnt < 0)
4320 return 0;
4321 if (blockcnt * fs->blocksize < p->inode->i_size) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004322 goto mark_dir;
4323 }
4324 return 0;
4325 }
4326
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004327 /*
4328 * Simplistic fragmentation check. We merely require that the
4329 * file be contiguous. (Which can never be true for really
4330 * big files that are greater than a block group.)
4331 */
4332 if (!HOLE_BLKADDR(p->previous_block)) {
4333 if (p->previous_block+1 != blk)
4334 p->fragmented = 1;
4335 }
4336 p->previous_block = blk;
4337
4338 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
4339 problem = PR_1_TOOBIG_DIR;
4340 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
4341 problem = PR_1_TOOBIG_REG;
4342 if (!p->is_dir && !p->is_reg && blockcnt > 0)
4343 problem = PR_1_TOOBIG_SYMLINK;
4344
4345 if (blk < fs->super->s_first_data_block ||
4346 blk >= fs->super->s_blocks_count)
4347 problem = PR_1_ILLEGAL_BLOCK_NUM;
4348
4349 if (problem) {
4350 p->num_illegal_blocks++;
4351 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
4352 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
4353 p->clear = 1;
4354 return BLOCK_ABORT;
4355 }
4356 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
4357 p->suppress = 1;
4358 set_latch_flags(PR_LATCH_BLOCK,
4359 PRL_SUPPRESS, 0);
4360 }
4361 }
4362 pctx->blk = blk;
4363 pctx->blkcount = blockcnt;
4364 if (fix_problem(ctx, problem, pctx)) {
4365 blk = *block_nr = 0;
4366 ret_code = BLOCK_CHANGED;
4367 goto mark_dir;
4368 } else
4369 return 0;
4370 }
4371
4372 if (p->ino == EXT2_RESIZE_INO) {
4373 /*
4374 * The resize inode has already be sanity checked
4375 * during pass #0 (the superblock checks). All we
4376 * have to do is mark the double indirect block as
4377 * being in use; all of the other blocks are handled
4378 * by mark_table_blocks()).
4379 */
4380 if (blockcnt == BLOCK_COUNT_DIND)
4381 mark_block_used(ctx, blk);
4382 } else
4383 mark_block_used(ctx, blk);
4384 p->num_blocks++;
4385 if (blockcnt >= 0)
4386 p->last_block = blockcnt;
4387mark_dir:
4388 if (p->is_dir && (blockcnt >= 0)) {
4389 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
4390 blk, blockcnt);
4391 if (pctx->errcode) {
4392 pctx->blk = blk;
4393 pctx->num = blockcnt;
4394 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
4395 /* Should never get here */
4396 ctx->flags |= E2F_FLAG_ABORT;
4397 return BLOCK_ABORT;
4398 }
4399 }
4400 return ret_code;
4401}
4402
Rob Landleyd8f66012006-05-05 17:29:09 +00004403static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004404 blk_t *block_nr,
4405 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004406 blk_t ref_block FSCK_ATTR((unused)),
4407 int ref_offset FSCK_ATTR((unused)),
Rob Landleyd8f66012006-05-05 17:29:09 +00004408 void *priv_data EXT2FS_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004409{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004410 /*
4411 * Note: This function processes blocks for the bad blocks
4412 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
4413 */
4414
Rob Landley7a260f02006-06-19 03:20:03 +00004415 printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr);
Rob Landleyd8f66012006-05-05 17:29:09 +00004416 return BLOCK_ERROR;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004417}
4418
4419/*
4420 * This routine gets called at the end of pass 1 if bad blocks are
4421 * detected in the superblock, group descriptors, inode_bitmaps, or
4422 * block bitmaps. At this point, all of the blocks have been mapped
4423 * out, so we can try to allocate new block(s) to replace the bad
4424 * blocks.
4425 */
4426static void handle_fs_bad_blocks(e2fsck_t ctx)
4427{
Rob Landleyd8f66012006-05-05 17:29:09 +00004428 printf("Bad blocks detected on your filesystem\n"
4429 "You should get your data off as the device will soon die\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004430}
4431
4432/*
4433 * This routine marks all blocks which are used by the superblock,
4434 * group descriptors, inode bitmaps, and block bitmaps.
4435 */
4436static void mark_table_blocks(e2fsck_t ctx)
4437{
4438 ext2_filsys fs = ctx->fs;
4439 blk_t block, b;
4440 dgrp_t i;
4441 int j;
4442 struct problem_context pctx;
4443
4444 clear_problem_context(&pctx);
4445
4446 block = fs->super->s_first_data_block;
4447 for (i = 0; i < fs->group_desc_count; i++) {
4448 pctx.group = i;
4449
4450 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
4451
4452 /*
4453 * Mark the blocks used for the inode table
4454 */
4455 if (fs->group_desc[i].bg_inode_table) {
4456 for (j = 0, b = fs->group_desc[i].bg_inode_table;
4457 j < fs->inode_blocks_per_group;
4458 j++, b++) {
4459 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4460 b)) {
4461 pctx.blk = b;
4462 if (fix_problem(ctx,
4463 PR_1_ITABLE_CONFLICT, &pctx)) {
4464 ctx->invalid_inode_table_flag[i]++;
4465 ctx->invalid_bitmaps++;
4466 }
4467 } else {
4468 ext2fs_mark_block_bitmap(ctx->block_found_map,
4469 b);
4470 }
4471 }
4472 }
4473
4474 /*
4475 * Mark block used for the block bitmap
4476 */
4477 if (fs->group_desc[i].bg_block_bitmap) {
4478 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4479 fs->group_desc[i].bg_block_bitmap)) {
4480 pctx.blk = fs->group_desc[i].bg_block_bitmap;
4481 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
4482 ctx->invalid_block_bitmap_flag[i]++;
4483 ctx->invalid_bitmaps++;
4484 }
4485 } else {
4486 ext2fs_mark_block_bitmap(ctx->block_found_map,
4487 fs->group_desc[i].bg_block_bitmap);
4488 }
4489
4490 }
4491 /*
4492 * Mark block used for the inode bitmap
4493 */
4494 if (fs->group_desc[i].bg_inode_bitmap) {
4495 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4496 fs->group_desc[i].bg_inode_bitmap)) {
4497 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
4498 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
4499 ctx->invalid_inode_bitmap_flag[i]++;
4500 ctx->invalid_bitmaps++;
4501 }
4502 } else {
4503 ext2fs_mark_block_bitmap(ctx->block_found_map,
4504 fs->group_desc[i].bg_inode_bitmap);
4505 }
4506 }
4507 block += fs->super->s_blocks_per_group;
4508 }
4509}
4510
4511/*
4512 * Thes subroutines short circuits ext2fs_get_blocks and
4513 * ext2fs_check_directory; we use them since we already have the inode
4514 * structure, so there's no point in letting the ext2fs library read
4515 * the inode again.
4516 */
4517static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
4518 blk_t *blocks)
4519{
4520 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4521 int i;
4522
4523 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4524 return EXT2_ET_CALLBACK_NOTHANDLED;
4525
4526 for (i=0; i < EXT2_N_BLOCKS; i++)
4527 blocks[i] = ctx->stashed_inode->i_block[i];
4528 return 0;
4529}
4530
4531static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
4532 struct ext2_inode *inode)
4533{
4534 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4535
4536 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4537 return EXT2_ET_CALLBACK_NOTHANDLED;
4538 *inode = *ctx->stashed_inode;
4539 return 0;
4540}
4541
4542static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
4543 struct ext2_inode *inode)
4544{
4545 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4546
4547 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
4548 *ctx->stashed_inode = *inode;
4549 return EXT2_ET_CALLBACK_NOTHANDLED;
4550}
4551
4552static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
4553{
4554 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4555
4556 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4557 return EXT2_ET_CALLBACK_NOTHANDLED;
4558
4559 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
4560 return EXT2_ET_NO_DIRECTORY;
4561 return 0;
4562}
4563
4564void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
4565{
4566 ext2_filsys fs = ctx->fs;
4567
4568 if (bool) {
4569 fs->get_blocks = pass1_get_blocks;
4570 fs->check_directory = pass1_check_directory;
4571 fs->read_inode = pass1_read_inode;
4572 fs->write_inode = pass1_write_inode;
4573 ctx->stashed_ino = 0;
4574 } else {
4575 fs->get_blocks = 0;
4576 fs->check_directory = 0;
4577 fs->read_inode = 0;
4578 fs->write_inode = 0;
4579 }
4580}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004581
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004582/*
4583 * pass1b.c --- Pass #1b of e2fsck
4584 *
4585 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
4586 * only invoked if pass 1 discovered blocks which are in use by more
4587 * than one inode.
4588 *
4589 * Pass1B scans the data blocks of all the inodes again, generating a
4590 * complete list of duplicate blocks and which inodes have claimed
4591 * them.
4592 *
4593 * Pass1C does a tree-traversal of the filesystem, to determine the
4594 * parent directories of these inodes. This step is necessary so that
4595 * e2fsck can print out the pathnames of affected inodes.
4596 *
4597 * Pass1D is a reconciliation pass. For each inode with duplicate
4598 * blocks, the user is prompted if s/he would like to clone the file
4599 * (so that the file gets a fresh copy of the duplicated blocks) or
4600 * simply to delete the file.
4601 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004602 */
4603
4604
4605/* Needed for architectures where sizeof(int) != sizeof(void *) */
4606#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
4607#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
4608
4609/* Define an extension to the ext2 library's block count information */
4610#define BLOCK_COUNT_EXTATTR (-5)
4611
4612struct block_el {
4613 blk_t block;
4614 struct block_el *next;
4615};
4616
4617struct inode_el {
4618 ext2_ino_t inode;
4619 struct inode_el *next;
4620};
4621
4622struct dup_block {
4623 int num_bad;
4624 struct inode_el *inode_list;
4625};
4626
4627/*
4628 * This structure stores information about a particular inode which
4629 * is sharing blocks with other inodes. This information is collected
4630 * to display to the user, so that the user knows what files he or she
4631 * is dealing with, when trying to decide how to resolve the conflict
4632 * of multiply-claimed blocks.
4633 */
4634struct dup_inode {
4635 ext2_ino_t dir;
4636 int num_dupblocks;
4637 struct ext2_inode inode;
4638 struct block_el *block_list;
4639};
4640
4641static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
4642 e2_blkcnt_t blockcnt, blk_t ref_blk,
4643 int ref_offset, void *priv_data);
4644static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
4645 struct dup_inode *dp, char *block_buf);
4646static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
4647 struct dup_inode *dp, char* block_buf);
4648static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
4649
4650static void pass1b(e2fsck_t ctx, char *block_buf);
4651static void pass1c(e2fsck_t ctx, char *block_buf);
4652static void pass1d(e2fsck_t ctx, char *block_buf);
4653
4654static int dup_inode_count = 0;
4655
4656static dict_t blk_dict, ino_dict;
4657
4658static ext2fs_inode_bitmap inode_dup_map;
4659
4660static int dict_int_cmp(const void *a, const void *b)
4661{
4662 intptr_t ia, ib;
4663
4664 ia = (intptr_t)a;
4665 ib = (intptr_t)b;
4666
4667 return (ia-ib);
4668}
4669
4670/*
4671 * Add a duplicate block record
4672 */
4673static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
4674 struct ext2_inode *inode)
4675{
4676 dnode_t *n;
4677 struct dup_block *db;
4678 struct dup_inode *di;
4679 struct block_el *blk_el;
4680 struct inode_el *ino_el;
4681
4682 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
4683 if (n)
4684 db = (struct dup_block *) dnode_get(n);
4685 else {
4686 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
4687 sizeof(struct dup_block), "duplicate block header");
4688 db->num_bad = 0;
4689 db->inode_list = 0;
4690 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
4691 }
4692 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
4693 sizeof(struct inode_el), "inode element");
4694 ino_el->inode = ino;
4695 ino_el->next = db->inode_list;
4696 db->inode_list = ino_el;
4697 db->num_bad++;
4698
4699 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
4700 if (n)
4701 di = (struct dup_inode *) dnode_get(n);
4702 else {
4703 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
4704 sizeof(struct dup_inode), "duplicate inode header");
4705 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
4706 di->num_dupblocks = 0;
4707 di->block_list = 0;
4708 di->inode = *inode;
4709 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
4710 }
4711 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
4712 sizeof(struct block_el), "block element");
4713 blk_el->block = blk;
4714 blk_el->next = di->block_list;
4715 di->block_list = blk_el;
4716 di->num_dupblocks++;
4717}
4718
4719/*
4720 * Free a duplicate inode record
4721 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004722static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004723{
4724 struct dup_inode *di;
4725 struct block_el *p, *next;
4726
4727 di = (struct dup_inode *) dnode_get(node);
4728 for (p = di->block_list; p; p = next) {
4729 next = p->next;
4730 free(p);
4731 }
4732 free(node);
4733}
4734
4735/*
4736 * Free a duplicate block record
4737 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004738static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004739{
4740 struct dup_block *db;
4741 struct inode_el *p, *next;
4742
4743 db = (struct dup_block *) dnode_get(node);
4744 for (p = db->inode_list; p; p = next) {
4745 next = p->next;
4746 free(p);
4747 }
4748 free(node);
4749}
4750
4751
4752/*
4753 * Main procedure for handling duplicate blocks
4754 */
4755void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
4756{
4757 ext2_filsys fs = ctx->fs;
4758 struct problem_context pctx;
4759
4760 clear_problem_context(&pctx);
4761
4762 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4763 _("multiply claimed inode map"), &inode_dup_map);
4764 if (pctx.errcode) {
4765 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
4766 ctx->flags |= E2F_FLAG_ABORT;
4767 return;
4768 }
4769
4770 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
4771 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004772 dict_set_allocator(&ino_dict, inode_dnode_free);
4773 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004774
4775 pass1b(ctx, block_buf);
4776 pass1c(ctx, block_buf);
4777 pass1d(ctx, block_buf);
4778
4779 /*
4780 * Time to free all of the accumulated data structures that we
4781 * don't need anymore.
4782 */
4783 dict_free_nodes(&ino_dict);
4784 dict_free_nodes(&blk_dict);
4785}
4786
4787/*
4788 * Scan the inodes looking for inodes that contain duplicate blocks.
4789 */
4790struct process_block_struct_1b {
4791 e2fsck_t ctx;
4792 ext2_ino_t ino;
4793 int dup_blocks;
4794 struct ext2_inode *inode;
4795 struct problem_context *pctx;
4796};
4797
4798static void pass1b(e2fsck_t ctx, char *block_buf)
4799{
4800 ext2_filsys fs = ctx->fs;
4801 ext2_ino_t ino;
4802 struct ext2_inode inode;
4803 ext2_inode_scan scan;
4804 struct process_block_struct_1b pb;
4805 struct problem_context pctx;
4806
4807 clear_problem_context(&pctx);
4808
4809 if (!(ctx->options & E2F_OPT_PREEN))
4810 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
4811 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
4812 &scan);
4813 if (pctx.errcode) {
4814 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4815 ctx->flags |= E2F_FLAG_ABORT;
4816 return;
4817 }
4818 ctx->stashed_inode = &inode;
4819 pb.ctx = ctx;
4820 pb.pctx = &pctx;
4821 pctx.str = "pass1b";
4822 while (1) {
4823 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
4824 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
4825 continue;
4826 if (pctx.errcode) {
4827 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4828 ctx->flags |= E2F_FLAG_ABORT;
4829 return;
4830 }
4831 if (!ino)
4832 break;
4833 pctx.ino = ctx->stashed_ino = ino;
4834 if ((ino != EXT2_BAD_INO) &&
4835 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
4836 continue;
4837
4838 pb.ino = ino;
4839 pb.dup_blocks = 0;
4840 pb.inode = &inode;
4841
4842 if (ext2fs_inode_has_valid_blocks(&inode) ||
4843 (ino == EXT2_BAD_INO))
4844 pctx.errcode = ext2fs_block_iterate2(fs, ino,
4845 0, block_buf, process_pass1b_block, &pb);
4846 if (inode.i_file_acl)
4847 process_pass1b_block(fs, &inode.i_file_acl,
4848 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
4849 if (pb.dup_blocks) {
4850 end_problem_latch(ctx, PR_LATCH_DBLOCK);
4851 if (ino >= EXT2_FIRST_INODE(fs->super) ||
4852 ino == EXT2_ROOT_INO)
4853 dup_inode_count++;
4854 }
4855 if (pctx.errcode)
4856 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
4857 }
4858 ext2fs_close_inode_scan(scan);
4859 e2fsck_use_inode_shortcuts(ctx, 0);
4860}
4861
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004862static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004863 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004864 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
4865 blk_t ref_blk FSCK_ATTR((unused)),
4866 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004867 void *priv_data)
4868{
4869 struct process_block_struct_1b *p;
4870 e2fsck_t ctx;
4871
4872 if (HOLE_BLKADDR(*block_nr))
4873 return 0;
4874 p = (struct process_block_struct_1b *) priv_data;
4875 ctx = p->ctx;
4876
4877 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
4878 return 0;
4879
4880 /* OK, this is a duplicate block */
4881 if (p->ino != EXT2_BAD_INO) {
4882 p->pctx->blk = *block_nr;
4883 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
4884 }
4885 p->dup_blocks++;
4886 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
4887
4888 add_dupe(ctx, p->ino, *block_nr, p->inode);
4889
4890 return 0;
4891}
4892
4893/*
4894 * Pass 1c: Scan directories for inodes with duplicate blocks. This
4895 * is used so that we can print pathnames when prompting the user for
4896 * what to do.
4897 */
4898struct search_dir_struct {
4899 int count;
4900 ext2_ino_t first_inode;
4901 ext2_ino_t max_inode;
4902};
4903
4904static int search_dirent_proc(ext2_ino_t dir, int entry,
4905 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004906 int offset FSCK_ATTR((unused)),
4907 int blocksize FSCK_ATTR((unused)),
4908 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004909 void *priv_data)
4910{
4911 struct search_dir_struct *sd;
4912 struct dup_inode *p;
4913 dnode_t *n;
4914
4915 sd = (struct search_dir_struct *) priv_data;
4916
4917 if (dirent->inode > sd->max_inode)
4918 /* Should abort this inode, but not everything */
4919 return 0;
4920
4921 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
4922 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
4923 return 0;
4924
4925 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
4926 if (!n)
4927 return 0;
4928 p = (struct dup_inode *) dnode_get(n);
4929 p->dir = dir;
4930 sd->count--;
4931
4932 return(sd->count ? 0 : DIRENT_ABORT);
4933}
4934
4935
4936static void pass1c(e2fsck_t ctx, char *block_buf)
4937{
4938 ext2_filsys fs = ctx->fs;
4939 struct search_dir_struct sd;
4940 struct problem_context pctx;
4941
4942 clear_problem_context(&pctx);
4943
4944 if (!(ctx->options & E2F_OPT_PREEN))
4945 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
4946
4947 /*
4948 * Search through all directories to translate inodes to names
4949 * (by searching for the containing directory for that inode.)
4950 */
4951 sd.count = dup_inode_count;
4952 sd.first_inode = EXT2_FIRST_INODE(fs->super);
4953 sd.max_inode = fs->super->s_inodes_count;
4954 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
4955 search_dirent_proc, &sd);
4956}
4957
4958static void pass1d(e2fsck_t ctx, char *block_buf)
4959{
4960 ext2_filsys fs = ctx->fs;
4961 struct dup_inode *p, *t;
4962 struct dup_block *q;
4963 ext2_ino_t *shared, ino;
4964 int shared_len;
4965 int i;
4966 int file_ok;
4967 int meta_data = 0;
4968 struct problem_context pctx;
4969 dnode_t *n, *m;
4970 struct block_el *s;
4971 struct inode_el *r;
4972
4973 clear_problem_context(&pctx);
4974
4975 if (!(ctx->options & E2F_OPT_PREEN))
4976 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
4977 e2fsck_read_bitmaps(ctx);
4978
4979 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
4980 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
4981 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
4982 sizeof(ext2_ino_t) * dict_count(&ino_dict),
4983 "Shared inode list");
4984 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
4985 p = (struct dup_inode *) dnode_get(n);
4986 shared_len = 0;
4987 file_ok = 1;
4988 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00004989 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004990 continue;
4991
4992 /*
4993 * Find all of the inodes which share blocks with this
4994 * one. First we find all of the duplicate blocks
4995 * belonging to this inode, and then search each block
4996 * get the list of inodes, and merge them together.
4997 */
4998 for (s = p->block_list; s; s = s->next) {
4999 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
5000 if (!m)
5001 continue; /* Should never happen... */
5002 q = (struct dup_block *) dnode_get(m);
5003 if (q->num_bad > 1)
5004 file_ok = 0;
5005 if (check_if_fs_block(ctx, s->block)) {
5006 file_ok = 0;
5007 meta_data = 1;
5008 }
5009
5010 /*
5011 * Add all inodes used by this block to the
5012 * shared[] --- which is a unique list, so
5013 * if an inode is already in shared[], don't
5014 * add it again.
5015 */
5016 for (r = q->inode_list; r; r = r->next) {
5017 if (r->inode == ino)
5018 continue;
5019 for (i = 0; i < shared_len; i++)
5020 if (shared[i] == r->inode)
5021 break;
5022 if (i == shared_len) {
5023 shared[shared_len++] = r->inode;
5024 }
5025 }
5026 }
5027
5028 /*
5029 * Report the inode that we are working on
5030 */
5031 pctx.inode = &p->inode;
5032 pctx.ino = ino;
5033 pctx.dir = p->dir;
5034 pctx.blkcount = p->num_dupblocks;
5035 pctx.num = meta_data ? shared_len+1 : shared_len;
5036 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
5037 pctx.blkcount = 0;
5038 pctx.num = 0;
5039
5040 if (meta_data)
5041 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
5042
5043 for (i = 0; i < shared_len; i++) {
5044 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
5045 if (!m)
5046 continue; /* should never happen */
5047 t = (struct dup_inode *) dnode_get(m);
5048 /*
5049 * Report the inode that we are sharing with
5050 */
5051 pctx.inode = &t->inode;
5052 pctx.ino = shared[i];
5053 pctx.dir = t->dir;
5054 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
5055 }
5056 if (file_ok) {
5057 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
5058 continue;
5059 }
5060 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
5061 pctx.errcode = clone_file(ctx, ino, p, block_buf);
5062 if (pctx.errcode)
5063 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
5064 else
5065 continue;
5066 }
5067 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
5068 delete_file(ctx, ino, p, block_buf);
5069 else
5070 ext2fs_unmark_valid(fs);
5071 }
5072 ext2fs_free_mem(&shared);
5073}
5074
5075/*
5076 * Drop the refcount on the dup_block structure, and clear the entry
5077 * in the block_dup_map if appropriate.
5078 */
5079static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
5080{
5081 p->num_bad--;
5082 if (p->num_bad <= 0 ||
5083 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
5084 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
5085}
5086
5087static int delete_file_block(ext2_filsys fs,
5088 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005089 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
5090 blk_t ref_block FSCK_ATTR((unused)),
5091 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005092 void *priv_data)
5093{
5094 struct process_block_struct_1b *pb;
5095 struct dup_block *p;
5096 dnode_t *n;
5097 e2fsck_t ctx;
5098
5099 pb = (struct process_block_struct_1b *) priv_data;
5100 ctx = pb->ctx;
5101
5102 if (HOLE_BLKADDR(*block_nr))
5103 return 0;
5104
5105 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5106 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5107 if (n) {
5108 p = (struct dup_block *) dnode_get(n);
5109 decrement_badcount(ctx, *block_nr, p);
5110 } else
Denis Vlasenko6d655be2006-09-06 19:02:46 +00005111 bb_error_msg(_("internal error; can't find dup_blk for %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005112 *block_nr);
5113 } else {
5114 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
5115 ext2fs_block_alloc_stats(fs, *block_nr, -1);
5116 }
5117
5118 return 0;
5119}
5120
5121static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5122 struct dup_inode *dp, char* block_buf)
5123{
5124 ext2_filsys fs = ctx->fs;
5125 struct process_block_struct_1b pb;
5126 struct ext2_inode inode;
5127 struct problem_context pctx;
5128 unsigned int count;
5129
5130 clear_problem_context(&pctx);
5131 pctx.ino = pb.ino = ino;
5132 pb.dup_blocks = dp->num_dupblocks;
5133 pb.ctx = ctx;
5134 pctx.str = "delete_file";
5135
5136 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5137 if (ext2fs_inode_has_valid_blocks(&inode))
5138 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5139 delete_file_block, &pb);
5140 if (pctx.errcode)
5141 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5142 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5143 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5144 if (ctx->inode_bad_map)
5145 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
5146 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
5147
5148 /* Inode may have changed by block_iterate, so reread it */
5149 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5150 inode.i_links_count = 0;
5151 inode.i_dtime = time(0);
5152 if (inode.i_file_acl &&
5153 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
5154 count = 1;
5155 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
5156 block_buf, -1, &count);
5157 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
5158 pctx.errcode = 0;
5159 count = 1;
5160 }
5161 if (pctx.errcode) {
5162 pctx.blk = inode.i_file_acl;
5163 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
5164 }
5165 /*
5166 * If the count is zero, then arrange to have the
5167 * block deleted. If the block is in the block_dup_map,
5168 * also call delete_file_block since it will take care
5169 * of keeping the accounting straight.
5170 */
5171 if ((count == 0) ||
5172 ext2fs_test_block_bitmap(ctx->block_dup_map,
5173 inode.i_file_acl))
5174 delete_file_block(fs, &inode.i_file_acl,
5175 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
5176 }
5177 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
5178}
5179
5180struct clone_struct {
5181 errcode_t errcode;
5182 ext2_ino_t dir;
5183 char *buf;
5184 e2fsck_t ctx;
5185};
5186
5187static int clone_file_block(ext2_filsys fs,
5188 blk_t *block_nr,
5189 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005190 blk_t ref_block FSCK_ATTR((unused)),
5191 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005192 void *priv_data)
5193{
5194 struct dup_block *p;
5195 blk_t new_block;
5196 errcode_t retval;
5197 struct clone_struct *cs = (struct clone_struct *) priv_data;
5198 dnode_t *n;
5199 e2fsck_t ctx;
5200
5201 ctx = cs->ctx;
5202
5203 if (HOLE_BLKADDR(*block_nr))
5204 return 0;
5205
5206 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5207 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5208 if (n) {
5209 p = (struct dup_block *) dnode_get(n);
5210 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
5211 &new_block);
5212 if (retval) {
5213 cs->errcode = retval;
5214 return BLOCK_ABORT;
5215 }
5216 if (cs->dir && (blockcnt >= 0)) {
5217 retval = ext2fs_set_dir_block(fs->dblist,
5218 cs->dir, new_block, blockcnt);
5219 if (retval) {
5220 cs->errcode = retval;
5221 return BLOCK_ABORT;
5222 }
5223 }
Rob Landley3e72c592006-04-06 22:49:04 +00005224
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005225 retval = io_channel_read_blk(fs->io, *block_nr, 1,
5226 cs->buf);
5227 if (retval) {
5228 cs->errcode = retval;
5229 return BLOCK_ABORT;
5230 }
5231 retval = io_channel_write_blk(fs->io, new_block, 1,
5232 cs->buf);
5233 if (retval) {
5234 cs->errcode = retval;
5235 return BLOCK_ABORT;
5236 }
5237 decrement_badcount(ctx, *block_nr, p);
5238 *block_nr = new_block;
5239 ext2fs_mark_block_bitmap(ctx->block_found_map,
5240 new_block);
5241 ext2fs_mark_block_bitmap(fs->block_map, new_block);
5242 return BLOCK_CHANGED;
5243 } else
Denis Vlasenko6d655be2006-09-06 19:02:46 +00005244 bb_error_msg(_("internal error; can't find dup_blk for %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005245 *block_nr);
5246 }
5247 return 0;
5248}
5249
5250static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5251 struct dup_inode *dp, char* block_buf)
5252{
5253 ext2_filsys fs = ctx->fs;
5254 errcode_t retval;
5255 struct clone_struct cs;
5256 struct problem_context pctx;
5257 blk_t blk;
5258 dnode_t *n;
5259 struct inode_el *ino_el;
5260 struct dup_block *db;
5261 struct dup_inode *di;
5262
5263 clear_problem_context(&pctx);
5264 cs.errcode = 0;
5265 cs.dir = 0;
5266 cs.ctx = ctx;
5267 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
5268 if (retval)
5269 return retval;
5270
5271 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
5272 cs.dir = ino;
5273
5274 pctx.ino = ino;
5275 pctx.str = "clone_file";
5276 if (ext2fs_inode_has_valid_blocks(&dp->inode))
5277 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5278 clone_file_block, &cs);
5279 ext2fs_mark_bb_dirty(fs);
5280 if (pctx.errcode) {
5281 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5282 retval = pctx.errcode;
5283 goto errout;
5284 }
5285 if (cs.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +00005286 bb_error_msg(_("returned from clone_file_block"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005287 retval = cs.errcode;
5288 goto errout;
5289 }
5290 /* The inode may have changed on disk, so we have to re-read it */
5291 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
5292 blk = dp->inode.i_file_acl;
5293 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
5294 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
5295 BLOCK_CHANGED)) {
5296 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
5297 /*
5298 * If we cloned the EA block, find all other inodes
5299 * which refered to that EA block, and modify
5300 * them to point to the new EA block.
5301 */
5302 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5303 db = (struct dup_block *) dnode_get(n);
5304 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
5305 if (ino_el->inode == ino)
5306 continue;
5307 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
5308 di = (struct dup_inode *) dnode_get(n);
5309 if (di->inode.i_file_acl == blk) {
5310 di->inode.i_file_acl = dp->inode.i_file_acl;
5311 e2fsck_write_inode(ctx, ino_el->inode,
5312 &di->inode, "clone file EA");
5313 decrement_badcount(ctx, blk, db);
5314 }
5315 }
5316 }
5317 retval = 0;
5318errout:
5319 ext2fs_free_mem(&cs.buf);
5320 return retval;
5321}
5322
5323/*
5324 * This routine returns 1 if a block overlaps with one of the superblocks,
5325 * group descriptors, inode bitmaps, or block bitmaps.
5326 */
5327static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
5328{
5329 ext2_filsys fs = ctx->fs;
5330 blk_t block;
5331 dgrp_t i;
5332
5333 block = fs->super->s_first_data_block;
5334 for (i = 0; i < fs->group_desc_count; i++) {
5335
5336 /* Check superblocks/block group descriptros */
5337 if (ext2fs_bg_has_super(fs, i)) {
5338 if (test_block >= block &&
5339 (test_block <= block + fs->desc_blocks))
5340 return 1;
5341 }
5342
5343 /* Check the inode table */
5344 if ((fs->group_desc[i].bg_inode_table) &&
5345 (test_block >= fs->group_desc[i].bg_inode_table) &&
5346 (test_block < (fs->group_desc[i].bg_inode_table +
5347 fs->inode_blocks_per_group)))
5348 return 1;
5349
5350 /* Check the bitmap blocks */
5351 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
5352 (test_block == fs->group_desc[i].bg_inode_bitmap))
5353 return 1;
5354
5355 block += fs->super->s_blocks_per_group;
5356 }
5357 return 0;
5358}
5359/*
5360 * pass2.c --- check directory structure
5361 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005362 * Pass 2 of e2fsck iterates through all active directory inodes, and
5363 * applies to following tests to each directory entry in the directory
5364 * blocks in the inodes:
5365 *
5366 * - The length of the directory entry (rec_len) should be at
5367 * least 8 bytes, and no more than the remaining space
5368 * left in the directory block.
5369 * - The length of the name in the directory entry (name_len)
5370 * should be less than (rec_len - 8).
5371 * - The inode number in the directory entry should be within
5372 * legal bounds.
5373 * - The inode number should refer to a in-use inode.
5374 * - The first entry should be '.', and its inode should be
5375 * the inode of the directory.
5376 * - The second entry should be '..'.
5377 *
5378 * To minimize disk seek time, the directory blocks are processed in
5379 * sorted order of block numbers.
5380 *
5381 * Pass 2 also collects the following information:
5382 * - The inode numbers of the subdirectories for each directory.
5383 *
5384 * Pass 2 relies on the following information from previous passes:
5385 * - The directory information collected in pass 1.
5386 * - The inode_used_map bitmap
5387 * - The inode_bad_map bitmap
5388 * - The inode_dir_map bitmap
5389 *
5390 * Pass 2 frees the following data structures
5391 * - The inode_bad_map bitmap
5392 * - The inode_reg_map bitmap
5393 */
5394
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005395/*
5396 * Keeps track of how many times an inode is referenced.
5397 */
5398static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
5399static int check_dir_block(ext2_filsys fs,
5400 struct ext2_db_entry *dir_blocks_info,
5401 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005402static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
5403 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005404static int update_dir_block(ext2_filsys fs,
5405 blk_t *block_nr,
5406 e2_blkcnt_t blockcnt,
5407 blk_t ref_block,
5408 int ref_offset,
5409 void *priv_data);
5410static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
5411static int htree_depth(struct dx_dir_info *dx_dir,
5412 struct dx_dirblock_info *dx_db);
Rob Landley7c94bed2006-05-03 21:58:45 +00005413static int special_dir_block_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005414
5415struct check_dir_struct {
5416 char *buf;
5417 struct problem_context pctx;
5418 int count, max;
5419 e2fsck_t ctx;
5420};
5421
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005422static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005423{
5424 struct ext2_super_block *sb = ctx->fs->super;
5425 struct problem_context pctx;
5426 ext2_filsys fs = ctx->fs;
5427 char *buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005428 struct dir_info *dir;
5429 struct check_dir_struct cd;
5430 struct dx_dir_info *dx_dir;
5431 struct dx_dirblock_info *dx_db, *dx_parent;
5432 int b;
5433 int i, depth;
5434 problem_t code;
5435 int bad_dir;
5436
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005437 clear_problem_context(&cd.pctx);
5438
Rob Landley3e72c592006-04-06 22:49:04 +00005439 /* Pass 2 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005440
5441 if (!(ctx->options & E2F_OPT_PREEN))
5442 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
5443
5444 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
5445 0, ctx->inode_link_info,
5446 &ctx->inode_count);
5447 if (cd.pctx.errcode) {
5448 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
5449 ctx->flags |= E2F_FLAG_ABORT;
5450 return;
5451 }
5452 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
5453 "directory scan buffer");
5454
5455 /*
5456 * Set up the parent pointer for the root directory, if
5457 * present. (If the root directory is not present, we will
5458 * create it in pass 3.)
5459 */
5460 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
5461 if (dir)
5462 dir->parent = EXT2_ROOT_INO;
5463
5464 cd.buf = buf;
5465 cd.ctx = ctx;
5466 cd.count = 1;
5467 cd.max = ext2fs_dblist_count(fs->dblist);
5468
5469 if (ctx->progress)
5470 (void) (ctx->progress)(ctx, 2, 0, cd.max);
5471
5472 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
5473 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
5474
5475 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
5476 &cd);
5477 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5478 return;
5479 if (cd.pctx.errcode) {
5480 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
5481 ctx->flags |= E2F_FLAG_ABORT;
5482 return;
5483 }
5484
5485#ifdef ENABLE_HTREE
5486 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
5487 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5488 return;
5489 if (dx_dir->numblocks == 0)
5490 continue;
5491 clear_problem_context(&pctx);
5492 bad_dir = 0;
5493 pctx.dir = dx_dir->ino;
5494 dx_db = dx_dir->dx_block;
5495 if (dx_db->flags & DX_FLAG_REFERENCED)
5496 dx_db->flags |= DX_FLAG_DUP_REF;
5497 else
5498 dx_db->flags |= DX_FLAG_REFERENCED;
5499 /*
5500 * Find all of the first and last leaf blocks, and
5501 * update their parent's min and max hash values
5502 */
5503 for (b=0, dx_db = dx_dir->dx_block;
5504 b < dx_dir->numblocks;
5505 b++, dx_db++) {
5506 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
5507 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
5508 continue;
5509 dx_parent = &dx_dir->dx_block[dx_db->parent];
5510 /*
5511 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
5512 */
5513 if (dx_db->flags & DX_FLAG_FIRST)
5514 dx_parent->min_hash = dx_db->min_hash;
5515 /*
5516 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
5517 */
5518 if (dx_db->flags & DX_FLAG_LAST)
5519 dx_parent->max_hash = dx_db->max_hash;
5520 }
5521
5522 for (b=0, dx_db = dx_dir->dx_block;
5523 b < dx_dir->numblocks;
5524 b++, dx_db++) {
5525 pctx.blkcount = b;
5526 pctx.group = dx_db->parent;
5527 code = 0;
5528 if (!(dx_db->flags & DX_FLAG_FIRST) &&
5529 (dx_db->min_hash < dx_db->node_min_hash)) {
5530 pctx.blk = dx_db->min_hash;
5531 pctx.blk2 = dx_db->node_min_hash;
5532 code = PR_2_HTREE_MIN_HASH;
5533 fix_problem(ctx, code, &pctx);
5534 bad_dir++;
5535 }
5536 if (dx_db->type == DX_DIRBLOCK_LEAF) {
5537 depth = htree_depth(dx_dir, dx_db);
5538 if (depth != dx_dir->depth) {
5539 code = PR_2_HTREE_BAD_DEPTH;
5540 fix_problem(ctx, code, &pctx);
5541 bad_dir++;
5542 }
5543 }
5544 /*
5545 * This test doesn't apply for the root block
5546 * at block #0
5547 */
5548 if (b &&
5549 (dx_db->max_hash > dx_db->node_max_hash)) {
5550 pctx.blk = dx_db->max_hash;
5551 pctx.blk2 = dx_db->node_max_hash;
5552 code = PR_2_HTREE_MAX_HASH;
5553 fix_problem(ctx, code, &pctx);
5554 bad_dir++;
5555 }
5556 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
5557 code = PR_2_HTREE_NOTREF;
5558 fix_problem(ctx, code, &pctx);
5559 bad_dir++;
5560 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
5561 code = PR_2_HTREE_DUPREF;
5562 fix_problem(ctx, code, &pctx);
5563 bad_dir++;
5564 }
5565 if (code == 0)
5566 continue;
5567 }
5568 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
5569 clear_htree(ctx, dx_dir->ino);
5570 dx_dir->numblocks = 0;
5571 }
5572 }
5573#endif
5574 ext2fs_free_mem(&buf);
5575 ext2fs_free_dblist(fs->dblist);
5576
Rob Landleye7c43b62006-03-01 16:39:45 +00005577 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
5578 ctx->inode_bad_map = 0;
5579 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
5580 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005581
5582 clear_problem_context(&pctx);
5583 if (ctx->large_files) {
5584 if (!(sb->s_feature_ro_compat &
5585 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
5586 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
5587 sb->s_feature_ro_compat |=
5588 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5589 ext2fs_mark_super_dirty(fs);
5590 }
5591 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
5592 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
5593 ext2fs_update_dynamic_rev(fs);
5594 ext2fs_mark_super_dirty(fs);
5595 }
5596 } else if (!ctx->large_files &&
5597 (sb->s_feature_ro_compat &
5598 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
5599 if (fs->flags & EXT2_FLAG_RW) {
5600 sb->s_feature_ro_compat &=
5601 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5602 ext2fs_mark_super_dirty(fs);
5603 }
5604 }
5605
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005606}
5607
5608#define MAX_DEPTH 32000
5609static int htree_depth(struct dx_dir_info *dx_dir,
5610 struct dx_dirblock_info *dx_db)
5611{
5612 int depth = 0;
5613
5614 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
5615 dx_db = &dx_dir->dx_block[dx_db->parent];
5616 depth++;
5617 }
5618 return depth;
5619}
5620
5621static int dict_de_cmp(const void *a, const void *b)
5622{
5623 const struct ext2_dir_entry *de_a, *de_b;
5624 int a_len, b_len;
5625
5626 de_a = (const struct ext2_dir_entry *) a;
5627 a_len = de_a->name_len & 0xFF;
5628 de_b = (const struct ext2_dir_entry *) b;
5629 b_len = de_b->name_len & 0xFF;
5630
5631 if (a_len != b_len)
5632 return (a_len - b_len);
5633
5634 return strncmp(de_a->name, de_b->name, a_len);
5635}
5636
5637/*
5638 * This is special sort function that makes sure that directory blocks
5639 * with a dirblock of zero are sorted to the beginning of the list.
5640 * This guarantees that the root node of the htree directories are
5641 * processed first, so we know what hash version to use.
5642 */
Rob Landley7c94bed2006-05-03 21:58:45 +00005643static int special_dir_block_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005644{
5645 const struct ext2_db_entry *db_a =
5646 (const struct ext2_db_entry *) a;
5647 const struct ext2_db_entry *db_b =
5648 (const struct ext2_db_entry *) b;
5649
5650 if (db_a->blockcnt && !db_b->blockcnt)
5651 return 1;
5652
5653 if (!db_a->blockcnt && db_b->blockcnt)
5654 return -1;
5655
5656 if (db_a->blk != db_b->blk)
5657 return (int) (db_a->blk - db_b->blk);
5658
5659 if (db_a->ino != db_b->ino)
5660 return (int) (db_a->ino - db_b->ino);
5661
5662 return (int) (db_a->blockcnt - db_b->blockcnt);
5663}
5664
5665
5666/*
5667 * Make sure the first entry in the directory is '.', and that the
5668 * directory entry is sane.
5669 */
5670static int check_dot(e2fsck_t ctx,
5671 struct ext2_dir_entry *dirent,
5672 ext2_ino_t ino, struct problem_context *pctx)
5673{
5674 struct ext2_dir_entry *nextdir;
5675 int status = 0;
5676 int created = 0;
5677 int new_len;
5678 int problem = 0;
5679
5680 if (!dirent->inode)
5681 problem = PR_2_MISSING_DOT;
5682 else if (((dirent->name_len & 0xFF) != 1) ||
5683 (dirent->name[0] != '.'))
5684 problem = PR_2_1ST_NOT_DOT;
5685 else if (dirent->name[1] != '\0')
5686 problem = PR_2_DOT_NULL_TERM;
5687
5688 if (problem) {
5689 if (fix_problem(ctx, problem, pctx)) {
5690 if (dirent->rec_len < 12)
5691 dirent->rec_len = 12;
5692 dirent->inode = ino;
5693 dirent->name_len = 1;
5694 dirent->name[0] = '.';
5695 dirent->name[1] = '\0';
5696 status = 1;
5697 created = 1;
5698 }
5699 }
5700 if (dirent->inode != ino) {
5701 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
5702 dirent->inode = ino;
5703 status = 1;
5704 }
5705 }
5706 if (dirent->rec_len > 12) {
5707 new_len = dirent->rec_len - 12;
5708 if (new_len > 12) {
5709 if (created ||
5710 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
5711 nextdir = (struct ext2_dir_entry *)
5712 ((char *) dirent + 12);
5713 dirent->rec_len = 12;
5714 nextdir->rec_len = new_len;
5715 nextdir->inode = 0;
5716 nextdir->name_len = 0;
5717 status = 1;
5718 }
5719 }
5720 }
5721 return status;
5722}
5723
5724/*
5725 * Make sure the second entry in the directory is '..', and that the
5726 * directory entry is sane. We do not check the inode number of '..'
5727 * here; this gets done in pass 3.
5728 */
5729static int check_dotdot(e2fsck_t ctx,
5730 struct ext2_dir_entry *dirent,
5731 struct dir_info *dir, struct problem_context *pctx)
5732{
5733 int problem = 0;
5734
5735 if (!dirent->inode)
5736 problem = PR_2_MISSING_DOT_DOT;
5737 else if (((dirent->name_len & 0xFF) != 2) ||
5738 (dirent->name[0] != '.') ||
5739 (dirent->name[1] != '.'))
5740 problem = PR_2_2ND_NOT_DOT_DOT;
5741 else if (dirent->name[2] != '\0')
5742 problem = PR_2_DOT_DOT_NULL_TERM;
5743
5744 if (problem) {
5745 if (fix_problem(ctx, problem, pctx)) {
5746 if (dirent->rec_len < 12)
5747 dirent->rec_len = 12;
5748 /*
5749 * Note: we don't have the parent inode just
5750 * yet, so we will fill it in with the root
5751 * inode. This will get fixed in pass 3.
5752 */
5753 dirent->inode = EXT2_ROOT_INO;
5754 dirent->name_len = 2;
5755 dirent->name[0] = '.';
5756 dirent->name[1] = '.';
5757 dirent->name[2] = '\0';
5758 return 1;
5759 }
5760 return 0;
5761 }
5762 dir->dotdot = dirent->inode;
5763 return 0;
5764}
5765
5766/*
5767 * Check to make sure a directory entry doesn't contain any illegal
5768 * characters.
5769 */
5770static int check_name(e2fsck_t ctx,
5771 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005772 struct problem_context *pctx)
5773{
5774 int i;
5775 int fixup = -1;
5776 int ret = 0;
5777
5778 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
5779 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
5780 if (fixup < 0) {
5781 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
5782 }
5783 if (fixup) {
5784 dirent->name[i] = '.';
5785 ret = 1;
5786 }
5787 }
5788 }
5789 return ret;
5790}
5791
5792/*
5793 * Check the directory filetype (if present)
5794 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005795
5796/*
5797 * Given a mode, return the ext2 file type
5798 */
5799static int ext2_file_type(unsigned int mode)
5800{
5801 if (LINUX_S_ISREG(mode))
5802 return EXT2_FT_REG_FILE;
5803
5804 if (LINUX_S_ISDIR(mode))
5805 return EXT2_FT_DIR;
5806
5807 if (LINUX_S_ISCHR(mode))
5808 return EXT2_FT_CHRDEV;
5809
5810 if (LINUX_S_ISBLK(mode))
5811 return EXT2_FT_BLKDEV;
5812
5813 if (LINUX_S_ISLNK(mode))
5814 return EXT2_FT_SYMLINK;
5815
5816 if (LINUX_S_ISFIFO(mode))
5817 return EXT2_FT_FIFO;
5818
5819 if (LINUX_S_ISSOCK(mode))
5820 return EXT2_FT_SOCK;
5821
5822 return 0;
5823}
5824
Rob Landley7c94bed2006-05-03 21:58:45 +00005825static int check_filetype(e2fsck_t ctx,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005826 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005827 struct problem_context *pctx)
5828{
5829 int filetype = dirent->name_len >> 8;
5830 int should_be = EXT2_FT_UNKNOWN;
5831 struct ext2_inode inode;
5832
5833 if (!(ctx->fs->super->s_feature_incompat &
5834 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
5835 if (filetype == 0 ||
5836 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
5837 return 0;
5838 dirent->name_len = dirent->name_len & 0xFF;
5839 return 1;
5840 }
5841
5842 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
5843 should_be = EXT2_FT_DIR;
5844 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
5845 dirent->inode)) {
5846 should_be = EXT2_FT_REG_FILE;
5847 } else if (ctx->inode_bad_map &&
5848 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
5849 dirent->inode))
5850 should_be = 0;
5851 else {
5852 e2fsck_read_inode(ctx, dirent->inode, &inode,
5853 "check_filetype");
5854 should_be = ext2_file_type(inode.i_mode);
5855 }
5856 if (filetype == should_be)
5857 return 0;
5858 pctx->num = should_be;
5859
5860 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
5861 pctx) == 0)
5862 return 0;
5863
5864 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
5865 return 1;
5866}
5867
5868#ifdef ENABLE_HTREE
5869static void parse_int_node(ext2_filsys fs,
5870 struct ext2_db_entry *db,
5871 struct check_dir_struct *cd,
5872 struct dx_dir_info *dx_dir,
5873 char *block_buf)
5874{
5875 struct ext2_dx_root_info *root;
5876 struct ext2_dx_entry *ent;
5877 struct ext2_dx_countlimit *limit;
5878 struct dx_dirblock_info *dx_db;
5879 int i, expect_limit, count;
5880 blk_t blk;
5881 ext2_dirhash_t min_hash = 0xffffffff;
5882 ext2_dirhash_t max_hash = 0;
5883 ext2_dirhash_t hash = 0, prev_hash;
5884
5885 if (db->blockcnt == 0) {
5886 root = (struct ext2_dx_root_info *) (block_buf + 24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005887 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
5888 } else {
5889 ent = (struct ext2_dx_entry *) (block_buf+8);
5890 }
5891 limit = (struct ext2_dx_countlimit *) ent;
5892
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005893 count = ext2fs_le16_to_cpu(limit->count);
5894 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
5895 sizeof(struct ext2_dx_entry);
5896 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
5897 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
5898 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
5899 goto clear_and_exit;
5900 }
5901 if (count > expect_limit) {
5902 cd->pctx.num = count;
5903 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
5904 goto clear_and_exit;
5905 count = expect_limit;
5906 }
5907
5908 for (i=0; i < count; i++) {
5909 prev_hash = hash;
5910 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005911 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
5912 /* Check to make sure the block is valid */
5913 if (blk > (blk_t) dx_dir->numblocks) {
5914 cd->pctx.blk = blk;
5915 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
5916 &cd->pctx))
5917 goto clear_and_exit;
5918 }
5919 if (hash < prev_hash &&
5920 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
5921 goto clear_and_exit;
5922 dx_db = &dx_dir->dx_block[blk];
5923 if (dx_db->flags & DX_FLAG_REFERENCED) {
5924 dx_db->flags |= DX_FLAG_DUP_REF;
5925 } else {
5926 dx_db->flags |= DX_FLAG_REFERENCED;
5927 dx_db->parent = db->blockcnt;
5928 }
5929 if (hash < min_hash)
5930 min_hash = hash;
5931 if (hash > max_hash)
5932 max_hash = hash;
5933 dx_db->node_min_hash = hash;
5934 if ((i+1) < count)
5935 dx_db->node_max_hash =
5936 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
5937 else {
5938 dx_db->node_max_hash = 0xfffffffe;
5939 dx_db->flags |= DX_FLAG_LAST;
5940 }
5941 if (i == 0)
5942 dx_db->flags |= DX_FLAG_FIRST;
5943 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005944 dx_db = &dx_dir->dx_block[db->blockcnt];
5945 dx_db->min_hash = min_hash;
5946 dx_db->max_hash = max_hash;
5947 return;
5948
5949clear_and_exit:
5950 clear_htree(cd->ctx, cd->pctx.ino);
5951 dx_dir->numblocks = 0;
5952}
5953#endif /* ENABLE_HTREE */
5954
5955/*
5956 * Given a busted directory, try to salvage it somehow.
5957 *
5958 */
5959static void salvage_directory(ext2_filsys fs,
5960 struct ext2_dir_entry *dirent,
5961 struct ext2_dir_entry *prev,
5962 unsigned int *offset)
5963{
5964 char *cp = (char *) dirent;
5965 int left = fs->blocksize - *offset - dirent->rec_len;
5966 int name_len = dirent->name_len & 0xFF;
5967
5968 /*
5969 * Special case of directory entry of size 8: copy what's left
5970 * of the directory block up to cover up the invalid hole.
5971 */
5972 if ((left >= 12) && (dirent->rec_len == 8)) {
5973 memmove(cp, cp+8, left);
5974 memset(cp + left, 0, 8);
5975 return;
5976 }
5977 /*
5978 * If the directory entry overruns the end of the directory
5979 * block, and the name is small enough to fit, then adjust the
5980 * record length.
5981 */
5982 if ((left < 0) &&
5983 (name_len + 8 <= dirent->rec_len + left) &&
5984 dirent->inode <= fs->super->s_inodes_count &&
5985 strnlen(dirent->name, name_len) == name_len) {
5986 dirent->rec_len += left;
5987 return;
5988 }
5989 /*
5990 * If the directory entry is a multiple of four, so it is
5991 * valid, let the previous directory entry absorb the invalid
5992 * one.
5993 */
5994 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
5995 prev->rec_len += dirent->rec_len;
5996 *offset += dirent->rec_len;
5997 return;
5998 }
5999 /*
6000 * Default salvage method --- kill all of the directory
6001 * entries for the rest of the block. We will either try to
6002 * absorb it into the previous directory entry, or create a
6003 * new empty directory entry the rest of the directory block.
6004 */
6005 if (prev) {
6006 prev->rec_len += fs->blocksize - *offset;
6007 *offset = fs->blocksize;
6008 } else {
6009 dirent->rec_len = fs->blocksize - *offset;
6010 dirent->name_len = 0;
6011 dirent->inode = 0;
6012 }
6013}
6014
6015static int check_dir_block(ext2_filsys fs,
6016 struct ext2_db_entry *db,
6017 void *priv_data)
6018{
6019 struct dir_info *subdir, *dir;
6020 struct dx_dir_info *dx_dir;
6021#ifdef ENABLE_HTREE
6022 struct dx_dirblock_info *dx_db = 0;
6023#endif /* ENABLE_HTREE */
6024 struct ext2_dir_entry *dirent, *prev;
6025 ext2_dirhash_t hash;
6026 unsigned int offset = 0;
6027 int dir_modified = 0;
6028 int dot_state;
6029 blk_t block_nr = db->blk;
6030 ext2_ino_t ino = db->ino;
6031 __u16 links;
6032 struct check_dir_struct *cd;
6033 char *buf;
6034 e2fsck_t ctx;
6035 int problem;
6036 struct ext2_dx_root_info *root;
6037 struct ext2_dx_countlimit *limit;
6038 static dict_t de_dict;
6039 struct problem_context pctx;
6040 int dups_found = 0;
6041
6042 cd = (struct check_dir_struct *) priv_data;
6043 buf = cd->buf;
6044 ctx = cd->ctx;
6045
6046 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6047 return DIRENT_ABORT;
6048
6049 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
6050 return DIRENT_ABORT;
6051
6052 /*
6053 * Make sure the inode is still in use (could have been
6054 * deleted in the duplicate/bad blocks pass.
6055 */
6056 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
6057 return 0;
6058
6059 cd->pctx.ino = ino;
6060 cd->pctx.blk = block_nr;
6061 cd->pctx.blkcount = db->blockcnt;
6062 cd->pctx.ino2 = 0;
6063 cd->pctx.dirent = 0;
6064 cd->pctx.num = 0;
6065
6066 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006067 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006068 return 0;
6069 block_nr = db->blk;
6070 }
6071
6072 if (db->blockcnt)
6073 dot_state = 2;
6074 else
6075 dot_state = 0;
6076
6077 if (ctx->dirs_to_hash &&
6078 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
6079 dups_found++;
6080
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006081 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
6082 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
6083 cd->pctx.errcode = 0; /* We'll handle this ourselves */
6084 if (cd->pctx.errcode) {
6085 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
6086 ctx->flags |= E2F_FLAG_ABORT;
6087 return DIRENT_ABORT;
6088 }
6089 memset(buf, 0, fs->blocksize);
6090 }
6091#ifdef ENABLE_HTREE
6092 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
6093 if (dx_dir && dx_dir->numblocks) {
6094 if (db->blockcnt >= dx_dir->numblocks) {
6095 printf("XXX should never happen!!!\n");
6096 abort();
6097 }
6098 dx_db = &dx_dir->dx_block[db->blockcnt];
6099 dx_db->type = DX_DIRBLOCK_LEAF;
6100 dx_db->phys = block_nr;
6101 dx_db->min_hash = ~0;
6102 dx_db->max_hash = 0;
6103
6104 dirent = (struct ext2_dir_entry *) buf;
6105 limit = (struct ext2_dx_countlimit *) (buf+8);
6106 if (db->blockcnt == 0) {
6107 root = (struct ext2_dx_root_info *) (buf + 24);
6108 dx_db->type = DX_DIRBLOCK_ROOT;
6109 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
6110 if ((root->reserved_zero ||
6111 root->info_length < 8 ||
6112 root->indirect_levels > 1) &&
6113 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
6114 clear_htree(ctx, ino);
6115 dx_dir->numblocks = 0;
6116 dx_db = 0;
6117 }
6118 dx_dir->hashversion = root->hash_version;
6119 dx_dir->depth = root->indirect_levels + 1;
6120 } else if ((dirent->inode == 0) &&
6121 (dirent->rec_len == fs->blocksize) &&
6122 (dirent->name_len == 0) &&
6123 (ext2fs_le16_to_cpu(limit->limit) ==
6124 ((fs->blocksize-8) /
6125 sizeof(struct ext2_dx_entry))))
6126 dx_db->type = DX_DIRBLOCK_NODE;
6127 }
6128#endif /* ENABLE_HTREE */
6129
6130 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
6131 prev = 0;
6132 do {
6133 problem = 0;
6134 dirent = (struct ext2_dir_entry *) (buf + offset);
6135 cd->pctx.dirent = dirent;
6136 cd->pctx.num = offset;
6137 if (((offset + dirent->rec_len) > fs->blocksize) ||
6138 (dirent->rec_len < 12) ||
6139 ((dirent->rec_len % 4) != 0) ||
6140 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
6141 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
6142 salvage_directory(fs, dirent, prev, &offset);
6143 dir_modified++;
6144 continue;
6145 } else
6146 goto abort_free_dict;
6147 }
6148 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
6149 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
6150 dirent->name_len = EXT2_NAME_LEN;
6151 dir_modified++;
6152 }
6153 }
6154
6155 if (dot_state == 0) {
6156 if (check_dot(ctx, dirent, ino, &cd->pctx))
6157 dir_modified++;
6158 } else if (dot_state == 1) {
6159 dir = e2fsck_get_dir_info(ctx, ino);
6160 if (!dir) {
6161 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6162 goto abort_free_dict;
6163 }
6164 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
6165 dir_modified++;
6166 } else if (dirent->inode == ino) {
6167 problem = PR_2_LINK_DOT;
6168 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
6169 dirent->inode = 0;
6170 dir_modified++;
6171 goto next;
6172 }
6173 }
6174 if (!dirent->inode)
6175 goto next;
6176
6177 /*
6178 * Make sure the inode listed is a legal one.
6179 */
6180 if (((dirent->inode != EXT2_ROOT_INO) &&
6181 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
6182 (dirent->inode > fs->super->s_inodes_count)) {
6183 problem = PR_2_BAD_INO;
6184 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
6185 dirent->inode))) {
6186 /*
6187 * If the inode is unused, offer to clear it.
6188 */
6189 problem = PR_2_UNUSED_INODE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006190 } else if ((dot_state > 1) &&
6191 ((dirent->name_len & 0xFF) == 1) &&
6192 (dirent->name[0] == '.')) {
6193 /*
6194 * If there's a '.' entry in anything other
6195 * than the first directory entry, it's a
6196 * duplicate entry that should be removed.
6197 */
6198 problem = PR_2_DUP_DOT;
6199 } else if ((dot_state > 1) &&
6200 ((dirent->name_len & 0xFF) == 2) &&
6201 (dirent->name[0] == '.') &&
6202 (dirent->name[1] == '.')) {
6203 /*
6204 * If there's a '..' entry in anything other
6205 * than the second directory entry, it's a
6206 * duplicate entry that should be removed.
6207 */
6208 problem = PR_2_DUP_DOT_DOT;
6209 } else if ((dot_state > 1) &&
6210 (dirent->inode == EXT2_ROOT_INO)) {
6211 /*
6212 * Don't allow links to the root directory.
6213 * We check this specially to make sure we
6214 * catch this error case even if the root
6215 * directory hasn't been created yet.
6216 */
6217 problem = PR_2_LINK_ROOT;
6218 } else if ((dot_state > 1) &&
6219 (dirent->name_len & 0xFF) == 0) {
6220 /*
6221 * Don't allow zero-length directory names.
6222 */
6223 problem = PR_2_NULL_NAME;
6224 }
6225
6226 if (problem) {
6227 if (fix_problem(ctx, problem, &cd->pctx)) {
6228 dirent->inode = 0;
6229 dir_modified++;
6230 goto next;
6231 } else {
6232 ext2fs_unmark_valid(fs);
6233 if (problem == PR_2_BAD_INO)
6234 goto next;
6235 }
6236 }
6237
6238 /*
6239 * If the inode was marked as having bad fields in
6240 * pass1, process it and offer to fix/clear it.
6241 * (We wait until now so that we can display the
6242 * pathname to the user.)
6243 */
6244 if (ctx->inode_bad_map &&
6245 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
6246 dirent->inode)) {
6247 if (e2fsck_process_bad_inode(ctx, ino,
6248 dirent->inode,
6249 buf + fs->blocksize)) {
6250 dirent->inode = 0;
6251 dir_modified++;
6252 goto next;
6253 }
6254 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6255 return DIRENT_ABORT;
6256 }
6257
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006258 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006259 dir_modified++;
6260
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006261 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006262 dir_modified++;
6263
6264#ifdef ENABLE_HTREE
6265 if (dx_db) {
6266 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
6267 (dirent->name_len & 0xFF),
6268 fs->super->s_hash_seed, &hash, 0);
6269 if (hash < dx_db->min_hash)
6270 dx_db->min_hash = hash;
6271 if (hash > dx_db->max_hash)
6272 dx_db->max_hash = hash;
6273 }
6274#endif
6275
6276 /*
6277 * If this is a directory, then mark its parent in its
6278 * dir_info structure. If the parent field is already
6279 * filled in, then this directory has more than one
6280 * hard link. We assume the first link is correct,
6281 * and ask the user if he/she wants to clear this one.
6282 */
6283 if ((dot_state > 1) &&
6284 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6285 dirent->inode))) {
6286 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
6287 if (!subdir) {
6288 cd->pctx.ino = dirent->inode;
6289 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6290 goto abort_free_dict;
6291 }
6292 if (subdir->parent) {
6293 cd->pctx.ino2 = subdir->parent;
6294 if (fix_problem(ctx, PR_2_LINK_DIR,
6295 &cd->pctx)) {
6296 dirent->inode = 0;
6297 dir_modified++;
6298 goto next;
6299 }
6300 cd->pctx.ino2 = 0;
6301 } else
6302 subdir->parent = ino;
6303 }
6304
6305 if (dups_found) {
6306 ;
6307 } else if (dict_lookup(&de_dict, dirent)) {
6308 clear_problem_context(&pctx);
6309 pctx.ino = ino;
6310 pctx.dirent = dirent;
6311 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
6312 if (!ctx->dirs_to_hash)
6313 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
6314 if (ctx->dirs_to_hash)
6315 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6316 dups_found++;
6317 } else
6318 dict_alloc_insert(&de_dict, dirent, dirent);
6319
6320 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
6321 &links);
6322 if (links > 1)
6323 ctx->fs_links_count++;
6324 ctx->fs_total_count++;
6325 next:
6326 prev = dirent;
6327 offset += dirent->rec_len;
6328 dot_state++;
6329 } while (offset < fs->blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006330#ifdef ENABLE_HTREE
6331 if (dx_db) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006332 cd->pctx.dir = cd->pctx.ino;
6333 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
6334 (dx_db->type == DX_DIRBLOCK_NODE))
6335 parse_int_node(fs, db, cd, dx_dir, buf);
6336 }
6337#endif /* ENABLE_HTREE */
6338 if (offset != fs->blocksize) {
6339 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
6340 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
6341 dirent->rec_len = cd->pctx.num;
6342 dir_modified++;
6343 }
6344 }
6345 if (dir_modified) {
6346 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
6347 if (cd->pctx.errcode) {
6348 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
6349 &cd->pctx))
6350 goto abort_free_dict;
6351 }
6352 ext2fs_mark_changed(fs);
6353 }
6354 dict_free_nodes(&de_dict);
6355 return 0;
6356abort_free_dict:
6357 dict_free_nodes(&de_dict);
6358 ctx->flags |= E2F_FLAG_ABORT;
6359 return DIRENT_ABORT;
6360}
6361
6362/*
6363 * This function is called to deallocate a block, and is an interator
6364 * functioned called by deallocate inode via ext2fs_iterate_block().
6365 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006366static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006367 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6368 blk_t ref_block FSCK_ATTR((unused)),
6369 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006370 void *priv_data)
6371{
6372 e2fsck_t ctx = (e2fsck_t) priv_data;
6373
6374 if (HOLE_BLKADDR(*block_nr))
6375 return 0;
6376 if ((*block_nr < fs->super->s_first_data_block) ||
6377 (*block_nr >= fs->super->s_blocks_count))
6378 return 0;
6379 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6380 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6381 return 0;
6382}
6383
6384/*
6385 * This fuction deallocates an inode
6386 */
6387static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
6388{
6389 ext2_filsys fs = ctx->fs;
6390 struct ext2_inode inode;
6391 struct problem_context pctx;
6392 __u32 count;
6393
6394 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
6395 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
6396 inode.i_links_count = 0;
6397 inode.i_dtime = time(0);
6398 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
6399 clear_problem_context(&pctx);
6400 pctx.ino = ino;
6401
6402 /*
6403 * Fix up the bitmaps...
6404 */
6405 e2fsck_read_bitmaps(ctx);
6406 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6407 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6408 if (ctx->inode_bad_map)
6409 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6410 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6411
6412 if (inode.i_file_acl &&
6413 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6414 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6415 block_buf, -1, &count);
6416 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6417 pctx.errcode = 0;
6418 count = 1;
6419 }
6420 if (pctx.errcode) {
6421 pctx.blk = inode.i_file_acl;
6422 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
6423 ctx->flags |= E2F_FLAG_ABORT;
6424 return;
6425 }
6426 if (count == 0) {
6427 ext2fs_unmark_block_bitmap(ctx->block_found_map,
6428 inode.i_file_acl);
6429 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
6430 }
6431 inode.i_file_acl = 0;
6432 }
6433
6434 if (!ext2fs_inode_has_valid_blocks(&inode))
6435 return;
6436
6437 if (LINUX_S_ISREG(inode.i_mode) &&
6438 (inode.i_size_high || inode.i_size & 0x80000000UL))
6439 ctx->large_files--;
6440
6441 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6442 deallocate_inode_block, ctx);
6443 if (pctx.errcode) {
6444 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
6445 ctx->flags |= E2F_FLAG_ABORT;
6446 return;
6447 }
6448}
6449
6450/*
6451 * This fuction clears the htree flag on an inode
6452 */
6453static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
6454{
6455 struct ext2_inode inode;
6456
6457 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
6458 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
6459 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
6460 if (ctx->dirs_to_hash)
6461 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6462}
6463
6464
6465static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
6466 ext2_ino_t ino, char *buf)
6467{
6468 ext2_filsys fs = ctx->fs;
6469 struct ext2_inode inode;
6470 int inode_modified = 0;
6471 int not_fixed = 0;
6472 unsigned char *frag, *fsize;
6473 struct problem_context pctx;
6474 int problem = 0;
6475
6476 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
6477
6478 clear_problem_context(&pctx);
6479 pctx.ino = ino;
6480 pctx.dir = dir;
6481 pctx.inode = &inode;
6482
6483 if (inode.i_file_acl &&
6484 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
6485 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
6486 inode.i_file_acl = 0;
Rob Landley7c94bed2006-05-03 21:58:45 +00006487#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006488 /*
6489 * This is a special kludge to deal with long symlinks
6490 * on big endian systems. i_blocks had already been
6491 * decremented earlier in pass 1, but since i_file_acl
6492 * hadn't yet been cleared, ext2fs_read_inode()
6493 * assumed that the file was short symlink and would
6494 * not have byte swapped i_block[0]. Hence, we have
6495 * to byte-swap it here.
6496 */
6497 if (LINUX_S_ISLNK(inode.i_mode) &&
6498 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
6499 (inode.i_blocks == fs->blocksize >> 9))
6500 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
6501#endif
6502 inode_modified++;
6503 } else
6504 not_fixed++;
6505
6506 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
6507 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
6508 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
6509 !(LINUX_S_ISSOCK(inode.i_mode)))
6510 problem = PR_2_BAD_MODE;
6511 else if (LINUX_S_ISCHR(inode.i_mode)
6512 && !e2fsck_pass1_check_device_inode(fs, &inode))
6513 problem = PR_2_BAD_CHAR_DEV;
6514 else if (LINUX_S_ISBLK(inode.i_mode)
6515 && !e2fsck_pass1_check_device_inode(fs, &inode))
6516 problem = PR_2_BAD_BLOCK_DEV;
6517 else if (LINUX_S_ISFIFO(inode.i_mode)
6518 && !e2fsck_pass1_check_device_inode(fs, &inode))
6519 problem = PR_2_BAD_FIFO;
6520 else if (LINUX_S_ISSOCK(inode.i_mode)
6521 && !e2fsck_pass1_check_device_inode(fs, &inode))
6522 problem = PR_2_BAD_SOCKET;
6523 else if (LINUX_S_ISLNK(inode.i_mode)
6524 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
6525 problem = PR_2_INVALID_SYMLINK;
6526 }
6527
6528 if (problem) {
6529 if (fix_problem(ctx, problem, &pctx)) {
6530 deallocate_inode(ctx, ino, 0);
6531 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6532 return 0;
6533 return 1;
6534 } else
6535 not_fixed++;
6536 problem = 0;
6537 }
6538
6539 if (inode.i_faddr) {
6540 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
6541 inode.i_faddr = 0;
6542 inode_modified++;
6543 } else
6544 not_fixed++;
6545 }
6546
6547 switch (fs->super->s_creator_os) {
6548 case EXT2_OS_LINUX:
6549 frag = &inode.osd2.linux2.l_i_frag;
6550 fsize = &inode.osd2.linux2.l_i_fsize;
6551 break;
6552 case EXT2_OS_HURD:
6553 frag = &inode.osd2.hurd2.h_i_frag;
6554 fsize = &inode.osd2.hurd2.h_i_fsize;
6555 break;
6556 case EXT2_OS_MASIX:
6557 frag = &inode.osd2.masix2.m_i_frag;
6558 fsize = &inode.osd2.masix2.m_i_fsize;
6559 break;
6560 default:
6561 frag = fsize = 0;
6562 }
6563 if (frag && *frag) {
6564 pctx.num = *frag;
6565 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
6566 *frag = 0;
6567 inode_modified++;
6568 } else
6569 not_fixed++;
6570 pctx.num = 0;
6571 }
6572 if (fsize && *fsize) {
6573 pctx.num = *fsize;
6574 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
6575 *fsize = 0;
6576 inode_modified++;
6577 } else
6578 not_fixed++;
6579 pctx.num = 0;
6580 }
6581
6582 if (inode.i_file_acl &&
6583 ((inode.i_file_acl < fs->super->s_first_data_block) ||
6584 (inode.i_file_acl >= fs->super->s_blocks_count))) {
6585 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
6586 inode.i_file_acl = 0;
6587 inode_modified++;
6588 } else
6589 not_fixed++;
6590 }
6591 if (inode.i_dir_acl &&
6592 LINUX_S_ISDIR(inode.i_mode)) {
6593 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
6594 inode.i_dir_acl = 0;
6595 inode_modified++;
6596 } else
6597 not_fixed++;
6598 }
6599
6600 if (inode_modified)
6601 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
6602 if (!not_fixed)
6603 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6604 return 0;
6605}
6606
6607
6608/*
6609 * allocate_dir_block --- this function allocates a new directory
6610 * block for a particular inode; this is done if a directory has
6611 * a "hole" in it, or if a directory has a illegal block number
6612 * that was zeroed out and now needs to be replaced.
6613 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006614static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006615 struct problem_context *pctx)
6616{
6617 ext2_filsys fs = ctx->fs;
6618 blk_t blk;
6619 char *block;
6620 struct ext2_inode inode;
6621
6622 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
6623 return 1;
6624
6625 /*
6626 * Read the inode and block bitmaps in; we'll be messing with
6627 * them.
6628 */
6629 e2fsck_read_bitmaps(ctx);
6630
6631 /*
6632 * First, find a free block
6633 */
6634 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6635 if (pctx->errcode) {
6636 pctx->str = "ext2fs_new_block";
6637 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6638 return 1;
6639 }
6640 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6641 ext2fs_mark_block_bitmap(fs->block_map, blk);
6642 ext2fs_mark_bb_dirty(fs);
6643
6644 /*
6645 * Now let's create the actual data block for the inode
6646 */
6647 if (db->blockcnt)
6648 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
6649 else
6650 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
6651 EXT2_ROOT_INO, &block);
6652
6653 if (pctx->errcode) {
6654 pctx->str = "ext2fs_new_dir_block";
6655 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6656 return 1;
6657 }
6658
6659 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
6660 ext2fs_free_mem(&block);
6661 if (pctx->errcode) {
6662 pctx->str = "ext2fs_write_dir_block";
6663 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6664 return 1;
6665 }
6666
6667 /*
6668 * Update the inode block count
6669 */
6670 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
6671 inode.i_blocks += fs->blocksize / 512;
6672 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
6673 inode.i_size = (db->blockcnt+1) * fs->blocksize;
6674 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
6675
6676 /*
6677 * Finally, update the block pointers for the inode
6678 */
6679 db->blk = blk;
6680 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
6681 0, update_dir_block, db);
6682 if (pctx->errcode) {
6683 pctx->str = "ext2fs_block_iterate";
6684 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6685 return 1;
6686 }
6687
6688 return 0;
6689}
6690
6691/*
6692 * This is a helper function for allocate_dir_block().
6693 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006694static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006695 blk_t *block_nr,
6696 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006697 blk_t ref_block FSCK_ATTR((unused)),
6698 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006699 void *priv_data)
6700{
6701 struct ext2_db_entry *db;
6702
6703 db = (struct ext2_db_entry *) priv_data;
6704 if (db->blockcnt == (int) blockcnt) {
6705 *block_nr = db->blk;
6706 return BLOCK_CHANGED;
6707 }
6708 return 0;
6709}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006710
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006711/*
6712 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
6713 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006714 * Pass #3 assures that all directories are connected to the
6715 * filesystem tree, using the following algorithm:
6716 *
6717 * First, the root directory is checked to make sure it exists; if
6718 * not, e2fsck will offer to create a new one. It is then marked as
6719 * "done".
6720 *
6721 * Then, pass3 interates over all directory inodes; for each directory
6722 * it attempts to trace up the filesystem tree, using dirinfo.parent
6723 * until it reaches a directory which has been marked "done". If it
6724 * can not do so, then the directory must be disconnected, and e2fsck
6725 * will offer to reconnect it to /lost+found. While it is chasing
6726 * parent pointers up the filesystem tree, if pass3 sees a directory
6727 * twice, then it has detected a filesystem loop, and it will again
6728 * offer to reconnect the directory to /lost+found in to break the
6729 * filesystem loop.
6730 *
6731 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
6732 * reconnect inodes to /lost+found; this subroutine is also used by
6733 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
6734 * is responsible for creating /lost+found if it does not exist.
6735 *
6736 * Pass 3 frees the following data structures:
6737 * - The dirinfo directory information cache.
6738 */
6739
6740static void check_root(e2fsck_t ctx);
6741static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6742 struct problem_context *pctx);
6743static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
6744
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006745static ext2fs_inode_bitmap inode_loop_detect;
6746static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006747
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006748static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006749{
6750 ext2_filsys fs = ctx->fs;
6751 int i;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006752 struct problem_context pctx;
6753 struct dir_info *dir;
6754 unsigned long maxdirs, count;
6755
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006756 clear_problem_context(&pctx);
6757
Rob Landley3e72c592006-04-06 22:49:04 +00006758 /* Pass 3 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006759
6760 if (!(ctx->options & E2F_OPT_PREEN))
6761 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
6762
6763 /*
6764 * Allocate some bitmaps to do loop detection.
6765 */
6766 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
6767 &inode_done_map);
6768 if (pctx.errcode) {
6769 pctx.num = 2;
6770 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
6771 ctx->flags |= E2F_FLAG_ABORT;
6772 goto abort_exit;
6773 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006774 check_root(ctx);
6775 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6776 goto abort_exit;
6777
6778 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
6779
6780 maxdirs = e2fsck_get_num_dirinfo(ctx);
6781 count = 1;
6782
6783 if (ctx->progress)
6784 if ((ctx->progress)(ctx, 3, 0, maxdirs))
6785 goto abort_exit;
6786
6787 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
6788 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6789 goto abort_exit;
6790 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
6791 goto abort_exit;
6792 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
6793 if (check_directory(ctx, dir, &pctx))
6794 goto abort_exit;
6795 }
6796
6797 /*
6798 * Force the creation of /lost+found if not present
6799 */
6800 if ((ctx->flags & E2F_OPT_READONLY) == 0)
6801 e2fsck_get_lost_and_found(ctx, 1);
6802
6803 /*
6804 * If there are any directories that need to be indexed or
6805 * optimized, do it here.
6806 */
6807 e2fsck_rehash_directories(ctx);
6808
6809abort_exit:
6810 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00006811 ext2fs_free_inode_bitmap(inode_loop_detect);
6812 inode_loop_detect = 0;
6813 ext2fs_free_inode_bitmap(inode_done_map);
6814 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006815}
6816
6817/*
6818 * This makes sure the root inode is present; if not, we ask if the
6819 * user wants us to create it. Not creating it is a fatal error.
6820 */
6821static void check_root(e2fsck_t ctx)
6822{
6823 ext2_filsys fs = ctx->fs;
6824 blk_t blk;
6825 struct ext2_inode inode;
6826 char * block;
6827 struct problem_context pctx;
6828
6829 clear_problem_context(&pctx);
6830
6831 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
6832 /*
6833 * If the root inode is not a directory, die here. The
6834 * user must have answered 'no' in pass1 when we
6835 * offered to clear it.
6836 */
6837 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6838 EXT2_ROOT_INO))) {
6839 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
6840 ctx->flags |= E2F_FLAG_ABORT;
6841 }
6842 return;
6843 }
6844
6845 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
6846 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
6847 ctx->flags |= E2F_FLAG_ABORT;
6848 return;
6849 }
6850
6851 e2fsck_read_bitmaps(ctx);
6852
6853 /*
6854 * First, find a free block
6855 */
6856 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6857 if (pctx.errcode) {
6858 pctx.str = "ext2fs_new_block";
6859 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6860 ctx->flags |= E2F_FLAG_ABORT;
6861 return;
6862 }
6863 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6864 ext2fs_mark_block_bitmap(fs->block_map, blk);
6865 ext2fs_mark_bb_dirty(fs);
6866
6867 /*
6868 * Now let's create the actual data block for the inode
6869 */
6870 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
6871 &block);
6872 if (pctx.errcode) {
6873 pctx.str = "ext2fs_new_dir_block";
6874 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6875 ctx->flags |= E2F_FLAG_ABORT;
6876 return;
6877 }
6878
6879 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
6880 if (pctx.errcode) {
6881 pctx.str = "ext2fs_write_dir_block";
6882 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6883 ctx->flags |= E2F_FLAG_ABORT;
6884 return;
6885 }
6886 ext2fs_free_mem(&block);
6887
6888 /*
6889 * Set up the inode structure
6890 */
6891 memset(&inode, 0, sizeof(inode));
6892 inode.i_mode = 040755;
6893 inode.i_size = fs->blocksize;
6894 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
6895 inode.i_links_count = 2;
6896 inode.i_blocks = fs->blocksize / 512;
6897 inode.i_block[0] = blk;
6898
6899 /*
6900 * Write out the inode.
6901 */
6902 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
6903 if (pctx.errcode) {
6904 pctx.str = "ext2fs_write_inode";
6905 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6906 ctx->flags |= E2F_FLAG_ABORT;
6907 return;
6908 }
6909
6910 /*
6911 * Miscellaneous bookkeeping...
6912 */
6913 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
6914 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
6915 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
6916
6917 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
6918 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
6919 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
6920 ext2fs_mark_ib_dirty(fs);
6921}
6922
6923/*
6924 * This subroutine is responsible for making sure that a particular
6925 * directory is connected to the root; if it isn't we trace it up as
6926 * far as we can go, and then offer to connect the resulting parent to
6927 * the lost+found. We have to do loop detection; if we ever discover
6928 * a loop, we treat that as a disconnected directory and offer to
6929 * reparent it to lost+found.
6930 *
6931 * However, loop detection is expensive, because for very large
6932 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
6933 * is non-trivial. Loops in filesystems are also a rare error case,
6934 * and we shouldn't optimize for error cases. So we try two passes of
6935 * the algorithm. The first time, we ignore loop detection and merely
6936 * increment a counter; if the counter exceeds some extreme threshold,
6937 * then we try again with the loop detection bitmap enabled.
6938 */
6939static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6940 struct problem_context *pctx)
6941{
6942 ext2_filsys fs = ctx->fs;
6943 struct dir_info *p = dir;
6944 int loop_pass = 0, parent_count = 0;
6945
6946 if (!p)
6947 return 0;
6948
6949 while (1) {
6950 /*
6951 * Mark this inode as being "done"; by the time we
6952 * return from this function, the inode we either be
6953 * verified as being connected to the directory tree,
6954 * or we will have offered to reconnect this to
6955 * lost+found.
6956 *
6957 * If it was marked done already, then we've reached a
6958 * parent we've already checked.
6959 */
6960 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
6961 break;
6962
6963 /*
6964 * If this directory doesn't have a parent, or we've
6965 * seen the parent once already, then offer to
6966 * reparent it to lost+found
6967 */
6968 if (!p->parent ||
6969 (loop_pass &&
6970 (ext2fs_test_inode_bitmap(inode_loop_detect,
6971 p->parent)))) {
6972 pctx->ino = p->ino;
6973 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
6974 if (e2fsck_reconnect_file(ctx, pctx->ino))
6975 ext2fs_unmark_valid(fs);
6976 else {
6977 p = e2fsck_get_dir_info(ctx, pctx->ino);
6978 p->parent = ctx->lost_and_found;
6979 fix_dotdot(ctx, p, ctx->lost_and_found);
6980 }
6981 }
6982 break;
6983 }
6984 p = e2fsck_get_dir_info(ctx, p->parent);
6985 if (!p) {
6986 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
6987 return 0;
6988 }
6989 if (loop_pass) {
6990 ext2fs_mark_inode_bitmap(inode_loop_detect,
6991 p->ino);
6992 } else if (parent_count++ > 2048) {
6993 /*
6994 * If we've run into a path depth that's
6995 * greater than 2048, try again with the inode
6996 * loop bitmap turned on and start from the
6997 * top.
6998 */
6999 loop_pass = 1;
7000 if (inode_loop_detect)
7001 ext2fs_clear_inode_bitmap(inode_loop_detect);
7002 else {
7003 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
7004 if (pctx->errcode) {
7005 pctx->num = 1;
7006 fix_problem(ctx,
7007 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
7008 ctx->flags |= E2F_FLAG_ABORT;
7009 return -1;
7010 }
7011 }
7012 p = dir;
7013 }
7014 }
7015
7016 /*
7017 * Make sure that .. and the parent directory are the same;
7018 * offer to fix it if not.
7019 */
7020 if (dir->parent != dir->dotdot) {
7021 pctx->ino = dir->ino;
7022 pctx->ino2 = dir->dotdot;
7023 pctx->dir = dir->parent;
7024 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
7025 fix_dotdot(ctx, dir, dir->parent);
7026 }
7027 return 0;
7028}
7029
7030/*
7031 * This routine gets the lost_and_found inode, making it a directory
7032 * if necessary
7033 */
7034ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
7035{
7036 ext2_filsys fs = ctx->fs;
7037 ext2_ino_t ino;
7038 blk_t blk;
7039 errcode_t retval;
7040 struct ext2_inode inode;
7041 char * block;
7042 static const char name[] = "lost+found";
7043 struct problem_context pctx;
7044 struct dir_info *dirinfo;
7045
7046 if (ctx->lost_and_found)
7047 return ctx->lost_and_found;
7048
7049 clear_problem_context(&pctx);
7050
7051 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
7052 sizeof(name)-1, 0, &ino);
7053 if (retval && !fix)
7054 return 0;
7055 if (!retval) {
7056 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
7057 ctx->lost_and_found = ino;
7058 return ino;
7059 }
7060
7061 /* Lost+found isn't a directory! */
7062 if (!fix)
7063 return 0;
7064 pctx.ino = ino;
7065 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
7066 return 0;
7067
7068 /* OK, unlink the old /lost+found file. */
7069 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
7070 if (pctx.errcode) {
7071 pctx.str = "ext2fs_unlink";
7072 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7073 return 0;
7074 }
7075 dirinfo = e2fsck_get_dir_info(ctx, ino);
7076 if (dirinfo)
7077 dirinfo->parent = 0;
7078 e2fsck_adjust_inode_count(ctx, ino, -1);
7079 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
7080 pctx.errcode = retval;
7081 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
7082 }
7083 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
7084 return 0;
7085
7086 /*
7087 * Read the inode and block bitmaps in; we'll be messing with
7088 * them.
7089 */
7090 e2fsck_read_bitmaps(ctx);
7091
7092 /*
7093 * First, find a free block
7094 */
7095 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7096 if (retval) {
7097 pctx.errcode = retval;
7098 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
7099 return 0;
7100 }
7101 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7102 ext2fs_block_alloc_stats(fs, blk, +1);
7103
7104 /*
7105 * Next find a free inode.
7106 */
7107 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
7108 ctx->inode_used_map, &ino);
7109 if (retval) {
7110 pctx.errcode = retval;
7111 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
7112 return 0;
7113 }
7114 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
7115 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
7116 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
7117
7118 /*
7119 * Now let's create the actual data block for the inode
7120 */
7121 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
7122 if (retval) {
7123 pctx.errcode = retval;
7124 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
7125 return 0;
7126 }
7127
7128 retval = ext2fs_write_dir_block(fs, blk, block);
7129 ext2fs_free_mem(&block);
7130 if (retval) {
7131 pctx.errcode = retval;
7132 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
7133 return 0;
7134 }
7135
7136 /*
7137 * Set up the inode structure
7138 */
7139 memset(&inode, 0, sizeof(inode));
7140 inode.i_mode = 040700;
7141 inode.i_size = fs->blocksize;
7142 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
7143 inode.i_links_count = 2;
7144 inode.i_blocks = fs->blocksize / 512;
7145 inode.i_block[0] = blk;
7146
7147 /*
7148 * Next, write out the inode.
7149 */
7150 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
7151 if (pctx.errcode) {
7152 pctx.str = "ext2fs_write_inode";
7153 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7154 return 0;
7155 }
7156 /*
7157 * Finally, create the directory link
7158 */
7159 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
7160 if (pctx.errcode) {
7161 pctx.str = "ext2fs_link";
7162 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7163 return 0;
7164 }
7165
7166 /*
7167 * Miscellaneous bookkeeping that needs to be kept straight.
7168 */
7169 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
7170 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
7171 ext2fs_icount_store(ctx->inode_count, ino, 2);
7172 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
7173 ctx->lost_and_found = ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007174 return ino;
7175}
7176
7177/*
7178 * This routine will connect a file to lost+found
7179 */
7180int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
7181{
7182 ext2_filsys fs = ctx->fs;
7183 errcode_t retval;
7184 char name[80];
7185 struct problem_context pctx;
7186 struct ext2_inode inode;
7187 int file_type = 0;
7188
7189 clear_problem_context(&pctx);
7190 pctx.ino = ino;
7191
7192 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
7193 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
7194 ctx->bad_lost_and_found++;
7195 }
7196 if (ctx->bad_lost_and_found) {
7197 fix_problem(ctx, PR_3_NO_LPF, &pctx);
7198 return 1;
7199 }
7200
7201 sprintf(name, "#%u", ino);
7202 if (ext2fs_read_inode(fs, ino, &inode) == 0)
7203 file_type = ext2_file_type(inode.i_mode);
7204 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
7205 if (retval == EXT2_ET_DIR_NO_SPACE) {
7206 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
7207 return 1;
7208 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
7209 1, 0);
7210 if (retval) {
7211 pctx.errcode = retval;
7212 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
7213 return 1;
7214 }
7215 retval = ext2fs_link(fs, ctx->lost_and_found, name,
7216 ino, file_type);
7217 }
7218 if (retval) {
7219 pctx.errcode = retval;
7220 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
7221 return 1;
7222 }
7223 e2fsck_adjust_inode_count(ctx, ino, 1);
7224
7225 return 0;
7226}
7227
7228/*
7229 * Utility routine to adjust the inode counts on an inode.
7230 */
7231errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
7232{
7233 ext2_filsys fs = ctx->fs;
7234 errcode_t retval;
7235 struct ext2_inode inode;
7236
7237 if (!ino)
7238 return 0;
7239
7240 retval = ext2fs_read_inode(fs, ino, &inode);
7241 if (retval)
7242 return retval;
7243
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007244 if (adj == 1) {
7245 ext2fs_icount_increment(ctx->inode_count, ino, 0);
7246 if (inode.i_links_count == (__u16) ~0)
7247 return 0;
7248 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
7249 inode.i_links_count++;
7250 } else if (adj == -1) {
7251 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
7252 if (inode.i_links_count == 0)
7253 return 0;
7254 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
7255 inode.i_links_count--;
7256 }
7257
7258 retval = ext2fs_write_inode(fs, ino, &inode);
7259 if (retval)
7260 return retval;
7261
7262 return 0;
7263}
7264
7265/*
7266 * Fix parent --- this routine fixes up the parent of a directory.
7267 */
7268struct fix_dotdot_struct {
7269 ext2_filsys fs;
7270 ext2_ino_t parent;
7271 int done;
7272 e2fsck_t ctx;
7273};
7274
7275static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007276 int offset FSCK_ATTR((unused)),
7277 int blocksize FSCK_ATTR((unused)),
7278 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007279 void *priv_data)
7280{
7281 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
7282 errcode_t retval;
7283 struct problem_context pctx;
7284
7285 if ((dirent->name_len & 0xFF) != 2)
7286 return 0;
7287 if (strncmp(dirent->name, "..", 2))
7288 return 0;
7289
7290 clear_problem_context(&pctx);
7291
7292 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
7293 if (retval) {
7294 pctx.errcode = retval;
7295 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7296 }
7297 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
7298 if (retval) {
7299 pctx.errcode = retval;
7300 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7301 }
7302 dirent->inode = fp->parent;
7303
7304 fp->done++;
7305 return DIRENT_ABORT | DIRENT_CHANGED;
7306}
7307
7308static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
7309{
7310 ext2_filsys fs = ctx->fs;
7311 errcode_t retval;
7312 struct fix_dotdot_struct fp;
7313 struct problem_context pctx;
7314
7315 fp.fs = fs;
7316 fp.parent = parent;
7317 fp.done = 0;
7318 fp.ctx = ctx;
7319
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007320 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
7321 0, fix_dotdot_proc, &fp);
7322 if (retval || !fp.done) {
7323 clear_problem_context(&pctx);
7324 pctx.ino = dir->ino;
7325 pctx.errcode = retval;
7326 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
7327 PR_3_FIX_PARENT_NOFIND, &pctx);
7328 ext2fs_unmark_valid(fs);
7329 }
7330 dir->dotdot = parent;
7331
7332 return;
7333}
7334
7335/*
7336 * These routines are responsible for expanding a /lost+found if it is
7337 * too small.
7338 */
7339
7340struct expand_dir_struct {
7341 int num;
7342 int guaranteed_size;
7343 int newblocks;
7344 int last_block;
7345 errcode_t err;
7346 e2fsck_t ctx;
7347};
7348
7349static int expand_dir_proc(ext2_filsys fs,
7350 blk_t *blocknr,
7351 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007352 blk_t ref_block FSCK_ATTR((unused)),
7353 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007354 void *priv_data)
7355{
7356 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
7357 blk_t new_blk;
7358 static blk_t last_blk = 0;
7359 char *block;
7360 errcode_t retval;
7361 e2fsck_t ctx;
7362
7363 ctx = es->ctx;
7364
7365 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
7366 return BLOCK_ABORT;
7367
7368 if (blockcnt > 0)
7369 es->last_block = blockcnt;
7370 if (*blocknr) {
7371 last_blk = *blocknr;
7372 return 0;
7373 }
7374 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
7375 &new_blk);
7376 if (retval) {
7377 es->err = retval;
7378 return BLOCK_ABORT;
7379 }
7380 if (blockcnt > 0) {
7381 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
7382 if (retval) {
7383 es->err = retval;
7384 return BLOCK_ABORT;
7385 }
7386 es->num--;
7387 retval = ext2fs_write_dir_block(fs, new_blk, block);
7388 } else {
7389 retval = ext2fs_get_mem(fs->blocksize, &block);
7390 if (retval) {
7391 es->err = retval;
7392 return BLOCK_ABORT;
7393 }
7394 memset(block, 0, fs->blocksize);
7395 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
7396 }
7397 if (retval) {
7398 es->err = retval;
7399 return BLOCK_ABORT;
7400 }
7401 ext2fs_free_mem(&block);
7402 *blocknr = new_blk;
7403 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
7404 ext2fs_block_alloc_stats(fs, new_blk, +1);
7405 es->newblocks++;
7406
7407 if (es->num == 0)
7408 return (BLOCK_CHANGED | BLOCK_ABORT);
7409 else
7410 return BLOCK_CHANGED;
7411}
7412
7413errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
7414 int num, int guaranteed_size)
7415{
7416 ext2_filsys fs = ctx->fs;
7417 errcode_t retval;
7418 struct expand_dir_struct es;
7419 struct ext2_inode inode;
7420
7421 if (!(fs->flags & EXT2_FLAG_RW))
7422 return EXT2_ET_RO_FILSYS;
7423
7424 /*
7425 * Read the inode and block bitmaps in; we'll be messing with
7426 * them.
7427 */
7428 e2fsck_read_bitmaps(ctx);
7429
7430 retval = ext2fs_check_directory(fs, dir);
7431 if (retval)
7432 return retval;
7433
7434 es.num = num;
7435 es.guaranteed_size = guaranteed_size;
7436 es.last_block = 0;
7437 es.err = 0;
7438 es.newblocks = 0;
7439 es.ctx = ctx;
7440
7441 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
7442 0, expand_dir_proc, &es);
7443
7444 if (es.err)
7445 return es.err;
7446
7447 /*
7448 * Update the size and block count fields in the inode.
7449 */
7450 retval = ext2fs_read_inode(fs, dir, &inode);
7451 if (retval)
7452 return retval;
7453
7454 inode.i_size = (es.last_block + 1) * fs->blocksize;
7455 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
7456
7457 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
7458
7459 return 0;
7460}
7461
7462/*
7463 * pass4.c -- pass #4 of e2fsck: Check reference counts
7464 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007465 * Pass 4 frees the following data structures:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007466 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
7467 */
7468
7469/*
7470 * This routine is called when an inode is not connected to the
7471 * directory tree.
7472 *
7473 * This subroutine returns 1 then the caller shouldn't bother with the
7474 * rest of the pass 4 tests.
7475 */
7476static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
7477{
7478 ext2_filsys fs = ctx->fs;
7479 struct ext2_inode inode;
7480 struct problem_context pctx;
7481
7482 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
7483 clear_problem_context(&pctx);
7484 pctx.ino = i;
7485 pctx.inode = &inode;
7486
7487 /*
7488 * Offer to delete any zero-length files that does not have
7489 * blocks. If there is an EA block, it might have useful
7490 * information, so we won't prompt to delete it, but let it be
7491 * reconnected to lost+found.
7492 */
7493 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
7494 LINUX_S_ISDIR(inode.i_mode))) {
7495 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
7496 ext2fs_icount_store(ctx->inode_link_info, i, 0);
7497 inode.i_links_count = 0;
7498 inode.i_dtime = time(0);
7499 e2fsck_write_inode(ctx, i, &inode,
7500 "disconnect_inode");
7501 /*
7502 * Fix up the bitmaps...
7503 */
7504 e2fsck_read_bitmaps(ctx);
7505 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
7506 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
7507 ext2fs_inode_alloc_stats2(fs, i, -1,
7508 LINUX_S_ISDIR(inode.i_mode));
7509 return 0;
7510 }
7511 }
7512
7513 /*
7514 * Prompt to reconnect.
7515 */
7516 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
7517 if (e2fsck_reconnect_file(ctx, i))
7518 ext2fs_unmark_valid(fs);
7519 } else {
7520 /*
7521 * If we don't attach the inode, then skip the
7522 * i_links_test since there's no point in trying to
7523 * force i_links_count to zero.
7524 */
7525 ext2fs_unmark_valid(fs);
7526 return 1;
7527 }
7528 return 0;
7529}
7530
7531
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007532static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007533{
7534 ext2_filsys fs = ctx->fs;
7535 ext2_ino_t i;
7536 struct ext2_inode inode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007537 struct problem_context pctx;
7538 __u16 link_count, link_counted;
7539 char *buf = 0;
7540 int group, maxgroup;
7541
Rob Landley3e72c592006-04-06 22:49:04 +00007542 /* Pass 4 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007543
7544 clear_problem_context(&pctx);
7545
7546 if (!(ctx->options & E2F_OPT_PREEN))
7547 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
7548
7549 group = 0;
7550 maxgroup = fs->group_desc_count;
7551 if (ctx->progress)
7552 if ((ctx->progress)(ctx, 4, 0, maxgroup))
7553 return;
7554
7555 for (i=1; i <= fs->super->s_inodes_count; i++) {
7556 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7557 return;
7558 if ((i % fs->super->s_inodes_per_group) == 0) {
7559 group++;
7560 if (ctx->progress)
7561 if ((ctx->progress)(ctx, 4, group, maxgroup))
7562 return;
7563 }
7564 if (i == EXT2_BAD_INO ||
7565 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
7566 continue;
7567 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
7568 (ctx->inode_imagic_map &&
Rob Landleyd8f66012006-05-05 17:29:09 +00007569 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007570 continue;
7571 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
7572 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
7573 if (link_counted == 0) {
7574 if (!buf)
7575 buf = e2fsck_allocate_memory(ctx,
7576 fs->blocksize, "bad_inode buffer");
7577 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
7578 continue;
7579 if (disconnect_inode(ctx, i))
7580 continue;
7581 ext2fs_icount_fetch(ctx->inode_link_info, i,
7582 &link_count);
7583 ext2fs_icount_fetch(ctx->inode_count, i,
7584 &link_counted);
7585 }
7586 if (link_counted != link_count) {
7587 e2fsck_read_inode(ctx, i, &inode, "pass4");
7588 pctx.ino = i;
7589 pctx.inode = &inode;
7590 if (link_count != inode.i_links_count) {
7591 pctx.num = link_count;
7592 fix_problem(ctx,
7593 PR_4_INCONSISTENT_COUNT, &pctx);
7594 }
7595 pctx.num = link_counted;
7596 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
7597 inode.i_links_count = link_counted;
7598 e2fsck_write_inode(ctx, i, &inode, "pass4");
7599 }
7600 }
7601 }
7602 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
7603 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007604 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
7605 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00007606 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007607}
7608
7609/*
7610 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007611 */
7612
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007613#define NO_BLK ((blk_t) -1)
7614
7615static void print_bitmap_problem(e2fsck_t ctx, int problem,
7616 struct problem_context *pctx)
7617{
7618 switch (problem) {
7619 case PR_5_BLOCK_UNUSED:
7620 if (pctx->blk == pctx->blk2)
7621 pctx->blk2 = 0;
7622 else
7623 problem = PR_5_BLOCK_RANGE_UNUSED;
7624 break;
7625 case PR_5_BLOCK_USED:
7626 if (pctx->blk == pctx->blk2)
7627 pctx->blk2 = 0;
7628 else
7629 problem = PR_5_BLOCK_RANGE_USED;
7630 break;
7631 case PR_5_INODE_UNUSED:
7632 if (pctx->ino == pctx->ino2)
7633 pctx->ino2 = 0;
7634 else
7635 problem = PR_5_INODE_RANGE_UNUSED;
7636 break;
7637 case PR_5_INODE_USED:
7638 if (pctx->ino == pctx->ino2)
7639 pctx->ino2 = 0;
7640 else
7641 problem = PR_5_INODE_RANGE_USED;
7642 break;
7643 }
7644 fix_problem(ctx, problem, pctx);
7645 pctx->blk = pctx->blk2 = NO_BLK;
7646 pctx->ino = pctx->ino2 = 0;
7647}
7648
7649static void check_block_bitmaps(e2fsck_t ctx)
7650{
7651 ext2_filsys fs = ctx->fs;
7652 blk_t i;
7653 int *free_array;
7654 int group = 0;
7655 unsigned int blocks = 0;
7656 unsigned int free_blocks = 0;
7657 int group_free = 0;
7658 int actual, bitmap;
7659 struct problem_context pctx;
7660 int problem, save_problem, fixit, had_problem;
7661 errcode_t retval;
7662
7663 clear_problem_context(&pctx);
7664 free_array = (int *) e2fsck_allocate_memory(ctx,
7665 fs->group_desc_count * sizeof(int), "free block count array");
7666
7667 if ((fs->super->s_first_data_block <
7668 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
7669 (fs->super->s_blocks_count-1 >
7670 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
7671 pctx.num = 1;
7672 pctx.blk = fs->super->s_first_data_block;
7673 pctx.blk2 = fs->super->s_blocks_count -1;
7674 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
7675 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
7676 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7677
7678 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7679 return;
7680 }
7681
7682 if ((fs->super->s_first_data_block <
7683 ext2fs_get_block_bitmap_start(fs->block_map)) ||
7684 (fs->super->s_blocks_count-1 >
7685 ext2fs_get_block_bitmap_end(fs->block_map))) {
7686 pctx.num = 2;
7687 pctx.blk = fs->super->s_first_data_block;
7688 pctx.blk2 = fs->super->s_blocks_count -1;
7689 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
7690 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
7691 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7692
7693 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7694 return;
7695 }
7696
7697redo_counts:
7698 had_problem = 0;
7699 save_problem = 0;
7700 pctx.blk = pctx.blk2 = NO_BLK;
7701 for (i = fs->super->s_first_data_block;
7702 i < fs->super->s_blocks_count;
7703 i++) {
7704 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
7705 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
7706
7707 if (actual == bitmap)
7708 goto do_counts;
7709
7710 if (!actual && bitmap) {
7711 /*
7712 * Block not used, but marked in use in the bitmap.
7713 */
7714 problem = PR_5_BLOCK_UNUSED;
7715 } else {
7716 /*
7717 * Block used, but not marked in use in the bitmap.
7718 */
7719 problem = PR_5_BLOCK_USED;
7720 }
7721 if (pctx.blk == NO_BLK) {
7722 pctx.blk = pctx.blk2 = i;
7723 save_problem = problem;
7724 } else {
7725 if ((problem == save_problem) &&
7726 (pctx.blk2 == i-1))
7727 pctx.blk2++;
7728 else {
7729 print_bitmap_problem(ctx, save_problem, &pctx);
7730 pctx.blk = pctx.blk2 = i;
7731 save_problem = problem;
7732 }
7733 }
7734 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7735 had_problem++;
7736
7737 do_counts:
7738 if (!bitmap) {
7739 group_free++;
7740 free_blocks++;
7741 }
7742 blocks ++;
7743 if ((blocks == fs->super->s_blocks_per_group) ||
7744 (i == fs->super->s_blocks_count-1)) {
7745 free_array[group] = group_free;
7746 group ++;
7747 blocks = 0;
7748 group_free = 0;
7749 if (ctx->progress)
7750 if ((ctx->progress)(ctx, 5, group,
7751 fs->group_desc_count*2))
7752 return;
7753 }
7754 }
7755 if (pctx.blk != NO_BLK)
7756 print_bitmap_problem(ctx, save_problem, &pctx);
7757 if (had_problem)
7758 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
7759 else
7760 fixit = -1;
7761 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7762
7763 if (fixit == 1) {
7764 ext2fs_free_block_bitmap(fs->block_map);
7765 retval = ext2fs_copy_bitmap(ctx->block_found_map,
7766 &fs->block_map);
7767 if (retval) {
7768 clear_problem_context(&pctx);
7769 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
7770 ctx->flags |= E2F_FLAG_ABORT;
7771 return;
7772 }
7773 ext2fs_set_bitmap_padding(fs->block_map);
7774 ext2fs_mark_bb_dirty(fs);
7775
7776 /* Redo the counts */
7777 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
7778 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7779 goto redo_counts;
7780 } else if (fixit == 0)
7781 ext2fs_unmark_valid(fs);
7782
7783 for (i = 0; i < fs->group_desc_count; i++) {
7784 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
7785 pctx.group = i;
7786 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
7787 pctx.blk2 = free_array[i];
7788
7789 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
7790 &pctx)) {
7791 fs->group_desc[i].bg_free_blocks_count =
7792 free_array[i];
7793 ext2fs_mark_super_dirty(fs);
7794 } else
7795 ext2fs_unmark_valid(fs);
7796 }
7797 }
7798 if (free_blocks != fs->super->s_free_blocks_count) {
7799 pctx.group = 0;
7800 pctx.blk = fs->super->s_free_blocks_count;
7801 pctx.blk2 = free_blocks;
7802
7803 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
7804 fs->super->s_free_blocks_count = free_blocks;
7805 ext2fs_mark_super_dirty(fs);
7806 } else
7807 ext2fs_unmark_valid(fs);
7808 }
7809 ext2fs_free_mem(&free_array);
7810}
7811
7812static void check_inode_bitmaps(e2fsck_t ctx)
7813{
7814 ext2_filsys fs = ctx->fs;
7815 ext2_ino_t i;
7816 unsigned int free_inodes = 0;
7817 int group_free = 0;
7818 int dirs_count = 0;
7819 int group = 0;
7820 unsigned int inodes = 0;
7821 int *free_array;
7822 int *dir_array;
7823 int actual, bitmap;
7824 errcode_t retval;
7825 struct problem_context pctx;
7826 int problem, save_problem, fixit, had_problem;
7827
7828 clear_problem_context(&pctx);
7829 free_array = (int *) e2fsck_allocate_memory(ctx,
7830 fs->group_desc_count * sizeof(int), "free inode count array");
7831
7832 dir_array = (int *) e2fsck_allocate_memory(ctx,
7833 fs->group_desc_count * sizeof(int), "directory count array");
7834
7835 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
7836 (fs->super->s_inodes_count >
7837 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
7838 pctx.num = 3;
7839 pctx.blk = 1;
7840 pctx.blk2 = fs->super->s_inodes_count;
7841 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
7842 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
7843 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7844
7845 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7846 return;
7847 }
7848 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
7849 (fs->super->s_inodes_count >
7850 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
7851 pctx.num = 4;
7852 pctx.blk = 1;
7853 pctx.blk2 = fs->super->s_inodes_count;
7854 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
7855 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
7856 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7857
7858 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7859 return;
7860 }
7861
7862redo_counts:
7863 had_problem = 0;
7864 save_problem = 0;
7865 pctx.ino = pctx.ino2 = 0;
7866 for (i = 1; i <= fs->super->s_inodes_count; i++) {
7867 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
7868 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
7869
7870 if (actual == bitmap)
7871 goto do_counts;
7872
7873 if (!actual && bitmap) {
7874 /*
7875 * Inode wasn't used, but marked in bitmap
7876 */
7877 problem = PR_5_INODE_UNUSED;
7878 } else /* if (actual && !bitmap) */ {
7879 /*
7880 * Inode used, but not in bitmap
7881 */
7882 problem = PR_5_INODE_USED;
7883 }
7884 if (pctx.ino == 0) {
7885 pctx.ino = pctx.ino2 = i;
7886 save_problem = problem;
7887 } else {
7888 if ((problem == save_problem) &&
7889 (pctx.ino2 == i-1))
7890 pctx.ino2++;
7891 else {
7892 print_bitmap_problem(ctx, save_problem, &pctx);
7893 pctx.ino = pctx.ino2 = i;
7894 save_problem = problem;
7895 }
7896 }
7897 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7898 had_problem++;
7899
7900do_counts:
7901 if (!bitmap) {
7902 group_free++;
7903 free_inodes++;
7904 } else {
7905 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
7906 dirs_count++;
7907 }
7908 inodes++;
7909 if ((inodes == fs->super->s_inodes_per_group) ||
7910 (i == fs->super->s_inodes_count)) {
7911 free_array[group] = group_free;
7912 dir_array[group] = dirs_count;
7913 group ++;
7914 inodes = 0;
7915 group_free = 0;
7916 dirs_count = 0;
7917 if (ctx->progress)
7918 if ((ctx->progress)(ctx, 5,
7919 group + fs->group_desc_count,
7920 fs->group_desc_count*2))
7921 return;
7922 }
7923 }
7924 if (pctx.ino)
7925 print_bitmap_problem(ctx, save_problem, &pctx);
7926
7927 if (had_problem)
7928 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
7929 else
7930 fixit = -1;
7931 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7932
7933 if (fixit == 1) {
7934 ext2fs_free_inode_bitmap(fs->inode_map);
7935 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
7936 &fs->inode_map);
7937 if (retval) {
7938 clear_problem_context(&pctx);
7939 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
7940 ctx->flags |= E2F_FLAG_ABORT;
7941 return;
7942 }
7943 ext2fs_set_bitmap_padding(fs->inode_map);
7944 ext2fs_mark_ib_dirty(fs);
7945
7946 /* redo counts */
7947 inodes = 0; free_inodes = 0; group_free = 0;
7948 dirs_count = 0; group = 0;
7949 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7950 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
7951 goto redo_counts;
7952 } else if (fixit == 0)
7953 ext2fs_unmark_valid(fs);
7954
7955 for (i = 0; i < fs->group_desc_count; i++) {
7956 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
7957 pctx.group = i;
7958 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
7959 pctx.ino2 = free_array[i];
7960 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
7961 &pctx)) {
7962 fs->group_desc[i].bg_free_inodes_count =
7963 free_array[i];
7964 ext2fs_mark_super_dirty(fs);
7965 } else
7966 ext2fs_unmark_valid(fs);
7967 }
7968 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
7969 pctx.group = i;
7970 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
7971 pctx.ino2 = dir_array[i];
7972
7973 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
7974 &pctx)) {
7975 fs->group_desc[i].bg_used_dirs_count =
7976 dir_array[i];
7977 ext2fs_mark_super_dirty(fs);
7978 } else
7979 ext2fs_unmark_valid(fs);
7980 }
7981 }
7982 if (free_inodes != fs->super->s_free_inodes_count) {
7983 pctx.group = -1;
7984 pctx.ino = fs->super->s_free_inodes_count;
7985 pctx.ino2 = free_inodes;
7986
7987 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
7988 fs->super->s_free_inodes_count = free_inodes;
7989 ext2fs_mark_super_dirty(fs);
7990 } else
7991 ext2fs_unmark_valid(fs);
7992 }
7993 ext2fs_free_mem(&free_array);
7994 ext2fs_free_mem(&dir_array);
7995}
7996
7997static void check_inode_end(e2fsck_t ctx)
7998{
7999 ext2_filsys fs = ctx->fs;
8000 ext2_ino_t end, save_inodes_count, i;
8001 struct problem_context pctx;
8002
8003 clear_problem_context(&pctx);
8004
8005 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
8006 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
8007 &save_inodes_count);
8008 if (pctx.errcode) {
8009 pctx.num = 1;
8010 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8011 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8012 return;
8013 }
8014 if (save_inodes_count == end)
8015 return;
8016
8017 for (i = save_inodes_count + 1; i <= end; i++) {
8018 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
8019 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
8020 for (i = save_inodes_count + 1; i <= end; i++)
8021 ext2fs_mark_inode_bitmap(fs->inode_map,
8022 i);
8023 ext2fs_mark_ib_dirty(fs);
8024 } else
8025 ext2fs_unmark_valid(fs);
8026 break;
8027 }
8028 }
8029
8030 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
8031 save_inodes_count, 0);
8032 if (pctx.errcode) {
8033 pctx.num = 2;
8034 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8035 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8036 return;
8037 }
8038}
8039
8040static void check_block_end(e2fsck_t ctx)
8041{
8042 ext2_filsys fs = ctx->fs;
8043 blk_t end, save_blocks_count, i;
8044 struct problem_context pctx;
8045
8046 clear_problem_context(&pctx);
8047
8048 end = fs->block_map->start +
8049 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
8050 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
8051 &save_blocks_count);
8052 if (pctx.errcode) {
8053 pctx.num = 3;
8054 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8055 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8056 return;
8057 }
8058 if (save_blocks_count == end)
8059 return;
8060
8061 for (i = save_blocks_count + 1; i <= end; i++) {
8062 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
8063 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
8064 for (i = save_blocks_count + 1; i <= end; i++)
8065 ext2fs_mark_block_bitmap(fs->block_map,
8066 i);
8067 ext2fs_mark_bb_dirty(fs);
8068 } else
8069 ext2fs_unmark_valid(fs);
8070 break;
8071 }
8072 }
8073
8074 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
8075 save_blocks_count, 0);
8076 if (pctx.errcode) {
8077 pctx.num = 4;
8078 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8079 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8080 return;
8081 }
8082}
8083
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008084static void e2fsck_pass5(e2fsck_t ctx)
8085{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008086 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008087
Rob Landley3e72c592006-04-06 22:49:04 +00008088 /* Pass 5 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008089
8090 clear_problem_context(&pctx);
8091
8092 if (!(ctx->options & E2F_OPT_PREEN))
8093 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
8094
8095 if (ctx->progress)
8096 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
8097 return;
8098
8099 e2fsck_read_bitmaps(ctx);
8100
8101 check_block_bitmaps(ctx);
8102 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8103 return;
8104 check_inode_bitmaps(ctx);
8105 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8106 return;
8107 check_inode_end(ctx);
8108 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8109 return;
8110 check_block_end(ctx);
8111 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8112 return;
8113
8114 ext2fs_free_inode_bitmap(ctx->inode_used_map);
8115 ctx->inode_used_map = 0;
8116 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
8117 ctx->inode_dir_map = 0;
8118 ext2fs_free_block_bitmap(ctx->block_found_map);
8119 ctx->block_found_map = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008120}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008121
8122/*
8123 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008124 */
8125
8126#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
8127#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
8128#define PR_NO_DEFAULT 0x000004 /* Default to no */
8129#define PR_MSG_ONLY 0x000008 /* Print message only */
8130
8131/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
8132
8133#define PR_FATAL 0x001000 /* Fatal error */
8134#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
8135 /* ask another */
8136#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
8137#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
8138#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
8139#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
8140#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
8141
8142
8143#define PROMPT_NONE 0
8144#define PROMPT_FIX 1
8145#define PROMPT_CLEAR 2
8146#define PROMPT_RELOCATE 3
8147#define PROMPT_ALLOCATE 4
8148#define PROMPT_EXPAND 5
8149#define PROMPT_CONNECT 6
8150#define PROMPT_CREATE 7
8151#define PROMPT_SALVAGE 8
8152#define PROMPT_TRUNCATE 9
8153#define PROMPT_CLEAR_INODE 10
8154#define PROMPT_ABORT 11
8155#define PROMPT_SPLIT 12
8156#define PROMPT_CONTINUE 13
8157#define PROMPT_CLONE 14
8158#define PROMPT_DELETE 15
8159#define PROMPT_SUPPRESS 16
8160#define PROMPT_UNLINK 17
8161#define PROMPT_CLEAR_HTREE 18
8162#define PROMPT_RECREATE 19
8163#define PROMPT_NULL 20
8164
8165struct e2fsck_problem {
8166 problem_t e2p_code;
8167 const char * e2p_description;
8168 char prompt;
8169 int flags;
8170 problem_t second_code;
8171};
8172
8173struct latch_descr {
8174 int latch_code;
8175 problem_t question;
8176 problem_t end_message;
8177 int flags;
8178};
8179
8180/*
8181 * These are the prompts which are used to ask the user if they want
8182 * to fix a problem.
8183 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008184static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008185 N_("(no prompt)"), /* 0 */
8186 N_("Fix"), /* 1 */
8187 N_("Clear"), /* 2 */
8188 N_("Relocate"), /* 3 */
8189 N_("Allocate"), /* 4 */
8190 N_("Expand"), /* 5 */
8191 N_("Connect to /lost+found"), /* 6 */
8192 N_("Create"), /* 7 */
8193 N_("Salvage"), /* 8 */
8194 N_("Truncate"), /* 9 */
8195 N_("Clear inode"), /* 10 */
8196 N_("Abort"), /* 11 */
8197 N_("Split"), /* 12 */
8198 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008199 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008200 N_("Delete file"), /* 15 */
8201 N_("Suppress messages"),/* 16 */
8202 N_("Unlink"), /* 17 */
8203 N_("Clear HTree index"),/* 18 */
8204 N_("Recreate"), /* 19 */
8205 "", /* 20 */
8206};
8207
8208/*
8209 * These messages are printed when we are preen mode and we will be
8210 * automatically fixing the problem.
8211 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008212static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008213 N_("(NONE)"), /* 0 */
8214 N_("FIXED"), /* 1 */
8215 N_("CLEARED"), /* 2 */
8216 N_("RELOCATED"), /* 3 */
8217 N_("ALLOCATED"), /* 4 */
8218 N_("EXPANDED"), /* 5 */
8219 N_("RECONNECTED"), /* 6 */
8220 N_("CREATED"), /* 7 */
8221 N_("SALVAGED"), /* 8 */
8222 N_("TRUNCATED"), /* 9 */
8223 N_("INODE CLEARED"), /* 10 */
8224 N_("ABORTED"), /* 11 */
8225 N_("SPLIT"), /* 12 */
8226 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008227 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008228 N_("FILE DELETED"), /* 15 */
8229 N_("SUPPRESSED"), /* 16 */
8230 N_("UNLINKED"), /* 17 */
8231 N_("HTREE INDEX CLEARED"),/* 18 */
8232 N_("WILL RECREATE"), /* 19 */
8233 "", /* 20 */
8234};
8235
8236static const struct e2fsck_problem problem_table[] = {
8237
8238 /* Pre-Pass 1 errors */
8239
8240 /* Block bitmap not in group */
8241 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
8242 PROMPT_RELOCATE, PR_LATCH_RELOC },
8243
8244 /* Inode bitmap not in group */
8245 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
8246 PROMPT_RELOCATE, PR_LATCH_RELOC },
8247
8248 /* Inode table not in group */
8249 { PR_0_ITABLE_NOT_GROUP,
8250 N_("@i table for @g %g is not in @g. (@b %b)\n"
8251 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
8252 PROMPT_RELOCATE, PR_LATCH_RELOC },
8253
8254 /* Superblock corrupt */
8255 { PR_0_SB_CORRUPT,
8256 N_("\nThe @S could not be read or does not describe a correct ext2\n"
8257 "@f. If the @v is valid and it really contains an ext2\n"
8258 "@f (and not swap or ufs or something else), then the @S\n"
8259 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
8260 " e2fsck -b %S <@v>\n\n"),
8261 PROMPT_NONE, PR_FATAL },
8262
8263 /* Filesystem size is wrong */
8264 { PR_0_FS_SIZE_WRONG,
8265 N_("The @f size (according to the @S) is %b @bs\n"
8266 "The physical size of the @v is %c @bs\n"
8267 "Either the @S or the partition table is likely to be corrupt!\n"),
8268 PROMPT_ABORT, 0 },
8269
8270 /* Fragments not supported */
8271 { PR_0_NO_FRAGMENTS,
8272 N_("@S @b_size = %b, fragsize = %c.\n"
8273 "This version of e2fsck does not support fragment sizes different\n"
8274 "from the @b size.\n"),
8275 PROMPT_NONE, PR_FATAL },
8276
8277 /* Bad blocks_per_group */
8278 { PR_0_BLOCKS_PER_GROUP,
8279 N_("@S @bs_per_group = %b, should have been %c\n"),
8280 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8281
8282 /* Bad first_data_block */
8283 { PR_0_FIRST_DATA_BLOCK,
8284 N_("@S first_data_@b = %b, should have been %c\n"),
8285 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8286
8287 /* Adding UUID to filesystem */
8288 { PR_0_ADD_UUID,
8289 N_("@f did not have a UUID; generating one.\n\n"),
8290 PROMPT_NONE, 0 },
8291
8292 /* Relocate hint */
8293 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00008294 N_("Note: if several inode or block bitmap blocks or part\n"
8295 "of the inode table require relocation, you may wish to try\n"
8296 "running e2fsck with the '-b %S' option first. The problem\n"
8297 "may lie only with the primary block group descriptors, and\n"
8298 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008299 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
8300
8301 /* Miscellaneous superblock corruption */
8302 { PR_0_MISC_CORRUPT_SUPER,
8303 N_("Corruption found in @S. (%s = %N).\n"),
8304 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8305
8306 /* Error determing physical device size of filesystem */
8307 { PR_0_GETSIZE_ERROR,
8308 N_("Error determining size of the physical @v: %m\n"),
8309 PROMPT_NONE, PR_FATAL },
8310
8311 /* Inode count in superblock is incorrect */
8312 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00008313 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008314 PROMPT_FIX, 0 },
8315
8316 { PR_0_HURD_CLEAR_FILETYPE,
8317 N_("The Hurd does not support the filetype feature.\n"),
8318 PROMPT_CLEAR, 0 },
8319
8320 /* Journal inode is invalid */
8321 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008322 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008323 PROMPT_CLEAR, PR_PREEN_OK },
8324
8325 /* The external journal has (unsupported) multiple filesystems */
8326 { PR_0_JOURNAL_UNSUPP_MULTIFS,
8327 N_("External @j has multiple @f users (unsupported).\n"),
8328 PROMPT_NONE, PR_FATAL },
8329
8330 /* Can't find external journal */
8331 { PR_0_CANT_FIND_JOURNAL,
8332 N_("Can't find external @j\n"),
8333 PROMPT_NONE, PR_FATAL },
8334
8335 /* External journal has bad superblock */
8336 { PR_0_EXT_JOURNAL_BAD_SUPER,
8337 N_("External @j has bad @S\n"),
8338 PROMPT_NONE, PR_FATAL },
8339
8340 /* Superblock has a bad journal UUID */
8341 { PR_0_JOURNAL_BAD_UUID,
8342 N_("External @j does not support this @f\n"),
8343 PROMPT_NONE, PR_FATAL },
8344
8345 /* Journal has an unknown superblock type */
8346 { PR_0_JOURNAL_UNSUPP_SUPER,
8347 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
8348 "It is likely that your copy of e2fsck is old and/or doesn't "
8349 "support this @j format.\n"
8350 "It is also possible the @j @S is corrupt.\n"),
8351 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
8352
8353 /* Journal superblock is corrupt */
8354 { PR_0_JOURNAL_BAD_SUPER,
8355 N_("Ext3 @j @S is corrupt.\n"),
8356 PROMPT_FIX, PR_PREEN_OK },
8357
8358 /* Superblock flag should be cleared */
8359 { PR_0_JOURNAL_HAS_JOURNAL,
8360 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
8361 PROMPT_CLEAR, PR_PREEN_OK },
8362
8363 /* Superblock flag is incorrect */
8364 { PR_0_JOURNAL_RECOVER_SET,
8365 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
8366 PROMPT_CLEAR, PR_PREEN_OK },
8367
8368 /* Journal has data, but recovery flag is clear */
8369 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008370 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008371 PROMPT_NONE, 0 },
8372
8373 /* Ask if we should clear the journal */
8374 { PR_0_JOURNAL_RESET_JOURNAL,
8375 N_("Clear @j"),
8376 PROMPT_NULL, PR_PREEN_NOMSG },
8377
8378 /* Ask if we should run the journal anyway */
8379 { PR_0_JOURNAL_RUN,
8380 N_("Run @j anyway"),
8381 PROMPT_NULL, 0 },
8382
8383 /* Run the journal by default */
8384 { PR_0_JOURNAL_RUN_DEFAULT,
8385 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
8386 PROMPT_NONE, 0 },
8387
8388 /* Clearing orphan inode */
8389 { PR_0_ORPHAN_CLEAR_INODE,
8390 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
8391 PROMPT_NONE, 0 },
8392
8393 /* Illegal block found in orphaned inode */
8394 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
8395 N_("@I @b #%B (%b) found in @o @i %i.\n"),
8396 PROMPT_NONE, 0 },
8397
8398 /* Already cleared block found in orphaned inode */
8399 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
8400 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
8401 PROMPT_NONE, 0 },
8402
8403 /* Illegal orphan inode in superblock */
8404 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
8405 N_("@I @o @i %i in @S.\n"),
8406 PROMPT_NONE, 0 },
8407
8408 /* Illegal inode in orphaned inode list */
8409 { PR_0_ORPHAN_ILLEGAL_INODE,
8410 N_("@I @i %i in @o @i list.\n"),
8411 PROMPT_NONE, 0 },
8412
8413 /* Filesystem revision is 0, but feature flags are set */
8414 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008415 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008416 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8417
8418 /* Journal superblock has an unknown read-only feature flag set */
8419 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
8420 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
8421 PROMPT_ABORT, 0 },
8422
8423 /* Journal superblock has an unknown incompatible feature flag set */
8424 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
8425 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
8426 PROMPT_ABORT, 0 },
8427
8428 /* Journal has unsupported version number */
8429 { PR_0_JOURNAL_UNSUPP_VERSION,
8430 N_("@j version not supported by this e2fsck.\n"),
8431 PROMPT_ABORT, 0 },
8432
8433 /* Moving journal to hidden file */
8434 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008435 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008436 PROMPT_NONE, 0 },
8437
8438 /* Error moving journal to hidden file */
8439 { PR_0_ERR_MOVE_JOURNAL,
8440 N_("Error moving @j: %m\n\n"),
8441 PROMPT_NONE, 0 },
8442
8443 /* Clearing V2 journal superblock */
8444 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008445 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008446 "Clearing fields beyond the V1 @j @S...\n\n"),
8447 PROMPT_NONE, 0 },
8448
8449 /* Backup journal inode blocks */
8450 { PR_0_BACKUP_JNL,
8451 N_("Backing up @j @i @b information.\n\n"),
8452 PROMPT_NONE, 0 },
8453
8454 /* Reserved blocks w/o resize_inode */
8455 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
8456 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
8457 "is %N; @s zero. "),
8458 PROMPT_FIX, 0 },
8459
8460 /* Resize_inode not enabled, but resize inode is non-zero */
8461 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008462 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008463 PROMPT_CLEAR, 0 },
8464
8465 /* Resize inode invalid */
8466 { PR_0_RESIZE_INODE_INVALID,
8467 N_("Resize @i not valid. "),
8468 PROMPT_RECREATE, 0 },
8469
8470 /* Pass 1 errors */
8471
8472 /* Pass 1: Checking inodes, blocks, and sizes */
8473 { PR_1_PASS_HEADER,
8474 N_("Pass 1: Checking @is, @bs, and sizes\n"),
8475 PROMPT_NONE, 0 },
8476
8477 /* Root directory is not an inode */
8478 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
8479 PROMPT_CLEAR, 0 },
8480
8481 /* Root directory has dtime set */
8482 { PR_1_ROOT_DTIME,
8483 N_("@r has dtime set (probably due to old mke2fs). "),
8484 PROMPT_FIX, PR_PREEN_OK },
8485
8486 /* Reserved inode has bad mode */
8487 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008488 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008489 PROMPT_CLEAR, PR_PREEN_OK },
8490
8491 /* Deleted inode has zero dtime */
8492 { PR_1_ZERO_DTIME,
8493 N_("@D @i %i has zero dtime. "),
8494 PROMPT_FIX, PR_PREEN_OK },
8495
8496 /* Inode in use, but dtime set */
8497 { PR_1_SET_DTIME,
8498 N_("@i %i is in use, but has dtime set. "),
8499 PROMPT_FIX, PR_PREEN_OK },
8500
8501 /* Zero-length directory */
8502 { PR_1_ZERO_LENGTH_DIR,
8503 N_("@i %i is a @z @d. "),
8504 PROMPT_CLEAR, PR_PREEN_OK },
8505
8506 /* Block bitmap conflicts with some other fs block */
8507 { PR_1_BB_CONFLICT,
8508 N_("@g %g's @b @B at %b @C.\n"),
8509 PROMPT_RELOCATE, 0 },
8510
8511 /* Inode bitmap conflicts with some other fs block */
8512 { PR_1_IB_CONFLICT,
8513 N_("@g %g's @i @B at %b @C.\n"),
8514 PROMPT_RELOCATE, 0 },
8515
8516 /* Inode table conflicts with some other fs block */
8517 { PR_1_ITABLE_CONFLICT,
8518 N_("@g %g's @i table at %b @C.\n"),
8519 PROMPT_RELOCATE, 0 },
8520
8521 /* Block bitmap is on a bad block */
8522 { PR_1_BB_BAD_BLOCK,
8523 N_("@g %g's @b @B (%b) is bad. "),
8524 PROMPT_RELOCATE, 0 },
8525
8526 /* Inode bitmap is on a bad block */
8527 { PR_1_IB_BAD_BLOCK,
8528 N_("@g %g's @i @B (%b) is bad. "),
8529 PROMPT_RELOCATE, 0 },
8530
8531 /* Inode has incorrect i_size */
8532 { PR_1_BAD_I_SIZE,
8533 N_("@i %i, i_size is %Is, @s %N. "),
8534 PROMPT_FIX, PR_PREEN_OK },
8535
8536 /* Inode has incorrect i_blocks */
8537 { PR_1_BAD_I_BLOCKS,
8538 N_("@i %i, i_@bs is %Ib, @s %N. "),
8539 PROMPT_FIX, PR_PREEN_OK },
8540
8541 /* Illegal blocknumber in inode */
8542 { PR_1_ILLEGAL_BLOCK_NUM,
8543 N_("@I @b #%B (%b) in @i %i. "),
8544 PROMPT_CLEAR, PR_LATCH_BLOCK },
8545
8546 /* Block number overlaps fs metadata */
8547 { PR_1_BLOCK_OVERLAPS_METADATA,
8548 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
8549 PROMPT_CLEAR, PR_LATCH_BLOCK },
8550
8551 /* Inode has illegal blocks (latch question) */
8552 { PR_1_INODE_BLOCK_LATCH,
8553 N_("@i %i has illegal @b(s). "),
8554 PROMPT_CLEAR, 0 },
8555
8556 /* Too many bad blocks in inode */
8557 { PR_1_TOO_MANY_BAD_BLOCKS,
8558 N_("Too many illegal @bs in @i %i.\n"),
8559 PROMPT_CLEAR_INODE, PR_NO_OK },
8560
8561 /* Illegal block number in bad block inode */
8562 { PR_1_BB_ILLEGAL_BLOCK_NUM,
8563 N_("@I @b #%B (%b) in bad @b @i. "),
8564 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8565
8566 /* Bad block inode has illegal blocks (latch question) */
8567 { PR_1_INODE_BBLOCK_LATCH,
8568 N_("Bad @b @i has illegal @b(s). "),
8569 PROMPT_CLEAR, 0 },
8570
8571 /* Duplicate or bad blocks in use! */
8572 { PR_1_DUP_BLOCKS_PREENSTOP,
8573 N_("Duplicate or bad @b in use!\n"),
8574 PROMPT_NONE, 0 },
8575
8576 /* Bad block used as bad block indirect block */
8577 { PR_1_BBINODE_BAD_METABLOCK,
8578 N_("Bad @b %b used as bad @b @i indirect @b. "),
8579 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8580
8581 /* Inconsistency can't be fixed prompt */
8582 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
8583 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
8584 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
8585 "in the @f.\n"),
8586 PROMPT_CONTINUE, PR_PREEN_NOMSG },
8587
8588 /* Bad primary block */
8589 { PR_1_BAD_PRIMARY_BLOCK,
8590 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
8591 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
8592
8593 /* Bad primary block prompt */
8594 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +00008595 N_("You can remove this @b from the bad @b list and hope\n"
8596 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008597 PROMPT_CLEAR, PR_PREEN_NOMSG },
8598
8599 /* Bad primary superblock */
8600 { PR_1_BAD_PRIMARY_SUPERBLOCK,
8601 N_("The primary @S (%b) is on the bad @b list.\n"),
8602 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8603
8604 /* Bad primary block group descriptors */
8605 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
8606 N_("Block %b in the primary @g descriptors "
8607 "is on the bad @b list\n"),
8608 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8609
8610 /* Bad superblock in group */
8611 { PR_1_BAD_SUPERBLOCK,
8612 N_("Warning: Group %g's @S (%b) is bad.\n"),
8613 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8614
8615 /* Bad block group descriptors in group */
8616 { PR_1_BAD_GROUP_DESCRIPTORS,
8617 N_("Warning: Group %g's copy of the @g descriptors has a bad "
8618 "@b (%b).\n"),
8619 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8620
8621 /* Block claimed for no reason */
8622 { PR_1_PROGERR_CLAIMED_BLOCK,
8623 N_("Programming error? @b #%b claimed for no reason in "
8624 "process_bad_@b.\n"),
8625 PROMPT_NONE, PR_PREEN_OK },
8626
8627 /* Error allocating blocks for relocating metadata */
8628 { PR_1_RELOC_BLOCK_ALLOCATE,
8629 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
8630 PROMPT_NONE, PR_PREEN_OK },
8631
8632 /* Error allocating block buffer during relocation process */
8633 { PR_1_RELOC_MEMORY_ALLOCATE,
8634 N_("@A @b buffer for relocating %s\n"),
8635 PROMPT_NONE, PR_PREEN_OK },
8636
8637 /* Relocating metadata group information from X to Y */
8638 { PR_1_RELOC_FROM_TO,
8639 N_("Relocating @g %g's %s from %b to %c...\n"),
8640 PROMPT_NONE, PR_PREEN_OK },
8641
8642 /* Relocating metatdata group information to X */
8643 { PR_1_RELOC_TO,
8644 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
8645 PROMPT_NONE, PR_PREEN_OK },
8646
8647 /* Block read error during relocation process */
8648 { PR_1_RELOC_READ_ERR,
8649 N_("Warning: could not read @b %b of %s: %m\n"),
8650 PROMPT_NONE, PR_PREEN_OK },
8651
8652 /* Block write error during relocation process */
8653 { PR_1_RELOC_WRITE_ERR,
8654 N_("Warning: could not write @b %b for %s: %m\n"),
8655 PROMPT_NONE, PR_PREEN_OK },
8656
8657 /* Error allocating inode bitmap */
8658 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008659 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008660 PROMPT_NONE, PR_FATAL },
8661
8662 /* Error allocating block bitmap */
8663 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008664 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008665 PROMPT_NONE, PR_FATAL },
8666
8667 /* Error allocating icount structure */
8668 { PR_1_ALLOCATE_ICOUNT,
8669 N_("@A icount link information: %m\n"),
8670 PROMPT_NONE, PR_FATAL },
8671
8672 /* Error allocating dbcount */
8673 { PR_1_ALLOCATE_DBCOUNT,
8674 N_("@A @d @b array: %m\n"),
8675 PROMPT_NONE, PR_FATAL },
8676
8677 /* Error while scanning inodes */
8678 { PR_1_ISCAN_ERROR,
8679 N_("Error while scanning @is (%i): %m\n"),
8680 PROMPT_NONE, PR_FATAL },
8681
8682 /* Error while iterating over blocks */
8683 { PR_1_BLOCK_ITERATE,
8684 N_("Error while iterating over @bs in @i %i: %m\n"),
8685 PROMPT_NONE, PR_FATAL },
8686
8687 /* Error while storing inode count information */
8688 { PR_1_ICOUNT_STORE,
8689 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
8690 PROMPT_NONE, PR_FATAL },
8691
8692 /* Error while storing directory block information */
8693 { PR_1_ADD_DBLOCK,
8694 N_("Error storing @d @b information "
8695 "(@i=%i, @b=%b, num=%N): %m\n"),
8696 PROMPT_NONE, PR_FATAL },
8697
8698 /* Error while reading inode (for clearing) */
8699 { PR_1_READ_INODE,
8700 N_("Error reading @i %i: %m\n"),
8701 PROMPT_NONE, PR_FATAL },
8702
8703 /* Suppress messages prompt */
8704 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
8705
8706 /* Imagic flag set on an inode when filesystem doesn't support it */
8707 { PR_1_SET_IMAGIC,
8708 N_("@i %i has imagic flag set. "),
8709 PROMPT_CLEAR, 0 },
8710
8711 /* Immutable flag set on a device or socket inode */
8712 { PR_1_SET_IMMUTABLE,
8713 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
8714 "or append-only flag set. "),
8715 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
8716
8717 /* Compression flag set on an inode when filesystem doesn't support it */
8718 { PR_1_COMPR_SET,
8719 N_("@i %i has @cion flag set on @f without @cion support. "),
8720 PROMPT_CLEAR, 0 },
8721
8722 /* Non-zero size for device, fifo or socket inode */
8723 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008724 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008725 PROMPT_FIX, PR_PREEN_OK },
8726
8727 /* Filesystem revision is 0, but feature flags are set */
8728 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008729 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008730 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8731
8732 /* Journal inode is not in use, but contains data */
8733 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008734 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008735 PROMPT_CLEAR, PR_PREEN_OK },
8736
8737 /* Journal has bad mode */
8738 { PR_1_JOURNAL_BAD_MODE,
8739 N_("@j is not regular file. "),
8740 PROMPT_FIX, PR_PREEN_OK },
8741
8742 /* Deal with inodes that were part of orphan linked list */
8743 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +00008744 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008745 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
8746
8747 /* Deal with inodes that were part of corrupted orphan linked
8748 list (latch question) */
8749 { PR_1_ORPHAN_LIST_REFUGEES,
8750 N_("@is that were part of a corrupted orphan linked list found. "),
8751 PROMPT_FIX, 0 },
8752
8753 /* Error allocating refcount structure */
8754 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008755 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008756 PROMPT_NONE, PR_FATAL },
8757
8758 /* Error reading extended attribute block */
8759 { PR_1_READ_EA_BLOCK,
8760 N_("Error reading @a @b %b for @i %i. "),
8761 PROMPT_CLEAR, 0 },
8762
8763 /* Invalid extended attribute block */
8764 { PR_1_BAD_EA_BLOCK,
8765 N_("@i %i has a bad @a @b %b. "),
8766 PROMPT_CLEAR, 0 },
8767
8768 /* Error reading Extended Attribute block while fixing refcount */
8769 { PR_1_EXTATTR_READ_ABORT,
8770 N_("Error reading @a @b %b (%m). "),
8771 PROMPT_ABORT, 0 },
8772
8773 /* Extended attribute reference count incorrect */
8774 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008775 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008776 PROMPT_FIX, 0 },
8777
8778 /* Error writing Extended Attribute block while fixing refcount */
8779 { PR_1_EXTATTR_WRITE,
8780 N_("Error writing @a @b %b (%m). "),
8781 PROMPT_ABORT, 0 },
8782
8783 /* Multiple EA blocks not supported */
8784 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008785 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008786 PROMPT_CLEAR, 0},
8787
8788 /* Error allocating EA region allocation structure */
8789 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +00008790 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008791 PROMPT_ABORT, 0},
8792
8793 /* Error EA allocation collision */
8794 { PR_1_EA_ALLOC_COLLISION,
8795 N_("@a @b %b is corrupt (allocation collision). "),
8796 PROMPT_CLEAR, 0},
8797
8798 /* Bad extended attribute name */
8799 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00008800 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008801 PROMPT_CLEAR, 0},
8802
8803 /* Bad extended attribute value */
8804 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +00008805 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008806 PROMPT_CLEAR, 0},
8807
8808 /* Inode too big (latch question) */
8809 { PR_1_INODE_TOOBIG,
8810 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
8811
8812 /* Directory too big */
8813 { PR_1_TOOBIG_DIR,
8814 N_("@b #%B (%b) causes @d to be too big. "),
8815 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8816
8817 /* Regular file too big */
8818 { PR_1_TOOBIG_REG,
8819 N_("@b #%B (%b) causes file to be too big. "),
8820 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8821
8822 /* Symlink too big */
8823 { PR_1_TOOBIG_SYMLINK,
8824 N_("@b #%B (%b) causes symlink to be too big. "),
8825 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8826
8827 /* INDEX_FL flag set on a non-HTREE filesystem */
8828 { PR_1_HTREE_SET,
8829 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
8830 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8831
8832 /* INDEX_FL flag set on a non-directory */
8833 { PR_1_HTREE_NODIR,
8834 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
8835 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8836
8837 /* Invalid root node in HTREE directory */
8838 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00008839 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008840 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8841
8842 /* Unsupported hash version in HTREE directory */
8843 { PR_1_HTREE_HASHV,
8844 N_("@h %i has an unsupported hash version (%N)\n"),
8845 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8846
8847 /* Incompatible flag in HTREE root node */
8848 { PR_1_HTREE_INCOMPAT,
8849 N_("@h %i uses an incompatible htree root node flag.\n"),
8850 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8851
8852 /* HTREE too deep */
8853 { PR_1_HTREE_DEPTH,
8854 N_("@h %i has a tree depth (%N) which is too big\n"),
8855 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8856
8857 /* Bad block has indirect block that conflicts with filesystem block */
8858 { PR_1_BB_FS_BLOCK,
8859 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
8860 "@f metadata. "),
8861 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8862
8863 /* Resize inode failed */
8864 { PR_1_RESIZE_INODE_CREATE,
8865 N_("Resize @i (re)creation failed: %m."),
8866 PROMPT_ABORT, 0 },
8867
8868 /* invalid inode->i_extra_isize */
8869 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008870 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008871 PROMPT_FIX, PR_PREEN_OK },
8872
8873 /* invalid ea entry->e_name_len */
8874 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +00008875 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008876 PROMPT_CLEAR, PR_PREEN_OK },
8877
8878 /* invalid ea entry->e_value_size */
8879 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008880 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008881 PROMPT_CLEAR, PR_PREEN_OK },
8882
8883 /* invalid ea entry->e_value_offs */
8884 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +00008885 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008886 PROMPT_CLEAR, PR_PREEN_OK },
8887
8888 /* invalid ea entry->e_value_block */
8889 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008890 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 +00008891 PROMPT_CLEAR, PR_PREEN_OK },
8892
8893 /* invalid ea entry->e_hash */
8894 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +00008895 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 +00008896 PROMPT_CLEAR, PR_PREEN_OK },
8897
8898 /* Pass 1b errors */
8899
8900 /* Pass 1B: Rescan for duplicate/bad blocks */
8901 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008902 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
8903 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008904 PROMPT_NONE, 0 },
8905
8906 /* Duplicate/bad block(s) header */
8907 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008908 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008909 PROMPT_NONE, 0 },
8910
8911 /* Duplicate/bad block(s) in inode */
8912 { PR_1B_DUP_BLOCK,
8913 " %b",
8914 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
8915
8916 /* Duplicate/bad block(s) end */
8917 { PR_1B_DUP_BLOCK_END,
8918 "\n",
8919 PROMPT_NONE, PR_PREEN_NOHDR },
8920
8921 /* Error while scanning inodes */
8922 { PR_1B_ISCAN_ERROR,
8923 N_("Error while scanning inodes (%i): %m\n"),
8924 PROMPT_NONE, PR_FATAL },
8925
8926 /* Error allocating inode bitmap */
8927 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008928 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008929 PROMPT_NONE, PR_FATAL },
8930
8931 /* Error while iterating over blocks */
8932 { PR_1B_BLOCK_ITERATE,
8933 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
8934 PROMPT_NONE, 0 },
8935
8936 /* Error adjusting EA refcount */
8937 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008938 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008939 PROMPT_NONE, 0 },
8940
8941
Mike Frysinger874af852006-03-08 07:03:27 +00008942 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008943 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008944 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008945 PROMPT_NONE, 0 },
8946
8947
Mike Frysinger874af852006-03-08 07:03:27 +00008948 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008949 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008950 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008951 PROMPT_NONE, 0 },
8952
8953 /* File has duplicate blocks */
8954 { PR_1D_DUP_FILE,
8955 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +00008956 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008957 PROMPT_NONE, 0 },
8958
8959 /* List of files sharing duplicate blocks */
8960 { PR_1D_DUP_FILE_LIST,
8961 N_("\t%Q (@i #%i, mod time %IM)\n"),
8962 PROMPT_NONE, 0 },
8963
8964 /* File sharing blocks with filesystem metadata */
8965 { PR_1D_SHARE_METADATA,
8966 N_("\t<@f metadata>\n"),
8967 PROMPT_NONE, 0 },
8968
8969 /* Report of how many duplicate/bad inodes */
8970 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +00008971 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008972 PROMPT_NONE, 0 },
8973
8974 /* Duplicated blocks already reassigned or cloned. */
8975 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +00008976 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008977 PROMPT_NONE, 0 },
8978
8979 /* Clone duplicate/bad blocks? */
8980 { PR_1D_CLONE_QUESTION,
8981 "", PROMPT_CLONE, PR_NO_OK },
8982
8983 /* Delete file? */
8984 { PR_1D_DELETE_QUESTION,
8985 "", PROMPT_DELETE, 0 },
8986
8987 /* Couldn't clone file (error) */
8988 { PR_1D_CLONE_ERROR,
8989 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
8990
8991 /* Pass 2 errors */
8992
8993 /* Pass 2: Checking directory structure */
8994 { PR_2_PASS_HEADER,
8995 N_("Pass 2: Checking @d structure\n"),
8996 PROMPT_NONE, 0 },
8997
8998 /* Bad inode number for '.' */
8999 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009000 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009001 PROMPT_FIX, 0 },
9002
9003 /* Directory entry has bad inode number */
9004 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +00009005 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009006 PROMPT_CLEAR, 0 },
9007
9008 /* Directory entry has deleted or unused inode */
9009 { PR_2_UNUSED_INODE,
9010 N_("@E has @D/unused @i %Di. "),
9011 PROMPT_CLEAR, PR_PREEN_OK },
9012
9013 /* Directry entry is link to '.' */
9014 { PR_2_LINK_DOT,
9015 N_("@E @L to '.' "),
9016 PROMPT_CLEAR, 0 },
9017
9018 /* Directory entry points to inode now located in a bad block */
9019 { PR_2_BB_INODE,
9020 N_("@E points to @i (%Di) located in a bad @b.\n"),
9021 PROMPT_CLEAR, 0 },
9022
9023 /* Directory entry contains a link to a directory */
9024 { PR_2_LINK_DIR,
9025 N_("@E @L to @d %P (%Di).\n"),
9026 PROMPT_CLEAR, 0 },
9027
9028 /* Directory entry contains a link to the root directry */
9029 { PR_2_LINK_ROOT,
9030 N_("@E @L to the @r.\n"),
9031 PROMPT_CLEAR, 0 },
9032
9033 /* Directory entry has illegal characters in its name */
9034 { PR_2_BAD_NAME,
9035 N_("@E has illegal characters in its name.\n"),
9036 PROMPT_FIX, 0 },
9037
9038 /* Missing '.' in directory inode */
9039 { PR_2_MISSING_DOT,
9040 N_("Missing '.' in @d @i %i.\n"),
9041 PROMPT_FIX, 0 },
9042
9043 /* Missing '..' in directory inode */
9044 { PR_2_MISSING_DOT_DOT,
9045 N_("Missing '..' in @d @i %i.\n"),
9046 PROMPT_FIX, 0 },
9047
9048 /* First entry in directory inode doesn't contain '.' */
9049 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009050 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009051 PROMPT_FIX, 0 },
9052
9053 /* Second entry in directory inode doesn't contain '..' */
9054 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009055 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009056 PROMPT_FIX, 0 },
9057
9058 /* i_faddr should be zero */
9059 { PR_2_FADDR_ZERO,
9060 N_("i_faddr @F %IF, @s zero.\n"),
9061 PROMPT_CLEAR, 0 },
9062
9063 /* i_file_acl should be zero */
9064 { PR_2_FILE_ACL_ZERO,
9065 N_("i_file_acl @F %If, @s zero.\n"),
9066 PROMPT_CLEAR, 0 },
9067
9068 /* i_dir_acl should be zero */
9069 { PR_2_DIR_ACL_ZERO,
9070 N_("i_dir_acl @F %Id, @s zero.\n"),
9071 PROMPT_CLEAR, 0 },
9072
9073 /* i_frag should be zero */
9074 { PR_2_FRAG_ZERO,
9075 N_("i_frag @F %N, @s zero.\n"),
9076 PROMPT_CLEAR, 0 },
9077
9078 /* i_fsize should be zero */
9079 { PR_2_FSIZE_ZERO,
9080 N_("i_fsize @F %N, @s zero.\n"),
9081 PROMPT_CLEAR, 0 },
9082
9083 /* inode has bad mode */
9084 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009085 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009086 PROMPT_CLEAR, 0 },
9087
9088 /* directory corrupted */
9089 { PR_2_DIR_CORRUPTED,
9090 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
9091 PROMPT_SALVAGE, 0 },
9092
9093 /* filename too long */
9094 { PR_2_FILENAME_LONG,
9095 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
9096 PROMPT_TRUNCATE, 0 },
9097
9098 /* Directory inode has a missing block (hole) */
9099 { PR_2_DIRECTORY_HOLE,
9100 N_("@d @i %i has an unallocated @b #%B. "),
9101 PROMPT_ALLOCATE, 0 },
9102
9103 /* '.' is not NULL terminated */
9104 { PR_2_DOT_NULL_TERM,
9105 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
9106 PROMPT_FIX, 0 },
9107
9108 /* '..' is not NULL terminated */
9109 { PR_2_DOT_DOT_NULL_TERM,
9110 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
9111 PROMPT_FIX, 0 },
9112
9113 /* Illegal character device inode */
9114 { PR_2_BAD_CHAR_DEV,
9115 N_("@i %i (%Q) is an @I character @v.\n"),
9116 PROMPT_CLEAR, 0 },
9117
9118 /* Illegal block device inode */
9119 { PR_2_BAD_BLOCK_DEV,
9120 N_("@i %i (%Q) is an @I @b @v.\n"),
9121 PROMPT_CLEAR, 0 },
9122
9123 /* Duplicate '.' entry */
9124 { PR_2_DUP_DOT,
9125 N_("@E is duplicate '.' @e.\n"),
9126 PROMPT_FIX, 0 },
9127
9128 /* Duplicate '..' entry */
9129 { PR_2_DUP_DOT_DOT,
9130 N_("@E is duplicate '..' @e.\n"),
9131 PROMPT_FIX, 0 },
9132
9133 /* Internal error: couldn't find dir_info */
9134 { PR_2_NO_DIRINFO,
9135 N_("Internal error: couldn't find dir_info for %i.\n"),
9136 PROMPT_NONE, PR_FATAL },
9137
9138 /* Final rec_len is wrong */
9139 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +00009140 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009141 PROMPT_FIX, 0 },
9142
9143 /* Error allocating icount structure */
9144 { PR_2_ALLOCATE_ICOUNT,
9145 N_("@A icount structure: %m\n"),
9146 PROMPT_NONE, PR_FATAL },
9147
9148 /* Error iterating over directory blocks */
9149 { PR_2_DBLIST_ITERATE,
9150 N_("Error iterating over @d @bs: %m\n"),
9151 PROMPT_NONE, PR_FATAL },
9152
9153 /* Error reading directory block */
9154 { PR_2_READ_DIRBLOCK,
9155 N_("Error reading @d @b %b (@i %i): %m\n"),
9156 PROMPT_CONTINUE, 0 },
9157
9158 /* Error writing directory block */
9159 { PR_2_WRITE_DIRBLOCK,
9160 N_("Error writing @d @b %b (@i %i): %m\n"),
9161 PROMPT_CONTINUE, 0 },
9162
9163 /* Error allocating new directory block */
9164 { PR_2_ALLOC_DIRBOCK,
9165 N_("@A new @d @b for @i %i (%s): %m\n"),
9166 PROMPT_NONE, 0 },
9167
9168 /* Error deallocating inode */
9169 { PR_2_DEALLOC_INODE,
9170 N_("Error deallocating @i %i: %m\n"),
9171 PROMPT_NONE, PR_FATAL },
9172
9173 /* Directory entry for '.' is big. Split? */
9174 { PR_2_SPLIT_DOT,
9175 N_("@d @e for '.' is big. "),
9176 PROMPT_SPLIT, PR_NO_OK },
9177
9178 /* Illegal FIFO inode */
9179 { PR_2_BAD_FIFO,
9180 N_("@i %i (%Q) is an @I FIFO.\n"),
9181 PROMPT_CLEAR, 0 },
9182
9183 /* Illegal socket inode */
9184 { PR_2_BAD_SOCKET,
9185 N_("@i %i (%Q) is an @I socket.\n"),
9186 PROMPT_CLEAR, 0 },
9187
9188 /* Directory filetype not set */
9189 { PR_2_SET_FILETYPE,
9190 N_("Setting filetype for @E to %N.\n"),
9191 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
9192
9193 /* Directory filetype incorrect */
9194 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +00009195 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009196 PROMPT_FIX, 0 },
9197
9198 /* Directory filetype set on filesystem */
9199 { PR_2_CLEAR_FILETYPE,
9200 N_("@E has filetype set.\n"),
9201 PROMPT_CLEAR, PR_PREEN_OK },
9202
9203 /* Directory filename is null */
9204 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00009205 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009206 PROMPT_CLEAR, 0 },
9207
9208 /* Invalid symlink */
9209 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +00009210 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009211 PROMPT_CLEAR, 0 },
9212
9213 /* i_file_acl (extended attribute block) is bad */
9214 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +00009215 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009216 PROMPT_CLEAR, 0 },
9217
9218 /* Filesystem contains large files, but has no such flag in sb */
9219 { PR_2_FEATURE_LARGE_FILES,
9220 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
9221 PROMPT_FIX, 0 },
9222
9223 /* Node in HTREE directory not referenced */
9224 { PR_2_HTREE_NOTREF,
9225 N_("@p @h %d: node (%B) not referenced\n"),
9226 PROMPT_NONE, 0 },
9227
9228 /* Node in HTREE directory referenced twice */
9229 { PR_2_HTREE_DUPREF,
9230 N_("@p @h %d: node (%B) referenced twice\n"),
9231 PROMPT_NONE, 0 },
9232
9233 /* Node in HTREE directory has bad min hash */
9234 { PR_2_HTREE_MIN_HASH,
9235 N_("@p @h %d: node (%B) has bad min hash\n"),
9236 PROMPT_NONE, 0 },
9237
9238 /* Node in HTREE directory has bad max hash */
9239 { PR_2_HTREE_MAX_HASH,
9240 N_("@p @h %d: node (%B) has bad max hash\n"),
9241 PROMPT_NONE, 0 },
9242
9243 /* Clear invalid HTREE directory */
9244 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009245 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009246
9247 /* Bad block in htree interior node */
9248 { PR_2_HTREE_BADBLK,
9249 N_("@p @h %d (%q): bad @b number %b.\n"),
9250 PROMPT_CLEAR_HTREE, 0 },
9251
9252 /* Error adjusting EA refcount */
9253 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009254 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009255 PROMPT_NONE, PR_FATAL },
9256
9257 /* Invalid HTREE root node */
9258 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009259 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009260 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9261
9262 /* Invalid HTREE limit */
9263 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +00009264 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009265 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9266
9267 /* Invalid HTREE count */
9268 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009269 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009270 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9271
9272 /* HTREE interior node has out-of-order hashes in table */
9273 { PR_2_HTREE_HASH_ORDER,
9274 N_("@p @h %d: node (%B) has an unordered hash table\n"),
9275 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9276
Mike Frysinger874af852006-03-08 07:03:27 +00009277 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009278 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +00009279 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009280 PROMPT_NONE, 0 },
9281
9282 /* Duplicate directory entry found */
9283 { PR_2_DUPLICATE_DIRENT,
9284 N_("Duplicate @E found. "),
9285 PROMPT_CLEAR, 0 },
9286
9287 /* Non-unique filename found */
9288 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
9289 N_("@E has a non-unique filename.\nRename to %s"),
9290 PROMPT_NULL, 0 },
9291
9292 /* Duplicate directory entry found */
9293 { PR_2_REPORT_DUP_DIRENT,
9294 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
9295 PROMPT_NONE, 0 },
9296
9297 /* Pass 3 errors */
9298
9299 /* Pass 3: Checking directory connectivity */
9300 { PR_3_PASS_HEADER,
9301 N_("Pass 3: Checking @d connectivity\n"),
9302 PROMPT_NONE, 0 },
9303
9304 /* Root inode not allocated */
9305 { PR_3_NO_ROOT_INODE,
9306 N_("@r not allocated. "),
9307 PROMPT_ALLOCATE, 0 },
9308
9309 /* No room in lost+found */
9310 { PR_3_EXPAND_LF_DIR,
9311 N_("No room in @l @d. "),
9312 PROMPT_EXPAND, 0 },
9313
9314 /* Unconnected directory inode */
9315 { PR_3_UNCONNECTED_DIR,
9316 N_("Unconnected @d @i %i (%p)\n"),
9317 PROMPT_CONNECT, 0 },
9318
9319 /* /lost+found not found */
9320 { PR_3_NO_LF_DIR,
9321 N_("/@l not found. "),
9322 PROMPT_CREATE, PR_PREEN_OK },
9323
9324 /* .. entry is incorrect */
9325 { PR_3_BAD_DOT_DOT,
9326 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
9327 PROMPT_FIX, 0 },
9328
9329 /* Bad or non-existent /lost+found. Cannot reconnect */
9330 { PR_3_NO_LPF,
9331 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
9332 PROMPT_NONE, 0 },
9333
9334 /* Could not expand /lost+found */
9335 { PR_3_CANT_EXPAND_LPF,
9336 N_("Could not expand /@l: %m\n"),
9337 PROMPT_NONE, 0 },
9338
9339 /* Could not reconnect inode */
9340 { PR_3_CANT_RECONNECT,
9341 N_("Could not reconnect %i: %m\n"),
9342 PROMPT_NONE, 0 },
9343
9344 /* Error while trying to find /lost+found */
9345 { PR_3_ERR_FIND_LPF,
9346 N_("Error while trying to find /@l: %m\n"),
9347 PROMPT_NONE, 0 },
9348
9349 /* Error in ext2fs_new_block while creating /lost+found */
9350 { PR_3_ERR_LPF_NEW_BLOCK,
9351 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
9352 PROMPT_NONE, 0 },
9353
9354 /* Error in ext2fs_new_inode while creating /lost+found */
9355 { PR_3_ERR_LPF_NEW_INODE,
9356 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
9357 PROMPT_NONE, 0 },
9358
9359 /* Error in ext2fs_new_dir_block while creating /lost+found */
9360 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
9361 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
9362 PROMPT_NONE, 0 },
9363
9364 /* Error while writing directory block for /lost+found */
9365 { PR_3_ERR_LPF_WRITE_BLOCK,
9366 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
9367 PROMPT_NONE, 0 },
9368
9369 /* Error while adjusting inode count */
9370 { PR_3_ADJUST_INODE,
9371 N_("Error while adjusting @i count on @i %i\n"),
9372 PROMPT_NONE, 0 },
9373
9374 /* Couldn't fix parent directory -- error */
9375 { PR_3_FIX_PARENT_ERR,
9376 N_("Couldn't fix parent of @i %i: %m\n\n"),
9377 PROMPT_NONE, 0 },
9378
9379 /* Couldn't fix parent directory -- couldn't find it */
9380 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +00009381 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 +00009382 PROMPT_NONE, 0 },
9383
9384 /* Error allocating inode bitmap */
9385 { PR_3_ALLOCATE_IBITMAP_ERROR,
9386 N_("@A @i @B (%N): %m\n"),
9387 PROMPT_NONE, PR_FATAL },
9388
9389 /* Error creating root directory */
9390 { PR_3_CREATE_ROOT_ERROR,
9391 N_("Error creating root @d (%s): %m\n"),
9392 PROMPT_NONE, PR_FATAL },
9393
9394 /* Error creating lost and found directory */
9395 { PR_3_CREATE_LPF_ERROR,
9396 N_("Error creating /@l @d (%s): %m\n"),
9397 PROMPT_NONE, PR_FATAL },
9398
9399 /* Root inode is not directory; aborting */
9400 { PR_3_ROOT_NOT_DIR_ABORT,
9401 N_("@r is not a @d; aborting.\n"),
9402 PROMPT_NONE, PR_FATAL },
9403
9404 /* Cannot proceed without a root inode. */
9405 { PR_3_NO_ROOT_INODE_ABORT,
9406 N_("Cannot proceed without a @r.\n"),
9407 PROMPT_NONE, PR_FATAL },
9408
9409 /* Internal error: couldn't find dir_info */
9410 { PR_3_NO_DIRINFO,
9411 N_("Internal error: couldn't find dir_info for %i.\n"),
9412 PROMPT_NONE, PR_FATAL },
9413
9414 /* Lost+found not a directory */
9415 { PR_3_LPF_NOTDIR,
9416 N_("/@l is not a @d (ino=%i)\n"),
9417 PROMPT_UNLINK, 0 },
9418
9419 /* Pass 3A Directory Optimization */
9420
9421 /* Pass 3A: Optimizing directories */
9422 { PR_3A_PASS_HEADER,
9423 N_("Pass 3A: Optimizing directories\n"),
9424 PROMPT_NONE, PR_PREEN_NOMSG },
9425
9426 /* Error iterating over directories */
9427 { PR_3A_OPTIMIZE_ITER,
9428 N_("Failed to create dirs_to_hash iterator: %m"),
9429 PROMPT_NONE, 0 },
9430
9431 /* Error rehash directory */
9432 { PR_3A_OPTIMIZE_DIR_ERR,
9433 N_("Failed to optimize directory %q (%d): %m"),
9434 PROMPT_NONE, 0 },
9435
9436 /* Rehashing dir header */
9437 { PR_3A_OPTIMIZE_DIR_HEADER,
9438 N_("Optimizing directories: "),
9439 PROMPT_NONE, PR_MSG_ONLY },
9440
9441 /* Rehashing directory %d */
9442 { PR_3A_OPTIMIZE_DIR,
9443 " %d",
9444 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
9445
9446 /* Rehashing dir end */
9447 { PR_3A_OPTIMIZE_DIR_END,
9448 "\n",
9449 PROMPT_NONE, PR_PREEN_NOHDR },
9450
9451 /* Pass 4 errors */
9452
9453 /* Pass 4: Checking reference counts */
9454 { PR_4_PASS_HEADER,
9455 N_("Pass 4: Checking reference counts\n"),
9456 PROMPT_NONE, 0 },
9457
9458 /* Unattached zero-length inode */
9459 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009460 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009461 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
9462
9463 /* Unattached inode */
9464 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009465 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009466 PROMPT_CONNECT, 0 },
9467
9468 /* Inode ref count wrong */
9469 { PR_4_BAD_REF_COUNT,
9470 N_("@i %i ref count is %Il, @s %N. "),
9471 PROMPT_FIX, PR_PREEN_OK },
9472
9473 { PR_4_INCONSISTENT_COUNT,
9474 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
9475 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
9476 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +00009477 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009478 PROMPT_NONE, 0 },
9479
9480 /* Pass 5 errors */
9481
9482 /* Pass 5: Checking group summary information */
9483 { PR_5_PASS_HEADER,
9484 N_("Pass 5: Checking @g summary information\n"),
9485 PROMPT_NONE, 0 },
9486
9487 /* Padding at end of inode bitmap is not set. */
9488 { PR_5_INODE_BMAP_PADDING,
9489 N_("Padding at end of @i @B is not set. "),
9490 PROMPT_FIX, PR_PREEN_OK },
9491
9492 /* Padding at end of block bitmap is not set. */
9493 { PR_5_BLOCK_BMAP_PADDING,
9494 N_("Padding at end of @b @B is not set. "),
9495 PROMPT_FIX, PR_PREEN_OK },
9496
9497 /* Block bitmap differences header */
9498 { PR_5_BLOCK_BITMAP_HEADER,
9499 N_("@b @B differences: "),
9500 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
9501
9502 /* Block not used, but marked in bitmap */
9503 { PR_5_BLOCK_UNUSED,
9504 " -%b",
9505 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9506
9507 /* Block used, but not marked used in bitmap */
9508 { PR_5_BLOCK_USED,
9509 " +%b",
9510 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9511
9512 /* Block bitmap differences end */
9513 { PR_5_BLOCK_BITMAP_END,
9514 "\n",
9515 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9516
9517 /* Inode bitmap differences header */
9518 { PR_5_INODE_BITMAP_HEADER,
9519 N_("@i @B differences: "),
9520 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9521
9522 /* Inode not used, but marked in bitmap */
9523 { PR_5_INODE_UNUSED,
9524 " -%i",
9525 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9526
9527 /* Inode used, but not marked used in bitmap */
9528 { PR_5_INODE_USED,
9529 " +%i",
9530 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9531
9532 /* Inode bitmap differences end */
9533 { PR_5_INODE_BITMAP_END,
9534 "\n",
9535 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9536
9537 /* Free inodes count for group wrong */
9538 { PR_5_FREE_INODE_COUNT_GROUP,
9539 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
9540 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9541
9542 /* Directories count for group wrong */
9543 { PR_5_FREE_DIR_COUNT_GROUP,
9544 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
9545 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9546
9547 /* Free inodes count wrong */
9548 { PR_5_FREE_INODE_COUNT,
9549 N_("Free @is count wrong (%i, counted=%j).\n"),
9550 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9551
9552 /* Free blocks count for group wrong */
9553 { PR_5_FREE_BLOCK_COUNT_GROUP,
9554 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
9555 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9556
9557 /* Free blocks count wrong */
9558 { PR_5_FREE_BLOCK_COUNT,
9559 N_("Free @bs count wrong (%b, counted=%c).\n"),
9560 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9561
9562 /* Programming error: bitmap endpoints don't match */
9563 { PR_5_BMAP_ENDPOINTS,
9564 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
9565 "match calculated @B endpoints (%i, %j)\n"),
9566 PROMPT_NONE, PR_FATAL },
9567
9568 /* Internal error: fudging end of bitmap */
9569 { PR_5_FUDGE_BITMAP_ERROR,
9570 N_("Internal error: fudging end of bitmap (%N)\n"),
9571 PROMPT_NONE, PR_FATAL },
9572
9573 /* Error copying in replacement inode bitmap */
9574 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009575 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009576 PROMPT_NONE, PR_FATAL },
9577
9578 /* Error copying in replacement block bitmap */
9579 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009580 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009581 PROMPT_NONE, PR_FATAL },
9582
9583 /* Block range not used, but marked in bitmap */
9584 { PR_5_BLOCK_RANGE_UNUSED,
9585 " -(%b--%c)",
9586 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9587
9588 /* Block range used, but not marked used in bitmap */
9589 { PR_5_BLOCK_RANGE_USED,
9590 " +(%b--%c)",
9591 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9592
9593 /* Inode range not used, but marked in bitmap */
9594 { PR_5_INODE_RANGE_UNUSED,
9595 " -(%i--%j)",
9596 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9597
9598 /* Inode range used, but not marked used in bitmap */
9599 { PR_5_INODE_RANGE_USED,
9600 " +(%i--%j)",
9601 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9602
9603 { 0 }
9604};
9605
9606/*
9607 * This is the latch flags register. It allows several problems to be
9608 * "latched" together. This means that the user has to answer but one
9609 * question for the set of problems, and all of the associated
9610 * problems will be either fixed or not fixed.
9611 */
9612static struct latch_descr pr_latch_info[] = {
9613 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
9614 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
9615 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
9616 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
9617 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
9618 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
9619 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
9620 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
9621 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
9622 { -1, 0, 0 },
9623};
9624
9625static const struct e2fsck_problem *find_problem(problem_t code)
9626{
9627 int i;
9628
9629 for (i=0; problem_table[i].e2p_code; i++) {
9630 if (problem_table[i].e2p_code == code)
9631 return &problem_table[i];
9632 }
9633 return 0;
9634}
9635
9636static struct latch_descr *find_latch(int code)
9637{
9638 int i;
9639
9640 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
9641 if (pr_latch_info[i].latch_code == code)
9642 return &pr_latch_info[i];
9643 }
9644 return 0;
9645}
9646
9647int end_problem_latch(e2fsck_t ctx, int mask)
9648{
9649 struct latch_descr *ldesc;
9650 struct problem_context pctx;
9651 int answer = -1;
9652
9653 ldesc = find_latch(mask);
9654 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
9655 clear_problem_context(&pctx);
9656 answer = fix_problem(ctx, ldesc->end_message, &pctx);
9657 }
9658 ldesc->flags &= ~(PRL_VARIABLE);
9659 return answer;
9660}
9661
9662int set_latch_flags(int mask, int setflags, int clearflags)
9663{
9664 struct latch_descr *ldesc;
9665
9666 ldesc = find_latch(mask);
9667 if (!ldesc)
9668 return -1;
9669 ldesc->flags |= setflags;
9670 ldesc->flags &= ~clearflags;
9671 return 0;
9672}
9673
9674void clear_problem_context(struct problem_context *ctx)
9675{
9676 memset(ctx, 0, sizeof(struct problem_context));
9677 ctx->blkcount = -1;
9678 ctx->group = -1;
9679}
9680
9681int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
9682{
9683 ext2_filsys fs = ctx->fs;
9684 const struct e2fsck_problem *ptr;
9685 struct latch_descr *ldesc = 0;
9686 const char *message;
9687 int def_yn, answer, ans;
9688 int print_answer = 0;
9689 int suppress = 0;
9690
9691 ptr = find_problem(code);
9692 if (!ptr) {
9693 printf(_("Unhandled error code (0x%x)!\n"), code);
9694 return 0;
9695 }
9696 def_yn = 1;
9697 if ((ptr->flags & PR_NO_DEFAULT) ||
9698 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
9699 (ctx->options & E2F_OPT_NO))
9700 def_yn= 0;
9701
9702 /*
9703 * Do special latch processing. This is where we ask the
9704 * latch question, if it exists
9705 */
9706 if (ptr->flags & PR_LATCH_MASK) {
9707 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
9708 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
9709 ans = fix_problem(ctx, ldesc->question, pctx);
9710 if (ans == 1)
9711 ldesc->flags |= PRL_YES;
9712 if (ans == 0)
9713 ldesc->flags |= PRL_NO;
9714 ldesc->flags |= PRL_LATCHED;
9715 }
9716 if (ldesc->flags & PRL_SUPPRESS)
9717 suppress++;
9718 }
9719 if ((ptr->flags & PR_PREEN_NOMSG) &&
9720 (ctx->options & E2F_OPT_PREEN))
9721 suppress++;
9722 if ((ptr->flags & PR_NO_NOMSG) &&
9723 (ctx->options & E2F_OPT_NO))
9724 suppress++;
9725 if (!suppress) {
9726 message = ptr->e2p_description;
9727 if ((ctx->options & E2F_OPT_PREEN) &&
9728 !(ptr->flags & PR_PREEN_NOHDR)) {
9729 printf("%s: ", ctx->device_name ?
9730 ctx->device_name : ctx->filesystem_name);
9731 }
9732 if (*message)
9733 print_e2fsck_message(ctx, _(message), pctx, 1);
9734 }
9735 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
9736 preenhalt(ctx);
9737
9738 if (ptr->flags & PR_FATAL)
Rob Landley7c94bed2006-05-03 21:58:45 +00009739 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009740
9741 if (ptr->prompt == PROMPT_NONE) {
9742 if (ptr->flags & PR_NOCOLLATE)
9743 answer = -1;
9744 else
9745 answer = def_yn;
9746 } else {
9747 if (ctx->options & E2F_OPT_PREEN) {
9748 answer = def_yn;
9749 if (!(ptr->flags & PR_PREEN_NOMSG))
9750 print_answer = 1;
9751 } else if ((ptr->flags & PR_LATCH_MASK) &&
9752 (ldesc->flags & (PRL_YES | PRL_NO))) {
9753 if (!suppress)
9754 print_answer = 1;
9755 if (ldesc->flags & PRL_YES)
9756 answer = 1;
9757 else
9758 answer = 0;
9759 } else
9760 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
9761 if (!answer && !(ptr->flags & PR_NO_OK))
9762 ext2fs_unmark_valid(fs);
9763
9764 if (print_answer)
9765 printf("%s.\n", answer ?
9766 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
9767
9768 }
9769
9770 if ((ptr->prompt == PROMPT_ABORT) && answer)
Rob Landley7c94bed2006-05-03 21:58:45 +00009771 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009772
9773 if (ptr->flags & PR_AFTER_CODE)
9774 answer = fix_problem(ctx, ptr->second_code, pctx);
9775
9776 return answer;
9777}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009778
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009779/*
9780 * linux/fs/recovery.c
9781 *
9782 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009783 */
9784
9785/*
9786 * Maintain information about the progress of the recovery job, so that
9787 * the different passes can carry information between them.
9788 */
9789struct recovery_info
9790{
9791 tid_t start_transaction;
9792 tid_t end_transaction;
9793
9794 int nr_replays;
9795 int nr_revokes;
9796 int nr_revoke_hits;
9797};
9798
9799enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
9800static int do_one_pass(journal_t *journal,
9801 struct recovery_info *info, enum passtype pass);
9802static int scan_revoke_records(journal_t *, struct buffer_head *,
9803 tid_t, struct recovery_info *);
9804
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009805/*
9806 * Read a block from the journal
9807 */
9808
9809static int jread(struct buffer_head **bhp, journal_t *journal,
9810 unsigned int offset)
9811{
9812 int err;
9813 unsigned long blocknr;
9814 struct buffer_head *bh;
9815
9816 *bhp = NULL;
9817
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009818 err = journal_bmap(journal, offset, &blocknr);
9819
9820 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +00009821 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009822 return err;
9823 }
9824
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009825 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009826 if (!bh)
9827 return -ENOMEM;
9828
9829 if (!buffer_uptodate(bh)) {
9830 /* If this is a brand new buffer, start readahead.
9831 Otherwise, we assume we are already reading it. */
9832 if (!buffer_req(bh))
9833 do_readahead(journal, offset);
9834 wait_on_buffer(bh);
9835 }
9836
9837 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +00009838 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009839 brelse(bh);
9840 return -EIO;
9841 }
9842
9843 *bhp = bh;
9844 return 0;
9845}
9846
9847
9848/*
9849 * Count the number of in-use tags in a journal descriptor block.
9850 */
9851
9852static int count_tags(struct buffer_head *bh, int size)
9853{
9854 char * tagp;
9855 journal_block_tag_t * tag;
9856 int nr = 0;
9857
9858 tagp = &bh->b_data[sizeof(journal_header_t)];
9859
9860 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
9861 tag = (journal_block_tag_t *) tagp;
9862
9863 nr++;
9864 tagp += sizeof(journal_block_tag_t);
9865 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
9866 tagp += 16;
9867
9868 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
9869 break;
9870 }
9871
9872 return nr;
9873}
9874
9875
9876/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00009877#define wrap(journal, var) \
9878do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009879 if (var >= (journal)->j_last) \
9880 var -= ((journal)->j_last - (journal)->j_first); \
9881} while (0)
9882
9883/**
9884 * int journal_recover(journal_t *journal) - recovers a on-disk journal
9885 * @journal: the journal to recover
9886 *
9887 * The primary function for recovering the log contents when mounting a
9888 * journaled device.
9889 *
9890 * Recovery is done in three passes. In the first pass, we look for the
9891 * end of the log. In the second, we assemble the list of revoke
9892 * blocks. In the third and final pass, we replay any un-revoked blocks
9893 * in the log.
9894 */
9895int journal_recover(journal_t *journal)
9896{
9897 int err;
9898 journal_superblock_t * sb;
9899
9900 struct recovery_info info;
9901
9902 memset(&info, 0, sizeof(info));
9903 sb = journal->j_superblock;
9904
9905 /*
9906 * The journal superblock's s_start field (the current log head)
9907 * is always zero if, and only if, the journal was cleanly
9908 * unmounted.
9909 */
9910
9911 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009912 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
9913 return 0;
9914 }
9915
9916 err = do_one_pass(journal, &info, PASS_SCAN);
9917 if (!err)
9918 err = do_one_pass(journal, &info, PASS_REVOKE);
9919 if (!err)
9920 err = do_one_pass(journal, &info, PASS_REPLAY);
9921
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009922 /* Restart the log at the next transaction ID, thus invalidating
9923 * any existing commit records in the log. */
9924 journal->j_transaction_sequence = ++info.end_transaction;
9925
9926 journal_clear_revoke(journal);
9927 sync_blockdev(journal->j_fs_dev);
9928 return err;
9929}
9930
9931static int do_one_pass(journal_t *journal,
9932 struct recovery_info *info, enum passtype pass)
9933{
9934 unsigned int first_commit_ID, next_commit_ID;
9935 unsigned long next_log_block;
9936 int err, success = 0;
9937 journal_superblock_t * sb;
9938 journal_header_t * tmp;
9939 struct buffer_head * bh;
9940 unsigned int sequence;
9941 int blocktype;
9942
9943 /* Precompute the maximum metadata descriptors in a descriptor block */
9944 int MAX_BLOCKS_PER_DESC;
9945 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
9946 / sizeof(journal_block_tag_t));
9947
9948 /*
9949 * First thing is to establish what we expect to find in the log
9950 * (in terms of transaction IDs), and where (in terms of log
9951 * block offsets): query the superblock.
9952 */
9953
9954 sb = journal->j_superblock;
9955 next_commit_ID = ntohl(sb->s_sequence);
9956 next_log_block = ntohl(sb->s_start);
9957
9958 first_commit_ID = next_commit_ID;
9959 if (pass == PASS_SCAN)
9960 info->start_transaction = first_commit_ID;
9961
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009962 /*
9963 * Now we walk through the log, transaction by transaction,
9964 * making sure that each transaction has a commit block in the
9965 * expected place. Each complete transaction gets replayed back
9966 * into the main filesystem.
9967 */
9968
9969 while (1) {
9970 int flags;
9971 char * tagp;
9972 journal_block_tag_t * tag;
9973 struct buffer_head * obh;
9974 struct buffer_head * nbh;
9975
9976 /* If we already know where to stop the log traversal,
9977 * check right now that we haven't gone past the end of
9978 * the log. */
9979
9980 if (pass != PASS_SCAN)
9981 if (tid_geq(next_commit_ID, info->end_transaction))
9982 break;
9983
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009984 /* Skip over each chunk of the transaction looking
9985 * either the next descriptor block or the final commit
9986 * record. */
9987
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009988 err = jread(&bh, journal, next_log_block);
9989 if (err)
9990 goto failed;
9991
9992 next_log_block++;
9993 wrap(journal, next_log_block);
9994
9995 /* What kind of buffer is it?
9996 *
9997 * If it is a descriptor block, check that it has the
9998 * expected sequence number. Otherwise, we're all done
9999 * here. */
10000
10001 tmp = (journal_header_t *)bh->b_data;
10002
10003 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
10004 brelse(bh);
10005 break;
10006 }
10007
10008 blocktype = ntohl(tmp->h_blocktype);
10009 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010010
10011 if (sequence != next_commit_ID) {
10012 brelse(bh);
10013 break;
10014 }
10015
10016 /* OK, we have a valid descriptor block which matches
10017 * all of the sequence number checks. What are we going
10018 * to do with it? That depends on the pass... */
10019
10020 switch(blocktype) {
10021 case JFS_DESCRIPTOR_BLOCK:
10022 /* If it is a valid descriptor block, replay it
10023 * in pass REPLAY; otherwise, just skip over the
10024 * blocks it describes. */
10025 if (pass != PASS_REPLAY) {
10026 next_log_block +=
10027 count_tags(bh, journal->j_blocksize);
10028 wrap(journal, next_log_block);
10029 brelse(bh);
10030 continue;
10031 }
10032
10033 /* A descriptor block: we can now write all of
10034 * the data blocks. Yay, useful work is finally
10035 * getting done here! */
10036
10037 tagp = &bh->b_data[sizeof(journal_header_t)];
10038 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
10039 <= journal->j_blocksize) {
10040 unsigned long io_block;
10041
10042 tag = (journal_block_tag_t *) tagp;
10043 flags = ntohl(tag->t_flags);
10044
10045 io_block = next_log_block++;
10046 wrap(journal, next_log_block);
10047 err = jread(&obh, journal, io_block);
10048 if (err) {
10049 /* Recover what we can, but
10050 * report failure at the end. */
10051 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000010052 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010053 "block %ld in log\n",
10054 err, io_block);
10055 } else {
10056 unsigned long blocknr;
10057
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010058 blocknr = ntohl(tag->t_blocknr);
10059
10060 /* If the block has been
10061 * revoked, then we're all done
10062 * here. */
10063 if (journal_test_revoke
10064 (journal, blocknr,
10065 next_commit_ID)) {
10066 brelse(obh);
10067 ++info->nr_revoke_hits;
10068 goto skip_write;
10069 }
10070
10071 /* Find a buffer for the new
10072 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010073 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010074 blocknr,
10075 journal->j_blocksize);
10076 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000010077 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010078 "during recovery.\n");
10079 err = -ENOMEM;
10080 brelse(bh);
10081 brelse(obh);
10082 goto failed;
10083 }
10084
10085 lock_buffer(nbh);
10086 memcpy(nbh->b_data, obh->b_data,
10087 journal->j_blocksize);
10088 if (flags & JFS_FLAG_ESCAPE) {
10089 *((unsigned int *)bh->b_data) =
10090 htonl(JFS_MAGIC_NUMBER);
10091 }
10092
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010093 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010094 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010095 ++info->nr_replays;
10096 /* ll_rw_block(WRITE, 1, &nbh); */
10097 unlock_buffer(nbh);
10098 brelse(obh);
10099 brelse(nbh);
10100 }
10101
10102 skip_write:
10103 tagp += sizeof(journal_block_tag_t);
10104 if (!(flags & JFS_FLAG_SAME_UUID))
10105 tagp += 16;
10106
10107 if (flags & JFS_FLAG_LAST_TAG)
10108 break;
10109 }
10110
10111 brelse(bh);
10112 continue;
10113
10114 case JFS_COMMIT_BLOCK:
10115 /* Found an expected commit block: not much to
10116 * do other than move on to the next sequence
10117 * number. */
10118 brelse(bh);
10119 next_commit_ID++;
10120 continue;
10121
10122 case JFS_REVOKE_BLOCK:
10123 /* If we aren't in the REVOKE pass, then we can
10124 * just skip over this block. */
10125 if (pass != PASS_REVOKE) {
10126 brelse(bh);
10127 continue;
10128 }
10129
10130 err = scan_revoke_records(journal, bh,
10131 next_commit_ID, info);
10132 brelse(bh);
10133 if (err)
10134 goto failed;
10135 continue;
10136
10137 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010138 goto done;
10139 }
10140 }
10141
10142 done:
10143 /*
10144 * We broke out of the log scan loop: either we came to the
10145 * known end of the log or we found an unexpected block in the
10146 * log. If the latter happened, then we know that the "current"
10147 * transaction marks the end of the valid log.
10148 */
10149
10150 if (pass == PASS_SCAN)
10151 info->end_transaction = next_commit_ID;
10152 else {
10153 /* It's really bad news if different passes end up at
10154 * different places (but possible due to IO errors). */
10155 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000010156 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010157 "transaction %u, expected %u\n",
10158 pass, next_commit_ID, info->end_transaction);
10159 if (!success)
10160 success = -EIO;
10161 }
10162 }
10163
10164 return success;
10165
10166 failed:
10167 return err;
10168}
10169
10170
10171/* Scan a revoke record, marking all blocks mentioned as revoked. */
10172
10173static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
10174 tid_t sequence, struct recovery_info *info)
10175{
10176 journal_revoke_header_t *header;
10177 int offset, max;
10178
10179 header = (journal_revoke_header_t *) bh->b_data;
10180 offset = sizeof(journal_revoke_header_t);
10181 max = ntohl(header->r_count);
10182
10183 while (offset < max) {
10184 unsigned long blocknr;
10185 int err;
10186
10187 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
10188 offset += 4;
10189 err = journal_set_revoke(journal, blocknr, sequence);
10190 if (err)
10191 return err;
10192 ++info->nr_revokes;
10193 }
10194 return 0;
10195}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010196
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010197
10198/*
10199 * rehash.c --- rebuild hash tree directories
10200 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010201 * This algorithm is designed for simplicity of implementation and to
10202 * pack the directory as much as possible. It however requires twice
10203 * as much memory as the size of the directory. The maximum size
10204 * directory supported using a 4k blocksize is roughly a gigabyte, and
10205 * so there may very well be problems with machines that don't have
10206 * virtual memory, and obscenely large directories.
10207 *
10208 * An alternate algorithm which is much more disk intensive could be
10209 * written, and probably will need to be written in the future. The
10210 * design goals of such an algorithm are: (a) use (roughly) constant
10211 * amounts of memory, no matter how large the directory, (b) the
10212 * directory must be safe at all times, even if e2fsck is interrupted
10213 * in the middle, (c) we must use minimal amounts of extra disk
10214 * blocks. This pretty much requires an incremental approach, where
10215 * we are reading from one part of the directory, and inserting into
10216 * the front half. So the algorithm will have to keep track of a
10217 * moving block boundary between the new tree and the old tree, and
10218 * files will need to be moved from the old directory and inserted
10219 * into the new tree. If the new directory requires space which isn't
10220 * yet available, blocks from the beginning part of the old directory
10221 * may need to be moved to the end of the directory to make room for
10222 * the new tree:
10223 *
10224 * --------------------------------------------------------
10225 * | new tree | | old tree |
10226 * --------------------------------------------------------
10227 * ^ ptr ^ptr
10228 * tail new head old
10229 *
10230 * This is going to be a pain in the tuckus to implement, and will
10231 * require a lot more disk accesses. So I'm going to skip it for now;
10232 * it's only really going to be an issue for really, really big
10233 * filesystems (when we reach the level of tens of millions of files
10234 * in a single directory). It will probably be easier to simply
10235 * require that e2fsck use VM first.
10236 */
10237
10238struct fill_dir_struct {
10239 char *buf;
10240 struct ext2_inode *inode;
10241 int err;
10242 e2fsck_t ctx;
10243 struct hash_entry *harray;
10244 int max_array, num_array;
10245 int dir_size;
10246 int compress;
10247 ino_t parent;
10248};
10249
10250struct hash_entry {
10251 ext2_dirhash_t hash;
10252 ext2_dirhash_t minor_hash;
10253 struct ext2_dir_entry *dir;
10254};
10255
10256struct out_dir {
10257 int num;
10258 int max;
10259 char *buf;
10260 ext2_dirhash_t *hashes;
10261};
10262
10263static int fill_dir_block(ext2_filsys fs,
10264 blk_t *block_nr,
10265 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010266 blk_t ref_block FSCK_ATTR((unused)),
10267 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010268 void *priv_data)
10269{
10270 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
10271 struct hash_entry *new_array, *ent;
10272 struct ext2_dir_entry *dirent;
10273 char *dir;
10274 unsigned int offset, dir_offset;
10275
10276 if (blockcnt < 0)
10277 return 0;
10278
10279 offset = blockcnt * fs->blocksize;
10280 if (offset + fs->blocksize > fd->inode->i_size) {
10281 fd->err = EXT2_ET_DIR_CORRUPTED;
10282 return BLOCK_ABORT;
10283 }
10284 dir = (fd->buf+offset);
10285 if (HOLE_BLKADDR(*block_nr)) {
10286 memset(dir, 0, fs->blocksize);
10287 dirent = (struct ext2_dir_entry *) dir;
10288 dirent->rec_len = fs->blocksize;
10289 } else {
10290 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
10291 if (fd->err)
10292 return BLOCK_ABORT;
10293 }
10294 /* While the directory block is "hot", index it. */
10295 dir_offset = 0;
10296 while (dir_offset < fs->blocksize) {
10297 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
10298 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
10299 (dirent->rec_len < 8) ||
10300 ((dirent->rec_len % 4) != 0) ||
10301 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
10302 fd->err = EXT2_ET_DIR_CORRUPTED;
10303 return BLOCK_ABORT;
10304 }
10305 dir_offset += dirent->rec_len;
10306 if (dirent->inode == 0)
10307 continue;
10308 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
10309 (dirent->name[0] == '.'))
10310 continue;
10311 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
10312 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
10313 fd->parent = dirent->inode;
10314 continue;
10315 }
10316 if (fd->num_array >= fd->max_array) {
10317 new_array = realloc(fd->harray,
10318 sizeof(struct hash_entry) * (fd->max_array+500));
10319 if (!new_array) {
10320 fd->err = ENOMEM;
10321 return BLOCK_ABORT;
10322 }
10323 fd->harray = new_array;
10324 fd->max_array += 500;
10325 }
10326 ent = fd->harray + fd->num_array++;
10327 ent->dir = dirent;
10328 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
10329 if (fd->compress)
10330 ent->hash = ent->minor_hash = 0;
10331 else {
10332 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
10333 dirent->name,
10334 dirent->name_len & 0xFF,
10335 fs->super->s_hash_seed,
10336 &ent->hash, &ent->minor_hash);
10337 if (fd->err)
10338 return BLOCK_ABORT;
10339 }
10340 }
10341
10342 return 0;
10343}
10344
10345/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010346static int name_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010347{
10348 const struct hash_entry *he_a = (const struct hash_entry *) a;
10349 const struct hash_entry *he_b = (const struct hash_entry *) b;
10350 int ret;
10351 int min_len;
10352
10353 min_len = he_a->dir->name_len;
10354 if (min_len > he_b->dir->name_len)
10355 min_len = he_b->dir->name_len;
10356
10357 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
10358 if (ret == 0) {
10359 if (he_a->dir->name_len > he_b->dir->name_len)
10360 ret = 1;
10361 else if (he_a->dir->name_len < he_b->dir->name_len)
10362 ret = -1;
10363 else
10364 ret = he_b->dir->inode - he_a->dir->inode;
10365 }
10366 return ret;
10367}
10368
10369/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010370static int hash_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010371{
10372 const struct hash_entry *he_a = (const struct hash_entry *) a;
10373 const struct hash_entry *he_b = (const struct hash_entry *) b;
10374 int ret;
10375
10376 if (he_a->hash > he_b->hash)
10377 ret = 1;
10378 else if (he_a->hash < he_b->hash)
10379 ret = -1;
10380 else {
10381 if (he_a->minor_hash > he_b->minor_hash)
10382 ret = 1;
10383 else if (he_a->minor_hash < he_b->minor_hash)
10384 ret = -1;
10385 else
10386 ret = name_cmp(a, b);
10387 }
10388 return ret;
10389}
10390
10391static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
10392 int blocks)
10393{
10394 void *new_mem;
10395
10396 if (outdir->max) {
10397 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
10398 if (!new_mem)
10399 return ENOMEM;
10400 outdir->buf = new_mem;
10401 new_mem = realloc(outdir->hashes,
10402 blocks * sizeof(ext2_dirhash_t));
10403 if (!new_mem)
10404 return ENOMEM;
10405 outdir->hashes = new_mem;
10406 } else {
10407 outdir->buf = malloc(blocks * fs->blocksize);
10408 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
10409 outdir->num = 0;
10410 }
10411 outdir->max = blocks;
10412 return 0;
10413}
10414
10415static void free_out_dir(struct out_dir *outdir)
10416{
Rob Landleye7c43b62006-03-01 16:39:45 +000010417 free(outdir->buf);
10418 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010419 outdir->max = 0;
10420 outdir->num =0;
10421}
10422
10423static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
10424 char ** ret)
10425{
10426 errcode_t retval;
10427
10428 if (outdir->num >= outdir->max) {
10429 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
10430 if (retval)
10431 return retval;
10432 }
10433 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
10434 memset(*ret, 0, fs->blocksize);
10435 return 0;
10436}
10437
10438/*
10439 * This function is used to make a unique filename. We do this by
10440 * appending ~0, and then incrementing the number. However, we cannot
10441 * expand the length of the filename beyond the padding available in
10442 * the directory entry.
10443 */
10444static void mutate_name(char *str, __u16 *len)
10445{
10446 int i;
10447 __u16 l = *len & 0xFF, h = *len & 0xff00;
10448
10449 /*
10450 * First check to see if it looks the name has been mutated
10451 * already
10452 */
10453 for (i = l-1; i > 0; i--) {
10454 if (!isdigit(str[i]))
10455 break;
10456 }
10457 if ((i == l-1) || (str[i] != '~')) {
10458 if (((l-1) & 3) < 2)
10459 l += 2;
10460 else
10461 l = (l+3) & ~3;
10462 str[l-2] = '~';
10463 str[l-1] = '0';
10464 *len = l | h;
10465 return;
10466 }
10467 for (i = l-1; i >= 0; i--) {
10468 if (isdigit(str[i])) {
10469 if (str[i] == '9')
10470 str[i] = '0';
10471 else {
10472 str[i]++;
10473 return;
10474 }
10475 continue;
10476 }
10477 if (i == 1) {
10478 if (str[0] == 'z')
10479 str[0] = 'A';
10480 else if (str[0] == 'Z') {
10481 str[0] = '~';
10482 str[1] = '0';
10483 } else
10484 str[0]++;
10485 } else if (i > 0) {
10486 str[i] = '1';
10487 str[i-1] = '~';
10488 } else {
10489 if (str[0] == '~')
10490 str[0] = 'a';
10491 else
10492 str[0]++;
10493 }
10494 break;
10495 }
10496}
10497
10498static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
10499 ext2_ino_t ino,
10500 struct fill_dir_struct *fd)
10501{
10502 struct problem_context pctx;
10503 struct hash_entry *ent, *prev;
10504 int i, j;
10505 int fixed = 0;
10506 char new_name[256];
10507 __u16 new_len;
10508
10509 clear_problem_context(&pctx);
10510 pctx.ino = ino;
10511
10512 for (i=1; i < fd->num_array; i++) {
10513 ent = fd->harray + i;
10514 prev = ent - 1;
10515 if (!ent->dir->inode ||
10516 ((ent->dir->name_len & 0xFF) !=
10517 (prev->dir->name_len & 0xFF)) ||
10518 (strncmp(ent->dir->name, prev->dir->name,
10519 ent->dir->name_len & 0xFF)))
10520 continue;
10521 pctx.dirent = ent->dir;
10522 if ((ent->dir->inode == prev->dir->inode) &&
10523 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
10524 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
10525 ent->dir->inode = 0;
10526 fixed++;
10527 continue;
10528 }
10529 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
10530 new_len = ent->dir->name_len;
10531 mutate_name(new_name, &new_len);
10532 for (j=0; j < fd->num_array; j++) {
10533 if ((i==j) ||
10534 ((ent->dir->name_len & 0xFF) !=
10535 (fd->harray[j].dir->name_len & 0xFF)) ||
10536 (strncmp(new_name, fd->harray[j].dir->name,
10537 new_len & 0xFF)))
10538 continue;
10539 mutate_name(new_name, &new_len);
10540
10541 j = -1;
10542 }
10543 new_name[new_len & 0xFF] = 0;
10544 pctx.str = new_name;
10545 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
10546 memcpy(ent->dir->name, new_name, new_len & 0xFF);
10547 ent->dir->name_len = new_len;
10548 ext2fs_dirhash(fs->super->s_def_hash_version,
10549 ent->dir->name,
10550 ent->dir->name_len & 0xFF,
10551 fs->super->s_hash_seed,
10552 &ent->hash, &ent->minor_hash);
10553 fixed++;
10554 }
10555 }
10556 return fixed;
10557}
10558
10559
10560static errcode_t copy_dir_entries(ext2_filsys fs,
10561 struct fill_dir_struct *fd,
10562 struct out_dir *outdir)
10563{
10564 errcode_t retval;
10565 char *block_start;
10566 struct hash_entry *ent;
10567 struct ext2_dir_entry *dirent;
10568 int i, rec_len, left;
10569 ext2_dirhash_t prev_hash;
10570 int offset;
10571
10572 outdir->max = 0;
10573 retval = alloc_size_dir(fs, outdir,
10574 (fd->dir_size / fs->blocksize) + 2);
10575 if (retval)
10576 return retval;
10577 outdir->num = fd->compress ? 0 : 1;
10578 offset = 0;
10579 outdir->hashes[0] = 0;
10580 prev_hash = 1;
10581 if ((retval = get_next_block(fs, outdir, &block_start)))
10582 return retval;
10583 dirent = (struct ext2_dir_entry *) block_start;
10584 left = fs->blocksize;
10585 for (i=0; i < fd->num_array; i++) {
10586 ent = fd->harray + i;
10587 if (ent->dir->inode == 0)
10588 continue;
10589 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
10590 if (rec_len > left) {
10591 if (left)
10592 dirent->rec_len += left;
10593 if ((retval = get_next_block(fs, outdir,
10594 &block_start)))
10595 return retval;
10596 offset = 0;
10597 }
10598 left = fs->blocksize - offset;
10599 dirent = (struct ext2_dir_entry *) (block_start + offset);
10600 if (offset == 0) {
10601 if (ent->hash == prev_hash)
10602 outdir->hashes[outdir->num-1] = ent->hash | 1;
10603 else
10604 outdir->hashes[outdir->num-1] = ent->hash;
10605 }
10606 dirent->inode = ent->dir->inode;
10607 dirent->name_len = ent->dir->name_len;
10608 dirent->rec_len = rec_len;
10609 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
10610 offset += rec_len;
10611 left -= rec_len;
10612 if (left < 12) {
10613 dirent->rec_len += left;
10614 offset += left;
10615 left = 0;
10616 }
10617 prev_hash = ent->hash;
10618 }
10619 if (left)
10620 dirent->rec_len += left;
10621
10622 return 0;
10623}
10624
10625
10626static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
10627 ext2_ino_t ino, ext2_ino_t parent)
10628{
10629 struct ext2_dir_entry *dir;
10630 struct ext2_dx_root_info *root;
10631 struct ext2_dx_countlimit *limits;
10632 int filetype = 0;
10633
10634 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
10635 filetype = EXT2_FT_DIR << 8;
10636
10637 memset(buf, 0, fs->blocksize);
10638 dir = (struct ext2_dir_entry *) buf;
10639 dir->inode = ino;
10640 dir->name[0] = '.';
10641 dir->name_len = 1 | filetype;
10642 dir->rec_len = 12;
10643 dir = (struct ext2_dir_entry *) (buf + 12);
10644 dir->inode = parent;
10645 dir->name[0] = '.';
10646 dir->name[1] = '.';
10647 dir->name_len = 2 | filetype;
10648 dir->rec_len = fs->blocksize - 12;
10649
10650 root = (struct ext2_dx_root_info *) (buf+24);
10651 root->reserved_zero = 0;
10652 root->hash_version = fs->super->s_def_hash_version;
10653 root->info_length = 8;
10654 root->indirect_levels = 0;
10655 root->unused_flags = 0;
10656
10657 limits = (struct ext2_dx_countlimit *) (buf+32);
10658 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
10659 limits->count = 0;
10660
10661 return root;
10662}
10663
10664
10665static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
10666{
10667 struct ext2_dir_entry *dir;
10668 struct ext2_dx_countlimit *limits;
10669
10670 memset(buf, 0, fs->blocksize);
10671 dir = (struct ext2_dir_entry *) buf;
10672 dir->inode = 0;
10673 dir->rec_len = fs->blocksize;
10674
10675 limits = (struct ext2_dx_countlimit *) (buf+8);
10676 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
10677 limits->count = 0;
10678
10679 return (struct ext2_dx_entry *) limits;
10680}
10681
10682/*
10683 * This function takes the leaf nodes which have been written in
10684 * outdir, and populates the root node and any necessary interior nodes.
10685 */
10686static errcode_t calculate_tree(ext2_filsys fs,
10687 struct out_dir *outdir,
10688 ext2_ino_t ino,
10689 ext2_ino_t parent)
10690{
10691 struct ext2_dx_root_info *root_info;
10692 struct ext2_dx_entry *root, *dx_ent = 0;
10693 struct ext2_dx_countlimit *root_limit, *limit;
10694 errcode_t retval;
10695 char * block_start;
10696 int i, c1, c2, nblks;
10697 int limit_offset, root_offset;
10698
10699 root_info = set_root_node(fs, outdir->buf, ino, parent);
10700 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
10701 root_info->info_length;
10702 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10703 c1 = root_limit->limit;
10704 nblks = outdir->num;
10705
10706 /* Write out the pointer blocks */
10707 if (nblks-1 <= c1) {
10708 /* Just write out the root block, and we're done */
10709 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
10710 for (i=1; i < nblks; i++) {
10711 root->block = ext2fs_cpu_to_le32(i);
10712 if (i != 1)
10713 root->hash =
10714 ext2fs_cpu_to_le32(outdir->hashes[i]);
10715 root++;
10716 c1--;
10717 }
10718 } else {
10719 c2 = 0;
10720 limit = 0;
10721 root_info->indirect_levels = 1;
10722 for (i=1; i < nblks; i++) {
10723 if (c1 == 0)
10724 return ENOSPC;
10725 if (c2 == 0) {
10726 if (limit)
10727 limit->limit = limit->count =
10728 ext2fs_cpu_to_le16(limit->limit);
10729 root = (struct ext2_dx_entry *)
10730 (outdir->buf + root_offset);
10731 root->block = ext2fs_cpu_to_le32(outdir->num);
10732 if (i != 1)
10733 root->hash =
10734 ext2fs_cpu_to_le32(outdir->hashes[i]);
10735 if ((retval = get_next_block(fs, outdir,
10736 &block_start)))
10737 return retval;
10738 dx_ent = set_int_node(fs, block_start);
10739 limit = (struct ext2_dx_countlimit *) dx_ent;
10740 c2 = limit->limit;
10741 root_offset += sizeof(struct ext2_dx_entry);
10742 c1--;
10743 }
10744 dx_ent->block = ext2fs_cpu_to_le32(i);
10745 if (c2 != limit->limit)
10746 dx_ent->hash =
10747 ext2fs_cpu_to_le32(outdir->hashes[i]);
10748 dx_ent++;
10749 c2--;
10750 }
10751 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
10752 limit->limit = ext2fs_cpu_to_le16(limit->limit);
10753 }
10754 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10755 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
10756 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
10757
10758 return 0;
10759}
10760
10761struct write_dir_struct {
10762 struct out_dir *outdir;
10763 errcode_t err;
10764 e2fsck_t ctx;
10765 int cleared;
10766};
10767
10768/*
10769 * Helper function which writes out a directory block.
10770 */
10771static int write_dir_block(ext2_filsys fs,
10772 blk_t *block_nr,
10773 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010774 blk_t ref_block FSCK_ATTR((unused)),
10775 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010776 void *priv_data)
10777{
10778 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
10779 blk_t blk;
10780 char *dir;
10781
10782 if (*block_nr == 0)
10783 return 0;
10784 if (blockcnt >= wd->outdir->num) {
10785 e2fsck_read_bitmaps(wd->ctx);
10786 blk = *block_nr;
10787 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
10788 ext2fs_block_alloc_stats(fs, blk, -1);
10789 *block_nr = 0;
10790 wd->cleared++;
10791 return BLOCK_CHANGED;
10792 }
10793 if (blockcnt < 0)
10794 return 0;
10795
10796 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
10797 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
10798 if (wd->err)
10799 return BLOCK_ABORT;
10800 return 0;
10801}
10802
10803static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
10804 struct out_dir *outdir,
10805 ext2_ino_t ino, int compress)
10806{
10807 struct write_dir_struct wd;
10808 errcode_t retval;
10809 struct ext2_inode inode;
10810
10811 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
10812 if (retval)
10813 return retval;
10814
10815 wd.outdir = outdir;
10816 wd.err = 0;
10817 wd.ctx = ctx;
10818 wd.cleared = 0;
10819
10820 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10821 write_dir_block, &wd);
10822 if (retval)
10823 return retval;
10824 if (wd.err)
10825 return wd.err;
10826
10827 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10828 if (compress)
10829 inode.i_flags &= ~EXT2_INDEX_FL;
10830 else
10831 inode.i_flags |= EXT2_INDEX_FL;
10832 inode.i_size = outdir->num * fs->blocksize;
10833 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
10834 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
10835
10836 return 0;
10837}
10838
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010839static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010840{
10841 ext2_filsys fs = ctx->fs;
10842 errcode_t retval;
10843 struct ext2_inode inode;
10844 char *dir_buf = 0;
10845 struct fill_dir_struct fd;
10846 struct out_dir outdir;
10847
10848 outdir.max = outdir.num = 0;
10849 outdir.buf = 0;
10850 outdir.hashes = 0;
10851 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10852
10853 retval = ENOMEM;
10854 fd.harray = 0;
10855 dir_buf = malloc(inode.i_size);
10856 if (!dir_buf)
10857 goto errout;
10858
10859 fd.max_array = inode.i_size / 32;
10860 fd.num_array = 0;
10861 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
10862 if (!fd.harray)
10863 goto errout;
10864
10865 fd.ctx = ctx;
10866 fd.buf = dir_buf;
10867 fd.inode = &inode;
10868 fd.err = 0;
10869 fd.dir_size = 0;
10870 fd.compress = 0;
10871 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
10872 (inode.i_size / fs->blocksize) < 2)
10873 fd.compress = 1;
10874 fd.parent = 0;
10875
10876 /* Read in the entire directory into memory */
10877 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10878 fill_dir_block, &fd);
10879 if (fd.err) {
10880 retval = fd.err;
10881 goto errout;
10882 }
10883
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010884 /* Sort the list */
10885resort:
10886 if (fd.compress)
10887 qsort(fd.harray+2, fd.num_array-2,
10888 sizeof(struct hash_entry), name_cmp);
10889 else
10890 qsort(fd.harray, fd.num_array,
10891 sizeof(struct hash_entry), hash_cmp);
10892
10893 /*
10894 * Look for duplicates
10895 */
10896 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
10897 goto resort;
10898
10899 if (ctx->options & E2F_OPT_NO) {
10900 retval = 0;
10901 goto errout;
10902 }
10903
10904 /*
10905 * Copy the directory entries. In a htree directory these
10906 * will become the leaf nodes.
10907 */
10908 retval = copy_dir_entries(fs, &fd, &outdir);
10909 if (retval)
10910 goto errout;
10911
10912 free(dir_buf); dir_buf = 0;
10913
10914 if (!fd.compress) {
10915 /* Calculate the interior nodes */
10916 retval = calculate_tree(fs, &outdir, ino, fd.parent);
10917 if (retval)
10918 goto errout;
10919 }
10920
10921 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010922
10923errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000010924 free(dir_buf);
10925 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010926
10927 free_out_dir(&outdir);
10928 return retval;
10929}
10930
10931void e2fsck_rehash_directories(e2fsck_t ctx)
10932{
10933 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010934 struct dir_info *dir;
10935 ext2_u32_iterate iter;
10936 ext2_ino_t ino;
10937 errcode_t retval;
10938 int i, cur, max, all_dirs, dir_index, first = 1;
10939
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010940 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
10941
10942 if (!ctx->dirs_to_hash && !all_dirs)
10943 return;
10944
10945 e2fsck_get_lost_and_found(ctx, 0);
10946
10947 clear_problem_context(&pctx);
10948
10949 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
10950 cur = 0;
10951 if (all_dirs) {
10952 i = 0;
10953 max = e2fsck_get_num_dirinfo(ctx);
10954 } else {
10955 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
10956 &iter);
10957 if (retval) {
10958 pctx.errcode = retval;
10959 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
10960 return;
10961 }
10962 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
10963 }
10964 while (1) {
10965 if (all_dirs) {
10966 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
10967 break;
10968 ino = dir->ino;
10969 } else {
10970 if (!ext2fs_u32_list_iterate(iter, &ino))
10971 break;
10972 }
10973 if (ino == ctx->lost_and_found)
10974 continue;
10975 pctx.dir = ino;
10976 if (first) {
10977 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
10978 first = 0;
10979 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010980 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
10981 if (pctx.errcode) {
10982 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
10983 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
10984 }
10985 if (ctx->progress && !ctx->progress_fd)
10986 e2fsck_simple_progress(ctx, "Rebuilding directory",
10987 100.0 * (float) (++cur) / (float) max, ino);
10988 }
10989 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
10990 if (!all_dirs)
10991 ext2fs_u32_list_iterate_end(iter);
10992
Rob Landleye7c43b62006-03-01 16:39:45 +000010993 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010994 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010995}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010996
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010997/*
10998 * linux/fs/revoke.c
10999 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011000 * Journal revoke routines for the generic filesystem journaling code;
11001 * part of the ext2fs journaling system.
11002 *
11003 * Revoke is the mechanism used to prevent old log records for deleted
11004 * metadata from being replayed on top of newer data using the same
11005 * blocks. The revoke mechanism is used in two separate places:
11006 *
11007 * + Commit: during commit we write the entire list of the current
11008 * transaction's revoked blocks to the journal
11009 *
11010 * + Recovery: during recovery we record the transaction ID of all
11011 * revoked blocks. If there are multiple revoke records in the log
11012 * for a single block, only the last one counts, and if there is a log
11013 * entry for a block beyond the last revoke, then that log entry still
11014 * gets replayed.
11015 *
11016 * We can get interactions between revokes and new log data within a
11017 * single transaction:
11018 *
11019 * Block is revoked and then journaled:
11020 * The desired end result is the journaling of the new block, so we
11021 * cancel the revoke before the transaction commits.
11022 *
11023 * Block is journaled and then revoked:
11024 * The revoke must take precedence over the write of the block, so we
11025 * need either to cancel the journal entry or to write the revoke
11026 * later in the log than the log block. In this case, we choose the
11027 * latter: journaling a block cancels any revoke record for that block
11028 * in the current transaction, so any revoke for that block in the
11029 * transaction must have happened after the block was journaled and so
11030 * the revoke must take precedence.
11031 *
11032 * Block is revoked and then written as data:
11033 * The data write is allowed to succeed, but the revoke is _not_
11034 * cancelled. We still need to prevent old log records from
11035 * overwriting the new data. We don't even need to clear the revoke
11036 * bit here.
11037 *
11038 * Revoke information on buffers is a tri-state value:
11039 *
11040 * RevokeValid clear: no cached revoke status, need to look it up
11041 * RevokeValid set, Revoked clear:
11042 * buffer has not been revoked, and cancel_revoke
11043 * need do nothing.
11044 * RevokeValid set, Revoked set:
11045 * buffer has been revoked.
11046 */
11047
11048static kmem_cache_t *revoke_record_cache;
11049static kmem_cache_t *revoke_table_cache;
11050
11051/* Each revoke record represents one single revoked block. During
11052 journal replay, this involves recording the transaction ID of the
11053 last transaction to revoke this block. */
11054
11055struct jbd_revoke_record_s
11056{
11057 struct list_head hash;
11058 tid_t sequence; /* Used for recovery only */
11059 unsigned long blocknr;
11060};
11061
11062
11063/* The revoke table is just a simple hash table of revoke records. */
11064struct jbd_revoke_table_s
11065{
11066 /* It is conceivable that we might want a larger hash table
11067 * for recovery. Must be a power of two. */
11068 int hash_size;
11069 int hash_shift;
11070 struct list_head *hash_table;
11071};
11072
11073
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011074/* Utility functions to maintain the revoke table */
11075
11076/* Borrowed from buffer.c: this is a tried and tested block hash function */
11077static inline int hash(journal_t *journal, unsigned long block)
11078{
11079 struct jbd_revoke_table_s *table = journal->j_revoke;
11080 int hash_shift = table->hash_shift;
11081
11082 return ((block << (hash_shift - 6)) ^
11083 (block >> 13) ^
11084 (block << (hash_shift - 12))) & (table->hash_size - 1);
11085}
11086
11087static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
11088 tid_t seq)
11089{
11090 struct list_head *hash_list;
11091 struct jbd_revoke_record_s *record;
11092
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011093 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
11094 if (!record)
11095 goto oom;
11096
11097 record->sequence = seq;
11098 record->blocknr = blocknr;
11099 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11100 list_add(&record->hash, hash_list);
11101 return 0;
11102
11103oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011104 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011105}
11106
11107/* Find a revoke record in the journal's hash table. */
11108
11109static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
11110 unsigned long blocknr)
11111{
11112 struct list_head *hash_list;
11113 struct jbd_revoke_record_s *record;
11114
11115 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11116
11117 record = (struct jbd_revoke_record_s *) hash_list->next;
11118 while (&(record->hash) != hash_list) {
11119 if (record->blocknr == blocknr)
11120 return record;
11121 record = (struct jbd_revoke_record_s *) record->hash.next;
11122 }
11123 return NULL;
11124}
11125
11126int journal_init_revoke_caches(void)
11127{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011128 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011129 if (revoke_record_cache == 0)
11130 return -ENOMEM;
11131
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011132 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011133 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011134 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011135 revoke_record_cache = NULL;
11136 return -ENOMEM;
11137 }
11138 return 0;
11139}
11140
11141void journal_destroy_revoke_caches(void)
11142{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011143 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011144 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011145 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011146 revoke_table_cache = 0;
11147}
11148
11149/* Initialise the revoke table for a given journal to a given size. */
11150
11151int journal_init_revoke(journal_t *journal, int hash_size)
11152{
11153 int shift, tmp;
11154
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011155 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
11156 if (!journal->j_revoke)
11157 return -ENOMEM;
11158
11159 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011160 journal->j_revoke->hash_size = hash_size;
11161
11162 shift = 0;
11163 tmp = hash_size;
11164 while((tmp >>= 1UL) != 0UL)
11165 shift++;
11166 journal->j_revoke->hash_shift = shift;
11167
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011168 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011169 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011170 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011171 journal->j_revoke = NULL;
11172 return -ENOMEM;
11173 }
11174
11175 for (tmp = 0; tmp < hash_size; tmp++)
11176 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
11177
11178 return 0;
11179}
11180
11181/* Destoy a journal's revoke table. The table must already be empty! */
11182
11183void journal_destroy_revoke(journal_t *journal)
11184{
11185 struct jbd_revoke_table_s *table;
11186 struct list_head *hash_list;
11187 int i;
11188
11189 table = journal->j_revoke;
11190 if (!table)
11191 return;
11192
11193 for (i=0; i<table->hash_size; i++) {
11194 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011195 }
11196
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011197 free(table->hash_table);
11198 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011199 journal->j_revoke = NULL;
11200}
11201
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011202/*
11203 * Revoke support for recovery.
11204 *
11205 * Recovery needs to be able to:
11206 *
11207 * record all revoke records, including the tid of the latest instance
11208 * of each revoke in the journal
11209 *
11210 * check whether a given block in a given transaction should be replayed
11211 * (ie. has not been revoked by a revoke record in that or a subsequent
11212 * transaction)
11213 *
11214 * empty the revoke table after recovery.
11215 */
11216
11217/*
11218 * First, setting revoke records. We create a new revoke record for
11219 * every block ever revoked in the log as we scan it for recovery, and
11220 * we update the existing records if we find multiple revokes for a
11221 * single block.
11222 */
11223
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011224int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011225 tid_t sequence)
11226{
11227 struct jbd_revoke_record_s *record;
11228
11229 record = find_revoke_record(journal, blocknr);
11230 if (record) {
11231 /* If we have multiple occurences, only record the
11232 * latest sequence number in the hashed record */
11233 if (tid_gt(sequence, record->sequence))
11234 record->sequence = sequence;
11235 return 0;
11236 }
11237 return insert_revoke_hash(journal, blocknr, sequence);
11238}
11239
11240/*
11241 * Test revoke records. For a given block referenced in the log, has
11242 * that block been revoked? A revoke record with a given transaction
11243 * sequence number revokes all blocks in that transaction and earlier
11244 * ones, but later transactions still need replayed.
11245 */
11246
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011247int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011248 tid_t sequence)
11249{
11250 struct jbd_revoke_record_s *record;
11251
11252 record = find_revoke_record(journal, blocknr);
11253 if (!record)
11254 return 0;
11255 if (tid_gt(sequence, record->sequence))
11256 return 0;
11257 return 1;
11258}
11259
11260/*
11261 * Finally, once recovery is over, we need to clear the revoke table so
11262 * that it can be reused by the running filesystem.
11263 */
11264
11265void journal_clear_revoke(journal_t *journal)
11266{
11267 int i;
11268 struct list_head *hash_list;
11269 struct jbd_revoke_record_s *record;
11270 struct jbd_revoke_table_s *revoke_var;
11271
11272 revoke_var = journal->j_revoke;
11273
11274 for (i = 0; i < revoke_var->hash_size; i++) {
11275 hash_list = &revoke_var->hash_table[i];
11276 while (!list_empty(hash_list)) {
11277 record = (struct jbd_revoke_record_s*) hash_list->next;
11278 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011279 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011280 }
11281 }
11282}
11283
11284/*
11285 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011286 */
11287
11288#define MIN_CHECK 1
11289#define MAX_CHECK 2
11290
11291static void check_super_value(e2fsck_t ctx, const char *descr,
11292 unsigned long value, int flags,
11293 unsigned long min_val, unsigned long max_val)
11294{
11295 struct problem_context pctx;
11296
11297 if (((flags & MIN_CHECK) && (value < min_val)) ||
11298 ((flags & MAX_CHECK) && (value > max_val))) {
11299 clear_problem_context(&pctx);
11300 pctx.num = value;
11301 pctx.str = descr;
11302 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11303 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11304 }
11305}
11306
11307/*
11308 * This routine may get stubbed out in special compilations of the
11309 * e2fsck code..
11310 */
11311#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011312static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011313{
11314 return (ext2fs_get_device_size(ctx->filesystem_name,
11315 EXT2_BLOCK_SIZE(ctx->fs->super),
11316 &ctx->num_blocks));
11317}
11318#endif
11319
11320/*
11321 * helper function to release an inode
11322 */
11323struct process_block_struct {
11324 e2fsck_t ctx;
11325 char *buf;
11326 struct problem_context *pctx;
11327 int truncating;
11328 int truncate_offset;
11329 e2_blkcnt_t truncate_block;
11330 int truncated_blocks;
11331 int abort;
11332 errcode_t errcode;
11333};
11334
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011335static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011336 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011337 blk_t ref_blk FSCK_ATTR((unused)),
11338 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011339 void *priv_data)
11340{
11341 struct process_block_struct *pb;
11342 e2fsck_t ctx;
11343 struct problem_context *pctx;
11344 blk_t blk = *block_nr;
11345 int retval = 0;
11346
11347 pb = (struct process_block_struct *) priv_data;
11348 ctx = pb->ctx;
11349 pctx = pb->pctx;
11350
11351 pctx->blk = blk;
11352 pctx->blkcount = blockcnt;
11353
11354 if (HOLE_BLKADDR(blk))
11355 return 0;
11356
11357 if ((blk < fs->super->s_first_data_block) ||
11358 (blk >= fs->super->s_blocks_count)) {
11359 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
11360 return_abort:
11361 pb->abort = 1;
11362 return BLOCK_ABORT;
11363 }
11364
11365 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
11366 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
11367 goto return_abort;
11368 }
11369
11370 /*
11371 * If we are deleting an orphan, then we leave the fields alone.
11372 * If we are truncating an orphan, then update the inode fields
11373 * and clean up any partial block data.
11374 */
11375 if (pb->truncating) {
11376 /*
11377 * We only remove indirect blocks if they are
11378 * completely empty.
11379 */
11380 if (blockcnt < 0) {
11381 int i, limit;
11382 blk_t *bp;
11383
11384 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11385 pb->buf);
11386 if (pb->errcode)
11387 goto return_abort;
11388
11389 limit = fs->blocksize >> 2;
11390 for (i = 0, bp = (blk_t *) pb->buf;
11391 i < limit; i++, bp++)
11392 if (*bp)
11393 return 0;
11394 }
11395 /*
11396 * We don't remove direct blocks until we've reached
11397 * the truncation block.
11398 */
11399 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
11400 return 0;
11401 /*
11402 * If part of the last block needs truncating, we do
11403 * it here.
11404 */
11405 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
11406 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11407 pb->buf);
11408 if (pb->errcode)
11409 goto return_abort;
11410 memset(pb->buf + pb->truncate_offset, 0,
11411 fs->blocksize - pb->truncate_offset);
11412 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
11413 pb->buf);
11414 if (pb->errcode)
11415 goto return_abort;
11416 }
11417 pb->truncated_blocks++;
11418 *block_nr = 0;
11419 retval |= BLOCK_CHANGED;
11420 }
11421
11422 ext2fs_block_alloc_stats(fs, blk, -1);
11423 return retval;
11424}
11425
11426/*
11427 * This function releases an inode. Returns 1 if an inconsistency was
11428 * found. If the inode has a link count, then it is being truncated and
11429 * not deleted.
11430 */
11431static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
11432 struct ext2_inode *inode, char *block_buf,
11433 struct problem_context *pctx)
11434{
11435 struct process_block_struct pb;
11436 ext2_filsys fs = ctx->fs;
11437 errcode_t retval;
11438 __u32 count;
11439
11440 if (!ext2fs_inode_has_valid_blocks(inode))
11441 return 0;
11442
11443 pb.buf = block_buf + 3 * ctx->fs->blocksize;
11444 pb.ctx = ctx;
11445 pb.abort = 0;
11446 pb.errcode = 0;
11447 pb.pctx = pctx;
11448 if (inode->i_links_count) {
11449 pb.truncating = 1;
11450 pb.truncate_block = (e2_blkcnt_t)
11451 ((((long long)inode->i_size_high << 32) +
11452 inode->i_size + fs->blocksize - 1) /
11453 fs->blocksize);
11454 pb.truncate_offset = inode->i_size % fs->blocksize;
11455 } else {
11456 pb.truncating = 0;
11457 pb.truncate_block = 0;
11458 pb.truncate_offset = 0;
11459 }
11460 pb.truncated_blocks = 0;
11461 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
11462 block_buf, release_inode_block, &pb);
11463 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011464 bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011465 ino);
11466 return 1;
11467 }
11468 if (pb.abort)
11469 return 1;
11470
11471 /* Refresh the inode since ext2fs_block_iterate may have changed it */
11472 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
11473
11474 if (pb.truncated_blocks)
11475 inode->i_blocks -= pb.truncated_blocks *
11476 (fs->blocksize / 512);
11477
11478 if (inode->i_file_acl) {
11479 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
11480 block_buf, -1, &count);
11481 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
11482 retval = 0;
11483 count = 1;
11484 }
11485 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011486 bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011487 ino);
11488 return 1;
11489 }
11490 if (count == 0)
11491 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
11492 inode->i_file_acl = 0;
11493 }
11494 return 0;
11495}
11496
11497/*
11498 * This function releases all of the orphan inodes. It returns 1 if
11499 * it hit some error, and 0 on success.
11500 */
11501static int release_orphan_inodes(e2fsck_t ctx)
11502{
11503 ext2_filsys fs = ctx->fs;
11504 ext2_ino_t ino, next_ino;
11505 struct ext2_inode inode;
11506 struct problem_context pctx;
11507 char *block_buf;
11508
11509 if ((ino = fs->super->s_last_orphan) == 0)
11510 return 0;
11511
11512 /*
11513 * Win or lose, we won't be using the head of the orphan inode
11514 * list again.
11515 */
11516 fs->super->s_last_orphan = 0;
11517 ext2fs_mark_super_dirty(fs);
11518
11519 /*
11520 * If the filesystem contains errors, don't run the orphan
11521 * list, since the orphan list can't be trusted; and we're
11522 * going to be running a full e2fsck run anyway...
11523 */
11524 if (fs->super->s_state & EXT2_ERROR_FS)
11525 return 0;
11526
11527 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
11528 (ino > fs->super->s_inodes_count)) {
11529 clear_problem_context(&pctx);
11530 pctx.ino = ino;
11531 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
11532 return 1;
11533 }
11534
11535 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
11536 "block iterate buffer");
11537 e2fsck_read_bitmaps(ctx);
11538
11539 while (ino) {
11540 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
11541 clear_problem_context(&pctx);
11542 pctx.ino = ino;
11543 pctx.inode = &inode;
11544 pctx.str = inode.i_links_count ? _("Truncating") :
11545 _("Clearing");
11546
11547 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
11548
11549 next_ino = inode.i_dtime;
11550 if (next_ino &&
11551 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
11552 (next_ino > fs->super->s_inodes_count))) {
11553 pctx.ino = next_ino;
11554 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
11555 goto return_abort;
11556 }
11557
11558 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
11559 goto return_abort;
11560
11561 if (!inode.i_links_count) {
11562 ext2fs_inode_alloc_stats2(fs, ino, -1,
11563 LINUX_S_ISDIR(inode.i_mode));
11564 inode.i_dtime = time(0);
11565 } else {
11566 inode.i_dtime = 0;
11567 }
11568 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
11569 ino = next_ino;
11570 }
11571 ext2fs_free_mem(&block_buf);
11572 return 0;
11573return_abort:
11574 ext2fs_free_mem(&block_buf);
11575 return 1;
11576}
11577
11578/*
11579 * Check the resize inode to make sure it is sane. We check both for
11580 * the case where on-line resizing is not enabled (in which case the
11581 * resize inode should be cleared) as well as the case where on-line
11582 * resizing is enabled.
11583 */
11584static void check_resize_inode(e2fsck_t ctx)
11585{
11586 ext2_filsys fs = ctx->fs;
11587 struct ext2_inode inode;
11588 struct problem_context pctx;
11589 int i, j, gdt_off, ind_off;
11590 blk_t blk, pblk, expect;
11591 __u32 *dind_buf = 0, *ind_buf;
11592 errcode_t retval;
11593
11594 clear_problem_context(&pctx);
11595
11596 /*
11597 * If the resize inode feature isn't set, then
11598 * s_reserved_gdt_blocks must be zero.
11599 */
11600 if (!(fs->super->s_feature_compat &
11601 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11602 if (fs->super->s_reserved_gdt_blocks) {
11603 pctx.num = fs->super->s_reserved_gdt_blocks;
11604 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
11605 &pctx)) {
11606 fs->super->s_reserved_gdt_blocks = 0;
11607 ext2fs_mark_super_dirty(fs);
11608 }
11609 }
11610 }
11611
Mike Frysinger874af852006-03-08 07:03:27 +000011612 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011613 pctx.ino = EXT2_RESIZE_INO;
11614 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
11615 if (retval) {
11616 if (fs->super->s_feature_compat &
11617 EXT2_FEATURE_COMPAT_RESIZE_INODE)
11618 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11619 return;
11620 }
11621
11622 /*
11623 * If the resize inode feature isn't set, check to make sure
11624 * the resize inode is cleared; then we're done.
11625 */
11626 if (!(fs->super->s_feature_compat &
11627 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11628 for (i=0; i < EXT2_N_BLOCKS; i++) {
11629 if (inode.i_block[i])
11630 break;
11631 }
11632 if ((i < EXT2_N_BLOCKS) &&
11633 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
11634 memset(&inode, 0, sizeof(inode));
11635 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11636 "clear_resize");
11637 }
11638 return;
11639 }
11640
11641 /*
11642 * The resize inode feature is enabled; check to make sure the
11643 * only block in use is the double indirect block
11644 */
11645 blk = inode.i_block[EXT2_DIND_BLOCK];
11646 for (i=0; i < EXT2_N_BLOCKS; i++) {
11647 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
11648 break;
11649 }
11650 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
11651 !(inode.i_mode & LINUX_S_IFREG) ||
11652 (blk < fs->super->s_first_data_block ||
11653 blk >= fs->super->s_blocks_count)) {
11654 resize_inode_invalid:
11655 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
11656 memset(&inode, 0, sizeof(inode));
11657 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11658 "clear_resize");
11659 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11660 }
11661 if (!(ctx->options & E2F_OPT_READONLY)) {
11662 fs->super->s_state &= ~EXT2_VALID_FS;
11663 ext2fs_mark_super_dirty(fs);
11664 }
11665 goto cleanup;
11666 }
11667 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
11668 "resize dind buffer");
11669 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
11670
11671 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
11672 if (retval)
11673 goto resize_inode_invalid;
11674
11675 gdt_off = fs->desc_blocks;
11676 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
11677 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
11678 i++, gdt_off++, pblk++) {
11679 gdt_off %= fs->blocksize/4;
11680 if (dind_buf[gdt_off] != pblk)
11681 goto resize_inode_invalid;
11682 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
11683 if (retval)
11684 goto resize_inode_invalid;
11685 ind_off = 0;
11686 for (j = 1; j < fs->group_desc_count; j++) {
11687 if (!ext2fs_bg_has_super(fs, j))
11688 continue;
11689 expect = pblk + (j * fs->super->s_blocks_per_group);
11690 if (ind_buf[ind_off] != expect)
11691 goto resize_inode_invalid;
11692 ind_off++;
11693 }
11694 }
11695
11696cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000011697 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011698
11699 }
11700
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011701static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011702{
11703 ext2_filsys fs = ctx->fs;
11704 blk_t first_block, last_block;
11705 struct ext2_super_block *sb = fs->super;
11706 struct ext2_group_desc *gd;
11707 blk_t blocks_per_group = fs->super->s_blocks_per_group;
11708 blk_t bpg_max;
11709 int inodes_per_block;
11710 int ipg_max;
11711 int inode_size;
11712 dgrp_t i;
11713 blk_t should_be;
11714 struct problem_context pctx;
11715 __u32 free_blocks = 0, free_inodes = 0;
11716
11717 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
11718 ipg_max = inodes_per_block * (blocks_per_group - 4);
11719 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
11720 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
11721 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
11722 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
11723 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
11724
11725 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11726 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
11727 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11728 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
11729 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
11730 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
11731
11732 clear_problem_context(&pctx);
11733
11734 /*
11735 * Verify the super block constants...
11736 */
11737 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
11738 MIN_CHECK, 1, 0);
11739 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
11740 MIN_CHECK, 1, 0);
11741 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
11742 MAX_CHECK, 0, sb->s_blocks_count);
11743 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
11744 MIN_CHECK | MAX_CHECK, 0,
11745 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
11746 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
11747 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
11748 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
11749 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
11750 bpg_max);
11751 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
11752 MIN_CHECK | MAX_CHECK, 8, bpg_max);
11753 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
11754 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
11755 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
11756 MAX_CHECK, 0, sb->s_blocks_count / 2);
11757 check_super_value(ctx, "reserved_gdt_blocks",
11758 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
11759 fs->blocksize/4);
11760 inode_size = EXT2_INODE_SIZE(sb);
11761 check_super_value(ctx, "inode_size",
11762 inode_size, MIN_CHECK | MAX_CHECK,
11763 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
11764 if (inode_size & (inode_size - 1)) {
11765 pctx.num = inode_size;
11766 pctx.str = "inode_size";
11767 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11768 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11769 return;
11770 }
11771
11772 if (!ctx->num_blocks) {
11773 pctx.errcode = e2fsck_get_device_size(ctx);
11774 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
11775 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
11776 ctx->flags |= E2F_FLAG_ABORT;
11777 return;
11778 }
11779 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
11780 (ctx->num_blocks < sb->s_blocks_count)) {
11781 pctx.blk = sb->s_blocks_count;
11782 pctx.blk2 = ctx->num_blocks;
11783 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
11784 ctx->flags |= E2F_FLAG_ABORT;
11785 return;
11786 }
11787 }
11788 }
11789
11790 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
11791 pctx.blk = EXT2_BLOCK_SIZE(sb);
11792 pctx.blk2 = EXT2_FRAG_SIZE(sb);
11793 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
11794 ctx->flags |= E2F_FLAG_ABORT;
11795 return;
11796 }
11797
11798 should_be = sb->s_frags_per_group >>
11799 (sb->s_log_block_size - sb->s_log_frag_size);
11800 if (sb->s_blocks_per_group != should_be) {
11801 pctx.blk = sb->s_blocks_per_group;
11802 pctx.blk2 = should_be;
11803 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
11804 ctx->flags |= E2F_FLAG_ABORT;
11805 return;
11806 }
11807
11808 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
11809 if (sb->s_first_data_block != should_be) {
11810 pctx.blk = sb->s_first_data_block;
11811 pctx.blk2 = should_be;
11812 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
11813 ctx->flags |= E2F_FLAG_ABORT;
11814 return;
11815 }
11816
11817 should_be = sb->s_inodes_per_group * fs->group_desc_count;
11818 if (sb->s_inodes_count != should_be) {
11819 pctx.ino = sb->s_inodes_count;
11820 pctx.ino2 = should_be;
11821 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
11822 sb->s_inodes_count = should_be;
11823 ext2fs_mark_super_dirty(fs);
11824 }
11825 }
11826
11827 /*
11828 * Verify the group descriptors....
11829 */
11830 first_block = sb->s_first_data_block;
11831 last_block = first_block + blocks_per_group;
11832
11833 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
11834 pctx.group = i;
11835
11836 if (i == fs->group_desc_count - 1)
11837 last_block = sb->s_blocks_count;
11838 if ((gd->bg_block_bitmap < first_block) ||
11839 (gd->bg_block_bitmap >= last_block)) {
11840 pctx.blk = gd->bg_block_bitmap;
11841 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
11842 gd->bg_block_bitmap = 0;
11843 }
11844 if (gd->bg_block_bitmap == 0) {
11845 ctx->invalid_block_bitmap_flag[i]++;
11846 ctx->invalid_bitmaps++;
11847 }
11848 if ((gd->bg_inode_bitmap < first_block) ||
11849 (gd->bg_inode_bitmap >= last_block)) {
11850 pctx.blk = gd->bg_inode_bitmap;
11851 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
11852 gd->bg_inode_bitmap = 0;
11853 }
11854 if (gd->bg_inode_bitmap == 0) {
11855 ctx->invalid_inode_bitmap_flag[i]++;
11856 ctx->invalid_bitmaps++;
11857 }
11858 if ((gd->bg_inode_table < first_block) ||
11859 ((gd->bg_inode_table +
11860 fs->inode_blocks_per_group - 1) >= last_block)) {
11861 pctx.blk = gd->bg_inode_table;
11862 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
11863 gd->bg_inode_table = 0;
11864 }
11865 if (gd->bg_inode_table == 0) {
11866 ctx->invalid_inode_table_flag[i]++;
11867 ctx->invalid_bitmaps++;
11868 }
11869 free_blocks += gd->bg_free_blocks_count;
11870 free_inodes += gd->bg_free_inodes_count;
11871 first_block += sb->s_blocks_per_group;
11872 last_block += sb->s_blocks_per_group;
11873
11874 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
11875 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
11876 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
11877 ext2fs_unmark_valid(fs);
11878
11879 }
11880
11881 /*
11882 * Update the global counts from the block group counts. This
11883 * is needed for an experimental patch which eliminates
11884 * locking the entire filesystem when allocating blocks or
11885 * inodes; if the filesystem is not unmounted cleanly, the
11886 * global counts may not be accurate.
11887 */
11888 if ((free_blocks != sb->s_free_blocks_count) ||
11889 (free_inodes != sb->s_free_inodes_count)) {
11890 if (ctx->options & E2F_OPT_READONLY)
11891 ext2fs_unmark_valid(fs);
11892 else {
11893 sb->s_free_blocks_count = free_blocks;
11894 sb->s_free_inodes_count = free_inodes;
11895 ext2fs_mark_super_dirty(fs);
11896 }
11897 }
11898
11899 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
11900 (sb->s_free_inodes_count > sb->s_inodes_count))
11901 ext2fs_unmark_valid(fs);
11902
11903
11904 /*
11905 * If we have invalid bitmaps, set the error state of the
11906 * filesystem.
11907 */
11908 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
11909 sb->s_state &= ~EXT2_VALID_FS;
11910 ext2fs_mark_super_dirty(fs);
11911 }
11912
11913 clear_problem_context(&pctx);
11914
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011915 /*
11916 * If the UUID field isn't assigned, assign it.
11917 */
11918 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
11919 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
11920 uuid_generate(sb->s_uuid);
11921 ext2fs_mark_super_dirty(fs);
11922 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
11923 }
11924 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011925
Rob Landley3e72c592006-04-06 22:49:04 +000011926 /* FIXME - HURD support?
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011927 * For the Hurd, check to see if the filetype option is set,
11928 * since it doesn't support it.
11929 */
11930 if (!(ctx->options & E2F_OPT_READONLY) &&
11931 fs->super->s_creator_os == EXT2_OS_HURD &&
11932 (fs->super->s_feature_incompat &
11933 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
11934 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
11935 fs->super->s_feature_incompat &=
11936 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
11937 ext2fs_mark_super_dirty(fs);
11938
11939 }
11940 }
11941
11942 /*
11943 * If we have any of the compatibility flags set, we need to have a
11944 * revision 1 filesystem. Most kernels will not check the flags on
11945 * a rev 0 filesystem and we may have corruption issues because of
11946 * the incompatible changes to the filesystem.
11947 */
11948 if (!(ctx->options & E2F_OPT_READONLY) &&
11949 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
11950 (fs->super->s_feature_compat ||
11951 fs->super->s_feature_ro_compat ||
11952 fs->super->s_feature_incompat) &&
11953 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
11954 ext2fs_update_dynamic_rev(fs);
11955 ext2fs_mark_super_dirty(fs);
11956 }
11957
11958 check_resize_inode(ctx);
11959
11960 /*
11961 * Clean up any orphan inodes, if present.
11962 */
11963 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
11964 fs->super->s_state &= ~EXT2_VALID_FS;
11965 ext2fs_mark_super_dirty(fs);
11966 }
11967
11968 /*
11969 * Move the ext3 journal file, if necessary.
11970 */
11971 e2fsck_move_ext3_journal(ctx);
11972 return;
11973}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011974
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011975/*
11976 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011977 */
11978
11979#ifdef ENABLE_SWAPFS
11980
11981struct swap_block_struct {
11982 ext2_ino_t ino;
11983 int isdir;
11984 errcode_t errcode;
11985 char *dir_buf;
11986 struct ext2_inode *inode;
11987};
11988
11989/*
11990 * This is a helper function for block_iterate. We mark all of the
11991 * indirect and direct blocks as changed, so that block_iterate will
11992 * write them out.
11993 */
11994static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
11995 void *priv_data)
11996{
11997 errcode_t retval;
11998
11999 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
12000
12001 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
12002 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
12003 if (retval) {
12004 sb->errcode = retval;
12005 return BLOCK_ABORT;
12006 }
12007 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
12008 if (retval) {
12009 sb->errcode = retval;
12010 return BLOCK_ABORT;
12011 }
12012 }
12013 if (blockcnt >= 0) {
12014 if (blockcnt < EXT2_NDIR_BLOCKS)
12015 return 0;
12016 return BLOCK_CHANGED;
12017 }
12018 if (blockcnt == BLOCK_COUNT_IND) {
12019 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
12020 return 0;
12021 return BLOCK_CHANGED;
12022 }
12023 if (blockcnt == BLOCK_COUNT_DIND) {
12024 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
12025 return 0;
12026 return BLOCK_CHANGED;
12027 }
12028 if (blockcnt == BLOCK_COUNT_TIND) {
12029 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
12030 return 0;
12031 return BLOCK_CHANGED;
12032 }
12033 return BLOCK_CHANGED;
12034}
12035
12036/*
12037 * This function is responsible for byte-swapping all of the indirect,
12038 * block pointers. It is also responsible for byte-swapping directories.
12039 */
12040static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
12041 struct ext2_inode *inode)
12042{
12043 errcode_t retval;
12044 struct swap_block_struct sb;
12045
12046 sb.ino = ino;
12047 sb.inode = inode;
12048 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
12049 sb.errcode = 0;
12050 sb.isdir = 0;
12051 if (LINUX_S_ISDIR(inode->i_mode))
12052 sb.isdir = 1;
12053
12054 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
12055 swap_block, &sb);
12056 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012057 bb_error_msg(_("while calling ext2fs_block_iterate"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012058 ctx->flags |= E2F_FLAG_ABORT;
12059 return;
12060 }
12061 if (sb.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012062 bb_error_msg(_("while calling iterator function"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012063 ctx->flags |= E2F_FLAG_ABORT;
12064 return;
12065 }
12066}
12067
12068static void swap_inodes(e2fsck_t ctx)
12069{
12070 ext2_filsys fs = ctx->fs;
12071 dgrp_t group;
12072 unsigned int i;
12073 ext2_ino_t ino = 1;
12074 char *buf, *block_buf;
12075 errcode_t retval;
12076 struct ext2_inode * inode;
12077
12078 e2fsck_use_inode_shortcuts(ctx, 1);
12079
12080 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
12081 &buf);
12082 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012083 bb_error_msg(_("while allocating inode buffer"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012084 ctx->flags |= E2F_FLAG_ABORT;
12085 return;
12086 }
12087 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
12088 "block interate buffer");
12089 for (group = 0; group < fs->group_desc_count; group++) {
12090 retval = io_channel_read_blk(fs->io,
12091 fs->group_desc[group].bg_inode_table,
12092 fs->inode_blocks_per_group, buf);
12093 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012094 bb_error_msg(_("while reading inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012095 group);
12096 ctx->flags |= E2F_FLAG_ABORT;
12097 return;
12098 }
12099 inode = (struct ext2_inode *) buf;
12100 for (i=0; i < fs->super->s_inodes_per_group;
12101 i++, ino++, inode++) {
12102 ctx->stashed_ino = ino;
12103 ctx->stashed_inode = inode;
12104
12105 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
12106 ext2fs_swap_inode(fs, inode, inode, 0);
12107
12108 /*
12109 * Skip deleted files.
12110 */
12111 if (inode->i_links_count == 0)
12112 continue;
12113
12114 if (LINUX_S_ISDIR(inode->i_mode) ||
12115 ((inode->i_block[EXT2_IND_BLOCK] ||
12116 inode->i_block[EXT2_DIND_BLOCK] ||
12117 inode->i_block[EXT2_TIND_BLOCK]) &&
12118 ext2fs_inode_has_valid_blocks(inode)))
12119 swap_inode_blocks(ctx, ino, block_buf, inode);
12120
12121 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12122 return;
12123
12124 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12125 ext2fs_swap_inode(fs, inode, inode, 1);
12126 }
12127 retval = io_channel_write_blk(fs->io,
12128 fs->group_desc[group].bg_inode_table,
12129 fs->inode_blocks_per_group, buf);
12130 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012131 bb_error_msg(_("while writing inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012132 group);
12133 ctx->flags |= E2F_FLAG_ABORT;
12134 return;
12135 }
12136 }
12137 ext2fs_free_mem(&buf);
12138 ext2fs_free_mem(&block_buf);
12139 e2fsck_use_inode_shortcuts(ctx, 0);
12140 ext2fs_flush_icache(fs);
12141}
12142
Rob Landley7c94bed2006-05-03 21:58:45 +000012143#if defined(__powerpc__) && BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012144/*
12145 * On the PowerPC, the big-endian variant of the ext2 filesystem
12146 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
12147 * of each word. Thus a bitmap with only bit 0 set would be, as
12148 * a string of bytes, 00 00 00 01 00 ...
12149 * To cope with this, we byte-reverse each word of a bitmap if
12150 * we have a big-endian filesystem, that is, if we are *not*
12151 * byte-swapping other word-sized numbers.
12152 */
12153#define EXT2_BIG_ENDIAN_BITMAPS
12154#endif
12155
12156#ifdef EXT2_BIG_ENDIAN_BITMAPS
12157static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
12158{
12159 __u32 *p = (__u32 *) bmap->bitmap;
12160 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
12161
12162 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
12163 *p = ext2fs_swab32(*p);
12164}
12165#endif
12166
12167
12168#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012169static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012170{
12171 ext2_filsys fs = ctx->fs;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012172 if (!(ctx->options & E2F_OPT_PREEN))
12173 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
12174
Rob Landley3e72c592006-04-06 22:49:04 +000012175 /* Byte swap */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012176
12177 if (fs->super->s_mnt_count) {
12178 fprintf(stderr, _("%s: the filesystem must be freshly "
12179 "checked using fsck\n"
12180 "and not mounted before trying to "
12181 "byte-swap it.\n"), ctx->device_name);
12182 ctx->flags |= E2F_FLAG_ABORT;
12183 return;
12184 }
12185 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
12186 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
12187 EXT2_FLAG_SWAP_BYTES_WRITE);
12188 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
12189 } else {
12190 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
12191 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
12192 }
12193 swap_inodes(ctx);
12194 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12195 return;
12196 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12197 fs->flags |= EXT2_FLAG_SWAP_BYTES;
12198 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
12199 EXT2_FLAG_SWAP_BYTES_WRITE);
12200
12201#ifdef EXT2_BIG_ENDIAN_BITMAPS
12202 e2fsck_read_bitmaps(ctx);
12203 ext2fs_swap_bitmap(fs->inode_map);
12204 ext2fs_swap_bitmap(fs->block_map);
12205 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
12206#endif
12207 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
12208 ext2fs_flush(fs);
12209 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012210}
12211#endif /* ENABLE_SWAPFS */
12212
12213#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012214
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012215/*
12216 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012217 */
12218
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012219
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012220void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
12221 const char *description)
12222{
12223 void *ret;
12224 char buf[256];
12225
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012226 ret = malloc(size);
12227 if (!ret) {
12228 sprintf(buf, "Can't allocate %s\n", description);
Rob Landley7c94bed2006-05-03 21:58:45 +000012229 bb_error_msg_and_die(buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012230 }
12231 memset(ret, 0, size);
12232 return ret;
12233}
12234
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012235static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012236{
12237 char *ret;
12238
12239 if (!str)
12240 return NULL;
12241 if (!len)
12242 len = strlen(str);
12243 ret = malloc(len+1);
12244 if (ret) {
12245 strncpy(ret, str, len);
12246 ret[len] = 0;
12247 }
12248 return ret;
12249}
12250
12251#ifndef HAVE_CONIO_H
12252static int read_a_char(void)
12253{
12254 char c;
12255 int r;
12256 int fail = 0;
12257
12258 while(1) {
12259 if (e2fsck_global_ctx &&
12260 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
12261 return 3;
12262 }
12263 r = read(0, &c, 1);
12264 if (r == 1)
12265 return c;
12266 if (fail++ > 100)
12267 break;
12268 }
12269 return EOF;
12270}
12271#endif
12272
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012273static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012274{
12275 int c;
12276 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012277 static const char short_yes[] = "yY";
12278 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012279
12280#ifdef HAVE_TERMIOS_H
12281 struct termios termios, tmp;
12282
12283 tcgetattr (0, &termios);
12284 tmp = termios;
12285 tmp.c_lflag &= ~(ICANON | ECHO);
12286 tmp.c_cc[VMIN] = 1;
12287 tmp.c_cc[VTIME] = 0;
12288 tcsetattr (0, TCSANOW, &tmp);
12289#endif
12290
12291 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012292 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012293 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012294 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012295 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012296 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012297 printf("%s%s? ", string, defstr);
12298 while (1) {
12299 fflush (stdout);
12300 if ((c = read_a_char()) == EOF)
12301 break;
12302 if (c == 3) {
12303#ifdef HAVE_TERMIOS_H
12304 tcsetattr (0, TCSANOW, &termios);
12305#endif
12306 if (e2fsck_global_ctx &&
12307 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
12308 puts("\n");
12309 longjmp(e2fsck_global_ctx->abort_loc, 1);
12310 }
12311 puts(_("cancelled!\n"));
12312 return 0;
12313 }
12314 if (strchr(short_yes, (char) c)) {
12315 def = 1;
12316 break;
12317 }
12318 else if (strchr(short_no, (char) c)) {
12319 def = 0;
12320 break;
12321 }
12322 else if ((c == ' ' || c == '\n') && (def != -1))
12323 break;
12324 }
12325 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012326 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012327 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012328 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012329#ifdef HAVE_TERMIOS_H
12330 tcsetattr (0, TCSANOW, &termios);
12331#endif
12332 return def;
12333}
12334
12335int ask (e2fsck_t ctx, const char * string, int def)
12336{
12337 if (ctx->options & E2F_OPT_NO) {
12338 printf (_("%s? no\n\n"), string);
12339 return 0;
12340 }
12341 if (ctx->options & E2F_OPT_YES) {
12342 printf (_("%s? yes\n\n"), string);
12343 return 1;
12344 }
12345 if (ctx->options & E2F_OPT_PREEN) {
12346 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
12347 return def;
12348 }
12349 return ask_yn(string, def);
12350}
12351
12352void e2fsck_read_bitmaps(e2fsck_t ctx)
12353{
12354 ext2_filsys fs = ctx->fs;
12355 errcode_t retval;
12356
12357 if (ctx->invalid_bitmaps) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012358 bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012359 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012360 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012361 }
12362
12363 ehandler_operation(_("reading inode and block bitmaps"));
12364 retval = ext2fs_read_bitmaps(fs);
12365 ehandler_operation(0);
12366 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012367 bb_error_msg(_("while retrying to read bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012368 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012369 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012370 }
12371}
12372
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012373static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012374{
12375 ext2_filsys fs = ctx->fs;
12376 errcode_t retval;
12377
12378 if (ext2fs_test_bb_dirty(fs)) {
12379 ehandler_operation(_("writing block bitmaps"));
12380 retval = ext2fs_write_block_bitmap(fs);
12381 ehandler_operation(0);
12382 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012383 bb_error_msg(_("while retrying to write block bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012384 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012385 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012386 }
12387 }
12388
12389 if (ext2fs_test_ib_dirty(fs)) {
12390 ehandler_operation(_("writing inode bitmaps"));
12391 retval = ext2fs_write_inode_bitmap(fs);
12392 ehandler_operation(0);
12393 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012394 bb_error_msg(_("while retrying to write inode bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012395 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012396 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012397 }
12398 }
12399}
12400
12401void preenhalt(e2fsck_t ctx)
12402{
12403 ext2_filsys fs = ctx->fs;
12404
12405 if (!(ctx->options & E2F_OPT_PREEN))
12406 return;
12407 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
12408 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
12409 ctx->device_name);
12410 if (fs != NULL) {
12411 fs->super->s_state |= EXT2_ERROR_FS;
12412 ext2fs_mark_super_dirty(fs);
12413 ext2fs_close(fs);
12414 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012415 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012416}
12417
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012418void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
12419 struct ext2_inode * inode, const char *proc)
12420{
12421 int retval;
12422
12423 retval = ext2fs_read_inode(ctx->fs, ino, inode);
12424 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012425 bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
12426 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012427 }
12428}
12429
12430extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
12431 struct ext2_inode * inode, int bufsize,
12432 const char *proc)
12433{
12434 int retval;
12435
12436 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
12437 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012438 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12439 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012440 }
12441}
12442
12443extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
12444 struct ext2_inode * inode, const char *proc)
12445{
12446 int retval;
12447
12448 retval = ext2fs_write_inode(ctx->fs, ino, inode);
12449 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012450 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12451 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012452 }
12453}
12454
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012455blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
12456 io_manager manager)
12457{
12458 struct ext2_super_block *sb;
12459 io_channel io = NULL;
12460 void *buf = NULL;
12461 int blocksize;
12462 blk_t superblock, ret_sb = 8193;
12463
12464 if (fs && fs->super) {
12465 ret_sb = (fs->super->s_blocks_per_group +
12466 fs->super->s_first_data_block);
12467 if (ctx) {
12468 ctx->superblock = ret_sb;
12469 ctx->blocksize = fs->blocksize;
12470 }
12471 return ret_sb;
12472 }
12473
12474 if (ctx) {
12475 if (ctx->blocksize) {
12476 ret_sb = ctx->blocksize * 8;
12477 if (ctx->blocksize == 1024)
12478 ret_sb++;
12479 ctx->superblock = ret_sb;
12480 return ret_sb;
12481 }
12482 ctx->superblock = ret_sb;
12483 ctx->blocksize = 1024;
12484 }
12485
12486 if (!name || !manager)
12487 goto cleanup;
12488
12489 if (manager->open(name, 0, &io) != 0)
12490 goto cleanup;
12491
12492 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
12493 goto cleanup;
12494 sb = (struct ext2_super_block *) buf;
12495
12496 for (blocksize = EXT2_MIN_BLOCK_SIZE;
12497 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
12498 superblock = blocksize*8;
12499 if (blocksize == 1024)
12500 superblock++;
12501 io_channel_set_blksize(io, blocksize);
12502 if (io_channel_read_blk(io, superblock,
12503 -SUPERBLOCK_SIZE, buf))
12504 continue;
Rob Landley7c94bed2006-05-03 21:58:45 +000012505#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012506 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
12507 ext2fs_swap_super(sb);
12508#endif
12509 if (sb->s_magic == EXT2_SUPER_MAGIC) {
12510 ret_sb = superblock;
12511 if (ctx) {
12512 ctx->superblock = superblock;
12513 ctx->blocksize = blocksize;
12514 }
12515 break;
12516 }
12517 }
12518
12519cleanup:
12520 if (io)
12521 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000012522 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012523 return (ret_sb);
12524}
12525
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012526
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012527/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012528 * This function runs through the e2fsck passes and calls them all,
12529 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012530 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012531typedef void (*pass_t)(e2fsck_t ctx);
12532
12533static const pass_t e2fsck_passes[] = {
12534 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
12535 e2fsck_pass5, 0 };
12536
12537#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
12538
12539static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012540{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012541 int i;
12542 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012543
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012544 if (setjmp(ctx->abort_loc)) {
12545 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
12546 return (ctx->flags & E2F_FLAG_RUN_RETURN);
12547 }
12548 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012549
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012550 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
12551 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12552 break;
12553 e2fsck_pass(ctx);
12554 if (ctx->progress)
12555 (void) (ctx->progress)(ctx, 0, 0, 0);
12556 }
12557 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012558
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012559 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12560 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012561 return 0;
12562}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012563
12564
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012565/*
12566 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012567 */
12568
12569
Mike Frysinger51a43b42005-09-24 07:11:16 +000012570/* Command line options */
12571static int swapfs;
12572#ifdef ENABLE_SWAPFS
12573static int normalize_swapfs;
12574#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012575static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000012576static int show_version_only;
12577static int verbose;
12578
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012579#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
12580
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012581static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012582{
12583 ext2_filsys fs = ctx->fs;
12584 int inodes, inodes_used, blocks, blocks_used;
12585 int dir_links;
12586 int num_files, num_links;
12587 int frag_percent;
12588
12589 dir_links = 2 * ctx->fs_directory_count - 1;
12590 num_files = ctx->fs_total_count - dir_links;
12591 num_links = ctx->fs_links_count - dir_links;
12592 inodes = fs->super->s_inodes_count;
12593 inodes_used = (fs->super->s_inodes_count -
12594 fs->super->s_free_inodes_count);
12595 blocks = fs->super->s_blocks_count;
12596 blocks_used = (fs->super->s_blocks_count -
12597 fs->super->s_free_blocks_count);
12598
12599 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
12600 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012601
Mike Frysinger51a43b42005-09-24 07:11:16 +000012602 if (!verbose) {
12603 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
12604 ctx->device_name, inodes_used, inodes,
12605 frag_percent / 10, frag_percent % 10,
12606 blocks_used, blocks);
12607 return;
12608 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012609 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
12610 100 * inodes_used / inodes);
12611 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
12612 P_E2("", "s", ctx->fs_fragmented),
12613 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012614 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
12615 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012616 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
12617 (int) ((long long) 100 * blocks_used / blocks));
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012618 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
12619 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
12620 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
12621 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
12622 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
12623 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
12624 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
12625 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
12626 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
12627 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
12628 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012629}
12630
12631static void check_mount(e2fsck_t ctx)
12632{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012633 errcode_t retval;
12634 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012635
12636 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
12637 &ctx->mount_flags);
12638 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012639 bb_error_msg(_("while determining whether %s is mounted."),
Mike Frysinger51a43b42005-09-24 07:11:16 +000012640 ctx->filesystem_name);
12641 return;
12642 }
12643
12644 /*
12645 * If the filesystem isn't mounted, or it's the root filesystem
12646 * and it's mounted read-only, then everything's fine.
12647 */
12648 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
12649 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
12650 (ctx->mount_flags & EXT2_MF_READONLY)))
12651 return;
12652
12653 if (ctx->options & E2F_OPT_READONLY) {
12654 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
12655 return;
12656 }
12657
12658 printf(_("%s is mounted. "), ctx->filesystem_name);
12659 if (!ctx->interactive)
Denis Vlasenko6d655be2006-09-06 19:02:46 +000012660 bb_error_msg_and_die(_("Cannot continue, aborting."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012661 printf(_("\n\n\007\007\007\007WARNING!!! "
12662 "Running e2fsck on a mounted filesystem may cause\n"
12663 "SEVERE filesystem damage.\007\007\007\n\n"));
12664 cont = ask_yn(_("Do you really want to continue"), -1);
12665 if (!cont) {
12666 printf (_("check aborted.\n"));
12667 exit (0);
12668 }
12669 return;
12670}
12671
12672static int is_on_batt(void)
12673{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012674 FILE *f;
12675 DIR *d;
12676 char tmp[80], tmp2[80], fname[80];
12677 unsigned int acflag;
12678 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012679
12680 f = fopen("/proc/apm", "r");
12681 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012682 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012683 acflag = 1;
12684 fclose(f);
12685 return (acflag != 1);
12686 }
12687 d = opendir("/proc/acpi/ac_adapter");
12688 if (d) {
12689 while ((de=readdir(d)) != NULL) {
12690 if (!strncmp(".", de->d_name, 1))
12691 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012692 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000012693 de->d_name);
12694 f = fopen(fname, "r");
12695 if (!f)
12696 continue;
12697 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
12698 tmp[0] = 0;
12699 fclose(f);
12700 if (strncmp(tmp, "off-line", 8) == 0) {
12701 closedir(d);
12702 return 1;
12703 }
12704 }
12705 closedir(d);
12706 }
12707 return 0;
12708}
12709
12710/*
12711 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012712 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000012713 * message explaining why a check is being forced.
12714 */
12715static void check_if_skip(e2fsck_t ctx)
12716{
12717 ext2_filsys fs = ctx->fs;
12718 const char *reason = NULL;
12719 unsigned int reason_arg = 0;
12720 long next_check;
12721 int batt = is_on_batt();
12722 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012723
Rob Landleyd8f66012006-05-05 17:29:09 +000012724 if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012725 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012726
Mike Frysinger51a43b42005-09-24 07:11:16 +000012727 if ((fs->super->s_state & EXT2_ERROR_FS) ||
12728 !ext2fs_test_valid(fs))
12729 reason = _(" contains a file system with errors");
12730 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
12731 reason = _(" was not cleanly unmounted");
12732 else if ((fs->super->s_max_mnt_count > 0) &&
12733 (fs->super->s_mnt_count >=
12734 (unsigned) fs->super->s_max_mnt_count)) {
12735 reason = _(" has been mounted %u times without being checked");
12736 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012737 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012738 (unsigned) fs->super->s_max_mnt_count*2))
12739 reason = 0;
12740 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012741 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000012742 fs->super->s_checkinterval)) {
12743 reason = _(" has gone %u days without being checked");
12744 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012745 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012746 fs->super->s_checkinterval*2))
12747 reason = 0;
12748 }
12749 if (reason) {
12750 fputs(ctx->device_name, stdout);
12751 printf(reason, reason_arg);
12752 fputs(_(", check forced.\n"), stdout);
12753 return;
12754 }
12755 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
12756 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
12757 fs->super->s_inodes_count,
12758 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
12759 fs->super->s_blocks_count);
12760 next_check = 100000;
12761 if (fs->super->s_max_mnt_count > 0) {
12762 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012763 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012764 next_check = 1;
12765 }
12766 if (fs->super->s_checkinterval &&
12767 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
12768 next_check = 1;
12769 if (next_check <= 5) {
12770 if (next_check == 1)
12771 fputs(_(" (check after next mount)"), stdout);
12772 else
12773 printf(_(" (check in %ld mounts)"), next_check);
12774 }
12775 fputc('\n', stdout);
12776 ext2fs_close(fs);
12777 ctx->fs = NULL;
12778 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012779 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012780}
12781
12782/*
12783 * For completion notice
12784 */
12785struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012786 int max_pass;
12787 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000012788};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012789static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000012790 5, { 0, 70, 90, 92, 95, 100 }
12791};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012792
Mike Frysinger51a43b42005-09-24 07:11:16 +000012793static char bar[128], spaces[128];
12794
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012795static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000012796 int max)
12797{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012798 float percent;
12799
Mike Frysinger51a43b42005-09-24 07:11:16 +000012800 if (pass <= 0)
12801 return 0.0;
12802 if (pass > tbl->max_pass || max == 0)
12803 return 100.0;
12804 percent = ((float) curr) / ((float) max);
12805 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
12806 + tbl->table[pass-1]);
12807}
12808
Rob Landleydfba7412006-03-06 20:47:33 +000012809void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012810{
12811 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
12812 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012813
Mike Frysinger51a43b42005-09-24 07:11:16 +000012814 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
12815 ctx->stop_meta);
12816 fflush(stdout);
12817 ctx->flags &= ~E2F_FLAG_PROG_BAR;
12818}
12819
12820int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
12821 unsigned int dpynum)
12822{
12823 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012824 int i;
12825 unsigned int tick;
12826 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012827 int dpywidth;
12828 int fixed_percent;
12829
12830 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
12831 return 0;
12832
12833 /*
12834 * Calculate the new progress position. If the
12835 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012836 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000012837 */
12838 fixed_percent = (int) ((10 * percent) + 0.5);
12839 if (ctx->progress_last_percent == fixed_percent)
12840 return 0;
12841 ctx->progress_last_percent = fixed_percent;
12842
12843 /*
12844 * If we've already updated the spinner once within
12845 * the last 1/8th of a second, no point doing it
12846 * again.
12847 */
12848 gettimeofday(&tv, NULL);
12849 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
12850 if ((tick == ctx->progress_last_time) &&
12851 (fixed_percent != 0) && (fixed_percent != 1000))
12852 return 0;
12853 ctx->progress_last_time = tick;
12854
12855 /*
12856 * Advance the spinner, and note that the progress bar
12857 * will be on the screen
12858 */
12859 ctx->progress_pos = (ctx->progress_pos+1) & 3;
12860 ctx->flags |= E2F_FLAG_PROG_BAR;
12861
12862 dpywidth = 66 - strlen(label);
12863 dpywidth = 8 * (dpywidth / 8);
12864 if (dpynum)
12865 dpywidth -= 8;
12866
12867 i = ((percent * dpywidth) + 50) / 100;
12868 printf("%s%s: |%s%s", ctx->start_meta, label,
12869 bar + (sizeof(bar) - (i+1)),
12870 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
12871 if (fixed_percent == 1000)
12872 fputc('|', stdout);
12873 else
12874 fputc(spinner[ctx->progress_pos & 3], stdout);
12875 printf(" %4.1f%% ", percent);
12876 if (dpynum)
12877 printf("%u\r", dpynum);
12878 else
12879 fputs(" \r", stdout);
12880 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012881
Mike Frysinger51a43b42005-09-24 07:11:16 +000012882 if (fixed_percent == 1000)
12883 e2fsck_clear_progbar(ctx);
12884 fflush(stdout);
12885
12886 return 0;
12887}
12888
12889static int e2fsck_update_progress(e2fsck_t ctx, int pass,
12890 unsigned long cur, unsigned long max)
12891{
12892 char buf[80];
12893 float percent;
12894
12895 if (pass == 0)
12896 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012897
Mike Frysinger51a43b42005-09-24 07:11:16 +000012898 if (ctx->progress_fd) {
12899 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
12900 write(ctx->progress_fd, buf, strlen(buf));
12901 } else {
12902 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
12903 e2fsck_simple_progress(ctx, ctx->device_name,
12904 percent, 0);
12905 }
12906 return 0;
12907}
12908
Mike Frysinger51a43b42005-09-24 07:11:16 +000012909static void reserve_stdio_fds(void)
12910{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012911 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012912
12913 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000012914 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012915 if (fd > 2)
12916 break;
12917 if (fd < 0) {
12918 fprintf(stderr, _("ERROR: Couldn't open "
12919 "/dev/null (%s)\n"),
12920 strerror(errno));
12921 break;
12922 }
12923 }
12924 close(fd);
12925}
12926
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012927static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012928{
12929 e2fsck_t ctx = e2fsck_global_ctx;
12930
12931 if (!ctx)
12932 return;
12933
12934 ctx->progress = e2fsck_update_progress;
12935 ctx->progress_fd = 0;
12936}
12937
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012938static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012939{
12940 e2fsck_t ctx = e2fsck_global_ctx;
12941
12942 if (!ctx)
12943 return;
12944
12945 e2fsck_clear_progbar(ctx);
12946 ctx->progress = 0;
12947}
12948
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012949static void signal_cancel(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012950{
12951 e2fsck_t ctx = e2fsck_global_ctx;
12952
12953 if (!ctx)
12954 exit(FSCK_CANCELED);
12955
12956 ctx->flags |= E2F_FLAG_CANCEL;
12957}
Mike Frysinger51a43b42005-09-24 07:11:16 +000012958
12959static void parse_extended_opts(e2fsck_t ctx, const char *opts)
12960{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012961 char *buf, *token, *next, *p, *arg;
12962 int ea_ver;
12963 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012964
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012965 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012966 for (token = buf; token && *token; token = next) {
12967 p = strchr(token, ',');
12968 next = 0;
12969 if (p) {
12970 *p = 0;
12971 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012972 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000012973 arg = strchr(token, '=');
12974 if (arg) {
12975 *arg = 0;
12976 arg++;
12977 }
12978 if (strcmp(token, "ea_ver") == 0) {
12979 if (!arg) {
12980 extended_usage++;
12981 continue;
12982 }
12983 ea_ver = strtoul(arg, &p, 0);
12984 if (*p ||
12985 ((ea_ver != 1) && (ea_ver != 2))) {
12986 fprintf(stderr,
12987 _("Invalid EA version.\n"));
12988 extended_usage++;
12989 continue;
12990 }
12991 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000012992 } else {
12993 fprintf(stderr, _("Unknown extended option: %s\n"),
12994 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012995 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000012996 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000012997 }
12998 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012999 bb_error_msg_and_die(
13000 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000013001 "and may take an argument which\n"
13002 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000013003 "Valid extended options are:\n"
13004 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000013005 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013006}
Mike Frysinger51a43b42005-09-24 07:11:16 +000013007
13008
13009static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
13010{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013011 int flush = 0;
13012 int c, fd;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013013 e2fsck_t ctx;
13014 errcode_t retval;
13015 struct sigaction sa;
13016 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013017
13018 retval = e2fsck_allocate_context(&ctx);
13019 if (retval)
13020 return retval;
13021
13022 *ret_ctx = ctx;
13023
13024 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
13025 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
13026 if (isatty(0) && isatty(1)) {
13027 ctx->interactive = 1;
13028 } else {
13029 ctx->start_meta[0] = '\001';
13030 ctx->stop_meta[0] = '\002';
13031 }
13032 memset(bar, '=', sizeof(bar)-1);
13033 memset(spaces, ' ', sizeof(spaces)-1);
13034 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013035
Mike Frysinger51a43b42005-09-24 07:11:16 +000013036 if (argc && *argv)
13037 ctx->program_name = *argv;
13038 else
13039 ctx->program_name = "e2fsck";
13040 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
13041 switch (c) {
13042 case 'C':
13043 ctx->progress = e2fsck_update_progress;
13044 ctx->progress_fd = atoi(optarg);
13045 if (!ctx->progress_fd)
13046 break;
13047 /* Validate the file descriptor to avoid disasters */
13048 fd = dup(ctx->progress_fd);
13049 if (fd < 0) {
13050 fprintf(stderr,
13051 _("Error validating file descriptor %d: %s\n"),
13052 ctx->progress_fd,
13053 error_message(errno));
Rob Landley7c94bed2006-05-03 21:58:45 +000013054 bb_error_msg_and_die(_("Invalid completion information file descriptor"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013055 } else
13056 close(fd);
13057 break;
13058 case 'D':
13059 ctx->options |= E2F_OPT_COMPRESS_DIRS;
13060 break;
13061 case 'E':
13062 extended_opts = optarg;
13063 break;
13064 case 'p':
13065 case 'a':
13066 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
13067 conflict_opt:
Rob Landley7c94bed2006-05-03 21:58:45 +000013068 bb_error_msg_and_die(_("Only one the options -p/-a, -n or -y may be specified."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013069 }
13070 ctx->options |= E2F_OPT_PREEN;
13071 break;
13072 case 'n':
13073 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
13074 goto conflict_opt;
13075 ctx->options |= E2F_OPT_NO;
13076 break;
13077 case 'y':
13078 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
13079 goto conflict_opt;
13080 ctx->options |= E2F_OPT_YES;
13081 break;
13082 case 't':
Rob Landley3e72c592006-04-06 22:49:04 +000013083 /* FIXME - This needs to go away in a future path - will change binary */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013084 fprintf(stderr, _("The -t option is not "
13085 "supported on this version of e2fsck.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013086 break;
13087 case 'c':
13088 if (cflag++)
13089 ctx->options |= E2F_OPT_WRITECHECK;
13090 ctx->options |= E2F_OPT_CHECKBLOCKS;
13091 break;
13092 case 'r':
13093 /* What we do by default, anyway! */
13094 break;
13095 case 'b':
13096 ctx->use_superblock = atoi(optarg);
13097 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
13098 break;
13099 case 'B':
13100 ctx->blocksize = atoi(optarg);
13101 break;
13102 case 'I':
13103 ctx->inode_buffer_blocks = atoi(optarg);
13104 break;
13105 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013106 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013107 break;
13108 case 'P':
13109 ctx->process_inode_size = atoi(optarg);
13110 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013111 case 'd':
13112 ctx->options |= E2F_OPT_DEBUG;
13113 break;
13114 case 'f':
13115 ctx->options |= E2F_OPT_FORCE;
13116 break;
13117 case 'F':
13118 flush = 1;
13119 break;
13120 case 'v':
13121 verbose = 1;
13122 break;
13123 case 'V':
13124 show_version_only = 1;
13125 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013126 case 'N':
13127 ctx->device_name = optarg;
13128 break;
13129#ifdef ENABLE_SWAPFS
13130 case 's':
13131 normalize_swapfs = 1;
13132 case 'S':
13133 swapfs = 1;
13134 break;
13135#else
13136 case 's':
13137 case 'S':
13138 fprintf(stderr, _("Byte-swapping filesystems "
13139 "not compiled in this version "
13140 "of e2fsck\n"));
13141 exit(1);
13142#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013143 default:
Rob Landley7c94bed2006-05-03 21:58:45 +000013144 bb_show_usage();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013145 }
13146 if (show_version_only)
13147 return 0;
13148 if (optind != argc - 1)
Rob Landley7c94bed2006-05-03 21:58:45 +000013149 bb_show_usage();
Rob Landleyd8f66012006-05-05 17:29:09 +000013150 if ((ctx->options & E2F_OPT_NO) &&
Mike Frysinger51a43b42005-09-24 07:11:16 +000013151 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
13152 ctx->options |= E2F_OPT_READONLY;
13153 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013154 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013155 *ctx->io_options++ = 0;
13156 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
13157 if (!ctx->filesystem_name) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013158 bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
13159 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013160 }
13161 if (extended_opts)
13162 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013163
Mike Frysinger51a43b42005-09-24 07:11:16 +000013164 if (flush) {
13165 fd = open(ctx->filesystem_name, O_RDONLY, 0);
13166 if (fd < 0) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013167 bb_error_msg(_("while opening %s for flushing"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013168 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013169 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013170 }
13171 if ((retval = ext2fs_sync_device(fd, 1))) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013172 bb_error_msg(_("while trying to flush %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013173 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013174 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013175 }
13176 close(fd);
13177 }
13178#ifdef ENABLE_SWAPFS
Rob Landleyd8f66012006-05-05 17:29:09 +000013179 if (swapfs && cflag) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013180 fprintf(stderr, _("Incompatible options not "
13181 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013182 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013183 }
13184#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013185 /*
13186 * Set up signal action
13187 */
13188 memset(&sa, 0, sizeof(struct sigaction));
13189 sa.sa_handler = signal_cancel;
13190 sigaction(SIGINT, &sa, 0);
13191 sigaction(SIGTERM, &sa, 0);
13192#ifdef SA_RESTART
13193 sa.sa_flags = SA_RESTART;
13194#endif
13195 e2fsck_global_ctx = ctx;
13196 sa.sa_handler = signal_progress_on;
13197 sigaction(SIGUSR1, &sa, 0);
13198 sa.sa_handler = signal_progress_off;
13199 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013200
13201 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013202 if (cflag)
13203 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013204 return 0;
13205}
13206
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013207static const char my_ver_string[] = E2FSPROGS_VERSION;
13208static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013209
Mike Frysinger51a43b42005-09-24 07:11:16 +000013210int e2fsck_main (int argc, char *argv[])
13211{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013212 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013213 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013214 ext2_filsys fs = 0;
13215 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013216 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013217 const char *lib_ver_date;
13218 int my_ver, lib_ver;
13219 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013220 struct problem_context pctx;
13221 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013222
Mike Frysinger51a43b42005-09-24 07:11:16 +000013223 clear_problem_context(&pctx);
Rob Landley3e72c592006-04-06 22:49:04 +000013224
Mike Frysinger51a43b42005-09-24 07:11:16 +000013225 my_ver = ext2fs_parse_version_string(my_ver_string);
13226 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
13227 if (my_ver > lib_ver) {
13228 fprintf( stderr, _("Error: ext2fs library version "
13229 "out of date!\n"));
13230 show_version_only++;
13231 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013232
Mike Frysinger51a43b42005-09-24 07:11:16 +000013233 retval = PRS(argc, argv, &ctx);
13234 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013235 bb_error_msg(_("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013236 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013237 }
13238 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013239
Mike Frysinger51a43b42005-09-24 07:11:16 +000013240 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
13241 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
13242 my_ver_date);
13243
13244 if (show_version_only) {
13245 fprintf(stderr, _("\tUsing %s, %s\n"),
13246 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013247 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013248 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013249
Mike Frysinger51a43b42005-09-24 07:11:16 +000013250 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013251
Mike Frysinger51a43b42005-09-24 07:11:16 +000013252 if (!(ctx->options & E2F_OPT_PREEN) &&
13253 !(ctx->options & E2F_OPT_NO) &&
13254 !(ctx->options & E2F_OPT_YES)) {
13255 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000013256 bb_error_msg_and_die(_("need terminal for interactive repairs"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013257 }
13258 ctx->superblock = ctx->use_superblock;
13259restart:
13260#ifdef CONFIG_TESTIO_DEBUG
13261 io_ptr = test_io_manager;
13262 test_io_backing_manager = unix_io_manager;
13263#else
13264 io_ptr = unix_io_manager;
13265#endif
13266 flags = 0;
13267 if ((ctx->options & E2F_OPT_READONLY) == 0)
13268 flags |= EXT2_FLAG_RW;
13269
13270 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013271 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013272 flags, ctx->superblock, ctx->blocksize,
13273 io_ptr, &fs);
13274 } else if (ctx->superblock) {
13275 int blocksize;
13276 for (blocksize = EXT2_MIN_BLOCK_SIZE;
13277 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013278 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013279 ctx->io_options, flags,
13280 ctx->superblock, blocksize,
13281 io_ptr, &fs);
13282 if (!retval)
13283 break;
13284 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013285 } else
13286 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013287 flags, 0, 0, io_ptr, &fs);
13288 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
13289 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
13290 ((retval == EXT2_ET_BAD_MAGIC) ||
13291 ((retval == 0) && ext2fs_check_desc(fs)))) {
13292 if (!fs || (fs->group_desc_count > 1)) {
13293 printf(_("%s trying backup blocks...\n"),
13294 retval ? _("Couldn't find ext2 superblock,") :
13295 _("Group descriptors look bad..."));
13296 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
13297 if (fs)
13298 ext2fs_close(fs);
13299 goto restart;
13300 }
13301 }
13302 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013303 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013304 ctx->filesystem_name);
13305 if (retval == EXT2_ET_REV_TOO_HIGH) {
13306 printf(_("The filesystem revision is apparently "
13307 "too high for this version of e2fsck.\n"
13308 "(Or the filesystem superblock "
13309 "is corrupt)\n\n"));
13310 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
13311 } else if (retval == EXT2_ET_SHORT_READ)
13312 printf(_("Could this be a zero-length partition?\n"));
13313 else if ((retval == EPERM) || (retval == EACCES))
13314 printf(_("You must have %s access to the "
13315 "filesystem or be root\n"),
13316 (ctx->options & E2F_OPT_READONLY) ?
13317 "r/o" : "r/w");
13318 else if (retval == ENXIO)
13319 printf(_("Possibly non-existent or swap device?\n"));
13320#ifdef EROFS
13321 else if (retval == EROFS)
13322 printf(_("Disk write-protected; use the -n option "
13323 "to do a read-only\n"
13324 "check of the device.\n"));
13325#endif
13326 else
13327 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
Rob Landley7c94bed2006-05-03 21:58:45 +000013328 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013329 }
13330 ctx->fs = fs;
13331 fs->priv_data = ctx;
13332 sb = fs->super;
13333 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013334 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013335 ctx->filesystem_name);
13336 get_newer:
Rob Landley7c94bed2006-05-03 21:58:45 +000013337 bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013338 }
13339
13340 /*
13341 * Set the device name, which is used whenever we print error
13342 * or informational messages to the user.
13343 */
13344 if (ctx->device_name == 0 &&
13345 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013346 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013347 sizeof(sb->s_volume_name));
13348 }
13349 if (ctx->device_name == 0)
13350 ctx->device_name = ctx->filesystem_name;
13351
13352 /*
13353 * Make sure the ext3 superblock fields are consistent.
13354 */
13355 retval = e2fsck_check_ext3_journal(ctx);
13356 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013357 bb_error_msg(_("while checking ext3 journal for %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013358 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013359 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013360 }
13361
13362 /*
13363 * Check to see if we need to do ext3-style recovery. If so,
13364 * do it, and then restart the fsck.
13365 */
13366 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
13367 if (ctx->options & E2F_OPT_READONLY) {
13368 printf(_("Warning: skipping journal recovery "
13369 "because doing a read-only filesystem "
13370 "check.\n"));
13371 io_channel_flush(ctx->fs->io);
13372 } else {
13373 if (ctx->flags & E2F_FLAG_RESTARTED) {
13374 /*
13375 * Whoops, we attempted to run the
13376 * journal twice. This should never
13377 * happen, unless the hardware or
13378 * device driver is being bogus.
13379 */
Denis Vlasenko6d655be2006-09-06 19:02:46 +000013380 bb_error_msg(_("unable to set superblock flags on %s"), ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013381 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013382 }
13383 retval = e2fsck_run_ext3_journal(ctx);
13384 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013385 bb_error_msg(_("while recovering ext3 journal of %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013386 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013387 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013388 }
13389 ext2fs_close(ctx->fs);
13390 ctx->fs = 0;
13391 ctx->flags |= E2F_FLAG_RESTARTED;
13392 goto restart;
13393 }
13394 }
13395
13396 /*
13397 * Check for compatibility with the feature sets. We need to
13398 * be more stringent than ext2fs_open().
13399 */
13400 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
13401 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013402 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013403 goto get_newer;
13404 }
13405 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013406 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013407 goto get_newer;
13408 }
13409#ifdef ENABLE_COMPRESSION
Rob Landley3e72c592006-04-06 22:49:04 +000013410 /* FIXME - do we support this at all? */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013411 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
Denis Vlasenko6d655be2006-09-06 19:02:46 +000013412 bb_error_msg(_("Warning: compression support is experimental."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013413#endif
13414#ifndef ENABLE_HTREE
13415 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013416 bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
Denis Vlasenko6d655be2006-09-06 19:02:46 +000013417 "but filesystem %s has HTREE directories."),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013418 ctx->device_name);
13419 goto get_newer;
13420 }
13421#endif
13422
13423 /*
13424 * If the user specified a specific superblock, presumably the
13425 * master superblock has been trashed. So we mark the
13426 * superblock as dirty, so it can be written out.
13427 */
13428 if (ctx->superblock &&
13429 !(ctx->options & E2F_OPT_READONLY))
13430 ext2fs_mark_super_dirty(fs);
13431
13432 /*
13433 * We only update the master superblock because (a) paranoia;
13434 * we don't want to corrupt the backup superblocks, and (b) we
13435 * don't need to update the mount count and last checked
13436 * fields in the backup superblock (the kernel doesn't
13437 * update the backup superblocks anyway).
13438 */
13439 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
13440
13441 ehandler_init(fs->io);
13442
13443 if (ctx->superblock)
13444 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
13445 ext2fs_mark_valid(fs);
13446 check_super_block(ctx);
13447 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013448 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013449 check_if_skip(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013450 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013451 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013452#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000013453
13454#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000013455#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000013456#else
Rob Landley8b606342006-01-24 02:38:28 +000013457#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000013458#endif
13459
13460
Mike Frysinger51a43b42005-09-24 07:11:16 +000013461 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000013462 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013463 fprintf(stderr, _("%s: Filesystem byte order "
13464 "already normalized.\n"), ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013465 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013466 }
13467 }
13468 if (swapfs) {
13469 swap_filesys(ctx);
13470 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013471 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013472 }
13473#endif
13474
13475 /*
13476 * Mark the system as valid, 'til proven otherwise
13477 */
13478 ext2fs_mark_valid(fs);
13479
13480 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
13481 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013482 bb_error_msg(_("while reading bad blocks inode"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013483 preenhalt(ctx);
13484 printf(_("This doesn't bode well,"
13485 " but we'll try to go on...\n"));
13486 }
13487
13488 run_result = e2fsck_run(ctx);
13489 e2fsck_clear_progbar(ctx);
13490 if (run_result == E2F_FLAG_RESTART) {
13491 printf(_("Restarting e2fsck from the beginning...\n"));
13492 retval = e2fsck_reset_context(ctx);
13493 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013494 bb_error_msg(_("while resetting context"));
13495 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013496 }
13497 ext2fs_close(fs);
13498 goto restart;
13499 }
13500 if (run_result & E2F_FLAG_CANCEL) {
13501 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
13502 ctx->device_name : ctx->filesystem_name);
13503 exit_value |= FSCK_CANCELED;
13504 }
13505 if (run_result & E2F_FLAG_ABORT)
Rob Landley7c94bed2006-05-03 21:58:45 +000013506 bb_error_msg_and_die(_("aborted"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013507
Rob Landley3e72c592006-04-06 22:49:04 +000013508 /* Cleanup */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013509 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013510 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013511 if (!(ctx->options & E2F_OPT_PREEN))
13512 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
13513 ctx->device_name);
13514 if (ctx->mount_flags & EXT2_MF_ISROOT) {
13515 printf(_("%s: ***** REBOOT LINUX *****\n"),
13516 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013517 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013518 }
13519 }
13520 if (!ext2fs_test_valid(fs)) {
13521 printf(_("\n%s: ********** WARNING: Filesystem still has "
13522 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013523 exit_value |= EXIT_UNCORRECTED;
13524 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013525 }
13526 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013527 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013528 else {
13529 show_stats(ctx);
13530 if (!(ctx->options & E2F_OPT_READONLY)) {
13531 if (ext2fs_test_valid(fs)) {
13532 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013533 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013534 sb->s_state = EXT2_VALID_FS;
13535 } else
13536 sb->s_state &= ~EXT2_VALID_FS;
13537 sb->s_mnt_count = 0;
13538 sb->s_lastcheck = time(NULL);
13539 ext2fs_mark_super_dirty(fs);
13540 }
13541 }
13542
13543 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013544
Mike Frysinger51a43b42005-09-24 07:11:16 +000013545 ext2fs_close(fs);
13546 ctx->fs = NULL;
13547 free(ctx->filesystem_name);
13548 free(ctx->journal_name);
13549 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013550
Mike Frysinger51a43b42005-09-24 07:11:16 +000013551 return exit_value;
13552}