diff --git a/demos/generic/main.c b/demos/generic/main.c index ab24bd7..e6e1f39 100644 --- a/demos/generic/main.c +++ b/demos/generic/main.c @@ -36,21 +36,28 @@ #include #include - - char input_name[128] = "ext2"; /**@brief Read-write size*/ static int rw_szie = 1024; /**@brief Read-write size*/ -static int rw_count = 1024; +static int rw_count = 10000; /**@brief Directory test count*/ static int dir_cnt = 10; +/**@brief Static or dynamic cache mode*/ static bool cache_mode = false; +/**@brief Cleanup after test.*/ +static bool cleanup_flag = false; + +/**@brief Block device stats.*/ +static bool bstat = false; + +/**@brief Superblock stats.*/ +static bool sbstat = false; /**@brief File write buffer*/ static uint8_t *wr_buff; @@ -70,9 +77,12 @@ Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) \n\ Usage: \n\ -i - input file (default = ext2) \n\ -rws - single R/W size (default = 1024) \n\ - -rwc - R/W count (default = 1024) \n\ - -cache - 0 static, 1 dynamic (default = 0) \n\ - -dirs - directory test count (default = 0) \n\ + -rwc - R/W count (default = 10000) \n\ + -cache - 0 static, 1 dynamic (default = 0) \n\ + -dirs - directory test count (default = 10) \n\ + -clean - clean up after test \n\ + -bstat - block device stats \n\ + -sbstat - superblock stats \n\ \n"; static char* entry_to_str(uint8_t type) @@ -192,10 +202,6 @@ static bool dir_test(int len) int i; char path[64]; - printf("Remove directory /mp/dir1\n"); - ext4_dir_rm("/mp/dir1"); - - printf("Directory create: /mp/dir1\n"); r = ext4_dir_mk("/mp/dir1"); if(r != EOK){ @@ -214,31 +220,17 @@ static bool dir_test(int len) } } - printf("Add directories to: /mp/dir1\n"); - for (i = 0; i < len; ++i) { - sprintf(path, "/mp/dir1/d%d", i); - r = ext4_dir_mk(path); - if(r != EOK){ - printf("Unable to create directory in directory: /mp/dir1\n"); - return false; - } - } - - printf("Add file directories in: /mp/dir1\n"); - - for (i = 0; i < len; ++i) { - sprintf(path, "/mp/dir1/d%d/ff", i); - r = ext4_fopen(&f, path, "wb"); - if(r != EOK){ - printf("Unable to create file in directory: /mp/dir1\n"); - return false; - } - } - dir_ls("/mp/dir1"); return true; } +static void cleanup(void) +{ + ext4_fremove("/mp/hello.txt"); + ext4_fremove("/mp/test1"); + ext4_dir_rm("/mp/dir1"); +} + int main(int argc, char **argv) { int option_index = 0; @@ -254,11 +246,14 @@ int main(int argc, char **argv) {"rws", required_argument, 0, 'b'}, {"rwc", required_argument, 0, 'c'}, {"cache", required_argument, 0, 'd'}, - {"dirs", required_argument, 0, 'e'}, + {"dirs", required_argument, 0, 'e'}, + {"clean", no_argument, 0, 'f'}, + {"bstat", no_argument, 0, 'g'}, + {"sbstat", no_argument, 0, 'h'}, {0, 0, 0, 0} }; - while(-1 != (c = getopt_long (argc, argv, "a:b:c:d:e:", long_options, &option_index))) { + while(-1 != (c = getopt_long (argc, argv, "a:b:c:d:e:fgh", long_options, &option_index))) { switch(c){ case 'a': @@ -276,6 +271,15 @@ int main(int argc, char **argv) case 'e': dir_cnt = atoi(optarg); break; + case 'f': + cleanup_flag = true; + break; + case 'g': + bstat = true; + break; + case 'h': + sbstat = true; + break; default: printf(usage); return EXIT_FAILURE; @@ -323,12 +327,14 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - dir_test(dir_cnt); + cleanup(); + + if(sbstat) + mp_stats(); - ext4_fremove("/mp/hello.txt"); - ext4_fremove("/mp/test1"); - mp_stats(); - dir_ls("/mp/"); + + dir_ls("/mp/"); + dir_test(dir_cnt); /*Add hello world file.*/ r = ext4_fopen(&f, "/mp/hello.txt", "wb"); @@ -390,16 +396,20 @@ int main(int argc, char **argv) } printf("OK\n"); - r = ext4_fclose(&f); - - mp_stats(); dir_ls("/mp/"); - block_stats(); - r = ext4_umount("/mp/"); + if(sbstat) + mp_stats(); + + if(bstat) + block_stats(); + if(cleanup_flag) + cleanup(); + + r = ext4_umount("/mp/"); printf("Test finish: OK\n"); return EXIT_SUCCESS; diff --git a/ext_images.7z b/ext_images.7z index 375a3ba..c7a629c 100644 Binary files a/ext_images.7z and b/ext_images.7z differ diff --git a/lwext4/ext4_block_group.c b/lwext4/ext4_block_group.c index d5def32..8da606c 100644 --- a/lwext4/ext4_block_group.c +++ b/lwext4/ext4_block_group.c @@ -43,11 +43,48 @@ #include +static uint16_t const crc16_tab[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len) { - // TODO - return 0; + while (len--) + + crc = (((crc >> 8) & 0xffU) ^ + crc16_tab[(crc ^ *buffer++) & 0xffU]) & 0x0000ffffU; + return crc; } /** diff --git a/lwext4/ext4_config.h b/lwext4/ext4_config.h index 45d353c..5080256 100644 --- a/lwext4/ext4_config.h +++ b/lwext4/ext4_config.h @@ -44,12 +44,12 @@ /**@brief Enable directory indexing feature (EXT3 feature)*/ #ifndef CONFIG_DIR_INDEX_ENABLE -#define CONFIG_DIR_INDEX_ENABLE 0 +#define CONFIG_DIR_INDEX_ENABLE 1 #endif /**@brief Enable extents feature (EXT4 feature)*/ #ifndef CONFIG_EXTENT_ENABLE -#define CONFIG_EXTENT_ENABLE 0 +#define CONFIG_EXTENT_ENABLE 1 #endif diff --git a/lwext4/ext4_debug.h b/lwext4/ext4_debug.h index 5941462..5a8abb7 100644 --- a/lwext4/ext4_debug.h +++ b/lwext4/ext4_debug.h @@ -72,6 +72,10 @@ /**@brief Debug mask: ext4_bcache.c*/ #define EXT4_DEBUG_BCACHE (1 << 9) +/**@brief Debug mask: ext4_extents.c*/ +#define EXT4_DEBUG_EXTENTS (1 << 10) + + /**@brief All debug printf enabled.*/ #define EXT4_DEBUG_ALL (0xFFFFFFFF) diff --git a/lwext4/ext4_extent.c b/lwext4/ext4_extent.c new file mode 100644 index 0000000..ca1a636 --- /dev/null +++ b/lwext4/ext4_extent.c @@ -0,0 +1,935 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_extent.c + * @brief More complex filesystem functions. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +uint32_t ext4_extent_get_first_block(struct ext4_extent *extent) +{ + return to_le32(extent->first_block); +} + + +void ext4_extent_set_first_block(struct ext4_extent *extent, uint32_t iblock) +{ + extent->first_block = to_le32(iblock); +} + + +uint16_t ext4_extent_get_block_count(struct ext4_extent *extent) +{ + return to_le16(extent->block_count); +} + + +void ext4_extent_set_block_count(struct ext4_extent *extent, uint16_t count) +{ + extent->block_count = to_le16(count); +} + + +uint64_t ext4_extent_get_start(struct ext4_extent *extent) +{ + return ((uint64_t)to_le16(extent->start_hi)) << 32 | + ((uint64_t)to_le32(extent->start_lo)); +} + + +void ext4_extent_set_start(struct ext4_extent *extent, uint64_t fblock) +{ + extent->start_lo = to_le32((fblock << 32) >> 32); + extent->start_hi = to_le16((uint16_t)(fblock >> 32)); +} + + +uint32_t ext4_extent_index_get_first_block(struct ext4_extent_index *index) +{ + return to_le32(index->first_block); +} + + +void ext4_extent_index_set_first_block(struct ext4_extent_index *index, + uint32_t iblock) +{ + index->first_block = to_le32(iblock); +} + + +uint64_t ext4_extent_index_get_leaf(struct ext4_extent_index *index) +{ + return ((uint64_t) to_le16(index->leaf_hi)) << 32 | + ((uint64_t)to_le32(index->leaf_lo)); +} + +void ext4_extent_index_set_leaf(struct ext4_extent_index *index, + uint64_t fblock) +{ + index->leaf_lo = to_le32((fblock << 32) >> 32); + index->leaf_hi = to_le16((uint16_t) (fblock >> 32)); +} + + +uint16_t ext4_extent_header_get_magic(struct ext4_extent_header *header) +{ + return to_le16(header->magic); +} + + +void ext4_extent_header_set_magic(struct ext4_extent_header *header, + uint16_t magic) +{ + header->magic = to_le16(magic); +} + + +uint16_t ext4_extent_header_get_entries_count(struct ext4_extent_header *header) +{ + return to_le16(header->entries_count); +} + + +void ext4_extent_header_set_entries_count(struct ext4_extent_header *header, + uint16_t count) +{ + header->entries_count = to_le16(count); +} + + +uint16_t ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header) +{ + return to_le16(header->max_entries_count); +} + + +void ext4_extent_header_set_max_entries_count(struct ext4_extent_header *header, + uint16_t max_count) +{ + header->max_entries_count = to_le16(max_count); +} + + +uint16_t ext4_extent_header_get_depth(struct ext4_extent_header *header) +{ + return to_le16(header->depth); +} + + +void ext4_extent_header_set_depth(struct ext4_extent_header *header, + uint16_t depth) +{ + header->depth = to_le16(depth); +} + + +uint32_t ext4_extent_header_get_generation(struct ext4_extent_header *header) +{ + return to_le32(header->generation); +} + + +void ext4_extent_header_set_generation(struct ext4_extent_header *header, + uint32_t generation) +{ + header->generation = to_le32(generation); +} + +/**@brief Binary search in extent index node. + * @param header Extent header of index node + * @param index Output value - found index will be set here + * @param iblock Logical block number to find in index node */ +static void ext4_extent_binsearch_idx(struct ext4_extent_header *header, + struct ext4_extent_index **index, uint32_t iblock) +{ + struct ext4_extent_index *r; + struct ext4_extent_index *l; + struct ext4_extent_index *m; + + uint16_t entries_count = + ext4_extent_header_get_entries_count(header); + + /* Initialize bounds */ + l = EXT4_EXTENT_FIRST_INDEX(header) + 1; + r = EXT4_EXTENT_FIRST_INDEX(header) + entries_count - 1; + + /* Do binary search */ + while (l <= r) { + m = l + (r - l) / 2; + uint32_t first_block = ext4_extent_index_get_first_block(m); + + if (iblock < first_block) + r = m - 1; + else + l = m + 1; + } + + /* Set output value */ + *index = l - 1; +} + +/**@brief Binary search in extent leaf node. + * @param header Extent header of leaf node + * @param extent Output value - found extent will be set here, + * or NULL if node is empty + * @param iblock Logical block number to find in leaf node */ +static void ext4_extent_binsearch(struct ext4_extent_header *header, + struct ext4_extent **extent, uint32_t iblock) +{ + struct ext4_extent *r; + struct ext4_extent *l; + struct ext4_extent *m; + + uint16_t entries_count = + ext4_extent_header_get_entries_count(header); + + if (entries_count == 0) { + /* this leaf is empty */ + *extent = NULL; + return; + } + + /* Initialize bounds */ + l = EXT4_EXTENT_FIRST(header) + 1; + r = EXT4_EXTENT_FIRST(header) + entries_count - 1; + + /* Do binary search */ + while (l <= r) { + m = l + (r - l) / 2; + uint32_t first_block = ext4_extent_get_first_block(m); + + if (iblock < first_block) + r = m - 1; + else + l = m + 1; + } + + /* Set output value */ + *extent = l - 1; +} + + +int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock, + uint32_t *fblock) +{ + /* Compute bound defined by i-node size */ + uint64_t inode_size = + ext4_inode_get_size(&inode_ref->fs->sb, inode_ref->inode); + + uint32_t block_size = + ext4_sb_get_block_size(&inode_ref->fs->sb); + + uint32_t last_idx = (inode_size - 1) / block_size; + + /* Check if requested iblock is not over size of i-node */ + if (iblock > last_idx) { + *fblock = 0; + return EOK; + } + + struct ext4_block block; + block.lb_id = 0; + + /* Walk through extent tree */ + struct ext4_extent_header *header = + ext4_inode_get_extent_header(inode_ref->inode); + + while (ext4_extent_header_get_depth(header) != 0) { + /* Search index in node */ + struct ext4_extent_index *index; + ext4_extent_binsearch_idx(header, &index, iblock); + + /* Load child node and set values for the next iteration */ + uint64_t child = ext4_extent_index_get_leaf(index); + + if (block.lb_id) + ext4_block_set(inode_ref->fs->bdev, &block); + + + int rc = ext4_block_get(inode_ref->fs->bdev, &block, child); + if (rc != EOK) + return rc; + + header = (struct ext4_extent_header *)block.data; + } + + /* Search extent in the leaf block */ + struct ext4_extent* extent = NULL; + ext4_extent_binsearch(header, &extent, iblock); + + /* Prevent empty leaf */ + if (extent == NULL) { + *fblock = 0; + } else { + /* Compute requested physical block address */ + uint32_t phys_block; + uint32_t first = ext4_extent_get_first_block(extent); + phys_block = ext4_extent_get_start(extent) + iblock - first; + + *fblock = phys_block; + } + + /* Cleanup */ + if (block.lb_id) + ext4_block_set(inode_ref->fs->bdev, &block); + + return EOK; +} + +/**@brief Find extent for specified iblock. + * This function is used for finding block in the extent tree with + * saving the path through the tree for possible future modifications. + * @param inode_ref I-node to read extent tree from + * @param iblock Iblock to find extent for + * @param ret_path Output value for loaded path from extent tree + * @return Error code */ +static int ext4_extent_find_extent(struct ext4_inode_ref *inode_ref, + uint32_t iblock, struct ext4_extent_path **ret_path) +{ + struct ext4_extent_header *eh = + ext4_inode_get_extent_header(inode_ref->inode); + + uint16_t depth = ext4_extent_header_get_depth(eh); + uint16_t i; + struct ext4_extent_path *tmp_path; + + /* Added 2 for possible tree growing */ + tmp_path = malloc(sizeof(struct ext4_extent_path) * (depth + 2)); + if (tmp_path == NULL) + return ENOMEM; + + /* Initialize structure for algorithm start */ + tmp_path[0].block = inode_ref->block; + tmp_path[0].header = eh; + + /* Walk through the extent tree */ + uint16_t pos = 0; + int rc; + while (ext4_extent_header_get_depth(eh) != 0) { + /* Search index in index node by iblock */ + ext4_extent_binsearch_idx(tmp_path[pos].header, + &tmp_path[pos].index, iblock); + + tmp_path[pos].depth = depth; + tmp_path[pos].extent = NULL; + + ext4_assert(tmp_path[pos].index != 0); + + /* Load information for the next iteration */ + uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index); + + struct ext4_block block; + rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock); + if (rc != EOK) + goto cleanup; + + pos++; + + eh = (struct ext4_extent_header *)block.data; + tmp_path[pos].block = block; + tmp_path[pos].header = eh; + } + + tmp_path[pos].depth = 0; + tmp_path[pos].extent = NULL; + tmp_path[pos].index = NULL; + + /* Find extent in the leaf node */ + ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock); + *ret_path = tmp_path; + + return EOK; + +cleanup: + /* + * Put loaded blocks + * From 1: 0 is a block with inode data + */ + for (i = 1; i < tmp_path->depth; ++i) { + if (tmp_path[i].block.lb_id) + ext4_block_set(inode_ref->fs->bdev, &tmp_path[i].block); + } + + /* Destroy temporary data structure */ + free(tmp_path); + + return rc; +} + +/**@brief Release extent and all data blocks covered by the extent. + * @param inode_ref I-node to release extent and block from + * @param extent Extent to release + * @return Error code */ +static int ext4_extent_release(struct ext4_inode_ref *inode_ref, + struct ext4_extent *extent) +{ + /* Compute number of the first physical block to release */ + uint64_t start = ext4_extent_get_start(extent); + uint16_t block_count = ext4_extent_get_block_count(extent); + + return ext4_balloc_free_blocks(inode_ref, start, block_count); +} + +/** Recursively release the whole branch of the extent tree. + * For each entry of the node release the subbranch and finally release + * the node. In the leaf node all extents will be released. + * @param inode_ref I-node where the branch is released + * @param index Index in the non-leaf node to be released + * with the whole subtree + * @return Error code */ +static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref, + struct ext4_extent_index *index) +{ + uint32_t fblock = ext4_extent_index_get_leaf(index); + uint32_t i; + struct ext4_block block; + int rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock); + if (rc != EOK) + return rc; + + struct ext4_extent_header *header = (void *)block.data; + + if (ext4_extent_header_get_depth(header)) { + /* The node is non-leaf, do recursion */ + struct ext4_extent_index *idx = EXT4_EXTENT_FIRST_INDEX(header); + + /* Release all subbranches */ + for (i = 0; i < ext4_extent_header_get_entries_count(header); + ++i, ++idx) { + rc = ext4_extent_release_branch(inode_ref, idx); + if (rc != EOK) + return rc; + } + } else { + /* Leaf node reached */ + struct ext4_extent *ext = EXT4_EXTENT_FIRST(header); + + /* Release all extents and stop recursion */ + for (i = 0; i < ext4_extent_header_get_entries_count(header); + ++i, ++ext) { + rc = ext4_extent_release(inode_ref, ext); + if (rc != EOK) + return rc; + } + } + + /* Release data block where the node was stored */ + + rc = ext4_block_set(inode_ref->fs->bdev, &block); + if (rc != EOK) + return rc; + + ext4_balloc_free_block(inode_ref, fblock); + + return EOK; +} + + +int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref, + uint32_t iblock_from) +{ + /* Find the first extent to modify */ + struct ext4_extent_path *path; + uint16_t i; + int rc = ext4_extent_find_extent(inode_ref, iblock_from, &path); + if (rc != EOK) + return rc; + + /* Jump to last item of the path (extent) */ + struct ext4_extent_path *path_ptr = path; + while (path_ptr->depth != 0) + path_ptr++; + + ext4_assert(path_ptr->extent != NULL); + + /* First extent maybe released partially */ + uint32_t first_iblock = + ext4_extent_get_first_block(path_ptr->extent); + uint32_t first_fblock = + ext4_extent_get_start(path_ptr->extent) + iblock_from - first_iblock; + + uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent); + + uint16_t delete_count = block_count - + (ext4_extent_get_start(path_ptr->extent) - first_fblock); + + /* Release all blocks */ + rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count); + if (rc != EOK) + goto cleanup; + + /* Correct counter */ + block_count -= delete_count; + ext4_extent_set_block_count(path_ptr->extent, block_count); + + /* Initialize the following loop */ + uint16_t entries = + ext4_extent_header_get_entries_count(path_ptr->header); + struct ext4_extent *tmp_ext = path_ptr->extent + 1; + struct ext4_extent *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries; + + /* If first extent empty, release it */ + if (block_count == 0) + entries--; + + /* Release all successors of the first extent in the same node */ + while (tmp_ext < stop_ext) { + first_fblock = ext4_extent_get_start(tmp_ext); + delete_count = ext4_extent_get_block_count(tmp_ext); + + rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count); + if (rc != EOK) + goto cleanup; + + entries--; + tmp_ext++; + } + + ext4_extent_header_set_entries_count(path_ptr->header, entries); + path_ptr->block.dirty = true; + + /* If leaf node is empty, parent entry must be modified */ + bool remove_parent_record = false; + + /* Don't release root block (including inode data) !!! */ + if ((path_ptr != path) && (entries == 0)) { + rc = ext4_balloc_free_block(inode_ref, path_ptr->block.lb_id); + if (rc != EOK) + goto cleanup; + + remove_parent_record = true; + } + + /* Jump to the parent */ + --path_ptr; + + /* Release all successors in all tree levels */ + while (path_ptr >= path) { + entries = ext4_extent_header_get_entries_count(path_ptr->header); + struct ext4_extent_index *index = path_ptr->index + 1; + struct ext4_extent_index *stop = + EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries; + + /* Correct entries count because of changes in the previous iteration */ + if (remove_parent_record) + entries--; + + /* Iterate over all entries and release the whole subtrees */ + while (index < stop) { + rc = ext4_extent_release_branch(inode_ref, index); + if (rc != EOK) + goto cleanup; + + ++index; + --entries; + } + + ext4_extent_header_set_entries_count(path_ptr->header, entries); + path_ptr->block.dirty = true; + + /* Free the node if it is empty */ + if ((entries == 0) && (path_ptr != path)) { + rc = ext4_balloc_free_block(inode_ref, path_ptr->block.lb_id); + if (rc != EOK) + goto cleanup; + + /* Mark parent to be checked */ + remove_parent_record = true; + } else + remove_parent_record = false; + + --path_ptr; + } + +cleanup: + /* + * Put loaded blocks + * starting from 1: 0 is a block with inode data + */ + for (i = 1; i <= path->depth; ++i) { + if (path[i].block.lb_id) + ext4_block_set(inode_ref->fs->bdev, &path[i].block); + } + + /* Destroy temporary data structure */ + free(path); + + return rc; +} + +/**@brief Append new extent to the i-node and do some splitting if necessary. + * @param inode_ref I-node to append extent to + * @param path Path in the extent tree for possible splitting + * @param last_path_item Input/output parameter for pointer to the last + * valid item in the extent tree path + * @param iblock Logical index of block to append extent for + * @return Error code */ +static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, uint32_t iblock) +{ + struct ext4_extent_path *path_ptr = path + path->depth; + + uint32_t block_size = + ext4_sb_get_block_size(&inode_ref->fs->sb); + + /* Start splitting */ + while (path_ptr > path) { + uint16_t entries = + ext4_extent_header_get_entries_count(path_ptr->header); + uint16_t limit = + ext4_extent_header_get_max_entries_count(path_ptr->header); + + if (entries == limit) { + /* Full node - allocate block for new one */ + uint32_t fblock; + int rc = ext4_balloc_alloc_block(inode_ref, &fblock); + if (rc != EOK) + return rc; + + struct ext4_block block; + rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock); + if (rc != EOK) { + ext4_balloc_free_block(inode_ref, fblock); + return rc; + } + + /* Put back not modified old block */ + ext4_block_set(inode_ref->fs->bdev, &path_ptr->block); + + /* Initialize newly allocated block and remember it */ + memset(block.data, 0, block_size); + path_ptr->block = block; + + /* Update pointers in extent path structure */ + path_ptr->header = (void *)block.data; + if (path_ptr->depth) { + path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header); + ext4_extent_index_set_first_block(path_ptr->index, iblock); + ext4_extent_index_set_leaf(path_ptr->index, + (path_ptr + 1)->block.lb_id); + limit = (block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent_index); + } else { + path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header); + ext4_extent_set_first_block(path_ptr->extent, iblock); + limit = (block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent); + } + + /* Initialize on-disk structure (header) */ + ext4_extent_header_set_entries_count(path_ptr->header, 1); + ext4_extent_header_set_max_entries_count(path_ptr->header, limit); + ext4_extent_header_set_magic(path_ptr->header, EXT4_EXTENT_MAGIC); + ext4_extent_header_set_depth(path_ptr->header, path_ptr->depth); + ext4_extent_header_set_generation(path_ptr->header, 0); + + path_ptr->block.dirty = true; + + /* Jump to the preceeding item */ + path_ptr--; + } else { + /* Node with free space */ + if (path_ptr->depth) { + path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries; + ext4_extent_index_set_first_block(path_ptr->index, iblock); + ext4_extent_index_set_leaf(path_ptr->index, + (path_ptr + 1)->block.lb_id); + } else { + path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries; + ext4_extent_set_first_block(path_ptr->extent, iblock); + } + + ext4_extent_header_set_entries_count(path_ptr->header, entries + 1); + path_ptr->block.dirty = true; + + /* No more splitting needed */ + return EOK; + } + } + + ext4_assert(path_ptr == path); + + /* Should be the root split too? */ + + uint16_t entries = ext4_extent_header_get_entries_count(path->header); + uint16_t limit = ext4_extent_header_get_max_entries_count(path->header); + + if (entries == limit) { + uint32_t new_fblock; + int rc = ext4_balloc_alloc_block(inode_ref, &new_fblock); + if (rc != EOK) + return rc; + + struct ext4_block block; + rc = ext4_block_get(inode_ref->fs->bdev, &block, new_fblock); + if (rc != EOK) + return rc; + + /* Initialize newly allocated block */ + memset(block.data, 0, block_size); + + /* Move data from root to the new block */ + memcpy(block.data, inode_ref->inode->blocks, + EXT4_INODE_BLOCKS * sizeof(uint32_t)); + + /* Data block is initialized */ + + struct ext4_block *root_block = &path->block; + uint16_t root_depth = path->depth; + struct ext4_extent_header *root_header = path->header; + + /* Make space for tree growing */ + struct ext4_extent_path *new_root = path; + struct ext4_extent_path *old_root = path + 1; + + size_t nbytes = sizeof(struct ext4_extent_path) * (path->depth + 1); + memmove(old_root, new_root, nbytes); + memset(new_root, 0, sizeof(struct ext4_extent_path)); + + /* Update old root structure */ + old_root->block = block; + old_root->header = (struct ext4_extent_header *)block.data; + + /* Add new entry and update limit for entries */ + if (old_root->depth) { + limit = (block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent_index); + old_root->index = EXT4_EXTENT_FIRST_INDEX(old_root->header) + entries; + ext4_extent_index_set_first_block(old_root->index, iblock); + ext4_extent_index_set_leaf(old_root->index, + (old_root + 1)->block.lb_id); + old_root->extent = NULL; + } else { + limit = (block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent); + old_root->extent = EXT4_EXTENT_FIRST(old_root->header) + entries; + ext4_extent_set_first_block(old_root->extent, iblock); + old_root->index = NULL; + } + + ext4_extent_header_set_entries_count(old_root->header, entries + 1); + ext4_extent_header_set_max_entries_count(old_root->header, limit); + + old_root->block.dirty = true; + + /* Re-initialize new root metadata */ + new_root->depth = root_depth + 1; + new_root->block = *root_block; + new_root->header = root_header; + new_root->extent = NULL; + new_root->index = EXT4_EXTENT_FIRST_INDEX(new_root->header); + + ext4_extent_header_set_depth(new_root->header, new_root->depth); + + /* Create new entry in root */ + ext4_extent_header_set_entries_count(new_root->header, 1); + ext4_extent_index_set_first_block(new_root->index, 0); + ext4_extent_index_set_leaf(new_root->index, new_fblock); + + new_root->block.dirty = true; + } else { + if (path->depth) { + path->index = EXT4_EXTENT_FIRST_INDEX(path->header) + entries; + ext4_extent_index_set_first_block(path->index, iblock); + ext4_extent_index_set_leaf(path->index, (path + 1)->block.lb_id); + } else { + path->extent = EXT4_EXTENT_FIRST(path->header) + entries; + ext4_extent_set_first_block(path->extent, iblock); + } + + ext4_extent_header_set_entries_count(path->header, entries + 1); + path->block.dirty = true; + } + + return EOK; +} + + +int ext4_extent_append_block(struct ext4_inode_ref *inode_ref, + uint32_t *iblock, uint32_t *fblock, bool update_size) +{ + uint16_t i; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode); + uint32_t block_size = ext4_sb_get_block_size(sb); + + /* Calculate number of new logical block */ + uint32_t new_block_idx = 0; + if (inode_size > 0) { + if ((inode_size % block_size) != 0) + inode_size += block_size - (inode_size % block_size); + + new_block_idx = inode_size / block_size; + } + + /* Load the nearest leaf (with extent) */ + struct ext4_extent_path *path; + int rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path); + if (rc != EOK) + return rc; + + /* Jump to last item of the path (extent) */ + struct ext4_extent_path *path_ptr = path; + while (path_ptr->depth != 0) + path_ptr++; + + /* Add new extent to the node if not present */ + if (path_ptr->extent == NULL) + goto append_extent; + + uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent); + uint16_t block_limit = (1 << 15); + + uint32_t phys_block = 0; + if (block_count < block_limit) { + /* There is space for new block in the extent */ + if (block_count == 0) { + /* Existing extent is empty */ + rc = ext4_balloc_alloc_block(inode_ref, &phys_block); + if (rc != EOK) + goto finish; + + /* Initialize extent */ + ext4_extent_set_first_block(path_ptr->extent, new_block_idx); + ext4_extent_set_start(path_ptr->extent, phys_block); + ext4_extent_set_block_count(path_ptr->extent, 1); + + /* Update i-node */ + if (update_size) { + ext4_inode_set_size(inode_ref->inode, inode_size + block_size); + inode_ref->dirty = true; + } + + path_ptr->block.dirty = true; + + goto finish; + } else { + /* Existing extent contains some blocks */ + phys_block = ext4_extent_get_start(path_ptr->extent); + phys_block += ext4_extent_get_block_count(path_ptr->extent); + + /* Check if the following block is free for allocation */ + bool free; + rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free); + if (rc != EOK) + goto finish; + + if (!free) { + /* Target is not free, new block must be appended to new extent */ + goto append_extent; + } + + /* Update extent */ + ext4_extent_set_block_count(path_ptr->extent, block_count + 1); + + /* Update i-node */ + if (update_size) { + ext4_inode_set_size(inode_ref->inode, inode_size + block_size); + inode_ref->dirty = true; + } + + path_ptr->block.dirty = true; + + goto finish; + } + } + + +append_extent: + /* Append new extent to the tree */ + phys_block = 0; + + /* Allocate new data block */ + rc = ext4_balloc_alloc_block(inode_ref, &phys_block); + if (rc != EOK) + goto finish; + + /* Append extent for new block (includes tree splitting if needed) */ + rc = ext4_extent_append_extent(inode_ref, path, new_block_idx); + if (rc != EOK) { + ext4_balloc_free_block(inode_ref, phys_block); + goto finish; + } + + uint32_t tree_depth = ext4_extent_header_get_depth(path->header); + path_ptr = path + tree_depth; + + /* Initialize newly created extent */ + ext4_extent_set_block_count(path_ptr->extent, 1); + ext4_extent_set_first_block(path_ptr->extent, new_block_idx); + ext4_extent_set_start(path_ptr->extent, phys_block); + + /* Update i-node */ + if (update_size) { + ext4_inode_set_size(inode_ref->inode, inode_size + block_size); + inode_ref->dirty = true; + } + + path_ptr->block.dirty = true; + +finish: + /* Set return values */ + *iblock = new_block_idx; + *fblock = phys_block; + + /* + * Put loaded blocks + * starting from 1: 0 is a block with inode data + */ + for (i = 1; i <= path->depth; ++i) { + if (path[i].block.lb_id) + ext4_block_set(inode_ref->fs->bdev, &path[i].block); + } + + /* Destroy temporary data structure */ + free(path); + + return rc; +} + +/** + * @} + */ diff --git a/lwext4/ext4_extent.h b/lwext4/ext4_extent.h new file mode 100644 index 0000000..86d3059 --- /dev/null +++ b/lwext4/ext4_extent.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_extent.h + * @brief More complex filesystem functions. + */ +#ifndef EXT4_EXTENT_H_ +#define EXT4_EXTENT_H_ + +#include +#include + +/**@brief Get logical number of the block covered by extent. + * @param extent Extent to load number from + * @return Logical number of the first block covered by extent */ +uint32_t ext4_extent_get_first_block(struct ext4_extent *extent); + +/**@brief Set logical number of the first block covered by extent. + * @param extent Extent to set number to + * @param iblock Logical number of the first block covered by extent */ +void ext4_extent_set_first_block(struct ext4_extent *extent, uint32_t iblock); + +/**@brief Get number of blocks covered by extent. + * @param extent Extent to load count from + * @return Number of blocks covered by extent */ +uint16_t ext4_extent_get_block_count(struct ext4_extent *extent); + +/**@brief Set number of blocks covered by extent. + * @param extent Extent to load count from + * @param count Number of blocks covered by extent */ +void ext4_extent_set_block_count(struct ext4_extent *extent, uint16_t count); + +/**@brief Get physical number of the first block covered by extent. + * @param extent Extent to load number + * @return Physical number of the first block covered by extent */ +uint64_t ext4_extent_get_start(struct ext4_extent *extent); + +/**@brief Set physical number of the first block covered by extent. + * @param extent Extent to load number + * @param fblock Physical number of the first block covered by extent */ +void ext4_extent_set_start(struct ext4_extent *extent, uint64_t fblock); + + +/**@brief Get logical number of the block covered by extent index. + * @param index Extent index to load number from + * @return Logical number of the first block covered by extent index */ +uint32_t ext4_extent_index_get_first_block(struct ext4_extent_index *index); + +/**@brief Set logical number of the block covered by extent index. + * @param index Extent index to set number to + * @param iblock Logical number of the first block covered by extent index */ +void ext4_extent_index_set_first_block(struct ext4_extent_index *index, + uint32_t iblock); + +/**@brief Get physical number of block where the child node is located. + * @param index Extent index to load number from + * @return Physical number of the block with child node */ +uint64_t ext4_extent_index_get_leaf(struct ext4_extent_index *index); + + +/**@brief Set physical number of block where the child node is located. + * @param index Extent index to set number to + * @param fblock Ohysical number of the block with child node */ +void ext4_extent_index_set_leaf(struct ext4_extent_index *index, + uint64_t fblock); + + +/**@brief Get magic value from extent header. + * @param header Extent header to load value from + * @return Magic value of extent header */ +uint16_t ext4_extent_header_get_magic(struct ext4_extent_header *header); + +/**@brief Set magic value to extent header. + * @param header Extent header to set value to + * @param magic Magic value of extent header */ +void ext4_extent_header_set_magic(struct ext4_extent_header *header, + uint16_t magic); + +/**@brief Get number of entries from extent header + * @param header Extent header to get value from + * @return Number of entries covered by extent header */ +uint16_t ext4_extent_header_get_entries_count(struct ext4_extent_header *header); + +/**@brief Set number of entries to extent header + * @param header Extent header to set value to + * @param count Number of entries covered by extent header */ +void ext4_extent_header_set_entries_count(struct ext4_extent_header *header, + uint16_t count); + +/**@brief Get maximum number of entries from extent header + * @param header Extent header to get value from + * @return Maximum number of entries covered by extent header */ +uint16_t ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header); + +/**@brief Set maximum number of entries to extent header + * @param header Extent header to set value to + * @param max_count Maximum number of entries covered by extent header */ +void ext4_extent_header_set_max_entries_count(struct ext4_extent_header *header, + uint16_t max_count); + +/**@brief Get depth of extent subtree. + * @param header Extent header to get value from + * @return Depth of extent subtree */ +uint16_t ext4_extent_header_get_depth(struct ext4_extent_header *header); + +/**@brief Set depth of extent subtree. + * @param header Extent header to set value to + * @param depth Depth of extent subtree */ +void ext4_extent_header_set_depth(struct ext4_extent_header *header, + uint16_t depth); + +/**@brief Get generation from extent header + * @param header Extent header to get value from + * @return Generation */ +uint32_t ext4_extent_header_get_generation(struct ext4_extent_header *header); + +/**@brief Set generation to extent header + * @param header Extent header to set value to + * @param generation Generation */ +void ext4_extent_header_set_generation(struct ext4_extent_header *header, + uint32_t generation); + +/**@brief Find physical block in the extent tree by logical block number. + * There is no need to save path in the tree during this algorithm. + * @param inode_ref I-node to load block from + * @param iblock Logical block number to find + * @param fblock Output value for physical block number + * @return Error code*/ +int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock, + uint32_t *fblock); + +/**@brief Release all data blocks starting from specified logical block. + * @param inode_ref I-node to release blocks from + * @param iblock_from First logical block to release + * @return Error code */ +int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref, + uint32_t iblock_from); + +/**@brief Append data block to the i-node. + * This function allocates data block, tries to append it + * to some existing extent or creates new extents. + * It includes possible extent tree modifications (splitting). + * @param inode_ref I-node to append block to + * @param iblock Output logical number of newly allocated block + * @param fblock Output physical block address of newly allocated block + * + * @return Error code*/ +int ext4_extent_append_block(struct ext4_inode_ref *inode_ref, + uint32_t *iblock, uint32_t *fblock, bool update_size); + + +#endif /* EXT4_EXTENT_H_ */ +/** + * @} + */ diff --git a/lwext4/ext4_fs.c b/lwext4/ext4_fs.c index 1516f6a..c06f43d 100644 --- a/lwext4/ext4_fs.c +++ b/lwext4/ext4_fs.c @@ -50,6 +50,7 @@ #include #include #include +#include #include int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev) @@ -125,24 +126,184 @@ int ext4_fs_fini(struct ext4_fs *fs) return ext4_sb_write(fs->bdev, &fs->sb); } +static void ext4_fs_debug_features_incomp(uint32_t features_incompatible) +{ + + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_COMPRESSION){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_COMPRESSION\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_FILETYPE){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_FILETYPE\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_RECOVER){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_RECOVER\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_JOURNAL_DEV){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_JOURNAL_DEV\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_META_BG){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_META_BG\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_EXTENTS){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_EXTENTS\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_64BIT){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_64BIT\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_MMP){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_MMP\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_FLEX_BG){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_FLEX_BG\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_EA_INODE){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_EA_INODE\n"); + } + if(features_incompatible & + EXT4_FEATURE_INCOMPAT_DIRDATA){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_INCOMPAT_DIRDATA\n"); + } +} +static void ext4_fs_debug_features_comp(uint32_t features_compatible) +{ + if(features_compatible & + EXT4_FEATURE_COMPAT_DIR_PREALLOC){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_COMPAT_DIR_PREALLOC\n"); + } + if(features_compatible & + EXT4_FEATURE_COMPAT_IMAGIC_INODES){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_COMPAT_IMAGIC_INODES\n"); + } + if(features_compatible & + EXT4_FEATURE_COMPAT_HAS_JOURNAL){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_COMPAT_HAS_JOURNAL\n"); + } + if(features_compatible & + EXT4_FEATURE_COMPAT_EXT_ATTR){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_COMPAT_EXT_ATTR\n"); + } + if(features_compatible & + EXT4_FEATURE_COMPAT_RESIZE_INODE){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_COMPAT_RESIZE_INODE\n"); + } + if(features_compatible & + EXT4_FEATURE_COMPAT_DIR_INDEX){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_COMPAT_DIR_INDEX\n"); + } +} + +static void ext4_fs_debug_features_ro(uint32_t features_ro) +{ + if(features_ro & + EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER\n"); + } + if(features_ro & + EXT4_FEATURE_RO_COMPAT_LARGE_FILE){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_LARGE_FILE\n"); + } + if(features_ro & + EXT4_FEATURE_RO_COMPAT_BTREE_DIR){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_BTREE_DIR\n"); + } + if(features_ro & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_HUGE_FILE\n"); + } + if(features_ro & + EXT4_FEATURE_RO_COMPAT_GDT_CSUM){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_GDT_CSUM\n"); + } + if(features_ro & + EXT4_FEATURE_RO_COMPAT_DIR_NLINK){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_DIR_NLINK\n"); + } + if(features_ro & + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE){ + ext4_dprintf(EXT4_DEBUG_FS, + "EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE\n"); + } +} + int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only) { ext4_assert(fs && read_only); - + uint32_t v; if(ext4_get32(&fs->sb, rev_level) == 0){ *read_only = false; return EOK; } + ext4_dprintf(EXT4_DEBUG_FS, + "\nSblock rev_level: \n%d\n", ext4_get32(&fs->sb, rev_level) ); + + ext4_dprintf(EXT4_DEBUG_FS, + "\nSblock minor_rev_level: \n%d\n", + ext4_get32(&fs->sb, minor_rev_level)); + + ext4_dprintf(EXT4_DEBUG_FS, + "\nSblock features_incompatible:\n"); + ext4_fs_debug_features_incomp(ext4_get32(&fs->sb, features_incompatible)); + + ext4_dprintf(EXT4_DEBUG_FS, + "\nSblock features_compatible:\n"); + ext4_fs_debug_features_comp(ext4_get32(&fs->sb, features_compatible)); + + ext4_dprintf(EXT4_DEBUG_FS, + "\nSblock features_read_only:\n"); + ext4_fs_debug_features_ro(ext4_get32(&fs->sb, features_read_only)); /*Check features_incompatible*/ - if ((ext4_get32(&fs->sb, features_incompatible) & - (~EXT4_FEATURE_INCOMPAT_SUPP)) ) + v = (ext4_get32(&fs->sb, features_incompatible) & + (~EXT4_FEATURE_INCOMPAT_SUPP)); + if (v){ + ext4_dprintf(EXT4_DEBUG_FS, + "\nERROR sblock features_incompatible. Unsupported:\n"); + ext4_fs_debug_features_incomp(v); return ENOTSUP; + } /*Check features_read_only*/ - if ((ext4_get32(&fs->sb, features_read_only) & - (~EXT4_FEATURE_RO_COMPAT_SUPP))){ + v = (ext4_get32(&fs->sb, features_read_only) & + (~EXT4_FEATURE_RO_COMPAT_SUPP)); + if (v){ + ext4_dprintf(EXT4_DEBUG_FS, + "\nERROR sblock features_read_only . Unsupported:\n"); + ext4_fs_debug_features_incomp(v); + *read_only = true; return EOK; } @@ -554,14 +715,14 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref, /* Initialize extent root header */ - ext4_extent_header_t *header = ext4_inode_get_extent_header(inode); + struct ext4_extent_header *header = ext4_inode_get_extent_header(inode); ext4_extent_header_set_depth(header, 0); ext4_extent_header_set_entries_count(header, 0); ext4_extent_header_set_generation(header, 0); ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC); uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) - - sizeof(ext4_extent_header_t)) / sizeof(ext4_extent_t); + sizeof(struct ext4_extent_header)) / sizeof(struct ext4_extent); ext4_extent_header_set_max_entries_count(header, max_entries); } diff --git a/lwext4/ext4_inode.c b/lwext4/ext4_inode.c index d359e99..1097d6b 100644 --- a/lwext4/ext4_inode.c +++ b/lwext4/ext4_inode.c @@ -180,7 +180,7 @@ uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) { /* 48-bit field */ - count = ((uint64_t) to_le16(inode->osd2.linux2.blocks_high)) << 32; + count |= ((uint64_t) to_le16(inode->osd2.linux2.blocks_high)) << 32; if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) {