diff options
author | Theodore Ts'o <tytso@mit.edu> | 2002-07-25 00:00:08 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2002-07-25 00:00:08 -0400 |
commit | 850d05e9aa405497e57c72090b9561af98b1b661 (patch) | |
tree | bc4e95c29fd6caa5d6683539af3e2ff4dcfffee2 /e2fsck/rehash.c | |
parent | 542867fb705f0c5705365e470f3e3dd097b5bded (diff) | |
download | e2fsprogs-850d05e9aa405497e57c72090b9561af98b1b661.tar.gz |
Add e2fsck -D option which optimizes/compresses directories.
General cleanup of rehash code. (Fixed a few bugs, reformatted
code to make it more maintainable, etc.)
Diffstat (limited to 'e2fsck/rehash.c')
-rw-r--r-- | e2fsck/rehash.c | 453 |
1 files changed, 284 insertions, 169 deletions
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 95bb6785..2c64ec4a 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -57,6 +57,7 @@ struct fill_dir_struct { struct hash_entry *harray; int max_array, num_array; int dir_size; + int compress; ino_t parent; }; @@ -115,14 +116,16 @@ static int fill_dir_block(ext2_filsys fs, fd->err = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } + dir_offset += dirent->rec_len; if (dirent->inode == 0) - goto next; - if (((dirent->name_len&0xFF) == 1) && (dirent->name[0] == '.')) - goto next; - if (((dirent->name_len&0xFF) == 2) && + continue; + if (!fd->compress && ((dirent->name_len&0xFF) == 1) && + (dirent->name[0] == '.')) + continue; + if (!fd->compress && ((dirent->name_len&0xFF) == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { fd->parent = dirent->inode; - goto next; + continue; } if (fd->num_array >= fd->max_array) { new_array = realloc(fd->harray, @@ -134,19 +137,20 @@ static int fill_dir_block(ext2_filsys fs, fd->harray = new_array; fd->max_array += 500; } - ent = fd->harray + fd->num_array; + ent = fd->harray + fd->num_array++; ent->dir = dirent; - fd->err = ext2fs_dirhash(fs->super->s_def_hash_version, - dirent->name, - dirent->name_len & 0xFF, - fs->super->s_hash_seed, - &ent->hash, &ent->minor_hash); - if (fd->err) - return BLOCK_ABORT; - fd->num_array++; fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); - next: - dir_offset += dirent->rec_len; + if (fd->compress) + ent->hash = ent->minor_hash = 0; + else { + fd->err = ext2fs_dirhash(fs->super->s_def_hash_version, + dirent->name, + dirent->name_len & 0xFF, + fs->super->s_hash_seed, + &ent->hash, &ent->minor_hash); + if (fd->err) + return BLOCK_ABORT; + } } return 0; @@ -174,6 +178,30 @@ static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b) return ret; } +/* Used for sorting the hash entry */ +static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b) +{ + const struct hash_entry *he_a = (const struct hash_entry *) a; + const struct hash_entry *he_b = (const struct hash_entry *) b; + int ret; + int min_len; + + min_len = he_a->dir->name_len; + if (min_len > he_b->dir->name_len) + min_len = he_b->dir->name_len; + + ret = strncmp(he_a->dir->name, he_b->dir->name, min_len); + if (ret == 0) { + if (he_a->dir->name_len > he_b->dir->name_len) + ret = 1; + else if (he_a->dir->name_len < he_b->dir->name_len) + ret = -1; + else + ret = 0; + } + return ret; +} + static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir, int blocks) { @@ -206,7 +234,7 @@ static void free_out_dir(struct out_dir *outdir) outdir->num =0; } -errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, +static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, char ** ret) { errcode_t retval; @@ -217,17 +245,87 @@ errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, return retval; } *ret = outdir->buf + (outdir->num++ * fs->blocksize); + memset(*ret, 0, fs->blocksize); return 0; } -struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, +static errcode_t copy_dir_entries(ext2_filsys fs, + struct fill_dir_struct *fd, + struct out_dir *outdir) +{ + errcode_t retval; + char *block_start; + struct hash_entry *ent; + struct ext2_dir_entry *dirent; + int i, rec_len, left; + ext2_dirhash_t prev_hash; + int offset; + + outdir->max = 0; + retval = alloc_size_dir(fs, outdir, + (fd->dir_size / fs->blocksize) + 2); + if (retval) + return retval; + outdir->num = fd->compress ? 0 : 1; + offset = 0; + outdir->hashes[0] = 0; + prev_hash = 1; + if ((retval = get_next_block(fs, outdir, &block_start))) + return retval; + dirent = (struct ext2_dir_entry *) block_start; + left = fs->blocksize; + for (i=0; i < fd->num_array; i++) { + ent = fd->harray + i; + rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF); + dirent = (struct ext2_dir_entry *) (block_start + offset); + left = fs->blocksize - offset; + if (rec_len > left) { + if (left) { + dirent->rec_len = left; + dirent->inode = 0; + dirent->name_len = 0; + offset += left; + left = 0; + } + if ((retval = get_next_block(fs, outdir, + &block_start))) + return retval; + offset = 0; left = fs->blocksize; + dirent = (struct ext2_dir_entry *) block_start; + } + if (offset == 0) { + if (ent->hash == prev_hash) + outdir->hashes[outdir->num-1] = ent->hash | 1; + else + outdir->hashes[outdir->num-1] = ent->hash; + } + dirent->inode = ent->dir->inode; + dirent->name_len = ent->dir->name_len; + dirent->rec_len = rec_len; + memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF); + offset += rec_len; + left -= rec_len; + if (left < 12) { + dirent->rec_len += left; + offset += left; + } + prev_hash = ent->hash; + } + if (left) + dirent->rec_len += left; + + return 0; +} + + +static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, ext2_ino_t ino, ext2_ino_t parent) { struct ext2_dir_entry *dir; struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limits; - int filetype; + int filetype = 0; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) filetype = EXT2_FT_DIR << 8; @@ -260,7 +358,7 @@ struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, } -struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) +static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) { struct ext2_dir_entry *dir; struct ext2_dx_countlimit *limits; @@ -277,6 +375,84 @@ struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) return (struct ext2_dx_entry *) limits; } +/* + * This function takes the leaf nodes which have been written in + * outdir, and populates the root node and any necessary interior nodes. + */ +static errcode_t calculate_tree(ext2_filsys fs, + struct out_dir *outdir, + ext2_ino_t ino, + ext2_ino_t parent) +{ + struct ext2_dx_root_info *root_info; + struct ext2_dx_entry *root, *dx_ent = 0; + struct ext2_dx_countlimit *root_limit, *limit; + errcode_t retval; + char * block_start; + int i, c1, c2, nblks; + int limit_offset, root_offset; + + root_info = set_root_node(fs, outdir->buf, ino, parent); + root_offset = limit_offset = ((char *) root_info - outdir->buf) + + root_info->info_length; + root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); + c1 = root_limit->limit; + nblks = outdir->num; + + /* Write out the pointer blocks */ + if (nblks-1 <= c1) { + /* Just write out the root block, and we're done */ + root = (struct ext2_dx_entry *) (outdir->buf + root_offset); + for (i=1; i < nblks; i++) { + root->block = ext2fs_cpu_to_le32(i); + if (i != 1) + root->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + root++; + c1--; + } + } else { + c2 = 0; + limit = 0; + root_info->indirect_levels = 1; + for (i=1; i < nblks; i++) { + if (c1 == 0) + return ENOSPC; + if (c2 == 0) { + if (limit) + limit->limit = limit->count = + ext2fs_cpu_to_le16(limit->limit); + root = (struct ext2_dx_entry *) + (outdir->buf + root_offset); + root->block = ext2fs_cpu_to_le32(outdir->num); + if (i != 1) + root->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + if ((retval = get_next_block(fs, outdir, + &block_start))) + return retval; + dx_ent = set_int_node(fs, block_start); + limit = (struct ext2_dx_countlimit *) dx_ent; + c2 = limit->limit; + root_offset += sizeof(struct ext2_dx_entry); + c1--; + } + dx_ent->block = ext2fs_cpu_to_le32(i); + if (c2 != limit->limit) + dx_ent->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + dx_ent++; + c2--; + } + limit->count = ext2fs_cpu_to_le16(limit->limit - c2); + limit->limit = ext2fs_cpu_to_le16(limit->limit); + } + root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); + root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1); + root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit); + + return 0; +} struct write_dir_struct { struct out_dir *outdir; @@ -286,8 +462,7 @@ struct write_dir_struct { }; /* - * This makes sure we have enough space to write out the modified - * directory. + * Helper function which writes out a directory block. */ static int write_dir_block(ext2_filsys fs, blk_t *block_nr, @@ -297,9 +472,8 @@ static int write_dir_block(ext2_filsys fs, void *priv_data) { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; - blk_t new_blk, blk; + blk_t blk; char *dir; - errcode_t retval; if (*block_nr == 0) return 0; @@ -322,9 +496,9 @@ static int write_dir_block(ext2_filsys fs, return 0; } - static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, - struct out_dir *outdir, ext2_ino_t ino) + struct out_dir *outdir, + ext2_ino_t ino, int compress) { struct write_dir_struct wd; errcode_t retval; @@ -347,7 +521,8 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, return wd.err; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); - inode.i_flags |= EXT2_INDEX_FL; + if (!compress) + inode.i_flags |= EXT2_INDEX_FL; inode.i_size = outdir->num * fs->blocksize; inode.i_blocks -= (fs->blocksize / 512) * wd.cleared; e2fsck_write_inode(ctx, ino, &inode, "rehash_dir"); @@ -360,22 +535,11 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) ext2_filsys fs = ctx->fs; errcode_t retval; struct ext2_inode inode; - char *dir_buf = 0, *block_start; - struct hash_entry *ent; - struct ext2_dir_entry *dirent; + char *dir_buf = 0; struct fill_dir_struct fd; - int i, rec_len, left, c1, c2, nblks; - ext2_dirhash_t prev_hash; - int offset; - void *new_mem; struct out_dir outdir; - struct ext2_dx_root_info *root_info; - struct ext2_dx_entry *root, *dx_ent; - struct ext2_dx_countlimit *root_limit, *limit; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); - if ((inode.i_size / fs->blocksize) < 3) - return 0; retval = ENOMEM; fd.harray = 0; @@ -394,6 +558,10 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) fd.inode = &inode; fd.err = 0; fd.dir_size = 0; + fd.compress = 0; + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || + (inode.i_size / fs->blocksize) < 3) + fd.compress = 1; fd.parent = 0; /* Read in the entire directory into memory */ @@ -409,170 +577,117 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) fd.num_array, fd.dir_size, ino); #endif - /* Sort the list into hash order */ - qsort(fd.harray, fd.num_array, sizeof(struct hash_entry), hash_cmp); - - outdir.max = 0; - retval = alloc_size_dir(fs, &outdir, - (fd.dir_size / fs->blocksize) + 2); + /* Sort the list */ + if (fd.compress) + qsort(fd.harray+2, fd.num_array-2, + sizeof(struct hash_entry), name_cmp); + else + qsort(fd.harray, fd.num_array, + sizeof(struct hash_entry), hash_cmp); + + /* + * Copy the directory entries. In a htree directory these + * will become the leaf nodes. + */ + retval = copy_dir_entries(fs, &fd, &outdir); if (retval) goto errout; - outdir.num = 1; offset = 0; - outdir.hashes[0] = 0; - prev_hash = 1; - if ((retval = get_next_block(fs, &outdir, &block_start))) - goto errout; - for (i=0; i < fd.num_array; i++) { - ent = fd.harray + i; - rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF); - dirent = (struct ext2_dir_entry *) (block_start + offset); - left = fs->blocksize - offset; - if (rec_len > left) { - if (left) { - dirent->rec_len = left; - dirent->inode = 0; - dirent->name_len = 0; - offset += left; - left = 0; - } - if ((retval = get_next_block(fs, &outdir, - &block_start))) - goto errout; - offset = 0; left = fs->blocksize; - dirent = (struct ext2_dir_entry *) block_start; - } - if (offset == 0) { - if (ent->hash == prev_hash) - outdir.hashes[outdir.num-1] = ent->hash | 1; - else - outdir.hashes[outdir.num-1] = ent->hash; - } - dirent->inode = ent->dir->inode; - dirent->name_len = ent->dir->name_len; - dirent->rec_len = rec_len; - memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF); - offset += rec_len; - left -= rec_len; - if (left < 12) { - dirent->rec_len += left; - offset += left; - } - prev_hash = ent->hash; - } - if (left) - dirent->rec_len += left; + free(dir_buf); dir_buf = 0; - root_info = set_root_node(fs, outdir.buf, ino, fd.parent); - root_limit = (struct ext2_dx_countlimit *) - ((char *)root_info + root_info->info_length); - root = (struct ext2_dx_entry *) root_limit; - c1 = root_limit->limit; - nblks = outdir.num; - - /* Write out the pointer blocks */ - if (nblks-1 <= c1) { - /* Just write out the root block, and we're done */ - for (i=1; i < nblks; i++) { - root->block = ext2fs_cpu_to_le32(i); - if (i != 1) - root->hash = - ext2fs_cpu_to_le32(outdir.hashes[i]); - root++; - c1--; - } - } else { - c2 = 0; - limit = 0; - root_info->indirect_levels = 1; - for (i=1; i < nblks; i++) { - if (c1 == 0) { - retval = ENOSPC; - goto errout; - } - if (c2 == 0) { - if (limit) - limit->limit = limit->count = - ext2fs_cpu_to_le16(limit->limit); - root->block = ext2fs_cpu_to_le32(outdir.num); - if (i != 1) - root->hash = - ext2fs_cpu_to_le32(outdir.hashes[i]); - if ((retval = get_next_block(fs, &outdir, - &block_start))) - goto errout; - dx_ent = set_int_node(fs, block_start); - limit = (struct ext2_dx_countlimit *) dx_ent; - c2 = limit->limit; - root++; - c1--; - } - dx_ent->block = ext2fs_cpu_to_le32(i); - if (c2 != limit->limit) - dx_ent->hash = - ext2fs_cpu_to_le32(outdir.hashes[i]); - dx_ent++; - c2--; - } - limit->count = ext2fs_cpu_to_le16(limit->limit - c2); - limit->limit = ext2fs_cpu_to_le16(limit->limit); + if (!fd.compress) { + /* Calculate the interior nodes */ + retval = calculate_tree(fs, &outdir, ino, fd.parent); + if (retval) + goto errout; } - root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1); - root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit); - - retval = write_directory(ctx, fs, &outdir, ino); + + retval = write_directory(ctx, fs, &outdir, ino, fd.compress); if (retval) goto errout; errout: if (dir_buf) free(dir_buf); + if (fd.harray) + free(fd.harray); + free_out_dir(&outdir); return retval; } void e2fsck_rehash_directories(e2fsck_t ctx) { - errcode_t retval; struct problem_context pctx; - ext2_u32_iterate iter; - ext2_ino_t ino,lpf; - static const char name[] = "lost+found"; - int first = 1; +#ifdef RESOURCE_TRACK + struct resource_track rtrack; +#endif + struct dir_info *dir; + ext2_u32_iterate iter; + ext2_ino_t ino; + errcode_t retval; + int i, all_dirs, dir_index, first = 1; + +#ifdef RESOURCE_TRACK + init_resource_track(&rtrack); +#endif - if (!ctx->dirs_to_hash) + all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS; + + if (!ctx->dirs_to_hash && !all_dirs) return; - retval = ext2fs_lookup(ctx->fs, EXT2_ROOT_INO, name, - sizeof(name)-1, 0, &lpf); - if (retval) - lpf = 0; + e2fsck_get_lost_and_found(ctx, 0); clear_problem_context(&pctx); - retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash, &iter); - if (retval) { - pctx.errcode = retval; - fix_problem(ctx, PR_3A_REHASH_ITER, &pctx); - return; + + dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX; + if (all_dirs) + i = 0; + else { + retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash, + &iter); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx); + return; + } } - while (ext2fs_u32_list_iterate(iter, &ino)) { - if (ino == lpf) + while (1) { + if (all_dirs) { + if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0) + break; + ino = dir->ino; + } else { + if (!ext2fs_u32_list_iterate(iter, &ino)) + break; + } + if (ino == ctx->lost_and_found) continue; pctx.dir = ino; if (first) { fix_problem(ctx, PR_3A_PASS_HEADER, &pctx); first = 0; } - fix_problem(ctx, PR_3A_REHASH_DIR, &pctx); + fix_problem(ctx, PR_3A_OPTIMIZE_DIR, &pctx); pctx.errcode = e2fsck_rehash_dir(ctx, ino); if (pctx.errcode) { - end_problem_latch(ctx, PR_LATCH_REHASH_DIR); - fix_problem(ctx, PR_3A_REHASH_DIR_ERR, &pctx); + end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR); + fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx); } } - end_problem_latch(ctx, PR_LATCH_REHASH_DIR); - ext2fs_u32_list_iterate_end(iter); + end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR); + if (!all_dirs) + ext2fs_u32_list_iterate_end(iter); - ext2fs_u32_list_free(ctx->dirs_to_hash); + if (ctx->dirs_to_hash) + ext2fs_u32_list_free(ctx->dirs_to_hash); ctx->dirs_to_hash = 0; + +#ifdef RESOURCE_TRACK + if (ctx->options & E2F_OPT_TIME2) { + e2fsck_clear_progbar(ctx); + print_resource_track("Pass 3A", &rtrack); + } +#endif } |