Whamcloud - gitweb
e2fsck: clean up xattr checking code
[tools/e2fsprogs.git] / e2fsck / rehash.c
1 /*
2  * rehash.c --- rebuild hash tree directories
3  *
4  * Copyright (C) 2002 Theodore Ts'o
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  * %End-Header%
10  *
11  * This algorithm is designed for simplicity of implementation and to
12  * pack the directory as much as possible.  It however requires twice
13  * as much memory as the size of the directory.  The maximum size
14  * directory supported using a 4k blocksize is roughly a gigabyte, and
15  * so there may very well be problems with machines that don't have
16  * virtual memory, and obscenely large directories.
17  *
18  * An alternate algorithm which is much more disk intensive could be
19  * written, and probably will need to be written in the future.  The
20  * design goals of such an algorithm are: (a) use (roughly) constant
21  * amounts of memory, no matter how large the directory, (b) the
22  * directory must be safe at all times, even if e2fsck is interrupted
23  * in the middle, (c) we must use minimal amounts of extra disk
24  * blocks.  This pretty much requires an incremental approach, where
25  * we are reading from one part of the directory, and inserting into
26  * the front half.  So the algorithm will have to keep track of a
27  * moving block boundary between the new tree and the old tree, and
28  * files will need to be moved from the old directory and inserted
29  * into the new tree.  If the new directory requires space which isn't
30  * yet available, blocks from the beginning part of the old directory
31  * may need to be moved to the end of the directory to make room for
32  * the new tree:
33  *
34  *    --------------------------------------------------------
35  *    |  new tree   |        | old tree                      |
36  *    --------------------------------------------------------
37  *                  ^ ptr    ^ptr
38  *                tail new   head old
39  *
40  * This is going to be a pain in the tuckus to implement, and will
41  * require a lot more disk accesses.  So I'm going to skip it for now;
42  * it's only really going to be an issue for really, really big
43  * filesystems (when we reach the level of tens of millions of files
44  * in a single directory).  It will probably be easier to simply
45  * require that e2fsck use VM first.
46  */
47
48 #include "config.h"
49 #include <string.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include "e2fsck.h"
53 #include "problem.h"
54
55 /* Schedule a dir to be rebuilt during pass 3A. */
56 void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
57 {
58         if (!ctx->dirs_to_hash)
59                 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
60         if (ctx->dirs_to_hash)
61                 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
62 }
63
64 /* Ask if a dir will be rebuilt during pass 3A. */
65 int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
66 {
67         if (ctx->options & E2F_OPT_COMPRESS_DIRS)
68                 return 1;
69         if (!ctx->dirs_to_hash)
70                 return 0;
71         return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
72 }
73
74 #undef REHASH_DEBUG
75
76 struct fill_dir_struct {
77         char *buf;
78         struct ext2_inode *inode;
79         ext2_ino_t ino;
80         errcode_t err;
81         e2fsck_t ctx;
82         struct hash_entry *harray;
83         int max_array, num_array;
84         unsigned int dir_size;
85         int compress;
86         ino_t parent;
87         ext2_ino_t dir;
88 };
89
90 struct hash_entry {
91         ext2_dirhash_t  hash;
92         ext2_dirhash_t  minor_hash;
93         ino_t           ino;
94         struct ext2_dir_entry   *dir;
95 };
96
97 struct out_dir {
98         int             num;
99         int             max;
100         char            *buf;
101         ext2_dirhash_t  *hashes;
102 };
103
104 static int fill_dir_block(ext2_filsys fs,
105                           blk64_t *block_nr,
106                           e2_blkcnt_t blockcnt,
107                           blk64_t ref_block EXT2FS_ATTR((unused)),
108                           int ref_offset EXT2FS_ATTR((unused)),
109                           void *priv_data)
110 {
111         struct fill_dir_struct  *fd = (struct fill_dir_struct *) priv_data;
112         struct hash_entry       *new_array, *ent;
113         struct ext2_dir_entry   *dirent;
114         char                    *dir;
115         unsigned int            offset, dir_offset, rec_len, name_len;
116         int                     hash_alg, hash_flags;
117
118         if (blockcnt < 0)
119                 return 0;
120
121         offset = blockcnt * fs->blocksize;
122         if (offset + fs->blocksize > fd->inode->i_size) {
123                 fd->err = EXT2_ET_DIR_CORRUPTED;
124                 return BLOCK_ABORT;
125         }
126
127         dir = (fd->buf+offset);
128         if (*block_nr == 0) {
129                 memset(dir, 0, fs->blocksize);
130                 dirent = (struct ext2_dir_entry *) dir;
131                 (void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
132         } else {
133                 int flags = fs->flags;
134                 fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
135                 fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
136                                                  fd->dir);
137                 fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
138                             (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
139                 if (fd->err)
140                         return BLOCK_ABORT;
141         }
142         hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL;
143         hash_alg = fs->super->s_def_hash_version;
144         if ((hash_alg <= EXT2_HASH_TEA) &&
145             (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
146                 hash_alg += 3;
147         /* While the directory block is "hot", index it. */
148         dir_offset = 0;
149         while (dir_offset < fs->blocksize) {
150                 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
151                 (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
152                 name_len = ext2fs_dirent_name_len(dirent);
153                 if (((dir_offset + rec_len) > fs->blocksize) ||
154                     (rec_len < 8) ||
155                     ((rec_len % 4) != 0) ||
156                     (name_len + 8 > rec_len)) {
157                         fd->err = EXT2_ET_DIR_CORRUPTED;
158                         return BLOCK_ABORT;
159                 }
160                 dir_offset += rec_len;
161                 if (dirent->inode == 0)
162                         continue;
163                 if (!fd->compress && (name_len == 1) &&
164                     (dirent->name[0] == '.'))
165                         continue;
166                 if (!fd->compress && (name_len == 2) &&
167                     (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
168                         fd->parent = dirent->inode;
169                         continue;
170                 }
171                 if (fd->num_array >= fd->max_array) {
172                         new_array = realloc(fd->harray,
173                             sizeof(struct hash_entry) * (fd->max_array+500));
174                         if (!new_array) {
175                                 fd->err = ENOMEM;
176                                 return BLOCK_ABORT;
177                         }
178                         fd->harray = new_array;
179                         fd->max_array += 500;
180                 }
181                 ent = fd->harray + fd->num_array++;
182                 ent->dir = dirent;
183                 fd->dir_size += EXT2_DIR_REC_LEN(name_len);
184                 ent->ino = dirent->inode;
185                 if (fd->compress)
186                         ent->hash = ent->minor_hash = 0;
187                 else {
188                         fd->err = ext2fs_dirhash2(hash_alg,
189                                                   dirent->name, name_len,
190                                                   fs->encoding, hash_flags,
191                                                   fs->super->s_hash_seed,
192                                                   &ent->hash, &ent->minor_hash);
193                         if (fd->err)
194                                 return BLOCK_ABORT;
195                 }
196         }
197
198         return 0;
199 }
200
201 /* Used for sorting the hash entry */
202 static EXT2_QSORT_TYPE ino_cmp(const void *a, const void *b)
203 {
204         const struct hash_entry *he_a = (const struct hash_entry *) a;
205         const struct hash_entry *he_b = (const struct hash_entry *) b;
206
207         return (he_a->ino - he_b->ino);
208 }
209
210 /* Used for sorting the hash entry */
211 static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b)
212 {
213         const struct hash_entry *he_a = (const struct hash_entry *) a;
214         const struct hash_entry *he_b = (const struct hash_entry *) b;
215         unsigned int he_a_len, he_b_len, min_len;
216         int     ret;
217
218         he_a_len = ext2fs_dirent_name_len(he_a->dir);
219         he_b_len = ext2fs_dirent_name_len(he_b->dir);
220         min_len = he_a_len;
221         if (min_len > he_b_len)
222                 min_len = he_b_len;
223
224         ret = memcmp(he_a->dir->name, he_b->dir->name, min_len);
225         if (ret == 0) {
226                 if (he_a_len > he_b_len)
227                         ret = 1;
228                 else if (he_a_len < he_b_len)
229                         ret = -1;
230                 else
231                         ret = he_b->dir->inode - he_a->dir->inode;
232         }
233         return ret;
234 }
235
236 /* Used for sorting the hash entry */
237 static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b)
238 {
239         const struct hash_entry *he_a = (const struct hash_entry *) a;
240         const struct hash_entry *he_b = (const struct hash_entry *) b;
241         int     ret;
242
243         if (he_a->hash > he_b->hash)
244                 ret = 1;
245         else if (he_a->hash < he_b->hash)
246                 ret = -1;
247         else {
248                 if (he_a->minor_hash > he_b->minor_hash)
249                         ret = 1;
250                 else if (he_a->minor_hash < he_b->minor_hash)
251                         ret = -1;
252                 else
253                         ret = name_cmp(a, b);
254         }
255         return ret;
256 }
257
258 static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
259                                 int blocks)
260 {
261         void                    *new_mem;
262
263         if (outdir->max) {
264                 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
265                 if (!new_mem)
266                         return ENOMEM;
267                 outdir->buf = new_mem;
268                 new_mem = realloc(outdir->hashes,
269                                   blocks * sizeof(ext2_dirhash_t));
270                 if (!new_mem)
271                         return ENOMEM;
272                 outdir->hashes = new_mem;
273         } else {
274                 outdir->buf = malloc(blocks * fs->blocksize);
275                 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
276                 outdir->num = 0;
277         }
278         outdir->max = blocks;
279         return 0;
280 }
281
282 static void free_out_dir(struct out_dir *outdir)
283 {
284         free(outdir->buf);
285         free(outdir->hashes);
286         outdir->max = 0;
287         outdir->num =0;
288 }
289
290 static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
291                          char ** ret)
292 {
293         errcode_t       retval;
294
295         if (outdir->num >= outdir->max) {
296                 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
297                 if (retval)
298                         return retval;
299         }
300         *ret = outdir->buf + (outdir->num++ * fs->blocksize);
301         memset(*ret, 0, fs->blocksize);
302         return 0;
303 }
304
305 /*
306  * This function is used to make a unique filename.  We do this by
307  * appending ~0, and then incrementing the number.  However, we cannot
308  * expand the length of the filename beyond the padding available in
309  * the directory entry.
310  */
311 static void mutate_name(char *str, unsigned int *len)
312 {
313         int i;
314         unsigned int l = *len;
315
316         /*
317          * First check to see if it looks the name has been mutated
318          * already
319          */
320         for (i = l-1; i > 0; i--) {
321                 if (!isdigit(str[i]))
322                         break;
323         }
324         if ((i == (int)l - 1) || (str[i] != '~')) {
325                 if (((l-1) & 3) < 2)
326                         l += 2;
327                 else
328                         l = (l+3) & ~3;
329                 str[l-2] = '~';
330                 str[l-1] = '0';
331                 *len = l;
332                 return;
333         }
334         for (i = l-1; i >= 0; i--) {
335                 if (isdigit(str[i])) {
336                         if (str[i] == '9')
337                                 str[i] = '0';
338                         else {
339                                 str[i]++;
340                                 return;
341                         }
342                         continue;
343                 }
344                 if (i == 1) {
345                         if (str[0] == 'z')
346                                 str[0] = 'A';
347                         else if (str[0] == 'Z') {
348                                 str[0] = '~';
349                                 str[1] = '0';
350                         } else
351                                 str[0]++;
352                 } else if (i > 0) {
353                         str[i] = '1';
354                         str[i-1] = '~';
355                 } else {
356                         if (str[0] == '~')
357                                 str[0] = 'a';
358                         else
359                                 str[0]++;
360                 }
361                 break;
362         }
363 }
364
365 static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
366                                     ext2_ino_t ino,
367                                     struct fill_dir_struct *fd)
368 {
369         struct problem_context  pctx;
370         struct hash_entry       *ent, *prev;
371         int                     i, j;
372         int                     fixed = 0;
373         char                    new_name[256];
374         unsigned int            new_len;
375         int                     hash_alg;
376         int hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL;
377
378         clear_problem_context(&pctx);
379         pctx.ino = ino;
380
381         hash_alg = fs->super->s_def_hash_version;
382         if ((hash_alg <= EXT2_HASH_TEA) &&
383             (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
384                 hash_alg += 3;
385
386         for (i=1; i < fd->num_array; i++) {
387                 ent = fd->harray + i;
388                 prev = ent - 1;
389                 if (!ent->dir->inode ||
390                     (ext2fs_dirent_name_len(ent->dir) !=
391                      ext2fs_dirent_name_len(prev->dir)) ||
392                     memcmp(ent->dir->name, prev->dir->name,
393                              ext2fs_dirent_name_len(ent->dir)))
394                         continue;
395                 pctx.dirent = ent->dir;
396                 if ((ent->dir->inode == prev->dir->inode) &&
397                     fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
398                         e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
399                         ent->dir->inode = 0;
400                         fixed++;
401                         continue;
402                 }
403                 new_len = ext2fs_dirent_name_len(ent->dir);
404                 memcpy(new_name, ent->dir->name, new_len);
405                 mutate_name(new_name, &new_len);
406                 for (j=0; j < fd->num_array; j++) {
407                         if ((i==j) ||
408                             (new_len !=
409                              (unsigned) ext2fs_dirent_name_len(fd->harray[j].dir)) ||
410                             memcmp(new_name, fd->harray[j].dir->name, new_len))
411                                 continue;
412                         mutate_name(new_name, &new_len);
413
414                         j = -1;
415                 }
416                 new_name[new_len] = 0;
417                 pctx.str = new_name;
418                 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
419                         memcpy(ent->dir->name, new_name, new_len);
420                         ext2fs_dirent_set_name_len(ent->dir, new_len);
421                         ext2fs_dirhash2(hash_alg, new_name, new_len,
422                                         fs->encoding, hash_flags,
423                                         fs->super->s_hash_seed,
424                                         &ent->hash, &ent->minor_hash);
425                         fixed++;
426                 }
427         }
428         return fixed;
429 }
430
431
432 static errcode_t copy_dir_entries(e2fsck_t ctx,
433                                   struct fill_dir_struct *fd,
434                                   struct out_dir *outdir)
435 {
436         ext2_filsys             fs = ctx->fs;
437         errcode_t               retval;
438         char                    *block_start;
439         struct hash_entry       *ent;
440         struct ext2_dir_entry   *dirent;
441         unsigned int            rec_len, prev_rec_len, left, slack, offset;
442         int                     i;
443         ext2_dirhash_t          prev_hash;
444         int                     csum_size = 0;
445         struct                  ext2_dir_entry_tail *t;
446
447         if (ctx->htree_slack_percentage == 255) {
448                 profile_get_uint(ctx->profile, "options",
449                                  "indexed_dir_slack_percentage",
450                                  0, 20,
451                                  &ctx->htree_slack_percentage);
452                 if (ctx->htree_slack_percentage > 100)
453                         ctx->htree_slack_percentage = 20;
454         }
455
456         if (ext2fs_has_feature_metadata_csum(fs->super))
457                 csum_size = sizeof(struct ext2_dir_entry_tail);
458
459         outdir->max = 0;
460         retval = alloc_size_dir(fs, outdir,
461                                 (fd->dir_size / fs->blocksize) + 2);
462         if (retval)
463                 return retval;
464         outdir->num = fd->compress ? 0 : 1;
465         offset = 0;
466         outdir->hashes[0] = 0;
467         prev_hash = 1;
468         if ((retval = get_next_block(fs, outdir, &block_start)))
469                 return retval;
470         dirent = (struct ext2_dir_entry *) block_start;
471         prev_rec_len = 0;
472         rec_len = 0;
473         left = fs->blocksize - csum_size;
474         slack = fd->compress ? 12 :
475                 ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
476         if (slack < 12)
477                 slack = 12;
478         for (i = 0; i < fd->num_array; i++) {
479                 ent = fd->harray + i;
480                 if (ent->dir->inode == 0)
481                         continue;
482                 rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir));
483                 if (rec_len > left) {
484                         if (left) {
485                                 left += prev_rec_len;
486                                 retval = ext2fs_set_rec_len(fs, left, dirent);
487                                 if (retval)
488                                         return retval;
489                         }
490                         if (csum_size) {
491                                 t = EXT2_DIRENT_TAIL(block_start,
492                                                      fs->blocksize);
493                                 ext2fs_initialize_dirent_tail(fs, t);
494                         }
495                         if ((retval = get_next_block(fs, outdir,
496                                                       &block_start)))
497                                 return retval;
498                         offset = 0;
499                 }
500                 left = (fs->blocksize - csum_size) - offset;
501                 dirent = (struct ext2_dir_entry *) (block_start + offset);
502                 if (offset == 0) {
503                         if (ent->hash == prev_hash)
504                                 outdir->hashes[outdir->num-1] = ent->hash | 1;
505                         else
506                                 outdir->hashes[outdir->num-1] = ent->hash;
507                 }
508                 dirent->inode = ent->dir->inode;
509                 ext2fs_dirent_set_name_len(dirent,
510                                            ext2fs_dirent_name_len(ent->dir));
511                 ext2fs_dirent_set_file_type(dirent,
512                                             ext2fs_dirent_file_type(ent->dir));
513                 retval = ext2fs_set_rec_len(fs, rec_len, dirent);
514                 if (retval)
515                         return retval;
516                 prev_rec_len = rec_len;
517                 memcpy(dirent->name, ent->dir->name,
518                        ext2fs_dirent_name_len(dirent));
519                 offset += rec_len;
520                 left -= rec_len;
521                 if (left < slack) {
522                         prev_rec_len += left;
523                         retval = ext2fs_set_rec_len(fs, prev_rec_len, dirent);
524                         if (retval)
525                                 return retval;
526                         offset += left;
527                         left = 0;
528                 }
529                 prev_hash = ent->hash;
530         }
531         if (left)
532                 retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
533         if (csum_size) {
534                 t = EXT2_DIRENT_TAIL(block_start, fs->blocksize);
535                 ext2fs_initialize_dirent_tail(fs, t);
536         }
537
538         return retval;
539 }
540
541
542 static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
543                                     ext2_ino_t ino, ext2_ino_t parent)
544 {
545         struct ext2_dir_entry           *dir;
546         struct ext2_dx_root_info        *root;
547         struct ext2_dx_countlimit       *limits;
548         int                             filetype = 0;
549         int                             csum_size = 0;
550
551         if (ext2fs_has_feature_filetype(fs->super))
552                 filetype = EXT2_FT_DIR;
553
554         memset(buf, 0, fs->blocksize);
555         dir = (struct ext2_dir_entry *) buf;
556         dir->inode = ino;
557         dir->name[0] = '.';
558         ext2fs_dirent_set_name_len(dir, 1);
559         ext2fs_dirent_set_file_type(dir, filetype);
560         dir->rec_len = 12;
561         dir = (struct ext2_dir_entry *) (buf + 12);
562         dir->inode = parent;
563         dir->name[0] = '.';
564         dir->name[1] = '.';
565         ext2fs_dirent_set_name_len(dir, 2);
566         ext2fs_dirent_set_file_type(dir, filetype);
567         dir->rec_len = fs->blocksize - 12;
568
569         root = (struct ext2_dx_root_info *) (buf+24);
570         root->reserved_zero = 0;
571         root->hash_version = fs->super->s_def_hash_version;
572         root->info_length = 8;
573         root->indirect_levels = 0;
574         root->unused_flags = 0;
575
576         if (ext2fs_has_feature_metadata_csum(fs->super))
577                 csum_size = sizeof(struct ext2_dx_tail);
578
579         limits = (struct ext2_dx_countlimit *) (buf+32);
580         limits->limit = (fs->blocksize - (32 + csum_size)) /
581                         sizeof(struct ext2_dx_entry);
582         limits->count = 0;
583
584         return root;
585 }
586
587
588 static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
589 {
590         struct ext2_dir_entry           *dir;
591         struct ext2_dx_countlimit       *limits;
592         int                             csum_size = 0;
593
594         memset(buf, 0, fs->blocksize);
595         dir = (struct ext2_dir_entry *) buf;
596         dir->inode = 0;
597         (void) ext2fs_set_rec_len(fs, fs->blocksize, dir);
598
599         if (ext2fs_has_feature_metadata_csum(fs->super))
600                 csum_size = sizeof(struct ext2_dx_tail);
601
602         limits = (struct ext2_dx_countlimit *) (buf+8);
603         limits->limit = (fs->blocksize - (8 + csum_size)) /
604                         sizeof(struct ext2_dx_entry);
605         limits->count = 0;
606
607         return (struct ext2_dx_entry *) limits;
608 }
609
610 static int alloc_blocks(ext2_filsys fs,
611                         struct ext2_dx_countlimit **limit,
612                         struct ext2_dx_entry **prev_ent,
613                         struct ext2_dx_entry **next_ent,
614                         int *prev_offset, int *next_offset,
615                         struct out_dir *outdir, int i,
616                         int *prev_count, int *next_count)
617 {
618         errcode_t       retval;
619         char            *block_start;
620
621         if (*limit)
622                 (*limit)->limit = (*limit)->count =
623                         ext2fs_cpu_to_le16((*limit)->limit);
624         *prev_ent = (struct ext2_dx_entry *) (outdir->buf + *prev_offset);
625         (*prev_ent)->block = ext2fs_cpu_to_le32(outdir->num);
626
627         if (i != 1)
628                 (*prev_ent)->hash =
629                         ext2fs_cpu_to_le32(outdir->hashes[i]);
630
631         retval = get_next_block(fs, outdir, &block_start);
632         if (retval)
633                 return retval;
634
635         *next_ent = set_int_node(fs, block_start);
636         *limit = (struct ext2_dx_countlimit *)(*next_ent);
637         if (next_offset)
638                 *next_offset = ((char *) *next_ent - outdir->buf);
639
640         *next_count = (*limit)->limit;
641         (*prev_offset) += sizeof(struct ext2_dx_entry);
642         (*prev_count)--;
643
644         return 0;
645 }
646
647 /*
648  * This function takes the leaf nodes which have been written in
649  * outdir, and populates the root node and any necessary interior nodes.
650  */
651 static errcode_t calculate_tree(ext2_filsys fs,
652                                 struct out_dir *outdir,
653                                 ext2_ino_t ino,
654                                 ext2_ino_t parent)
655 {
656         struct ext2_dx_root_info        *root_info;
657         struct ext2_dx_entry            *root, *int_ent, *dx_ent = 0;
658         struct ext2_dx_countlimit       *root_limit, *int_limit, *limit;
659         errcode_t                       retval;
660         int                             i, c1, c2, c3, nblks;
661         int                             limit_offset, int_offset, root_offset;
662
663         root_info = set_root_node(fs, outdir->buf, ino, parent);
664         root_offset = limit_offset = ((char *) root_info - outdir->buf) +
665                 root_info->info_length;
666         root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
667         c1 = root_limit->limit;
668         nblks = outdir->num;
669
670         /* Write out the pointer blocks */
671         if (nblks - 1 <= c1) {
672                 /* Just write out the root block, and we're done */
673                 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
674                 for (i=1; i < nblks; i++) {
675                         root->block = ext2fs_cpu_to_le32(i);
676                         if (i != 1)
677                                 root->hash =
678                                         ext2fs_cpu_to_le32(outdir->hashes[i]);
679                         root++;
680                         c1--;
681                 }
682         } else if (nblks - 1 <= ext2fs_htree_intnode_maxrecs(fs, c1)) {
683                 c2 = 0;
684                 limit = NULL;
685                 root_info->indirect_levels = 1;
686                 for (i=1; i < nblks; i++) {
687                         if (c2 == 0 && c1 == 0)
688                                 return ENOSPC;
689                         if (c2 == 0) {
690                                 retval = alloc_blocks(fs, &limit, &root,
691                                                       &dx_ent, &root_offset,
692                                                       NULL, outdir, i, &c1,
693                                                       &c2);
694                                 if (retval)
695                                         return retval;
696                         }
697                         dx_ent->block = ext2fs_cpu_to_le32(i);
698                         if (c2 != limit->limit)
699                                 dx_ent->hash =
700                                         ext2fs_cpu_to_le32(outdir->hashes[i]);
701                         dx_ent++;
702                         c2--;
703                 }
704                 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
705                 limit->limit = ext2fs_cpu_to_le16(limit->limit);
706         } else {
707                 c2 = 0;
708                 c3 = 0;
709                 limit = NULL;
710                 int_limit = 0;
711                 root_info->indirect_levels = 2;
712                 for (i = 1; i < nblks; i++) {
713                         if (c3 == 0 && c2 == 0 && c1 == 0)
714                                 return ENOSPC;
715                         if (c3 == 0 && c2 == 0) {
716                                 retval = alloc_blocks(fs, &int_limit, &root,
717                                                       &int_ent, &root_offset,
718                                                       &int_offset, outdir, i,
719                                                       &c1, &c2);
720                                 if (retval)
721                                         return retval;
722                         }
723                         if (c3 == 0) {
724                                 retval = alloc_blocks(fs, &limit, &int_ent,
725                                                       &dx_ent, &int_offset,
726                                                       NULL, outdir, i, &c2,
727                                                       &c3);
728                                 if (retval)
729                                         return retval;
730
731                         }
732                         dx_ent->block = ext2fs_cpu_to_le32(i);
733                         if (c3 != limit->limit)
734                                 dx_ent->hash =
735                                         ext2fs_cpu_to_le32(outdir->hashes[i]);
736                         dx_ent++;
737                         c3--;
738                 }
739                 int_limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
740                 int_limit->limit = ext2fs_cpu_to_le16(limit->limit);
741
742                 limit->count = ext2fs_cpu_to_le16(limit->limit - c3);
743                 limit->limit = ext2fs_cpu_to_le16(limit->limit);
744
745         }
746         root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
747         root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
748         root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
749
750         return 0;
751 }
752
753 struct write_dir_struct {
754         struct out_dir *outdir;
755         errcode_t       err;
756         ext2_ino_t      ino;
757         e2fsck_t        ctx;
758         ext2_ino_t      dir;
759 };
760
761 /*
762  * Helper function which writes out a directory block.
763  */
764 static int write_dir_block(ext2_filsys fs,
765                            blk64_t *block_nr,
766                            e2_blkcnt_t blockcnt,
767                            blk64_t ref_block EXT2FS_ATTR((unused)),
768                            int ref_offset EXT2FS_ATTR((unused)),
769                            void *priv_data)
770 {
771         struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
772         char    *dir, *buf = 0;
773
774 #ifdef REHASH_DEBUG
775         printf("%u: write_dir_block %lld:%lld", wd->ino, blockcnt, *block_nr);
776 #endif
777         if ((*block_nr == 0) || (blockcnt < 0)) {
778 #ifdef REHASH_DEBUG
779                 printf(" - skip\n");
780 #endif
781                 return 0;
782         }
783         if (blockcnt < wd->outdir->num)
784                 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
785         else if (wd->ctx->lost_and_found == wd->dir) {
786                 /* Don't release any extra directory blocks for lost+found */
787                 wd->err = ext2fs_new_dir_block(fs, 0, 0, &buf);
788                 if (wd->err)
789                         return BLOCK_ABORT;
790                 dir = buf;
791                 wd->outdir->num++;
792         } else {
793                 /* Don't free blocks at the end of the directory, they
794                  * will be truncated by the caller. */
795 #ifdef REHASH_DEBUG
796                 printf(" - not freed\n");
797 #endif
798                 return 0;
799         }
800         wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir);
801         if (buf)
802                 ext2fs_free_mem(&buf);
803
804 #ifdef REHASH_DEBUG
805         printf(" - write (%d)\n", wd->err);
806 #endif
807         if (wd->err)
808                 return BLOCK_ABORT;
809         return 0;
810 }
811
812 static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
813                                  struct out_dir *outdir,
814                                  ext2_ino_t ino, struct ext2_inode *inode,
815                                  int compress)
816 {
817         struct write_dir_struct wd;
818         errcode_t       retval;
819
820         retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
821         if (retval)
822                 return retval;
823
824         wd.outdir = outdir;
825         wd.err = 0;
826         wd.ino = ino;
827         wd.ctx = ctx;
828         wd.dir = ino;
829
830         retval = ext2fs_block_iterate3(fs, ino, 0, NULL,
831                                        write_dir_block, &wd);
832         if (retval)
833                 return retval;
834         if (wd.err)
835                 return wd.err;
836
837         e2fsck_read_inode(ctx, ino, inode, "rehash_dir");
838         if (compress)
839                 inode->i_flags &= ~EXT2_INDEX_FL;
840         else
841                 inode->i_flags |= EXT2_INDEX_FL;
842 #ifdef REHASH_DEBUG
843         printf("%u: set inode size to %u blocks = %u bytes\n",
844                ino, outdir->num, outdir->num * fs->blocksize);
845 #endif
846         retval = ext2fs_inode_size_set(fs, inode, (ext2_off64_t)outdir->num *
847                                                    fs->blocksize);
848         if (retval)
849                 return retval;
850
851         /* ext2fs_punch() calls ext2fs_write_inode() which writes the size */
852         return ext2fs_punch(fs, ino, inode, NULL, outdir->num, ~0ULL);
853 }
854
855 errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
856                             struct problem_context *pctx)
857 {
858         ext2_filsys             fs = ctx->fs;
859         errcode_t               retval;
860         struct ext2_inode       inode;
861         char                    *dir_buf = 0;
862         struct fill_dir_struct  fd = { NULL, NULL, 0, 0, 0, NULL,
863                                        0, 0, 0, 0, 0, 0 };
864         struct out_dir          outdir = { 0, 0, 0, 0 };
865
866         e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
867
868         if (ext2fs_has_feature_inline_data(fs->super) &&
869            (inode.i_flags & EXT4_INLINE_DATA_FL))
870                 return 0;
871
872         retval = ENOMEM;
873         dir_buf = malloc(inode.i_size);
874         if (!dir_buf)
875                 goto errout;
876
877         fd.max_array = inode.i_size / 32;
878         fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
879         if (!fd.harray)
880                 goto errout;
881
882         fd.ino = ino;
883         fd.ctx = ctx;
884         fd.buf = dir_buf;
885         fd.inode = &inode;
886         fd.dir = ino;
887         if (!ext2fs_has_feature_dir_index(fs->super) ||
888             (inode.i_size / fs->blocksize) < 2)
889                 fd.compress = 1;
890         fd.parent = 0;
891
892 retry_nohash:
893         /* Read in the entire directory into memory */
894         retval = ext2fs_block_iterate3(fs, ino, 0, 0,
895                                        fill_dir_block, &fd);
896         if (fd.err) {
897                 retval = fd.err;
898                 goto errout;
899         }
900
901         /* 
902          * If the entries read are less than a block, then don't index
903          * the directory
904          */
905         if (!fd.compress && (fd.dir_size < (fs->blocksize - 24))) {
906                 fd.compress = 1;
907                 fd.dir_size = 0;
908                 fd.num_array = 0;
909                 goto retry_nohash;
910         }
911
912 #if 0
913         printf("%d entries (%d bytes) found in inode %d\n",
914                fd.num_array, fd.dir_size, ino);
915 #endif
916
917         /* Sort the list */
918 resort:
919         if (fd.compress && fd.num_array > 1)
920                 qsort(fd.harray+2, fd.num_array-2, sizeof(struct hash_entry),
921                       hash_cmp);
922         else
923                 qsort(fd.harray, fd.num_array, sizeof(struct hash_entry),
924                       hash_cmp);
925
926         /*
927          * Look for duplicates
928          */
929         if (duplicate_search_and_fix(ctx, fs, ino, &fd))
930                 goto resort;
931
932         if (ctx->options & E2F_OPT_NO) {
933                 retval = 0;
934                 goto errout;
935         }
936
937         /* Sort non-hashed directories by inode number */
938         if (fd.compress && fd.num_array > 1)
939                 qsort(fd.harray+2, fd.num_array-2,
940                       sizeof(struct hash_entry), ino_cmp);
941
942         /*
943          * Copy the directory entries.  In a htree directory these
944          * will become the leaf nodes.
945          */
946         retval = copy_dir_entries(ctx, &fd, &outdir);
947         if (retval)
948                 goto errout;
949
950         free(dir_buf); dir_buf = 0;
951
952         if (!fd.compress) {
953                 /* Calculate the interior nodes */
954                 retval = calculate_tree(fs, &outdir, ino, fd.parent);
955                 if (retval)
956                         goto errout;
957         }
958
959         retval = write_directory(ctx, fs, &outdir, ino, &inode, fd.compress);
960         if (retval)
961                 goto errout;
962
963         if (ctx->options & E2F_OPT_CONVERT_BMAP)
964                 retval = e2fsck_rebuild_extents_later(ctx, ino);
965         else
966                 retval = e2fsck_check_rebuild_extents(ctx, ino, &inode, pctx);
967 errout:
968         free(dir_buf);
969         free(fd.harray);
970
971         free_out_dir(&outdir);
972         return retval;
973 }
974
975 void e2fsck_rehash_directories(e2fsck_t ctx)
976 {
977         struct problem_context  pctx;
978 #ifdef RESOURCE_TRACK
979         struct resource_track   rtrack;
980 #endif
981         struct dir_info         *dir;
982         ext2_u32_iterate        iter;
983         struct dir_info_iter *  dirinfo_iter = 0;
984         ext2_ino_t              ino;
985         errcode_t               retval;
986         int                     cur, max, all_dirs, first = 1;
987
988         init_resource_track(&rtrack, ctx->fs->io);
989         all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
990
991         if (!ctx->dirs_to_hash && !all_dirs)
992                 return;
993
994         (void) e2fsck_get_lost_and_found(ctx, 0);
995
996         clear_problem_context(&pctx);
997
998         cur = 0;
999         if (all_dirs) {
1000                 dirinfo_iter = e2fsck_dir_info_iter_begin(ctx);
1001                 max = e2fsck_get_num_dirinfo(ctx);
1002         } else {
1003                 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
1004                                                        &iter);
1005                 if (retval) {
1006                         pctx.errcode = retval;
1007                         fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
1008                         return;
1009                 }
1010                 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
1011         }
1012         while (1) {
1013                 if (all_dirs) {
1014                         if ((dir = e2fsck_dir_info_iter(ctx,
1015                                                         dirinfo_iter)) == 0)
1016                                 break;
1017                         ino = dir->ino;
1018                 } else {
1019                         if (!ext2fs_u32_list_iterate(iter, &ino))
1020                                 break;
1021                 }
1022
1023                 pctx.dir = ino;
1024                 if (first) {
1025                         fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
1026                         first = 0;
1027                 }
1028 #if 0
1029                 fix_problem(ctx, PR_3A_OPTIMIZE_DIR, &pctx);
1030 #endif
1031                 pctx.errcode = e2fsck_rehash_dir(ctx, ino, &pctx);
1032                 if (pctx.errcode) {
1033                         end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
1034                         fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
1035                 }
1036                 if (ctx->progress && !ctx->progress_fd)
1037                         e2fsck_simple_progress(ctx, "Rebuilding directory",
1038                                100.0 * (float) (++cur) / (float) max, ino);
1039         }
1040         end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
1041         if (all_dirs)
1042                 e2fsck_dir_info_iter_end(ctx, dirinfo_iter);
1043         else
1044                 ext2fs_u32_list_iterate_end(iter);
1045
1046         if (ctx->dirs_to_hash)
1047                 ext2fs_u32_list_free(ctx->dirs_to_hash);
1048         ctx->dirs_to_hash = 0;
1049
1050         print_resource_track(ctx, "Pass 3A", &rtrack, ctx->fs->io);
1051 }