Merge pull request #49 from sirocyl/patch-1
[lwext4.git] / src / ext4_fs.c
index 7a97bc6c352279fe12cf12d80ef66e7f0dad2ce3..8e229ef3f08302d3f847a12cba9028cba1913c25 100644 (file)
  * @brief More complex filesystem functions.
  */
 
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_fs.h"
-#include "ext4_errno.h"
-#include "ext4_blockdev.h"
-#include "ext4_super.h"
-#include "ext4_crc32.h"
-#include "ext4_debug.h"
-#include "ext4_block_group.h"
-#include "ext4_balloc.h"
-#include "ext4_bitmap.h"
-#include "ext4_inode.h"
-#include "ext4_ialloc.h"
-#include "ext4_extent.h"
+#include <ext4_config.h>
+#include <ext4_types.h>
+#include <ext4_misc.h>
+#include <ext4_errno.h>
+#include <ext4_debug.h>
+
+#include <ext4_trans.h>
+#include <ext4_fs.h>
+#include <ext4_blockdev.h>
+#include <ext4_super.h>
+#include <ext4_crc32.h>
+#include <ext4_block_group.h>
+#include <ext4_balloc.h>
+#include <ext4_bitmap.h>
+#include <ext4_inode.h>
+#include <ext4_ialloc.h>
+#include <ext4_extent.h>
 
 #include <string.h>
 
@@ -66,6 +69,8 @@ int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev,
 
        fs->bdev = bdev;
 
+       fs->read_only = read_only;
+
        r = ext4_sb_read(fs->bdev, &fs->sb);
        if (r != EOK)
                return r;
@@ -82,7 +87,7 @@ int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev,
                return r;
 
        if (read_only)
-               fs->read_only = true;
+               fs->read_only = read_only;
 
        /* Compute limits for indirect block levels */
        uint32_t blocks_id = bsize / sizeof(uint32_t);
@@ -288,12 +293,13 @@ static int ext4_fs_init_block_bitmap(struct ext4_block_group_ref *bg_ref)
        struct ext4_bgroup *bg = bg_ref->block_group;
        int rc;
 
-       uint32_t i, bit, bit_max;
+       uint32_t bit, bit_max;
        uint32_t group_blocks;
        uint16_t inode_size = ext4_get16(sb, inode_size);
        uint32_t block_size = ext4_sb_get_block_size(sb);
        uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
 
+       ext4_fsblk_t i;
        ext4_fsblk_t bmp_blk = ext4_bg_get_block_bitmap(bg, sb);
        ext4_fsblk_t bmp_inode = ext4_bg_get_inode_bitmap(bg, sb);
        ext4_fsblk_t inode_table = ext4_bg_get_inode_table_first_block(bg, sb);
@@ -425,7 +431,7 @@ static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref)
        struct ext4_sblock *sb = &bg_ref->fs->sb;
        struct ext4_bgroup *bg = bg_ref->block_group;
 
-       uint32_t inode_size = ext4_get32(sb, inode_size);
+       uint32_t inode_size = ext4_get16(sb, inode_size);
        uint32_t block_size = ext4_sb_get_block_size(sb);
        uint32_t inodes_per_block = block_size / inode_size;
        uint32_t inodes_in_group = ext4_inodes_in_group_cnt(sb, bg_ref->index);
@@ -450,7 +456,7 @@ static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref)
                memset(b.data, 0, block_size);
                ext4_trans_set_block_dirty(b.buf);
 
-               ext4_block_set(bg_ref->fs->bdev, &b);
+               rc = ext4_block_set(bg_ref->fs->bdev, &b);
                if (rc != EOK)
                        return rc;
        }
