ext4: Scan all directory blocks for space when inserting a new entry
authorStefan Brüns <stefan.bruens@rwth-aachen.de>
Tue, 6 Sep 2016 02:36:44 +0000 (04:36 +0200)
committerTom Rini <trini@konsulko.com>
Fri, 23 Sep 2016 13:02:36 +0000 (09:02 -0400)
Previously, only the last directory block was scanned for available space.
Instead, scan all blocks back to front, and if no sufficient space is
found, eventually append a new block.
Blocks are only appended if the directory does not use extents or the new
block would require insertion of indirect blocks, as the old code does.

Signed-off-by: Stefan Brüns <stefan.bruens@rwth-aachen.de>
Reviewed-by: Lukasz Majewski <l.majewski@samsung.com>
fs/ext4/ext4_common.c

index eebca7de91d11dce6d97e77639ad5008b62f5e16..3937e978e179d0840fb959b902c6df49ad953a0c 100644 (file)
@@ -370,14 +370,10 @@ int ext4fs_update_parent_dentry(char *filename, int file_type)
 {
        unsigned int *zero_buffer = NULL;
        char *root_first_block_buffer = NULL;
-       int direct_blk_idx;
-       long int root_blknr;
+       int blk_idx;
        long int first_block_no_of_root = 0;
-       long int previous_blknr = -1;
        int totalbytes = 0;
-       short int padding_factor = 0;
        unsigned int new_entry_byte_reqd;
-       unsigned int last_entry_dirlen;
        int sizeof_void_space = 0;
        int templength = 0;
        int inodeno = -1;
@@ -389,6 +385,7 @@ int ext4fs_update_parent_dentry(char *filename, int file_type)
        uint32_t new_blk_no;
        uint32_t new_size;
        uint32_t new_blockcnt;
+       uint32_t directory_blocks;
 
        zero_buffer = zalloc(fs->blksz);
        if (!zero_buffer) {
@@ -401,19 +398,18 @@ int ext4fs_update_parent_dentry(char *filename, int file_type)
                printf("No Memory\n");
                return -1;
        }
+       new_entry_byte_reqd = ROUND(strlen(filename) +
+                                   sizeof(struct ext2_dirent), 4);
 restart:
+       directory_blocks = le32_to_cpu(g_parent_inode->size) >>
+               LOG2_BLOCK_SIZE(ext4fs_root);
+       blk_idx = directory_blocks - 1;
 
+restart_read:
        /* read the block no allocated to a file */
-       for (direct_blk_idx = 0; direct_blk_idx < INDIRECT_BLOCKS;
-            direct_blk_idx++) {
-               root_blknr = read_allocated_block(g_parent_inode,
-                                                 direct_blk_idx);
-               if (root_blknr == 0) {
-                       first_block_no_of_root = previous_blknr;
-                       break;
-               }
-               previous_blknr = root_blknr;
-       }
+       first_block_no_of_root = read_allocated_block(g_parent_inode, blk_idx);
+       if (first_block_no_of_root <= 0)
+               goto fail;
 
        status = ext4fs_devread((lbaint_t)first_block_no_of_root
                                * fs->sect_perblk,
@@ -425,42 +421,33 @@ restart:
                goto fail;
        dir = (struct ext2_dirent *)root_first_block_buffer;
        totalbytes = 0;
+
        while (le16_to_cpu(dir->direntlen) > 0) {
-               /*
-                * blocksize-totalbytes because last directory length
-                * i.e. dir->direntlen is free availble space in the
-                * block that means  it is a last entry of directory
-                * entry
-                */
+               unsigned short used_len = ROUND(dir->namelen +
+                   sizeof(struct ext2_dirent), 4);
 
-               /* traversing the each directory entry */
+               /* last entry of block */
                if (fs->blksz - totalbytes == le16_to_cpu(dir->direntlen)) {
-                       if (strlen(filename) % 4 != 0)
-                               padding_factor = 4 - (strlen(filename) % 4);
-
-                       new_entry_byte_reqd = strlen(filename) +
-                           sizeof(struct ext2_dirent) + padding_factor;
-                       padding_factor = 0;
-                       /*
-                        * update last directory entry length to its
-                        * length because we are creating new directory
-                        * entry
-                        */
-                       if (dir->namelen % 4 != 0)
-                               padding_factor = 4 - (dir->namelen % 4);
 
-                       last_entry_dirlen = dir->namelen +
-                           sizeof(struct ext2_dirent) + padding_factor;
-                       if ((fs->blksz - totalbytes - last_entry_dirlen) <
-                               new_entry_byte_reqd) {
-                               printf("Last Block Full:Allocate new block\n");
+                       /* check if new entry fits */
+                       if ((used_len + new_entry_byte_reqd) <=
+                           le16_to_cpu(dir->direntlen)) {
+                               dir->direntlen = cpu_to_le16(used_len);
+                               break;
+                       } else {
+                               if (blk_idx > 0) {
+                                       printf("Block full, trying previous\n");
+                                       blk_idx--;
+                                       goto restart_read;
+                               }
+                               printf("All blocks full: Allocate new\n");
 
                                if (le32_to_cpu(g_parent_inode->flags) &
                                                EXT4_EXTENTS_FL) {
                                        printf("Directory uses extents\n");
                                        goto fail;
                                }
-                               if (direct_blk_idx == INDIRECT_BLOCKS - 1) {
+                               if (directory_blocks >= INDIRECT_BLOCKS) {
                                        printf("Directory exceeds limit\n");
                                        goto fail;
                                }
@@ -470,7 +457,8 @@ restart:
                                        goto fail;
                                }
                                put_ext4((uint64_t)new_blk_no * fs->blksz, zero_buffer, fs->blksz);
-                               g_parent_inode->b.blocks.dir_blocks[direct_blk_idx] =
+                               g_parent_inode->b.blocks.
+                                       dir_blocks[directory_blocks] =
                                        cpu_to_le32(new_blk_no);
 
                                new_size = le32_to_cpu(g_parent_inode->size);
@@ -487,8 +475,6 @@ restart:
                                        goto fail;
                                goto restart;
                        }
-                       dir->direntlen = cpu_to_le16(last_entry_dirlen);
-                       break;
                }
 
                templength = le16_to_cpu(dir->direntlen);