Whamcloud - gitweb
e2fsck: add support for dirdata feature
[tools/e2fsprogs.git] / e2fsck / rehash.c
index 22a58f3..475e991 100644 (file)
@@ -85,6 +85,8 @@ struct fill_dir_struct {
        int compress;
        ino_t parent;
        ext2_ino_t dir;
+       struct ext2_dir_entry *dot_de;
+       struct ext2_dir_entry *dotdot_de;
 };
 
 struct hash_entry {
@@ -160,11 +162,14 @@ static int fill_dir_block(ext2_filsys fs,
                if (dirent->inode == 0)
                        continue;
                if (!fd->compress && (name_len == 1) &&
-                   (dirent->name[0] == '.'))
+                   (dirent->name[0] == '.')) {
+                       fd->dot_de = dirent;
                        continue;
+               }
                if (!fd->compress && (name_len == 2) &&
                    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
                        fd->parent = dirent->inode;
+                       fd->dotdot_de = dirent;
                        continue;
                }
                if (fd->num_array >= fd->max_array) {
@@ -179,7 +184,7 @@ static int fill_dir_block(ext2_filsys fs,
                }
                ent = fd->harray + fd->num_array++;
                ent->dir = dirent;
-               fd->dir_size += EXT2_DIR_REC_LEN(name_len);
+               fd->dir_size += EXT2_DIR_REC_LEN(dirent);
                ent->ino = dirent->inode;
                if (fd->compress)
                        ent->hash = ent->minor_hash = 0;
@@ -219,7 +224,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;
@@ -386,7 +391,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;
@@ -404,7 +409,7 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
                        if ((i==j) ||
                            (new_len !=
                             (unsigned) ext2fs_dirent_name_len(fd->harray[j].dir)) ||
-                           strncmp(new_name, fd->harray[j].dir->name, new_len))
+                           memcmp(new_name, fd->harray[j].dir->name, new_len))
                                continue;
                        mutate_name(new_name, &new_len);
 
@@ -475,7 +480,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
                ent = fd->harray + i;
                if (ent->dir->inode == 0)
                        continue;
-               rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir));
+               rec_len = EXT2_DIR_REC_LEN(ent->dir);
                if (rec_len > left) {
                        if (left) {
                                left += prev_rec_len;
@@ -510,8 +515,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
                if (retval)
                        return retval;
                prev_rec_len = rec_len;
-               memcpy(dirent->name, ent->dir->name,
-                      ext2fs_dirent_name_len(dirent));
+               memcpy(dirent->name, ent->dir->name, rec_len);
                offset += rec_len;
                left -= rec_len;
                if (left < slack) {
@@ -536,44 +540,49 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
 
 
 static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
-                                   ext2_ino_t ino, ext2_ino_t parent)
+                                       ext2_ino_t ino, ext2_ino_t parent,
+                                       struct ext2_dir_entry *dot_de,
+                                       struct ext2_dir_entry *dotdot_de)
 {
-       struct ext2_dir_entry           *dir;
-       struct ext2_dx_root_info        *root;
+       struct ext2_dir_entry           *dirent;
+       struct ext2_dx_root_info        *root;
        struct ext2_dx_countlimit       *limits;
-       int                             filetype = 0;
        int                             csum_size = 0;
-
-       if (ext2fs_has_feature_filetype(fs->super))
-               filetype = EXT2_FT_DIR;
+       int                             offset;
+       int                             rec_len;
 
        memset(buf, 0, fs->blocksize);
-       dir = (struct ext2_dir_entry *) buf;
-       dir->inode = ino;
-       dir->name[0] = '.';
-       ext2fs_dirent_set_name_len(dir, 1);
-       ext2fs_dirent_set_file_type(dir, filetype);
-       dir->rec_len = 12;
-       dir = (struct ext2_dir_entry *) (buf + 12);
-       dir->inode = parent;
-       dir->name[0] = '.';
-       dir->name[1] = '.';
-       ext2fs_dirent_set_name_len(dir, 2);
-       ext2fs_dirent_set_file_type(dir, filetype);
-       dir->rec_len = fs->blocksize - 12;
-
-       root = (struct ext2_dx_root_info *) (buf+24);
+       dirent = (struct ext2_dir_entry *) buf;
+       dirent->inode = ino;
+
+       dirent->name_len = dot_de->name_len;
+       offset = rec_len = dirent->rec_len = dot_de->rec_len;
+       memcpy(dirent->name, dot_de->name, rec_len);
+
+       dirent = EXT2_NEXT_DIRENT(dirent);
+       /* set to jump over the index block */
+
+       dirent->inode = parent;
+
+       dirent->name_len = dotdot_de->name_len;
+       dirent->rec_len = fs->blocksize - rec_len;
+       rec_len = EXT2_DIR_REC_LEN(dotdot_de);
+       memcpy(dirent->name, dotdot_de->name, rec_len);
+       offset += rec_len;
+
+       root = (struct ext2_dx_root_info *)(buf + offset);
        root->reserved_zero = 0;
        root->hash_version = fs->super->s_def_hash_version;
-       root->info_length = 8;
+       root->info_length = sizeof(*root);
        root->indirect_levels = 0;
        root->unused_flags = 0;
+       offset += root->info_length;
 
        if (ext2fs_has_feature_metadata_csum(fs->super))
                csum_size = sizeof(struct ext2_dx_tail);
 
-       limits = (struct ext2_dx_countlimit *) (buf+32);
-       limits->limit = (fs->blocksize - (32 + csum_size)) /
+       limits = (struct ext2_dx_countlimit *) (buf + offset);
+       limits->limit = (fs->blocksize - (offset + csum_size)) /
                        sizeof(struct ext2_dx_entry);
        limits->count = 0;
 
@@ -603,6 +612,43 @@ 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;
+
+       *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.
@@ -610,17 +656,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
 static errcode_t calculate_tree(ext2_filsys fs,
                                struct out_dir *outdir,
                                ext2_ino_t ino,
-                               ext2_ino_t parent)
+                               ext2_ino_t parent,
+                               struct ext2_dir_entry *dot_de,
+                               struct ext2_dir_entry *dotdot_de)
 {
-       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, dot_de,
+                                 dotdot_de);
 
-       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);
@@ -628,7 +677,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++) {
@@ -639,31 +688,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)
@@ -674,6 +712,45 @@ 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) {
+                               retval = alloc_blocks(fs, &limit, &int_ent,
+                                                     &dx_ent, &int_offset,
+                                                     NULL, outdir, i, &c2,
+                                                     &c3);
+                               if (retval)
+                                       return retval;
+
+                       }
+                       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);
@@ -879,11 +956,10 @@ resort:
        if (retval)
                goto errout;
 
-       free(dir_buf); dir_buf = 0;
-
        if (!fd.compress) {
                /* Calculate the interior nodes */
-               retval = calculate_tree(fs, &outdir, ino, fd.parent);
+               retval = calculate_tree(fs, &outdir, ino, fd.parent,
+                                       fd.dot_de, fd.dotdot_de);
                if (retval)
                        goto errout;
        }