@@ -799,13 +805,18 @@ int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref)
 void ext4_fs_inode_blocks_init(struct ext4_fs *fs,
                               struct ext4_inode_ref *inode_ref)
 {
-       int i;
        struct ext4_inode *inode = inode_ref->inode;
 
-       for (i = 0; i < EXT4_INODE_BLOCKS; i++)
-               inode->blocks[i] = 0;
+       /* Reset blocks array. For inode which is not directory or file, just
+        * fill in blocks with 0 */
+       switch (ext4_inode_type(&fs->sb, inode_ref->inode)) {
+       case EXT4_INODE_MODE_FILE:
+       case EXT4_INODE_MODE_DIRECTORY:
+               break;
+       default:
+               return;
+       }
 
-       (void)fs;
 #if CONFIG_EXTENT_ENABLE
        /* Initialize extents if needed */
        if (ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) {
@@ -814,6 +825,8 @@ void ext4_fs_inode_blocks_init(struct ext4_fs *fs,
                /* Initialize extent root header */
                ext4_extent_tree_init(inode_ref);
        }
+
+       inode_ref->dirty = true;
 #endif
 }
 
@@ -826,11 +839,17 @@ uint32_t ext4_fs_correspond_inode_mode(int filetype)
                return EXT4_INODE_MODE_FILE;
        case EXT4_DE_SYMLINK:
                return EXT4_INODE_MODE_SOFTLINK;
-       default:
-               /* FIXME: right now we only support 3 file type. */
-               ext4_assert(0);
+       case EXT4_DE_CHRDEV:
+               return EXT4_INODE_MODE_CHARDEV;
+       case EXT4_DE_BLKDEV:
+               return EXT4_INODE_MODE_BLOCKDEV;
+       case EXT4_DE_FIFO:
+               return EXT4_INODE_MODE_FIFO;
+       case EXT4_DE_SOCK:
+               return EXT4_INODE_MODE_SOCKET;
        }
-       return 0;
+       /* FIXME: unsupported filetype */
+       return EXT4_INODE_MODE_FILE;
 }
 
 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
@@ -858,6 +877,8 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
        /* Initialize i-node */
        struct ext4_inode *inode = inode_ref->inode;
 
+       memset(inode, 0, inode_size);
+
        uint32_t mode;
        if (is_dir) {
                /*
@@ -868,6 +889,14 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
 
                mode = 0777;
                mode |= EXT4_INODE_MODE_DIRECTORY;
+       } else if (filetype == EXT4_DE_SYMLINK) {
+               /*
+                * Default symbolic link permissions to be compatible with other systems
+                * 0777 (octal) == rwxrwxrwx
+                */
+
+               mode = 0777;
+               mode |= EXT4_INODE_MODE_SOFTLINK;
        } else {
                /*
                 * Default file permissions to be compatible with other systems
@@ -891,21 +920,11 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
        ext4_inode_set_flags(inode, 0);
        ext4_inode_set_generation(inode, 0);
        if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
-               uint16_t off = offsetof(struct ext4_inode, extra_isize);
-               uint16_t size = sizeof(struct ext4_inode) - off;
-               ext4_inode_set_extra_isize(inode, size);
-       }
-
-       /* Reset blocks array. For symbolic link inode, just
-        * fill in blocks with 0 */
-       if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
-               for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
-                       inode->blocks[i] = 0;
-
-       } else {
-               ext4_fs_inode_blocks_init(fs, inode_ref);
+               uint16_t size = ext4_get16(&fs->sb, want_extra_isize);
+               ext4_inode_set_extra_isize(&fs->sb, inode, size);
        }
 
+       memset(inode->blocks, 0, sizeof(inode->blocks));
        inode_ref->dirty = true;
 
        return EOK;
@@ -1057,7 +1076,7 @@ finish:
  * @return Error code
  */
 static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
-                               uint32_t iblock)
+                               ext4_lblk_t iblock)
 {
        ext4_fsblk_t fblock;
 
@@ -1098,11 +1117,11 @@ static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
 
        /* Compute offsets for the topmost level */
        uint32_t block_offset_in_level =
-           iblock - fs->inode_block_limits[level - 1];
+               (uint32_t)(iblock - fs->inode_block_limits[level - 1]);
        ext4_fsblk_t current_block =
            ext4_inode_get_indirect_block(inode, level - 1);
        uint32_t offset_in_block =
-           block_offset_in_level / fs->inode_blocks_per_level[level - 1];
+           (uint32_t)(block_offset_in_level / fs->inode_blocks_per_level[level - 1]);
 
        /*
         * Navigate through other levels, until we find the block number
@@ -1144,8 +1163,8 @@ static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
 
                /* Visit the next level */
                block_offset_in_level %= fs->inode_blocks_per_level[level];
-               offset_in_block = block_offset_in_level /
-                                 fs->inode_blocks_per_level[level - 1];
+               offset_in_block = (uint32_t)(block_offset_in_level /
+                                 fs->inode_blocks_per_level[level - 1]);
        }
 
        fblock = current_block;
@@ -1161,6 +1180,7 @@ int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
        struct ext4_sblock *sb = &inode_ref->fs->sb;
        uint32_t i;
        int r;
