ref: c897dc5a646527c2b87e4bc0399db59b46edbb1d
parent: 00affc044c56435a81fe070a53729c0f6fcbdc08
author: gkostka <kostka.grzegorz@gmail.com>
date: Wed Sep 16 19:46:35 EDT 2015
Linux line-endings
--- a/lwext4/CMakeLists.txt
+++ b/lwext4/CMakeLists.txt
@@ -1,8 +1,8 @@
-
-#LIBRARY
-include_directories(.)
-aux_source_directory(. LWEXT4_SRC)
-add_library(lwext4 ${LWEXT4_SRC})
-add_custom_target(lib_size ALL DEPENDS lwext4 COMMAND ${SIZE} liblwext4.a)
-
-
+
+#LIBRARY
+include_directories(.)
+aux_source_directory(. LWEXT4_SRC)
+add_library(lwext4 ${LWEXT4_SRC})
+add_custom_target(lib_size ALL DEPENDS lwext4 COMMAND ${SIZE} liblwext4.a)
+
+
--- a/lwext4/ext4.c
+++ b/lwext4/ext4.c
@@ -1,1927 +1,1927 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * 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.h
- * @brief Ext4 high level operations (file, directory, mountpoints...)
- */
-
-#include "ext4_config.h"
-#include "ext4_blockdev.h"
-#include "ext4_types.h"
-#include "ext4_debug.h"
-#include "ext4_errno.h"
-#include "ext4_fs.h"
-#include "ext4_dir.h"
-#include "ext4_inode.h"
-#include "ext4_super.h"
-#include "ext4_dir_idx.h"
-#include "ext4.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-/**@brief Mount point OS dependent lock*/
-#define EXT4_MP_LOCK(_m) \
- do { \
- if ((_m)->os_locks) \
- (_m)->os_locks->lock(); \
- } while (0)
-
-/**@brief Mount point OS dependent unlock*/
-#define EXT4_MP_UNLOCK(_m) \
- do { \
- if ((_m)->os_locks) \
- (_m)->os_locks->unlock(); \
- } while (0)
-
-/**@brief Mount point descriptor.*/
-struct ext4_mountpoint {
-
- /**@brief Mount done flag.*/
- bool mounted;
-
- /**@brief Mount point name (@ref ext4_mount)*/
- char name[32];
-
- /**@brief OS dependent lock/unlock functions.*/
- const struct ext4_lock *os_locks;
-
- /**@brief Ext4 filesystem internals.*/
- struct ext4_fs fs;
-
- /**@brief Dynamic allocation cache flag.*/
- bool cache_dynamic;
-};
-
-/**@brief Block devices descriptor.*/
-struct _ext4_devices {
-
- /**@brief Block device name (@ref ext4_device_register)*/
- char name[32];
-
- /**@brief Block device handle.*/
- struct ext4_blockdev *bd;
-
- /**@brief Block cache handle.*/
- struct ext4_bcache *bc;
-};
-
-/**@brief Block devices.*/
-struct _ext4_devices _bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
-
-/**@brief Mountpoints.*/
-struct ext4_mountpoint _mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
-
-int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,
- const char *dev_name)
-{
- uint32_t i;
- ext4_assert(bd && dev_name);
-
- for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
- if (!_bdevices[i].bd) {
- strcpy(_bdevices[i].name, dev_name);
- _bdevices[i].bd = bd;
- _bdevices[i].bc = bc;
- return EOK;
- }
-
- if (!strcmp(_bdevices[i].name, dev_name))
- return EOK;
- }
- return ENOSPC;
-}
-
-/****************************************************************************/
-
-static bool ext4_is_dots(const uint8_t *name, size_t name_size)
-{
- if ((name_size == 1) && (name[0] == '.'))
- return true;
-
- if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
- return true;
-
- return false;
-}
-
-static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)
-{
- struct ext4_fs *fs = enode->fs;
-
- /* Check if node is directory */
- if (!ext4_inode_is_type(&fs->sb, enode->inode,
- EXT4_INODE_MODE_DIRECTORY)) {
- *has_children = false;
- return EOK;
- }
-
- struct ext4_directory_iterator it;
- int rc = ext4_dir_iterator_init(&it, enode, 0);
- if (rc != EOK)
- return rc;
-
- /* Find a non-empty directory entry */
- bool found = false;
- while (it.current != NULL) {
- if (ext4_dir_entry_ll_get_inode(it.current) != 0) {
- uint16_t name_size = ext4_dir_entry_ll_get_name_length(
- &fs->sb, it.current);
- if (!ext4_is_dots(it.current->name, name_size)) {
- found = true;
- break;
- }
- }
-
- rc = ext4_dir_iterator_next(&it);
- if (rc != EOK) {
- ext4_dir_iterator_fini(&it);
- return rc;
- }
- }
-
- rc = ext4_dir_iterator_fini(&it);
- if (rc != EOK)
- return rc;
-
- *has_children = found;
-
- return EOK;
-}
-
-static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,
- struct ext4_inode_ref *child, const char *name,
- uint32_t name_len)
-{
- /* Check maximum name length */
- if (name_len > EXT4_DIRECTORY_FILENAME_LEN)
- return EINVAL;
-
- /* Add entry to parent directory */
- int rc = ext4_dir_add_entry(parent, name, name_len, child);
- if (rc != EOK)
- return rc;
-
- /* Fill new dir -> add '.' and '..' entries.
- * Also newly allocated inode should have 0 link count.
- */
- if (ext4_inode_is_type(&mp->fs.sb, child->inode,
- EXT4_INODE_MODE_DIRECTORY) &&
- ext4_inode_get_links_count(child->inode) == 0) {
- rc = ext4_dir_add_entry(child, ".", strlen("."), child);
- if (rc != EOK) {
- ext4_dir_remove_entry(parent, name, strlen(name));
- return rc;
- }
-
- rc = ext4_dir_add_entry(child, "..", strlen(".."), parent);
- if (rc != EOK) {
- ext4_dir_remove_entry(parent, name, strlen(name));
- ext4_dir_remove_entry(child, ".", strlen("."));
- return rc;
- }
-
- /*New empty directory. Two links (. and ..) */
- ext4_inode_set_links_count(child->inode, 2);
-
-#if CONFIG_DIR_INDEX_ENABLE
- /* Initialize directory index if supported */
- if (ext4_sb_has_feature_compatible(
- &mp->fs.sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
- rc = ext4_dir_dx_init(child);
- if (rc != EOK)
- return rc;
-
- ext4_inode_set_flag(child->inode,
- EXT4_INODE_FLAG_INDEX);
- child->dirty = true;
- }
-#endif
-
- ext4_fs_inode_links_count_inc(parent);
- child->dirty = true;
- parent->dirty = true;
- } else {
- if (ext4_inode_is_type(&mp->fs.sb, child->inode,
- EXT4_INODE_MODE_DIRECTORY)) {
- /* FIXME: SO TRICKY. */
- int has_flag_index = ext4_inode_has_flag(
- child->inode, EXT4_INODE_FLAG_INDEX);
- struct ext4_directory_search_result result;
- if (has_flag_index)
- ext4_inode_clear_flag(child->inode,
- EXT4_INODE_FLAG_INDEX);
-
- rc = ext4_dir_find_entry(&result, child, "..",
- strlen(".."));
- if (has_flag_index)
- ext4_inode_set_flag(child->inode,
- EXT4_INODE_FLAG_INDEX);
-
- if (rc != EOK)
- return EIO;
-
- ext4_dir_entry_ll_set_inode(result.dentry,
- parent->index);
- result.block.dirty = true;
- rc = ext4_dir_destroy_result(child, &result);
- if (rc != EOK)
- return rc;
-
- ext4_fs_inode_links_count_inc(parent);
- parent->dirty = true;
- } else {
- ext4_fs_inode_links_count_inc(child);
- child->dirty = true;
- }
- }
-
- return rc;
-}
-
-static int ext4_unlink(struct ext4_mountpoint *mp,
- struct ext4_inode_ref *parent,
- struct ext4_inode_ref *child_inode_ref, const char *name,
- uint32_t name_len)
-{
- bool has_children;
- int rc = ext4_has_children(&has_children, child_inode_ref);
- if (rc != EOK)
- return rc;
-
- /* Cannot unlink non-empty node */
- if (has_children)
- return ENOTSUP;
-
- /* Remove entry from parent directory */
- rc = ext4_dir_remove_entry(parent, name, name_len);
- if (rc != EOK)
- return rc;
-
- bool is_dir = ext4_inode_is_type(&mp->fs.sb, child_inode_ref->inode,
- EXT4_INODE_MODE_DIRECTORY);
-
- /* If directory - handle links from parent */
- if (is_dir) {
- // ext4_assert(ext4_inode_get_links_count(child_inode_ref->inode)
- // == 1);
- ext4_fs_inode_links_count_dec(parent);
- parent->dirty = true;
- }
-
- /*
- * TODO: Update timestamps of the parent
- * (when we have wall-clock time).
- *
- * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
- * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
- * parent->dirty = true;
- */
-
- /*
- * TODO: Update timestamp for inode.
- *
- * ext4_inode_set_change_inode_time(child_inode_ref->inode,
- * (uint32_t) now);
- */
- if (ext4_inode_get_links_count(child_inode_ref->inode)) {
- ext4_fs_inode_links_count_dec(child_inode_ref);
- child_inode_ref->dirty = true;
- }
-
- return EOK;
-}
-
-/****************************************************************************/
-
-int ext4_mount(const char *dev_name, const char *mount_point)
-{
- ext4_assert(mount_point && dev_name);
- int r;
- int i;
-
- uint32_t bsize;
- struct ext4_blockdev *bd = 0;
- struct ext4_bcache *bc = 0;
- struct ext4_mountpoint *mp = 0;
-
- if (mount_point[strlen(mount_point) - 1] != '/')
- return ENOTSUP;
-
- for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
- if (_bdevices[i].name) {
- if (!strcmp(dev_name, _bdevices[i].name)) {
- bd = _bdevices[i].bd;
- bc = _bdevices[i].bc;
- break;
- }
- }
- }
-
- if (!bd)
- return ENODEV;
-
- for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
- if (!_mp[i].mounted) {
- strcpy(_mp[i].name, mount_point);
- _mp[i].mounted = 1;
- mp = &_mp[i];
- break;
- }
-
- if (!strcmp(_mp[i].name, mount_point))
- return EOK;
- }
-
- if (!mp)
- return ENOMEM;
-
- r = ext4_block_init(bd);
- if (r != EOK)
- return r;
-
- r = ext4_fs_init(&mp->fs, bd);
- if (r != EOK) {
- ext4_block_fini(bd);
- return r;
- }
-
- bsize = ext4_sb_get_block_size(&mp->fs.sb);
- ext4_block_set_lb_size(bd, bsize);
-
- mp->cache_dynamic = 0;
-
- if (!bc) {
- /*Automatic block cache alloc.*/
- mp->cache_dynamic = 1;
- bc = malloc(sizeof(struct ext4_bcache));
-
- r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
- bsize);
- if (r != EOK) {
- free(bc);
- ext4_block_fini(bd);
- return r;
- }
- }
-
- if (bsize != bc->itemsize)
- return ENOTSUP;
-
- /*Bind block cache to block device*/
- r = ext4_block_bind_bcache(bd, bc);
- if (r != EOK) {
- ext4_block_fini(bd);
- if (mp->cache_dynamic) {
- ext4_bcache_fini_dynamic(bc);
- free(bc);
- }
- return r;
- }
-
- return r;
-}
-
-int ext4_umount(const char *mount_point)
-{
- int i;
- int r;
- struct ext4_mountpoint *mp = 0;
-
- for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
- if (!strcmp(_mp[i].name, mount_point)) {
- mp = &_mp[i];
- break;
- }
- }
-
- if (!mp)
- return ENODEV;
-
- r = ext4_fs_fini(&mp->fs);
- if (r != EOK)
- return r;
-
- mp->mounted = 0;
-
- if (mp->cache_dynamic) {
- ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
- free(mp->fs.bdev->bc);
- }
-
- return ext4_block_fini(mp->fs.bdev);
-}
-
-int ext4_mount_point_stats(const char *mount_point,
- struct ext4_mount_stats *stats)
-{
- uint32_t i;
- struct ext4_mountpoint *mp = 0;
-
- for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
- if (!strcmp(_mp[i].name, mount_point)) {
- mp = &_mp[i];
- break;
- }
- }
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
- stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);
- stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);
- stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb);
- stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);
- stats->block_size = ext4_sb_get_block_size(&mp->fs.sb);
-
- stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);
- stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);
- stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);
-
- memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);
- EXT4_MP_UNLOCK(mp);
-
- return EOK;
-}
-
-int ext4_mount_setup_locks(const char *mount_point,
- const struct ext4_lock *locks)
-{
- uint32_t i;
- struct ext4_mountpoint *mp = 0;
-
- for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
- if (!strcmp(_mp[i].name, mount_point)) {
- mp = &_mp[i];
- break;
- }
- }
- if (!mp)
- return ENOENT;
-
- mp->os_locks = locks;
- return EOK;
-}
-
-/********************************FILE OPERATIONS*****************************/
-
-static struct ext4_mountpoint *ext4_get_mount(const char *path)
-{
- int i;
- for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
-
- if (!_mp[i].mounted)
- continue;
-
- if (!strncmp(_mp[i].name, path, strlen(_mp[i].name)))
- return &_mp[i];
- }
- return 0;
-}
-
-static int ext4_path_check(const char *path, bool *is_goal)
-{
- int i;
-
- for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
-
- if (path[i] == '/') {
- *is_goal = false;
- return i;
- }
-
- if (path[i] == 0) {
- *is_goal = true;
- return i;
- }
- }
-
- return 0;
-}
-
-static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
-{
- if (!flags)
- return false;
-
- if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {
- *file_flags = O_RDONLY;
- return true;
- }
-
- if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {
- *file_flags = O_WRONLY | O_CREAT | O_TRUNC;
- return true;
- }
-
- if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {
- *file_flags = O_WRONLY | O_CREAT | O_APPEND;
- return true;
- }
-
- if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||
- !strcmp(flags, "r+b")) {
- *file_flags = O_RDWR;
- return true;
- }
-
- if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||
- !strcmp(flags, "w+b")) {
- *file_flags = O_RDWR | O_CREAT | O_TRUNC;
- return true;
- }
-
- if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||
- !strcmp(flags, "a+b")) {
- *file_flags = O_RDWR | O_CREAT | O_APPEND;
- return true;
- }
-
- return false;
-}
-
-/*
- * NOTICE: if filetype is equal to EXT4_DIRECTORY_FILETYPE_UNKNOWN,
- * any filetype of the target dir entry will be accepted.
- */
-static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
- int filetype, uint32_t *parent_inode,
- uint32_t *name_off)
-{
- bool is_goal = false;
- uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;
- uint32_t next_inode;
-
- int r;
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- struct ext4_directory_search_result result;
- struct ext4_inode_ref ref;
-
- f->mp = 0;
-
- if (!mp)
- return ENOENT;
-
- f->flags = flags;
-
- /*Skip mount point*/
- path += strlen(mp->name);
-
- if (name_off)
- *name_off = strlen(mp->name);
-
- /*Load root*/
- r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
-
- if (r != EOK)
- return r;
-
- if (parent_inode)
- *parent_inode = ref.index;
-
- int len = ext4_path_check(path, &is_goal);
-
- while (1) {
-
- len = ext4_path_check(path, &is_goal);
-
- if (!len) {
- /*If root open was request.*/
- if (is_goal &&
- ((filetype == EXT4_DIRECTORY_FILETYPE_DIR) ||
- (filetype == EXT4_DIRECTORY_FILETYPE_UNKNOWN)))
- break;
-
- r = ENOENT;
- break;
- }
-
- r = ext4_dir_find_entry(&result, &ref, path, len);
- if (r != EOK) {
-
- if (r != ENOENT)
- break;
-
- if (!(f->flags & O_CREAT))
- break;
-
- /*O_CREAT allows create new entry*/
- struct ext4_inode_ref child_ref;
- r = ext4_fs_alloc_inode(
- &mp->fs, &child_ref,
- is_goal ? (filetype == EXT4_DIRECTORY_FILETYPE_DIR)
- : true);
- if (r != EOK)
- break;
-
- /*Destroy last result*/
- ext4_dir_destroy_result(&ref, &result);
-
- /*Link with root dir.*/
- r = ext4_link(mp, &ref, &child_ref, path, len);
- if (r != EOK) {
- /*Fail. Free new inode.*/
- ext4_fs_free_inode(&child_ref);
- /*We do not want to write new inode.
- But block has to be released.*/
- child_ref.dirty = false;
- ext4_fs_put_inode_ref(&child_ref);
- break;
- }
-
- ext4_fs_put_inode_ref(&child_ref);
-
- continue;
- }
-
- if (parent_inode)
- *parent_inode = ref.index;
-
- next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
- inode_type =
- ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
-
- r = ext4_dir_destroy_result(&ref, &result);
- if (r != EOK)
- break;
-
- /*If expected file error*/
- if (inode_type != EXT4_DIRECTORY_FILETYPE_DIR && !is_goal) {
- r = ENOENT;
- break;
- }
- if (filetype != EXT4_DIRECTORY_FILETYPE_UNKNOWN) {
- if ((inode_type != filetype) && is_goal) {
- r = ENOENT;
- break;
- }
- }
-
- r = ext4_fs_put_inode_ref(&ref);
- if (r != EOK)
- break;
-
- r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
- if (r != EOK)
- break;
-
- if (is_goal)
- break;
-
- path += len + 1;
-
- if (name_off)
- *name_off += len + 1;
- };
-
- if (r != EOK) {
- ext4_fs_put_inode_ref(&ref);
- return r;
- }
-
- if (is_goal) {
-
- if ((f->flags & O_TRUNC) &&
- (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)) {
-
- r = ext4_fs_truncate_inode(&ref, 0);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&ref);
- return r;
- }
- }
-
- f->mp = mp;
- f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
- f->inode = ref.index;
- f->fpos = 0;
-
- if (f->flags & O_APPEND)
- f->fpos = f->fsize;
- }
-
- r = ext4_fs_put_inode_ref(&ref);
- return r;
-}
-
-/****************************************************************************/
-
-static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
- bool file_expect, uint32_t *parent_inode,
- uint32_t *name_off)
-{
- uint32_t iflags;
- int filetype;
- if (ext4_parse_flags(flags, &iflags) == false)
- return EINVAL;
-
- if (file_expect == true)
- filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;
- else
- filetype = EXT4_DIRECTORY_FILETYPE_DIR;
-
- return ext4_generic_open2(f, path, iflags, filetype, parent_inode,
- name_off);
-}
-
-static int __ext4_create_hardlink(const char *path,
- struct ext4_inode_ref *child_ref)
-{
- bool is_goal = false;
- uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;
- uint32_t next_inode;
-
- int r;
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- struct ext4_directory_search_result result;
- struct ext4_inode_ref ref;
-
- if (!mp)
- return ENOENT;
-
- /*Skip mount point*/
- path += strlen(mp->name);
-
- /*Load root*/
- r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
-
- if (r != EOK)
- return r;
-
- int len = ext4_path_check(path, &is_goal);
-
- while (1) {
-
- len = ext4_path_check(path, &is_goal);
-
- if (!len) {
- /*If root open was request.*/
- if (is_goal)
- r = EINVAL;
- else
- r = ENOENT;
- break;
- }
-
- r = ext4_dir_find_entry(&result, &ref, path, len);
- if (r != EOK) {
-
- if (r != ENOENT || !is_goal)
- break;
-
- /*Destroy last result*/
- ext4_dir_destroy_result(&ref, &result);
-
- /*Link with root dir.*/
- r = ext4_link(mp, &ref, child_ref, path, len);
- break;
- }
-
- next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
- inode_type =
- ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
-
- r = ext4_dir_destroy_result(&ref, &result);
- if (r != EOK)
- break;
-
- if (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE) {
- if (is_goal)
- r = EEXIST;
- else
- r = ENOENT;
-
- break;
- }
-
- r = ext4_fs_put_inode_ref(&ref);
- if (r != EOK)
- break;
-
- r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
- if (r != EOK)
- break;
-
- if (is_goal)
- break;
-
- path += len + 1;
- };
-
- if (r != EOK) {
- ext4_fs_put_inode_ref(&ref);
- return r;
- }
-
- r = ext4_fs_put_inode_ref(&ref);
- return r;
-}
-
-static int __ext4_get_inode_ref_remove_hardlink(const char *path,
- struct ext4_inode_ref *child)
-{
- ext4_file f;
- uint32_t parent_inode;
- uint32_t name_off;
- bool is_goal;
- int r;
- int len;
- struct ext4_inode_ref parent;
- struct ext4_mountpoint *mp = ext4_get_mount(path);
-
- if (!mp)
- return ENOENT;
-
- r = ext4_generic_open2(&f, path, O_RDONLY,
- EXT4_DIRECTORY_FILETYPE_UNKNOWN, &parent_inode,
- &name_off);
- if (r != EOK)
- return r;
-
- /*Load parent*/
- r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
- if (r != EOK) {
- return r;
- }
-
- /*We have file to unlink. Load it.*/
- r = ext4_fs_get_inode_ref(&mp->fs, f.inode, child);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&parent);
- return r;
- }
-
- if (r != EOK)
- goto Finish;
-
- /*Set path*/
- path += name_off;
-
- len = ext4_path_check(path, &is_goal);
-
- /*Unlink from parent*/
- r = ext4_unlink(mp, &parent, child, path, len);
- if (r != EOK)
- goto Finish;
-
-Finish:
- if (r != EOK)
- ext4_fs_put_inode_ref(child);
-
- ext4_fs_put_inode_ref(&parent);
- return r;
-}
-
-int ext4_frename(const char *path, const char *new_path)
-{
- int r;
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- struct ext4_inode_ref inode_ref;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- r = __ext4_get_inode_ref_remove_hardlink(path, &inode_ref);
- if (r != EOK)
- goto Finish;
-
- r = __ext4_create_hardlink(new_path, &inode_ref);
- if (r != EOK)
- r = __ext4_create_hardlink(path, &inode_ref);
-
- ext4_fs_put_inode_ref(&inode_ref);
-
-Finish:
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-/****************************************************************************/
-
-int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
-{
- struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-
- if (!mp)
- return ENOENT;
-
- *sb = &mp->fs.sb;
- return EOK;
-}
-
-int ext4_cache_write_back(const char *path, bool on)
-{
- struct ext4_mountpoint *mp = ext4_get_mount(path);
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
- ext4_block_cache_write_back(mp->fs.bdev, on);
- EXT4_MP_UNLOCK(mp);
- return EOK;
-}
-
-int ext4_fremove(const char *path)
-{
- ext4_file f;
- uint32_t parent_inode;
- uint32_t name_off;
- bool is_goal;
- int r;
- int len;
- struct ext4_inode_ref child;
- struct ext4_inode_ref parent;
- struct ext4_mountpoint *mp = ext4_get_mount(path);
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
- r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- /*Load parent*/
- r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- /*We have file to delete. Load it.*/
- r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&parent);
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- /*Set path*/
- path += name_off;
-
- len = ext4_path_check(path, &is_goal);
-
- /*Unlink from parent*/
- r = ext4_unlink(mp, &parent, &child, path, len);
- if (r != EOK)
- goto Finish;
-
- /*Link count is zero, the inode should be freed. */
- if (!ext4_inode_get_links_count(child.inode)) {
- printf("ttttt\n");
- ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);
- /*Turncate*/
- ext4_block_cache_write_back(mp->fs.bdev, 1);
- /*Truncate may be IO heavy. Do it writeback cache mode.*/
- r = ext4_fs_truncate_inode(&child, 0);
- ext4_block_cache_write_back(mp->fs.bdev, 0);
-
- if (r != EOK)
- goto Finish;
-
- r = ext4_fs_free_inode(&child);
- if (r != EOK)
- goto Finish;
- }
-
-Finish:
- ext4_fs_put_inode_ref(&child);
- ext4_fs_put_inode_ref(&parent);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_fill_raw_inode(const char *mount_point, uint32_t ino,
- struct ext4_inode *inode)
-{
- int r;
- struct ext4_inode_ref inode_ref;
- struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- /*Load parent*/
- r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
-
- ext4_fs_put_inode_ref(&inode_ref);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_fopen(ext4_file *f, const char *path, const char *flags)
-{
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- int r;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
- ext4_block_cache_write_back(mp->fs.bdev, 1);
- r = ext4_generic_open(f, path, flags, true, 0, 0);
- ext4_block_cache_write_back(mp->fs.bdev, 0);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect)
-{
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- int r;
- int filetype;
-
- if (!mp)
- return ENOENT;
-
- if (file_expect == true)
- filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;
- else
- filetype = EXT4_DIRECTORY_FILETYPE_DIR;
-
- EXT4_MP_LOCK(mp);
- ext4_block_cache_write_back(mp->fs.bdev, 1);
- r = ext4_generic_open2(f, path, flags, filetype, 0, 0);
- ext4_block_cache_write_back(mp->fs.bdev, 0);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_fclose(ext4_file *f)
-{
- ext4_assert(f && f->mp);
-
- f->mp = 0;
- f->flags = 0;
- f->inode = 0;
- f->fpos = f->fsize = 0;
-
- return EOK;
-}
-
-int ext4_ftruncate(ext4_file *f, uint64_t size)
-{
- struct ext4_inode_ref ref;
- int r;
-
- ext4_assert(f && f->mp);
-
- if (f->flags & O_RDONLY)
- return EPERM;
-
- EXT4_MP_LOCK(f->mp);
-
- r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(f->mp);
- return r;
- }
-
- /*Sync file size*/
- f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
- if (f->fsize <= size) {
- r = EOK;
- goto Finish;
- }
-
- /*Start write back cache mode.*/
- r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
- if (r != EOK)
- goto Finish;
-
- r = ext4_fs_truncate_inode(&ref, size);
- if (r != EOK)
- goto Finish;
-
- f->fsize = size;
- if (f->fpos > size)
- f->fpos = size;
-
- /*Stop write back cache mode*/
- ext4_block_cache_write_back(f->mp->fs.bdev, 0);
-
- if (r != EOK)
- goto Finish;
-
-Finish:
- ext4_fs_put_inode_ref(&ref);
- EXT4_MP_UNLOCK(f->mp);
- return r;
-}
-
-int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)
-{
- uint32_t u;
- uint32_t fblock;
- uint32_t fblock_start;
- uint32_t fblock_cnt;
- uint32_t sblock;
- uint32_t sblock_end;
- uint32_t block_size;
- uint8_t *u8_buf = buf;
- int r;
- struct ext4_block b;
- struct ext4_inode_ref ref;
-
- ext4_assert(f && f->mp);
-
- if (f->flags & O_WRONLY)
- return EPERM;
-
- if (!size)
- return EOK;
-
- EXT4_MP_LOCK(f->mp);
-
- if (rcnt)
- *rcnt = 0;
-
- r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(f->mp);
- return r;
- }
-
- /*Sync file size*/
- f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
-
- block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
- size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;
- sblock = (f->fpos) / block_size;
- sblock_end = (f->fpos + size) / block_size;
- u = (f->fpos) % block_size;
-
- if (u) {
-
- uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
-
- r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
- if (r != EOK)
- goto Finish;
-
- r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
- if (r != EOK)
- goto Finish;
-
- memcpy(u8_buf, b.data + u, ll);
-
- r = ext4_block_set(f->mp->fs.bdev, &b);
- if (r != EOK)
- goto Finish;
-
- u8_buf += ll;
- size -= ll;
- f->fpos += ll;
-
- if (rcnt)
- *rcnt += ll;
-
- sblock++;
- }
-
- fblock_start = 0;
- fblock_cnt = 0;
- while (size >= block_size) {
- while (sblock < sblock_end) {
- r = ext4_fs_get_inode_data_block_index(&ref, sblock,
- &fblock);
- if (r != EOK)
- goto Finish;
-
- sblock++;
-
- if (!fblock_start) {
- fblock_start = fblock;
- }
-
- if ((fblock_start + fblock_cnt) != fblock)
- break;
-
- fblock_cnt++;
- }
-
- r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
- fblock_cnt);
- if (r != EOK)
- goto Finish;
-
- size -= block_size * fblock_cnt;
- u8_buf += block_size * fblock_cnt;
- f->fpos += block_size * fblock_cnt;
-
- if (rcnt)
- *rcnt += block_size * fblock_cnt;
-
- fblock_start = fblock;
- fblock_cnt = 1;
- }
-
- if (size) {
- r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
- if (r != EOK)
- goto Finish;
-
- r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
- if (r != EOK)
- goto Finish;
-
- memcpy(u8_buf, b.data, size);
-
- r = ext4_block_set(f->mp->fs.bdev, &b);
- if (r != EOK)
- goto Finish;
-
- f->fpos += size;
-
- if (rcnt)
- *rcnt += size;
- }
-
-Finish:
- ext4_fs_put_inode_ref(&ref);
- EXT4_MP_UNLOCK(f->mp);
- return r;
-}
-
-int ext4_fwrite(ext4_file *f, const void *buf, uint32_t size, uint32_t *wcnt)
-{
- uint32_t u;
- uint32_t fblock;
-
- uint32_t sblock;
- uint32_t sblock_end;
- uint32_t file_blocks;
- uint32_t block_size;
- uint32_t fblock_start;
- uint32_t fblock_cnt;
-
- struct ext4_block b;
- struct ext4_inode_ref ref;
- const uint8_t *u8_buf = buf;
- int r;
-
- ext4_assert(f && f->mp);
-
- if (f->flags & O_RDONLY)
- return EPERM;
-
- if (!size)
- return EOK;
-
- EXT4_MP_LOCK(f->mp);
-
- if (wcnt)
- *wcnt = 0;
-
- r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(f->mp);
- return r;
- }
-
- /*Sync file size*/
- f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
-
- block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
-
- sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;
- sblock_end /= block_size;
- file_blocks = (f->fsize / block_size);
-
- if (f->fsize % block_size)
- file_blocks++;
-
- sblock = (f->fpos) / block_size;
-
- u = (f->fpos) % block_size;
-
- if (u) {
- uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
-
- r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
- if (r != EOK)
- goto Finish;
-
- r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
- if (r != EOK)
- goto Finish;
-
- memcpy(b.data + u, u8_buf, ll);
- b.dirty = true;
-
- r = ext4_block_set(f->mp->fs.bdev, &b);
- if (r != EOK)
- goto Finish;
-
- u8_buf += ll;
- size -= ll;
- f->fpos += ll;
-
- if (wcnt)
- *wcnt += ll;
-
- sblock++;
- }
-
- /*Start write back cache mode.*/
- r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
- if (r != EOK)
- goto Finish;
-
- fblock_start = 0;
- fblock_cnt = 0;
- while (size >= block_size) {
-
- while (sblock < sblock_end) {
- if (sblock < file_blocks) {
- r = ext4_fs_get_inode_data_block_index(
- &ref, sblock, &fblock);
- if (r != EOK)
- break;
- } else {
- r = ext4_fs_append_inode_block(&ref, &fblock,
- &sblock);
- if (r != EOK)
- break;
- }
-
- sblock++;
-
- if (!fblock_start) {
- fblock_start = fblock;
- }
-
- if ((fblock_start + fblock_cnt) != fblock)
- break;
-
- fblock_cnt++;
- }
-
- r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
- fblock_cnt);
- if (r != EOK)
- break;
-
- size -= block_size * fblock_cnt;
- u8_buf += block_size * fblock_cnt;
- f->fpos += block_size * fblock_cnt;
-
- if (wcnt)
- *wcnt += block_size * fblock_cnt;
-
- fblock_start = fblock;
- fblock_cnt = 1;
- }
-
- /*Stop write back cache mode*/
- ext4_block_cache_write_back(f->mp->fs.bdev, 0);
-
- if (r != EOK)
- goto Finish;
-
- if (size) {
- if (sblock < file_blocks) {
- r = ext4_fs_get_inode_data_block_index(&ref, sblock,
- &fblock);
- if (r != EOK)
- goto Finish;
- } else {
- r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
- if (r != EOK)
- goto Finish;
- }
-
- r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
- if (r != EOK)
- goto Finish;
-
- memcpy(b.data, u8_buf, size);
- b.dirty = true;
-
- r = ext4_block_set(f->mp->fs.bdev, &b);
- if (r != EOK)
- goto Finish;
-
- f->fpos += size;
-
- if (wcnt)
- *wcnt += size;
- }
-
- if (f->fpos > f->fsize) {
- f->fsize = f->fpos;
- ext4_inode_set_size(ref.inode, f->fsize);
- ref.dirty = true;
- }
-
-Finish:
- ext4_fs_put_inode_ref(&ref);
- EXT4_MP_UNLOCK(f->mp);
- return r;
-}
-
-int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
-{
- switch (origin) {
- case SEEK_SET:
- if (offset > f->fsize)
- return EINVAL;
-
- f->fpos = offset;
- return EOK;
- case SEEK_CUR:
- if ((offset + f->fpos) > f->fsize)
- return EINVAL;
-
- f->fpos += offset;
- return EOK;
- case SEEK_END:
- if (offset > f->fsize)
- return EINVAL;
-
- f->fpos = f->fsize - offset;
- return EOK;
- }
- return EINVAL;
-}
-
-uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
-
-uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
-
-int ext4_fchmod(ext4_file *f, uint32_t mode)
-{
- int r;
- uint32_t ino;
- struct ext4_sblock *sb;
- struct ext4_inode_ref inode_ref;
- struct ext4_mountpoint *mp = f->mp;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- ino = f->inode;
- r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- sb = &f->mp->fs.sb;
- ext4_inode_set_mode(sb, inode_ref.inode, mode);
- inode_ref.dirty = true;
-
- ext4_fs_put_inode_ref(&inode_ref);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid)
-{
- int r;
- uint32_t ino;
- struct ext4_inode_ref inode_ref;
- struct ext4_mountpoint *mp = f->mp;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- ino = f->inode;
- r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- ext4_inode_set_uid(inode_ref.inode, uid);
- ext4_inode_set_gid(inode_ref.inode, gid);
- inode_ref.dirty = true;
-
- ext4_fs_put_inode_ref(&inode_ref);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_file_set_atime(ext4_file *f, uint32_t atime)
-{
- int r;
- uint32_t ino;
- struct ext4_inode_ref inode_ref;
- struct ext4_mountpoint *mp = f->mp;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- ino = f->inode;
- r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- ext4_inode_set_access_time(inode_ref.inode, atime);
- inode_ref.dirty = true;
-
- ext4_fs_put_inode_ref(&inode_ref);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_file_set_mtime(ext4_file *f, uint32_t mtime)
-{
- int r;
- uint32_t ino;
- struct ext4_inode_ref inode_ref;
- struct ext4_mountpoint *mp = f->mp;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- ino = f->inode;
- r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- ext4_inode_set_modification_time(inode_ref.inode, mtime);
- inode_ref.dirty = true;
-
- ext4_fs_put_inode_ref(&inode_ref);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_file_set_ctime(ext4_file *f, uint32_t ctime)
-{
- int r;
- uint32_t ino;
- struct ext4_inode_ref inode_ref;
- struct ext4_mountpoint *mp = f->mp;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- ino = f->inode;
- r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
- inode_ref.dirty = true;
-
- ext4_fs_put_inode_ref(&inode_ref);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-/*********************************DIRECTORY OPERATION************************/
-
-int ext4_dir_rm(const char *path)
-{
- int r;
- int len;
- ext4_file f;
-
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- struct ext4_inode_ref current;
- struct ext4_inode_ref child;
- struct ext4_directory_iterator it;
-
- uint32_t name_off;
- uint32_t inode_up;
- uint32_t inode_current;
- uint32_t depth = 1;
-
- bool has_children;
- bool is_goal;
- bool dir_end;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- /*Check if exist.*/
- r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- path += name_off;
- len = ext4_path_check(path, &is_goal);
-
- inode_current = f.inode;
- dir_end = false;
-
- ext4_block_cache_write_back(mp->fs.bdev, 1);
-
- do {
- /*Load directory node.*/
- r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, ¤t);
- if (r != EOK) {
- break;
- }
-
- /*Initialize iterator.*/
- r = ext4_dir_iterator_init(&it, ¤t, 0);
- if (r != EOK) {
- ext4_fs_put_inode_ref(¤t);
- break;
- }
-
- while (r == EOK) {
-
- if (!it.current) {
- dir_end = true;
- break;
- }
-
- /*Get up directory inode when ".." entry*/
- if ((it.current->name_length == 2) &&
- ext4_is_dots(it.current->name,
- it.current->name_length)) {
- inode_up = ext4_dir_entry_ll_get_inode(it.current);
- }
-
- /*If directory or file entry, but not "." ".." entry*/
- if (!ext4_is_dots(it.current->name,
- it.current->name_length)) {
-
- /*Get child inode reference do unlink
- * directory/file.*/
- r = ext4_fs_get_inode_ref(&f.mp->fs,
- ext4_dir_entry_ll_get_inode(it.current),
- &child);
- if (r != EOK)
- break;
-
- /*If directory with no leaf children*/
- r = ext4_has_children(&has_children, &child);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&child);
- break;
- }
-
- if (has_children) {
- /*Has directory children. Go into this
- * directory.*/
- inode_up = inode_current;
- inode_current = ext4_dir_entry_ll_get_inode(it.current);
- depth++;
- ext4_fs_put_inode_ref(&child);
- break;
- }
-
- /*No children in child directory or file. Just
- * unlink.*/
- r = ext4_unlink(f.mp, ¤t, &child,
- (char *)it.current->name,
- it.current->name_length);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&child);
- break;
- }
-
- ext4_inode_set_deletion_time(child.inode,
- 0xFFFFFFFF);
- ext4_inode_set_links_count(child.inode, 0);
- child.dirty = true;
- /*Turncate*/
- r = ext4_fs_truncate_inode(&child, 0);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&child);
- break;
- }
-
- r = ext4_fs_free_inode(&child);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&child);
- break;
- }
-
- r = ext4_fs_put_inode_ref(&child);
- if (r != EOK)
- break;
- }
-
- r = ext4_dir_iterator_next(&it);
- }
-
- if (dir_end) {
- /*Directory iterator reached last entry*/
- ext4_has_children(&has_children, ¤t);
- if (!has_children) {
- inode_current = inode_up;
- if (depth)
- depth--;
- }
- /*Last unlink*/
- if (!depth) {
- /*Load parent.*/
- struct ext4_inode_ref parent;
- r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
- &parent);
- if (r != EOK)
- goto End;
-
- /* In this place all directories should be
- * unlinked.
- * Last unlink from root of current directory*/
- r = ext4_unlink(f.mp, &parent, ¤t,
- (char *)path, len);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&parent);
- goto End;
- }
-
- if (ext4_inode_get_links_count(current.inode) ==
- 2) {
- ext4_inode_set_deletion_time(
- current.inode, 0xFFFFFFFF);
- ext4_inode_set_links_count(
- current.inode, 0);
- current.dirty = true;
- /*Turncate*/
- r = ext4_fs_truncate_inode(¤t, 0);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&parent);
- goto End;
- }
-
- r = ext4_fs_free_inode(¤t);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&parent);
- goto End;
- }
- }
-
- r = ext4_fs_put_inode_ref(&parent);
- if (r != EOK)
- goto End;
- }
- }
-
- End:
- ext4_dir_iterator_fini(&it);
- ext4_fs_put_inode_ref(¤t);
- dir_end = false;
-
- /*When something goes wrong. End loop.*/
- if (r != EOK)
- break;
-
- } while (depth);
-
- ext4_block_cache_write_back(mp->fs.bdev, 0);
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_dir_mk(const char *path)
-{
- int r;
- ext4_file f;
-
- struct ext4_mountpoint *mp = ext4_get_mount(path);
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
-
- /*Check if exist.*/
- r = ext4_generic_open(&f, path, "r", false, 0, 0);
- if (r == EOK) {
- /*Directory already created*/
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- /*Create new dir*/
- r = ext4_generic_open(&f, path, "w", false, 0, 0);
- if (r != EOK) {
- EXT4_MP_UNLOCK(mp);
- return r;
- }
-
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_dir_open(ext4_dir *d, const char *path)
-{
- struct ext4_mountpoint *mp = ext4_get_mount(path);
- int r;
-
- if (!mp)
- return ENOENT;
-
- EXT4_MP_LOCK(mp);
- r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
- d->next_off = 0;
- EXT4_MP_UNLOCK(mp);
- return r;
-}
-
-int ext4_dir_close(ext4_dir *d) { return ext4_fclose(&d->f); }
-
-const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
-{
-#define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
-
- int r;
- ext4_direntry *de = 0;
- struct ext4_inode_ref dir;
- struct ext4_directory_iterator it;
-
- EXT4_MP_LOCK(d->f.mp);
-
- if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM)
- return 0;
-
- r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
- if (r != EOK) {
- goto Finish;
- }
-
- r = ext4_dir_iterator_init(&it, &dir, d->next_off);
- if (r != EOK) {
- ext4_fs_put_inode_ref(&dir);
- goto Finish;
- }
-
- memcpy(&d->de, it.current, sizeof(ext4_direntry));
- de = &d->de;
-
- ext4_dir_iterator_next(&it);
-
- d->next_off =
- it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;
-
- ext4_dir_iterator_fini(&it);
- ext4_fs_put_inode_ref(&dir);
-
-Finish:
- EXT4_MP_UNLOCK(d->f.mp);
- return de;
-}
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * 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.h
+ * @brief Ext4 high level operations (file, directory, mountpoints...)
+ */
+
+#include "ext4_config.h"
+#include "ext4_blockdev.h"
+#include "ext4_types.h"
+#include "ext4_debug.h"
+#include "ext4_errno.h"
+#include "ext4_fs.h"
+#include "ext4_dir.h"
+#include "ext4_inode.h"
+#include "ext4_super.h"
+#include "ext4_dir_idx.h"
+#include "ext4.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/**@brief Mount point OS dependent lock*/
+#define EXT4_MP_LOCK(_m) \
+ do { \
+ if ((_m)->os_locks) \
+ (_m)->os_locks->lock(); \
+ } while (0)
+
+/**@brief Mount point OS dependent unlock*/
+#define EXT4_MP_UNLOCK(_m) \
+ do { \
+ if ((_m)->os_locks) \
+ (_m)->os_locks->unlock(); \
+ } while (0)
+
+/**@brief Mount point descriptor.*/
+struct ext4_mountpoint {
+
+ /**@brief Mount done flag.*/
+ bool mounted;
+
+ /**@brief Mount point name (@ref ext4_mount)*/
+ char name[32];
+
+ /**@brief OS dependent lock/unlock functions.*/
+ const struct ext4_lock *os_locks;
+
+ /**@brief Ext4 filesystem internals.*/
+ struct ext4_fs fs;
+
+ /**@brief Dynamic allocation cache flag.*/
+ bool cache_dynamic;
+};
+
+/**@brief Block devices descriptor.*/
+struct _ext4_devices {
+
+ /**@brief Block device name (@ref ext4_device_register)*/
+ char name[32];
+
+ /**@brief Block device handle.*/
+ struct ext4_blockdev *bd;
+
+ /**@brief Block cache handle.*/
+ struct ext4_bcache *bc;
+};
+
+/**@brief Block devices.*/
+struct _ext4_devices _bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
+
+/**@brief Mountpoints.*/
+struct ext4_mountpoint _mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
+
+int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,
+ const char *dev_name)
+{
+ uint32_t i;
+ ext4_assert(bd && dev_name);
+
+ for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
+ if (!_bdevices[i].bd) {
+ strcpy(_bdevices[i].name, dev_name);
+ _bdevices[i].bd = bd;
+ _bdevices[i].bc = bc;
+ return EOK;
+ }
+
+ if (!strcmp(_bdevices[i].name, dev_name))
+ return EOK;
+ }
+ return ENOSPC;
+}
+
+/****************************************************************************/
+
+static bool ext4_is_dots(const uint8_t *name, size_t name_size)
+{
+ if ((name_size == 1) && (name[0] == '.'))
+ return true;
+
+ if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
+ return true;
+
+ return false;
+}
+
+static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)
+{
+ struct ext4_fs *fs = enode->fs;
+
+ /* Check if node is directory */
+ if (!ext4_inode_is_type(&fs->sb, enode->inode,
+ EXT4_INODE_MODE_DIRECTORY)) {
+ *has_children = false;
+ return EOK;
+ }
+
+ struct ext4_directory_iterator it;
+ int rc = ext4_dir_iterator_init(&it, enode, 0);
+ if (rc != EOK)
+ return rc;
+
+ /* Find a non-empty directory entry */
+ bool found = false;
+ while (it.current != NULL) {
+ if (ext4_dir_entry_ll_get_inode(it.current) != 0) {
+ uint16_t name_size = ext4_dir_entry_ll_get_name_length(
+ &fs->sb, it.current);
+ if (!ext4_is_dots(it.current->name, name_size)) {
+ found = true;
+ break;
+ }
+ }
+
+ rc = ext4_dir_iterator_next(&it);
+ if (rc != EOK) {
+ ext4_dir_iterator_fini(&it);
+ return rc;
+ }
+ }
+
+ rc = ext4_dir_iterator_fini(&it);
+ if (rc != EOK)
+ return rc;
+
+ *has_children = found;
+
+ return EOK;
+}
+
+static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,
+ struct ext4_inode_ref *child, const char *name,
+ uint32_t name_len)
+{
+ /* Check maximum name length */
+ if (name_len > EXT4_DIRECTORY_FILENAME_LEN)
+ return EINVAL;
+
+ /* Add entry to parent directory */
+ int rc = ext4_dir_add_entry(parent, name, name_len, child);
+ if (rc != EOK)
+ return rc;
+
+ /* Fill new dir -> add '.' and '..' entries.
+ * Also newly allocated inode should have 0 link count.
+ */
+ if (ext4_inode_is_type(&mp->fs.sb, child->inode,
+ EXT4_INODE_MODE_DIRECTORY) &&
+ ext4_inode_get_links_count(child->inode) == 0) {
+ rc = ext4_dir_add_entry(child, ".", strlen("."), child);
+ if (rc != EOK) {
+ ext4_dir_remove_entry(parent, name, strlen(name));
+ return rc;
+ }
+
+ rc = ext4_dir_add_entry(child, "..", strlen(".."), parent);
+ if (rc != EOK) {
+ ext4_dir_remove_entry(parent, name, strlen(name));
+ ext4_dir_remove_entry(child, ".", strlen("."));
+ return rc;
+ }
+
+ /*New empty directory. Two links (. and ..) */
+ ext4_inode_set_links_count(child->inode, 2);
+
+#if CONFIG_DIR_INDEX_ENABLE
+ /* Initialize directory index if supported */
+ if (ext4_sb_has_feature_compatible(
+ &mp->fs.sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
+ rc = ext4_dir_dx_init(child);
+ if (rc != EOK)
+ return rc;
+
+ ext4_inode_set_flag(child->inode,
+ EXT4_INODE_FLAG_INDEX);
+ child->dirty = true;
+ }
+#endif
+
+ ext4_fs_inode_links_count_inc(parent);
+ child->dirty = true;
+ parent->dirty = true;
+ } else {
+ if (ext4_inode_is_type(&mp->fs.sb, child->inode,
+ EXT4_INODE_MODE_DIRECTORY)) {
+ /* FIXME: SO TRICKY. */
+ int has_flag_index = ext4_inode_has_flag(
+ child->inode, EXT4_INODE_FLAG_INDEX);
+ struct ext4_directory_search_result result;
+ if (has_flag_index)
+ ext4_inode_clear_flag(child->inode,
+ EXT4_INODE_FLAG_INDEX);
+
+ rc = ext4_dir_find_entry(&result, child, "..",
+ strlen(".."));
+ if (has_flag_index)
+ ext4_inode_set_flag(child->inode,
+ EXT4_INODE_FLAG_INDEX);
+
+ if (rc != EOK)
+ return EIO;
+
+ ext4_dir_entry_ll_set_inode(result.dentry,
+ parent->index);
+ result.block.dirty = true;
+ rc = ext4_dir_destroy_result(child, &result);
+ if (rc != EOK)
+ return rc;
+
+ ext4_fs_inode_links_count_inc(parent);
+ parent->dirty = true;
+ } else {
+ ext4_fs_inode_links_count_inc(child);
+ child->dirty = true;
+ }
+ }
+
+ return rc;
+}
+
+static int ext4_unlink(struct ext4_mountpoint *mp,
+ struct ext4_inode_ref *parent,
+ struct ext4_inode_ref *child_inode_ref, const char *name,
+ uint32_t name_len)
+{
+ bool has_children;
+ int rc = ext4_has_children(&has_children, child_inode_ref);
+ if (rc != EOK)
+ return rc;
+
+ /* Cannot unlink non-empty node */
+ if (has_children)
+ return ENOTSUP;
+
+ /* Remove entry from parent directory */
+ rc = ext4_dir_remove_entry(parent, name, name_len);
+ if (rc != EOK)
+ return rc;
+
+ bool is_dir = ext4_inode_is_type(&mp->fs.sb, child_inode_ref->inode,
+ EXT4_INODE_MODE_DIRECTORY);
+
+ /* If directory - handle links from parent */
+ if (is_dir) {
+ // ext4_assert(ext4_inode_get_links_count(child_inode_ref->inode)
+ // == 1);
+ ext4_fs_inode_links_count_dec(parent);
+ parent->dirty = true;
+ }
+
+ /*
+ * TODO: Update timestamps of the parent
+ * (when we have wall-clock time).
+ *
+ * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
+ * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
+ * parent->dirty = true;
+ */
+
+ /*
+ * TODO: Update timestamp for inode.
+ *
+ * ext4_inode_set_change_inode_time(child_inode_ref->inode,
+ * (uint32_t) now);
+ */
+ if (ext4_inode_get_links_count(child_inode_ref->inode)) {
+ ext4_fs_inode_links_count_dec(child_inode_ref);
+ child_inode_ref->dirty = true;
+ }
+
+ return EOK;
+}
+
+/****************************************************************************/
+
+int ext4_mount(const char *dev_name, const char *mount_point)
+{
+ ext4_assert(mount_point && dev_name);
+ int r;
+ int i;
+
+ uint32_t bsize;
+ struct ext4_blockdev *bd = 0;
+ struct ext4_bcache *bc = 0;
+ struct ext4_mountpoint *mp = 0;
+
+ if (mount_point[strlen(mount_point) - 1] != '/')
+ return ENOTSUP;
+
+ for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
+ if (_bdevices[i].name) {
+ if (!strcmp(dev_name, _bdevices[i].name)) {
+ bd = _bdevices[i].bd;
+ bc = _bdevices[i].bc;
+ break;
+ }
+ }
+ }
+
+ if (!bd)
+ return ENODEV;
+
+ for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
+ if (!_mp[i].mounted) {
+ strcpy(_mp[i].name, mount_point);
+ _mp[i].mounted = 1;
+ mp = &_mp[i];
+ break;
+ }
+
+ if (!strcmp(_mp[i].name, mount_point))
+ return EOK;
+ }
+
+ if (!mp)
+ return ENOMEM;
+
+ r = ext4_block_init(bd);
+ if (r != EOK)
+ return r;
+
+ r = ext4_fs_init(&mp->fs, bd);
+ if (r != EOK) {
+ ext4_block_fini(bd);
+ return r;
+ }
+
+ bsize = ext4_sb_get_block_size(&mp->fs.sb);
+ ext4_block_set_lb_size(bd, bsize);
+
+ mp->cache_dynamic = 0;
+
+ if (!bc) {
+ /*Automatic block cache alloc.*/
+ mp->cache_dynamic = 1;
+ bc = malloc(sizeof(struct ext4_bcache));
+
+ r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
+ bsize);
+ if (r != EOK) {
+ free(bc);
+ ext4_block_fini(bd);
+ return r;
+ }
+ }
+
+ if (bsize != bc->itemsize)
+ return ENOTSUP;
+
+ /*Bind block cache to block device*/
+ r = ext4_block_bind_bcache(bd, bc);
+ if (r != EOK) {
+ ext4_block_fini(bd);
+ if (mp->cache_dynamic) {
+ ext4_bcache_fini_dynamic(bc);
+ free(bc);
+ }
+ return r;
+ }
+
+ return r;
+}
+
+int ext4_umount(const char *mount_point)
+{
+ int i;
+ int r;
+ struct ext4_mountpoint *mp = 0;
+
+ for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
+ if (!strcmp(_mp[i].name, mount_point)) {
+ mp = &_mp[i];
+ break;
+ }
+ }
+
+ if (!mp)
+ return ENODEV;
+
+ r = ext4_fs_fini(&mp->fs);
+ if (r != EOK)
+ return r;
+
+ mp->mounted = 0;
+
+ if (mp->cache_dynamic) {
+ ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
+ free(mp->fs.bdev->bc);
+ }
+
+ return ext4_block_fini(mp->fs.bdev);
+}
+
+int ext4_mount_point_stats(const char *mount_point,
+ struct ext4_mount_stats *stats)
+{
+ uint32_t i;
+ struct ext4_mountpoint *mp = 0;
+
+ for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
+ if (!strcmp(_mp[i].name, mount_point)) {
+ mp = &_mp[i];
+ break;
+ }
+ }
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+ stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);
+ stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);
+ stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb);
+ stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);
+ stats->block_size = ext4_sb_get_block_size(&mp->fs.sb);
+
+ stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);
+ stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);
+ stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);
+
+ memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);
+ EXT4_MP_UNLOCK(mp);
+
+ return EOK;
+}
+
+int ext4_mount_setup_locks(const char *mount_point,
+ const struct ext4_lock *locks)
+{
+ uint32_t i;
+ struct ext4_mountpoint *mp = 0;
+
+ for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
+ if (!strcmp(_mp[i].name, mount_point)) {
+ mp = &_mp[i];
+ break;
+ }
+ }
+ if (!mp)
+ return ENOENT;
+
+ mp->os_locks = locks;
+ return EOK;
+}
+
+/********************************FILE OPERATIONS*****************************/
+
+static struct ext4_mountpoint *ext4_get_mount(const char *path)
+{
+ int i;
+ for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
+
+ if (!_mp[i].mounted)
+ continue;
+
+ if (!strncmp(_mp[i].name, path, strlen(_mp[i].name)))
+ return &_mp[i];
+ }
+ return 0;
+}
+
+static int ext4_path_check(const char *path, bool *is_goal)
+{
+ int i;
+
+ for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
+
+ if (path[i] == '/') {
+ *is_goal = false;
+ return i;
+ }
+
+ if (path[i] == 0) {
+ *is_goal = true;
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
+{
+ if (!flags)
+ return false;
+
+ if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {
+ *file_flags = O_RDONLY;
+ return true;
+ }
+
+ if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {
+ *file_flags = O_WRONLY | O_CREAT | O_TRUNC;
+ return true;
+ }
+
+ if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {
+ *file_flags = O_WRONLY | O_CREAT | O_APPEND;
+ return true;
+ }
+
+ if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||
+ !strcmp(flags, "r+b")) {
+ *file_flags = O_RDWR;
+ return true;
+ }
+
+ if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||
+ !strcmp(flags, "w+b")) {
+ *file_flags = O_RDWR | O_CREAT | O_TRUNC;
+ return true;
+ }
+
+ if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||
+ !strcmp(flags, "a+b")) {
+ *file_flags = O_RDWR | O_CREAT | O_APPEND;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * NOTICE: if filetype is equal to EXT4_DIRECTORY_FILETYPE_UNKNOWN,
+ * any filetype of the target dir entry will be accepted.
+ */
+static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
+ int filetype, uint32_t *parent_inode,
+ uint32_t *name_off)
+{
+ bool is_goal = false;
+ uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;
+ uint32_t next_inode;
+
+ int r;
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ struct ext4_directory_search_result result;
+ struct ext4_inode_ref ref;
+
+ f->mp = 0;
+
+ if (!mp)
+ return ENOENT;
+
+ f->flags = flags;
+
+ /*Skip mount point*/
+ path += strlen(mp->name);
+
+ if (name_off)
+ *name_off = strlen(mp->name);
+
+ /*Load root*/
+ r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
+
+ if (r != EOK)
+ return r;
+
+ if (parent_inode)
+ *parent_inode = ref.index;
+
+ int len = ext4_path_check(path, &is_goal);
+
+ while (1) {
+
+ len = ext4_path_check(path, &is_goal);
+
+ if (!len) {
+ /*If root open was request.*/
+ if (is_goal &&
+ ((filetype == EXT4_DIRECTORY_FILETYPE_DIR) ||
+ (filetype == EXT4_DIRECTORY_FILETYPE_UNKNOWN)))
+ break;
+
+ r = ENOENT;
+ break;
+ }
+
+ r = ext4_dir_find_entry(&result, &ref, path, len);
+ if (r != EOK) {
+
+ if (r != ENOENT)
+ break;
+
+ if (!(f->flags & O_CREAT))
+ break;
+
+ /*O_CREAT allows create new entry*/
+ struct ext4_inode_ref child_ref;
+ r = ext4_fs_alloc_inode(
+ &mp->fs, &child_ref,
+ is_goal ? (filetype == EXT4_DIRECTORY_FILETYPE_DIR)
+ : true);
+ if (r != EOK)
+ break;
+
+ /*Destroy last result*/
+ ext4_dir_destroy_result(&ref, &result);
+
+ /*Link with root dir.*/
+ r = ext4_link(mp, &ref, &child_ref, path, len);
+ if (r != EOK) {
+ /*Fail. Free new inode.*/
+ ext4_fs_free_inode(&child_ref);
+ /*We do not want to write new inode.
+ But block has to be released.*/
+ child_ref.dirty = false;
+ ext4_fs_put_inode_ref(&child_ref);
+ break;
+ }
+
+ ext4_fs_put_inode_ref(&child_ref);
+
+ continue;
+ }
+
+ if (parent_inode)
+ *parent_inode = ref.index;
+
+ next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
+ inode_type =
+ ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
+
+ r = ext4_dir_destroy_result(&ref, &result);
+ if (r != EOK)
+ break;
+
+ /*If expected file error*/
+ if (inode_type != EXT4_DIRECTORY_FILETYPE_DIR && !is_goal) {
+ r = ENOENT;
+ break;
+ }
+ if (filetype != EXT4_DIRECTORY_FILETYPE_UNKNOWN) {
+ if ((inode_type != filetype) && is_goal) {
+ r = ENOENT;
+ break;
+ }
+ }
+
+ r = ext4_fs_put_inode_ref(&ref);
+ if (r != EOK)
+ break;
+
+ r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
+ if (r != EOK)
+ break;
+
+ if (is_goal)
+ break;
+
+ path += len + 1;
+
+ if (name_off)
+ *name_off += len + 1;
+ };
+
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&ref);
+ return r;
+ }
+
+ if (is_goal) {
+
+ if ((f->flags & O_TRUNC) &&
+ (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)) {
+
+ r = ext4_fs_truncate_inode(&ref, 0);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&ref);
+ return r;
+ }
+ }
+
+ f->mp = mp;
+ f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
+ f->inode = ref.index;
+ f->fpos = 0;
+
+ if (f->flags & O_APPEND)
+ f->fpos = f->fsize;
+ }
+
+ r = ext4_fs_put_inode_ref(&ref);
+ return r;
+}
+
+/****************************************************************************/
+
+static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
+ bool file_expect, uint32_t *parent_inode,
+ uint32_t *name_off)
+{
+ uint32_t iflags;
+ int filetype;
+ if (ext4_parse_flags(flags, &iflags) == false)
+ return EINVAL;
+
+ if (file_expect == true)
+ filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;
+ else
+ filetype = EXT4_DIRECTORY_FILETYPE_DIR;
+
+ return ext4_generic_open2(f, path, iflags, filetype, parent_inode,
+ name_off);
+}
+
+static int __ext4_create_hardlink(const char *path,
+ struct ext4_inode_ref *child_ref)
+{
+ bool is_goal = false;
+ uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;
+ uint32_t next_inode;
+
+ int r;
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ struct ext4_directory_search_result result;
+ struct ext4_inode_ref ref;
+
+ if (!mp)
+ return ENOENT;
+
+ /*Skip mount point*/
+ path += strlen(mp->name);
+
+ /*Load root*/
+ r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
+
+ if (r != EOK)
+ return r;
+
+ int len = ext4_path_check(path, &is_goal);
+
+ while (1) {
+
+ len = ext4_path_check(path, &is_goal);
+
+ if (!len) {
+ /*If root open was request.*/
+ if (is_goal)
+ r = EINVAL;
+ else
+ r = ENOENT;
+ break;
+ }
+
+ r = ext4_dir_find_entry(&result, &ref, path, len);
+ if (r != EOK) {
+
+ if (r != ENOENT || !is_goal)
+ break;
+
+ /*Destroy last result*/
+ ext4_dir_destroy_result(&ref, &result);
+
+ /*Link with root dir.*/
+ r = ext4_link(mp, &ref, child_ref, path, len);
+ break;
+ }
+
+ next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
+ inode_type =
+ ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
+
+ r = ext4_dir_destroy_result(&ref, &result);
+ if (r != EOK)
+ break;
+
+ if (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE) {
+ if (is_goal)
+ r = EEXIST;
+ else
+ r = ENOENT;
+
+ break;
+ }
+
+ r = ext4_fs_put_inode_ref(&ref);
+ if (r != EOK)
+ break;
+
+ r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
+ if (r != EOK)
+ break;
+
+ if (is_goal)
+ break;
+
+ path += len + 1;
+ };
+
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&ref);
+ return r;
+ }
+
+ r = ext4_fs_put_inode_ref(&ref);
+ return r;
+}
+
+static int __ext4_get_inode_ref_remove_hardlink(const char *path,
+ struct ext4_inode_ref *child)
+{
+ ext4_file f;
+ uint32_t parent_inode;
+ uint32_t name_off;
+ bool is_goal;
+ int r;
+ int len;
+ struct ext4_inode_ref parent;
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+ if (!mp)
+ return ENOENT;
+
+ r = ext4_generic_open2(&f, path, O_RDONLY,
+ EXT4_DIRECTORY_FILETYPE_UNKNOWN, &parent_inode,
+ &name_off);
+ if (r != EOK)
+ return r;
+
+ /*Load parent*/
+ r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
+ if (r != EOK) {
+ return r;
+ }
+
+ /*We have file to unlink. Load it.*/
+ r = ext4_fs_get_inode_ref(&mp->fs, f.inode, child);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&parent);
+ return r;
+ }
+
+ if (r != EOK)
+ goto Finish;
+
+ /*Set path*/
+ path += name_off;
+
+ len = ext4_path_check(path, &is_goal);
+
+ /*Unlink from parent*/
+ r = ext4_unlink(mp, &parent, child, path, len);
+ if (r != EOK)
+ goto Finish;
+
+Finish:
+ if (r != EOK)
+ ext4_fs_put_inode_ref(child);
+
+ ext4_fs_put_inode_ref(&parent);
+ return r;
+}
+
+int ext4_frename(const char *path, const char *new_path)
+{
+ int r;
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ struct ext4_inode_ref inode_ref;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ r = __ext4_get_inode_ref_remove_hardlink(path, &inode_ref);
+ if (r != EOK)
+ goto Finish;
+
+ r = __ext4_create_hardlink(new_path, &inode_ref);
+ if (r != EOK)
+ r = __ext4_create_hardlink(path, &inode_ref);
+
+ ext4_fs_put_inode_ref(&inode_ref);
+
+Finish:
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+/****************************************************************************/
+
+int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+
+ if (!mp)
+ return ENOENT;
+
+ *sb = &mp->fs.sb;
+ return EOK;
+}
+
+int ext4_cache_write_back(const char *path, bool on)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+ ext4_block_cache_write_back(mp->fs.bdev, on);
+ EXT4_MP_UNLOCK(mp);
+ return EOK;
+}
+
+int ext4_fremove(const char *path)
+{
+ ext4_file f;
+ uint32_t parent_inode;
+ uint32_t name_off;
+ bool is_goal;
+ int r;
+ int len;
+ struct ext4_inode_ref child;
+ struct ext4_inode_ref parent;
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+ r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ /*Load parent*/
+ r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ /*We have file to delete. Load it.*/
+ r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&parent);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ /*Set path*/
+ path += name_off;
+
+ len = ext4_path_check(path, &is_goal);
+
+ /*Unlink from parent*/
+ r = ext4_unlink(mp, &parent, &child, path, len);
+ if (r != EOK)
+ goto Finish;
+
+ /*Link count is zero, the inode should be freed. */
+ if (!ext4_inode_get_links_count(child.inode)) {
+ printf("ttttt\n");
+ ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);
+ /*Turncate*/
+ ext4_block_cache_write_back(mp->fs.bdev, 1);
+ /*Truncate may be IO heavy. Do it writeback cache mode.*/
+ r = ext4_fs_truncate_inode(&child, 0);
+ ext4_block_cache_write_back(mp->fs.bdev, 0);
+
+ if (r != EOK)
+ goto Finish;
+
+ r = ext4_fs_free_inode(&child);
+ if (r != EOK)
+ goto Finish;
+ }
+
+Finish:
+ ext4_fs_put_inode_ref(&child);
+ ext4_fs_put_inode_ref(&parent);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_fill_raw_inode(const char *mount_point, uint32_t ino,
+ struct ext4_inode *inode)
+{
+ int r;
+ struct ext4_inode_ref inode_ref;
+ struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ /*Load parent*/
+ r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
+
+ ext4_fs_put_inode_ref(&inode_ref);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_fopen(ext4_file *f, const char *path, const char *flags)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ int r;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+ ext4_block_cache_write_back(mp->fs.bdev, 1);
+ r = ext4_generic_open(f, path, flags, true, 0, 0);
+ ext4_block_cache_write_back(mp->fs.bdev, 0);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ int r;
+ int filetype;
+
+ if (!mp)
+ return ENOENT;
+
+ if (file_expect == true)
+ filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;
+ else
+ filetype = EXT4_DIRECTORY_FILETYPE_DIR;
+
+ EXT4_MP_LOCK(mp);
+ ext4_block_cache_write_back(mp->fs.bdev, 1);
+ r = ext4_generic_open2(f, path, flags, filetype, 0, 0);
+ ext4_block_cache_write_back(mp->fs.bdev, 0);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_fclose(ext4_file *f)
+{
+ ext4_assert(f && f->mp);
+
+ f->mp = 0;
+ f->flags = 0;
+ f->inode = 0;
+ f->fpos = f->fsize = 0;
+
+ return EOK;
+}
+
+int ext4_ftruncate(ext4_file *f, uint64_t size)
+{
+ struct ext4_inode_ref ref;
+ int r;
+
+ ext4_assert(f && f->mp);
+
+ if (f->flags & O_RDONLY)
+ return EPERM;
+
+ EXT4_MP_LOCK(f->mp);
+
+ r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+ }
+
+ /*Sync file size*/
+ f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
+ if (f->fsize <= size) {
+ r = EOK;
+ goto Finish;
+ }
+
+ /*Start write back cache mode.*/
+ r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
+ if (r != EOK)
+ goto Finish;
+
+ r = ext4_fs_truncate_inode(&ref, size);
+ if (r != EOK)
+ goto Finish;
+
+ f->fsize = size;
+ if (f->fpos > size)
+ f->fpos = size;
+
+ /*Stop write back cache mode*/
+ ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+ if (r != EOK)
+ goto Finish;
+
+Finish:
+ ext4_fs_put_inode_ref(&ref);
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+}
+
+int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)
+{
+ uint32_t u;
+ uint32_t fblock;
+ uint32_t fblock_start;
+ uint32_t fblock_cnt;
+ uint32_t sblock;
+ uint32_t sblock_end;
+ uint32_t block_size;
+ uint8_t *u8_buf = buf;
+ int r;
+ struct ext4_block b;
+ struct ext4_inode_ref ref;
+
+ ext4_assert(f && f->mp);
+
+ if (f->flags & O_WRONLY)
+ return EPERM;
+
+ if (!size)
+ return EOK;
+
+ EXT4_MP_LOCK(f->mp);
+
+ if (rcnt)
+ *rcnt = 0;
+
+ r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+ }
+
+ /*Sync file size*/
+ f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
+
+ block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
+ size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;
+ sblock = (f->fpos) / block_size;
+ sblock_end = (f->fpos + size) / block_size;
+ u = (f->fpos) % block_size;
+
+ if (u) {
+
+ uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
+
+ r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
+ if (r != EOK)
+ goto Finish;
+
+ r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
+ if (r != EOK)
+ goto Finish;
+
+ memcpy(u8_buf, b.data + u, ll);
+
+ r = ext4_block_set(f->mp->fs.bdev, &b);
+ if (r != EOK)
+ goto Finish;
+
+ u8_buf += ll;
+ size -= ll;
+ f->fpos += ll;
+
+ if (rcnt)
+ *rcnt += ll;
+
+ sblock++;
+ }
+
+ fblock_start = 0;
+ fblock_cnt = 0;
+ while (size >= block_size) {
+ while (sblock < sblock_end) {
+ r = ext4_fs_get_inode_data_block_index(&ref, sblock,
+ &fblock);
+ if (r != EOK)
+ goto Finish;
+
+ sblock++;
+
+ if (!fblock_start) {
+ fblock_start = fblock;
+ }
+
+ if ((fblock_start + fblock_cnt) != fblock)
+ break;
+
+ fblock_cnt++;
+ }
+
+ r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
+ fblock_cnt);
+ if (r != EOK)
+ goto Finish;
+
+ size -= block_size * fblock_cnt;
+ u8_buf += block_size * fblock_cnt;
+ f->fpos += block_size * fblock_cnt;
+
+ if (rcnt)
+ *rcnt += block_size * fblock_cnt;
+
+ fblock_start = fblock;
+ fblock_cnt = 1;
+ }
+
+ if (size) {
+ r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
+ if (r != EOK)
+ goto Finish;
+
+ r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
+ if (r != EOK)
+ goto Finish;
+
+ memcpy(u8_buf, b.data, size);
+
+ r = ext4_block_set(f->mp->fs.bdev, &b);
+ if (r != EOK)
+ goto Finish;
+
+ f->fpos += size;
+
+ if (rcnt)
+ *rcnt += size;
+ }
+
+Finish:
+ ext4_fs_put_inode_ref(&ref);
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+}
+
+int ext4_fwrite(ext4_file *f, const void *buf, uint32_t size, uint32_t *wcnt)
+{
+ uint32_t u;
+ uint32_t fblock;
+
+ uint32_t sblock;
+ uint32_t sblock_end;
+ uint32_t file_blocks;
+ uint32_t block_size;
+ uint32_t fblock_start;
+ uint32_t fblock_cnt;
+
+ struct ext4_block b;
+ struct ext4_inode_ref ref;
+ const uint8_t *u8_buf = buf;
+ int r;
+
+ ext4_assert(f && f->mp);
+
+ if (f->flags & O_RDONLY)
+ return EPERM;
+
+ if (!size)
+ return EOK;
+
+ EXT4_MP_LOCK(f->mp);
+
+ if (wcnt)
+ *wcnt = 0;
+
+ r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+ }
+
+ /*Sync file size*/
+ f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
+
+ block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
+
+ sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;
+ sblock_end /= block_size;
+ file_blocks = (f->fsize / block_size);
+
+ if (f->fsize % block_size)
+ file_blocks++;
+
+ sblock = (f->fpos) / block_size;
+
+ u = (f->fpos) % block_size;
+
+ if (u) {
+ uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
+
+ r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
+ if (r != EOK)
+ goto Finish;
+
+ r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
+ if (r != EOK)
+ goto Finish;
+
+ memcpy(b.data + u, u8_buf, ll);
+ b.dirty = true;
+
+ r = ext4_block_set(f->mp->fs.bdev, &b);
+ if (r != EOK)
+ goto Finish;
+
+ u8_buf += ll;
+ size -= ll;
+ f->fpos += ll;
+
+ if (wcnt)
+ *wcnt += ll;
+
+ sblock++;
+ }
+
+ /*Start write back cache mode.*/
+ r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
+ if (r != EOK)
+ goto Finish;
+
+ fblock_start = 0;
+ fblock_cnt = 0;
+ while (size >= block_size) {
+
+ while (sblock < sblock_end) {
+ if (sblock < file_blocks) {
+ r = ext4_fs_get_inode_data_block_index(
+ &ref, sblock, &fblock);
+ if (r != EOK)
+ break;
+ } else {
+ r = ext4_fs_append_inode_block(&ref, &fblock,
+ &sblock);
+ if (r != EOK)
+ break;
+ }
+
+ sblock++;
+
+ if (!fblock_start) {
+ fblock_start = fblock;
+ }
+
+ if ((fblock_start + fblock_cnt) != fblock)
+ break;
+
+ fblock_cnt++;
+ }
+
+ r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
+ fblock_cnt);
+ if (r != EOK)
+ break;
+
+ size -= block_size * fblock_cnt;
+ u8_buf += block_size * fblock_cnt;
+ f->fpos += block_size * fblock_cnt;
+
+ if (wcnt)
+ *wcnt += block_size * fblock_cnt;
+
+ fblock_start = fblock;
+ fblock_cnt = 1;
+ }
+
+ /*Stop write back cache mode*/
+ ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+ if (r != EOK)
+ goto Finish;
+
+ if (size) {
+ if (sblock < file_blocks) {
+ r = ext4_fs_get_inode_data_block_index(&ref, sblock,
+ &fblock);
+ if (r != EOK)
+ goto Finish;
+ } else {
+ r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
+ if (r != EOK)
+ goto Finish;
+ }
+
+ r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
+ if (r != EOK)
+ goto Finish;
+
+ memcpy(b.data, u8_buf, size);
+ b.dirty = true;
+
+ r = ext4_block_set(f->mp->fs.bdev, &b);
+ if (r != EOK)
+ goto Finish;
+
+ f->fpos += size;
+
+ if (wcnt)
+ *wcnt += size;
+ }
+
+ if (f->fpos > f->fsize) {
+ f->fsize = f->fpos;
+ ext4_inode_set_size(ref.inode, f->fsize);
+ ref.dirty = true;
+ }
+
+Finish:
+ ext4_fs_put_inode_ref(&ref);
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+}
+
+int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
+{
+ switch (origin) {
+ case SEEK_SET:
+ if (offset > f->fsize)
+ return EINVAL;
+
+ f->fpos = offset;
+ return EOK;
+ case SEEK_CUR:
+ if ((offset + f->fpos) > f->fsize)
+ return EINVAL;
+
+ f->fpos += offset;
+ return EOK;
+ case SEEK_END:
+ if (offset > f->fsize)
+ return EINVAL;
+
+ f->fpos = f->fsize - offset;
+ return EOK;
+ }
+ return EINVAL;
+}
+
+uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
+
+uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
+
+int ext4_fchmod(ext4_file *f, uint32_t mode)
+{
+ int r;
+ uint32_t ino;
+ struct ext4_sblock *sb;
+ struct ext4_inode_ref inode_ref;
+ struct ext4_mountpoint *mp = f->mp;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ ino = f->inode;
+ r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ sb = &f->mp->fs.sb;
+ ext4_inode_set_mode(sb, inode_ref.inode, mode);
+ inode_ref.dirty = true;
+
+ ext4_fs_put_inode_ref(&inode_ref);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid)
+{
+ int r;
+ uint32_t ino;
+ struct ext4_inode_ref inode_ref;
+ struct ext4_mountpoint *mp = f->mp;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ ino = f->inode;
+ r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ ext4_inode_set_uid(inode_ref.inode, uid);
+ ext4_inode_set_gid(inode_ref.inode, gid);
+ inode_ref.dirty = true;
+
+ ext4_fs_put_inode_ref(&inode_ref);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_file_set_atime(ext4_file *f, uint32_t atime)
+{
+ int r;
+ uint32_t ino;
+ struct ext4_inode_ref inode_ref;
+ struct ext4_mountpoint *mp = f->mp;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ ino = f->inode;
+ r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ ext4_inode_set_access_time(inode_ref.inode, atime);
+ inode_ref.dirty = true;
+
+ ext4_fs_put_inode_ref(&inode_ref);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_file_set_mtime(ext4_file *f, uint32_t mtime)
+{
+ int r;
+ uint32_t ino;
+ struct ext4_inode_ref inode_ref;
+ struct ext4_mountpoint *mp = f->mp;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ ino = f->inode;
+ r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ ext4_inode_set_modification_time(inode_ref.inode, mtime);
+ inode_ref.dirty = true;
+
+ ext4_fs_put_inode_ref(&inode_ref);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_file_set_ctime(ext4_file *f, uint32_t ctime)
+{
+ int r;
+ uint32_t ino;
+ struct ext4_inode_ref inode_ref;
+ struct ext4_mountpoint *mp = f->mp;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ ino = f->inode;
+ r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
+ inode_ref.dirty = true;
+
+ ext4_fs_put_inode_ref(&inode_ref);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+/*********************************DIRECTORY OPERATION************************/
+
+int ext4_dir_rm(const char *path)
+{
+ int r;
+ int len;
+ ext4_file f;
+
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ struct ext4_inode_ref current;
+ struct ext4_inode_ref child;
+ struct ext4_directory_iterator it;
+
+ uint32_t name_off;
+ uint32_t inode_up;
+ uint32_t inode_current;
+ uint32_t depth = 1;
+
+ bool has_children;
+ bool is_goal;
+ bool dir_end;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ /*Check if exist.*/
+ r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ path += name_off;
+ len = ext4_path_check(path, &is_goal);
+
+ inode_current = f.inode;
+ dir_end = false;
+
+ ext4_block_cache_write_back(mp->fs.bdev, 1);
+
+ do {
+ /*Load directory node.*/
+ r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, ¤t);
+ if (r != EOK) {
+ break;
+ }
+
+ /*Initialize iterator.*/
+ r = ext4_dir_iterator_init(&it, ¤t, 0);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(¤t);
+ break;
+ }
+
+ while (r == EOK) {
+
+ if (!it.current) {
+ dir_end = true;
+ break;
+ }
+
+ /*Get up directory inode when ".." entry*/
+ if ((it.current->name_length == 2) &&
+ ext4_is_dots(it.current->name,
+ it.current->name_length)) {
+ inode_up = ext4_dir_entry_ll_get_inode(it.current);
+ }
+
+ /*If directory or file entry, but not "." ".." entry*/
+ if (!ext4_is_dots(it.current->name,
+ it.current->name_length)) {
+
+ /*Get child inode reference do unlink
+ * directory/file.*/
+ r = ext4_fs_get_inode_ref(&f.mp->fs,
+ ext4_dir_entry_ll_get_inode(it.current),
+ &child);
+ if (r != EOK)
+ break;
+
+ /*If directory with no leaf children*/
+ r = ext4_has_children(&has_children, &child);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&child);
+ break;
+ }
+
+ if (has_children) {
+ /*Has directory children. Go into this
+ * directory.*/
+ inode_up = inode_current;
+ inode_current = ext4_dir_entry_ll_get_inode(it.current);
+ depth++;
+ ext4_fs_put_inode_ref(&child);
+ break;
+ }
+
+ /*No children in child directory or file. Just
+ * unlink.*/
+ r = ext4_unlink(f.mp, ¤t, &child,
+ (char *)it.current->name,
+ it.current->name_length);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&child);
+ break;
+ }
+
+ ext4_inode_set_deletion_time(child.inode,
+ 0xFFFFFFFF);
+ ext4_inode_set_links_count(child.inode, 0);
+ child.dirty = true;
+ /*Turncate*/
+ r = ext4_fs_truncate_inode(&child, 0);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&child);
+ break;
+ }
+
+ r = ext4_fs_free_inode(&child);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&child);
+ break;
+ }
+
+ r = ext4_fs_put_inode_ref(&child);
+ if (r != EOK)
+ break;
+ }
+
+ r = ext4_dir_iterator_next(&it);
+ }
+
+ if (dir_end) {
+ /*Directory iterator reached last entry*/
+ ext4_has_children(&has_children, ¤t);
+ if (!has_children) {
+ inode_current = inode_up;
+ if (depth)
+ depth--;
+ }
+ /*Last unlink*/
+ if (!depth) {
+ /*Load parent.*/
+ struct ext4_inode_ref parent;
+ r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
+ &parent);
+ if (r != EOK)
+ goto End;
+
+ /* In this place all directories should be
+ * unlinked.
+ * Last unlink from root of current directory*/
+ r = ext4_unlink(f.mp, &parent, ¤t,
+ (char *)path, len);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&parent);
+ goto End;
+ }
+
+ if (ext4_inode_get_links_count(current.inode) ==
+ 2) {
+ ext4_inode_set_deletion_time(
+ current.inode, 0xFFFFFFFF);
+ ext4_inode_set_links_count(
+ current.inode, 0);
+ current.dirty = true;
+ /*Turncate*/
+ r = ext4_fs_truncate_inode(¤t, 0);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&parent);
+ goto End;
+ }
+
+ r = ext4_fs_free_inode(¤t);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&parent);
+ goto End;
+ }
+ }
+
+ r = ext4_fs_put_inode_ref(&parent);
+ if (r != EOK)
+ goto End;
+ }
+ }
+
+ End:
+ ext4_dir_iterator_fini(&it);
+ ext4_fs_put_inode_ref(¤t);
+ dir_end = false;
+
+ /*When something goes wrong. End loop.*/
+ if (r != EOK)
+ break;
+
+ } while (depth);
+
+ ext4_block_cache_write_back(mp->fs.bdev, 0);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_dir_mk(const char *path)
+{
+ int r;
+ ext4_file f;
+
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+
+ /*Check if exist.*/
+ r = ext4_generic_open(&f, path, "r", false, 0, 0);
+ if (r == EOK) {
+ /*Directory already created*/
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ /*Create new dir*/
+ r = ext4_generic_open(&f, path, "w", false, 0, 0);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(mp);
+ return r;
+ }
+
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_dir_open(ext4_dir *d, const char *path)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ int r;
+
+ if (!mp)
+ return ENOENT;
+
+ EXT4_MP_LOCK(mp);
+ r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
+ d->next_off = 0;
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_dir_close(ext4_dir *d) { return ext4_fclose(&d->f); }
+
+const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
+{
+#define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
+
+ int r;
+ ext4_direntry *de = 0;
+ struct ext4_inode_ref dir;
+ struct ext4_directory_iterator it;
+
+ EXT4_MP_LOCK(d->f.mp);
+
+ if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM)
+ return 0;
+
+ r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
+ if (r != EOK) {
+ goto Finish;
+ }
+
+ r = ext4_dir_iterator_init(&it, &dir, d->next_off);
+ if (r != EOK) {
+ ext4_fs_put_inode_ref(&dir);
+ goto Finish;
+ }
+
+ memcpy(&d->de, it.current, sizeof(ext4_direntry));
+ de = &d->de;
+
+ ext4_dir_iterator_next(&it);
+
+ d->next_off =
+ it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;
+
+ ext4_dir_iterator_fini(&it);
+ ext4_fs_put_inode_ref(&dir);
+
+Finish:
+ EXT4_MP_UNLOCK(d->f.mp);
+ return de;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4.h
+++ b/lwext4/ext4.h
@@ -1,389 +1,389 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * 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.h
- * @brief Ext4 high level operations (files, directories, mount points...).
- * Client has to include only this file.
- */
-
-#ifndef EXT4_H_
-#define EXT4_H_
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_blockdev.h"
-
-#include <stdint.h>
-
-/********************************FILE OPEN FLAGS*****************************/
-
-#ifndef O_RDONLY
-#define O_RDONLY 00
-#endif
-
-#ifndef O_WRONLY
-#define O_WRONLY 01
-#endif
-
-#ifndef O_RDWR
-#define O_RDWR 02
-#endif
-
-#ifndef O_CREAT
-#define O_CREAT 0100
-#endif
-
-#ifndef O_EXCL
-#define O_EXCL 0200
-#endif
-
-#ifndef O_TRUNC
-#define O_TRUNC 01000
-#endif
-
-#ifndef O_APPEND
-#define O_APPEND 02000
-#endif
-
-/********************************FILE SEEK FLAGS*****************************/
-
-#ifndef SEEK_SET
-#define SEEK_SET 0
-#endif
-
-#ifndef SEEK_CUR
-#define SEEK_CUR 1
-#endif
-
-#ifndef SEEK_END
-#define SEEK_END 2
-#endif
-
-/********************************OS LOCK INFERFACE***************************/
-
-/**@brief OS dependent lock interface.*/
-struct ext4_lock {
-
- /**@brief Lock access to mount point*/
- void (*lock)(void);
-
- /**@brief Unlock access to mount point*/
- void (*unlock)(void);
-};
-
-/********************************FILE DESCRIPTOR*****************************/
-
-/**@brief File descriptor*/
-typedef struct ext4_file {
-
- /**@brief Mount point handle.*/
- struct ext4_mountpoint *mp;
-
- /**@brief File inode id*/
- uint32_t inode;
-
- /**@brief Open flags.*/
- uint32_t flags;
-
- /**@brief File size.*/
- uint64_t fsize;
-
- /**@brief File position*/
- uint64_t fpos;
-} ext4_file;
-
-/*****************************DIRECTORY DESCRIPTOR***************************/
-/**@brief Directory entry types. Copy from ext4_types.h*/
-enum { EXT4_DIRENTRY_UNKNOWN = 0,
- EXT4_DIRENTRY_REG_FILE,
- EXT4_DIRENTRY_DIR,
- EXT4_DIRENTRY_CHRDEV,
- EXT4_DIRENTRY_BLKDEV,
- EXT4_DIRENTRY_FIFO,
- EXT4_DIRENTRY_SOCK,
- EXT4_DIRENTRY_SYMLINK };
-
-/**@brief Directory entry descriptor. Copy from ext4_types.h*/
-typedef struct {
- uint32_t inode;
- uint16_t entry_length;
- uint8_t name_length;
- uint8_t inode_type;
- uint8_t name[255];
-} ext4_direntry;
-
-typedef struct {
- /**@brief File descriptor*/
- ext4_file f;
- /**@brief Current directory entry.*/
- ext4_direntry de;
- /**@brief Next entry offset*/
- uint64_t next_off;
-} ext4_dir;
-
-/********************************MOUNT OPERATIONS****************************/
-
-/**@brief Register a block device to a name.
- * @warning Block device has to be filled by
- * @ref EXT4_BLOCKDEV_STATIC_INSTANCE. Block cache may be created
- * @ref EXT4_BCACHE_STATIC_INSTANCE.
- * Block cache may by created automatically when bc parameter is 0.
- * @param bd block device
- * @param bd block device cache (0 = automatic cache mode)
- * @param dev_name register name
- * @param standard error code*/
-int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,
- const char *dev_name);
-
-/**@brief Mount a block device with EXT4 partition to the mount point.
- * @param dev_name block device name (@ref ext4_device_register)
- * @param mount_point mount point, for example
- * - /
- * - /my_partition/
- * - /my_second_partition/
- *
- * @return standard error code */
-int ext4_mount(const char *dev_name, const char *mount_point);
-
-/**@brief Umount operation.
- * @param mount_point mount name
- * @return standard error code */
-int ext4_umount(const char *mount_point);
-
-/**@brief Some of the filesystem stats.*/
-struct ext4_mount_stats {
- uint32_t inodes_count;
- uint32_t free_inodes_count;
- uint64_t blocks_count;
- uint64_t free_blocks_count;
-
- uint32_t block_size;
- uint32_t block_group_count;
- uint32_t blocks_per_group;
- uint32_t inodes_per_group;
-
- char volume_name[16];
-};
-
-/**@brief Get file system params.
- * @param mount_point mount path
- * @param stats ext fs stats
- * @return standard error code */
-int ext4_mount_point_stats(const char *mount_point,
- struct ext4_mount_stats *stats);
-
-/**@brief Setup OS lock routines.
- * @param mount_point mount path
- * @param locks - lock and unlock functions
- * @return standard error code */
-int ext4_mount_setup_locks(const char *mount_point,
- const struct ext4_lock *locks);
-
-/**@brief Acquire the filesystem superblock pointer of a mp.
- * @param mount_point mount path
- * @param superblock pointer
- * @return standard error code */
-int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb);
-
-/**@brief Enable/disable write back cache mode.
- * @warning Default model of cache is write trough. It means that when You do:
- *
- * ext4_fopen(...);
- * ext4_fwrie(...);
- * < --- data is flushed to physical drive
- *
- * When you do:
- * ext4_cache_write_back(..., 1);
- * ext4_fopen(...);
- * ext4_fwrie(...);
- * < --- data is NOT flushed to physical drive
- * ext4_cache_write_back(..., 0);
- * < --- when write back mode is disabled all
- * cache data will be flushed
- * To enable write back mode permanently just call this function
- * once after ext4_mount (and disable before ext4_umount).
- *
- * Some of the function use write back cache mode internally.
- * If you enable write back mode twice you have to disable it twice
- * to flush all data:
- *
- * ext4_cache_write_back(..., 1);
- * ext4_cache_write_back(..., 1);
- *
- * ext4_cache_write_back(..., 0);
- * ext4_cache_write_back(..., 0);
- *
- * Write back mode is useful when you want to create a lot of empty
- * files/directories.
- *
- * @param path mount point path
- * @param on enable/disable
- *
- * @return standard error code */
-int ext4_cache_write_back(const char *path, bool on);
-
-/********************************FILE OPERATIONS*****************************/
-
-/**@brief Remove file by path.
- * @param path path to file
- * @return standard error code */
-int ext4_fremove(const char *path);
-
-/**@brief Rename file
- * @param path source
- * @param new_path destination
- * @return standard error code */
-int ext4_frename(const char *path, const char *new_path);
-
-/**@brief File open function.
- * @param filename, (has to start from mount point)
- * /my_partition/my_file
- * @param flags open file flags
- * |---------------------------------------------------------------|
- * | r or rb O_RDONLY |
- * |---------------------------------------------------------------|
- * | w or wb O_WRONLY|O_CREAT|O_TRUNC |
- * |---------------------------------------------------------------|
- * | a or ab O_WRONLY|O_CREAT|O_APPEND |
- * |---------------------------------------------------------------|
- * | r+ or rb+ or r+b O_RDWR |
- * |---------------------------------------------------------------|
- * | w+ or wb+ or w+b O_RDWR|O_CREAT|O_TRUNC |
- * |---------------------------------------------------------------|
- * | a+ or ab+ or a+b O_RDWR|O_CREAT|O_APPEND |
- * |---------------------------------------------------------------|
- *
- * @return standard error code*/
-int ext4_fopen(ext4_file *f, const char *path, const char *flags);
-
-/**@brief Alternate file open function.
- * @param filename, (has to start from mount point)
- * /my_partition/my_file
- * @param flags open file flags
- * @return standard error code*/
-int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect);
-
-/**@brief File close function.
- * @param f file handle
- * @return standard error code*/
-int ext4_fclose(ext4_file *f);
-
-/**@brief Fill in the ext4_inode buffer.
- * @param mount_point
- * @param inode no.
- * @param ext4_inode buffer
- * @return standard error code*/
-int ext4_fill_raw_inode(const char *mount_point, uint32_t ino,
- struct ext4_inode *inode);
-
-/**@brief File truncate function.
- * @param f file handle
- * @param new file size
- * @return standard error code*/
-int ext4_ftruncate(ext4_file *f, uint64_t size);
-
-/**@brief Read data from file.
- * @param f file handle
- * @param buf output buffer
- * @param size bytes to read
- * @param rcnt bytes read (may be NULL)
- * @return standard error code*/
-int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt);
-
-/**@brief Write data to file.
- * @param f file handle
- * @param buf data to write
- * @param size write length
- * @param wcnt bytes written (may be NULL)
- * @return standard error code*/
-int ext4_fwrite(ext4_file *f, const void *buf, uint32_t size, uint32_t *wcnt);
-
-/**@brief File seek operation.
- * @param f file handle
- * @param offset offset to seek
- * @param origin seek type:
- * @ref SEEK_SET
- * @ref SEEK_CUR
- * @ref SEEK_END
- * @return standard error code*/
-int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin);
-
-/**@brief Get file position.
- * @param f file handle
- * @return actual file position */
-uint64_t ext4_ftell(ext4_file *f);
-
-/**@brief Get file size.
- * @param f file handle
- * @return file size */
-uint64_t ext4_fsize(ext4_file *f);
-
-int ext4_fchmod(ext4_file *f, uint32_t mode);
-int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid);
-int ext4_file_set_atime(ext4_file *f, uint32_t atime);
-int ext4_file_set_mtime(ext4_file *f, uint32_t mtime);
-int ext4_file_set_ctime(ext4_file *f, uint32_t ctime);
-
-/*********************************DIRECTORY OPERATION***********************/
-
-/**@brief Recursive directory remove.
- * @param path directory path to remove
- * @return standard error code*/
-int ext4_dir_rm(const char *path);
-
-/**@brief Create new directory.
- * @param name new directory name
- * @return standard error code*/
-int ext4_dir_mk(const char *path);
-
-/**@brief Directory open.
- * @param d directory handle
- * @param path directory path
- * @return standard error code*/
-int ext4_dir_open(ext4_dir *d, const char *path);
-
-/**@brief Directory close.
- * @param d directory handle
- * @return standard error code*/
-int ext4_dir_close(ext4_dir *d);
-
-/**@brief Return next directory entry.
- * @param d directory handle
- * @param id entry id
- * @return directory entry id (NULL if no entry)*/
-const ext4_direntry *ext4_dir_entry_next(ext4_dir *d);
-
-#endif /* EXT4_H_ */
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * 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.h
+ * @brief Ext4 high level operations (files, directories, mount points...).
+ * Client has to include only this file.
+ */
+
+#ifndef EXT4_H_
+#define EXT4_H_
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_blockdev.h"
+
+#include <stdint.h>
+
+/********************************FILE OPEN FLAGS*****************************/
+
+#ifndef O_RDONLY
+#define O_RDONLY 00
+#endif
+
+#ifndef O_WRONLY
+#define O_WRONLY 01
+#endif
+
+#ifndef O_RDWR
+#define O_RDWR 02
+#endif
+
+#ifndef O_CREAT
+#define O_CREAT 0100
+#endif
+
+#ifndef O_EXCL
+#define O_EXCL 0200
+#endif
+
+#ifndef O_TRUNC
+#define O_TRUNC 01000
+#endif
+
+#ifndef O_APPEND
+#define O_APPEND 02000
+#endif
+
+/********************************FILE SEEK FLAGS*****************************/
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+/********************************OS LOCK INFERFACE***************************/
+
+/**@brief OS dependent lock interface.*/
+struct ext4_lock {
+
+ /**@brief Lock access to mount point*/
+ void (*lock)(void);
+
+ /**@brief Unlock access to mount point*/
+ void (*unlock)(void);
+};
+
+/********************************FILE DESCRIPTOR*****************************/
+
+/**@brief File descriptor*/
+typedef struct ext4_file {
+
+ /**@brief Mount point handle.*/
+ struct ext4_mountpoint *mp;
+
+ /**@brief File inode id*/
+ uint32_t inode;
+
+ /**@brief Open flags.*/
+ uint32_t flags;
+
+ /**@brief File size.*/
+ uint64_t fsize;
+
+ /**@brief File position*/
+ uint64_t fpos;
+} ext4_file;
+
+/*****************************DIRECTORY DESCRIPTOR***************************/
+/**@brief Directory entry types. Copy from ext4_types.h*/
+enum { EXT4_DIRENTRY_UNKNOWN = 0,
+ EXT4_DIRENTRY_REG_FILE,
+ EXT4_DIRENTRY_DIR,
+ EXT4_DIRENTRY_CHRDEV,
+ EXT4_DIRENTRY_BLKDEV,
+ EXT4_DIRENTRY_FIFO,
+ EXT4_DIRENTRY_SOCK,
+ EXT4_DIRENTRY_SYMLINK };
+
+/**@brief Directory entry descriptor. Copy from ext4_types.h*/
+typedef struct {
+ uint32_t inode;
+ uint16_t entry_length;
+ uint8_t name_length;
+ uint8_t inode_type;
+ uint8_t name[255];
+} ext4_direntry;
+
+typedef struct {
+ /**@brief File descriptor*/
+ ext4_file f;
+ /**@brief Current directory entry.*/
+ ext4_direntry de;
+ /**@brief Next entry offset*/
+ uint64_t next_off;
+} ext4_dir;
+
+/********************************MOUNT OPERATIONS****************************/
+
+/**@brief Register a block device to a name.
+ * @warning Block device has to be filled by
+ * @ref EXT4_BLOCKDEV_STATIC_INSTANCE. Block cache may be created
+ * @ref EXT4_BCACHE_STATIC_INSTANCE.
+ * Block cache may by created automatically when bc parameter is 0.
+ * @param bd block device
+ * @param bd block device cache (0 = automatic cache mode)
+ * @param dev_name register name
+ * @param standard error code*/
+int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,
+ const char *dev_name);
+
+/**@brief Mount a block device with EXT4 partition to the mount point.
+ * @param dev_name block device name (@ref ext4_device_register)
+ * @param mount_point mount point, for example
+ * - /
+ * - /my_partition/
+ * - /my_second_partition/
+ *
+ * @return standard error code */
+int ext4_mount(const char *dev_name, const char *mount_point);
+
+/**@brief Umount operation.
+ * @param mount_point mount name
+ * @return standard error code */
+int ext4_umount(const char *mount_point);
+
+/**@brief Some of the filesystem stats.*/
+struct ext4_mount_stats {
+ uint32_t inodes_count;
+ uint32_t free_inodes_count;
+ uint64_t blocks_count;
+ uint64_t free_blocks_count;
+
+ uint32_t block_size;
+ uint32_t block_group_count;
+ uint32_t blocks_per_group;
+ uint32_t inodes_per_group;
+
+ char volume_name[16];
+};
+
+/**@brief Get file system params.
+ * @param mount_point mount path
+ * @param stats ext fs stats
+ * @return standard error code */
+int ext4_mount_point_stats(const char *mount_point,
+ struct ext4_mount_stats *stats);
+
+/**@brief Setup OS lock routines.
+ * @param mount_point mount path
+ * @param locks - lock and unlock functions
+ * @return standard error code */
+int ext4_mount_setup_locks(const char *mount_point,
+ const struct ext4_lock *locks);
+
+/**@brief Acquire the filesystem superblock pointer of a mp.
+ * @param mount_point mount path
+ * @param superblock pointer
+ * @return standard error code */
+int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb);
+
+/**@brief Enable/disable write back cache mode.
+ * @warning Default model of cache is write trough. It means that when You do:
+ *
+ * ext4_fopen(...);
+ * ext4_fwrie(...);
+ * < --- data is flushed to physical drive
+ *
+ * When you do:
+ * ext4_cache_write_back(..., 1);
+ * ext4_fopen(...);
+ * ext4_fwrie(...);
+ * < --- data is NOT flushed to physical drive
+ * ext4_cache_write_back(..., 0);
+ * < --- when write back mode is disabled all
+ * cache data will be flushed
+ * To enable write back mode permanently just call this function
+ * once after ext4_mount (and disable before ext4_umount).
+ *
+ * Some of the function use write back cache mode internally.
+ * If you enable write back mode twice you have to disable it twice
+ * to flush all data:
+ *
+ * ext4_cache_write_back(..., 1);
+ * ext4_cache_write_back(..., 1);
+ *
+ * ext4_cache_write_back(..., 0);
+ * ext4_cache_write_back(..., 0);
+ *
+ * Write back mode is useful when you want to create a lot of empty
+ * files/directories.
+ *
+ * @param path mount point path
+ * @param on enable/disable
+ *
+ * @return standard error code */
+int ext4_cache_write_back(const char *path, bool on);
+
+/********************************FILE OPERATIONS*****************************/
+
+/**@brief Remove file by path.
+ * @param path path to file
+ * @return standard error code */
+int ext4_fremove(const char *path);
+
+/**@brief Rename file
+ * @param path source
+ * @param new_path destination
+ * @return standard error code */
+int ext4_frename(const char *path, const char *new_path);
+
+/**@brief File open function.
+ * @param filename, (has to start from mount point)
+ * /my_partition/my_file
+ * @param flags open file flags
+ * |---------------------------------------------------------------|
+ * | r or rb O_RDONLY |
+ * |---------------------------------------------------------------|
+ * | w or wb O_WRONLY|O_CREAT|O_TRUNC |
+ * |---------------------------------------------------------------|
+ * | a or ab O_WRONLY|O_CREAT|O_APPEND |
+ * |---------------------------------------------------------------|
+ * | r+ or rb+ or r+b O_RDWR |
+ * |---------------------------------------------------------------|
+ * | w+ or wb+ or w+b O_RDWR|O_CREAT|O_TRUNC |
+ * |---------------------------------------------------------------|
+ * | a+ or ab+ or a+b O_RDWR|O_CREAT|O_APPEND |
+ * |---------------------------------------------------------------|
+ *
+ * @return standard error code*/
+int ext4_fopen(ext4_file *f, const char *path, const char *flags);
+
+/**@brief Alternate file open function.
+ * @param filename, (has to start from mount point)
+ * /my_partition/my_file
+ * @param flags open file flags
+ * @return standard error code*/
+int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect);
+
+/**@brief File close function.
+ * @param f file handle
+ * @return standard error code*/
+int ext4_fclose(ext4_file *f);
+
+/**@brief Fill in the ext4_inode buffer.
+ * @param mount_point
+ * @param inode no.
+ * @param ext4_inode buffer
+ * @return standard error code*/
+int ext4_fill_raw_inode(const char *mount_point, uint32_t ino,
+ struct ext4_inode *inode);
+
+/**@brief File truncate function.
+ * @param f file handle
+ * @param new file size
+ * @return standard error code*/
+int ext4_ftruncate(ext4_file *f, uint64_t size);
+
+/**@brief Read data from file.
+ * @param f file handle
+ * @param buf output buffer
+ * @param size bytes to read
+ * @param rcnt bytes read (may be NULL)
+ * @return standard error code*/
+int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt);
+
+/**@brief Write data to file.
+ * @param f file handle
+ * @param buf data to write
+ * @param size write length
+ * @param wcnt bytes written (may be NULL)
+ * @return standard error code*/
+int ext4_fwrite(ext4_file *f, const void *buf, uint32_t size, uint32_t *wcnt);
+
+/**@brief File seek operation.
+ * @param f file handle
+ * @param offset offset to seek
+ * @param origin seek type:
+ * @ref SEEK_SET
+ * @ref SEEK_CUR
+ * @ref SEEK_END
+ * @return standard error code*/
+int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin);
+
+/**@brief Get file position.
+ * @param f file handle
+ * @return actual file position */
+uint64_t ext4_ftell(ext4_file *f);
+
+/**@brief Get file size.
+ * @param f file handle
+ * @return file size */
+uint64_t ext4_fsize(ext4_file *f);
+
+int ext4_fchmod(ext4_file *f, uint32_t mode);
+int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid);
+int ext4_file_set_atime(ext4_file *f, uint32_t atime);
+int ext4_file_set_mtime(ext4_file *f, uint32_t mtime);
+int ext4_file_set_ctime(ext4_file *f, uint32_t ctime);
+
+/*********************************DIRECTORY OPERATION***********************/
+
+/**@brief Recursive directory remove.
+ * @param path directory path to remove
+ * @return standard error code*/
+int ext4_dir_rm(const char *path);
+
+/**@brief Create new directory.
+ * @param name new directory name
+ * @return standard error code*/
+int ext4_dir_mk(const char *path);
+
+/**@brief Directory open.
+ * @param d directory handle
+ * @param path directory path
+ * @return standard error code*/
+int ext4_dir_open(ext4_dir *d, const char *path);
+
+/**@brief Directory close.
+ * @param d directory handle
+ * @return standard error code*/
+int ext4_dir_close(ext4_dir *d);
+
+/**@brief Return next directory entry.
+ * @param d directory handle
+ * @param id entry id
+ * @return directory entry id (NULL if no entry)*/
+const ext4_direntry *ext4_dir_entry_next(ext4_dir *d);
+
+#endif /* EXT4_H_ */
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_bcache.c
+++ b/lwext4/ext4_bcache.c
@@ -1,213 +1,213 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * 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_bcache.c
- * @brief Block cache allocator.
- */
-
-#include "ext4_config.h"
-#include "ext4_bcache.h"
-#include "ext4_debug.h"
-#include "ext4_errno.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
- uint32_t itemsize)
-{
- ext4_assert(bc && cnt && itemsize);
-
- memset(bc, 0, sizeof(struct ext4_bcache));
-
- bc->data = malloc(cnt * itemsize);
- if (!bc->data)
- goto error;
-
- bc->cnt = cnt;
- bc->itemsize = itemsize;
- bc->ref_blocks = 0;
- bc->max_ref_blocks = 0;
-
- return EOK;
-
-error:
-
- if (bc->data)
- free(bc->data);
-
- memset(bc, 0, sizeof(struct ext4_bcache));
-
- return ENOMEM;
-}
-
-int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
-{
- if (bc->data)
- free(bc->data);
-
- memset(bc, 0, sizeof(struct ext4_bcache));
-
- return EOK;
-}
-
-int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
- bool *is_new)
-{
- uint32_t i;
- ext4_assert(bc && b && is_new);
-
- /*Check if valid.*/
- ext4_assert(b->lb_id);
- if (!b->lb_id) {
- ext4_assert(b->lb_id);
- }
-
- uint32_t cache_id = bc->cnt;
- uint32_t alloc_id = 0;
-
- *is_new = false;
-
- /*Find in free blocks (Last Recently Used).*/
- for (i = 0; i < bc->cnt; ++i) {
-
- /*Check if block is already in cache*/
- if (b->lb_id == bc->lba[i]) {
-
- if (!bc->refctr[i] && !bc->free_delay[i])
- bc->ref_blocks++;
-
- /*Update reference counter*/
- bc->refctr[i]++;
-
- /*Update usage marker*/
- bc->lru_id[i] = ++bc->lru_ctr;
-
- /*Set valid cache data and id*/
- b->data = bc->data + i * bc->itemsize;
- b->cache_id = i;
-
- return EOK;
- }
-
- /*Best fit calculations.*/
- if (bc->refctr[i])
- continue;
-
- if (bc->free_delay[i])
- continue;
-
- /*Block is unreferenced, but it may exist block with
- * lower usage marker*/
-
- /*First find.*/
- if (cache_id == bc->cnt) {
- cache_id = i;
- alloc_id = bc->lru_id[i];
- continue;
- }
-
- /*Next find*/
- if (alloc_id <= bc->lru_id[i])
- continue;
-
- /*This block has lower alloc id marker*/
- cache_id = i;
- alloc_id = bc->lru_id[i];
- }
-
- if (cache_id != bc->cnt) {
- /*There was unreferenced block*/
- bc->lba[cache_id] = b->lb_id;
- bc->refctr[cache_id] = 1;
- bc->lru_id[cache_id] = ++bc->lru_ctr;
-
- /*Set valid cache data and id*/
- b->data = bc->data + cache_id * bc->itemsize;
- b->cache_id = cache_id;
-
- /*Statistics*/
- bc->ref_blocks++;
- if (bc->ref_blocks > bc->max_ref_blocks)
- bc->max_ref_blocks = bc->ref_blocks;
-
- /*Block needs to be read.*/
- *is_new = true;
-
- return EOK;
- }
-
- ext4_dprintf(EXT4_DEBUG_BCACHE,
- "ext4_bcache_alloc: FAIL, unable to alloc block cache!\n");
- return ENOMEM;
-}
-
-int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b,
- uint8_t free_delay)
-{
- ext4_assert(bc && b);
-
- /*Check if valid.*/
- ext4_assert(b->lb_id);
-
- /*Block should be in cache.*/
- ext4_assert(b->cache_id < bc->cnt);
-
- /*Check if someone don't try free unreferenced block cache.*/
- ext4_assert(bc->refctr[b->cache_id]);
-
- /*Just decrease reference counter*/
- if (bc->refctr[b->cache_id])
- bc->refctr[b->cache_id]--;
-
- if (free_delay)
- bc->free_delay[b->cache_id] = free_delay;
-
- /*Update statistics*/
- if (!bc->refctr[b->cache_id] && !bc->free_delay[b->cache_id])
- bc->ref_blocks--;
-
- b->lb_id = 0;
- b->data = 0;
- b->cache_id = 0;
-
- return EOK;
-}
-
-bool ext4_bcache_is_full(struct ext4_bcache *bc)
-{
- return (bc->cnt == bc->ref_blocks);
-}
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * 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_bcache.c
+ * @brief Block cache allocator.
+ */
+
+#include "ext4_config.h"
+#include "ext4_bcache.h"
+#include "ext4_debug.h"
+#include "ext4_errno.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
+ uint32_t itemsize)
+{
+ ext4_assert(bc && cnt && itemsize);
+
+ memset(bc, 0, sizeof(struct ext4_bcache));
+
+ bc->data = malloc(cnt * itemsize);
+ if (!bc->data)
+ goto error;
+
+ bc->cnt = cnt;
+ bc->itemsize = itemsize;
+ bc->ref_blocks = 0;
+ bc->max_ref_blocks = 0;
+
+ return EOK;
+
+error:
+
+ if (bc->data)
+ free(bc->data);
+
+ memset(bc, 0, sizeof(struct ext4_bcache));
+
+ return ENOMEM;
+}
+
+int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
+{
+ if (bc->data)
+ free(bc->data);
+
+ memset(bc, 0, sizeof(struct ext4_bcache));
+
+ return EOK;
+}
+
+int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
+ bool *is_new)
+{
+ uint32_t i;
+ ext4_assert(bc && b && is_new);
+
+ /*Check if valid.*/
+ ext4_assert(b->lb_id);
+ if (!b->lb_id) {
+ ext4_assert(b->lb_id);
+ }
+
+ uint32_t cache_id = bc->cnt;
+ uint32_t alloc_id = 0;
+
+ *is_new = false;
+
+ /*Find in free blocks (Last Recently Used).*/
+ for (i = 0; i < bc->cnt; ++i) {
+
+ /*Check if block is already in cache*/
+ if (b->lb_id == bc->lba[i]) {
+
+ if (!bc->refctr[i] && !bc->free_delay[i])
+ bc->ref_blocks++;
+
+ /*Update reference counter*/
+ bc->refctr[i]++;
+
+ /*Update usage marker*/
+ bc->lru_id[i] = ++bc->lru_ctr;
+
+ /*Set valid cache data and id*/
+ b->data = bc->data + i * bc->itemsize;
+ b->cache_id = i;
+
+ return EOK;
+ }
+
+ /*Best fit calculations.*/
+ if (bc->refctr[i])
+ continue;
+
+ if (bc->free_delay[i])
+ continue;
+
+ /*Block is unreferenced, but it may exist block with
+ * lower usage marker*/
+
+ /*First find.*/
+ if (cache_id == bc->cnt) {
+ cache_id = i;
+ alloc_id = bc->lru_id[i];
+ continue;
+ }
+
+ /*Next find*/
+ if (alloc_id <= bc->lru_id[i])
+ continue;
+
+ /*This block has lower alloc id marker*/
+ cache_id = i;
+ alloc_id = bc->lru_id[i];
+ }
+
+ if (cache_id != bc->cnt) {
+ /*There was unreferenced block*/
+ bc->lba[cache_id] = b->lb_id;
+ bc->refctr[cache_id] = 1;
+ bc->lru_id[cache_id] = ++bc->lru_ctr;
+
+ /*Set valid cache data and id*/
+ b->data = bc->data + cache_id * bc->itemsize;
+ b->cache_id = cache_id;
+
+ /*Statistics*/
+ bc->ref_blocks++;
+ if (bc->ref_blocks > bc->max_ref_blocks)
+ bc->max_ref_blocks = bc->ref_blocks;
+
+ /*Block needs to be read.*/
+ *is_new = true;
+
+ return EOK;
+ }
+
+ ext4_dprintf(EXT4_DEBUG_BCACHE,
+ "ext4_bcache_alloc: FAIL, unable to alloc block cache!\n");
+ return ENOMEM;
+}
+
+int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b,
+ uint8_t free_delay)
+{
+ ext4_assert(bc && b);
+
+ /*Check if valid.*/
+ ext4_assert(b->lb_id);
+
+ /*Block should be in cache.*/
+ ext4_assert(b->cache_id < bc->cnt);
+
+ /*Check if someone don't try free unreferenced block cache.*/
+ ext4_assert(bc->refctr[b->cache_id]);
+
+ /*Just decrease reference counter*/
+ if (bc->refctr[b->cache_id])
+ bc->refctr[b->cache_id]--;
+
+ if (free_delay)
+ bc->free_delay[b->cache_id] = free_delay;
+
+ /*Update statistics*/
+ if (!bc->refctr[b->cache_id] && !bc->free_delay[b->cache_id])
+ bc->ref_blocks--;
+
+ b->lb_id = 0;
+ b->data = 0;
+ b->cache_id = 0;
+
+ return EOK;
+}
+
+bool ext4_bcache_is_full(struct ext4_bcache *bc)
+{
+ return (bc->cnt == bc->ref_blocks);
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_bcache.h
+++ b/lwext4/ext4_bcache.h
@@ -1,147 +1,147 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * 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_bcache.h
- * @brief Block cache allocator.
- */
-
-#ifndef EXT4_BCACHE_H_
-#define EXT4_BCACHE_H_
-
-#include "ext4_config.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/**@brief Single block descriptor*/
-struct ext4_block {
- /**@brief Dirty flag*/
- bool dirty;
-
- /**@brief Logical block ID*/
- uint64_t lb_id;
-
- /**@brief Cache id*/
- uint32_t cache_id;
-
- /**@brief Data buffer.*/
- uint8_t *data;
-};
-
-/**@brief Block cache descriptor*/
-struct ext4_bcache {
-
- /**@brief Item count in block cache*/
- uint32_t cnt;
-
- /**@brief Item size in block cache*/
- uint32_t itemsize;
-
- /**@brief Last recently used counter*/
- uint32_t lru_ctr;
-
- /**@brief Reference count table*/
- uint32_t refctr[CONFIG_BLOCK_DEV_CACHE_SIZE];
-
- /**@brief Last recently used ID table*/
- uint32_t lru_id[CONFIG_BLOCK_DEV_CACHE_SIZE];
-
- /**@brief Writeback free delay mode table*/
- uint8_t free_delay[CONFIG_BLOCK_DEV_CACHE_SIZE];
-
- /**@brief Logical block table*/
- uint64_t lba[CONFIG_BLOCK_DEV_CACHE_SIZE];
-
- /**@brief Dirty mark*/
- bool dirty[CONFIG_BLOCK_DEV_CACHE_SIZE];
-
- /**@brief Cache data buffers*/
- uint8_t *data;
-
- /**@brief Currently referenced datablocks*/
- uint32_t ref_blocks;
-
- /**@brief Maximum referenced datablocks*/
- uint32_t max_ref_blocks;
-};
-
-/**@brief Static initializer of block cache structure.*/
-#define EXT4_BCACHE_STATIC_INSTANCE(__name, __cnt, __itemsize) \
- static uint8_t __name##_data[(__cnt) * (__itemsize)]; \
- static struct ext4_bcache __name = { \
- .cnt = __cnt, \
- .itemsize = __itemsize, \
- .lru_ctr = 0, \
- .data = __name##_data, \
- }
-
-/**@brief Dynamic initialization of block cache.
- * @param bc block cache descriptor
- * @param cnt items count in block cache
- * @param itemsize single item size (in bytes)
- * @return standard error code*/
-int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
- uint32_t itemsize);
-
-/**@brief Dynamic de-initialization of block cache.
- * @param bc block cache descriptor
- * @return standard error code*/
-int ext4_bcache_fini_dynamic(struct ext4_bcache *bc);
-
-/**@brief Allocate block from block cache memory.
- * Unreferenced block allocation is based on LRU
- * (Last Recently Used) algorithm.
- * @param bc block cache descriptor
- * @param b block to alloc
- * @param is_new block is new (needs to be read)
- * @return standard error code*/
-int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
- bool *is_new);
-
-/**@brief Free block from cache memory (decrement reference counter).
- * @param bc block cache descriptor
- * @param b block to free
- * @param cache writeback mode
- * @return standard error code*/
-int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b,
- uint8_t free_delay);
-
-/**@brief Return a full status of block cache.
- * @param bc block cache descriptor
- * @return full status*/
-bool ext4_bcache_is_full(struct ext4_bcache *bc);
-
-#endif /* EXT4_BCACHE_H_ */
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * 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_bcache.h
+ * @brief Block cache allocator.
+ */
+
+#ifndef EXT4_BCACHE_H_
+#define EXT4_BCACHE_H_
+
+#include "ext4_config.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**@brief Single block descriptor*/
+struct ext4_block {
+ /**@brief Dirty flag*/
+ bool dirty;
+
+ /**@brief Logical block ID*/
+ uint64_t lb_id;
+
+ /**@brief Cache id*/
+ uint32_t cache_id;
+
+ /**@brief Data buffer.*/
+ uint8_t *data;
+};
+
+/**@brief Block cache descriptor*/
+struct ext4_bcache {
+
+ /**@brief Item count in block cache*/
+ uint32_t cnt;
+
+ /**@brief Item size in block cache*/
+ uint32_t itemsize;
+
+ /**@brief Last recently used counter*/
+ uint32_t lru_ctr;
+
+ /**@brief Reference count table*/
+ uint32_t refctr[CONFIG_BLOCK_DEV_CACHE_SIZE];
+
+ /**@brief Last recently used ID table*/
+ uint32_t lru_id[CONFIG_BLOCK_DEV_CACHE_SIZE];
+
+ /**@brief Writeback free delay mode table*/
+ uint8_t free_delay[CONFIG_BLOCK_DEV_CACHE_SIZE];
+
+ /**@brief Logical block table*/
+ uint64_t lba[CONFIG_BLOCK_DEV_CACHE_SIZE];
+
+ /**@brief Dirty mark*/
+ bool dirty[CONFIG_BLOCK_DEV_CACHE_SIZE];
+
+ /**@brief Cache data buffers*/
+ uint8_t *data;
+
+ /**@brief Currently referenced datablocks*/
+ uint32_t ref_blocks;
+
+ /**@brief Maximum referenced datablocks*/
+ uint32_t max_ref_blocks;
+};
+
+/**@brief Static initializer of block cache structure.*/
+#define EXT4_BCACHE_STATIC_INSTANCE(__name, __cnt, __itemsize) \
+ static uint8_t __name##_data[(__cnt) * (__itemsize)]; \
+ static struct ext4_bcache __name = { \
+ .cnt = __cnt, \
+ .itemsize = __itemsize, \
+ .lru_ctr = 0, \
+ .data = __name##_data, \
+ }
+
+/**@brief Dynamic initialization of block cache.
+ * @param bc block cache descriptor
+ * @param cnt items count in block cache
+ * @param itemsize single item size (in bytes)
+ * @return standard error code*/
+int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
+ uint32_t itemsize);
+
+/**@brief Dynamic de-initialization of block cache.
+ * @param bc block cache descriptor
+ * @return standard error code*/
+int ext4_bcache_fini_dynamic(struct ext4_bcache *bc);
+
+/**@brief Allocate block from block cache memory.
+ * Unreferenced block allocation is based on LRU
+ * (Last Recently Used) algorithm.
+ * @param bc block cache descriptor
+ * @param b block to alloc
+ * @param is_new block is new (needs to be read)
+ * @return standard error code*/
+int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
+ bool *is_new);
+
+/**@brief Free block from cache memory (decrement reference counter).
+ * @param bc block cache descriptor
+ * @param b block to free
+ * @param cache writeback mode
+ * @return standard error code*/
+int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b,
+ uint8_t free_delay);
+
+/**@brief Return a full status of block cache.
+ * @param bc block cache descriptor
+ * @return full status*/
+bool ext4_bcache_is_full(struct ext4_bcache *bc);
+
+#endif /* EXT4_BCACHE_H_ */
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_dir.c
+++ b/lwext4/ext4_dir.c
@@ -1,574 +1,574 @@
-/*
- * 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_dir.h
- * @brief Directory handle procedures.
- */
-
-#include "ext4_config.h"
-#include "ext4_dir.h"
-#include "ext4_dir_idx.h"
-#include "ext4_inode.h"
-#include "ext4_fs.h"
-
-#include <string.h>
-
-/****************************************************************************/
-
-/**@brief Do some checks before returning iterator.
- * @param it Iterator to be checked
- * @param block_size Size of data block
- * @return Error code
- */
-static int ext4_dir_iterator_set(struct ext4_directory_iterator *it,
- uint32_t block_size)
-{
- it->current = NULL;
-
- uint32_t offset_in_block = it->current_offset % block_size;
-
- /* Ensure proper alignment */
- if ((offset_in_block % 4) != 0)
- return EIO;
-
- /* Ensure that the core of the entry does not overflow the block */
- if (offset_in_block > block_size - 8)
- return EIO;
-
- struct ext4_directory_entry_ll *entry =
- (void *)(it->current_block.data + offset_in_block);
-
- /* Ensure that the whole entry does not overflow the block */
- uint16_t length = ext4_dir_entry_ll_get_entry_length(entry);
- if (offset_in_block + length > block_size)
- return EIO;
-
- /* Ensure the name length is not too large */
- if (ext4_dir_entry_ll_get_name_length(&it->inode_ref->fs->sb, entry) >
- length - 8)
- return EIO;
-
- /* Everything OK - "publish" the entry */
- it->current = entry;
- return EOK;
-}
-
-/**@brief Seek to next valid directory entry.
- * Here can be jumped to the next data block.
- * @param it Initialized iterator
- * @param pos Position of the next entry
- * @return Error code
- */
-static int ext4_dir_iterator_seek(struct ext4_directory_iterator *it,
- uint64_t pos)
-{
- uint64_t size =
- ext4_inode_get_size(&it->inode_ref->fs->sb, it->inode_ref->inode);
-
- /* The iterator is not valid until we seek to the desired position */
- it->current = NULL;
-
- /* Are we at the end? */
- if (pos >= size) {
- if (it->current_block.lb_id) {
-
- int rc = ext4_block_set(it->inode_ref->fs->bdev,
- &it->current_block);
- it->current_block.lb_id = 0;
-
- if (rc != EOK)
- return rc;
- }
-
- it->current_offset = pos;
- return EOK;
- }
-
- /* Compute next block address */
- uint32_t block_size = ext4_sb_get_block_size(&it->inode_ref->fs->sb);
- uint64_t current_block_idx = it->current_offset / block_size;
- uint64_t next_block_idx = pos / block_size;
-
- /*
- * If we don't have a block or are moving across block boundary,
- * we need to get another block
- */
- if ((it->current_block.lb_id == 0) ||
- (current_block_idx != next_block_idx)) {
- if (it->current_block.lb_id) {
- int rc = ext4_block_set(it->inode_ref->fs->bdev,
- &it->current_block);
- it->current_block.lb_id = 0;
-
- if (rc != EOK)
- return rc;
- }
-
- uint32_t next_block_phys_idx;
- int rc = ext4_fs_get_inode_data_block_index(
- it->inode_ref, next_block_idx, &next_block_phys_idx);
- if (rc != EOK)
- return rc;
-
- rc = ext4_block_get(it->inode_ref->fs->bdev, &it->current_block,
- next_block_phys_idx);
- if (rc != EOK) {
- it->current_block.lb_id = 0;
- return rc;
- }
- }
-
- it->current_offset = pos;
-
- return ext4_dir_iterator_set(it, block_size);
-}
-
-int ext4_dir_iterator_init(struct ext4_directory_iterator *it,
- struct ext4_inode_ref *inode_ref, uint64_t pos)
-{
- it->inode_ref = inode_ref;
- it->current = 0;
- it->current_offset = 0;
- it->current_block.lb_id = 0;
-
- return ext4_dir_iterator_seek(it, pos);
-}
-
-int ext4_dir_iterator_next(struct ext4_directory_iterator *it)
-{
- int r = EOK;
- uint16_t skip;
-
- while (r == EOK) {
- skip = ext4_dir_entry_ll_get_entry_length(it->current);
- r = ext4_dir_iterator_seek(it, it->current_offset + skip);
-
- if (!it->current)
- break;
- /*Skip NULL referenced entry*/
- if (ext4_dir_entry_ll_get_inode(it->current) != 0)
- break;
- }
-
- return r;
-}
-
-int ext4_dir_iterator_fini(struct ext4_directory_iterator *it)
-{
- it->current = 0;
-
- if (it->current_block.lb_id)
- return ext4_block_set(it->inode_ref->fs->bdev,
- &it->current_block);
-
- return EOK;
-}
-
-void ext4_dir_write_entry(struct ext4_sblock *sb,
- struct ext4_directory_entry_ll *entry,
- uint16_t entry_len, struct ext4_inode_ref *child,
- const char *name, size_t name_len)
-{
- /* Check maximum entry length */
- ext4_assert(entry_len <= ext4_sb_get_block_size(sb));
-
- /* Set basic attributes */
- ext4_dir_entry_ll_set_inode(entry, child->index);
- ext4_dir_entry_ll_set_entry_length(entry, entry_len);
- ext4_dir_entry_ll_set_name_length(sb, entry, name_len);
-
- /* Write name */
- memcpy(entry->name, name, name_len);
-
- /* Set type of entry */
- if (ext4_inode_is_type(sb, child->inode, EXT4_INODE_MODE_DIRECTORY))
- ext4_dir_entry_ll_set_inode_type(sb, entry,
- EXT4_DIRECTORY_FILETYPE_DIR);
- else
- ext4_dir_entry_ll_set_inode_type(
- sb, entry, EXT4_DIRECTORY_FILETYPE_REG_FILE);
-}
-
-int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
- uint32_t name_len, struct ext4_inode_ref *child)
-{
- struct ext4_fs *fs = parent->fs;
-
-#if CONFIG_DIR_INDEX_ENABLE
- /* Index adding (if allowed) */
- if ((ext4_sb_has_feature_compatible(&fs->sb,
- EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
- (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
- int rc = ext4_dir_dx_add_entry(parent, child, name);
-
- /* Check if index is not corrupted */
- if (rc != EXT4_ERR_BAD_DX_DIR) {
- if (rc != EOK)
- return rc;
-
- return EOK;
- }
-
- /* Needed to clear dir index flag if corrupted */
- ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
- parent->dirty = true;
- }
-#endif
-
- /* Linear algorithm */
- uint32_t iblock = 0;
- uint32_t fblock = 0;
- uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
- uint32_t inode_size = ext4_inode_get_size(&fs->sb, parent->inode);
- uint32_t total_blocks = inode_size / block_size;
-
- /* Find block, where is space for new entry and try to add */
- bool success = false;
- for (iblock = 0; iblock < total_blocks; ++iblock) {
- int rc =
- ext4_fs_get_inode_data_block_index(parent, iblock, &fblock);
- if (rc != EOK)
- return rc;
-
- struct ext4_block block;
- rc = ext4_block_get(fs->bdev, &block, fblock);
- if (rc != EOK)
- return rc;
-
- /* If adding is successful, function can finish */
- rc = ext4_dir_try_insert_entry(&fs->sb, &block, child, name,
- name_len);
- if (rc == EOK)
- success = true;
-
- rc = ext4_block_set(fs->bdev, &block);
- if (rc != EOK)
- return rc;
-
- if (success)
- return EOK;
- }
-
- /* No free block found - needed to allocate next data block */
-
- iblock = 0;
- fblock = 0;
- int rc = ext4_fs_append_inode_block(parent, &fblock, &iblock);
- if (rc != EOK)
- return rc;
-
- /* Load new block */
- struct ext4_block new_block;
-
- rc = ext4_block_get(fs->bdev, &new_block, fblock);
- if (rc != EOK)
- return rc;
-
- /* Fill block with zeroes */
- memset(new_block.data, 0, block_size);
- struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;
- ext4_dir_write_entry(&fs->sb, block_entry, block_size, child, name,
- name_len);
-
- /* Save new block */
- new_block.dirty = true;
- rc = ext4_block_set(fs->bdev, &new_block);
-
- return rc;
-}
-
-int ext4_dir_find_entry(struct ext4_directory_search_result *result,
- struct ext4_inode_ref *parent, const char *name,
- uint32_t name_len)
-{
- struct ext4_sblock *sb = &parent->fs->sb;
-
-#if CONFIG_DIR_INDEX_ENABLE
- /* Index search */
- if ((ext4_sb_has_feature_compatible(sb,
- EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
- (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
- int rc = ext4_dir_dx_find_entry(result, parent, name_len, name);
-
- /* Check if index is not corrupted */
- if (rc != EXT4_ERR_BAD_DX_DIR) {
- if (rc != EOK)
- return rc;
-
- return EOK;
- }
-
- /* Needed to clear dir index flag if corrupted */
- ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
- parent->dirty = true;
- }
-#endif
-
- /* Linear algorithm */
-
- uint32_t iblock;
- uint32_t fblock;
- uint32_t block_size = ext4_sb_get_block_size(sb);
- uint32_t inode_size = ext4_inode_get_size(sb, parent->inode);
- uint32_t total_blocks = inode_size / block_size;
-
- /* Walk through all data blocks */
- for (iblock = 0; iblock < total_blocks; ++iblock) {
- /* Load block address */
- int rc =
- ext4_fs_get_inode_data_block_index(parent, iblock, &fblock);
- if (rc != EOK)
- return rc;
-
- /* Load data block */
- struct ext4_block block;
- rc = ext4_block_get(parent->fs->bdev, &block, fblock);
- if (rc != EOK)
- return rc;
-
- /* Try to find entry in block */
- struct ext4_directory_entry_ll *res_entry;
- rc = ext4_dir_find_in_block(&block, sb, name_len, name,
- &res_entry);
- if (rc == EOK) {
- result->block = block;
- result->dentry = res_entry;
- return EOK;
- }
-
- /* Entry not found - put block and continue to the next block */
-
- rc = ext4_block_set(parent->fs->bdev, &block);
- if (rc != EOK)
- return rc;
- }
-
- /* Entry was not found */
-
- result->block.lb_id = 0;
- result->dentry = NULL;
-
- return ENOENT;
-}
-
-int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name,
- uint32_t name_len)
-{
- /* Check if removing from directory */
- if (!ext4_inode_is_type(&parent->fs->sb, parent->inode,
- EXT4_INODE_MODE_DIRECTORY))
- return ENOTDIR;
-
- /* Try to find entry */
- struct ext4_directory_search_result result;
- int rc = ext4_dir_find_entry(&result, parent, name, name_len);
- if (rc != EOK)
- return rc;
-
- /* Invalidate entry */
- ext4_dir_entry_ll_set_inode(result.dentry, 0);
-
- /* Store entry position in block */
- uint32_t pos = (uint8_t *)result.dentry - result.block.data;
-
- /*
- * If entry is not the first in block, it must be merged
- * with previous entry
- */
- if (pos != 0) {
- uint32_t offset = 0;
-
- /* Start from the first entry in block */
- struct ext4_directory_entry_ll *tmp_dentry =
- (void *)result.block.data;
- uint16_t tmp_dentry_length =
- ext4_dir_entry_ll_get_entry_length(tmp_dentry);
-
- /* Find direct predecessor of removed entry */
- while ((offset + tmp_dentry_length) < pos) {
- offset +=
- ext4_dir_entry_ll_get_entry_length(tmp_dentry);
- tmp_dentry = (void *)(result.block.data + offset);
- tmp_dentry_length =
- ext4_dir_entry_ll_get_entry_length(tmp_dentry);
- }
-
- ext4_assert(tmp_dentry_length + offset == pos);
-
- /* Add to removed entry length to predecessor's length */
- uint16_t del_entry_length =
- ext4_dir_entry_ll_get_entry_length(result.dentry);
- ext4_dir_entry_ll_set_entry_length(
- tmp_dentry, tmp_dentry_length + del_entry_length);
- }
-
- result.block.dirty = true;
-
- return ext4_dir_destroy_result(parent, &result);
-}
-
-int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
- struct ext4_block *target_block,
- struct ext4_inode_ref *child, const char *name,
- uint32_t name_len)
-{
- /* Compute required length entry and align it to 4 bytes */
- uint32_t block_size = ext4_sb_get_block_size(sb);
- uint16_t required_len =
- sizeof(struct ext4_fake_directory_entry) + name_len;
-
- if ((required_len % 4) != 0)
- required_len += 4 - (required_len % 4);
-
- /* Initialize pointers, stop means to upper bound */
- struct ext4_directory_entry_ll *dentry = (void *)target_block->data;
- struct ext4_directory_entry_ll *stop =
- (void *)(target_block->data + block_size);
-
- /*
- * Walk through the block and check for invalid entries
- * or entries with free space for new entry
- */
- while (dentry < stop) {
- uint32_t inode = ext4_dir_entry_ll_get_inode(dentry);
- uint16_t rec_len = ext4_dir_entry_ll_get_entry_length(dentry);
-
- /* If invalid and large enough entry, use it */
- if ((inode == 0) && (rec_len >= required_len)) {
- ext4_dir_write_entry(sb, dentry, rec_len, child, name,
- name_len);
- target_block->dirty = true;
-
- return EOK;
- }
-
- /* Valid entry, try to split it */
- if (inode != 0) {
- uint16_t used_name_len =
- ext4_dir_entry_ll_get_name_length(sb, dentry);
-
- uint16_t used_space =
- sizeof(struct ext4_fake_directory_entry) +
- used_name_len;
-
- if ((used_name_len % 4) != 0)
- used_space += 4 - (used_name_len % 4);
-
- uint16_t free_space = rec_len - used_space;
-
- /* There is free space for new entry */
- if (free_space >= required_len) {
- /* Cut tail of current entry */
- ext4_dir_entry_ll_set_entry_length(dentry,
- used_space);
- struct ext4_directory_entry_ll *new_entry =
- (void *)((uint8_t *)dentry + used_space);
- ext4_dir_write_entry(sb, new_entry, free_space,
- child, name, name_len);
-
- target_block->dirty = true;
- return EOK;
- }
- }
-
- /* Jump to the next entry */
- dentry = (void *)((uint8_t *)dentry + rec_len);
- }
-
- /* No free space found for new entry */
- return ENOSPC;
-}
-
-int ext4_dir_find_in_block(struct ext4_block *block, struct ext4_sblock *sb,
- size_t name_len, const char *name,
- struct ext4_directory_entry_ll **res_entry)
-{
- /* Start from the first entry in block */
- struct ext4_directory_entry_ll *dentry =
- (struct ext4_directory_entry_ll *)block->data;
-
- /* Set upper bound for cycling */
- uint8_t *addr_limit = block->data + ext4_sb_get_block_size(sb);
-
- /* Walk through the block and check entries */
- while ((uint8_t *)dentry < addr_limit) {
- /* Termination condition */
- if ((uint8_t *)dentry + name_len > addr_limit)
- break;
-
- /* Valid entry - check it */
- if (ext4_dir_entry_ll_get_inode(dentry) != 0) {
- /* For more efficient compare only lengths firstly*/
- if (ext4_dir_entry_ll_get_name_length(sb, dentry) ==
- name_len) {
- /* Compare names */
- if (memcmp((uint8_t *)name, dentry->name,
- name_len) == 0) {
- *res_entry = dentry;
- return EOK;
- }
- }
- }
-
- uint16_t dentry_len =
- ext4_dir_entry_ll_get_entry_length(dentry);
-
- /* Corrupted entry */
- if (dentry_len == 0)
- return EINVAL;
-
- /* Jump to next entry */
- dentry = (struct ext4_directory_entry_ll *)((uint8_t *)dentry +
- dentry_len);
- }
-
- /* Entry not found */
- return ENOENT;
-}
-
-int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
- struct ext4_directory_search_result *result)
-{
- if (result->block.lb_id)
- return ext4_block_set(parent->fs->bdev, &result->block);
-
- return EOK;
-}
-
-/**
- * @}
- */
+/*
+ * 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_dir.h
+ * @brief Directory handle procedures.
+ */
+
+#include "ext4_config.h"
+#include "ext4_dir.h"
+#include "ext4_dir_idx.h"
+#include "ext4_inode.h"
+#include "ext4_fs.h"
+
+#include <string.h>
+
+/****************************************************************************/
+
+/**@brief Do some checks before returning iterator.
+ * @param it Iterator to be checked
+ * @param block_size Size of data block
+ * @return Error code
+ */
+static int ext4_dir_iterator_set(struct ext4_directory_iterator *it,
+ uint32_t block_size)
+{
+ it->current = NULL;
+
+ uint32_t offset_in_block = it->current_offset % block_size;
+
+ /* Ensure proper alignment */
+ if ((offset_in_block % 4) != 0)
+ return EIO;
+
+ /* Ensure that the core of the entry does not overflow the block */
+ if (offset_in_block > block_size - 8)
+ return EIO;
+
+ struct ext4_directory_entry_ll *entry =
+ (void *)(it->current_block.data + offset_in_block);
+
+ /* Ensure that the whole entry does not overflow the block */
+ uint16_t length = ext4_dir_entry_ll_get_entry_length(entry);
+ if (offset_in_block + length > block_size)
+ return EIO;
+
+ /* Ensure the name length is not too large */
+ if (ext4_dir_entry_ll_get_name_length(&it->inode_ref->fs->sb, entry) >
+ length - 8)
+ return EIO;
+
+ /* Everything OK - "publish" the entry */
+ it->current = entry;
+ return EOK;
+}
+
+/**@brief Seek to next valid directory entry.
+ * Here can be jumped to the next data block.
+ * @param it Initialized iterator
+ * @param pos Position of the next entry
+ * @return Error code
+ */
+static int ext4_dir_iterator_seek(struct ext4_directory_iterator *it,
+ uint64_t pos)
+{
+ uint64_t size =
+ ext4_inode_get_size(&it->inode_ref->fs->sb, it->inode_ref->inode);
+
+ /* The iterator is not valid until we seek to the desired position */
+ it->current = NULL;
+
+ /* Are we at the end? */
+ if (pos >= size) {
+ if (it->current_block.lb_id) {
+
+ int rc = ext4_block_set(it->inode_ref->fs->bdev,
+ &it->current_block);
+ it->current_block.lb_id = 0;
+
+ if (rc != EOK)
+ return rc;
+ }
+
+ it->current_offset = pos;
+ return EOK;
+ }
+
+ /* Compute next block address */
+ uint32_t block_size = ext4_sb_get_block_size(&it->inode_ref->fs->sb);
+ uint64_t current_block_idx = it->current_offset / block_size;
+ uint64_t next_block_idx = pos / block_size;
+
+ /*
+ * If we don't have a block or are moving across block boundary,
+ * we need to get another block
+ */
+ if ((it->current_block.lb_id == 0) ||
+ (current_block_idx != next_block_idx)) {
+ if (it->current_block.lb_id) {
+ int rc = ext4_block_set(it->inode_ref->fs->bdev,
+ &it->current_block);
+ it->current_block.lb_id = 0;
+
+ if (rc != EOK)
+ return rc;
+ }
+
+ uint32_t next_block_phys_idx;
+ int rc = ext4_fs_get_inode_data_block_index(
+ it->inode_ref, next_block_idx, &next_block_phys_idx);
+ if (rc != EOK)
+ return rc;
+
+ rc = ext4_block_get(it->inode_ref->fs->bdev, &it->current_block,
+ next_block_phys_idx);
+ if (rc != EOK) {
+ it->current_block.lb_id = 0;
+ return rc;
+ }
+ }
+
+ it->current_offset = pos;
+
+ return ext4_dir_iterator_set(it, block_size);
+}
+
+int ext4_dir_iterator_init(struct ext4_directory_iterator *it,
+ struct ext4_inode_ref *inode_ref, uint64_t pos)
+{
+ it->inode_ref = inode_ref;
+ it->current = 0;
+ it->current_offset = 0;
+ it->current_block.lb_id = 0;
+
+ return ext4_dir_iterator_seek(it, pos);
+}
+
+int ext4_dir_iterator_next(struct ext4_directory_iterator *it)
+{
+ int r = EOK;
+ uint16_t skip;
+
+ while (r == EOK) {
+ skip = ext4_dir_entry_ll_get_entry_length(it->current);
+ r = ext4_dir_iterator_seek(it, it->current_offset + skip);
+
+ if (!it->current)
+ break;
+ /*Skip NULL referenced entry*/
+ if (ext4_dir_entry_ll_get_inode(it->current) != 0)
+ break;
+ }
+
+ return r;
+}
+
+int ext4_dir_iterator_fini(struct ext4_directory_iterator *it)
+{
+ it->current = 0;
+
+ if (it->current_block.lb_id)
+ return ext4_block_set(it->inode_ref->fs->bdev,
+ &it->current_block);
+
+ return EOK;
+}
+
+void ext4_dir_write_entry(struct ext4_sblock *sb,
+ struct ext4_directory_entry_ll *entry,
+ uint16_t entry_len, struct ext4_inode_ref *child,
+ const char *name, size_t name_len)
+{
+ /* Check maximum entry length */
+ ext4_assert(entry_len <= ext4_sb_get_block_size(sb));
+
+ /* Set basic attributes */
+ ext4_dir_entry_ll_set_inode(entry, child->index);
+ ext4_dir_entry_ll_set_entry_length(entry, entry_len);
+ ext4_dir_entry_ll_set_name_length(sb, entry, name_len);
+
+ /* Write name */
+ memcpy(entry->name, name, name_len);
+
+ /* Set type of entry */
+ if (ext4_inode_is_type(sb, child->inode, EXT4_INODE_MODE_DIRECTORY))
+ ext4_dir_entry_ll_set_inode_type(sb, entry,
+ EXT4_DIRECTORY_FILETYPE_DIR);
+ else
+ ext4_dir_entry_ll_set_inode_type(
+ sb, entry, EXT4_DIRECTORY_FILETYPE_REG_FILE);
+}
+
+int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
+ uint32_t name_len, struct ext4_inode_ref *child)
+{
+ struct ext4_fs *fs = parent->fs;
+
+#if CONFIG_DIR_INDEX_ENABLE
+ /* Index adding (if allowed) */
+ if ((ext4_sb_has_feature_compatible(&fs->sb,
+ EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
+ (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
+ int rc = ext4_dir_dx_add_entry(parent, child, name);
+
+ /* Check if index is not corrupted */
+ if (rc != EXT4_ERR_BAD_DX_DIR) {
+ if (rc != EOK)
+ return rc;
+
+ return EOK;
+ }
+
+ /* Needed to clear dir index flag if corrupted */
+ ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
+ parent->dirty = true;
+ }
+#endif
+
+ /* Linear algorithm */
+ uint32_t iblock = 0;
+ uint32_t fblock = 0;
+ uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
+ uint32_t inode_size = ext4_inode_get_size(&fs->sb, parent->inode);
+ uint32_t total_blocks = inode_size / block_size;
+
+ /* Find block, where is space for new entry and try to add */
+ bool success = false;
+ for (iblock = 0; iblock < total_blocks; ++iblock) {
+ int rc =
+ ext4_fs_get_inode_data_block_index(parent, iblock, &fblock);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_block block;
+ rc = ext4_block_get(fs->bdev, &block, fblock);
+ if (rc != EOK)
+ return rc;
+
+ /* If adding is successful, function can finish */
+ rc = ext4_dir_try_insert_entry(&fs->sb, &block, child, name,
+ name_len);
+ if (rc == EOK)
+ success = true;
+
+ rc = ext4_block_set(fs->bdev, &block);
+ if (rc != EOK)
+ return rc;
+
+ if (success)
+ return EOK;
+ }
+
+ /* No free block found - needed to allocate next data block */
+
+ iblock = 0;
+ fblock = 0;
+ int rc = ext4_fs_append_inode_block(parent, &fblock, &iblock);
+ if (rc != EOK)
+ return rc;
+
+ /* Load new block */
+ struct ext4_block new_block;
+
+ rc = ext4_block_get(fs->bdev, &new_block, fblock);
+ if (rc != EOK)
+ return rc;
+
+ /* Fill block with zeroes */
+ memset(new_block.data, 0, block_size);
+ struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;
+ ext4_dir_write_entry(&fs->sb, block_entry, block_size, child, name,
+ name_len);
+
+ /* Save new block */
+ new_block.dirty = true;
+ rc = ext4_block_set(fs->bdev, &new_block);
+
+ return rc;
+}
+
+int ext4_dir_find_entry(struct ext4_directory_search_result *result,
+ struct ext4_inode_ref *parent, const char *name,
+ uint32_t name_len)
+{
+ struct ext4_sblock *sb = &parent->fs->sb;
+
+#if CONFIG_DIR_INDEX_ENABLE
+ /* Index search */
+ if ((ext4_sb_has_feature_compatible(sb,
+ EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
+ (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
+ int rc = ext4_dir_dx_find_entry(result, parent, name_len, name);
+
+ /* Check if index is not corrupted */
+ if (rc != EXT4_ERR_BAD_DX_DIR) {
+ if (rc != EOK)
+ return rc;
+
+ return EOK;
+ }
+
+ /* Needed to clear dir index flag if corrupted */
+ ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
+ parent->dirty = true;
+ }
+#endif
+
+ /* Linear algorithm */
+
+ uint32_t iblock;
+ uint32_t fblock;
+ uint32_t block_size = ext4_sb_get_block_size(sb);
+ uint32_t inode_size = ext4_inode_get_size(sb, parent->inode);
+ uint32_t total_blocks = inode_size / block_size;
+
+ /* Walk through all data blocks */
+ for (iblock = 0; iblock < total_blocks; ++iblock) {
+ /* Load block address */
+ int rc =
+ ext4_fs_get_inode_data_block_index(parent, iblock, &fblock);
+ if (rc != EOK)
+ return rc;
+
+ /* Load data block */
+ struct ext4_block block;
+ rc = ext4_block_get(parent->fs->bdev, &block, fblock);
+ if (rc != EOK)
+ return rc;
+
+ /* Try to find entry in block */
+ struct ext4_directory_entry_ll *res_entry;
+ rc = ext4_dir_find_in_block(&block, sb, name_len, name,
+ &res_entry);
+ if (rc == EOK) {
+ result->block = block;
+ result->dentry = res_entry;
+ return EOK;
+ }
+
+ /* Entry not found - put block and continue to the next block */
+
+ rc = ext4_block_set(parent->fs->bdev, &block);
+ if (rc != EOK)
+ return rc;
+ }
+
+ /* Entry was not found */
+
+ result->block.lb_id = 0;
+ result->dentry = NULL;
+
+ return ENOENT;
+}
+
+int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name,
+ uint32_t name_len)
+{
+ /* Check if removing from directory */
+ if (!ext4_inode_is_type(&parent->fs->sb, parent->inode,
+ EXT4_INODE_MODE_DIRECTORY))
+ return ENOTDIR;
+
+ /* Try to find entry */
+ struct ext4_directory_search_result result;
+ int rc = ext4_dir_find_entry(&result, parent, name, name_len);
+ if (rc != EOK)
+ return rc;
+
+ /* Invalidate entry */
+ ext4_dir_entry_ll_set_inode(result.dentry, 0);
+
+ /* Store entry position in block */
+ uint32_t pos = (uint8_t *)result.dentry - result.block.data;
+
+ /*
+ * If entry is not the first in block, it must be merged
+ * with previous entry
+ */
+ if (pos != 0) {
+ uint32_t offset = 0;
+
+ /* Start from the first entry in block */
+ struct ext4_directory_entry_ll *tmp_dentry =
+ (void *)result.block.data;
+ uint16_t tmp_dentry_length =
+ ext4_dir_entry_ll_get_entry_length(tmp_dentry);
+
+ /* Find direct predecessor of removed entry */
+ while ((offset + tmp_dentry_length) < pos) {
+ offset +=
+ ext4_dir_entry_ll_get_entry_length(tmp_dentry);
+ tmp_dentry = (void *)(result.block.data + offset);
+ tmp_dentry_length =
+ ext4_dir_entry_ll_get_entry_length(tmp_dentry);
+ }
+
+ ext4_assert(tmp_dentry_length + offset == pos);
+
+ /* Add to removed entry length to predecessor's length */
+ uint16_t del_entry_length =
+ ext4_dir_entry_ll_get_entry_length(result.dentry);
+ ext4_dir_entry_ll_set_entry_length(
+ tmp_dentry, tmp_dentry_length + del_entry_length);
+ }
+
+ result.block.dirty = true;
+
+ return ext4_dir_destroy_result(parent, &result);
+}
+
+int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
+ struct ext4_block *target_block,
+ struct ext4_inode_ref *child, const char *name,
+ uint32_t name_len)
+{
+ /* Compute required length entry and align it to 4 bytes */
+ uint32_t block_size = ext4_sb_get_block_size(sb);
+ uint16_t required_len =
+ sizeof(struct ext4_fake_directory_entry) + name_len;
+
+ if ((required_len % 4) != 0)
+ required_len += 4 - (required_len % 4);
+
+ /* Initialize pointers, stop means to upper bound */
+ struct ext4_directory_entry_ll *dentry = (void *)target_block->data;
+ struct ext4_directory_entry_ll *stop =
+ (void *)(target_block->data + block_size);
+
+ /*
+ * Walk through the block and check for invalid entries
+ * or entries with free space for new entry
+ */
+ while (dentry < stop) {
+ uint32_t inode = ext4_dir_entry_ll_get_inode(dentry);
+ uint16_t rec_len = ext4_dir_entry_ll_get_entry_length(dentry);
+
+ /* If invalid and large enough entry, use it */
+ if ((inode == 0) && (rec_len >= required_len)) {
+ ext4_dir_write_entry(sb, dentry, rec_len, child, name,
+ name_len);
+ target_block->dirty = true;
+
+ return EOK;
+ }
+
+ /* Valid entry, try to split it */
+ if (inode != 0) {
+ uint16_t used_name_len =
+ ext4_dir_entry_ll_get_name_length(sb, dentry);
+
+ uint16_t used_space =
+ sizeof(struct ext4_fake_directory_entry) +
+ used_name_len;
+
+ if ((used_name_len % 4) != 0)
+ used_space += 4 - (used_name_len % 4);
+
+ uint16_t free_space = rec_len - used_space;
+
+ /* There is free space for new entry */
+ if (free_space >= required_len) {
+ /* Cut tail of current entry */
+ ext4_dir_entry_ll_set_entry_length(dentry,
+ used_space);
+ struct ext4_directory_entry_ll *new_entry =
+ (void *)((uint8_t *)dentry + used_space);
+ ext4_dir_write_entry(sb, new_entry, free_space,
+ child, name, name_len);
+
+ target_block->dirty = true;
+ return EOK;
+ }
+ }
+
+ /* Jump to the next entry */
+ dentry = (void *)((uint8_t *)dentry + rec_len);
+ }
+
+ /* No free space found for new entry */
+ return ENOSPC;
+}
+
+int ext4_dir_find_in_block(struct ext4_block *block, struct ext4_sblock *sb,
+ size_t name_len, const char *name,
+ struct ext4_directory_entry_ll **res_entry)
+{
+ /* Start from the first entry in block */
+ struct ext4_directory_entry_ll *dentry =
+ (struct ext4_directory_entry_ll *)block->data;
+
+ /* Set upper bound for cycling */
+ uint8_t *addr_limit = block->data + ext4_sb_get_block_size(sb);
+
+ /* Walk through the block and check entries */
+ while ((uint8_t *)dentry < addr_limit) {
+ /* Termination condition */
+ if ((uint8_t *)dentry + name_len > addr_limit)
+ break;
+
+ /* Valid entry - check it */
+ if (ext4_dir_entry_ll_get_inode(dentry) != 0) {
+ /* For more efficient compare only lengths firstly*/
+ if (ext4_dir_entry_ll_get_name_length(sb, dentry) ==
+ name_len) {
+ /* Compare names */
+ if (memcmp((uint8_t *)name, dentry->name,
+ name_len) == 0) {
+ *res_entry = dentry;
+ return EOK;
+ }
+ }
+ }
+
+ uint16_t dentry_len =
+ ext4_dir_entry_ll_get_entry_length(dentry);
+
+ /* Corrupted entry */
+ if (dentry_len == 0)
+ return EINVAL;
+
+ /* Jump to next entry */
+ dentry = (struct ext4_directory_entry_ll *)((uint8_t *)dentry +
+ dentry_len);
+ }
+
+ /* Entry not found */
+ return ENOENT;
+}
+
+int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
+ struct ext4_directory_search_result *result)
+{
+ if (result->block.lb_id)
+ return ext4_block_set(parent->fs->bdev, &result->block);
+
+ return EOK;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_dir.h
+++ b/lwext4/ext4_dir.h
@@ -1,258 +1,258 @@
-/*
- * 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_dir.h
- * @brief Directory handle procedures.
- */
-
-#ifndef EXT4_DIR_H_
-#define EXT4_DIR_H_
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_blockdev.h"
-#include "ext4_super.h"
-
-#include <stdint.h>
-
-/**@brief Get i-node number from directory entry.
- * @param de Directory entry
- * @return I-node number
- */
-static inline uint32_t
-ext4_dir_entry_ll_get_inode(struct ext4_directory_entry_ll *de)
-{
- return to_le32(de->inode);
-}
-
-/**@brief Set i-node number to directory entry.
- * @param de Directory entry
- * @param inode I-node number
- */
-static inline void
-ext4_dir_entry_ll_set_inode(struct ext4_directory_entry_ll *de, uint32_t inode)
-{
- de->inode = to_le32(inode);
-}
-
-/**@brief Get directory entry length.
- * @param de Directory entry
- * @return Entry length
- */
-static inline uint16_t
-ext4_dir_entry_ll_get_entry_length(struct ext4_directory_entry_ll *de)
-{
- return to_le16(de->entry_length);
-}
-
-/**@brief Set directory entry length.
- * @param de Directory entry
- * @param length Entry length
- */
-static inline void
-ext4_dir_entry_ll_set_entry_length(struct ext4_directory_entry_ll *de,
- uint16_t len)
-{
- de->entry_length = to_le16(len);
-}
-
-/**@brief Get directory entry name length.
- * @param sb Superblock
- * @param de Directory entry
- * @return Entry name length
- */
-static inline uint16_t
-ext4_dir_entry_ll_get_name_length(struct ext4_sblock *sb,
- struct ext4_directory_entry_ll *de)
-{
- uint16_t v = de->name_length;
-
- if ((ext4_get32(sb, rev_level) == 0) &&
- (ext4_get32(sb, minor_rev_level) < 5))
- v |= ((uint16_t)de->in.name_length_high) << 8;
-
- return v;
-}
-
-/**@brief Set directory entry name length.
- * @param sb Superblock
- * @param de Directory entry
- * @param length Entry name length
- */
-static inline void ext4_dir_entry_ll_set_name_length(
- struct ext4_sblock *sb, struct ext4_directory_entry_ll *de, uint16_t len)
-{
- de->name_length = (len << 8) >> 8;
-
- if ((ext4_get32(sb, rev_level) == 0) &&
- (ext4_get32(sb, minor_rev_level) < 5))
- de->in.name_length_high = len >> 8;
-}
-
-/**@brief Get i-node type of directory entry.
- * @param sb Superblock
- * @param de Directory entry
- * @return I-node type (file, dir, etc.)
- */
-static inline uint8_t
-ext4_dir_entry_ll_get_inode_type(struct ext4_sblock *sb,
- struct ext4_directory_entry_ll *de)
-{
- if ((ext4_get32(sb, rev_level) > 0) ||
- (ext4_get32(sb, minor_rev_level) >= 5))
- return de->in.inode_type;
-
- return EXT4_DIRECTORY_FILETYPE_UNKNOWN;
-}
-/**@brief Set i-node type of directory entry.
- * @param sb Superblock
- * @param de Directory entry
- * @param type I-node type (file, dir, etc.)
- */
-
-static inline void ext4_dir_entry_ll_set_inode_type(
- struct ext4_sblock *sb, struct ext4_directory_entry_ll *de, uint8_t type)
-{
- if ((ext4_get32(sb, rev_level) > 0) ||
- (ext4_get32(sb, minor_rev_level) >= 5))
- de->in.inode_type = type;
-}
-
-/**@brief Initialize directory iterator.
- * Set position to the first valid entry from the required position.
- * @param it Pointer to iterator to be initialized
- * @param inode_ref Directory i-node
- * @param pos Position to start reading entries from
- * @return Error code
- */
-int ext4_dir_iterator_init(struct ext4_directory_iterator *it,
- struct ext4_inode_ref *inode_ref, uint64_t pos);
-
-/**@brief Jump to the next valid entry
- * @param it Initialized iterator
- * @return Error code
- */
-int ext4_dir_iterator_next(struct ext4_directory_iterator *it);
-
-/**@brief Uninitialize directory iterator.
- * Release all allocated structures.
- * @param it Iterator to be finished
- * @return Error code
- */
-int ext4_dir_iterator_fini(struct ext4_directory_iterator *it);
-
-/**@brief Write directory entry to concrete data block.
- * @param sb Superblock
- * @param entry Pointer to entry to be written
- * @param entry_len Length of new entry
- * @param child Child i-node to be written to new entry
- * @param name Name of the new entry
- * @param name_len Length of entry name
- */
-void ext4_dir_write_entry(struct ext4_sblock *sb,
- struct ext4_directory_entry_ll *entry,
- uint16_t entry_len, struct ext4_inode_ref *child,
- const char *name, size_t name_len);
-
-/**@brief Add new entry to the directory.
- * @param parent Directory i-node
- * @param name Name of new entry
- * @param child I-node to be referenced from new entry
- * @return Error code
- */
-int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
- uint32_t name_len, struct ext4_inode_ref *child);
-
-/**@brief Find directory entry with passed name.
- * @param result Result structure to be returned if entry found
- * @param parent Directory i-node
- * @param name Name of entry to be found
- * @param name_len Name length
- * @return Error code
- */
-int ext4_dir_find_entry(struct ext4_directory_search_result *result,
- struct ext4_inode_ref *parent, const char *name,
- uint32_t name_len);
-
-/**@brief Remove directory entry.
- * @param parent Directory i-node
- * @param name Name of the entry to be removed
- * @param name_len Name length
- * @return Error code
- */
-int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name,
- uint32_t name_len);
-
-/**@brief Try to insert entry to concrete data block.
- * @param sb Superblock
- * @param target_block Block to try to insert entry to
- * @param child Child i-node to be inserted by new entry
- * @param name Name of the new entry
- * @param name_len Length of the new entry name
- * @return Error code
- */
-int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
- struct ext4_block *target_block,
- struct ext4_inode_ref *child, const char *name,
- uint32_t name_len);
-
-/**@brief Try to find entry in block by name.
- * @param block Block containing entries
- * @param sb Superblock
- * @param name_len Length of entry name
- * @param name Name of entry to be found
- * @param res_entry Output pointer to found entry, NULL if not found
- * @return Error code
- */
-int ext4_dir_find_in_block(struct ext4_block *block, struct ext4_sblock *sb,
- size_t name_len, const char *name,
- struct ext4_directory_entry_ll **res_entry);
-
-/**@brief Simple function to release allocated data from result.
- * @param parent Parent inode
- * @param result Search result to destroy
- * @return Error code
- *
- */
-int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
- struct ext4_directory_search_result *result);
-
-#endif /* EXT4_DIR_H_ */
-
-/**
- * @}
- */
+/*
+ * 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_dir.h
+ * @brief Directory handle procedures.
+ */
+
+#ifndef EXT4_DIR_H_
+#define EXT4_DIR_H_
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_blockdev.h"
+#include "ext4_super.h"
+
+#include <stdint.h>
+
+/**@brief Get i-node number from directory entry.
+ * @param de Directory entry
+ * @return I-node number
+ */
+static inline uint32_t
+ext4_dir_entry_ll_get_inode(struct ext4_directory_entry_ll *de)
+{
+ return to_le32(de->inode);
+}
+
+/**@brief Set i-node number to directory entry.
+ * @param de Directory entry
+ * @param inode I-node number
+ */
+static inline void
+ext4_dir_entry_ll_set_inode(struct ext4_directory_entry_ll *de, uint32_t inode)
+{
+ de->inode = to_le32(inode);
+}
+
+/**@brief Get directory entry length.
+ * @param de Directory entry
+ * @return Entry length
+ */
+static inline uint16_t
+ext4_dir_entry_ll_get_entry_length(struct ext4_directory_entry_ll *de)
+{
+ return to_le16(de->entry_length);
+}
+
+/**@brief Set directory entry length.
+ * @param de Directory entry
+ * @param length Entry length
+ */
+static inline void
+ext4_dir_entry_ll_set_entry_length(struct ext4_directory_entry_ll *de,
+ uint16_t len)
+{
+ de->entry_length = to_le16(len);
+}
+
+/**@brief Get directory entry name length.
+ * @param sb Superblock
+ * @param de Directory entry
+ * @return Entry name length
+ */
+static inline uint16_t
+ext4_dir_entry_ll_get_name_length(struct ext4_sblock *sb,
+ struct ext4_directory_entry_ll *de)
+{
+ uint16_t v = de->name_length;
+
+ if ((ext4_get32(sb, rev_level) == 0) &&
+ (ext4_get32(sb, minor_rev_level) < 5))
+ v |= ((uint16_t)de->in.name_length_high) << 8;
+
+ return v;
+}
+
+/**@brief Set directory entry name length.
+ * @param sb Superblock
+ * @param de Directory entry
+ * @param length Entry name length
+ */
+static inline void ext4_dir_entry_ll_set_name_length(
+ struct ext4_sblock *sb, struct ext4_directory_entry_ll *de, uint16_t len)
+{
+ de->name_length = (len << 8) >> 8;
+
+ if ((ext4_get32(sb, rev_level) == 0) &&
+ (ext4_get32(sb, minor_rev_level) < 5))
+ de->in.name_length_high = len >> 8;
+}
+
+/**@brief Get i-node type of directory entry.
+ * @param sb Superblock
+ * @param de Directory entry
+ * @return I-node type (file, dir, etc.)
+ */
+static inline uint8_t
+ext4_dir_entry_ll_get_inode_type(struct ext4_sblock *sb,
+ struct ext4_directory_entry_ll *de)
+{
+ if ((ext4_get32(sb, rev_level) > 0) ||
+ (ext4_get32(sb, minor_rev_level) >= 5))
+ return de->in.inode_type;
+
+ return EXT4_DIRECTORY_FILETYPE_UNKNOWN;
+}
+/**@brief Set i-node type of directory entry.
+ * @param sb Superblock
+ * @param de Directory entry
+ * @param type I-node type (file, dir, etc.)
+ */
+
+static inline void ext4_dir_entry_ll_set_inode_type(
+ struct ext4_sblock *sb, struct ext4_directory_entry_ll *de, uint8_t type)
+{
+ if ((ext4_get32(sb, rev_level) > 0) ||
+ (ext4_get32(sb, minor_rev_level) >= 5))
+ de->in.inode_type = type;
+}
+
+/**@brief Initialize directory iterator.
+ * Set position to the first valid entry from the required position.
+ * @param it Pointer to iterator to be initialized
+ * @param inode_ref Directory i-node
+ * @param pos Position to start reading entries from
+ * @return Error code
+ */
+int ext4_dir_iterator_init(struct ext4_directory_iterator *it,
+ struct ext4_inode_ref *inode_ref, uint64_t pos);
+
+/**@brief Jump to the next valid entry
+ * @param it Initialized iterator
+ * @return Error code
+ */
+int ext4_dir_iterator_next(struct ext4_directory_iterator *it);
+
+/**@brief Uninitialize directory iterator.
+ * Release all allocated structures.
+ * @param it Iterator to be finished
+ * @return Error code
+ */
+int ext4_dir_iterator_fini(struct ext4_directory_iterator *it);
+
+/**@brief Write directory entry to concrete data block.
+ * @param sb Superblock
+ * @param entry Pointer to entry to be written
+ * @param entry_len Length of new entry
+ * @param child Child i-node to be written to new entry
+ * @param name Name of the new entry
+ * @param name_len Length of entry name
+ */
+void ext4_dir_write_entry(struct ext4_sblock *sb,
+ struct ext4_directory_entry_ll *entry,
+ uint16_t entry_len, struct ext4_inode_ref *child,
+ const char *name, size_t name_len);
+
+/**@brief Add new entry to the directory.
+ * @param parent Directory i-node
+ * @param name Name of new entry
+ * @param child I-node to be referenced from new entry
+ * @return Error code
+ */
+int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
+ uint32_t name_len, struct ext4_inode_ref *child);
+
+/**@brief Find directory entry with passed name.
+ * @param result Result structure to be returned if entry found
+ * @param parent Directory i-node
+ * @param name Name of entry to be found
+ * @param name_len Name length
+ * @return Error code
+ */
+int ext4_dir_find_entry(struct ext4_directory_search_result *result,
+ struct ext4_inode_ref *parent, const char *name,
+ uint32_t name_len);
+
+/**@brief Remove directory entry.
+ * @param parent Directory i-node
+ * @param name Name of the entry to be removed
+ * @param name_len Name length
+ * @return Error code
+ */
+int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name,
+ uint32_t name_len);
+
+/**@brief Try to insert entry to concrete data block.
+ * @param sb Superblock
+ * @param target_block Block to try to insert entry to
+ * @param child Child i-node to be inserted by new entry
+ * @param name Name of the new entry
+ * @param name_len Length of the new entry name
+ * @return Error code
+ */
+int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
+ struct ext4_block *target_block,
+ struct ext4_inode_ref *child, const char *name,
+ uint32_t name_len);
+
+/**@brief Try to find entry in block by name.
+ * @param block Block containing entries
+ * @param sb Superblock
+ * @param name_len Length of entry name
+ * @param name Name of entry to be found
+ * @param res_entry Output pointer to found entry, NULL if not found
+ * @return Error code
+ */
+int ext4_dir_find_in_block(struct ext4_block *block, struct ext4_sblock *sb,
+ size_t name_len, const char *name,
+ struct ext4_directory_entry_ll **res_entry);
+
+/**@brief Simple function to release allocated data from result.
+ * @param parent Parent inode
+ * @param result Search result to destroy
+ * @return Error code
+ *
+ */
+int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
+ struct ext4_directory_search_result *result);
+
+#endif /* EXT4_DIR_H_ */
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_dir_idx.c
+++ b/lwext4/ext4_dir_idx.c
@@ -1,1198 +1,1198 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * 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_dir_idx.c
- * @brief Directory indexing procedures.
- */
-
-#include "ext4_config.h"
-#include "ext4_dir_idx.h"
-#include "ext4_dir.h"
-#include "ext4_blockdev.h"
-#include "ext4_fs.h"
-#include "ext4_super.h"
-#include "ext4_hash.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-/**@brief Get hash version used in directory index.
- * @param root_info Pointer to root info structure of index
- * @return Hash algorithm version
- */
-static inline uint8_t ext4_dir_dx_root_info_get_hash_version(
- struct ext4_directory_dx_root_info *root_info)
-{
- return root_info->hash_version;
-}
-
-/**@brief Set hash version, that will be used in directory index.
- * @param root_info Pointer to root info structure of index
- * @param v Hash algorithm version
- */
-static inline void ext4_dir_dx_root_info_set_hash_version(
- struct ext4_directory_dx_root_info *root_info, uint8_t v)
-{
- root_info->hash_version = v;
-}
-
-/**@brief Get length of root_info structure in bytes.
- * @param root_info Pointer to root info structure of index
- * @return Length of the structure
- */
-static inline uint8_t ext4_dir_dx_root_info_get_info_length(
- struct ext4_directory_dx_root_info *root_info)
-{
- return root_info->info_length;
-}
-
-/**@brief Set length of root_info structure in bytes.
- * @param root_info Pointer to root info structure of index
- * @param info_length Length of the structure
- */
-static inline void ext4_dir_dx_root_info_set_info_length(
- struct ext4_directory_dx_root_info *root_info, uint8_t len)
-{
- root_info->info_length = len;
-}
-
-/**@brief Get number of indirect levels of HTree.
- * @param root_info Pointer to root info structure of index
- * @return Height of HTree (actually only 0 or 1)
- */
-static inline uint8_t ext4_dir_dx_root_info_get_indirect_levels(
- struct ext4_directory_dx_root_info *root_info)
-{
- return root_info->indirect_levels;
-}
-
-/**@brief Set number of indirect levels of HTree.
- * @param root_info Pointer to root info structure of index
- * @param lvl Height of HTree (actually only 0 or 1)
- */
-static inline void ext4_dir_dx_root_info_set_indirect_levels(
- struct ext4_directory_dx_root_info *root_info, uint8_t lvl)
-{
- root_info->indirect_levels = lvl;
-}
-
-/**@brief Get maximum number of index node entries.
- * @param climit Pointer to counlimit structure
- * @return Maximum of entries in node
- */
-static inline uint16_t
-ext4_dir_dx_countlimit_get_limit(struct ext4_directory_dx_countlimit *climit)
-{
- return to_le16(climit->limit);
-}
-
-/**@brief Set maximum number of index node entries.
- * @param climit Pointer to counlimit structure
- * @param limit Maximum of entries in node
- */
-static inline void
-ext4_dir_dx_countlimit_set_limit(struct ext4_directory_dx_countlimit *climit,
- uint16_t limit)
-{
- climit->limit = to_le16(limit);
-}
-
-/**@brief Get current number of index node entries.
- * @param climit Pointer to counlimit structure
- * @return Number of entries in node
- */
-static inline uint16_t
-ext4_dir_dx_countlimit_get_count(struct ext4_directory_dx_countlimit *climit)
-{
- return to_le16(climit->count);
-}
-
-/**@brief Set current number of index node entries.
- * @param climit Pointer to counlimit structure
- * @param count Number of entries in node
- */
-static inline void
-ext4_dir_dx_countlimit_set_count(struct ext4_directory_dx_countlimit *climit,
- uint16_t count)
-{
- climit->count = to_le16(count);
-}
-
-/**@brief Get hash value of index entry.
- * @param entry Pointer to index entry
- * @return Hash value
- */
-static inline uint32_t
-ext4_dir_dx_entry_get_hash(struct ext4_directory_dx_entry *entry)
-{
- return to_le32(entry->hash);
-}
-
-/**@brief Set hash value of index entry.
- * @param entry Pointer to index entry
- * @param hash Hash value
- */
-static inline void
-ext4_dir_dx_entry_set_hash(struct ext4_directory_dx_entry *entry, uint32_t hash)
-{
- entry->hash = to_le32(hash);
-}
-
-/**@brief Get block address where child node is located.
- * @param entry Pointer to index entry
- * @return Block address of child node
- */
-static inline uint32_t
-ext4_dir_dx_entry_get_block(struct ext4_directory_dx_entry *entry)
-{
- return to_le32(entry->block);
-}
-
-/**@brief Set block address where child node is located.
- * @param entry Pointer to index entry
- * @param block Block address of child node
- */
-static inline void
-ext4_dir_dx_entry_set_block(struct ext4_directory_dx_entry *entry,
- uint32_t block)
-{
- entry->block = to_le32(block);
-}
-
-/**@brief Sort entry item.*/
-struct ext4_dx_sort_entry {
- uint32_t hash;
- uint32_t rec_len;
- void *dentry;
-};
-
-static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len,
- const char *name)
-{
- return ext2_htree_hash(name, len, hinfo->seed, hinfo->hash_version,
- &hinfo->hash, &hinfo->minor_hash);
-}
-
-/****************************************************************************/
-
-int ext4_dir_dx_init(struct ext4_inode_ref *dir)
-{
- /* Load block 0, where will be index root located */
- uint32_t fblock;
- int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock);
- if (rc != EOK)
- return rc;
-
- struct ext4_block block;
- rc = ext4_block_get(dir->fs->bdev, &block, fblock);
- if (rc != EOK)
- return rc;
-
- /* Initialize pointers to data structures */
- struct ext4_directory_dx_root *root = (void *)block.data;
- struct ext4_directory_dx_root_info *info = &(root->info);
-
- /* Initialize root info structure */
- uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version);
-
- ext4_dir_dx_root_info_set_hash_version(info, hash_version);
- ext4_dir_dx_root_info_set_indirect_levels(info, 0);
- ext4_dir_dx_root_info_set_info_length(info, 8);
-
- /* Set limit and current number of entries */
- struct ext4_directory_dx_countlimit *countlimit =
- (struct ext4_directory_dx_countlimit *)&root->entries;
-
- ext4_dir_dx_countlimit_set_count(countlimit, 1);
-
- uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb);
- uint32_t entry_space = block_size -
- 2 * sizeof(struct ext4_directory_dx_dot_entry) -
- sizeof(struct ext4_directory_dx_root_info);
- uint16_t root_limit =
- entry_space / sizeof(struct ext4_directory_dx_entry);
- ext4_dir_dx_countlimit_set_limit(countlimit, root_limit);
-
- /* Append new block, where will be new entries inserted in the future */
- uint32_t iblock;
- rc = ext4_fs_append_inode_block(dir, &fblock, &iblock);
- if (rc != EOK) {
- ext4_block_set(dir->fs->bdev, &block);
- return rc;
- }
-
- struct ext4_block new_block;
-
- rc = ext4_block_get(dir->fs->bdev, &new_block, fblock);
- if (rc != EOK) {
- ext4_block_set(dir->fs->bdev, &block);
- return rc;
- }
-
- /* Fill the whole block with empty entry */
- struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;
-
- ext4_dir_entry_ll_set_entry_length(block_entry, block_size);
- ext4_dir_entry_ll_set_inode(block_entry, 0);
-
- new_block.dirty = true;
- rc = ext4_block_set(dir->fs->bdev, &new_block);
- if (rc != EOK) {
- ext4_block_set(dir->fs->bdev, &block);
- return rc;
- }
-
- /* Connect new block to the only entry in index */
- struct ext4_directory_dx_entry *entry = root->entries;
- ext4_dir_dx_entry_set_block(entry, iblock);
-
- block.dirty = true;
-
- return ext4_block_set(dir->fs->bdev, &block);
-}
-
-/**@brief Initialize hash info structure necessary for index operations.
- * @param hinfo Pointer to hinfo to be initialized
- * @param root_block Root block (number 0) of index
- * @param sb Pointer to superblock
- * @param name_len Length of name to be computed hash value from
- * @param name Name to be computed hash value from
- * @return Standard error code
- */
-static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo,
- struct ext4_block *root_block,
- struct ext4_sblock *sb, size_t name_len,
- const char *name)
-{
- struct ext4_directory_dx_root *root =
- (struct ext4_directory_dx_root *)root_block->data;
-
- if ((root->info.hash_version != EXT2_HTREE_LEGACY) &&
- (root->info.hash_version != EXT2_HTREE_HALF_MD4) &&
- (root->info.hash_version != EXT2_HTREE_TEA))
- return EXT4_ERR_BAD_DX_DIR;
-
- /* Check unused flags */
- if (root->info.unused_flags != 0)
- return EXT4_ERR_BAD_DX_DIR;
-
- /* Check indirect levels */
- if (root->info.indirect_levels > 1)
- return EXT4_ERR_BAD_DX_DIR;
-
- /* Check if node limit is correct */
- uint32_t block_size = ext4_sb_get_block_size(sb);
- uint32_t entry_space = block_size;
- entry_space -= 2 * sizeof(struct ext4_directory_dx_dot_entry);
- entry_space -= sizeof(struct ext4_directory_dx_root_info);
- entry_space = entry_space / sizeof(struct ext4_directory_dx_entry);
-
- uint16_t limit = ext4_dir_dx_countlimit_get_limit(
- (struct ext4_directory_dx_countlimit *)&root->entries);
- if (limit != entry_space)
- return EXT4_ERR_BAD_DX_DIR;
-
- /* Check hash version and modify if necessary */
- hinfo->hash_version =
- ext4_dir_dx_root_info_get_hash_version(&root->info);
- if ((hinfo->hash_version <= EXT2_HTREE_TEA) &&
- (ext4_sb_check_flag(sb, EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH))) {
- /* Use unsigned hash */
- hinfo->hash_version += 3;
- }
-
- /* Load hash seed from superblock */
-
- hinfo->seed = ext4_get8(sb, hash_seed);
-
- /* Compute hash value of name */
- if (name)
- return ext4_dir_dx_hash_string(hinfo, name_len, name);
-
- return EOK;
-}
-
-/**@brief Walk through index tree and load leaf with corresponding hash value.
- * @param hinfo Initialized hash info structure
- * @param inode_ref Current i-node
- * @param root_block Root block (iblock 0), where is root node located
- * @param dx_block Pointer to leaf node in dx_blocks array
- * @param dx_blocks Array with the whole path from root to leaf
- * @return Standard error code
- */
-static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo,
- struct ext4_inode_ref *inode_ref,
- struct ext4_block *root_block,
- struct ext4_directory_dx_block **dx_block,
- struct ext4_directory_dx_block *dx_blocks)
-{
- struct ext4_directory_dx_block *tmp_dx_block = dx_blocks;
- struct ext4_directory_dx_root *root =
- (struct ext4_directory_dx_root *)root_block->data;
- struct ext4_directory_dx_entry *entries =
- (struct ext4_directory_dx_entry *)&root->entries;
-
- uint16_t limit = ext4_dir_dx_countlimit_get_limit(
- (struct ext4_directory_dx_countlimit *)entries);
- uint8_t indirect_level =
- ext4_dir_dx_root_info_get_indirect_levels(&root->info);
-
- struct ext4_block *tmp_block = root_block;
- struct ext4_directory_dx_entry *p;
- struct ext4_directory_dx_entry *q;
- struct ext4_directory_dx_entry *m;
- struct ext4_directory_dx_entry *at;
-
- /* Walk through the index tree */
- while (true) {
- uint16_t count = ext4_dir_dx_countlimit_get_count(
- (struct ext4_directory_dx_countlimit *)entries);
- if ((count == 0) || (count > limit))
- return EXT4_ERR_BAD_DX_DIR;
-
- /* Do binary search in every node */
- p = entries + 1;
- q = entries + count - 1;
-
- while (p <= q) {
- m = p + (q - p) / 2;
- if (ext4_dir_dx_entry_get_hash(m) > hinfo->hash)
- q = m - 1;
- else
- p = m + 1;
- }
-
- at = p - 1;
-
- /* Write results */
-
- memcpy(&tmp_dx_block->block, tmp_block,
- sizeof(struct ext4_block));
- tmp_dx_block->entries = entries;
- tmp_dx_block->position = at;
-
- /* Is algorithm in the leaf? */
- if (indirect_level == 0) {
- *dx_block = tmp_dx_block;
- return EOK;
- }
-
- /* Goto child node */
- uint32_t next_block = ext4_dir_dx_entry_get_block(at);
-
- indirect_level--;
-
- uint32_t fblock;
- int rc = ext4_fs_get_inode_data_block_index(
- inode_ref, next_block, &fblock);
- if (rc != EOK)
- return rc;
-
- rc = ext4_block_get(inode_ref->fs->bdev, tmp_block, fblock);
- if (rc != EOK)
- return rc;
-
- entries =
- ((struct ext4_directory_dx_node *)tmp_block->data)->entries;
- limit = ext4_dir_dx_countlimit_get_limit(
- (struct ext4_directory_dx_countlimit *)entries);
-
- uint16_t entry_space =
- ext4_sb_get_block_size(&inode_ref->fs->sb) -
- sizeof(struct ext4_fake_directory_entry);
-
- entry_space =
- entry_space / sizeof(struct ext4_directory_dx_entry);
-
- if (limit != entry_space) {
- ext4_block_set(inode_ref->fs->bdev, tmp_block);
- return EXT4_ERR_BAD_DX_DIR;
- }
-
- ++tmp_dx_block;
- }
-
- /* Unreachable */
- return EOK;
-}
-
-/**@brief Check if the the next block would be checked during entry search.
- * @param inode_ref Directory i-node
- * @param hash Hash value to check
- * @param dx_block Current block
- * @param dx_blocks Array with path from root to leaf node
- * @return Standard Error code
- */
-static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref,
- uint32_t hash,
- struct ext4_directory_dx_block *dx_block,
- struct ext4_directory_dx_block *dx_blocks)
-{
- uint32_t num_handles = 0;
- struct ext4_directory_dx_block *p = dx_block;
-
- /* Try to find data block with next bunch of entries */
- while (true) {
- p->position++;
- uint16_t count = ext4_dir_dx_countlimit_get_count(
- (struct ext4_directory_dx_countlimit *)p->entries);
-
- if (p->position < p->entries + count)
- break;
-
- if (p == dx_blocks)
- return EOK;
-
- num_handles++;
- p--;
- }
-
- /* Check hash collision (if not occurred - no next block cannot be
- * used)*/
- uint32_t current_hash = ext4_dir_dx_entry_get_hash(p->position);
- if ((hash & 1) == 0) {
- if ((current_hash & ~1) != hash)
- return 0;
- }
-
- /* Fill new path */
- while (num_handles--) {
- uint32_t block_idx = ext4_dir_dx_entry_get_block(p->position);
- uint32_t block_addr;
-
- int rc = ext4_fs_get_inode_data_block_index(
- inode_ref, block_idx, &block_addr);
- if (rc != EOK)
- return rc;
-
- struct ext4_block block;
- rc = ext4_block_get(inode_ref->fs->bdev, &block, block_addr);
- if (rc != EOK)
- return rc;
-
- p++;
-
- /* Don't forget to put old block (prevent memory leak) */
- rc = ext4_block_set(inode_ref->fs->bdev, &p->block);
- if (rc != EOK)
- return rc;
-
- memcpy(&p->block, &p->block, sizeof(block));
- p->entries =
- ((struct ext4_directory_dx_node *)block.data)->entries;
- p->position = p->entries;
- }
-
- return ENOENT;
-}
-
-int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result,
- struct ext4_inode_ref *inode_ref, size_t name_len,
- const char *name)
-{
- /* Load direct block 0 (index root) */
- uint32_t root_block_addr;
- int rc2;
- int rc =
- ext4_fs_get_inode_data_block_index(inode_ref, 0, &root_block_addr);
- if (rc != EOK)
- return rc;
-
- struct ext4_fs *fs = inode_ref->fs;
-
- struct ext4_block root_block;
- rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
- if (rc != EOK)
- return rc;
-
- /* Initialize hash info (compute hash value) */
- struct ext4_hash_info hinfo;
- rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
- if (rc != EOK) {
- ext4_block_set(fs->bdev, &root_block);
- return EXT4_ERR_BAD_DX_DIR;
- }
-
- /*
- * Hardcoded number 2 means maximum height of index tree,
- * specified in the Linux driver.
- */
- struct ext4_directory_dx_block dx_blocks[2];
- struct ext4_directory_dx_block *dx_block;
- struct ext4_directory_dx_block *tmp;
-
- rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block, &dx_block,
- dx_blocks);
- if (rc != EOK) {
- ext4_block_set(fs->bdev, &root_block);
- return EXT4_ERR_BAD_DX_DIR;
- }
-
- do {
- /* Load leaf block */
- uint32_t leaf_block_idx =
- ext4_dir_dx_entry_get_block(dx_block->position);
- uint32_t leaf_block_addr;
-
- rc = ext4_fs_get_inode_data_block_index(
- inode_ref, leaf_block_idx, &leaf_block_addr);
- if (rc != EOK)
- goto cleanup;
-
- struct ext4_block leaf_block;
- rc = ext4_block_get(fs->bdev, &leaf_block, leaf_block_addr);
- if (rc != EOK)
- goto cleanup;
-
- /* Linear search inside block */
- struct ext4_directory_entry_ll *res_dentry;
- rc = ext4_dir_find_in_block(&leaf_block, &fs->sb, name_len,
- name, &res_dentry);
-
- /* Found => return it */
- if (rc == EOK) {
- result->block = leaf_block;
- result->dentry = res_dentry;
- goto cleanup;
- }
-
- /* Not found, leave untouched */
- rc2 = ext4_block_set(fs->bdev, &leaf_block);
- if (rc2 != EOK)
- goto cleanup;
-
- if (rc != ENOENT)
- goto cleanup;
-
- /* check if the next block could be checked */
- rc = ext4_dir_dx_next_block(inode_ref, hinfo.hash, dx_block,
- &dx_blocks[0]);
- if (rc < 0)
- goto cleanup;
- } while (rc == ENOENT);
-
- /* Entry not found */
- rc = ENOENT;
-
-cleanup:
- /* The whole path must be released (preventing memory leak) */
- tmp = dx_blocks;
-
- while (tmp <= dx_block) {
- rc2 = ext4_block_set(fs->bdev, &tmp->block);
- if (rc == EOK && rc2 != EOK)
- rc = rc2;
- ++tmp;
- }
-
- return rc;
-}
-
-#if CONFIG_DIR_INDEX_COMB_SORT
-#define SWAP_ENTRY(se1, se2) \
- do { \
- struct ext4_dx_sort_entry tmp = se1; \
- se1 = se2; \
- se2 = tmp; \
- \
-} while (0)
-
-static void comb_sort(struct ext4_dx_sort_entry *se, uint32_t count)
-{
- struct ext4_dx_sort_entry *p, *q, *top = se + count - 1;
- bool more;
- /* Combsort */
- while (count > 2) {
- count = (count * 10) / 13;
- if (count - 9 < 2)
- count = 11;
- for (p = top, q = p - count; q >= se; p--, q--)
- if (p->hash < q->hash)
- SWAP_ENTRY(*p, *q);
- }
- /* Bubblesort */
- do {
- more = 0;
- q = top;
- while (q-- > se) {
- if (q[1].hash >= q[0].hash)
- continue;
- SWAP_ENTRY(*(q + 1), *q);
- more = 1;
- }
- } while (more);
-}
-#else
-
-/**@brief Compare function used to pass in quicksort implementation.
- * It can compare two entries by hash value.
- * @param arg1 First entry
- * @param arg2 Second entry
- * @param dummy Unused parameter, can be NULL
- *
- * @return Classic compare result
- * (0: equal, -1: arg1 < arg2, 1: arg1 > arg2)
- */
-static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2)
-{
- struct ext4_dx_sort_entry *entry1 = (void *)arg1;
- struct ext4_dx_sort_entry *entry2 = (void *)arg2;
-
- if (entry1->hash == entry2->hash)
- return 0;
-
- if (entry1->hash < entry2->hash)
- return -1;
- else
- return 1;
-}
-#endif
-
-/**@brief Insert new index entry to block.
- * Note that space for new entry must be checked by caller.
- * @param index_block Block where to insert new entry
- * @param hash Hash value covered by child node
- * @param iblock Logical number of child block
- *
- */
-static void
-ext4_dir_dx_insert_entry(struct ext4_directory_dx_block *index_block,
- uint32_t hash, uint32_t iblock)
-{
- struct ext4_directory_dx_entry *old_index_entry = index_block->position;
- struct ext4_directory_dx_entry *new_index_entry = old_index_entry + 1;
-
- struct ext4_directory_dx_countlimit *countlimit =
- (struct ext4_directory_dx_countlimit *)index_block->entries;
- uint32_t count = ext4_dir_dx_countlimit_get_count(countlimit);
-
- struct ext4_directory_dx_entry *start_index = index_block->entries;
- size_t bytes =
- (uint8_t *)(start_index + count) - (uint8_t *)(new_index_entry);
-
- memmove(new_index_entry + 1, new_index_entry, bytes);
-
- ext4_dir_dx_entry_set_block(new_index_entry, iblock);
- ext4_dir_dx_entry_set_hash(new_index_entry, hash);
-
- ext4_dir_dx_countlimit_set_count(countlimit, count + 1);
-
- index_block->block.dirty = true;
-}
-
-/**@brief Split directory entries to two parts preventing node overflow.
- * @param inode_ref Directory i-node
- * @param hinfo Hash info
- * @param old_data_block Block with data to be split
- * @param index_block Block where index entries are located
- * @param new_data_block Output value for newly allocated data block
- */
-static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref,
- struct ext4_hash_info *hinfo,
- struct ext4_block *old_data_block,
- struct ext4_directory_dx_block *index_block,
- struct ext4_block *new_data_block)
-{
- int rc = EOK;
-
- /* Allocate buffer for directory entries */
- uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
-
- uint8_t *entry_buffer = malloc(block_size);
- if (entry_buffer == NULL)
- return ENOMEM;
-
- /* dot entry has the smallest size available */
- uint32_t max_entry_count =
- block_size / sizeof(struct ext4_directory_dx_dot_entry);
-
- /* Allocate sort entry */
- struct ext4_dx_sort_entry *sort_array =
- malloc(max_entry_count * sizeof(struct ext4_dx_sort_entry));
-
- if (sort_array == NULL) {
- free(entry_buffer);
- return ENOMEM;
- }
-
- uint32_t idx = 0;
- uint32_t real_size = 0;
-
- /* Initialize hinfo */
- struct ext4_hash_info tmp_hinfo;
- memcpy(&tmp_hinfo, hinfo, sizeof(struct ext4_hash_info));
-
- /* Load all valid entries to the buffer */
- struct ext4_directory_entry_ll *dentry = (void *)old_data_block->data;
- uint8_t *entry_buffer_ptr = entry_buffer;
- while ((void *)dentry < (void *)(old_data_block->data + block_size)) {
- /* Read only valid entries */
- if (ext4_dir_entry_ll_get_inode(dentry) &&
- dentry->name_length) {
- uint8_t len = ext4_dir_entry_ll_get_name_length(
- &inode_ref->fs->sb, dentry);
-
- rc = ext4_dir_dx_hash_string(&tmp_hinfo, len,
- (char *)dentry->name);
- if (rc != EOK) {
- free(sort_array);
- free(entry_buffer);
- return rc;
- }
-
- uint32_t rec_len = 8 + len;
-
- if ((rec_len % 4) != 0)
- rec_len += 4 - (rec_len % 4);
-
- memcpy(entry_buffer_ptr, dentry, rec_len);
-
- sort_array[idx].dentry = entry_buffer_ptr;
- sort_array[idx].rec_len = rec_len;
- sort_array[idx].hash = tmp_hinfo.hash;
-
- entry_buffer_ptr += rec_len;
- real_size += rec_len;
- idx++;
- }
-
- dentry = (void *)((uint8_t *)dentry +
- ext4_dir_entry_ll_get_entry_length(dentry));
- }
-
-/* Sort all entries */
-#if CONFIG_DIR_INDEX_COMB_SORT
- comb_sort(sort_array, idx);
-#else
- qsort(sort_array, idx, sizeof(struct ext4_dx_sort_entry),
- ext4_dir_dx_entry_comparator);
-#endif
- /* Allocate new block for store the second part of entries */
- uint32_t new_fblock;
- uint32_t new_iblock;
- rc = ext4_fs_append_inode_block(inode_ref, &new_fblock, &new_iblock);
- if (rc != EOK) {
- free(sort_array);
- free(entry_buffer);
- return rc;
- }
-
- /* Load new block */
- struct ext4_block new_data_block_tmp;
- rc = ext4_block_get(inode_ref->fs->bdev, &new_data_block_tmp,
- new_fblock);
- if (rc != EOK) {
- free(sort_array);
- free(entry_buffer);
- return rc;
- }
-
- /*
- * Distribute entries to two blocks (by size)
- * - compute the half
- */
- uint32_t new_hash = 0;
- uint32_t current_size = 0;
- uint32_t mid = 0;
- uint32_t i;
- for (i = 0; i < idx; ++i) {
- if ((current_size + sort_array[i].rec_len) > (block_size / 2)) {
- new_hash = sort_array[i].hash;
- mid = i;
- break;
- }
-
- current_size += sort_array[i].rec_len;
- }
-
- /* Check hash collision */
- uint32_t continued = 0;
- if (new_hash == sort_array[mid - 1].hash)
- continued = 1;
-
- uint32_t offset = 0;
- void *ptr;
-
- /* First part - to the old block */
- for (i = 0; i < mid; ++i) {
- ptr = old_data_block->data + offset;
- memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
-
- struct ext4_directory_entry_ll *tmp = ptr;
- if (i < (mid - 1))
- ext4_dir_entry_ll_set_entry_length(
- tmp, sort_array[i].rec_len);
- else
- ext4_dir_entry_ll_set_entry_length(tmp,
- block_size - offset);
-
- offset += sort_array[i].rec_len;
- }
-
- /* Second part - to the new block */
- offset = 0;
- for (i = mid; i < idx; ++i) {
- ptr = new_data_block_tmp.data + offset;
- memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
-
- struct ext4_directory_entry_ll *tmp = ptr;
- if (i < (idx - 1))
- ext4_dir_entry_ll_set_entry_length(
- tmp, sort_array[i].rec_len);
- else
- ext4_dir_entry_ll_set_entry_length(tmp,
- block_size - offset);
-
- offset += sort_array[i].rec_len;
- }
-
- /* Do some steps to finish operation */
- old_data_block->dirty = true;
- new_data_block_tmp.dirty = true;
-
- free(sort_array);
- free(entry_buffer);
-
- ext4_dir_dx_insert_entry(index_block, new_hash + continued, new_iblock);
-
- *new_data_block = new_data_block_tmp;
-
- return EOK;
-}
-
-/**@brief Split index node and maybe some parent nodes in the tree hierarchy.
- * @param inode_ref Directory i-node
- * @param dx_blocks Array with path from root to leaf node
- * @param dx_block Leaf block to be split if needed
- * @return Error code
- */
-static int
-ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref,
- struct ext4_directory_dx_block *dx_blocks,
- struct ext4_directory_dx_block *dx_block,
- struct ext4_directory_dx_block **new_dx_block)
-{
- struct ext4_directory_dx_entry *entries;
-
- if (dx_block == dx_blocks)
- entries =
- ((struct ext4_directory_dx_root *)dx_block->block.data)
- ->entries;
- else
- entries =
- ((struct ext4_directory_dx_node *)dx_block->block.data)
- ->entries;
-
- struct ext4_directory_dx_countlimit *countlimit =
- (struct ext4_directory_dx_countlimit *)entries;
-
- uint16_t leaf_limit = ext4_dir_dx_countlimit_get_limit(countlimit);
- uint16_t leaf_count = ext4_dir_dx_countlimit_get_count(countlimit);
-
- /* Check if is necessary to split index block */
- if (leaf_limit == leaf_count) {
- size_t levels = dx_block - dx_blocks;
-
- struct ext4_directory_dx_entry *root_entries =
- ((struct ext4_directory_dx_root *)dx_blocks[0].block.data)
- ->entries;
-
- struct ext4_directory_dx_countlimit *root_countlimit =
- (struct ext4_directory_dx_countlimit *)root_entries;
- uint16_t root_limit =
- ext4_dir_dx_countlimit_get_limit(root_countlimit);
- uint16_t root_count =
- ext4_dir_dx_countlimit_get_count(root_countlimit);
-
- /* Linux limitation */
- if ((levels > 0) && (root_limit == root_count))
- return ENOSPC;
-
- /* Add new block to directory */
- uint32_t new_fblock;
- uint32_t new_iblock;
- int rc = ext4_fs_append_inode_block(inode_ref, &new_fblock,
- &new_iblock);
- if (rc != EOK)
- return rc;
-
- /* load new block */
- struct ext4_block new_block;
- rc =
- ext4_block_get(inode_ref->fs->bdev, &new_block, new_fblock);
- if (rc != EOK)
- return rc;
-
- struct ext4_directory_dx_node *new_node =
- (void *)new_block.data;
- struct ext4_directory_dx_entry *new_entries = new_node->entries;
-
- memset(&new_node->fake, 0,
- sizeof(struct ext4_fake_directory_entry));
-
- uint32_t block_size =
- ext4_sb_get_block_size(&inode_ref->fs->sb);
-
- new_node->fake.entry_length = block_size;
-
- /* Split leaf node */
- if (levels > 0) {
- uint32_t count_left = leaf_count / 2;
- uint32_t count_right = leaf_count - count_left;
- uint32_t hash_right =
- ext4_dir_dx_entry_get_hash(entries + count_left);
-
- /* Copy data to new node */
- memcpy((void *)new_entries,
- (void *)(entries + count_left),
- count_right *
- sizeof(struct ext4_directory_dx_entry));
-
- /* Initialize new node */
- struct ext4_directory_dx_countlimit *left_countlimit =
- (struct ext4_directory_dx_countlimit *)entries;
- struct ext4_directory_dx_countlimit *right_countlimit =
- (struct ext4_directory_dx_countlimit *)new_entries;
-
- ext4_dir_dx_countlimit_set_count(left_countlimit,
- count_left);
- ext4_dir_dx_countlimit_set_count(right_countlimit,
- count_right);
-
- uint32_t entry_space =
- block_size -
- sizeof(struct ext4_fake_directory_entry);
- uint32_t node_limit =
- entry_space /
- sizeof(struct ext4_directory_dx_entry);
- ext4_dir_dx_countlimit_set_limit(right_countlimit,
- node_limit);
-
- /* Which index block is target for new entry */
- uint32_t position_index =
- (dx_block->position - dx_block->entries);
- if (position_index >= count_left) {
- dx_block->block.dirty = true;
-
- struct ext4_block block_tmp = dx_block->block;
-
- dx_block->block = new_block;
-
- dx_block->position =
- new_entries + position_index - count_left;
- dx_block->entries = new_entries;
-
- new_block = block_tmp;
- }
-
- /* Finally insert new entry */
- ext4_dir_dx_insert_entry(dx_blocks, hash_right,
- new_iblock);
- dx_blocks[0].block.dirty = true;
- dx_blocks[1].block.dirty = true;
-
- new_block.dirty = true;
- return ext4_block_set(inode_ref->fs->bdev, &new_block);
- } else {
- /* Create second level index */
-
- /* Copy data from root to child block */
- memcpy((void *)new_entries, (void *)entries,
- leaf_count *
- sizeof(struct ext4_directory_dx_entry));
-
- struct ext4_directory_dx_countlimit *new_countlimit =
- (struct ext4_directory_dx_countlimit *)new_entries;
-
- uint32_t entry_space =
- block_size -
- sizeof(struct ext4_fake_directory_entry);
- uint32_t node_limit =
- entry_space /
- sizeof(struct ext4_directory_dx_entry);
- ext4_dir_dx_countlimit_set_limit(new_countlimit,
- node_limit);
-
- /* Set values in root node */
- struct ext4_directory_dx_countlimit
- *new_root_countlimit =
- (struct ext4_directory_dx_countlimit *)entries;
-
- ext4_dir_dx_countlimit_set_count(new_root_countlimit,
- 1);
- ext4_dir_dx_entry_set_block(entries, new_iblock);
-
- ((struct ext4_directory_dx_root *)dx_blocks[0]
- .block.data)
- ->info.indirect_levels = 1;
-
- /* Add new entry to the path */
- dx_block = dx_blocks + 1;
- dx_block->position =
- dx_blocks->position - entries + new_entries;
- dx_block->entries = new_entries;
- dx_block->block = new_block;
-
- *new_dx_block = dx_block;
-
- dx_blocks[0].block.dirty = true;
- dx_blocks[1].block.dirty = true;
- }
- }
-
- return EOK;
-}
-
-int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
- struct ext4_inode_ref *child, const char *name)
-{
- int rc2 = EOK;
-
- /* Get direct block 0 (index root) */
- uint32_t root_block_addr;
- int rc =
- ext4_fs_get_inode_data_block_index(parent, 0, &root_block_addr);
- if (rc != EOK)
- return rc;
-
- struct ext4_fs *fs = parent->fs;
- struct ext4_block root_block;
-
- rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
- if (rc != EOK)
- return rc;
-
- /* Initialize hinfo structure (mainly compute hash) */
- uint32_t name_len = strlen(name);
- struct ext4_hash_info hinfo;
- rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
- if (rc != EOK) {
- ext4_block_set(fs->bdev, &root_block);
- return EXT4_ERR_BAD_DX_DIR;
- }
-
- /*
- * Hardcoded number 2 means maximum height of index
- * tree defined in Linux.
- */
- struct ext4_directory_dx_block dx_blocks[2];
- struct ext4_directory_dx_block *dx_block;
- struct ext4_directory_dx_block *dx_it;
-
- rc = ext4_dir_dx_get_leaf(&hinfo, parent, &root_block, &dx_block,
- dx_blocks);
- if (rc != EOK) {
- rc = EXT4_ERR_BAD_DX_DIR;
- goto release_index;
- }
-
- /* Try to insert to existing data block */
- uint32_t leaf_block_idx =
- ext4_dir_dx_entry_get_block(dx_block->position);
- uint32_t leaf_block_addr;
- rc = ext4_fs_get_inode_data_block_index(parent, leaf_block_idx,
- &leaf_block_addr);
- if (rc != EOK)
- goto release_index;
-
- /*
- * Check if there is needed to split index node
- * (and recursively also parent nodes)
- */
- rc = ext4_dir_dx_split_index(parent, dx_blocks, dx_block, &dx_block);
- if (rc != EOK)
- goto release_target_index;
-
- struct ext4_block target_block;
- rc = ext4_block_get(fs->bdev, &target_block, leaf_block_addr);
- if (rc != EOK)
- goto release_index;
-
- /* Check if insert operation passed */
- rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child, name,
- name_len);
- if (rc == EOK)
- goto release_target_index;
-
- /* Split entries to two blocks (includes sorting by hash value) */
- struct ext4_block new_block;
- rc = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_block,
- &new_block);
- if (rc != EOK) {
- rc2 = rc;
- goto release_target_index;
- }
-
- /* Where to save new entry */
- uint32_t new_block_hash =
- ext4_dir_dx_entry_get_hash(dx_block->position + 1);
- if (hinfo.hash >= new_block_hash)
- rc = ext4_dir_try_insert_entry(&fs->sb, &new_block, child, name,
- name_len);
- else
- rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child,
- name, name_len);
-
- /* Cleanup */
- rc = ext4_block_set(fs->bdev, &new_block);
- if (rc != EOK)
- return rc;
-
-/* Cleanup operations */
-
-release_target_index:
- rc2 = rc;
-
- rc = ext4_block_set(fs->bdev, &target_block);
- if (rc != EOK)
- return rc;
-
-release_index:
- if (rc != EOK)
- rc2 = rc;
-
- dx_it = dx_blocks;
-
- while (dx_it <= dx_block) {
- rc = ext4_block_set(fs->bdev, &dx_it->block);
- if (rc != EOK)
- return rc;
-
- dx_it++;
- }
-
- return rc2;
-}
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * 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_dir_idx.c
+ * @brief Directory indexing procedures.
+ */
+
+#include "ext4_config.h"
+#include "ext4_dir_idx.h"
+#include "ext4_dir.h"
+#include "ext4_blockdev.h"
+#include "ext4_fs.h"
+#include "ext4_super.h"
+#include "ext4_hash.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/**@brief Get hash version used in directory index.
+ * @param root_info Pointer to root info structure of index
+ * @return Hash algorithm version
+ */
+static inline uint8_t ext4_dir_dx_root_info_get_hash_version(
+ struct ext4_directory_dx_root_info *root_info)
+{
+ return root_info->hash_version;
+}
+
+/**@brief Set hash version, that will be used in directory index.
+ * @param root_info Pointer to root info structure of index
+ * @param v Hash algorithm version
+ */
+static inline void ext4_dir_dx_root_info_set_hash_version(
+ struct ext4_directory_dx_root_info *root_info, uint8_t v)
+{
+ root_info->hash_version = v;
+}
+
+/**@brief Get length of root_info structure in bytes.
+ * @param root_info Pointer to root info structure of index
+ * @return Length of the structure
+ */
+static inline uint8_t ext4_dir_dx_root_info_get_info_length(
+ struct ext4_directory_dx_root_info *root_info)
+{
+ return root_info->info_length;
+}
+
+/**@brief Set length of root_info structure in bytes.
+ * @param root_info Pointer to root info structure of index
+ * @param info_length Length of the structure
+ */
+static inline void ext4_dir_dx_root_info_set_info_length(
+ struct ext4_directory_dx_root_info *root_info, uint8_t len)
+{
+ root_info->info_length = len;
+}
+
+/**@brief Get number of indirect levels of HTree.
+ * @param root_info Pointer to root info structure of index
+ * @return Height of HTree (actually only 0 or 1)
+ */
+static inline uint8_t ext4_dir_dx_root_info_get_indirect_levels(
+ struct ext4_directory_dx_root_info *root_info)
+{
+ return root_info->indirect_levels;
+}
+
+/**@brief Set number of indirect levels of HTree.
+ * @param root_info Pointer to root info structure of index
+ * @param lvl Height of HTree (actually only 0 or 1)
+ */
+static inline void ext4_dir_dx_root_info_set_indirect_levels(
+ struct ext4_directory_dx_root_info *root_info, uint8_t lvl)
+{
+ root_info->indirect_levels = lvl;
+}
+
+/**@brief Get maximum number of index node entries.
+ * @param climit Pointer to counlimit structure
+ * @return Maximum of entries in node
+ */
+static inline uint16_t
+ext4_dir_dx_countlimit_get_limit(struct ext4_directory_dx_countlimit *climit)
+{
+ return to_le16(climit->limit);
+}
+
+/**@brief Set maximum number of index node entries.
+ * @param climit Pointer to counlimit structure
+ * @param limit Maximum of entries in node
+ */
+static inline void
+ext4_dir_dx_countlimit_set_limit(struct ext4_directory_dx_countlimit *climit,
+ uint16_t limit)
+{
+ climit->limit = to_le16(limit);
+}
+
+/**@brief Get current number of index node entries.
+ * @param climit Pointer to counlimit structure
+ * @return Number of entries in node
+ */
+static inline uint16_t
+ext4_dir_dx_countlimit_get_count(struct ext4_directory_dx_countlimit *climit)
+{
+ return to_le16(climit->count);
+}
+
+/**@brief Set current number of index node entries.
+ * @param climit Pointer to counlimit structure
+ * @param count Number of entries in node
+ */
+static inline void
+ext4_dir_dx_countlimit_set_count(struct ext4_directory_dx_countlimit *climit,
+ uint16_t count)
+{
+ climit->count = to_le16(count);
+}
+
+/**@brief Get hash value of index entry.
+ * @param entry Pointer to index entry
+ * @return Hash value
+ */
+static inline uint32_t
+ext4_dir_dx_entry_get_hash(struct ext4_directory_dx_entry *entry)
+{
+ return to_le32(entry->hash);
+}
+
+/**@brief Set hash value of index entry.
+ * @param entry Pointer to index entry
+ * @param hash Hash value
+ */
+static inline void
+ext4_dir_dx_entry_set_hash(struct ext4_directory_dx_entry *entry, uint32_t hash)
+{
+ entry->hash = to_le32(hash);
+}
+
+/**@brief Get block address where child node is located.
+ * @param entry Pointer to index entry
+ * @return Block address of child node
+ */
+static inline uint32_t
+ext4_dir_dx_entry_get_block(struct ext4_directory_dx_entry *entry)
+{
+ return to_le32(entry->block);
+}
+
+/**@brief Set block address where child node is located.
+ * @param entry Pointer to index entry
+ * @param block Block address of child node
+ */
+static inline void
+ext4_dir_dx_entry_set_block(struct ext4_directory_dx_entry *entry,
+ uint32_t block)
+{
+ entry->block = to_le32(block);
+}
+
+/**@brief Sort entry item.*/
+struct ext4_dx_sort_entry {
+ uint32_t hash;
+ uint32_t rec_len;
+ void *dentry;
+};
+
+static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len,
+ const char *name)
+{
+ return ext2_htree_hash(name, len, hinfo->seed, hinfo->hash_version,
+ &hinfo->hash, &hinfo->minor_hash);
+}
+
+/****************************************************************************/
+
+int ext4_dir_dx_init(struct ext4_inode_ref *dir)
+{
+ /* Load block 0, where will be index root located */
+ uint32_t fblock;
+ int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_block block;
+ rc = ext4_block_get(dir->fs->bdev, &block, fblock);
+ if (rc != EOK)
+ return rc;
+
+ /* Initialize pointers to data structures */
+ struct ext4_directory_dx_root *root = (void *)block.data;
+ struct ext4_directory_dx_root_info *info = &(root->info);
+
+ /* Initialize root info structure */
+ uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version);
+
+ ext4_dir_dx_root_info_set_hash_version(info, hash_version);
+ ext4_dir_dx_root_info_set_indirect_levels(info, 0);
+ ext4_dir_dx_root_info_set_info_length(info, 8);
+
+ /* Set limit and current number of entries */
+ struct ext4_directory_dx_countlimit *countlimit =
+ (struct ext4_directory_dx_countlimit *)&root->entries;
+
+ ext4_dir_dx_countlimit_set_count(countlimit, 1);
+
+ uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb);
+ uint32_t entry_space = block_size -
+ 2 * sizeof(struct ext4_directory_dx_dot_entry) -
+ sizeof(struct ext4_directory_dx_root_info);
+ uint16_t root_limit =
+ entry_space / sizeof(struct ext4_directory_dx_entry);
+ ext4_dir_dx_countlimit_set_limit(countlimit, root_limit);
+
+ /* Append new block, where will be new entries inserted in the future */
+ uint32_t iblock;
+ rc = ext4_fs_append_inode_block(dir, &fblock, &iblock);
+ if (rc != EOK) {
+ ext4_block_set(dir->fs->bdev, &block);
+ return rc;
+ }
+
+ struct ext4_block new_block;
+
+ rc = ext4_block_get(dir->fs->bdev, &new_block, fblock);
+ if (rc != EOK) {
+ ext4_block_set(dir->fs->bdev, &block);
+ return rc;
+ }
+
+ /* Fill the whole block with empty entry */
+ struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;
+
+ ext4_dir_entry_ll_set_entry_length(block_entry, block_size);
+ ext4_dir_entry_ll_set_inode(block_entry, 0);
+
+ new_block.dirty = true;
+ rc = ext4_block_set(dir->fs->bdev, &new_block);
+ if (rc != EOK) {
+ ext4_block_set(dir->fs->bdev, &block);
+ return rc;
+ }
+
+ /* Connect new block to the only entry in index */
+ struct ext4_directory_dx_entry *entry = root->entries;
+ ext4_dir_dx_entry_set_block(entry, iblock);
+
+ block.dirty = true;
+
+ return ext4_block_set(dir->fs->bdev, &block);
+}
+
+/**@brief Initialize hash info structure necessary for index operations.
+ * @param hinfo Pointer to hinfo to be initialized
+ * @param root_block Root block (number 0) of index
+ * @param sb Pointer to superblock
+ * @param name_len Length of name to be computed hash value from
+ * @param name Name to be computed hash value from
+ * @return Standard error code
+ */
+static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo,
+ struct ext4_block *root_block,
+ struct ext4_sblock *sb, size_t name_len,
+ const char *name)
+{
+ struct ext4_directory_dx_root *root =
+ (struct ext4_directory_dx_root *)root_block->data;
+
+ if ((root->info.hash_version != EXT2_HTREE_LEGACY) &&
+ (root->info.hash_version != EXT2_HTREE_HALF_MD4) &&
+ (root->info.hash_version != EXT2_HTREE_TEA))
+ return EXT4_ERR_BAD_DX_DIR;
+
+ /* Check unused flags */
+ if (root->info.unused_flags != 0)
+ return EXT4_ERR_BAD_DX_DIR;
+
+ /* Check indirect levels */
+ if (root->info.indirect_levels > 1)
+ return EXT4_ERR_BAD_DX_DIR;
+
+ /* Check if node limit is correct */
+ uint32_t block_size = ext4_sb_get_block_size(sb);
+ uint32_t entry_space = block_size;
+ entry_space -= 2 * sizeof(struct ext4_directory_dx_dot_entry);
+ entry_space -= sizeof(struct ext4_directory_dx_root_info);
+ entry_space = entry_space / sizeof(struct ext4_directory_dx_entry);
+
+ uint16_t limit = ext4_dir_dx_countlimit_get_limit(
+ (struct ext4_directory_dx_countlimit *)&root->entries);
+ if (limit != entry_space)
+ return EXT4_ERR_BAD_DX_DIR;
+
+ /* Check hash version and modify if necessary */
+ hinfo->hash_version =
+ ext4_dir_dx_root_info_get_hash_version(&root->info);
+ if ((hinfo->hash_version <= EXT2_HTREE_TEA) &&
+ (ext4_sb_check_flag(sb, EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH))) {
+ /* Use unsigned hash */
+ hinfo->hash_version += 3;
+ }
+
+ /* Load hash seed from superblock */
+
+ hinfo->seed = ext4_get8(sb, hash_seed);
+
+ /* Compute hash value of name */
+ if (name)
+ return ext4_dir_dx_hash_string(hinfo, name_len, name);
+
+ return EOK;
+}
+
+/**@brief Walk through index tree and load leaf with corresponding hash value.
+ * @param hinfo Initialized hash info structure
+ * @param inode_ref Current i-node
+ * @param root_block Root block (iblock 0), where is root node located
+ * @param dx_block Pointer to leaf node in dx_blocks array
+ * @param dx_blocks Array with the whole path from root to leaf
+ * @return Standard error code
+ */
+static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo,
+ struct ext4_inode_ref *inode_ref,
+ struct ext4_block *root_block,
+ struct ext4_directory_dx_block **dx_block,
+ struct ext4_directory_dx_block *dx_blocks)
+{
+ struct ext4_directory_dx_block *tmp_dx_block = dx_blocks;
+ struct ext4_directory_dx_root *root =
+ (struct ext4_directory_dx_root *)root_block->data;
+ struct ext4_directory_dx_entry *entries =
+ (struct ext4_directory_dx_entry *)&root->entries;
+
+ uint16_t limit = ext4_dir_dx_countlimit_get_limit(
+ (struct ext4_directory_dx_countlimit *)entries);
+ uint8_t indirect_level =
+ ext4_dir_dx_root_info_get_indirect_levels(&root->info);
+
+ struct ext4_block *tmp_block = root_block;
+ struct ext4_directory_dx_entry *p;
+ struct ext4_directory_dx_entry *q;
+ struct ext4_directory_dx_entry *m;
+ struct ext4_directory_dx_entry *at;
+
+ /* Walk through the index tree */
+ while (true) {
+ uint16_t count = ext4_dir_dx_countlimit_get_count(
+ (struct ext4_directory_dx_countlimit *)entries);
+ if ((count == 0) || (count > limit))
+ return EXT4_ERR_BAD_DX_DIR;
+
+ /* Do binary search in every node */
+ p = entries + 1;
+ q = entries + count - 1;
+
+ while (p <= q) {
+ m = p + (q - p) / 2;
+ if (ext4_dir_dx_entry_get_hash(m) > hinfo->hash)
+ q = m - 1;
+ else
+ p = m + 1;
+ }
+
+ at = p - 1;
+
+ /* Write results */
+
+ memcpy(&tmp_dx_block->block, tmp_block,
+ sizeof(struct ext4_block));
+ tmp_dx_block->entries = entries;
+ tmp_dx_block->position = at;
+
+ /* Is algorithm in the leaf? */
+ if (indirect_level == 0) {
+ *dx_block = tmp_dx_block;
+ return EOK;
+ }
+
+ /* Goto child node */
+ uint32_t next_block = ext4_dir_dx_entry_get_block(at);
+
+ indirect_level--;
+
+ uint32_t fblock;
+ int rc = ext4_fs_get_inode_data_block_index(
+ inode_ref, next_block, &fblock);
+ if (rc != EOK)
+ return rc;
+
+ rc = ext4_block_get(inode_ref->fs->bdev, tmp_block, fblock);
+ if (rc != EOK)
+ return rc;
+
+ entries =
+ ((struct ext4_directory_dx_node *)tmp_block->data)->entries;
+ limit = ext4_dir_dx_countlimit_get_limit(
+ (struct ext4_directory_dx_countlimit *)entries);
+
+ uint16_t entry_space =
+ ext4_sb_get_block_size(&inode_ref->fs->sb) -
+ sizeof(struct ext4_fake_directory_entry);
+
+ entry_space =
+ entry_space / sizeof(struct ext4_directory_dx_entry);
+
+ if (limit != entry_space) {
+ ext4_block_set(inode_ref->fs->bdev, tmp_block);
+ return EXT4_ERR_BAD_DX_DIR;
+ }
+
+ ++tmp_dx_block;
+ }
+
+ /* Unreachable */
+ return EOK;
+}
+
+/**@brief Check if the the next block would be checked during entry search.
+ * @param inode_ref Directory i-node
+ * @param hash Hash value to check
+ * @param dx_block Current block
+ * @param dx_blocks Array with path from root to leaf node
+ * @return Standard Error code
+ */
+static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref,
+ uint32_t hash,
+ struct ext4_directory_dx_block *dx_block,
+ struct ext4_directory_dx_block *dx_blocks)
+{
+ uint32_t num_handles = 0;
+ struct ext4_directory_dx_block *p = dx_block;
+
+ /* Try to find data block with next bunch of entries */
+ while (true) {
+ p->position++;
+ uint16_t count = ext4_dir_dx_countlimit_get_count(
+ (struct ext4_directory_dx_countlimit *)p->entries);
+
+ if (p->position < p->entries + count)
+ break;
+
+ if (p == dx_blocks)
+ return EOK;
+
+ num_handles++;
+ p--;
+ }
+
+ /* Check hash collision (if not occurred - no next block cannot be
+ * used)*/
+ uint32_t current_hash = ext4_dir_dx_entry_get_hash(p->position);
+ if ((hash & 1) == 0) {
+ if ((current_hash & ~1) != hash)
+ return 0;
+ }
+
+ /* Fill new path */
+ while (num_handles--) {
+ uint32_t block_idx = ext4_dir_dx_entry_get_block(p->position);
+ uint32_t block_addr;
+
+ int rc = ext4_fs_get_inode_data_block_index(
+ inode_ref, block_idx, &block_addr);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_block block;
+ rc = ext4_block_get(inode_ref->fs->bdev, &block, block_addr);
+ if (rc != EOK)
+ return rc;
+
+ p++;
+
+ /* Don't forget to put old block (prevent memory leak) */
+ rc = ext4_block_set(inode_ref->fs->bdev, &p->block);
+ if (rc != EOK)
+ return rc;
+
+ memcpy(&p->block, &p->block, sizeof(block));
+ p->entries =
+ ((struct ext4_directory_dx_node *)block.data)->entries;
+ p->position = p->entries;
+ }
+
+ return ENOENT;
+}
+
+int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result,
+ struct ext4_inode_ref *inode_ref, size_t name_len,
+ const char *name)
+{
+ /* Load direct block 0 (index root) */
+ uint32_t root_block_addr;
+ int rc2;
+ int rc =
+ ext4_fs_get_inode_data_block_index(inode_ref, 0, &root_block_addr);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_fs *fs = inode_ref->fs;
+
+ struct ext4_block root_block;
+ rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
+ if (rc != EOK)
+ return rc;
+
+ /* Initialize hash info (compute hash value) */
+ struct ext4_hash_info hinfo;
+ rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
+ if (rc != EOK) {
+ ext4_block_set(fs->bdev, &root_block);
+ return EXT4_ERR_BAD_DX_DIR;
+ }
+
+ /*
+ * Hardcoded number 2 means maximum height of index tree,
+ * specified in the Linux driver.
+ */
+ struct ext4_directory_dx_block dx_blocks[2];
+ struct ext4_directory_dx_block *dx_block;
+ struct ext4_directory_dx_block *tmp;
+
+ rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block, &dx_block,
+ dx_blocks);
+ if (rc != EOK) {
+ ext4_block_set(fs->bdev, &root_block);
+ return EXT4_ERR_BAD_DX_DIR;
+ }
+
+ do {
+ /* Load leaf block */
+ uint32_t leaf_block_idx =
+ ext4_dir_dx_entry_get_block(dx_block->position);
+ uint32_t leaf_block_addr;
+
+ rc = ext4_fs_get_inode_data_block_index(
+ inode_ref, leaf_block_idx, &leaf_block_addr);
+ if (rc != EOK)
+ goto cleanup;
+
+ struct ext4_block leaf_block;
+ rc = ext4_block_get(fs->bdev, &leaf_block, leaf_block_addr);
+ if (rc != EOK)
+ goto cleanup;
+
+ /* Linear search inside block */
+ struct ext4_directory_entry_ll *res_dentry;
+ rc = ext4_dir_find_in_block(&leaf_block, &fs->sb, name_len,
+ name, &res_dentry);
+
+ /* Found => return it */
+ if (rc == EOK) {
+ result->block = leaf_block;
+ result->dentry = res_dentry;
+ goto cleanup;
+ }
+
+ /* Not found, leave untouched */
+ rc2 = ext4_block_set(fs->bdev, &leaf_block);
+ if (rc2 != EOK)
+ goto cleanup;
+
+ if (rc != ENOENT)
+ goto cleanup;
+
+ /* check if the next block could be checked */
+ rc = ext4_dir_dx_next_block(inode_ref, hinfo.hash, dx_block,
+ &dx_blocks[0]);
+ if (rc < 0)
+ goto cleanup;
+ } while (rc == ENOENT);
+
+ /* Entry not found */
+ rc = ENOENT;
+
+cleanup:
+ /* The whole path must be released (preventing memory leak) */
+ tmp = dx_blocks;
+
+ while (tmp <= dx_block) {
+ rc2 = ext4_block_set(fs->bdev, &tmp->block);
+ if (rc == EOK && rc2 != EOK)
+ rc = rc2;
+ ++tmp;
+ }
+
+ return rc;
+}
+
+#if CONFIG_DIR_INDEX_COMB_SORT
+#define SWAP_ENTRY(se1, se2) \
+ do { \
+ struct ext4_dx_sort_entry tmp = se1; \
+ se1 = se2; \
+ se2 = tmp; \
+ \
+} while (0)
+
+static void comb_sort(struct ext4_dx_sort_entry *se, uint32_t count)
+{
+ struct ext4_dx_sort_entry *p, *q, *top = se + count - 1;
+ bool more;
+ /* Combsort */
+ while (count > 2) {
+ count = (count * 10) / 13;
+ if (count - 9 < 2)
+ count = 11;
+ for (p = top, q = p - count; q >= se; p--, q--)
+ if (p->hash < q->hash)
+ SWAP_ENTRY(*p, *q);
+ }
+ /* Bubblesort */
+ do {
+ more = 0;
+ q = top;
+ while (q-- > se) {
+ if (q[1].hash >= q[0].hash)
+ continue;
+ SWAP_ENTRY(*(q + 1), *q);
+ more = 1;
+ }
+ } while (more);
+}
+#else
+
+/**@brief Compare function used to pass in quicksort implementation.
+ * It can compare two entries by hash value.
+ * @param arg1 First entry
+ * @param arg2 Second entry
+ * @param dummy Unused parameter, can be NULL
+ *
+ * @return Classic compare result
+ * (0: equal, -1: arg1 < arg2, 1: arg1 > arg2)
+ */
+static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2)
+{
+ struct ext4_dx_sort_entry *entry1 = (void *)arg1;
+ struct ext4_dx_sort_entry *entry2 = (void *)arg2;
+
+ if (entry1->hash == entry2->hash)
+ return 0;
+
+ if (entry1->hash < entry2->hash)
+ return -1;
+ else
+ return 1;
+}
+#endif
+
+/**@brief Insert new index entry to block.
+ * Note that space for new entry must be checked by caller.
+ * @param index_block Block where to insert new entry
+ * @param hash Hash value covered by child node
+ * @param iblock Logical number of child block
+ *
+ */
+static void
+ext4_dir_dx_insert_entry(struct ext4_directory_dx_block *index_block,
+ uint32_t hash, uint32_t iblock)
+{
+ struct ext4_directory_dx_entry *old_index_entry = index_block->position;
+ struct ext4_directory_dx_entry *new_index_entry = old_index_entry + 1;
+
+ struct ext4_directory_dx_countlimit *countlimit =
+ (struct ext4_directory_dx_countlimit *)index_block->entries;
+ uint32_t count = ext4_dir_dx_countlimit_get_count(countlimit);
+
+ struct ext4_directory_dx_entry *start_index = index_block->entries;
+ size_t bytes =
+ (uint8_t *)(start_index + count) - (uint8_t *)(new_index_entry);
+
+ memmove(new_index_entry + 1, new_index_entry, bytes);
+
+ ext4_dir_dx_entry_set_block(new_index_entry, iblock);
+ ext4_dir_dx_entry_set_hash(new_index_entry, hash);
+
+ ext4_dir_dx_countlimit_set_count(countlimit, count + 1);
+
+ index_block->block.dirty = true;
+}
+
+/**@brief Split directory entries to two parts preventing node overflow.
+ * @param inode_ref Directory i-node
+ * @param hinfo Hash info
+ * @param old_data_block Block with data to be split
+ * @param index_block Block where index entries are located
+ * @param new_data_block Output value for newly allocated data block
+ */
+static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref,
+ struct ext4_hash_info *hinfo,
+ struct ext4_block *old_data_block,
+ struct ext4_directory_dx_block *index_block,
+ struct ext4_block *new_data_block)
+{
+ int rc = EOK;
+
+ /* Allocate buffer for directory entries */
+ uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
+
+ uint8_t *entry_buffer = malloc(block_size);
+ if (entry_buffer == NULL)
+ return ENOMEM;
+
+ /* dot entry has the smallest size available */
+ uint32_t max_entry_count =
+ block_size / sizeof(struct ext4_directory_dx_dot_entry);
+
+ /* Allocate sort entry */
+ struct ext4_dx_sort_entry *sort_array =
+ malloc(max_entry_count * sizeof(struct ext4_dx_sort_entry));
+
+ if (sort_array == NULL) {
+ free(entry_buffer);
+ return ENOMEM;
+ }
+
+ uint32_t idx = 0;
+ uint32_t real_size = 0;
+
+ /* Initialize hinfo */
+ struct ext4_hash_info tmp_hinfo;
+ memcpy(&tmp_hinfo, hinfo, sizeof(struct ext4_hash_info));
+
+ /* Load all valid entries to the buffer */
+ struct ext4_directory_entry_ll *dentry = (void *)old_data_block->data;
+ uint8_t *entry_buffer_ptr = entry_buffer;
+ while ((void *)dentry < (void *)(old_data_block->data + block_size)) {
+ /* Read only valid entries */
+ if (ext4_dir_entry_ll_get_inode(dentry) &&
+ dentry->name_length) {
+ uint8_t len = ext4_dir_entry_ll_get_name_length(
+ &inode_ref->fs->sb, dentry);
+
+ rc = ext4_dir_dx_hash_string(&tmp_hinfo, len,
+ (char *)dentry->name);
+ if (rc != EOK) {
+ free(sort_array);
+ free(entry_buffer);
+ return rc;
+ }
+
+ uint32_t rec_len = 8 + len;
+
+ if ((rec_len % 4) != 0)
+ rec_len += 4 - (rec_len % 4);
+
+ memcpy(entry_buffer_ptr, dentry, rec_len);
+
+ sort_array[idx].dentry = entry_buffer_ptr;
+ sort_array[idx].rec_len = rec_len;
+ sort_array[idx].hash = tmp_hinfo.hash;
+
+ entry_buffer_ptr += rec_len;
+ real_size += rec_len;
+ idx++;
+ }
+
+ dentry = (void *)((uint8_t *)dentry +
+ ext4_dir_entry_ll_get_entry_length(dentry));
+ }
+
+/* Sort all entries */
+#if CONFIG_DIR_INDEX_COMB_SORT
+ comb_sort(sort_array, idx);
+#else
+ qsort(sort_array, idx, sizeof(struct ext4_dx_sort_entry),
+ ext4_dir_dx_entry_comparator);
+#endif
+ /* Allocate new block for store the second part of entries */
+ uint32_t new_fblock;
+ uint32_t new_iblock;
+ rc = ext4_fs_append_inode_block(inode_ref, &new_fblock, &new_iblock);
+ if (rc != EOK) {
+ free(sort_array);
+ free(entry_buffer);
+ return rc;
+ }
+
+ /* Load new block */
+ struct ext4_block new_data_block_tmp;
+ rc = ext4_block_get(inode_ref->fs->bdev, &new_data_block_tmp,
+ new_fblock);
+ if (rc != EOK) {
+ free(sort_array);
+ free(entry_buffer);
+ return rc;
+ }
+
+ /*
+ * Distribute entries to two blocks (by size)
+ * - compute the half
+ */
+ uint32_t new_hash = 0;
+ uint32_t current_size = 0;
+ uint32_t mid = 0;
+ uint32_t i;
+ for (i = 0; i < idx; ++i) {
+ if ((current_size + sort_array[i].rec_len) > (block_size / 2)) {
+ new_hash = sort_array[i].hash;
+ mid = i;
+ break;
+ }
+
+ current_size += sort_array[i].rec_len;
+ }
+
+ /* Check hash collision */
+ uint32_t continued = 0;
+ if (new_hash == sort_array[mid - 1].hash)
+ continued = 1;
+
+ uint32_t offset = 0;
+ void *ptr;
+
+ /* First part - to the old block */
+ for (i = 0; i < mid; ++i) {
+ ptr = old_data_block->data + offset;
+ memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
+
+ struct ext4_directory_entry_ll *tmp = ptr;
+ if (i < (mid - 1))
+ ext4_dir_entry_ll_set_entry_length(
+ tmp, sort_array[i].rec_len);
+ else
+ ext4_dir_entry_ll_set_entry_length(tmp,
+ block_size - offset);
+
+ offset += sort_array[i].rec_len;
+ }
+
+ /* Second part - to the new block */
+ offset = 0;
+ for (i = mid; i < idx; ++i) {
+ ptr = new_data_block_tmp.data + offset;
+ memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
+
+ struct ext4_directory_entry_ll *tmp = ptr;
+ if (i < (idx - 1))
+ ext4_dir_entry_ll_set_entry_length(
+ tmp, sort_array[i].rec_len);
+ else
+ ext4_dir_entry_ll_set_entry_length(tmp,
+ block_size - offset);
+
+ offset += sort_array[i].rec_len;
+ }
+
+ /* Do some steps to finish operation */
+ old_data_block->dirty = true;
+ new_data_block_tmp.dirty = true;
+
+ free(sort_array);
+ free(entry_buffer);
+
+ ext4_dir_dx_insert_entry(index_block, new_hash + continued, new_iblock);
+
+ *new_data_block = new_data_block_tmp;
+
+ return EOK;
+}
+
+/**@brief Split index node and maybe some parent nodes in the tree hierarchy.
+ * @param inode_ref Directory i-node
+ * @param dx_blocks Array with path from root to leaf node
+ * @param dx_block Leaf block to be split if needed
+ * @return Error code
+ */
+static int
+ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref,
+ struct ext4_directory_dx_block *dx_blocks,
+ struct ext4_directory_dx_block *dx_block,
+ struct ext4_directory_dx_block **new_dx_block)
+{
+ struct ext4_directory_dx_entry *entries;
+
+ if (dx_block == dx_blocks)
+ entries =
+ ((struct ext4_directory_dx_root *)dx_block->block.data)
+ ->entries;
+ else
+ entries =
+ ((struct ext4_directory_dx_node *)dx_block->block.data)
+ ->entries;
+
+ struct ext4_directory_dx_countlimit *countlimit =
+ (struct ext4_directory_dx_countlimit *)entries;
+
+ uint16_t leaf_limit = ext4_dir_dx_countlimit_get_limit(countlimit);
+ uint16_t leaf_count = ext4_dir_dx_countlimit_get_count(countlimit);
+
+ /* Check if is necessary to split index block */
+ if (leaf_limit == leaf_count) {
+ size_t levels = dx_block - dx_blocks;
+
+ struct ext4_directory_dx_entry *root_entries =
+ ((struct ext4_directory_dx_root *)dx_blocks[0].block.data)
+ ->entries;
+
+ struct ext4_directory_dx_countlimit *root_countlimit =
+ (struct ext4_directory_dx_countlimit *)root_entries;
+ uint16_t root_limit =
+ ext4_dir_dx_countlimit_get_limit(root_countlimit);
+ uint16_t root_count =
+ ext4_dir_dx_countlimit_get_count(root_countlimit);
+
+ /* Linux limitation */
+ if ((levels > 0) && (root_limit == root_count))
+ return ENOSPC;
+
+ /* Add new block to directory */
+ uint32_t new_fblock;
+ uint32_t new_iblock;
+ int rc = ext4_fs_append_inode_block(inode_ref, &new_fblock,
+ &new_iblock);
+ if (rc != EOK)
+ return rc;
+
+ /* load new block */
+ struct ext4_block new_block;
+ rc =
+ ext4_block_get(inode_ref->fs->bdev, &new_block, new_fblock);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_directory_dx_node *new_node =
+ (void *)new_block.data;
+ struct ext4_directory_dx_entry *new_entries = new_node->entries;
+
+ memset(&new_node->fake, 0,
+ sizeof(struct ext4_fake_directory_entry));
+
+ uint32_t block_size =
+ ext4_sb_get_block_size(&inode_ref->fs->sb);
+
+ new_node->fake.entry_length = block_size;
+
+ /* Split leaf node */
+ if (levels > 0) {
+ uint32_t count_left = leaf_count / 2;
+ uint32_t count_right = leaf_count - count_left;
+ uint32_t hash_right =
+ ext4_dir_dx_entry_get_hash(entries + count_left);
+
+ /* Copy data to new node */
+ memcpy((void *)new_entries,
+ (void *)(entries + count_left),
+ count_right *
+ sizeof(struct ext4_directory_dx_entry));
+
+ /* Initialize new node */
+ struct ext4_directory_dx_countlimit *left_countlimit =
+ (struct ext4_directory_dx_countlimit *)entries;
+ struct ext4_directory_dx_countlimit *right_countlimit =
+ (struct ext4_directory_dx_countlimit *)new_entries;
+
+ ext4_dir_dx_countlimit_set_count(left_countlimit,
+ count_left);
+ ext4_dir_dx_countlimit_set_count(right_countlimit,
+ count_right);
+
+ uint32_t entry_space =
+ block_size -
+ sizeof(struct ext4_fake_directory_entry);
+ uint32_t node_limit =
+ entry_space /
+ sizeof(struct ext4_directory_dx_entry);
+ ext4_dir_dx_countlimit_set_limit(right_countlimit,
+ node_limit);
+
+ /* Which index block is target for new entry */
+ uint32_t position_index =
+ (dx_block->position - dx_block->entries);
+ if (position_index >= count_left) {
+ dx_block->block.dirty = true;
+
+ struct ext4_block block_tmp = dx_block->block;
+
+ dx_block->block = new_block;
+
+ dx_block->position =
+ new_entries + position_index - count_left;
+ dx_block->entries = new_entries;
+
+ new_block = block_tmp;
+ }
+
+ /* Finally insert new entry */
+ ext4_dir_dx_insert_entry(dx_blocks, hash_right,
+ new_iblock);
+ dx_blocks[0].block.dirty = true;
+ dx_blocks[1].block.dirty = true;
+
+ new_block.dirty = true;
+ return ext4_block_set(inode_ref->fs->bdev, &new_block);
+ } else {
+ /* Create second level index */
+
+ /* Copy data from root to child block */
+ memcpy((void *)new_entries, (void *)entries,
+ leaf_count *
+ sizeof(struct ext4_directory_dx_entry));
+
+ struct ext4_directory_dx_countlimit *new_countlimit =
+ (struct ext4_directory_dx_countlimit *)new_entries;
+
+ uint32_t entry_space =
+ block_size -
+ sizeof(struct ext4_fake_directory_entry);
+ uint32_t node_limit =
+ entry_space /
+ sizeof(struct ext4_directory_dx_entry);
+ ext4_dir_dx_countlimit_set_limit(new_countlimit,
+ node_limit);
+
+ /* Set values in root node */
+ struct ext4_directory_dx_countlimit
+ *new_root_countlimit =
+ (struct ext4_directory_dx_countlimit *)entries;
+
+ ext4_dir_dx_countlimit_set_count(new_root_countlimit,
+ 1);
+ ext4_dir_dx_entry_set_block(entries, new_iblock);
+
+ ((struct ext4_directory_dx_root *)dx_blocks[0]
+ .block.data)
+ ->info.indirect_levels = 1;
+
+ /* Add new entry to the path */
+ dx_block = dx_blocks + 1;
+ dx_block->position =
+ dx_blocks->position - entries + new_entries;
+ dx_block->entries = new_entries;
+ dx_block->block = new_block;
+
+ *new_dx_block = dx_block;
+
+ dx_blocks[0].block.dirty = true;
+ dx_blocks[1].block.dirty = true;
+ }
+ }
+
+ return EOK;
+}
+
+int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
+ struct ext4_inode_ref *child, const char *name)
+{
+ int rc2 = EOK;
+
+ /* Get direct block 0 (index root) */
+ uint32_t root_block_addr;
+ int rc =
+ ext4_fs_get_inode_data_block_index(parent, 0, &root_block_addr);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_fs *fs = parent->fs;
+ struct ext4_block root_block;
+
+ rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
+ if (rc != EOK)
+ return rc;
+
+ /* Initialize hinfo structure (mainly compute hash) */
+ uint32_t name_len = strlen(name);
+ struct ext4_hash_info hinfo;
+ rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
+ if (rc != EOK) {
+ ext4_block_set(fs->bdev, &root_block);
+ return EXT4_ERR_BAD_DX_DIR;
+ }
+
+ /*
+ * Hardcoded number 2 means maximum height of index
+ * tree defined in Linux.
+ */
+ struct ext4_directory_dx_block dx_blocks[2];
+ struct ext4_directory_dx_block *dx_block;
+ struct ext4_directory_dx_block *dx_it;
+
+ rc = ext4_dir_dx_get_leaf(&hinfo, parent, &root_block, &dx_block,
+ dx_blocks);
+ if (rc != EOK) {
+ rc = EXT4_ERR_BAD_DX_DIR;
+ goto release_index;
+ }
+
+ /* Try to insert to existing data block */
+ uint32_t leaf_block_idx =
+ ext4_dir_dx_entry_get_block(dx_block->position);
+ uint32_t leaf_block_addr;
+ rc = ext4_fs_get_inode_data_block_index(parent, leaf_block_idx,
+ &leaf_block_addr);
+ if (rc != EOK)
+ goto release_index;
+
+ /*
+ * Check if there is needed to split index node
+ * (and recursively also parent nodes)
+ */
+ rc = ext4_dir_dx_split_index(parent, dx_blocks, dx_block, &dx_block);
+ if (rc != EOK)
+ goto release_target_index;
+
+ struct ext4_block target_block;
+ rc = ext4_block_get(fs->bdev, &target_block, leaf_block_addr);
+ if (rc != EOK)
+ goto release_index;
+
+ /* Check if insert operation passed */
+ rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child, name,
+ name_len);
+ if (rc == EOK)
+ goto release_target_index;
+
+ /* Split entries to two blocks (includes sorting by hash value) */
+ struct ext4_block new_block;
+ rc = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_block,
+ &new_block);
+ if (rc != EOK) {
+ rc2 = rc;
+ goto release_target_index;
+ }
+
+ /* Where to save new entry */
+ uint32_t new_block_hash =
+ ext4_dir_dx_entry_get_hash(dx_block->position + 1);
+ if (hinfo.hash >= new_block_hash)
+ rc = ext4_dir_try_insert_entry(&fs->sb, &new_block, child, name,
+ name_len);
+ else
+ rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child,
+ name, name_len);
+
+ /* Cleanup */
+ rc = ext4_block_set(fs->bdev, &new_block);
+ if (rc != EOK)
+ return rc;
+
+/* Cleanup operations */
+
+release_target_index:
+ rc2 = rc;
+
+ rc = ext4_block_set(fs->bdev, &target_block);
+ if (rc != EOK)
+ return rc;
+
+release_index:
+ if (rc != EOK)
+ rc2 = rc;
+
+ dx_it = dx_blocks;
+
+ while (dx_it <= dx_block) {
+ rc = ext4_block_set(fs->bdev, &dx_it->block);
+ if (rc != EOK)
+ return rc;
+
+ dx_it++;
+ }
+
+ return rc2;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_dir_idx.h
+++ b/lwext4/ext4_dir_idx.h
@@ -1,82 +1,82 @@
-/*
- * 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_dir_idx.h
- * @brief Directory indexing procedures.
- */
-
-#ifndef EXT4_DIR_IDX_H_
-#define EXT4_DIR_IDX_H_
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/**@brief Initialize index structure of new directory.
- * @param dir Pointer to directory i-node
- * @return Error code
- */
-int ext4_dir_dx_init(struct ext4_inode_ref *dir);
-
-/**@brief Try to find directory entry using directory index.
- * @param result Output value - if entry will be found,
- * than will be passed through this parameter
- * @param inode_ref Directory i-node
- * @param name_len Length of name to be found
- * @param name Name to be found
- * @return Error code
- */
-int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result,
- struct ext4_inode_ref *inode_ref, size_t name_len,
- const char *name);
-
-/**@brief Add new entry to indexed directory
- * @param parent Directory i-node
- * @param child I-node to be referenced from directory entry
- * @param name Name of new directory entry
- * @return Error code
- */
-int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
- struct ext4_inode_ref *child, const char *name);
-
-#endif /* EXT4_DIR_IDX_H_ */
-
-/**
- * @}
- */
+/*
+ * 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_dir_idx.h
+ * @brief Directory indexing procedures.
+ */
+
+#ifndef EXT4_DIR_IDX_H_
+#define EXT4_DIR_IDX_H_
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**@brief Initialize index structure of new directory.
+ * @param dir Pointer to directory i-node
+ * @return Error code
+ */
+int ext4_dir_dx_init(struct ext4_inode_ref *dir);
+
+/**@brief Try to find directory entry using directory index.
+ * @param result Output value - if entry will be found,
+ * than will be passed through this parameter
+ * @param inode_ref Directory i-node
+ * @param name_len Length of name to be found
+ * @param name Name to be found
+ * @return Error code
+ */
+int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result,
+ struct ext4_inode_ref *inode_ref, size_t name_len,
+ const char *name);
+
+/**@brief Add new entry to indexed directory
+ * @param parent Directory i-node
+ * @param child I-node to be referenced from directory entry
+ * @param name Name of new directory entry
+ * @return Error code
+ */
+int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
+ struct ext4_inode_ref *child, const char *name);
+
+#endif /* EXT4_DIR_IDX_H_ */
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_extent.c
+++ b/lwext4/ext4_extent.c
@@ -1,984 +1,984 @@
-/*
- * 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)
-{
- int rc;
- /* 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) {
- rc = ext4_block_set(inode_ref->fs->bdev, &block);
- if (rc != EOK)
- return rc;
- }
-
- 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) {
- rc = ext4_block_set(inode_ref->fs->bdev, &block);
- if (rc != EOK)
- return rc;
- }
-
- 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) {
- int r = ext4_block_set(inode_ref->fs->bdev,
- &tmp_path[i].block);
- if (r != EOK)
- rc = r;
- }
- }
-
- /* 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;
-
- return ext4_balloc_free_block(inode_ref, fblock);
-}
-
-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;
- }
-
- if (!entries)
- ext4_extent_header_set_depth(path->header, 0);
-
-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) {
- int r =
- ext4_block_set(inode_ref->fs->bdev, &path[i].block);
- if (r != EOK)
- rc = r;
- }
- }
-
- /* 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 */
- rc = ext4_block_set(inode_ref->fs->bdev,
- &path_ptr->block);
- if (rc != EOK) {
- ext4_balloc_free_block(inode_ref, fblock);
- return rc;
- }
-
- /* 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 preceding 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) {
- int r =
- ext4_block_set(inode_ref->fs->bdev, &path[i].block);
- if (r != EOK)
- rc = r;
- }
- }
-
- /* Destroy temporary data structure */
- free(path);
-
- return rc;
-}
-
-/**
- * @}
- */
+/*
+ * 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)
+{
+ int rc;
+ /* 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) {
+ rc = ext4_block_set(inode_ref->fs->bdev, &block);
+ if (rc != EOK)
+ return rc;
+ }
+
+ 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) {
+ rc = ext4_block_set(inode_ref->fs->bdev, &block);
+ if (rc != EOK)
+ return rc;
+ }
+
+ 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) {
+ int r = ext4_block_set(inode_ref->fs->bdev,
+ &tmp_path[i].block);
+ if (r != EOK)
+ rc = r;
+ }
+ }
+
+ /* 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;
+
+ return ext4_balloc_free_block(inode_ref, fblock);
+}
+
+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;
+ }
+
+ if (!entries)
+ ext4_extent_header_set_depth(path->header, 0);
+
+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) {
+ int r =
+ ext4_block_set(inode_ref->fs->bdev, &path[i].block);
+ if (r != EOK)
+ rc = r;
+ }
+ }
+
+ /* 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 */
+ rc = ext4_block_set(inode_ref->fs->bdev,
+ &path_ptr->block);
+ if (rc != EOK) {
+ ext4_balloc_free_block(inode_ref, fblock);
+ return rc;
+ }
+
+ /* 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 preceding 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) {
+ int r =
+ ext4_block_set(inode_ref->fs->bdev, &path[i].block);
+ if (r != EOK)
+ rc = r;
+ }
+ }
+
+ /* Destroy temporary data structure */
+ free(path);
+
+ return rc;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_extent.h
+++ b/lwext4/ext4_extent.h
@@ -1,187 +1,187 @@
-/*
- * 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_ */
- /**
- * @}
- */
+/*
+ * 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_hash.c
+++ b/lwext4/ext4_hash.c
@@ -1,322 +1,322 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- *
- * FreeBSD:
- * Copyright (c) 2010, 2013 Zheng Liu <lz@freebsd.org>
- * Copyright (c) 2012, Vyacheslav Matyushin
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
- *
- */
-
-/*
- * The following notice applies to the code in ext2_half_md4():
- *
- * Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
- *
- * License to copy and use this software is granted provided that it
- * is identified as the "RSA Data Security, Inc. MD4 Message-Digest
- * Algorithm" in all material mentioning or referencing this software
- * or this function.
- *
- * License is also granted to make and use derivative works provided
- * that such works are identified as "derived from the RSA Data
- * Security, Inc. MD4 Message-Digest Algorithm" in all material
- * mentioning or referencing the derived work.
- *
- * RSA Data Security, Inc. makes no representations concerning either
- * the merchantability of this software or the suitability of this
- * software for any particular purpose. It is provided "as is"
- * without express or implied warranty of any kind.
- *
- * These notices must be retained in any copies of any part of this
- * documentation and/or software.
- */
-
-/** @addtogroup lwext4
- * @{
- */
-/**
- * @file ext4_hash.c
- * @brief Directory indexing hash functions.
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_errno.h"
-
-#include <string.h>
-
-/* F, G, and H are MD4 functions */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-
-/* ROTATE_LEFT rotates x left n bits */
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
-
-/*
- * FF, GG, and HH are transformations for rounds 1, 2, and 3.
- * Rotation is separated from addition to prevent recomputation.
- */
-#define FF(a, b, c, d, x, s) \
- { \
- (a) += F((b), (c), (d)) + (x); \
- (a) = ROTATE_LEFT((a), (s)); \
- \
-}
-
-#define GG(a, b, c, d, x, s) \
- { \
- (a) += G((b), (c), (d)) + (x) + (uint32_t)0x5A827999; \
- (a) = ROTATE_LEFT((a), (s)); \
- \
-}
-
-#define HH(a, b, c, d, x, s) \
- { \
- (a) += H((b), (c), (d)) + (x) + (uint32_t)0x6ED9EBA1; \
- (a) = ROTATE_LEFT((a), (s)); \
- \
-}
-
-/*
- * MD4 basic transformation. It transforms state based on block.
- *
- * This is a half md4 algorithm since Linux uses this algorithm for dir
- * index. This function is derived from the RSA Data Security, Inc. MD4
- * Message-Digest Algorithm and was modified as necessary.
- *
- * The return value of this function is uint32_t in Linux, but actually we don't
- * need to check this value, so in our version this function doesn't return any
- * value.
- */
-static void ext2_half_md4(uint32_t hash[4], uint32_t data[8])
-{
- uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3];
-
- /* Round 1 */
- FF(a, b, c, d, data[0], 3);
- FF(d, a, b, c, data[1], 7);
- FF(c, d, a, b, data[2], 11);
- FF(b, c, d, a, data[3], 19);
- FF(a, b, c, d, data[4], 3);
- FF(d, a, b, c, data[5], 7);
- FF(c, d, a, b, data[6], 11);
- FF(b, c, d, a, data[7], 19);
-
- /* Round 2 */
- GG(a, b, c, d, data[1], 3);
- GG(d, a, b, c, data[3], 5);
- GG(c, d, a, b, data[5], 9);
- GG(b, c, d, a, data[7], 13);
- GG(a, b, c, d, data[0], 3);
- GG(d, a, b, c, data[2], 5);
- GG(c, d, a, b, data[4], 9);
- GG(b, c, d, a, data[6], 13);
-
- /* Round 3 */
- HH(a, b, c, d, data[3], 3);
- HH(d, a, b, c, data[7], 9);
- HH(c, d, a, b, data[2], 11);
- HH(b, c, d, a, data[6], 15);
- HH(a, b, c, d, data[1], 3);
- HH(d, a, b, c, data[5], 9);
- HH(c, d, a, b, data[0], 11);
- HH(b, c, d, a, data[4], 15);
-
- hash[0] += a;
- hash[1] += b;
- hash[2] += c;
- hash[3] += d;
-}
-
-/*
- * Tiny Encryption Algorithm.
- */
-static void ext2_tea(uint32_t hash[4], uint32_t data[8])
-{
- uint32_t tea_delta = 0x9E3779B9;
- uint32_t sum;
- uint32_t x = hash[0], y = hash[1];
- int n = 16;
- int i = 1;
-
- while (n-- > 0) {
- sum = i * tea_delta;
- x += ((y << 4) + data[0]) ^ (y + sum) ^ ((y >> 5) + data[1]);
- y += ((x << 4) + data[2]) ^ (x + sum) ^ ((x >> 5) + data[3]);
- i++;
- }
-
- hash[0] += x;
- hash[1] += y;
-}
-
-static uint32_t ext2_legacy_hash(const char *name, int len, int unsigned_char)
-{
- uint32_t h0, h1 = 0x12A3FE2D, h2 = 0x37ABE8F9;
- uint32_t multi = 0x6D22F5;
- const unsigned char *uname = (const unsigned char *)name;
- const signed char *sname = (const signed char *)name;
- int val, i;
-
- for (i = 0; i < len; i++) {
- if (unsigned_char)
- val = (unsigned int)*uname++;
- else
- val = (int)*sname++;
-
- h0 = h2 + (h1 ^ (val * multi));
- if (h0 & 0x80000000)
- h0 -= 0x7FFFFFFF;
- h2 = h1;
- h1 = h0;
- }
-
- return (h1 << 1);
-}
-
-static void ext2_prep_hashbuf(const char *src, uint32_t slen, uint32_t *dst,
- int dlen, int unsigned_char)
-{
- uint32_t padding = slen | (slen << 8) | (slen << 16) | (slen << 24);
- uint32_t buf_val;
- int len, i;
- int buf_byte;
- const unsigned char *ubuf = (const unsigned char *)src;
- const signed char *sbuf = (const signed char *)src;
-
- if (slen > (uint32_t)dlen)
- len = dlen;
- else
- len = slen;
-
- buf_val = padding;
-
- for (i = 0; i < len; i++) {
- if (unsigned_char)
- buf_byte = (unsigned int)ubuf[i];
- else
- buf_byte = (int)sbuf[i];
-
- if ((i % 4) == 0)
- buf_val = padding;
-
- buf_val <<= 8;
- buf_val += buf_byte;
-
- if ((i % 4) == 3) {
- *dst++ = buf_val;
- dlen -= sizeof(uint32_t);
- buf_val = padding;
- }
- }
-
- dlen -= sizeof(uint32_t);
- if (dlen >= 0)
- *dst++ = buf_val;
-
- dlen -= sizeof(uint32_t);
- while (dlen >= 0) {
- *dst++ = padding;
- dlen -= sizeof(uint32_t);
- }
-}
-
-int ext2_htree_hash(const char *name, int len, const uint32_t *hash_seed,
- int hash_version, uint32_t *hash_major,
- uint32_t *hash_minor)
-{
- uint32_t hash[4];
- uint32_t data[8];
- uint32_t major = 0, minor = 0;
- int unsigned_char = 0;
-
- if (!name || !hash_major)
- return (-1);
-
- if (len < 1 || len > 255)
- goto error;
-
- hash[0] = 0x67452301;
- hash[1] = 0xEFCDAB89;
- hash[2] = 0x98BADCFE;
- hash[3] = 0x10325476;
-
- if (hash_seed)
- memcpy(hash, hash_seed, sizeof(hash));
-
- switch (hash_version) {
- case EXT2_HTREE_TEA_UNSIGNED:
- unsigned_char = 1;
- case EXT2_HTREE_TEA:
- while (len > 0) {
- ext2_prep_hashbuf(name, len, data, 16, unsigned_char);
- ext2_tea(hash, data);
- len -= 16;
- name += 16;
- }
- major = hash[0];
- minor = hash[1];
- break;
- case EXT2_HTREE_LEGACY_UNSIGNED:
- unsigned_char = 1;
- case EXT2_HTREE_LEGACY:
- major = ext2_legacy_hash(name, len, unsigned_char);
- break;
- case EXT2_HTREE_HALF_MD4_UNSIGNED:
- unsigned_char = 1;
- case EXT2_HTREE_HALF_MD4:
- while (len > 0) {
- ext2_prep_hashbuf(name, len, data, 32, unsigned_char);
- ext2_half_md4(hash, data);
- len -= 32;
- name += 32;
- }
- major = hash[1];
- minor = hash[2];
- break;
- default:
- goto error;
- }
-
- major &= ~1;
- if (major == (EXT2_HTREE_EOF << 1))
- major = (EXT2_HTREE_EOF - 1) << 1;
- *hash_major = major;
- if (hash_minor)
- *hash_minor = minor;
-
- return EOK;
-
-error:
- *hash_major = 0;
- if (hash_minor)
- *hash_minor = 0;
- return ENOTSUP;
-}
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ *
+ * FreeBSD:
+ * Copyright (c) 2010, 2013 Zheng Liu <lz@freebsd.org>
+ * Copyright (c) 2012, Vyacheslav Matyushin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ */
+
+/*
+ * The following notice applies to the code in ext2_half_md4():
+ *
+ * Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD4 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+/** @addtogroup lwext4
+ * @{
+ */
+/**
+ * @file ext4_hash.c
+ * @brief Directory indexing hash functions.
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_errno.h"
+
+#include <string.h>
+
+/* F, G, and H are MD4 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/*
+ * FF, GG, and HH are transformations for rounds 1, 2, and 3.
+ * Rotation is separated from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s) \
+ { \
+ (a) += F((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT((a), (s)); \
+ \
+}
+
+#define GG(a, b, c, d, x, s) \
+ { \
+ (a) += G((b), (c), (d)) + (x) + (uint32_t)0x5A827999; \
+ (a) = ROTATE_LEFT((a), (s)); \
+ \
+}
+
+#define HH(a, b, c, d, x, s) \
+ { \
+ (a) += H((b), (c), (d)) + (x) + (uint32_t)0x6ED9EBA1; \
+ (a) = ROTATE_LEFT((a), (s)); \
+ \
+}
+
+/*
+ * MD4 basic transformation. It transforms state based on block.
+ *
+ * This is a half md4 algorithm since Linux uses this algorithm for dir
+ * index. This function is derived from the RSA Data Security, Inc. MD4
+ * Message-Digest Algorithm and was modified as necessary.
+ *
+ * The return value of this function is uint32_t in Linux, but actually we don't
+ * need to check this value, so in our version this function doesn't return any
+ * value.
+ */
+static void ext2_half_md4(uint32_t hash[4], uint32_t data[8])
+{
+ uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3];
+
+ /* Round 1 */
+ FF(a, b, c, d, data[0], 3);
+ FF(d, a, b, c, data[1], 7);
+ FF(c, d, a, b, data[2], 11);
+ FF(b, c, d, a, data[3], 19);
+ FF(a, b, c, d, data[4], 3);
+ FF(d, a, b, c, data[5], 7);
+ FF(c, d, a, b, data[6], 11);
+ FF(b, c, d, a, data[7], 19);
+
+ /* Round 2 */
+ GG(a, b, c, d, data[1], 3);
+ GG(d, a, b, c, data[3], 5);
+ GG(c, d, a, b, data[5], 9);
+ GG(b, c, d, a, data[7], 13);
+ GG(a, b, c, d, data[0], 3);
+ GG(d, a, b, c, data[2], 5);
+ GG(c, d, a, b, data[4], 9);
+ GG(b, c, d, a, data[6], 13);
+
+ /* Round 3 */
+ HH(a, b, c, d, data[3], 3);
+ HH(d, a, b, c, data[7], 9);
+ HH(c, d, a, b, data[2], 11);
+ HH(b, c, d, a, data[6], 15);
+ HH(a, b, c, d, data[1], 3);
+ HH(d, a, b, c, data[5], 9);
+ HH(c, d, a, b, data[0], 11);
+ HH(b, c, d, a, data[4], 15);
+
+ hash[0] += a;
+ hash[1] += b;
+ hash[2] += c;
+ hash[3] += d;
+}
+
+/*
+ * Tiny Encryption Algorithm.
+ */
+static void ext2_tea(uint32_t hash[4], uint32_t data[8])
+{
+ uint32_t tea_delta = 0x9E3779B9;
+ uint32_t sum;
+ uint32_t x = hash[0], y = hash[1];
+ int n = 16;
+ int i = 1;
+
+ while (n-- > 0) {
+ sum = i * tea_delta;
+ x += ((y << 4) + data[0]) ^ (y + sum) ^ ((y >> 5) + data[1]);
+ y += ((x << 4) + data[2]) ^ (x + sum) ^ ((x >> 5) + data[3]);
+ i++;
+ }
+
+ hash[0] += x;
+ hash[1] += y;
+}
+
+static uint32_t ext2_legacy_hash(const char *name, int len, int unsigned_char)
+{
+ uint32_t h0, h1 = 0x12A3FE2D, h2 = 0x37ABE8F9;
+ uint32_t multi = 0x6D22F5;
+ const unsigned char *uname = (const unsigned char *)name;
+ const signed char *sname = (const signed char *)name;
+ int val, i;
+
+ for (i = 0; i < len; i++) {
+ if (unsigned_char)
+ val = (unsigned int)*uname++;
+ else
+ val = (int)*sname++;
+
+ h0 = h2 + (h1 ^ (val * multi));
+ if (h0 & 0x80000000)
+ h0 -= 0x7FFFFFFF;
+ h2 = h1;
+ h1 = h0;
+ }
+
+ return (h1 << 1);
+}
+
+static void ext2_prep_hashbuf(const char *src, uint32_t slen, uint32_t *dst,
+ int dlen, int unsigned_char)
+{
+ uint32_t padding = slen | (slen << 8) | (slen << 16) | (slen << 24);
+ uint32_t buf_val;
+ int len, i;
+ int buf_byte;
+ const unsigned char *ubuf = (const unsigned char *)src;
+ const signed char *sbuf = (const signed char *)src;
+
+ if (slen > (uint32_t)dlen)
+ len = dlen;
+ else
+ len = slen;
+
+ buf_val = padding;
+
+ for (i = 0; i < len; i++) {
+ if (unsigned_char)
+ buf_byte = (unsigned int)ubuf[i];
+ else
+ buf_byte = (int)sbuf[i];
+
+ if ((i % 4) == 0)
+ buf_val = padding;
+
+ buf_val <<= 8;
+ buf_val += buf_byte;
+
+ if ((i % 4) == 3) {
+ *dst++ = buf_val;
+ dlen -= sizeof(uint32_t);
+ buf_val = padding;
+ }
+ }
+
+ dlen -= sizeof(uint32_t);
+ if (dlen >= 0)
+ *dst++ = buf_val;
+
+ dlen -= sizeof(uint32_t);
+ while (dlen >= 0) {
+ *dst++ = padding;
+ dlen -= sizeof(uint32_t);
+ }
+}
+
+int ext2_htree_hash(const char *name, int len, const uint32_t *hash_seed,
+ int hash_version, uint32_t *hash_major,
+ uint32_t *hash_minor)
+{
+ uint32_t hash[4];
+ uint32_t data[8];
+ uint32_t major = 0, minor = 0;
+ int unsigned_char = 0;
+
+ if (!name || !hash_major)
+ return (-1);
+
+ if (len < 1 || len > 255)
+ goto error;
+
+ hash[0] = 0x67452301;
+ hash[1] = 0xEFCDAB89;
+ hash[2] = 0x98BADCFE;
+ hash[3] = 0x10325476;
+
+ if (hash_seed)
+ memcpy(hash, hash_seed, sizeof(hash));
+
+ switch (hash_version) {
+ case EXT2_HTREE_TEA_UNSIGNED:
+ unsigned_char = 1;
+ case EXT2_HTREE_TEA:
+ while (len > 0) {
+ ext2_prep_hashbuf(name, len, data, 16, unsigned_char);
+ ext2_tea(hash, data);
+ len -= 16;
+ name += 16;
+ }
+ major = hash[0];
+ minor = hash[1];
+ break;
+ case EXT2_HTREE_LEGACY_UNSIGNED:
+ unsigned_char = 1;
+ case EXT2_HTREE_LEGACY:
+ major = ext2_legacy_hash(name, len, unsigned_char);
+ break;
+ case EXT2_HTREE_HALF_MD4_UNSIGNED:
+ unsigned_char = 1;
+ case EXT2_HTREE_HALF_MD4:
+ while (len > 0) {
+ ext2_prep_hashbuf(name, len, data, 32, unsigned_char);
+ ext2_half_md4(hash, data);
+ len -= 32;
+ name += 32;
+ }
+ major = hash[1];
+ minor = hash[2];
+ break;
+ default:
+ goto error;
+ }
+
+ major &= ~1;
+ if (major == (EXT2_HTREE_EOF << 1))
+ major = (EXT2_HTREE_EOF - 1) << 1;
+ *hash_major = major;
+ if (hash_minor)
+ *hash_minor = minor;
+
+ return EOK;
+
+error:
+ *hash_major = 0;
+ if (hash_minor)
+ *hash_minor = 0;
+ return ENOTSUP;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_hash.h
+++ b/lwext4/ext4_hash.h
@@ -1,60 +1,60 @@
-/*
- * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * 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_hash.h
- * @brief Directory indexing hash functions.
- */
-
-#ifndef EXT4_HASH_H_
-#define EXT4_HASH_H_
-
-#include "ext4_config.h"
-
-#include <stdint.h>
-
-/**@brief Directory entry name hash function.
- * @param name entry name
- * @param len entry name length
- * @param hash_seed (from superblock)
- * @param hash version (from superblock)
- * @param hash_minor output value
- * @param hash_major output value
- * @return standard error code*/
-int ext2_htree_hash(const char *name, int len, const uint32_t *hash_seed,
- int hash_version, uint32_t *hash_major,
- uint32_t *hash_minor);
-
-#endif /* EXT4_HASH_H_ */
-
-/**
- * @}
- */
+/*
+ * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * 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_hash.h
+ * @brief Directory indexing hash functions.
+ */
+
+#ifndef EXT4_HASH_H_
+#define EXT4_HASH_H_
+
+#include "ext4_config.h"
+
+#include <stdint.h>
+
+/**@brief Directory entry name hash function.
+ * @param name entry name
+ * @param len entry name length
+ * @param hash_seed (from superblock)
+ * @param hash version (from superblock)
+ * @param hash_minor output value
+ * @param hash_major output value
+ * @return standard error code*/
+int ext2_htree_hash(const char *name, int len, const uint32_t *hash_seed,
+ int hash_version, uint32_t *hash_major,
+ uint32_t *hash_minor);
+
+#endif /* EXT4_HASH_H_ */
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_ialloc.c
+++ b/lwext4/ext4_ialloc.c
@@ -1,296 +1,296 @@
-/*
- * 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_ialloc.c
- * @brief Inode allocation procedures.
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_ialloc.h"
-#include "ext4_super.h"
-#include "ext4_fs.h"
-#include "ext4_blockdev.h"
-#include "ext4_block_group.h"
-#include "ext4_bitmap.h"
-
-/**@brief Convert i-node number to relative index in block group.
- * @param sb Superblock
- * @param inode I-node number to be converted
- * @return Index of the i-node in the block group
- */
-static uint32_t ext4_ialloc_inode2index_in_group(struct ext4_sblock *sb,
- uint32_t inode)
-{
- uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
- return (inode - 1) % inodes_per_group;
-}
-
-/**@brief Convert relative index of i-node to absolute i-node number.
- * @param sb Superblock
- * @param index Index to be converted
- * @return Absolute number of the i-node
- *
- */
-static uint32_t ext4_ialloc_index_in_group2inode(struct ext4_sblock *sb,
- uint32_t index, uint32_t bgid)
-{
- uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
- return bgid * inodes_per_group + (index + 1);
-}
-
-/**@brief Compute block group number from the i-node number.
- * @param sb Superblock
- * @param inode I-node number to be found the block group for
- * @return Block group number computed from i-node number
- */
-static uint32_t ext4_ialloc_get_bgid_of_inode(struct ext4_sblock *sb,
- uint32_t inode)
-{
- uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
- return (inode - 1) / inodes_per_group;
-}
-
-int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir)
-{
- struct ext4_sblock *sb = &fs->sb;
-
- /* Compute index of block group and load it */
- uint32_t block_group = ext4_ialloc_get_bgid_of_inode(sb, index);
-
- struct ext4_block_group_ref bg_ref;
- int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
- if (rc != EOK)
- return rc;
-
- /* Load i-node bitmap */
- uint32_t bitmap_block_addr =
- ext4_bg_get_inode_bitmap(bg_ref.block_group, sb);
-
- struct ext4_block bitmap_block;
- rc = ext4_block_get(fs->bdev, &bitmap_block, bitmap_block_addr);
- if (rc != EOK)
- return rc;
-
- /* Free i-node in the bitmap */
- uint32_t index_in_group = ext4_ialloc_inode2index_in_group(sb, index);
- ext4_bmap_bit_clr(bitmap_block.data, index_in_group);
- bitmap_block.dirty = true;
-
- /* Put back the block with bitmap */
- rc = ext4_block_set(fs->bdev, &bitmap_block);
- if (rc != EOK) {
- /* Error in saving bitmap */
- ext4_fs_put_block_group_ref(&bg_ref);
- return rc;
- }
-
- /* If released i-node is a directory, decrement used directories count
- */
- if (is_dir) {
- uint32_t bg_used_dirs =
- ext4_bg_get_used_dirs_count(bg_ref.block_group, sb);
- bg_used_dirs--;
- ext4_bg_set_used_dirs_count(bg_ref.block_group, sb,
- bg_used_dirs);
- }
-
- /* Update block group free inodes count */
- uint32_t free_inodes =
- ext4_bg_get_free_inodes_count(bg_ref.block_group, sb);
- free_inodes++;
- ext4_bg_set_free_inodes_count(bg_ref.block_group, sb, free_inodes);
-
- bg_ref.dirty = true;
-
- /* Put back the modified block group */
- rc = ext4_fs_put_block_group_ref(&bg_ref);
- if (rc != EOK)
- return rc;
-
- /* Update superblock free inodes count */
- ext4_set32(sb, free_inodes_count,
- ext4_get32(sb, free_inodes_count) + 1);
-
- return EOK;
-}
-
-int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *index, bool is_dir)
-{
- struct ext4_sblock *sb = &fs->sb;
-
- uint32_t bgid = fs->last_inode_bg_id;
- uint32_t bg_count = ext4_block_group_cnt(sb);
- uint32_t sb_free_inodes = ext4_get32(sb, free_inodes_count);
- bool rewind = false;
-
- /* Try to find free i-node in all block groups */
- while (bgid <= bg_count) {
-
- if (bgid == bg_count) {
- if (rewind)
- break;
- bg_count = fs->last_inode_bg_id;
- bgid = 0;
- rewind = true;
- continue;
- }
-
- /* Load block group to check */
- struct ext4_block_group_ref bg_ref;
- int rc = ext4_fs_get_block_group_ref(fs, bgid, &bg_ref);
- if (rc != EOK)
- return rc;
-
- struct ext4_bgroup *bg = bg_ref.block_group;
-
- /* Read necessary values for algorithm */
- uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
- uint32_t used_dirs = ext4_bg_get_used_dirs_count(bg, sb);
-
- /* Check if this block group is good candidate for allocation */
- if (free_inodes > 0) {
- /* Load block with bitmap */
- uint32_t bitmap_block_addr =
- ext4_bg_get_inode_bitmap(bg_ref.block_group, sb);
-
- struct ext4_block bitmap_block;
- rc = ext4_block_get(fs->bdev, &bitmap_block,
- bitmap_block_addr);
- if (rc != EOK) {
- ext4_fs_put_block_group_ref(&bg_ref);
- return rc;
- }
-
- /* Try to allocate i-node in the bitmap */
- uint32_t inodes_in_group =
- ext4_inodes_in_group_cnt(sb, bgid);
- uint32_t index_in_group;
-
- rc = ext4_bmap_bit_find_clr(bitmap_block.data, 0,
- inodes_in_group,
- &index_in_group);
- /* Block group has not any free i-node */
- if (rc == ENOSPC) {
- rc = ext4_block_set(fs->bdev, &bitmap_block);
- if (rc != EOK) {
- ext4_fs_put_block_group_ref(&bg_ref);
- return rc;
- }
-
- rc = ext4_fs_put_block_group_ref(&bg_ref);
- if (rc != EOK)
- return rc;
-
- continue;
- }
-
- ext4_bmap_bit_set(bitmap_block.data, index_in_group);
-
- /* Free i-node found, save the bitmap */
- bitmap_block.dirty = true;
-
- ext4_block_set(fs->bdev, &bitmap_block);
- if (rc != EOK) {
- ext4_fs_put_block_group_ref(&bg_ref);
- return rc;
- }
-
- /* Modify filesystem counters */
- free_inodes--;
- ext4_bg_set_free_inodes_count(bg, sb, free_inodes);
-
- /* Increment used directories counter */
- if (is_dir) {
- used_dirs++;
- ext4_bg_set_used_dirs_count(bg, sb, used_dirs);
- }
-
- /* Decrease unused inodes count */
- if (ext4_bg_has_flag(bg,
- EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
- uint32_t unused =
- ext4_bg_get_itable_unused(bg, sb);
-
- uint32_t inodes_in_group =
- ext4_inodes_in_group_cnt(sb, bgid);
-
- uint32_t free = inodes_in_group - unused;
-
- if (index_in_group >= free) {
- unused = inodes_in_group -
- (index_in_group + 1);
- ext4_bg_set_itable_unused(bg, sb,
- unused);
- }
- }
-
- /* Save modified block group */
- bg_ref.dirty = true;
-
- rc = ext4_fs_put_block_group_ref(&bg_ref);
- if (rc != EOK)
- return rc;
-
- /* Update superblock */
- sb_free_inodes--;
- ext4_set32(sb, free_inodes_count, sb_free_inodes);
-
- /* Compute the absolute i-nodex number */
- *index = ext4_ialloc_index_in_group2inode(
- sb, index_in_group, bgid);
-
- fs->last_inode_bg_id = bgid;
-
- return EOK;
- }
-
- /* Block group not modified, put it and jump to the next block
- * group */
- ext4_fs_put_block_group_ref(&bg_ref);
- if (rc != EOK)
- return rc;
-
- ++bgid;
- }
-
- return ENOSPC;
-}
-
-/**
- * @}
- */
+/*
+ * 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_ialloc.c
+ * @brief Inode allocation procedures.
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_ialloc.h"
+#include "ext4_super.h"
+#include "ext4_fs.h"
+#include "ext4_blockdev.h"
+#include "ext4_block_group.h"
+#include "ext4_bitmap.h"
+
+/**@brief Convert i-node number to relative index in block group.
+ * @param sb Superblock
+ * @param inode I-node number to be converted
+ * @return Index of the i-node in the block group
+ */
+static uint32_t ext4_ialloc_inode2index_in_group(struct ext4_sblock *sb,
+ uint32_t inode)
+{
+ uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
+ return (inode - 1) % inodes_per_group;
+}
+
+/**@brief Convert relative index of i-node to absolute i-node number.
+ * @param sb Superblock
+ * @param index Index to be converted
+ * @return Absolute number of the i-node
+ *
+ */
+static uint32_t ext4_ialloc_index_in_group2inode(struct ext4_sblock *sb,
+ uint32_t index, uint32_t bgid)
+{
+ uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
+ return bgid * inodes_per_group + (index + 1);
+}
+
+/**@brief Compute block group number from the i-node number.
+ * @param sb Superblock
+ * @param inode I-node number to be found the block group for
+ * @return Block group number computed from i-node number
+ */
+static uint32_t ext4_ialloc_get_bgid_of_inode(struct ext4_sblock *sb,
+ uint32_t inode)
+{
+ uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
+ return (inode - 1) / inodes_per_group;
+}
+
+int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir)
+{
+ struct ext4_sblock *sb = &fs->sb;
+
+ /* Compute index of block group and load it */
+ uint32_t block_group = ext4_ialloc_get_bgid_of_inode(sb, index);
+
+ struct ext4_block_group_ref bg_ref;
+ int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
+ if (rc != EOK)
+ return rc;
+
+ /* Load i-node bitmap */
+ uint32_t bitmap_block_addr =
+ ext4_bg_get_inode_bitmap(bg_ref.block_group, sb);
+
+ struct ext4_block bitmap_block;
+ rc = ext4_block_get(fs->bdev, &bitmap_block, bitmap_block_addr);
+ if (rc != EOK)
+ return rc;
+
+ /* Free i-node in the bitmap */
+ uint32_t index_in_group = ext4_ialloc_inode2index_in_group(sb, index);
+ ext4_bmap_bit_clr(bitmap_block.data, index_in_group);
+ bitmap_block.dirty = true;
+
+ /* Put back the block with bitmap */
+ rc = ext4_block_set(fs->bdev, &bitmap_block);
+ if (rc != EOK) {
+ /* Error in saving bitmap */
+ ext4_fs_put_block_group_ref(&bg_ref);
+ return rc;
+ }
+
+ /* If released i-node is a directory, decrement used directories count
+ */
+ if (is_dir) {
+ uint32_t bg_used_dirs =
+ ext4_bg_get_used_dirs_count(bg_ref.block_group, sb);
+ bg_used_dirs--;
+ ext4_bg_set_used_dirs_count(bg_ref.block_group, sb,
+ bg_used_dirs);
+ }
+
+ /* Update block group free inodes count */
+ uint32_t free_inodes =
+ ext4_bg_get_free_inodes_count(bg_ref.block_group, sb);
+ free_inodes++;
+ ext4_bg_set_free_inodes_count(bg_ref.block_group, sb, free_inodes);
+
+ bg_ref.dirty = true;
+
+ /* Put back the modified block group */
+ rc = ext4_fs_put_block_group_ref(&bg_ref);
+ if (rc != EOK)
+ return rc;
+
+ /* Update superblock free inodes count */
+ ext4_set32(sb, free_inodes_count,
+ ext4_get32(sb, free_inodes_count) + 1);
+
+ return EOK;
+}
+
+int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *index, bool is_dir)
+{
+ struct ext4_sblock *sb = &fs->sb;
+
+ uint32_t bgid = fs->last_inode_bg_id;
+ uint32_t bg_count = ext4_block_group_cnt(sb);
+ uint32_t sb_free_inodes = ext4_get32(sb, free_inodes_count);
+ bool rewind = false;
+
+ /* Try to find free i-node in all block groups */
+ while (bgid <= bg_count) {
+
+ if (bgid == bg_count) {
+ if (rewind)
+ break;
+ bg_count = fs->last_inode_bg_id;
+ bgid = 0;
+ rewind = true;
+ continue;
+ }
+
+ /* Load block group to check */
+ struct ext4_block_group_ref bg_ref;
+ int rc = ext4_fs_get_block_group_ref(fs, bgid, &bg_ref);
+ if (rc != EOK)
+ return rc;
+
+ struct ext4_bgroup *bg = bg_ref.block_group;
+
+ /* Read necessary values for algorithm */
+ uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
+ uint32_t used_dirs = ext4_bg_get_used_dirs_count(bg, sb);
+
+ /* Check if this block group is good candidate for allocation */
+ if (free_inodes > 0) {
+ /* Load block with bitmap */
+ uint32_t bitmap_block_addr =
+ ext4_bg_get_inode_bitmap(bg_ref.block_group, sb);
+
+ struct ext4_block bitmap_block;
+ rc = ext4_block_get(fs->bdev, &bitmap_block,
+ bitmap_block_addr);
+ if (rc != EOK) {
+ ext4_fs_put_block_group_ref(&bg_ref);
+ return rc;
+ }
+
+ /* Try to allocate i-node in the bitmap */
+ uint32_t inodes_in_group =
+ ext4_inodes_in_group_cnt(sb, bgid);
+ uint32_t index_in_group;
+
+ rc = ext4_bmap_bit_find_clr(bitmap_block.data, 0,
+ inodes_in_group,
+ &index_in_group);
+ /* Block group has not any free i-node */
+ if (rc == ENOSPC) {
+ rc = ext4_block_set(fs->bdev, &bitmap_block);
+ if (rc != EOK) {
+ ext4_fs_put_block_group_ref(&bg_ref);
+ return rc;
+ }
+
+ rc = ext4_fs_put_block_group_ref(&bg_ref);
+ if (rc != EOK)
+ return rc;
+
+ continue;
+ }
+
+ ext4_bmap_bit_set(bitmap_block.data, index_in_group);
+
+ /* Free i-node found, save the bitmap */
+ bitmap_block.dirty = true;
+
+ ext4_block_set(fs->bdev, &bitmap_block);
+ if (rc != EOK) {
+ ext4_fs_put_block_group_ref(&bg_ref);
+ return rc;
+ }
+
+ /* Modify filesystem counters */
+ free_inodes--;
+ ext4_bg_set_free_inodes_count(bg, sb, free_inodes);
+
+ /* Increment used directories counter */
+ if (is_dir) {
+ used_dirs++;
+ ext4_bg_set_used_dirs_count(bg, sb, used_dirs);
+ }
+
+ /* Decrease unused inodes count */
+ if (ext4_bg_has_flag(bg,
+ EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
+ uint32_t unused =
+ ext4_bg_get_itable_unused(bg, sb);
+
+ uint32_t inodes_in_group =
+ ext4_inodes_in_group_cnt(sb, bgid);
+
+ uint32_t free = inodes_in_group - unused;
+
+ if (index_in_group >= free) {
+ unused = inodes_in_group -
+ (index_in_group + 1);
+ ext4_bg_set_itable_unused(bg, sb,
+ unused);
+ }
+ }
+
+ /* Save modified block group */
+ bg_ref.dirty = true;
+
+ rc = ext4_fs_put_block_group_ref(&bg_ref);
+ if (rc != EOK)
+ return rc;
+
+ /* Update superblock */
+ sb_free_inodes--;
+ ext4_set32(sb, free_inodes_count, sb_free_inodes);
+
+ /* Compute the absolute i-nodex number */
+ *index = ext4_ialloc_index_in_group2inode(
+ sb, index_in_group, bgid);
+
+ fs->last_inode_bg_id = bgid;
+
+ return EOK;
+ }
+
+ /* Block group not modified, put it and jump to the next block
+ * group */
+ ext4_fs_put_block_group_ref(&bg_ref);
+ if (rc != EOK)
+ return rc;
+
+ ++bgid;
+ }
+
+ return ENOSPC;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_ialloc.h
+++ b/lwext4/ext4_ialloc.h
@@ -1,76 +1,76 @@
-/*
- * 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_ialloc.c
- * @brief Inode allocation procedures.
- */
-
-#ifndef EXT4_IALLOC_H_
-#define EXT4_IALLOC_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-/**@brief Free i-node number and modify filesystem data structers.
- * @param fs Filesystem, where the i-node is located
- * @param index Index of i-node to be release
- * @param is_dir Flag us for information whether i-node is directory or not
- */
-int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir);
-
-/**@brief I-node allocation algorithm.
- * This is more simple algorithm, than Orlov allocator used
- * in the Linux kernel.
- * @param fs Filesystem to allocate i-node on
- * @param index Output value - allocated i-node number
- * @param is_dir Flag if allocated i-node will be file or directory
- * @return Error code
- */
-int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *index, bool is_dir);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* EXT4_IALLOC_H_ */
-
-/**
- * @}
- */
+/*
+ * 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_ialloc.c
+ * @brief Inode allocation procedures.
+ */
+
+#ifndef EXT4_IALLOC_H_
+#define EXT4_IALLOC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+/**@brief Free i-node number and modify filesystem data structers.
+ * @param fs Filesystem, where the i-node is located
+ * @param index Index of i-node to be release
+ * @param is_dir Flag us for information whether i-node is directory or not
+ */
+int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir);
+
+/**@brief I-node allocation algorithm.
+ * This is more simple algorithm, than Orlov allocator used
+ * in the Linux kernel.
+ * @param fs Filesystem to allocate i-node on
+ * @param index Output value - allocated i-node number
+ * @param is_dir Flag if allocated i-node will be file or directory
+ * @return Error code
+ */
+int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *index, bool is_dir);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* EXT4_IALLOC_H_ */
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_inode.c
+++ b/lwext4/ext4_inode.c
@@ -1,340 +1,340 @@
-/*
- * 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_inode.c
- * @brief Inode handle functions
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_inode.h"
-#include "ext4_super.h"
-
-/**@brief Compute number of bits for block count.
- * @param block_size Filesystem block_size
- * @return Number of bits
- */
-static uint32_t ext4_inode_block_bits_count(uint32_t block_size)
-{
- uint32_t bits = 8;
- uint32_t size = block_size;
-
- do {
- bits++;
- size = size >> 1;
- } while (size > 256);
-
- return bits;
-}
-
-uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode)
-{
- uint32_t v = to_le16(inode->mode);
-
- if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD) {
- v |= ((uint32_t)to_le16(inode->osd2.hurd2.mode_high)) << 16;
- }
-
- return v;
-}
-
-void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode,
- uint32_t mode)
-{
- inode->mode = to_le16((mode << 16) >> 16);
-
- if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD)
- inode->osd2.hurd2.mode_high = to_le16(mode >> 16);
-}
-
-uint32_t ext4_inode_get_uid(struct ext4_inode *inode)
-{
- return to_le32(inode->uid);
-}
-
-void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid)
-{
- inode->uid = to_le32(uid);
-}
-
-uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode)
-{
- uint64_t v = to_le32(inode->size_lo);
-
- if ((ext4_get32(sb, rev_level) > 0) &&
- (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)))
- v |= ((uint64_t)to_le32(inode->size_hi)) << 32;
-
- return v;
-}
-
-void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size)
-{
- inode->size_lo = to_le32((size << 32) >> 32);
- inode->size_hi = to_le32(size >> 32);
-}
-
-uint32_t ext4_inode_get_access_time(struct ext4_inode *inode)
-{
- return to_le32(inode->access_time);
-}
-void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time)
-{
- inode->access_time = to_le32(time);
-}
-
-uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode)
-{
- return to_le32(inode->change_inode_time);
-}
-void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time)
-{
- inode->change_inode_time = to_le32(time);
-}
-
-uint32_t ext4_inode_get_modification_time(struct ext4_inode *inode)
-{
- return to_le32(inode->modification_time);
-}
-
-void ext4_inode_set_modification_time(struct ext4_inode *inode, uint32_t time)
-{
- inode->modification_time = to_le32(time);
-}
-
-uint32_t ext4_inode_get_deletion_time(struct ext4_inode *inode)
-{
- return to_le32(inode->deletion_time);
-}
-
-void ext4_inode_set_deletion_time(struct ext4_inode *inode, uint32_t time)
-{
- inode->deletion_time = to_le32(time);
-}
-
-uint32_t ext4_inode_get_gid(struct ext4_inode *inode)
-{
- return to_le32(inode->gid);
-}
-void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid)
-{
- inode->gid = to_le32(gid);
-}
-
-uint16_t ext4_inode_get_links_count(struct ext4_inode *inode)
-{
- return to_le16(inode->links_count);
-}
-void ext4_inode_set_links_count(struct ext4_inode *inode, uint16_t cnt)
-{
- inode->links_count = to_le16(cnt);
-}
-
-uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb,
- struct ext4_inode *inode)
-{
- uint64_t count = to_le32(inode->blocks_count_lo);
-
- if (ext4_sb_has_feature_read_only(sb,
- EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
-
- /* 48-bit field */
- count |= ((uint64_t)to_le16(inode->osd2.linux2.blocks_high))
- << 32;
-
- if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) {
-
- uint32_t block_bits = ext4_inode_block_bits_count(
- ext4_sb_get_block_size(sb));
- return count << (block_bits - 9);
- }
- }
-
- return count;
-}
-
-int ext4_inode_set_blocks_count(struct ext4_sblock *sb,
- struct ext4_inode *inode, uint64_t count)
-{
- /* 32-bit maximum */
- uint64_t max = 0;
- max = ~max >> 32;
-
- if (count <= max) {
- inode->blocks_count_lo = to_le32(count);
- inode->osd2.linux2.blocks_high = 0;
- ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
-
- return EOK;
- }
-
- /* Check if there can be used huge files (many blocks) */
- if (!ext4_sb_has_feature_read_only(sb,
- EXT4_FEATURE_RO_COMPAT_HUGE_FILE))
- return EINVAL;
-
- /* 48-bit maximum */
- max = 0;
- max = ~max >> 16;
-
- if (count <= max) {
- inode->blocks_count_lo = to_le32(count);
- inode->osd2.linux2.blocks_high = to_le16(count >> 32);
- ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
- } else {
- uint32_t block_bits =
- ext4_inode_block_bits_count(ext4_sb_get_block_size(sb));
-
- ext4_inode_set_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
- count = count >> (block_bits - 9);
- inode->blocks_count_lo = to_le32(count);
- inode->osd2.linux2.blocks_high = to_le16(count >> 32);
- }
-
- return EOK;
-}
-
-uint32_t ext4_inode_get_flags(struct ext4_inode *inode)
-{
- return to_le32(inode->flags);
-}
-void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags)
-{
- inode->flags = to_le32(flags);
-}
-
-uint32_t ext4_inode_get_generation(struct ext4_inode *inode)
-{
- return to_le32(inode->generation);
-}
-void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen)
-{
- inode->generation = to_le32(gen);
-}
-
-uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode,
- struct ext4_sblock *sb)
-{
- /*TODO: Verify it*/
- uint64_t v = to_le32(inode->file_acl_lo);
-
- if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_LINUX)
- v |= ((uint32_t)to_le16(inode->osd2.linux2.file_acl_high))
- << 16;
-
- return v;
-}
-
-void ext4_inode_set_file_acl(struct ext4_inode *inode, struct ext4_sblock *sb,
- uint64_t acl)
-{
- /*TODO: Verify it*/
- inode->file_acl_lo = to_le32((acl << 32) >> 32);
-
- if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_LINUX)
- inode->osd2.linux2.file_acl_high = to_le16(acl >> 32);
-}
-
-uint32_t ext4_inode_get_direct_block(struct ext4_inode *inode, uint32_t idx)
-{
- return to_le32(inode->blocks[idx]);
-}
-void ext4_inode_set_direct_block(struct ext4_inode *inode, uint32_t idx,
- uint32_t block)
-{
- inode->blocks[idx] = to_le32(block);
-}
-
-uint32_t ext4_inode_get_indirect_block(struct ext4_inode *inode, uint32_t idx)
-{
- return to_le32(inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK]);
-}
-
-void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
- uint32_t block)
-{
- inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK] = to_le32(block);
-}
-
-bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
- uint32_t type)
-{
- return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK) ==
- type;
-}
-
-bool ext4_inode_has_flag(struct ext4_inode *inode, uint32_t f)
-{
- return ext4_inode_get_flags(inode) & f;
-}
-
-void ext4_inode_clear_flag(struct ext4_inode *inode, uint32_t f)
-{
- uint32_t flags = ext4_inode_get_flags(inode);
- flags = flags & (~f);
- ext4_inode_set_flags(inode, flags);
-}
-
-void ext4_inode_set_flag(struct ext4_inode *inode, uint32_t f)
-{
- uint32_t flags = ext4_inode_get_flags(inode);
- flags = flags | f;
- ext4_inode_set_flags(inode, flags);
-}
-
-bool ext4_inode_can_truncate(struct ext4_sblock *sb, struct ext4_inode *inode)
-{
- if ((ext4_inode_has_flag(inode, EXT4_INODE_FLAG_APPEND)) ||
- (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_IMMUTABLE)))
- return false;
-
- if ((ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)) ||
- (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_DIRECTORY)))
- return true;
-
- return false;
-}
-
-struct ext4_extent_header *
-ext4_inode_get_extent_header(struct ext4_inode *inode)
-{
- return (struct ext4_extent_header *)inode->blocks;
-}
-
-/**
- * @}
- */
+/*
+ * 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_inode.c
+ * @brief Inode handle functions
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_inode.h"
+#include "ext4_super.h"
+
+/**@brief Compute number of bits for block count.
+ * @param block_size Filesystem block_size
+ * @return Number of bits
+ */
+static uint32_t ext4_inode_block_bits_count(uint32_t block_size)
+{
+ uint32_t bits = 8;
+ uint32_t size = block_size;
+
+ do {
+ bits++;
+ size = size >> 1;
+ } while (size > 256);
+
+ return bits;
+}
+
+uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode)
+{
+ uint32_t v = to_le16(inode->mode);
+
+ if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD) {
+ v |= ((uint32_t)to_le16(inode->osd2.hurd2.mode_high)) << 16;
+ }
+
+ return v;
+}
+
+void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode,
+ uint32_t mode)
+{
+ inode->mode = to_le16((mode << 16) >> 16);
+
+ if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD)
+ inode->osd2.hurd2.mode_high = to_le16(mode >> 16);
+}
+
+uint32_t ext4_inode_get_uid(struct ext4_inode *inode)
+{
+ return to_le32(inode->uid);
+}
+
+void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid)
+{
+ inode->uid = to_le32(uid);
+}
+
+uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode)
+{
+ uint64_t v = to_le32(inode->size_lo);
+
+ if ((ext4_get32(sb, rev_level) > 0) &&
+ (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)))
+ v |= ((uint64_t)to_le32(inode->size_hi)) << 32;
+
+ return v;
+}
+
+void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size)
+{
+ inode->size_lo = to_le32((size << 32) >> 32);
+ inode->size_hi = to_le32(size >> 32);
+}
+
+uint32_t ext4_inode_get_access_time(struct ext4_inode *inode)
+{
+ return to_le32(inode->access_time);
+}
+void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time)
+{
+ inode->access_time = to_le32(time);
+}
+
+uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode)
+{
+ return to_le32(inode->change_inode_time);
+}
+void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time)
+{
+ inode->change_inode_time = to_le32(time);
+}
+
+uint32_t ext4_inode_get_modification_time(struct ext4_inode *inode)
+{
+ return to_le32(inode->modification_time);
+}
+
+void ext4_inode_set_modification_time(struct ext4_inode *inode, uint32_t time)
+{
+ inode->modification_time = to_le32(time);
+}
+
+uint32_t ext4_inode_get_deletion_time(struct ext4_inode *inode)
+{
+ return to_le32(inode->deletion_time);
+}
+
+void ext4_inode_set_deletion_time(struct ext4_inode *inode, uint32_t time)
+{
+ inode->deletion_time = to_le32(time);
+}
+
+uint32_t ext4_inode_get_gid(struct ext4_inode *inode)
+{
+ return to_le32(inode->gid);
+}
+void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid)
+{
+ inode->gid = to_le32(gid);
+}
+
+uint16_t ext4_inode_get_links_count(struct ext4_inode *inode)
+{
+ return to_le16(inode->links_count);
+}
+void ext4_inode_set_links_count(struct ext4_inode *inode, uint16_t cnt)
+{
+ inode->links_count = to_le16(cnt);
+}
+
+uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb,
+ struct ext4_inode *inode)
+{
+ uint64_t count = to_le32(inode->blocks_count_lo);
+
+ if (ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
+
+ /* 48-bit field */
+ count |= ((uint64_t)to_le16(inode->osd2.linux2.blocks_high))
+ << 32;
+
+ if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) {
+
+ uint32_t block_bits = ext4_inode_block_bits_count(
+ ext4_sb_get_block_size(sb));
+ return count << (block_bits - 9);
+ }
+ }
+
+ return count;
+}
+
+int ext4_inode_set_blocks_count(struct ext4_sblock *sb,
+ struct ext4_inode *inode, uint64_t count)
+{
+ /* 32-bit maximum */
+ uint64_t max = 0;
+ max = ~max >> 32;
+
+ if (count <= max) {
+ inode->blocks_count_lo = to_le32(count);
+ inode->osd2.linux2.blocks_high = 0;
+ ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
+
+ return EOK;
+ }
+
+ /* Check if there can be used huge files (many blocks) */
+ if (!ext4_sb_has_feature_read_only(sb,
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE))
+ return EINVAL;
+
+ /* 48-bit maximum */
+ max = 0;
+ max = ~max >> 16;
+
+ if (count <= max) {
+ inode->blocks_count_lo = to_le32(count);
+ inode->osd2.linux2.blocks_high = to_le16(count >> 32);
+ ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
+ } else {
+ uint32_t block_bits =
+ ext4_inode_block_bits_count(ext4_sb_get_block_size(sb));
+
+ ext4_inode_set_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
+ count = count >> (block_bits - 9);
+ inode->blocks_count_lo = to_le32(count);
+ inode->osd2.linux2.blocks_high = to_le16(count >> 32);
+ }
+
+ return EOK;
+}
+
+uint32_t ext4_inode_get_flags(struct ext4_inode *inode)
+{
+ return to_le32(inode->flags);
+}
+void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags)
+{
+ inode->flags = to_le32(flags);
+}
+
+uint32_t ext4_inode_get_generation(struct ext4_inode *inode)
+{
+ return to_le32(inode->generation);
+}
+void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen)
+{
+ inode->generation = to_le32(gen);
+}
+
+uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode,
+ struct ext4_sblock *sb)
+{
+ /*TODO: Verify it*/
+ uint64_t v = to_le32(inode->file_acl_lo);
+
+ if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_LINUX)
+ v |= ((uint32_t)to_le16(inode->osd2.linux2.file_acl_high))
+ << 16;
+
+ return v;
+}
+
+void ext4_inode_set_file_acl(struct ext4_inode *inode, struct ext4_sblock *sb,
+ uint64_t acl)
+{
+ /*TODO: Verify it*/
+ inode->file_acl_lo = to_le32((acl << 32) >> 32);
+
+ if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_LINUX)
+ inode->osd2.linux2.file_acl_high = to_le16(acl >> 32);
+}
+
+uint32_t ext4_inode_get_direct_block(struct ext4_inode *inode, uint32_t idx)
+{
+ return to_le32(inode->blocks[idx]);
+}
+void ext4_inode_set_direct_block(struct ext4_inode *inode, uint32_t idx,
+ uint32_t block)
+{
+ inode->blocks[idx] = to_le32(block);
+}
+
+uint32_t ext4_inode_get_indirect_block(struct ext4_inode *inode, uint32_t idx)
+{
+ return to_le32(inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK]);
+}
+
+void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
+ uint32_t block)
+{
+ inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK] = to_le32(block);
+}
+
+bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
+ uint32_t type)
+{
+ return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK) ==
+ type;
+}
+
+bool ext4_inode_has_flag(struct ext4_inode *inode, uint32_t f)
+{
+ return ext4_inode_get_flags(inode) & f;
+}
+
+void ext4_inode_clear_flag(struct ext4_inode *inode, uint32_t f)
+{
+ uint32_t flags = ext4_inode_get_flags(inode);
+ flags = flags & (~f);
+ ext4_inode_set_flags(inode, flags);
+}
+
+void ext4_inode_set_flag(struct ext4_inode *inode, uint32_t f)
+{
+ uint32_t flags = ext4_inode_get_flags(inode);
+ flags = flags | f;
+ ext4_inode_set_flags(inode, flags);
+}
+
+bool ext4_inode_can_truncate(struct ext4_sblock *sb, struct ext4_inode *inode)
+{
+ if ((ext4_inode_has_flag(inode, EXT4_INODE_FLAG_APPEND)) ||
+ (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_IMMUTABLE)))
+ return false;
+
+ if ((ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)) ||
+ (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_DIRECTORY)))
+ return true;
+
+ return false;
+}
+
+struct ext4_extent_header *
+ext4_inode_get_extent_header(struct ext4_inode *inode)
+{
+ return (struct ext4_extent_header *)inode->blocks;
+}
+
+/**
+ * @}
+ */
--- a/lwext4/ext4_inode.h
+++ b/lwext4/ext4_inode.h
@@ -1,294 +1,294 @@
-/*
- * 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_inode.h
- * @brief Inode handle functions
- */
-
-#ifndef EXT4_INODE_H_
-#define EXT4_INODE_H_
-
-#include "ext4_config.h"
-
-#include <stdint.h>
-
-/**@brief Get mode of the i-node.
- * @param sb Superblock
- * @param inode I-node to load mode from
- * @return Mode of the i-node
- */
-uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode);
-
-/**@brief Set mode of the i-node.
- * @param sb Superblock
- * @param inode I-node to set mode to
- * @param mode Mode to set to i-node
- */
-void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode,
- uint32_t mode);
-
-/**@brief Get ID of the i-node owner (user id).
- * @param inode I-node to load uid from
- * @return User ID of the i-node owner
- */
-uint32_t ext4_inode_get_uid(struct ext4_inode *inode);
-
-/**@brief Set ID of the i-node owner.
- * @param inode I-node to set uid to
- * @param uid ID of the i-node owner
- */
-void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid);
-
-/**@brief Get real i-node size.
- * @param sb Superblock
- * @param inode I-node to load size from
- * @return Real size of i-node
- */
-uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode);
-
-/**@brief Set real i-node size.
- * @param inode I-node to set size to
- * @param size Size of the i-node
- */
-void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size);
-
-/**@brief Get time, when i-node was last accessed.
- * @param inode I-node
- * @return Time of the last access (POSIX)
- */
-uint32_t ext4_inode_get_access_time(struct ext4_inode *inode);
-
-/**@brief Set time, when i-node was last accessed.
- * @param inode I-node
- * @param time Time of the last access (POSIX)
- */
-void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time);
-
-/**@brief Get time, when i-node was last changed.
- * @param inode I-node
- * @return Time of the last change (POSIX)
- */
-uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode);
-
-/**@brief Set time, when i-node was last changed.
- * @param inode I-node
- * @param time Time of the last change (POSIX)
- */
-void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time);
-
-/**@brief Get time, when i-node content was last modified.
- * @param inode I-node
- * @return Time of the last content modification (POSIX)
- */
-uint32_t ext4_inode_get_modification_time(struct ext4_inode *inode);
-
-/**@brief Set time, when i-node content was last modified.
- * @param inode I-node
- * @param time Time of the last content modification (POSIX)
- */
-void ext4_inode_set_modification_time(struct ext4_inode *inode, uint32_t time);
-
-/**@brief Get time, when i-node was deleted.
- * @param inode I-node
- * @return Time of the delete action (POSIX)
- */
-uint32_t ext4_inode_get_deletion_time(struct ext4_inode *inode);
-
-/**@brief Set time, when i-node was deleted.
- * @param inode I-node
- * @param time Time of the delete action (POSIX)
- */
-void ext4_inode_set_deletion_time(struct ext4_inode *inode, uint32_t time);
-
-/**@brief Get ID of the i-node owner's group.
- * @param inode I-node to load gid from
- * @return Group ID of the i-node owner
- */
-uint32_t ext4_inode_get_gid(struct ext4_inode *inode);
-
-/**@brief Set ID to the i-node owner's group.
- * @param inode I-node to set gid to
- * @param gid Group ID of the i-node owner
- */
-void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid);
-
-/**@brief Get number of links to i-node.
- * @param inode I-node to load number of links from
- * @return Number of links to i-node
- */
-uint16_t ext4_inode_get_links_count(struct ext4_inode *inode);
-
-/**@brief Set number of links to i-node.
- * @param inode I-node to set number of links to
- * @param count Number of links to i-node
- */
-void ext4_inode_set_links_count(struct ext4_inode *inode, uint16_t cnt);
-
-/**@brief Get number of 512-bytes blocks used for i-node.
- * @param sb Superblock
- * @param inode I-node
- * @return Number of 512-bytes blocks
- */
-uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb,
- struct ext4_inode *inode);
-
-/**@brief Set number of 512-bytes blocks used for i-node.
- * @param sb Superblock
- * @param inode I-node
- * @param count Number of 512-bytes blocks
- * @return Error code
- */
-int ext4_inode_set_blocks_count(struct ext4_sblock *sb,
- struct ext4_inode *inode, uint64_t cnt);
-
-/**@brief Get flags (features) of i-node.
- * @param inode I-node to get flags from
- * @return Flags (bitmap)
- */
-uint32_t ext4_inode_get_flags(struct ext4_inode *inode);
-
-/**@brief Set flags (features) of i-node.
- * @param inode I-node to set flags to
- * @param flags Flags to set to i-node
- */
-void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags);
-
-/**@brief Get file generation (used by NFS).
- * @param inode I-node
- * @return File generation
- */
-uint32_t ext4_inode_get_generation(struct ext4_inode *inode);
-
-/**@brief Set file generation (used by NFS).
- * @param inode I-node
- * @param generation File generation
- */
-void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen);
-
-/**@brief Get address of block, where are extended attributes located.
- * @param inode I-node
- * @param sb Superblock
- * @return Block address
- */
-uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode,
- struct ext4_sblock *sb);
-
-/**@brief Set address of block, where are extended attributes located.
- * @param inode I-node
- * @param sb Superblock
- * @param file_acl Block address
- */
-void ext4_inode_set_file_acl(struct ext4_inode *inode, struct ext4_sblock *sb,
- uint64_t acl);
-
-/**@brief Get block address of specified direct block.
- * @param inode I-node to load block from
- * @param idx Index of logical block
- * @return Physical block address
- */
-uint32_t ext4_inode_get_direct_block(struct ext4_inode *inode, uint32_t idx);
-
-/**@brief Set block address of specified direct block.
- * @param inode I-node to set block address to
- * @param idx Index of logical block
- * @param fblock Physical block address
- */
-void ext4_inode_set_direct_block(struct ext4_inode *inode, uint32_t idx,
- uint32_t block);
-
-/**@brief Get block address of specified indirect block.
- * @param inode I-node to get block address from
- * @param idx Index of indirect block
- * @return Physical block address
- */
-uint32_t ext4_inode_get_indirect_block(struct ext4_inode *inode, uint32_t idx);
-
-/**@brief Set block address of specified indirect block.
- * @param inode I-node to set block address to
- * @param idx Index of indirect block
- * @param fblock Physical block address
- */
-void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
- uint32_t block);
-
-/**@brief Check if i-node has specified type.
- * @param sb Superblock
- * @param inode I-node to check type of
- * @param type Type to check
- * @return Result of check operation
- */
-bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
- uint32_t type);
-
-/**@brief Check if i-node has specified flag.
- * @param inode I-node to check flags of
- * @param flag Flag to check
- * @return Result of check operation
- */
-bool ext4_inode_has_flag(struct ext4_inode *inode, uint32_t f);
-
-/**@brief Remove specified flag from i-node.
- * @param inode I-node to clear flag on
- * @param clear_flag Flag to be cleared
- */
-void ext4_inode_clear_flag(struct ext4_inode *inode, uint32_t f);
-
-/**@brief Set specified flag to i-node.
- * @param inode I-node to set flag on
- * @param set_flag Flag to be set
- */
-void ext4_inode_set_flag(struct ext4_inode *inode, uint32_t f);
-
-/**@brief Check if i-node can be truncated.
- * @param sb Superblock
- * @param inode I-node to check
- * @return Result of the check operation
- */
-bool ext4_inode_can_truncate(struct ext4_sblock *sb, struct ext4_inode *inode);
-
-/**@brief Get extent header from the root of the extent tree.
- * @param inode I-node to get extent header from
- * @return Pointer to extent header of the root node
- */
-struct ext4_extent_header *
-ext4_inode_get_extent_header(struct ext4_inode *inode);
-
-#endif /* EXT4_INODE_H_ */
-
-/**
- * @}
- */
+/*
+ * 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_inode.h
+ * @brief Inode handle functions
+ */
+
+#ifndef EXT4_INODE_H_
+#define EXT4_INODE_H_
+
+#include "ext4_config.h"
+
+#include <stdint.h>
+
+/**@brief Get mode of the i-node.
+ * @param sb Superblock
+ * @param inode I-node to load mode from
+ * @return Mode of the i-node
+ */
+uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode);
+
+/**@brief Set mode of the i-node.
+ * @param sb Superblock
+ * @param inode I-node to set mode to
+ * @param mode Mode to set to i-node
+ */
+void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode,
+ uint32_t mode);
+
+/**@brief Get ID of the i-node owner (user id).
+ * @param inode I-node to load uid from
+ * @return User ID of the i-node owner
+ */
+uint32_t ext4_inode_get_uid(struct ext4_inode *inode);
+
+/**@brief Set ID of the i-node owner.
+ * @param inode I-node to set uid to
+ * @param uid ID of the i-node owner
+ */
+void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid);
+
+/**@brief Get real i-node size.
+ * @param sb Superblock
+ * @param inode I-node to load size from
+ * @return Real size of i-node
+ */
+uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode);
+
+/**@brief Set real i-node size.
+ * @param inode I-node to set size to
+ * @param size Size of the i-node
+ */
+void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size);
+
+/**@brief Get time, when i-node was last accessed.
+ * @param inode I-node
+ * @return Time of the last access (POSIX)
+ */
+uint32_t ext4_inode_get_access_time(struct ext4_inode *inode);
+
+/**@brief Set time, when i-node was last accessed.
+ * @param inode I-node
+ * @param time Time of the last access (POSIX)
+ */
+void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time);
+
+/**@brief Get time, when i-node was last changed.
+ * @param inode I-node
+ * @return Time of the last change (POSIX)
+ */
+uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode);
+
+/**@brief Set time, when i-node was last changed.
+ * @param inode I-node
+ * @param time Time of the last change (POSIX)
+ */
+void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time);
+
+/**@brief Get time, when i-node content was last modified.
+ * @param inode I-node
+ * @return Time of the last content modification (POSIX)
+ */
+uint32_t ext4_inode_get_modification_time(struct ext4_inode *inode);
+
+/**@brief Set time, when i-node content was last modified.
+ * @param inode I-node
+ * @param time Time of the last content modification (POSIX)
+ */
+void ext4_inode_set_modification_time(struct ext4_inode *inode, uint32_t time);
+
+/**@brief Get time, when i-node was deleted.
+ * @param inode I-node
+ * @return Time of the delete action (POSIX)
+ */
+uint32_t ext4_inode_get_deletion_time(struct ext4_inode *inode);
+
+/**@brief Set time, when i-node was deleted.
+ * @param inode I-node
+ * @param time Time of the delete action (POSIX)
+ */
+void ext4_inode_set_deletion_time(struct ext4_inode *inode, uint32_t time);
+
+/**@brief Get ID of the i-node owner's group.
+ * @param inode I-node to load gid from
+ * @return Group ID of the i-node owner
+ */
+uint32_t ext4_inode_get_gid(struct ext4_inode *inode);
+
+/**@brief Set ID to the i-node owner's group.
+ * @param inode I-node to set gid to
+ * @param gid Group ID of the i-node owner
+ */
+void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid);
+
+/**@brief Get number of links to i-node.
+ * @param inode I-node to load number of links from
+ * @return Number of links to i-node
+ */
+uint16_t ext4_inode_get_links_count(struct ext4_inode *inode);
+
+/**@brief Set number of links to i-node.
+ * @param inode I-node to set number of links to
+ * @param count Number of links to i-node
+ */
+void ext4_inode_set_links_count(struct ext4_inode *inode, uint16_t cnt);
+
+/**@brief Get number of 512-bytes blocks used for i-node.
+ * @param sb Superblock
+ * @param inode I-node
+ * @return Number of 512-bytes blocks
+ */
+uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb,
+ struct ext4_inode *inode);
+
+/**@brief Set number of 512-bytes blocks used for i-node.
+ * @param sb Superblock
+ * @param inode I-node
+ * @param count Number of 512-bytes blocks
+ * @return Error code
+ */
+int ext4_inode_set_blocks_count(struct ext4_sblock *sb,
+ struct ext4_inode *inode, uint64_t cnt);
+
+/**@brief Get flags (features) of i-node.
+ * @param inode I-node to get flags from
+ * @return Flags (bitmap)
+ */
+uint32_t ext4_inode_get_flags(struct ext4_inode *inode);
+
+/**@brief Set flags (features) of i-node.
+ * @param inode I-node to set flags to
+ * @param flags Flags to set to i-node
+ */
+void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags);
+
+/**@brief Get file generation (used by NFS).
+ * @param inode I-node
+ * @return File generation
+ */
+uint32_t ext4_inode_get_generation(struct ext4_inode *inode);
+
+/**@brief Set file generation (used by NFS).
+ * @param inode I-node
+ * @param generation File generation
+ */
+void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen);
+
+/**@brief Get address of block, where are extended attributes located.
+ * @param inode I-node
+ * @param sb Superblock
+ * @return Block address
+ */
+uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode,
+ struct ext4_sblock *sb);
+
+/**@brief Set address of block, where are extended attributes located.
+ * @param inode I-node
+ * @param sb Superblock
+ * @param file_acl Block address
+ */
+void ext4_inode_set_file_acl(struct ext4_inode *inode, struct ext4_sblock *sb,
+ uint64_t acl);
+
+/**@brief Get block address of specified direct block.
+ * @param inode I-node to load block from
+ * @param idx Index of logical block
+ * @return Physical block address
+ */
+uint32_t ext4_inode_get_direct_block(struct ext4_inode *inode, uint32_t idx);
+
+/**@brief Set block address of specified direct block.
+ * @param inode I-node to set block address to
+ * @param idx Index of logical block
+ * @param fblock Physical block address
+ */
+void ext4_inode_set_direct_block(struct ext4_inode *inode, uint32_t idx,
+ uint32_t block);
+
+/**@brief Get block address of specified indirect block.
+ * @param inode I-node to get block address from
+ * @param idx Index of indirect block
+ * @return Physical block address
+ */
+uint32_t ext4_inode_get_indirect_block(struct ext4_inode *inode, uint32_t idx);
+
+/**@brief Set block address of specified indirect block.
+ * @param inode I-node to set block address to
+ * @param idx Index of indirect block
+ * @param fblock Physical block address
+ */
+void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
+ uint32_t block);
+
+/**@brief Check if i-node has specified type.
+ * @param sb Superblock
+ * @param inode I-node to check type of
+ * @param type Type to check
+ * @return Result of check operation
+ */
+bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
+ uint32_t type);
+
+/**@brief Check if i-node has specified flag.
+ * @param inode I-node to check flags of
+ * @param flag Flag to check
+ * @return Result of check operation
+ */
+bool ext4_inode_has_flag(struct ext4_inode *inode, uint32_t f);
+
+/**@brief Remove specified flag from i-node.
+ * @param inode I-node to clear flag on
+ * @param clear_flag Flag to be cleared
+ */
+void ext4_inode_clear_flag(struct ext4_inode *inode, uint32_t f);
+
+/**@brief Set specified flag to i-node.
+ * @param inode I-node to set flag on
+ * @param set_flag Flag to be set
+ */
+void ext4_inode_set_flag(struct ext4_inode *inode, uint32_t f);
+
+/**@brief Check if i-node can be truncated.
+ * @param sb Superblock
+ * @param inode I-node to check
+ * @return Result of the check operation
+ */
+bool ext4_inode_can_truncate(struct ext4_sblock *sb, struct ext4_inode *inode);
+
+/**@brief Get extent header from the root of the extent tree.
+ * @param inode I-node to get extent header from
+ * @return Pointer to extent header of the root node
+ */
+struct ext4_extent_header *
+ext4_inode_get_extent_header(struct ext4_inode *inode);
+
+#endif /* EXT4_INODE_H_ */
+
+/**
+ * @}
+ */