ref: c91beecad10bf077412d7464d98e0618ca1cb9cd
parent: 4bd842820e2740c23908932e264cbf43cb1fdd27
author: gkostka <kostka.grzegorz@gmail.com>
date: Thu Oct 24 17:21:19 EDT 2013
FEATURES: - extent support - block group crc support - extent and directory indexing is enabled by default - new images (100MB ext2, ext3, ext4) - fs features debug in ext4_fs.c - new demo app features BUGFIX: - ext4_inode_get_blocks_count fix
--- a/demos/generic/main.c
+++ b/demos/generic/main.c
@@ -36,8 +36,6 @@
#include <ext4_filedev.h>
#include <ext4.h>
-
-
char input_name[128] = "ext2";
/**@brief Read-write size*/
@@ -44,14 +42,23 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
{"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 @@
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,13 +327,15 @@
return EXIT_FAILURE;
}
- dir_test(dir_cnt);
+ cleanup();
- ext4_fremove("/mp/hello.txt");
- ext4_fremove("/mp/test1");
- mp_stats();
- dir_ls("/mp/");
+ if(sbstat)
+ mp_stats();
+
+ dir_ls("/mp/");
+ dir_test(dir_cnt);
+
/*Add hello world file.*/
r = ext4_fopen(&f, "/mp/hello.txt", "wb");
r = ext4_fwrite(&f, "Hello World !\n", strlen("Hello World !\n"), 0);
@@ -390,16 +396,20 @@
}
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;
binary files a/ext_images.7z b/ext_images.7z differ
--- a/lwext4/ext4_block_group.c
+++ b/lwext4/ext4_block_group.c
@@ -43,11 +43,48 @@
#include <ext4_block_group.h>
+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;
}
/**
--- 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
--- 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)
--- /dev/null
+++ b/lwext4/ext4_extent.c
@@ -1,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 <ext4_config.h>
+#include <ext4_extent.h>
+#include <ext4_inode.h>
+#include <ext4_super.h>
+#include <ext4_blockdev.h>
+#include <ext4_balloc.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+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;
+}
+
+/**
+ * @}
+ */
--- /dev/null
+++ b/lwext4/ext4_extent.h
@@ -1,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 <ext4_config.h>
+#include <ext4_types.h>
+
+/**@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_ */
+/**
+ * @}
+ */
--- a/lwext4/ext4_fs.c
+++ b/lwext4/ext4_fs.c
@@ -50,6 +50,7 @@
#include <ext4_bitmap.h>
#include <ext4_inode.h>
#include <ext4_ialloc.h>
+#include <ext4_extent.h>
#include <string.h>
int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev)
@@ -125,24 +126,184 @@
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,7 +715,7 @@
/* 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);
@@ -561,7 +722,7 @@
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);
}
--- a/lwext4/ext4_inode.c
+++ b/lwext4/ext4_inode.c
@@ -180,7 +180,7 @@
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)) {