+       bool v;
 
        /* Check flags, if i-node can be truncated */
        if (!ext4_inode_can_truncate(sb, inode_ref->inode))
@@ -1175,7 +1195,7 @@ int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
        if (old_size < new_size)
                return EINVAL;
 
-       bool v;
+       /* For symbolic link which is small enough */
        v = ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK);
        if (v && old_size < sizeof(inode_ref->inode->blocks) &&
            !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
@@ -1188,6 +1208,17 @@ int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
                return EOK;
        }
 
+       i = ext4_inode_type(sb, inode_ref->inode);
+       if (i == EXT4_INODE_MODE_CHARDEV ||
+           i == EXT4_INODE_MODE_BLOCKDEV ||
+           i == EXT4_INODE_MODE_SOCKET) {
+               inode_ref->inode->blocks[0] = 0;
+               inode_ref->inode->blocks[1] = 0;
+
+               inode_ref->dirty = true;
+               return EOK;
+       }
+
        /* Compute how many blocks will be released */
        uint32_t block_size = ext4_sb_get_block_size(sb);
        uint32_t new_blocks_cnt = (uint32_t)((new_size + block_size - 1) / block_size);
@@ -1313,7 +1344,7 @@ int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
 }
 
 static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref,
-                                      uint64_t iblock, ext4_fsblk_t *fblock,
+                                      ext4_lblk_t iblock, ext4_fsblk_t *fblock,
                                       bool extent_create,
                                       bool support_unwritten __unused)
 {
@@ -1371,9 +1402,9 @@ static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref,
                return EIO;
 
        /* Compute offsets for the topmost level */
-       uint32_t blk_off_in_lvl =  iblock - fs->inode_block_limits[l - 1];
+       uint32_t blk_off_in_lvl = (uint32_t)(iblock - fs->inode_block_limits[l - 1]);
        current_block = ext4_inode_get_indirect_block(inode, l - 1);
-       uint32_t off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+       uint32_t off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]);
 
        /* Sparse file */
        if (current_block == 0) {
@@ -1418,7 +1449,7 @@ static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref,
 
                /* Visit the next level */
                blk_off_in_lvl %= fs->inode_blocks_per_level[l];
-               off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+               off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]);
        }
 
        *fblock = current_block;
@@ -1428,7 +1459,7 @@ static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref,
 
 
 int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
-                              uint64_t iblock, ext4_fsblk_t *fblock,
+                              ext4_lblk_t iblock, ext4_fsblk_t *fblock,
                               bool support_unwritten)
 {
        return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock,
@@ -1436,14 +1467,14 @@ int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
 }
 
 int ext4_fs_init_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
-                               uint64_t iblock, ext4_fsblk_t *fblock)
+                               ext4_lblk_t iblock, ext4_fsblk_t *fblock)
 {
        return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock,
                                                   true, true);
 }
 
 static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
-                                      uint32_t iblock, ext4_fsblk_t fblock)
+                                      ext4_lblk_t iblock, ext4_fsblk_t fblock)
 {
        struct ext4_fs *fs = inode_ref->fs;
 
@@ -1481,10 +1512,10 @@ static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
        uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
 
        /* Compute offsets for the topmost level */
-       uint32_t blk_off_in_lvl =  iblock - fs->inode_block_limits[l - 1];
+       uint32_t blk_off_in_lvl = (uint32_t)(iblock - fs->inode_block_limits[l - 1]);
        ext4_fsblk_t current_block =
                        ext4_inode_get_indirect_block(inode_ref->inode, l - 1);
-       uint32_t off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+       uint32_t off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]);
 
        ext4_fsblk_t new_blk;
 
@@ -1601,7 +1632,7 @@ static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
 
                /* Visit the next level */
                blk_off_in_lvl %= fs->inode_blocks_per_level[l];
-               off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+               off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]);
        }
 
        return EOK;
@@ -1609,7 +1640,7 @@ static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
 
 
 int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref,
-                             ext4_fsblk_t *fblock, uint32_t *iblock)
+                             ext4_fsblk_t *fblock, ext4_lblk_t *iblock)
 {
 #if CONFIG_EXTENT_ENABLE
        /* Handle extents separately */
@@ -1624,6 +1655,8 @@ int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref,
 
                rc = ext4_extent_get_blocks(inode_ref, *iblock, 1,
                                                &current_fsblk, true, NULL);
+               if (rc != EOK)
+                       return rc;
 
                *fblock = current_fsblk;
                ext4_assert(*fblock);