X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=e2fsck%2Frehash.c;h=30e510a6c07e8fb9845520a9c448697ee1e69f72;hb=3ba3ec0b334532c947f240927febe9e4e2351975;hp=15d3dd937ae006ef9ab0515d15409b8096f80829;hpb=86f3b6cf98a72c6dad0738e3af2512ddcbd49be9;p=tools%2Fe2fsprogs.git diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 15d3dd9..30e510a 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -71,6 +71,8 @@ int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino) return ext2fs_u32_list_test(ctx->dirs_to_hash, ino); } +#undef REHASH_DEBUG + struct fill_dir_struct { char *buf; struct ext2_inode *inode; @@ -78,10 +80,10 @@ struct fill_dir_struct { errcode_t err; e2fsck_t ctx; struct hash_entry *harray; - int max_array, num_array; - unsigned int dir_size; + blk_t max_array, num_array; + ext2_off64_t dir_size; int compress; - ino_t parent; + ext2_ino_t parent; ext2_ino_t dir; }; @@ -93,8 +95,8 @@ struct hash_entry { }; struct out_dir { - int num; - int max; + blk_t num; + blk_t max; char *buf; ext2_dirhash_t *hashes; }; @@ -107,11 +109,11 @@ static int fill_dir_block(ext2_filsys fs, void *priv_data) { struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data; - struct hash_entry *new_array, *ent; + struct hash_entry *ent; struct ext2_dir_entry *dirent; char *dir; unsigned int offset, dir_offset, rec_len, name_len; - int hash_alg; + int hash_alg, hash_flags; if (blockcnt < 0) return 0; @@ -137,6 +139,7 @@ static int fill_dir_block(ext2_filsys fs, if (fd->err) return BLOCK_ABORT; } + hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL; hash_alg = fs->super->s_def_hash_version; if ((hash_alg <= EXT2_HASH_TEA) && (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) @@ -157,6 +160,10 @@ static int fill_dir_block(ext2_filsys fs, dir_offset += rec_len; if (dirent->inode == 0) continue; + if ((name_len) == 0) { + fd->err = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } if (!fd->compress && (name_len == 1) && (dirent->name[0] == '.')) continue; @@ -166,13 +173,16 @@ static int fill_dir_block(ext2_filsys fs, continue; } if (fd->num_array >= fd->max_array) { - new_array = realloc(fd->harray, - sizeof(struct hash_entry) * (fd->max_array+500)); - if (!new_array) { - fd->err = ENOMEM; + errcode_t retval; + + retval = ext2fs_resize_array(sizeof(struct hash_entry), + fd->max_array, + fd->max_array + 500, + &fd->harray); + if (retval) { + fd->err = retval; return BLOCK_ABORT; } - fd->harray = new_array; fd->max_array += 500; } ent = fd->harray + fd->num_array++; @@ -182,10 +192,11 @@ static int fill_dir_block(ext2_filsys fs, if (fd->compress) ent->hash = ent->minor_hash = 0; else { - fd->err = ext2fs_dirhash(hash_alg, dirent->name, - name_len, - fs->super->s_hash_seed, - &ent->hash, &ent->minor_hash); + fd->err = ext2fs_dirhash2(hash_alg, + dirent->name, name_len, + fs->encoding, hash_flags, + fs->super->s_hash_seed, + &ent->hash, &ent->minor_hash); if (fd->err) return BLOCK_ABORT; } @@ -208,9 +219,8 @@ 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; - unsigned int he_a_len, he_b_len; + unsigned int he_a_len, he_b_len, min_len; int ret; - int min_len; he_a_len = ext2fs_dirent_name_len(he_a->dir); he_b_len = ext2fs_dirent_name_len(he_b->dir); @@ -218,7 +228,7 @@ static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b) if (min_len > he_b_len) min_len = he_b_len; - ret = strncmp(he_a->dir->name, he_b->dir->name, min_len); + ret = memcmp(he_a->dir->name, he_b->dir->name, min_len); if (ret == 0) { if (he_a_len > he_b_len) ret = 1; @@ -253,23 +263,28 @@ static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b) } static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir, - int blocks) + blk_t blocks) { - void *new_mem; + errcode_t retval; if (outdir->max) { - new_mem = realloc(outdir->buf, blocks * fs->blocksize); - if (!new_mem) - return ENOMEM; - outdir->buf = new_mem; - new_mem = realloc(outdir->hashes, - blocks * sizeof(ext2_dirhash_t)); - if (!new_mem) - return ENOMEM; - outdir->hashes = new_mem; + retval = ext2fs_resize_array(fs->blocksize, outdir->max, blocks, + &outdir->buf); + if (retval) + return retval; + retval = ext2fs_resize_array(sizeof(ext2_dirhash_t), + outdir->max, blocks, + &outdir->hashes); + if (retval) + return retval; } else { - outdir->buf = malloc(blocks * fs->blocksize); - outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t)); + retval = ext2fs_get_array(fs->blocksize, blocks, &outdir->buf); + if (retval) + return retval; + retval = ext2fs_get_array(sizeof(ext2_dirhash_t), blocks, + &outdir->hashes); + if (retval) + return retval; outdir->num = 0; } outdir->max = blocks; @@ -290,11 +305,15 @@ static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, errcode_t retval; if (outdir->num >= outdir->max) { - retval = alloc_size_dir(fs, outdir, outdir->max + 50); + int increment = outdir->max / 10; + + if (increment < 50) + increment = 50; + retval = alloc_size_dir(fs, outdir, outdir->max + increment); if (retval) return retval; } - *ret = outdir->buf + (outdir->num++ * fs->blocksize); + *ret = outdir->buf + (size_t)outdir->num++ * fs->blocksize; memset(*ret, 0, fs->blocksize); return 0; } @@ -307,7 +326,7 @@ static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, */ static void mutate_name(char *str, unsigned int *len) { - int i; + int i; unsigned int l = *len; /* @@ -318,7 +337,7 @@ static void mutate_name(char *str, unsigned int *len) if (!isdigit(str[i])) break; } - if ((i == l-1) || (str[i] != '~')) { + if ((i == (int)l - 1) || (str[i] != '~')) { if (((l-1) & 3) < 2) l += 2; else @@ -364,12 +383,13 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, struct fill_dir_struct *fd) { struct problem_context pctx; - struct hash_entry *ent, *prev; - int i, j; + struct hash_entry *ent, *prev; + blk_t i, j; int fixed = 0; char new_name[256]; unsigned int new_len; int hash_alg; + int hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL; clear_problem_context(&pctx); pctx.ino = ino; @@ -385,7 +405,7 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, if (!ent->dir->inode || (ext2fs_dirent_name_len(ent->dir) != ext2fs_dirent_name_len(prev->dir)) || - strncmp(ent->dir->name, prev->dir->name, + memcmp(ent->dir->name, prev->dir->name, ext2fs_dirent_name_len(ent->dir))) continue; pctx.dirent = ent->dir; @@ -397,13 +417,18 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, continue; } new_len = ext2fs_dirent_name_len(ent->dir); + if (new_len == 0) { + /* should never happen */ + ext2fs_unmark_valid(fs); + continue; + } memcpy(new_name, ent->dir->name, new_len); mutate_name(new_name, &new_len); for (j=0; j < fd->num_array; j++) { if ((i==j) || (new_len != - ext2fs_dirent_name_len(fd->harray[j].dir)) || - strncmp(new_name, fd->harray[j].dir->name, new_len)) + (unsigned) ext2fs_dirent_name_len(fd->harray[j].dir)) || + memcmp(new_name, fd->harray[j].dir->name, new_len)) continue; mutate_name(new_name, &new_len); @@ -414,9 +439,10 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) { memcpy(ent->dir->name, new_name, new_len); ext2fs_dirent_set_name_len(ent->dir, new_len); - ext2fs_dirhash(hash_alg, new_name, new_len, - fs->super->s_hash_seed, - &ent->hash, &ent->minor_hash); + ext2fs_dirhash2(hash_alg, new_name, new_len, + fs->encoding, hash_flags, + fs->super->s_hash_seed, + &ent->hash, &ent->minor_hash); fixed++; } } @@ -434,7 +460,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, struct hash_entry *ent; struct ext2_dir_entry *dirent; unsigned int rec_len, prev_rec_len, left, slack, offset; - int i; + blk_t i; ext2_dirhash_t prev_hash; int csum_size = 0; struct ext2_dir_entry_tail *t; @@ -602,6 +628,46 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) return (struct ext2_dx_entry *) limits; } +static int alloc_blocks(ext2_filsys fs, + struct ext2_dx_countlimit **limit, + struct ext2_dx_entry **prev_ent, + struct ext2_dx_entry **next_ent, + int *prev_offset, int *next_offset, + struct out_dir *outdir, int i, + int *prev_count, int *next_count) +{ + errcode_t retval; + char *block_start; + + if (*limit) + (*limit)->limit = (*limit)->count = + ext2fs_cpu_to_le16((*limit)->limit); + *prev_ent = (struct ext2_dx_entry *) (outdir->buf + *prev_offset); + (*prev_ent)->block = ext2fs_cpu_to_le32(outdir->num); + + if (i != 1) + (*prev_ent)->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + + retval = get_next_block(fs, outdir, &block_start); + if (retval) + return retval; + + /* outdir->buf might be reallocated */ + *prev_ent = (struct ext2_dx_entry *) (outdir->buf + *prev_offset); + + *next_ent = set_int_node(fs, block_start); + *limit = (struct ext2_dx_countlimit *)(*next_ent); + if (next_offset) + *next_offset = ((char *) *next_ent - outdir->buf); + + *next_count = (*limit)->limit; + (*prev_offset) += sizeof(struct ext2_dx_entry); + (*prev_count)--; + + return 0; +} + /* * This function takes the leaf nodes which have been written in * outdir, and populates the root node and any necessary interior nodes. @@ -611,13 +677,12 @@ static errcode_t calculate_tree(ext2_filsys fs, 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; + struct ext2_dx_root_info *root_info; + struct ext2_dx_entry *root, *int_ent, *dx_ent = 0; + struct ext2_dx_countlimit *root_limit, *int_limit, *limit; errcode_t retval; - char * block_start; - int i, c1, c2, nblks; - int limit_offset, root_offset; + int i, c1, c2, c3, nblks; + int limit_offset, int_offset, root_offset; root_info = set_root_node(fs, outdir->buf, ino, parent); root_offset = limit_offset = ((char *) root_info - outdir->buf) + @@ -627,7 +692,7 @@ static errcode_t calculate_tree(ext2_filsys fs, nblks = outdir->num; /* Write out the pointer blocks */ - if (nblks-1 <= c1) { + 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++) { @@ -638,31 +703,20 @@ static errcode_t calculate_tree(ext2_filsys fs, root++; c1--; } - } else { + } else if (nblks - 1 <= ext2fs_htree_intnode_maxrecs(fs, c1)) { c2 = 0; - limit = 0; + limit = NULL; root_info->indirect_levels = 1; for (i=1; i < nblks; i++) { - if (c1 == 0) + if (c2 == 0 && 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))) + retval = alloc_blocks(fs, &limit, &root, + &dx_ent, &root_offset, + NULL, outdir, i, &c1, + &c2); + if (retval) 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) @@ -673,6 +727,53 @@ static errcode_t calculate_tree(ext2_filsys fs, } limit->count = ext2fs_cpu_to_le16(limit->limit - c2); limit->limit = ext2fs_cpu_to_le16(limit->limit); + } else { + c2 = 0; + c3 = 0; + limit = NULL; + int_limit = 0; + root_info->indirect_levels = 2; + for (i = 1; i < nblks; i++) { + if (c3 == 0 && c2 == 0 && c1 == 0) + return ENOSPC; + if (c3 == 0 && c2 == 0) { + retval = alloc_blocks(fs, &int_limit, &root, + &int_ent, &root_offset, + &int_offset, outdir, i, + &c1, &c2); + if (retval) + return retval; + } + if (c3 == 0) { + int delta1 = (char *)int_limit - outdir->buf; + int delta2 = (char *)root - outdir->buf; + + retval = alloc_blocks(fs, &limit, &int_ent, + &dx_ent, &int_offset, + NULL, outdir, i, &c2, + &c3); + if (retval) + return retval; + + /* outdir->buf might be reallocated */ + int_limit = (struct ext2_dx_countlimit *) + (outdir->buf + delta1); + root = (struct ext2_dx_entry *) + (outdir->buf + delta2); + } + dx_ent->block = ext2fs_cpu_to_le32(i); + if (c3 != limit->limit) + dx_ent->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + dx_ent++; + c3--; + } + int_limit->count = ext2fs_cpu_to_le16(limit->limit - c2); + int_limit->limit = ext2fs_cpu_to_le16(limit->limit); + + limit->count = ext2fs_cpu_to_le16(limit->limit - c3); + 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); @@ -684,8 +785,8 @@ static errcode_t calculate_tree(ext2_filsys fs, struct write_dir_struct { struct out_dir *outdir; errcode_t err; + ext2_ino_t ino; e2fsck_t ctx; - blk64_t cleared; ext2_ino_t dir; }; @@ -700,13 +801,17 @@ static int write_dir_block(ext2_filsys fs, void *priv_data) { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; - blk64_t blk; char *dir, *buf = 0; - if (*block_nr == 0) - return 0; - if (blockcnt < 0) +#ifdef REHASH_DEBUG + printf("%u: write_dir_block %lld:%lld", wd->ino, blockcnt, *block_nr); +#endif + if ((*block_nr == 0) || (blockcnt < 0)) { +#ifdef REHASH_DEBUG + printf(" - skip\n"); +#endif return 0; + } if (blockcnt < wd->outdir->num) dir = wd->outdir->buf + (blockcnt * fs->blocksize); else if (wd->ctx->lost_and_found == wd->dir) { @@ -717,26 +822,20 @@ static int write_dir_block(ext2_filsys fs, dir = buf; wd->outdir->num++; } else { - /* We don't need this block, so release it */ - e2fsck_read_bitmaps(wd->ctx); - blk = *block_nr; - /* - * In theory, we only release blocks from the end of the - * directory file, so it's fine to clobber a whole cluster at - * once. - */ - if (blk % EXT2FS_CLUSTER_RATIO(fs) == 0) { - ext2fs_block_alloc_stats2(fs, blk, -1); - wd->cleared++; - } - *block_nr = 0; - return BLOCK_CHANGED; + /* Don't free blocks at the end of the directory, they + * will be truncated by the caller. */ +#ifdef REHASH_DEBUG + printf(" - not freed\n"); +#endif + return 0; } - wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir); if (buf) ext2fs_free_mem(&buf); +#ifdef REHASH_DEBUG + printf(" - write (%d)\n", wd->err); +#endif if (wd->err) return BLOCK_ABORT; return 0; @@ -756,11 +855,11 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, wd.outdir = outdir; wd.err = 0; + wd.ino = ino; wd.ctx = ctx; - wd.cleared = 0; wd.dir = ino; - retval = ext2fs_block_iterate3(fs, ino, 0, 0, + retval = ext2fs_block_iterate3(fs, ino, 0, NULL, write_dir_block, &wd); if (retval) return retval; @@ -772,14 +871,17 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, inode->i_flags &= ~EXT2_INDEX_FL; else inode->i_flags |= EXT2_INDEX_FL; - retval = ext2fs_inode_size_set(fs, inode, - outdir->num * fs->blocksize); +#ifdef REHASH_DEBUG + printf("%u: set inode size to %u blocks = %u bytes\n", + ino, outdir->num, outdir->num * fs->blocksize); +#endif + retval = ext2fs_inode_size_set(fs, inode, (ext2_off64_t)outdir->num * + fs->blocksize); if (retval) return retval; - ext2fs_iblk_sub_blocks(fs, inode, wd.cleared); - e2fsck_write_inode(ctx, ino, inode, "rehash_dir"); - return 0; + /* ext2fs_punch() calls ext2fs_write_inode() which writes the size */ + return ext2fs_punch(fs, ino, inode, NULL, outdir->num, ~0ULL); } errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino, @@ -789,37 +891,30 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino, errcode_t retval; struct ext2_inode inode; char *dir_buf = 0; - struct fill_dir_struct fd; - struct out_dir outdir; + struct fill_dir_struct fd = { NULL, NULL, 0, 0, 0, NULL, + 0, 0, 0, 0, 0, 0 }; + struct out_dir outdir = { 0, 0, 0, 0 }; - outdir.max = outdir.num = 0; - outdir.buf = 0; - outdir.hashes = 0; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); if (ext2fs_has_feature_inline_data(fs->super) && (inode.i_flags & EXT4_INLINE_DATA_FL)) return 0; - retval = ENOMEM; - fd.harray = 0; - dir_buf = malloc(inode.i_size); - if (!dir_buf) + retval = ext2fs_get_mem(inode.i_size, &dir_buf); + if (retval) goto errout; fd.max_array = inode.i_size / 32; - fd.num_array = 0; - fd.harray = malloc(fd.max_array * sizeof(struct hash_entry)); - if (!fd.harray) + retval = ext2fs_get_array(sizeof(struct hash_entry), + fd.max_array, &fd.harray); + if (retval) goto errout; + fd.ino = ino; fd.ctx = ctx; fd.buf = dir_buf; fd.inode = &inode; - fd.ino = ino; - fd.err = 0; - fd.dir_size = 0; - fd.compress = 0; fd.dir = ino; if (!ext2fs_has_feature_dir_index(fs->super) || (inode.i_size / fs->blocksize) < 2) @@ -902,8 +997,8 @@ resort: else retval = e2fsck_check_rebuild_extents(ctx, ino, &inode, pctx); errout: - free(dir_buf); - free(fd.harray); + ext2fs_free_mem(&dir_buf); + ext2fs_free_mem(&fd.harray); free_out_dir(&outdir); return retval; @@ -956,6 +1051,8 @@ void e2fsck_rehash_directories(e2fsck_t ctx) if (!ext2fs_u32_list_iterate(iter, &ino)) break; } + if (!ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino)) + continue; pctx.dir = ino; if (first) {