diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index 5aaff7b7..5da29d9d 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,3 +1,13 @@ +2002-08-01 Theodore Ts'o + + * dict.c, dict.h: New file from kazlib 1.20 which implements a + red-black tree + + * pass1b.c: Massive changes to take advantage of dict.c. This + removes several O(n**2) algorithms from the rare case + where there are a large number of blocks claimed by + multiple inodes. + 2002-07-24 Theodore Ts'o * e2fsck.8.in, e2fsck.h, pass3.c (e2fsck_pass3), unix.c, rehash.c: diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in index 44732803..d6bf0e8d 100644 --- a/e2fsck/Makefile.in +++ b/e2fsck/Makefile.in @@ -54,13 +54,13 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \ # #MCHECK= -DMCHECK -OBJS= unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \ +OBJS= dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \ pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o dx_dirinfo.o \ ehandler.o problem.o message.o recovery.o region.o revoke.o \ ea_refcount.o rehash.o $(MTRACE_OBJ) -PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \ - profiled/pass1.o profiled/pass1b.o \ +PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \ + profiled/super.o profiled/pass1.o profiled/pass1b.o \ profiled/pass2.o profiled/pass3.o profiled/pass4.o profiled/pass5.o \ profiled/journal.o profiled/badblocks.o profiled/util.o \ profiled/dirinfo.o profiled/dx_dirinfo.o profiled/ehandler.o \ @@ -69,6 +69,7 @@ PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \ profiled/ea_refcount.o profiled/rehash.o SRCS= $(srcdir)/e2fsck.c \ + $(srcdir)/dict.c \ $(srcdir)/super.c \ $(srcdir)/pass1.c \ $(srcdir)/pass1b.c \ diff --git a/e2fsck/dict.c b/e2fsck/dict.c new file mode 100644 index 00000000..27652541 --- /dev/null +++ b/e2fsck/dict.c @@ -0,0 +1,1515 @@ +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $ + * $Name: kazlib_1_20 $ + */ + +#define NDEBUG + +#include +#include +#include +#define DICT_IMPLEMENTATION +#include "dict.h" + +#ifdef KAZLIB_RCSID +static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $"; +#endif + +/* + * These macros provide short convenient names for structure members, + * which are embellished with dict_ prefixes so that they are + * properly confined to the documented namespace. It's legal for a + * program which uses dict to define, for instance, a macro called ``parent''. + * Such a macro would interfere with the dnode_t struct definition. + * In general, highly portable and reusable C modules which expose their + * structures need to confine structure member names to well-defined spaces. + * The resulting identifiers aren't necessarily convenient to use, nor + * readable, in the implementation, however! + */ + +#define left dict_left +#define right dict_right +#define parent dict_parent +#define color dict_color +#define key dict_key +#define data dict_data + +#define nilnode dict_nilnode +#define nodecount dict_nodecount +#define maxcount dict_maxcount +#define compare dict_compare +#define allocnode dict_allocnode +#define freenode dict_freenode +#define context dict_context +#define dupes dict_dupes + +#define dictptr dict_dictptr + +#define dict_root(D) ((D)->nilnode.left) +#define dict_nil(D) (&(D)->nilnode) +#define DICT_DEPTH_MAX 64 + +static dnode_t *dnode_alloc(void *context); +static void dnode_free(dnode_t *node, void *context); + +/* + * Perform a ``left rotation'' adjustment on the tree. The given node P and + * its right child C are rearranged so that the P instead becomes the left + * child of C. The left subtree of C is inherited as the new right subtree + * for P. The ordering of the keys within the tree is thus preserved. + */ + +static void rotate_left(dnode_t *upper) +{ + dnode_t *lower, *lowleft, *upparent; + + lower = upper->right; + upper->right = lowleft = lower->left; + lowleft->parent = upper; + + lower->parent = upparent = upper->parent; + + /* don't need to check for root node here because root->parent is + the sentinel nil node, and root->parent->left points back to root */ + + if (upper == upparent->left) { + upparent->left = lower; + } else { + assert (upper == upparent->right); + upparent->right = lower; + } + + lower->left = upper; + upper->parent = lower; +} + +/* + * This operation is the ``mirror'' image of rotate_left. It is + * the same procedure, but with left and right interchanged. + */ + +static void rotate_right(dnode_t *upper) +{ + dnode_t *lower, *lowright, *upparent; + + lower = upper->left; + upper->left = lowright = lower->right; + lowright->parent = upper; + + lower->parent = upparent = upper->parent; + + if (upper == upparent->right) { + upparent->right = lower; + } else { + assert (upper == upparent->left); + upparent->left = lower; + } + + lower->right = upper; + upper->parent = lower; +} + +/* + * Do a postorder traversal of the tree rooted at the specified + * node and free everything under it. Used by dict_free(). + */ + +static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) +{ + if (node == nil) + return; + free_nodes(dict, node->left, nil); + free_nodes(dict, node->right, nil); + dict->freenode(node, dict->context); +} + +/* + * This procedure performs a verification that the given subtree is a binary + * search tree. It performs an inorder traversal of the tree using the + * dict_next() successor function, verifying that the key of each node is + * strictly lower than that of its successor, if duplicates are not allowed, + * or lower or equal if duplicates are allowed. This function is used for + * debugging purposes. + */ +#ifndef NDEBUG +static int verify_bintree(dict_t *dict) +{ + dnode_t *first, *next; + + first = dict_first(dict); + + if (dict->dupes) { + while (first && (next = dict_next(dict, first))) { + if (dict->compare(first->key, next->key) > 0) + return 0; + first = next; + } + } else { + while (first && (next = dict_next(dict, first))) { + if (dict->compare(first->key, next->key) >= 0) + return 0; + first = next; + } + } + return 1; +} + +/* + * This function recursively verifies that the given binary subtree satisfies + * three of the red black properties. It checks that every red node has only + * black children. It makes sure that each node is either red or black. And it + * checks that every path has the same count of black nodes from root to leaf. + * It returns the blackheight of the given subtree; this allows blackheights to + * be computed recursively and compared for left and right siblings for + * mismatches. It does not check for every nil node being black, because there + * is only one sentinel nil node. The return value of this function is the + * black height of the subtree rooted at the node ``root'', or zero if the + * subtree is not red-black. + */ + +static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) +{ + unsigned height_left, height_right; + + if (root != nil) { + height_left = verify_redblack(nil, root->left); + height_right = verify_redblack(nil, root->right); + if (height_left == 0 || height_right == 0) + return 0; + if (height_left != height_right) + return 0; + if (root->color == dnode_red) { + if (root->left->color != dnode_black) + return 0; + if (root->right->color != dnode_black) + return 0; + return height_left; + } + if (root->color != dnode_black) + return 0; + return height_left + 1; + } + return 1; +} + +/* + * Compute the actual count of nodes by traversing the tree and + * return it. This could be compared against the stored count to + * detect a mismatch. + */ + +static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) +{ + if (root == nil) + return 0; + else + return 1 + verify_node_count(nil, root->left) + + verify_node_count(nil, root->right); +} +#endif + +/* + * Verify that the tree contains the given node. This is done by + * traversing all of the nodes and comparing their pointers to the + * given pointer. Returns 1 if the node is found, otherwise + * returns zero. It is intended for debugging purposes. + */ + +static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) +{ + if (root != nil) { + return root == node + || verify_dict_has_node(nil, root->left, node) + || verify_dict_has_node(nil, root->right, node); + } + return 0; +} + + +#ifdef E2FSCK_NOTUSED +/* + * Dynamically allocate and initialize a dictionary object. + */ + +dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) +{ + dict_t *new = malloc(sizeof *new); + + if (new) { + new->compare = comp; + new->allocnode = dnode_alloc; + new->freenode = dnode_free; + new->context = NULL; + new->nodecount = 0; + new->maxcount = maxcount; + new->nilnode.left = &new->nilnode; + new->nilnode.right = &new->nilnode; + new->nilnode.parent = &new->nilnode; + new->nilnode.color = dnode_black; + new->dupes = 0; + } + return new; +} +#endif /* E2FSCK_NOTUSED */ + +/* + * Select a different set of node allocator routines. + */ + +void dict_set_allocator(dict_t *dict, dnode_alloc_t al, + dnode_free_t fr, void *context) +{ + assert (dict_count(dict) == 0); + assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); + + dict->allocnode = al ? al : dnode_alloc; + dict->freenode = fr ? fr : dnode_free; + dict->context = context; +} + +#ifdef E2FSCK_NOTUSED +/* + * Free a dynamically allocated dictionary object. Removing the nodes + * from the tree before deleting it is required. + */ + +void dict_destroy(dict_t *dict) +{ + assert (dict_isempty(dict)); + free(dict); +} +#endif + +/* + * Free all the nodes in the dictionary by using the dictionary's + * installed free routine. The dictionary is emptied. + */ + +void dict_free_nodes(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict); + free_nodes(dict, root, nil); + dict->nodecount = 0; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; +} + +#ifdef E2FSCK_NOTUSED +/* + * Obsolescent function, equivalent to dict_free_nodes + */ +void dict_free(dict_t *dict) +{ +#ifdef KAZLIB_OBSOLESCENT_DEBUG + assert ("call to obsolescent function dict_free()" && 0); +#endif + dict_free_nodes(dict); +} +#endif + +/* + * Initialize a user-supplied dictionary object. + */ + +dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) +{ + dict->compare = comp; + dict->allocnode = dnode_alloc; + dict->freenode = dnode_free; + dict->context = NULL; + dict->nodecount = 0; + dict->maxcount = maxcount; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + dict->nilnode.color = dnode_black; + dict->dupes = 0; + return dict; +} + +#ifdef E2FSCK_NOTUSED +/* + * Initialize a dictionary in the likeness of another dictionary + */ + +void dict_init_like(dict_t *dict, const dict_t *template) +{ + dict->compare = template->compare; + dict->allocnode = template->allocnode; + dict->freenode = template->freenode; + dict->context = template->context; + dict->nodecount = 0; + dict->maxcount = template->maxcount; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + dict->nilnode.color = dnode_black; + dict->dupes = template->dupes; + + assert (dict_similar(dict, template)); +} + +/* + * Remove all nodes from the dictionary (without freeing them in any way). + */ + +static void dict_clear(dict_t *dict) +{ + dict->nodecount = 0; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + assert (dict->nilnode.color == dnode_black); +} + + +/* + * Verify the integrity of the dictionary structure. This is provided for + * debugging purposes, and should be placed in assert statements. Just because + * this function succeeds doesn't mean that the tree is not corrupt. Certain + * corruptions in the tree may simply cause undefined behavior. + */ + +int dict_verify(dict_t *dict) +{ +#ifndef NDEBUG + dnode_t *nil = dict_nil(dict), *root = dict_root(dict); + + /* check that the sentinel node and root node are black */ + if (root->color != dnode_black) + return 0; + if (nil->color != dnode_black) + return 0; + if (nil->right != nil) + return 0; + /* nil->left is the root node; check that its parent pointer is nil */ + if (nil->left->parent != nil) + return 0; + /* perform a weak test that the tree is a binary search tree */ + if (!verify_bintree(dict)) + return 0; + /* verify that the tree is a red-black tree */ + if (!verify_redblack(nil, root)) + return 0; + if (verify_node_count(nil, root) != dict_count(dict)) + return 0; +#endif + return 1; +} + +/* + * Determine whether two dictionaries are similar: have the same comparison and + * allocator functions, and same status as to whether duplicates are allowed. + */ + +int dict_similar(const dict_t *left, const dict_t *right) +{ + if (left->compare != right->compare) + return 0; + + if (left->allocnode != right->allocnode) + return 0; + + if (left->freenode != right->freenode) + return 0; + + if (left->context != right->context) + return 0; + + if (left->dupes != right->dupes) + return 0; + + return 1; +} +#endif /* E2FSCK_NOTUSED */ + +/* + * Locate a node in the dictionary having the given key. + * If the node is not found, a null a pointer is returned (rather than + * a pointer that dictionary's nil sentinel node), otherwise a pointer to the + * located node is returned. + */ + +dnode_t *dict_lookup(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *saved; + int result; + + /* simple binary search adapted for trees that contain duplicate keys */ + + while (root != nil) { + result = dict->compare(key, root->key); + if (result < 0) + root = root->left; + else if (result > 0) + root = root->right; + else { + if (!dict->dupes) { /* no duplicates, return match */ + return root; + } else { /* could be dupes, find leftmost one */ + do { + saved = root; + root = root->left; + while (root != nil && dict->compare(key, root->key)) + root = root->right; + } while (root != nil); + return saved; + } + } + } + + return NULL; +} + +#ifdef E2FSCK_NOTUSED +/* + * Look for the node corresponding to the lowest key that is equal to or + * greater than the given key. If there is no such node, return null. + */ + +dnode_t *dict_lower_bound(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *tentative = 0; + + while (root != nil) { + int result = dict->compare(key, root->key); + + if (result > 0) { + root = root->right; + } else if (result < 0) { + tentative = root; + root = root->left; + } else { + if (!dict->dupes) { + return root; + } else { + tentative = root; + root = root->left; + } + } + } + + return tentative; +} + +/* + * Look for the node corresponding to the greatest key that is equal to or + * lower than the given key. If there is no such node, return null. + */ + +dnode_t *dict_upper_bound(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *tentative = 0; + + while (root != nil) { + int result = dict->compare(key, root->key); + + if (result < 0) { + root = root->left; + } else if (result > 0) { + tentative = root; + root = root->right; + } else { + if (!dict->dupes) { + return root; + } else { + tentative = root; + root = root->right; + } + } + } + + return tentative; +} +#endif + +/* + * Insert a node into the dictionary. The node should have been + * initialized with a data field. All other fields are ignored. + * The behavior is undefined if the user attempts to insert into + * a dictionary that is already full (for which the dict_isfull() + * function returns true). + */ + +void dict_insert(dict_t *dict, dnode_t *node, const void *key) +{ + dnode_t *where = dict_root(dict), *nil = dict_nil(dict); + dnode_t *parent = nil, *uncle, *grandpa; + int result = -1; + + node->key = key; + + assert (!dict_isfull(dict)); + assert (!dict_contains(dict, node)); + assert (!dnode_is_in_a_dict(node)); + + /* basic binary tree insert */ + + while (where != nil) { + parent = where; + result = dict->compare(key, where->key); + /* trap attempts at duplicate key insertion unless it's explicitly allowed */ + assert (dict->dupes || result != 0); + if (result < 0) + where = where->left; + else + where = where->right; + } + + assert (where == nil); + + if (result < 0) + parent->left = node; + else + parent->right = node; + + node->parent = parent; + node->left = nil; + node->right = nil; + + dict->nodecount++; + + /* red black adjustments */ + + node->color = dnode_red; + + while (parent->color == dnode_red) { + grandpa = parent->parent; + if (parent == grandpa->left) { + uncle = grandpa->right; + if (uncle->color == dnode_red) { /* red parent, red uncle */ + parent->color = dnode_black; + uncle->color = dnode_black; + grandpa->color = dnode_red; + node = grandpa; + parent = grandpa->parent; + } else { /* red parent, black uncle */ + if (node == parent->right) { + rotate_left(parent); + parent = node; + assert (grandpa == parent->parent); + /* rotation between parent and child preserves grandpa */ + } + parent->color = dnode_black; + grandpa->color = dnode_red; + rotate_right(grandpa); + break; + } + } else { /* symmetric cases: parent == parent->parent->right */ + uncle = grandpa->left; + if (uncle->color == dnode_red) { + parent->color = dnode_black; + uncle->color = dnode_black; + grandpa->color = dnode_red; + node = grandpa; + parent = grandpa->parent; + } else { + if (node == parent->left) { + rotate_right(parent); + parent = node; + assert (grandpa == parent->parent); + } + parent->color = dnode_black; + grandpa->color = dnode_red; + rotate_left(grandpa); + break; + } + } + } + + dict_root(dict)->color = dnode_black; + + assert (dict_verify(dict)); +} + +#ifdef E2FSCK_NOTUSED +/* + * Delete the given node from the dictionary. If the given node does not belong + * to the given dictionary, undefined behavior results. A pointer to the + * deleted node is returned. + */ + +dnode_t *dict_delete(dict_t *dict, dnode_t *delete) +{ + dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; + + /* basic deletion */ + + assert (!dict_isempty(dict)); + assert (dict_contains(dict, delete)); + + /* + * If the node being deleted has two children, then we replace it with its + * successor (i.e. the leftmost node in the right subtree.) By doing this, + * we avoid the traditional algorithm under which the successor's key and + * value *only* move to the deleted node and the successor is spliced out + * from the tree. We cannot use this approach because the user may hold + * pointers to the successor, or nodes may be inextricably tied to some + * other structures by way of embedding, etc. So we must splice out the + * node we are given, not some other node, and must not move contents from + * one node to another behind the user's back. + */ + + if (delete->left != nil && delete->right != nil) { + dnode_t *next = dict_next(dict, delete); + dnode_t *nextparent = next->parent; + dnode_color_t nextcolor = next->color; + + assert (next != nil); + assert (next->parent != nil); + assert (next->left == nil); + + /* + * First, splice out the successor from the tree completely, by + * moving up its right child into its place. + */ + + child = next->right; + child->parent = nextparent; + + if (nextparent->left == next) { + nextparent->left = child; + } else { + assert (nextparent->right == next); + nextparent->right = child; + } + + /* + * Now that the successor has been extricated from the tree, install it + * in place of the node that we want deleted. + */ + + next->parent = delparent; + next->left = delete->left; + next->right = delete->right; + next->left->parent = next; + next->right->parent = next; + next->color = delete->color; + delete->color = nextcolor; + + if (delparent->left == delete) { + delparent->left = next; + } else { + assert (delparent->right == delete); + delparent->right = next; + } + + } else { + assert (delete != nil); + assert (delete->left == nil || delete->right == nil); + + child = (delete->left != nil) ? delete->left : delete->right; + + child->parent = delparent = delete->parent; + + if (delete == delparent->left) { + delparent->left = child; + } else { + assert (delete == delparent->right); + delparent->right = child; + } + } + + delete->parent = NULL; + delete->right = NULL; + delete->left = NULL; + + dict->nodecount--; + + assert (verify_bintree(dict)); + + /* red-black adjustments */ + + if (delete->color == dnode_black) { + dnode_t *parent, *sister; + + dict_root(dict)->color = dnode_red; + + while (child->color == dnode_black) { + parent = child->parent; + if (child == parent->left) { + sister = parent->right; + assert (sister != nil); + if (sister->color == dnode_red) { + sister->color = dnode_black; + parent->color = dnode_red; + rotate_left(parent); + sister = parent->right; + assert (sister != nil); + } + if (sister->left->color == dnode_black + && sister->right->color == dnode_black) { + sister->color = dnode_red; + child = parent; + } else { + if (sister->right->color == dnode_black) { + assert (sister->left->color == dnode_red); + sister->left->color = dnode_black; + sister->color = dnode_red; + rotate_right(sister); + sister = parent->right; + assert (sister != nil); + } + sister->color = parent->color; + sister->right->color = dnode_black; + parent->color = dnode_black; + rotate_left(parent); + break; + } + } else { /* symmetric case: child == child->parent->right */ + assert (child == parent->right); + sister = parent->left; + assert (sister != nil); + if (sister->color == dnode_red) { + sister->color = dnode_black; + parent->color = dnode_red; + rotate_right(parent); + sister = parent->left; + assert (sister != nil); + } + if (sister->right->color == dnode_black + && sister->left->color == dnode_black) { + sister->color = dnode_red; + child = parent; + } else { + if (sister->left->color == dnode_black) { + assert (sister->right->color == dnode_red); + sister->right->color = dnode_black; + sister->color = dnode_red; + rotate_left(sister); + sister = parent->left; + assert (sister != nil); + } + sister->color = parent->color; + sister->left->color = dnode_black; + parent->color = dnode_black; + rotate_right(parent); + break; + } + } + } + + child->color = dnode_black; + dict_root(dict)->color = dnode_black; + } + + assert (dict_verify(dict)); + + return delete; +} +#endif /* E2FSCK_NOTUSED */ + +/* + * Allocate a node using the dictionary's allocator routine, give it + * the data item. + */ + +int dict_alloc_insert(dict_t *dict, const void *key, void *data) +{ + dnode_t *node = dict->allocnode(dict->context); + + if (node) { + dnode_init(node, data); + dict_insert(dict, node, key); + return 1; + } + return 0; +} + +#ifdef E2FSCK_NOTUSED +void dict_delete_free(dict_t *dict, dnode_t *node) +{ + dict_delete(dict, node); + dict->freenode(node, dict->context); +} +#endif + +/* + * Return the node with the lowest (leftmost) key. If the dictionary is empty + * (that is, dict_isempty(dict) returns 1) a null pointer is returned. + */ + +dnode_t *dict_first(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; + + if (root != nil) + while ((left = root->left) != nil) + root = left; + + return (root == nil) ? NULL : root; +} + +/* + * Return the node with the highest (rightmost) key. If the dictionary is empty + * (that is, dict_isempty(dict) returns 1) a null pointer is returned. + */ + +dnode_t *dict_last(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; + + if (root != nil) + while ((right = root->right) != nil) + root = right; + + return (root == nil) ? NULL : root; +} + +/* + * Return the given node's successor node---the node which has the + * next key in the the left to right ordering. If the node has + * no successor, a null pointer is returned rather than a pointer to + * the nil node. + */ + +dnode_t *dict_next(dict_t *dict, dnode_t *curr) +{ + dnode_t *nil = dict_nil(dict), *parent, *left; + + if (curr->right != nil) { + curr = curr->right; + while ((left = curr->left) != nil) + curr = left; + return curr; + } + + parent = curr->parent; + + while (parent != nil && curr == parent->right) { + curr = parent; + parent = curr->parent; + } + + return (parent == nil) ? NULL : parent; +} + +/* + * Return the given node's predecessor, in the key order. + * The nil sentinel node is returned if there is no predecessor. + */ + +dnode_t *dict_prev(dict_t *dict, dnode_t *curr) +{ + dnode_t *nil = dict_nil(dict), *parent, *right; + + if (curr->left != nil) { + curr = curr->left; + while ((right = curr->right) != nil) + curr = right; + return curr; + } + + parent = curr->parent; + + while (parent != nil && curr == parent->left) { + curr = parent; + parent = curr->parent; + } + + return (parent == nil) ? NULL : parent; +} + +void dict_allow_dupes(dict_t *dict) +{ + dict->dupes = 1; +} + +#undef dict_count +#undef dict_isempty +#undef dict_isfull +#undef dnode_get +#undef dnode_put +#undef dnode_getkey + +dictcount_t dict_count(dict_t *dict) +{ + return dict->nodecount; +} + +int dict_isempty(dict_t *dict) +{ + return dict->nodecount == 0; +} + +int dict_isfull(dict_t *dict) +{ + return dict->nodecount == dict->maxcount; +} + +int dict_contains(dict_t *dict, dnode_t *node) +{ + return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); +} + +static dnode_t *dnode_alloc(void *context) +{ + return malloc(sizeof *dnode_alloc(NULL)); +} + +static void dnode_free(dnode_t *node, void *context) +{ + free(node); +} + +dnode_t *dnode_create(void *data) +{ + dnode_t *new = malloc(sizeof *new); + if (new) { + new->data = data; + new->parent = NULL; + new->left = NULL; + new->right = NULL; + } + return new; +} + +dnode_t *dnode_init(dnode_t *dnode, void *data) +{ + dnode->data = data; + dnode->parent = NULL; + dnode->left = NULL; + dnode->right = NULL; + return dnode; +} + +void dnode_destroy(dnode_t *dnode) +{ + assert (!dnode_is_in_a_dict(dnode)); + free(dnode); +} + +void *dnode_get(dnode_t *dnode) +{ + return dnode->data; +} + +const void *dnode_getkey(dnode_t *dnode) +{ + return dnode->key; +} + +#ifdef E2FSCK_NOTUSED +void dnode_put(dnode_t *dnode, void *data) +{ + dnode->data = data; +} + +int dnode_is_in_a_dict(dnode_t *dnode) +{ + return (dnode->parent && dnode->left && dnode->right); +} + +void dict_process(dict_t *dict, void *context, dnode_process_t function) +{ + dnode_t *node = dict_first(dict), *next; + + while (node != NULL) { + /* check for callback function deleting */ + /* the next node from under us */ + assert (dict_contains(dict, node)); + next = dict_next(dict, node); + function(dict, node, context); + node = next; + } +} + +static void load_begin_internal(dict_load_t *load, dict_t *dict) +{ + load->dictptr = dict; + load->nilnode.left = &load->nilnode; + load->nilnode.right = &load->nilnode; +} + +void dict_load_begin(dict_load_t *load, dict_t *dict) +{ + assert (dict_isempty(dict)); + load_begin_internal(load, dict); +} + +void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) +{ + dict_t *dict = load->dictptr; + dnode_t *nil = &load->nilnode; + + assert (!dnode_is_in_a_dict(newnode)); + assert (dict->nodecount < dict->maxcount); + + #ifndef NDEBUG + if (dict->nodecount > 0) { + if (dict->dupes) + assert (dict->compare(nil->left->key, key) <= 0); + else + assert (dict->compare(nil->left->key, key) < 0); + } + #endif + + newnode->key = key; + nil->right->left = newnode; + nil->right = newnode; + newnode->left = nil; + dict->nodecount++; +} + +void dict_load_end(dict_load_t *load) +{ + dict_t *dict = load->dictptr; + dnode_t *tree[DICT_DEPTH_MAX] = { 0 }; + dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next; + dnode_t *complete = 0; + dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; + dictcount_t botrowcount; + unsigned baselevel = 0, level = 0, i; + + assert (dnode_red == 0 && dnode_black == 1); + + while (fullcount >= nodecount && fullcount) + fullcount >>= 1; + + botrowcount = nodecount - fullcount; + + for (curr = loadnil->left; curr != loadnil; curr = next) { + next = curr->left; + + if (complete == NULL && botrowcount-- == 0) { + assert (baselevel == 0); + assert (level == 0); + baselevel = level = 1; + complete = tree[0]; + + if (complete != 0) { + tree[0] = 0; + complete->right = dictnil; + while (tree[level] != 0) { + tree[level]->right = complete; + complete->parent = tree[level]; + complete = tree[level]; + tree[level++] = 0; + } + } + } + + if (complete == NULL) { + curr->left = dictnil; + curr->right = dictnil; + curr->color = level % 2; + complete = curr; + + assert (level == baselevel); + while (tree[level] != 0) { + tree[level]->right = complete; + complete->parent = tree[level]; + complete = tree[level]; + tree[level++] = 0; + } + } else { + curr->left = complete; + curr->color = (level + 1) % 2; + complete->parent = curr; + tree[level] = curr; + complete = 0; + level = baselevel; + } + } + + if (complete == NULL) + complete = dictnil; + + for (i = 0; i < DICT_DEPTH_MAX; i++) { + if (tree[i] != 0) { + tree[i]->right = complete; + complete->parent = tree[i]; + complete = tree[i]; + } + } + + dictnil->color = dnode_black; + dictnil->right = dictnil; + complete->parent = dictnil; + complete->color = dnode_black; + dict_root(dict) = complete; + + assert (dict_verify(dict)); +} + +void dict_merge(dict_t *dest, dict_t *source) +{ + dict_load_t load; + dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); + + assert (dict_similar(dest, source)); + + if (source == dest) + return; + + dest->nodecount = 0; + load_begin_internal(&load, dest); + + for (;;) { + if (leftnode != NULL && rightnode != NULL) { + if (dest->compare(leftnode->key, rightnode->key) < 0) + goto copyleft; + else + goto copyright; + } else if (leftnode != NULL) { + goto copyleft; + } else if (rightnode != NULL) { + goto copyright; + } else { + assert (leftnode == NULL && rightnode == NULL); + break; + } + + copyleft: + { + dnode_t *next = dict_next(dest, leftnode); + #ifndef NDEBUG + leftnode->left = NULL; /* suppress assertion in dict_load_next */ + #endif + dict_load_next(&load, leftnode, leftnode->key); + leftnode = next; + continue; + } + + copyright: + { + dnode_t *next = dict_next(source, rightnode); + #ifndef NDEBUG + rightnode->left = NULL; + #endif + dict_load_next(&load, rightnode, rightnode->key); + rightnode = next; + continue; + } + } + + dict_clear(source); + dict_load_end(&load); +} +#endif /* E2FSCK_NOTUSED */ + +#ifdef KAZLIB_TEST_MAIN + +#include +#include +#include +#include + +typedef char input_t[256]; + +static int tokenize(char *string, ...) +{ + char **tokptr; + va_list arglist; + int tokcount = 0; + + va_start(arglist, string); + tokptr = va_arg(arglist, char **); + while (tokptr) { + while (*string && isspace((unsigned char) *string)) + string++; + if (!*string) + break; + *tokptr = string; + while (*string && !isspace((unsigned char) *string)) + string++; + tokptr = va_arg(arglist, char **); + tokcount++; + if (!*string) + break; + *string++ = 0; + } + va_end(arglist); + + return tokcount; +} + +static int comparef(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} + +static char *dupstring(char *str) +{ + int sz = strlen(str) + 1; + char *new = malloc(sz); + if (new) + memcpy(new, str, sz); + return new; +} + +static dnode_t *new_node(void *c) +{ + static dnode_t few[5]; + static int count; + + if (count < 5) + return few + count++; + + return NULL; +} + +static void del_node(dnode_t *n, void *c) +{ +} + +static int prompt = 0; + +static void construct(dict_t *d) +{ + input_t in; + int done = 0; + dict_load_t dl; + dnode_t *dn; + char *tok1, *tok2, *val; + const char *key; + char *help = + "p turn prompt on\n" + "q finish construction\n" + "a add new entry\n"; + + if (!dict_isempty(d)) + puts("warning: dictionary not empty!"); + + dict_load_begin(&dl, d); + + while (!done) { + if (prompt) + putchar('>'); + fflush(stdout); + + if (!fgets(in, sizeof(input_t), stdin)) + break; + + switch (in[0]) { + case '?': + puts(help); + break; + case 'p': + prompt = 1; + break; + case 'q': + done = 1; + break; + case 'a': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } + key = dupstring(tok1); + val = dupstring(tok2); + dn = dnode_create(val); + + if (!key || !val || !dn) { + puts("out of memory"); + free((void *) key); + free(val); + if (dn) + dnode_destroy(dn); + } + + dict_load_next(&dl, dn, key); + break; + default: + putchar('?'); + putchar('\n'); + break; + } + } + + dict_load_end(&dl); +} + +int main(void) +{ + input_t in; + dict_t darray[10]; + dict_t *d = &darray[0]; + dnode_t *dn; + int i; + char *tok1, *tok2, *val; + const char *key; + + char *help = + "a add value to dictionary\n" + "d delete value from dictionary\n" + "l lookup value in dictionary\n" + "( lookup lower bound\n" + ") lookup upper bound\n" + "# switch to alternate dictionary (0-9)\n" + "j merge two dictionaries\n" + "f free the whole dictionary\n" + "k allow duplicate keys\n" + "c show number of entries\n" + "t dump whole dictionary in sort order\n" + "m make dictionary out of sorted items\n" + "p turn prompt on\n" + "s switch to non-functioning allocator\n" + "q quit"; + + for (i = 0; i < sizeof darray / sizeof *darray; i++) + dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); + + for (;;) { + if (prompt) + putchar('>'); + fflush(stdout); + + if (!fgets(in, sizeof(input_t), stdin)) + break; + + switch(in[0]) { + case '?': + puts(help); + break; + case 'a': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } + key = dupstring(tok1); + val = dupstring(tok2); + + if (!key || !val) { + puts("out of memory"); + free((void *) key); + free(val); + } + + if (!dict_alloc_insert(d, key, val)) { + puts("dict_alloc_insert failed"); + free((void *) key); + free(val); + break; + } + break; + case 'd': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + dn = dict_lookup(d, tok1); + if (!dn) { + puts("dict_lookup failed"); + break; + } + val = dnode_get(dn); + key = dnode_getkey(dn); + dict_delete_free(d, dn); + + free(val); + free((void *) key); + break; + case 'f': + dict_free(d); + break; + case 'l': + case '(': + case ')': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + dn = 0; + switch (in[0]) { + case 'l': + dn = dict_lookup(d, tok1); + break; + case '(': + dn = dict_lower_bound(d, tok1); + break; + case ')': + dn = dict_upper_bound(d, tok1); + break; + } + if (!dn) { + puts("lookup failed"); + break; + } + val = dnode_get(dn); + puts(val); + break; + case 'm': + construct(d); + break; + case 'k': + dict_allow_dupes(d); + break; + case 'c': + printf("%lu\n", (unsigned long) dict_count(d)); + break; + case 't': + for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { + printf("%s\t%s\n", (char *) dnode_getkey(dn), + (char *) dnode_get(dn)); + } + break; + case 'q': + exit(0); + break; + case '\0': + break; + case 'p': + prompt = 1; + break; + case 's': + dict_set_allocator(d, new_node, del_node, NULL); + break; + case '#': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } else { + int dictnum = atoi(tok1); + if (dictnum < 0 || dictnum > 9) { + puts("invalid number"); + break; + } + d = &darray[dictnum]; + } + break; + case 'j': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } else { + int dict1 = atoi(tok1), dict2 = atoi(tok2); + if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) { + puts("invalid number"); + break; + } + dict_merge(&darray[dict1], &darray[dict2]); + } + break; + default: + putchar('?'); + putchar('\n'); + break; + } + } + + return 0; +} + +#endif diff --git a/e2fsck/dict.h b/e2fsck/dict.h new file mode 100644 index 00000000..85c9f1de --- /dev/null +++ b/e2fsck/dict.h @@ -0,0 +1,144 @@ +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $ + * $Name: kazlib_1_20 $ + */ + +#ifndef DICT_H +#define DICT_H + +#include +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#include "sfx.h" +#endif + +/* + * Blurb for inclusion into C++ translation units + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long dictcount_t; +#define DICTCOUNT_T_MAX ULONG_MAX + +/* + * The dictionary is implemented as a red-black tree + */ + +typedef enum { dnode_red, dnode_black } dnode_color_t; + +typedef struct dnode_t { + #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + struct dnode_t *dict_left; + struct dnode_t *dict_right; + struct dnode_t *dict_parent; + dnode_color_t dict_color; + const void *dict_key; + void *dict_data; + #else + int dict_dummy; + #endif +} dnode_t; + +typedef int (*dict_comp_t)(const void *, const void *); +typedef dnode_t *(*dnode_alloc_t)(void *); +typedef void (*dnode_free_t)(dnode_t *, void *); + +typedef struct dict_t { + #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + dnode_t dict_nilnode; + dictcount_t dict_nodecount; + dictcount_t dict_maxcount; + dict_comp_t dict_compare; + dnode_alloc_t dict_allocnode; + dnode_free_t dict_freenode; + void *dict_context; + int dict_dupes; + #else + int dict_dummmy; + #endif +} dict_t; + +typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); + +typedef struct dict_load_t { + #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + dict_t *dict_dictptr; + dnode_t dict_nilnode; + #else + int dict_dummmy; + #endif +} dict_load_t; + +extern dict_t *dict_create(dictcount_t, dict_comp_t); +extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); +extern void dict_destroy(dict_t *); +extern void dict_free_nodes(dict_t *); +extern void dict_free(dict_t *); +extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); +extern void dict_init_like(dict_t *, const dict_t *); +extern int dict_verify(dict_t *); +extern int dict_similar(const dict_t *, const dict_t *); +extern dnode_t *dict_lookup(dict_t *, const void *); +extern dnode_t *dict_lower_bound(dict_t *, const void *); +extern dnode_t *dict_upper_bound(dict_t *, const void *); +extern void dict_insert(dict_t *, dnode_t *, const void *); +extern dnode_t *dict_delete(dict_t *, dnode_t *); +extern int dict_alloc_insert(dict_t *, const void *, void *); +extern void dict_delete_free(dict_t *, dnode_t *); +extern dnode_t *dict_first(dict_t *); +extern dnode_t *dict_last(dict_t *); +extern dnode_t *dict_next(dict_t *, dnode_t *); +extern dnode_t *dict_prev(dict_t *, dnode_t *); +extern dictcount_t dict_count(dict_t *); +extern int dict_isempty(dict_t *); +extern int dict_isfull(dict_t *); +extern int dict_contains(dict_t *, dnode_t *); +extern void dict_allow_dupes(dict_t *); +extern int dnode_is_in_a_dict(dnode_t *); +extern dnode_t *dnode_create(void *); +extern dnode_t *dnode_init(dnode_t *, void *); +extern void dnode_destroy(dnode_t *); +extern void *dnode_get(dnode_t *); +extern const void *dnode_getkey(dnode_t *); +extern void dnode_put(dnode_t *, void *); +extern void dict_process(dict_t *, void *, dnode_process_t); +extern void dict_load_begin(dict_load_t *, dict_t *); +extern void dict_load_next(dict_load_t *, dnode_t *, const void *); +extern void dict_load_end(dict_load_t *); +extern void dict_merge(dict_t *, dict_t *); + +#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount) +#else +#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) +#endif +#define dict_count(D) ((D)->dict_nodecount) +#define dict_isempty(D) ((D)->dict_nodecount == 0) +#define dnode_get(N) ((N)->dict_data) +#define dnode_getkey(N) ((N)->dict_key) +#define dnode_put(N, X) ((N)->dict_data = (X)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index e156433a..8549a655 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -36,48 +36,25 @@ #include "e2fsck.h" #include "problem.h" +#include "dict.h" /* Define an extension to the ext2 library's block count information */ #define BLOCK_COUNT_EXTATTR (-5) -/* - * This is structure is allocated for each time that a block is - * claimed by more than one file. So if a particular block is claimed - * by 3 files, then three copies of this structure will be allocated, - * one for each conflict. - * - * The linked list structure is as follows: - * - * dup_blk --> block #34 --> block #35 --> block #47 - * inode #12 inode #14 inode #17 - * num_bad = 3 num_bad = 2 num_bad = 2 - * | | | - * V V V - * block #34 block #35 block #47 - * inode #14 inode #15 inode #23 - * | - * V - * block #34 - * inode #15 - * - * The num_bad field indicates how many inodes are sharing a - * particular block, and is only stored in the first element of the - * linked list for a particular block. As the block conflicts are - * resolved, num_bad is decremented; when it reaches 1, then we no - * longer need to worry about that block. - */ -struct dup_block { - blk_t block; /* Block number */ - ext2_ino_t ino; /* Inode number */ - int num_bad; - int flags; - /* Pointer to next dup record with different block */ - struct dup_block *next_block; - /* Pointer to next dup record with different inode */ - struct dup_block *next_inode; +struct block_el { + blk_t block; + struct block_el *next; }; -#define FLAG_EXTATTR (1) +struct inode_el { + ext2_ino_t inode; + struct inode_el *next; +}; + +struct dup_block { + int num_bad; + struct inode_el *inode_list; +}; /* * This structure stores information about a particular inode which @@ -87,38 +64,129 @@ struct dup_block { * of multiply-claimed blocks. */ struct dup_inode { - ext2_ino_t ino, dir; + ext2_ino_t dir; int num_dupblocks; struct ext2_inode inode; - struct dup_inode *next; + struct block_el *block_list; }; static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data); -static void delete_file(e2fsck_t ctx, struct dup_inode *dp, - char *block_buf); -static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf); +static void delete_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char *block_buf); +static int clone_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char* block_buf); static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk); static void pass1b(e2fsck_t ctx, char *block_buf); static void pass1c(e2fsck_t ctx, char *block_buf); static void pass1d(e2fsck_t ctx, char *block_buf); -static struct dup_block *dup_blk = 0; -static struct dup_inode *dup_ino = 0; static int dup_inode_count = 0; +static dict_t blk_dict, ino_dict; + static ext2fs_inode_bitmap inode_dup_map; +static int dict_int_cmp(const void *a, const void *b) +{ + int ia, ib; + + ia = (int) a; + ib = (int) b; + + return (ia-ib); +} + +/* + * Add a duplicate block record + */ +static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk, + struct ext2_inode *inode) +{ + dnode_t *n; + struct dup_block *db; + struct dup_inode *di; + struct block_el *blk_el; + struct inode_el *ino_el; + + n = dict_lookup(&blk_dict, (void *) blk); + if (n) + db = (struct dup_block *) dnode_get(n); + else { + db = (struct dup_block *) e2fsck_allocate_memory(ctx, + sizeof(struct dup_block), "duplicate block header"); + db->num_bad = 0; + db->inode_list = 0; + dict_alloc_insert(&blk_dict, (void *) blk, db); + } + ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx, + sizeof(struct inode_el), "inode element"); + ino_el->inode = ino; + ino_el->next = db->inode_list; + db->inode_list = ino_el; + db->num_bad++; + + n = dict_lookup(&ino_dict, (void *) ino); + if (n) + di = (struct dup_inode *) dnode_get(n); + else { + di = (struct dup_inode *) e2fsck_allocate_memory(ctx, + sizeof(struct dup_inode), "duplicate inode header"); + di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ; + di->num_dupblocks = 0; + di->block_list = 0; + di->inode = *inode; + dict_alloc_insert(&ino_dict, (void *) ino, di); + } + blk_el = (struct block_el *) e2fsck_allocate_memory(ctx, + sizeof(struct block_el), "block element"); + blk_el->block = blk; + blk_el->next = di->block_list; + di->block_list = blk_el; + di->num_dupblocks++; +} + +/* + * Free a duplicate inode record + */ +static void inode_dnode_free(dnode_t *node, void *context) +{ + struct dup_inode *di; + struct block_el *p, *next; + + di = (struct dup_inode *) dnode_get(node); + for (p = di->block_list; p; p = next) { + next = p->next; + free(p); + } + free(node); +} + +/* + * Free a duplicate block record + */ +static void block_dnode_free(dnode_t *node, void *context) +{ + struct dup_block *db; + struct inode_el *p, *next; + + db = (struct dup_block *) dnode_get(node); + for (p = db->inode_list; p; p = next) { + next = p->next; + free(p); + } + free(node); +} + + /* * Main procedure for handling duplicate blocks */ void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; - struct dup_block *p, *q, *next_p, *next_q; - struct dup_inode *r, *next_r; struct problem_context pctx; clear_problem_context(&pctx); @@ -130,6 +198,11 @@ void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf) ctx->flags |= E2F_FLAG_ABORT; return; } + + dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp); + dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp); + dict_set_allocator(&ino_dict, NULL, inode_dnode_free, NULL); + dict_set_allocator(&blk_dict, NULL, block_dnode_free, NULL); pass1b(ctx, block_buf); pass1c(ctx, block_buf); @@ -139,28 +212,18 @@ void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf) * Time to free all of the accumulated data structures that we * don't need anymore. */ - ext2fs_free_inode_bitmap(inode_dup_map); inode_dup_map = 0; - ext2fs_free_block_bitmap(ctx->block_dup_map); ctx->block_dup_map = 0; - for (p = dup_blk; p; p = next_p) { - next_p = p->next_block; - for (q = p; q; q = next_q) { - next_q = q->next_inode; - ext2fs_free_mem((void **) &q); - } - } - for (r = dup_ino; r; r = next_r) { - next_r = r->next; - ext2fs_free_mem((void **) &r); - } + dict_free_nodes(&ino_dict); + dict_free_nodes(&blk_dict); } /* * Scan the inodes looking for inodes that contain duplicate blocks. */ struct process_block_struct { + e2fsck_t ctx; ext2_ino_t ino; int dup_blocks; - e2fsck_t ctx; + struct ext2_inode *inode; struct problem_context *pctx; }; @@ -171,10 +234,7 @@ static void pass1b(e2fsck_t ctx, char *block_buf) struct ext2_inode inode; ext2_inode_scan scan; struct process_block_struct pb; - struct dup_inode *dp; - struct dup_block *q, *r; struct problem_context pctx; - int i, ea_flag; clear_problem_context(&pctx); @@ -205,6 +265,7 @@ static void pass1b(e2fsck_t ctx, char *block_buf) pb.ino = ino; pb.dup_blocks = 0; + pb.inode = &inode; pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_pass1b_block, &pb); if (inode.i_file_acl) @@ -212,16 +273,8 @@ static void pass1b(e2fsck_t ctx, char *block_buf) BLOCK_COUNT_EXTATTR, 0, 0, &pb); if (pb.dup_blocks) { end_problem_latch(ctx, PR_LATCH_DBLOCK); - dp = (struct dup_inode *) e2fsck_allocate_memory(ctx, - sizeof(struct dup_inode), - "duplicate inode record"); - dp->ino = ino; - dp->dir = 0; - dp->inode = inode; - dp->num_dupblocks = pb.dup_blocks; - dp->next = dup_ino; - dup_ino = dp; - if (ino != EXT2_BAD_INO) + if (ino >= EXT2_FIRST_INODE(fs->super) || + ino == EXT2_ROOT_INO) dup_inode_count++; } if (pctx.errcode) @@ -238,21 +291,6 @@ static void pass1b(e2fsck_t ctx, char *block_buf) } ext2fs_close_inode_scan(scan); e2fsck_use_inode_shortcuts(ctx, 0); - /* - * Set the num_bad field - */ - for (q = dup_blk; q; q = q->next_block) { - i = 0; - ea_flag = 0; - for (r = q; r; r = r->next_inode) { - if (r->flags & FLAG_EXTATTR) { - if (ea_flag++) - continue; - } - i++; - } - q->num_bad = i; - } } static int process_pass1b_block(ext2_filsys fs, @@ -263,7 +301,6 @@ static int process_pass1b_block(ext2_filsys fs, void *priv_data) { struct process_block_struct *p; - struct dup_block *dp, *q; e2fsck_t ctx; if (HOLE_BLKADDR(*block_nr)) @@ -271,37 +308,19 @@ static int process_pass1b_block(ext2_filsys fs, p = (struct process_block_struct *) priv_data; ctx = p->ctx; - if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { - /* OK, this is a duplicate block */ - if (p->ino != EXT2_BAD_INO) { - p->pctx->blk = *block_nr; - fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx); - } - p->dup_blocks++; - ext2fs_mark_block_bitmap(ctx->block_dup_map, *block_nr); - ext2fs_mark_inode_bitmap(inode_dup_map, p->ino); - dp = (struct dup_block *) e2fsck_allocate_memory(ctx, - sizeof(struct dup_block), - "duplicate block record"); - dp->block = *block_nr; - dp->ino = p->ino; - dp->num_bad = 0; - dp->flags = (blockcnt == BLOCK_COUNT_EXTATTR) ? - FLAG_EXTATTR : 0; - q = dup_blk; - while (q) { - if (q->block == *block_nr) - break; - q = q->next_block; - } - if (q) { - dp->next_inode = q->next_inode; - q->next_inode = dp; - } else { - dp->next_block = dup_blk; - dup_blk = dp; - } + if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) + return 0; + + /* OK, this is a duplicate block */ + if (p->ino != EXT2_BAD_INO) { + p->pctx->blk = *block_nr; + fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx); } + p->dup_blocks++; + ext2fs_mark_inode_bitmap(inode_dup_map, p->ino); + + add_dupe(ctx, p->ino, *block_nr, p->inode); + return 0; } @@ -323,6 +342,7 @@ static int search_dirent_proc(ext2_ino_t dir, int entry, { struct search_dir_struct *sd; struct dup_inode *p; + dnode_t *n; sd = (struct search_dir_struct *) priv_data; @@ -330,19 +350,14 @@ static int search_dirent_proc(ext2_ino_t dir, int entry, /* Should abort this inode, but not everything */ return 0; - if (!dirent->inode || (entry < DIRENT_OTHER_FILE) || + if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) || !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode)) return 0; - for (p = dup_ino; p; p = p->next) { - if ((p->ino >= sd->first_inode) && - (p->ino == dirent->inode)) - break; - } - - if (!p || p->dir) + n = dict_lookup(&ino_dict, (void *) dirent->inode); + if (!n) return 0; - + p = (struct dup_inode *) dnode_get(n); p->dir = dir; sd->count--; @@ -353,8 +368,6 @@ static int search_dirent_proc(ext2_ino_t dir, int entry, static void pass1c(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; - struct dup_inode *p; - int inodes_left = dup_inode_count; struct search_dir_struct sd; struct problem_context pctx; @@ -362,22 +375,11 @@ static void pass1c(e2fsck_t ctx, char *block_buf) fix_problem(ctx, PR_1C_PASS_HEADER, &pctx); - /* - * First check to see if any of the inodes with dup blocks is - * a special inode. (Note that the bad block inode isn't - * counted.) - */ - for (p = dup_ino; p; p = p->next) { - if ((p->ino < EXT2_FIRST_INODE(fs->super)) && - (p->ino != EXT2_BAD_INO)) - inodes_left--; - } - /* * Search through all directories to translate inodes to names * (by searching for the containing directory for that inode.) */ - sd.count = inodes_left; + sd.count = dup_inode_count; sd.first_inode = EXT2_FIRST_INODE(fs->super); sd.max_inode = fs->super->s_inodes_count; ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf, @@ -387,48 +389,50 @@ static void pass1c(e2fsck_t ctx, char *block_buf) static void pass1d(e2fsck_t ctx, char *block_buf) { ext2_filsys fs = ctx->fs; - struct dup_inode *p, *s; - struct dup_block *q, *r; - ext2_ino_t *shared; + struct dup_inode *p, *t; + struct dup_block *q; + ext2_ino_t *shared, ino; int shared_len; int i; int file_ok; int meta_data = 0; struct problem_context pctx; - + dnode_t *n, *m; + struct block_el *s; + struct inode_el *r; + clear_problem_context(&pctx); fix_problem(ctx, PR_1D_PASS_HEADER, &pctx); e2fsck_read_bitmaps(ctx); - pctx.num = dup_inode_count; + pctx.num = dup_inode_count; /* dict_count(&ino_dict); */ fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx); shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx, - sizeof(ext2_ino_t) * dup_inode_count, + sizeof(ext2_ino_t) * dict_count(&ino_dict), "Shared inode list"); - for (p = dup_ino; p; p = p->next) { + for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) { + p = (struct dup_inode *) dnode_get(n); shared_len = 0; file_ok = 1; - if (p->ino == EXT2_BAD_INO) + ino = (ext2_ino_t) dnode_getkey(n); + if (ino == EXT2_BAD_INO) continue; /* - * Search through the duplicate records to see which - * inodes share blocks with this one + * Find all of the inodes which share blocks with this + * one. First we find all of the duplicate blocks + * belonging to this inode, and then search each block + * get the list of inodes, and merge them together. */ - for (q = dup_blk; q; q = q->next_block) { - /* - * See if this block is used by this inode. - * If it isn't, continue. - */ - for (r = q; r; r = r->next_inode) - if (r->ino == p->ino) - break; - if (!r) - continue; + for (s = p->block_list; s; s = s->next) { + m = dict_lookup(&blk_dict, (void *) s->block); + if (!m) + continue; /* Should never happen... */ + q = (struct dup_block *) dnode_get(m); if (q->num_bad > 1) file_ok = 0; - if (check_if_fs_block(ctx, q->block)) { + if (check_if_fs_block(ctx, s->block)) { file_ok = 0; meta_data = 1; } @@ -439,14 +443,14 @@ static void pass1d(e2fsck_t ctx, char *block_buf) * if an inode is already in shared[], don't * add it again. */ - for (r = q; r; r = r->next_inode) { - if (r->ino == p->ino) + for (r = q->inode_list; r; r = r->next) { + if (r->inode == ino) continue; for (i = 0; i < shared_len; i++) - if (shared[i] == r->ino) + if (shared[i] == r->inode) break; if (i == shared_len) { - shared[shared_len++] = r->ino; + shared[shared_len++] = r->inode; } } } @@ -455,7 +459,7 @@ static void pass1d(e2fsck_t ctx, char *block_buf) * Report the inode that we are working on */ pctx.inode = &p->inode; - pctx.ino = p->ino; + pctx.ino = ino; pctx.dir = p->dir; pctx.blkcount = p->num_dupblocks; pctx.num = meta_data ? shared_len+1 : shared_len; @@ -467,17 +471,16 @@ static void pass1d(e2fsck_t ctx, char *block_buf) fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx); for (i = 0; i < shared_len; i++) { - for (s = dup_ino; s; s = s->next) - if (s->ino == shared[i]) - break; - if (!s) - continue; + m = dict_lookup(&ino_dict, (void *) shared[i]); + if (!m) + continue; /* should never happen */ + t = (struct dup_inode *) dnode_get(m); /* * Report the inode that we are sharing with */ - pctx.inode = &s->inode; - pctx.ino = s->ino; - pctx.dir = s->dir; + pctx.inode = &t->inode; + pctx.ino = shared[i]; + pctx.dir = t->dir; fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx); } if (file_ok) { @@ -485,14 +488,14 @@ static void pass1d(e2fsck_t ctx, char *block_buf) continue; } if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) { - pctx.errcode = clone_file(ctx, p, block_buf); + pctx.errcode = clone_file(ctx, ino, p, block_buf); if (pctx.errcode) fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx); else continue; } if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx)) - delete_file(ctx, p, block_buf); + delete_file(ctx, ino, p, block_buf); else ext2fs_unmark_valid(fs); } @@ -503,12 +506,12 @@ static void pass1d(e2fsck_t ctx, char *block_buf) * Drop the refcount on the dup_block structure, and clear the entry * in the block_dup_map if appropriate. */ -static void decrement_badcount(e2fsck_t ctx, struct dup_block *p) +static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p) { p->num_bad--; if (p->num_bad <= 0 || - (p->num_bad == 1 && !check_if_fs_block(ctx, p->block))) - ext2fs_unmark_block_bitmap(ctx->block_dup_map, p->block); + (p->num_bad == 1 && !check_if_fs_block(ctx, block))) + ext2fs_unmark_block_bitmap(ctx->block_dup_map, block); } static int delete_file_block(ext2_filsys fs, @@ -520,6 +523,7 @@ static int delete_file_block(ext2_filsys fs, { struct process_block_struct *pb; struct dup_block *p; + dnode_t *n; e2fsck_t ctx; pb = (struct process_block_struct *) priv_data; @@ -529,11 +533,10 @@ static int delete_file_block(ext2_filsys fs, return 0; if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { - for (p = dup_blk; p; p = p->next_block) - if (p->block == *block_nr) - break; - if (p) { - decrement_badcount(ctx, p); + n = dict_lookup(&blk_dict, (void *) *block_nr); + if (n) { + p = (struct dup_block *) dnode_get(n); + decrement_badcount(ctx, *block_nr, p); } else com_err("delete_file_block", 0, _("internal error; can't find dup_blk for %d\n"), @@ -546,7 +549,8 @@ static int delete_file_block(ext2_filsys fs, return 0; } -static void delete_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) +static void delete_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char* block_buf) { ext2_filsys fs = ctx->fs; struct process_block_struct pb; @@ -554,29 +558,29 @@ static void delete_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) struct problem_context pctx; clear_problem_context(&pctx); - pctx.ino = pb.ino = dp->ino; + pctx.ino = pb.ino = ino; pb.dup_blocks = dp->num_dupblocks; pb.ctx = ctx; pctx.str = "delete_file"; - pctx.errcode = ext2fs_block_iterate2(fs, dp->ino, 0, block_buf, + pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, delete_file_block, &pb); if (pctx.errcode) fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); - ext2fs_unmark_inode_bitmap(ctx->inode_used_map, dp->ino); - ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, dp->ino); + ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); if (ctx->inode_bad_map) - ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, dp->ino); - ext2fs_unmark_inode_bitmap(fs->inode_map, dp->ino); + ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); + ext2fs_unmark_inode_bitmap(fs->inode_map, ino); ext2fs_mark_ib_dirty(fs); ext2fs_mark_bb_dirty(fs); - e2fsck_read_inode(ctx, dp->ino, &inode, "delete_file"); + e2fsck_read_inode(ctx, ino, &inode, "delete_file"); inode.i_links_count = 0; inode.i_dtime = time(0); if (inode.i_file_acl) delete_file_block(fs, &inode.i_file_acl, BLOCK_COUNT_EXTATTR, 0, 0, &pb); - e2fsck_write_inode(ctx, dp->ino, &inode, "delete_file"); + e2fsck_write_inode(ctx, ino, &inode, "delete_file"); } struct clone_struct { @@ -597,6 +601,7 @@ static int clone_file_block(ext2_filsys fs, blk_t new_block; errcode_t retval; struct clone_struct *cs = (struct clone_struct *) priv_data; + dnode_t *n; e2fsck_t ctx; ctx = cs->ctx; @@ -605,10 +610,9 @@ static int clone_file_block(ext2_filsys fs, return 0; if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { - for (p = dup_blk; p; p = p->next_block) - if (p->block == *block_nr) - break; - if (p) { + n = dict_lookup(&blk_dict, (void *) *block_nr); + if (n) { + p = (struct dup_block *) dnode_get(n); retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &new_block); if (retval) { @@ -639,7 +643,7 @@ static int clone_file_block(ext2_filsys fs, cs->errcode = retval; return BLOCK_ABORT; } - decrement_badcount(ctx, p); + decrement_badcount(ctx, *block_nr, p); *block_nr = new_block; ext2fs_mark_block_bitmap(ctx->block_found_map, new_block); @@ -653,13 +657,18 @@ static int clone_file_block(ext2_filsys fs, return 0; } -static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) +static int clone_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char* block_buf) { ext2_filsys fs = ctx->fs; errcode_t retval; struct clone_struct cs; struct problem_context pctx; blk_t blk; + dnode_t *n; + struct inode_el *ino_el; + struct dup_block *db; + struct dup_inode *di; clear_problem_context(&pctx); cs.errcode = 0; @@ -669,12 +678,12 @@ static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) if (retval) return retval; - if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dp->ino)) - cs.dir = dp->ino; + if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) + cs.dir = ino; - pctx.ino = dp->ino; + pctx.ino = ino; pctx.str = "clone_file"; - pctx.errcode = ext2fs_block_iterate2(fs, dp->ino, 0, block_buf, + pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, clone_file_block, &cs); ext2fs_mark_bb_dirty(fs); if (pctx.errcode) { @@ -692,31 +701,25 @@ static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) if (blk && (clone_file_block(fs, &dp->inode.i_file_acl, BLOCK_COUNT_EXTATTR, 0, 0, &cs) == BLOCK_CHANGED)) { - struct dup_block *p, *q; - struct dup_inode *r; - + e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA"); /* * If we cloned the EA block, find all other inodes * which refered to that EA block, and modify * them to point to the new EA block. */ - for (p = dup_blk; p; p = p->next_block) { - if (p->block == blk) - break; - } - for (q = p; q ; q = q->next_inode) { - if (!(q->flags & FLAG_EXTATTR)) + n = dict_lookup(&blk_dict, (void *) blk); + db = (struct dup_block *) dnode_get(n); + for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) { + if (ino_el->inode == ino) continue; - for (r = dup_ino; r; r = r->next) - if (r->ino == q->ino) - break; - if (r) { - r->inode.i_file_acl = dp->inode.i_file_acl; - e2fsck_write_inode(ctx, q->ino, &r->inode, - "clone file EA"); + n = dict_lookup(&ino_dict, (void *) ino_el->inode); + di = (struct dup_inode *) dnode_get(n); + if (di->inode.i_file_acl == blk) { + di->inode.i_file_acl = dp->inode.i_file_acl; + e2fsck_write_inode(ctx, ino_el->inode, + &dp->inode, "clone file EA"); + decrement_badcount(ctx, blk, db); } - q->ino = 0; /* Should free the structure... */ - decrement_badcount(ctx, p); } } retval = 0; diff --git a/tests/ChangeLog b/tests/ChangeLog index 7d359407..98511519 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2002-08-01 Theodore Ts'o + + * f_dup, f_dup2, f_dup3, f_bbfile, f_dupfsblks: Update expect + scripts to deal with ordering changes caused by use of a + red-block tree in pass1b. + 2002-07-19 Theodore Ts'o * f_expand, f_h_badnode, f_h_badroot: Modify the expect scripts to diff --git a/tests/f_bbfile/expect.1 b/tests/f_bbfile/expect.1 index 6ffed264..a8b3ee7b 100644 --- a/tests/f_bbfile/expect.1 +++ b/tests/f_bbfile/expect.1 @@ -13,8 +13,8 @@ Pass 1C: Scan directories for inodes with dup blocks. Pass 1D: Reconciling duplicate blocks (There are 3 inodes containing duplicate/bad blocks.) -File /termcap (inode #12, mod time Sun Jan 2 08:29:13 1994) - has 2 duplicate block(s), shared with 1 file(s): +File / (inode #2, mod time Sun Jan 2 08:29:13 1994) + has 1 duplicate block(s), shared with 1 file(s): (inode #1, mod time Sun Jul 17 00:47:58 1994) Clone duplicate/bad blocks? yes @@ -23,8 +23,8 @@ File /lost+found (inode #11, mod time Sun Jan 2 08:28:40 1994) (inode #1, mod time Sun Jul 17 00:47:58 1994) Clone duplicate/bad blocks? yes -File / (inode #2, mod time Sun Jan 2 08:29:13 1994) - has 1 duplicate block(s), shared with 1 file(s): +File /termcap (inode #12, mod time Sun Jan 2 08:29:13 1994) + has 2 duplicate block(s), shared with 1 file(s): (inode #1, mod time Sun Jul 17 00:47:58 1994) Clone duplicate/bad blocks? yes diff --git a/tests/f_dup/expect.1 b/tests/f_dup/expect.1 index 8375b4ed..a6745c41 100644 --- a/tests/f_dup/expect.1 +++ b/tests/f_dup/expect.1 @@ -9,14 +9,14 @@ Pass 1C: Scan directories for inodes with dup blocks. Pass 1D: Reconciling duplicate blocks (There are 2 inodes containing duplicate/bad blocks.) -File /motd (inode #13, mod time Tue Sep 21 03:19:20 1993) - has 2 duplicate block(s), shared with 1 file(s): - /termcap (inode #12, mod time Tue Sep 21 03:19:14 1993) -Clone duplicate/bad blocks? yes - File /termcap (inode #12, mod time Tue Sep 21 03:19:14 1993) has 2 duplicate block(s), shared with 1 file(s): /motd (inode #13, mod time Tue Sep 21 03:19:20 1993) +Clone duplicate/bad blocks? yes + +File /motd (inode #13, mod time Tue Sep 21 03:19:20 1993) + has 2 duplicate block(s), shared with 1 file(s): + /termcap (inode #12, mod time Tue Sep 21 03:19:14 1993) Duplicated blocks already reassigned or cloned. Pass 2: Checking directory structure diff --git a/tests/f_dup/expect.2 b/tests/f_dup/expect.2 index d1530c2c..1aeb159b 100644 --- a/tests/f_dup/expect.2 +++ b/tests/f_dup/expect.2 @@ -3,5 +3,5 @@ Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information -test_filesys: 13/16 files (7.7% non-contiguous), 40/100 blocks +test_filesys: 13/16 files (15.4% non-contiguous), 40/100 blocks Exit status is 0 diff --git a/tests/f_dup2/expect.1 b/tests/f_dup2/expect.1 index ff5d30ac..28b74042 100644 --- a/tests/f_dup2/expect.1 +++ b/tests/f_dup2/expect.1 @@ -10,7 +10,7 @@ Pass 1C: Scan directories for inodes with dup blocks. Pass 1D: Reconciling duplicate blocks (There are 3 inodes containing duplicate/bad blocks.) -File /pass1.c (inode #14, mod time Tue Sep 21 04:28:37 1993) +File /termcap (inode #12, mod time Tue Sep 21 03:19:14 1993) has 2 duplicate block(s), shared with 1 file(s): /motd (inode #13, mod time Tue Sep 21 03:19:20 1993) Clone duplicate/bad blocks? yes @@ -21,7 +21,7 @@ File /motd (inode #13, mod time Tue Sep 21 03:19:20 1993) /termcap (inode #12, mod time Tue Sep 21 03:19:14 1993) Clone duplicate/bad blocks? yes -File /termcap (inode #12, mod time Tue Sep 21 03:19:14 1993) +File /pass1.c (inode #14, mod time Tue Sep 21 04:28:37 1993) has 2 duplicate block(s), shared with 1 file(s): /motd (inode #13, mod time Tue Sep 21 03:19:20 1993) Duplicated blocks already reassigned or cloned. diff --git a/tests/f_dup3/expect.1 b/tests/f_dup3/expect.1 index 88f30086..ff1109a2 100644 --- a/tests/f_dup3/expect.1 +++ b/tests/f_dup3/expect.1 @@ -10,7 +10,7 @@ Pass 1C: Scan directories for inodes with dup blocks. Pass 1D: Reconciling duplicate blocks (There are 3 inodes containing duplicate/bad blocks.) -File /e2fsck (inode #16, mod time Tue Sep 21 04:32:22 1993) +File /lost+found (inode #11, mod time Mon Sep 20 03:26:36 1993) has 2 duplicate block(s), shared with 0 file(s): Clone duplicate/bad blocks? yes @@ -18,7 +18,7 @@ File /pass1.c (inode #14, mod time Tue Sep 21 04:28:37 1993) has 2 duplicate block(s), shared with 0 file(s): Clone duplicate/bad blocks? yes -File /lost+found (inode #11, mod time Mon Sep 20 03:26:36 1993) +File /e2fsck (inode #16, mod time Tue Sep 21 04:32:22 1993) has 2 duplicate block(s), shared with 0 file(s): Clone duplicate/bad blocks? yes diff --git a/tests/f_dupfsblks/expect.1 b/tests/f_dupfsblks/expect.1 index caa090d0..0f49ad3b 100644 --- a/tests/f_dupfsblks/expect.1 +++ b/tests/f_dupfsblks/expect.1 @@ -14,8 +14,8 @@ Pass 1C: Scan directories for inodes with dup blocks. Pass 1D: Reconciling duplicate blocks (There are 3 inodes containing duplicate/bad blocks.) -File /quux (inode #14, mod time Thu Aug 5 07:18:09 1999) - has 1 duplicate block(s), shared with 2 file(s): +File /foo (inode #12, mod time Thu Apr 28 17:57:53 1994) + has 4 duplicate block(s), shared with 2 file(s): /bar (inode #13, mod time Thu Aug 5 07:17:17 1999) Clone duplicate/bad blocks? yes @@ -23,12 +23,12 @@ Clone duplicate/bad blocks? yes File /bar (inode #13, mod time Thu Aug 5 07:17:17 1999) has 2 duplicate block(s), shared with 3 file(s): - /quux (inode #14, mod time Thu Aug 5 07:18:09 1999) /foo (inode #12, mod time Thu Apr 28 17:57:53 1994) + /quux (inode #14, mod time Thu Aug 5 07:18:09 1999) Clone duplicate/bad blocks? yes -File /foo (inode #12, mod time Thu Apr 28 17:57:53 1994) - has 4 duplicate block(s), shared with 2 file(s): +File /quux (inode #14, mod time Thu Aug 5 07:18:09 1999) + has 1 duplicate block(s), shared with 2 file(s): /bar (inode #13, mod time Thu Aug 5 07:17:17 1999) Clone duplicate/bad blocks? yes diff --git a/tests/f_dupfsblks/expect.2 b/tests/f_dupfsblks/expect.2 index 3717fec1..c1726b6e 100644 --- a/tests/f_dupfsblks/expect.2 +++ b/tests/f_dupfsblks/expect.2 @@ -3,5 +3,5 @@ Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information -test_filesys: 14/32 files (7.1% non-contiguous), 30/100 blocks +test_filesys: 14/32 files (0.0% non-contiguous), 30/100 blocks Exit status is 0