Reconstruct source directory tree.
authorngkaho1234 <ngkaho1234@gmail.com>
Thu, 28 Jan 2016 14:46:36 +0000 (22:46 +0800)
committerngkaho1234 <ngkaho1234@gmail.com>
Thu, 28 Jan 2016 14:46:36 +0000 (22:46 +0800)
99 files changed:
CMakeLists.txt
include/ext4.h [new file with mode: 0644]
include/ext4_balloc.h [new file with mode: 0644]
include/ext4_bcache.h [new file with mode: 0644]
include/ext4_bitmap.h [new file with mode: 0644]
include/ext4_block_group.h [new file with mode: 0644]
include/ext4_blockdev.h [new file with mode: 0644]
include/ext4_config.h [new file with mode: 0644]
include/ext4_crc32.h [new file with mode: 0644]
include/ext4_debug.h [new file with mode: 0644]
include/ext4_dir.h [new file with mode: 0644]
include/ext4_dir_idx.h [new file with mode: 0644]
include/ext4_errno.h [new file with mode: 0644]
include/ext4_extent.h [new file with mode: 0644]
include/ext4_fs.h [new file with mode: 0644]
include/ext4_hash.h [new file with mode: 0644]
include/ext4_ialloc.h [new file with mode: 0644]
include/ext4_inode.h [new file with mode: 0644]
include/ext4_journal.h [new file with mode: 0644]
include/ext4_mbr.h [new file with mode: 0644]
include/ext4_mkfs.h [new file with mode: 0644]
include/ext4_oflags.h [new file with mode: 0644]
include/ext4_super.h [new file with mode: 0644]
include/ext4_trans.h [new file with mode: 0644]
include/ext4_types.h [new file with mode: 0644]
include/ext4_xattr.h [new file with mode: 0644]
include/misc/queue.h [new file with mode: 0644]
include/misc/tree.h [new file with mode: 0644]
lwext4/CMakeLists.txt [deleted file]
lwext4/ext4.c [deleted file]
lwext4/ext4.h [deleted file]
lwext4/ext4_balloc.c [deleted file]
lwext4/ext4_balloc.h [deleted file]
lwext4/ext4_bcache.c [deleted file]
lwext4/ext4_bcache.h [deleted file]
lwext4/ext4_bitmap.c [deleted file]
lwext4/ext4_bitmap.h [deleted file]
lwext4/ext4_block_group.c [deleted file]
lwext4/ext4_block_group.h [deleted file]
lwext4/ext4_blockdev.c [deleted file]
lwext4/ext4_blockdev.h [deleted file]
lwext4/ext4_config.h [deleted file]
lwext4/ext4_crc32.c [deleted file]
lwext4/ext4_crc32.h [deleted file]
lwext4/ext4_debug.c [deleted file]
lwext4/ext4_debug.h [deleted file]
lwext4/ext4_dir.c [deleted file]
lwext4/ext4_dir.h [deleted file]
lwext4/ext4_dir_idx.c [deleted file]
lwext4/ext4_dir_idx.h [deleted file]
lwext4/ext4_errno.h [deleted file]
lwext4/ext4_extent.c [deleted file]
lwext4/ext4_extent.h [deleted file]
lwext4/ext4_fs.c [deleted file]
lwext4/ext4_fs.h [deleted file]
lwext4/ext4_hash.c [deleted file]
lwext4/ext4_hash.h [deleted file]
lwext4/ext4_ialloc.c [deleted file]
lwext4/ext4_ialloc.h [deleted file]
lwext4/ext4_inode.c [deleted file]
lwext4/ext4_inode.h [deleted file]
lwext4/ext4_journal.c [deleted file]
lwext4/ext4_journal.h [deleted file]
lwext4/ext4_mbr.c [deleted file]
lwext4/ext4_mbr.h [deleted file]
lwext4/ext4_mkfs.c [deleted file]
lwext4/ext4_mkfs.h [deleted file]
lwext4/ext4_oflags.h [deleted file]
lwext4/ext4_super.c [deleted file]
lwext4/ext4_super.h [deleted file]
lwext4/ext4_trans.c [deleted file]
lwext4/ext4_trans.h [deleted file]
lwext4/ext4_types.h [deleted file]
lwext4/ext4_xattr.c [deleted file]
lwext4/ext4_xattr.h [deleted file]
lwext4/misc/queue.h [deleted file]
lwext4/misc/tree.h [deleted file]
src/CMakeLists.txt [new file with mode: 0644]
src/ext4.c [new file with mode: 0644]
src/ext4_balloc.c [new file with mode: 0644]
src/ext4_bcache.c [new file with mode: 0644]
src/ext4_bitmap.c [new file with mode: 0644]
src/ext4_block_group.c [new file with mode: 0644]
src/ext4_blockdev.c [new file with mode: 0644]
src/ext4_crc32.c [new file with mode: 0644]
src/ext4_debug.c [new file with mode: 0644]
src/ext4_dir.c [new file with mode: 0644]
src/ext4_dir_idx.c [new file with mode: 0644]
src/ext4_extent.c [new file with mode: 0644]
src/ext4_fs.c [new file with mode: 0644]
src/ext4_hash.c [new file with mode: 0644]
src/ext4_ialloc.c [new file with mode: 0644]
src/ext4_inode.c [new file with mode: 0644]
src/ext4_journal.c [new file with mode: 0644]
src/ext4_mbr.c [new file with mode: 0644]
src/ext4_mkfs.c [new file with mode: 0644]
src/ext4_super.c [new file with mode: 0644]
src/ext4_trans.c [new file with mode: 0644]
src/ext4_xattr.c [new file with mode: 0644]

index e07c91ea45eb7b98b3e5b42fbafa9718da8d8df6..97fde99947a2a590c36ac3ea488f1abcb691d6c7 100644 (file)
@@ -2,7 +2,7 @@ project(lwext4 C)
 cmake_minimum_required(VERSION 2.8)
 
 
-include_directories(lwext4)
+include_directories(include)
 include_directories(blockdev/filedev)
 include_directories(blockdev/filedev_win)
 
@@ -46,7 +46,7 @@ endif()
 add_subdirectory(blockdev)
 
 #Library build
-add_subdirectory(lwext4)
+add_subdirectory(src)
 #Detect all possible warnings for lwext4 target
 if (NOT CMAKE_COMPILER_IS_GNUCC)
     set_target_properties(lwext4 PROPERTIES COMPILE_FLAGS "")
diff --git a/include/ext4.h b/include/ext4.h
new file mode 100644 (file)
index 0000000..4bf31ac
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "ext4_config.h"
+#include "ext4_errno.h"
+#include "ext4_oflags.h"
+#include "ext4_types.h"
+#include "ext4_blockdev.h"
+
+/********************************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 descriptor. Copy from ext4_types.h*/
+typedef struct ext4_direntry {
+       uint32_t inode;
+       uint16_t entry_length;
+       uint8_t name_length;
+       uint8_t inode_type;
+       uint8_t name[255];
+} ext4_direntry;
+
+typedef struct ext4_dir {
+       /**@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
+ *          Block cache may by created automatically when bc parameter is NULL.
+ * @param   bd block device
+ * @param   bd block device cache
+ * @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   Start journaling. Journaling start/stop functions are transparent
+ *          and might be used on filesystems without journaling support.
+ * @warning Usage:
+ *              ext4_mount("sda1", "/");
+ *              ext4_journal_start("/");
+ *
+ *              //File operations here...
+ *
+ *              ext4_journal_stop("/");
+ *              ext4_umount("/");
+ * @param   mount_point mount name
+ * @return  standard error code */
+int ext4_journal_start(const char *mount_point);
+
+/**@brief   Stop journaling. Journaling start/stop functions are transparent
+ *          and might be used on filesystems without journaling support.
+ * @param   mount_point mount name
+ * @return  standard error code */
+int ext4_journal_stop(const char *mount_point);
+
+/**@brief   Journal recovery.
+ * @param   mount_point mount point
+ * @warning Must be called after @ref ext4_mount
+ * @return standard error code */
+int ext4_recover(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   create a hardlink for a file.
+ * @param   path path to file
+ * @param   hardlink_path path of hardlink
+ * @return  standard error code */
+int ext4_flink(const char *path, const char *hardlink_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   path 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);
+
+/**@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   path fetch inode data of the path
+ * @param   ret_ino the inode id of the path
+ * @param   ext4_inode buffer
+ * @return  standard error code*/
+int ext4_fill_raw_inode(const char *path, uint32_t *ret_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, size_t size, size_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, size_t size, size_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);
+
+/**@brief Change file/directory/link mode bits
+ * @param path to file/dir/link
+ * @param mode new mode bits (for example 0777)
+ * @return  standard error code*/
+int ext4_chmod(const char *path, uint32_t mode);
+
+/**@brief Change file owner and group
+ * @param path to file/dir/link
+ * @param uid user id
+ * @param gid group id
+ * @return  standard error code*/
+int ext4_chown(const char *path, uint32_t uid, uint32_t gid);
+
+/**@brief Set file access time
+ * @param path to file/dir/link
+ * @param atime access timestamp
+ * @return  standard error code*/
+int ext4_file_set_atime(const char *path, uint32_t atime);
+
+/**@brief Set file modify time
+ * @param path to file/dir/link
+ * @param mtime modify timestamp
+ * @return  standard error code*/
+int ext4_file_set_mtime(const char *path, uint32_t mtime);
+
+/**@brief Set file change time
+ * @param path to file/dir/link
+ * @param ctime change timestamp
+ * @return  standard error code*/
+int ext4_file_set_ctime(const char *path, uint32_t ctime);
+
+/**@brief Create symbolic link
+ * @param target destination path
+ * @param path source entry
+ * @return standard error code*/
+int ext4_fsymlink(const char *target, const char *path);
+
+
+/**@brief Read symbolic link payload
+ * @param path to symlink
+ * @param buf output buffer
+ * @param bufsize output buffer max size
+ * @param rcnt bytes read
+ * @return standard error code*/
+int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt);
+
+/**@brief Set extended attribute
+ * @param path to file/directory
+ * @param name name of the entry to add
+ * @param name_len length of @name in bytes
+ * @param data data of the entry to add
+ * @param data_size size of data to add
+ * @param replace whether existing entries should be replaced
+ * @return standard error code*/
+int ext4_setxattr(const char *path, const char *name, size_t name_len,
+                 const void *data, size_t data_size, bool replace);
+
+/**@brief Get extended attribute
+ * @param path to file/directory
+ * @param name name of the entry to get
+ * @param name_len length of @name in bytes
+ * @param data data of the entry to get
+ * @param data_size size of data to get
+ * @return standard error code*/
+int ext4_getxattr(const char *path, const char *name, size_t name_len,
+                 void *buf, size_t buf_size, size_t *data_size);
+
+/**@brief List extended attributes
+ * @param path to file/directory
+ * @param list list to hold the name of entries
+ * @param size size of @list in bytes
+ * @param ret_size used bytes of @list
+ * @return standard error code*/
+int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size);
+
+/**@brief Remove extended attribute
+ * @param path to file/directory
+ * @param name name of the entry to remove
+ * @param name_len length of @name in bytes
+ * @return standard error code*/
+int ext4_removexattr(const char *path, const char *name, size_t name_len);
+
+
+/*********************************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);
+
+/**@brief   Rewine directory entry offset.
+ * @param   d directory handle*/
+void ext4_dir_entry_rewind(ext4_dir *d);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_balloc.h b/include/ext4_balloc.h
new file mode 100644 (file)
index 0000000..f2c3dc9
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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_balloc.h
+ * @brief Physical block allocator.
+ */
+
+#ifndef EXT4_BALLOC_H_
+#define EXT4_BALLOC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**@brief Compute number of block group from block address.
+ * @param sb superblock pointer.
+ * @param baddr Absolute address of block.
+ * @return Block group index
+ */
+uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s,
+                                      ext4_fsblk_t baddr);
+
+/**@brief Compute the starting block address of a block group
+ * @param sb   superblock pointer.
+ * @param bgid block group index
+ * @return Block address
+ */
+ext4_fsblk_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s,
+                                          uint32_t bgid);
+
+/**@brief Calculate and set checksum of block bitmap.
+ * @param sb superblock pointer.
+ * @param bg block group
+ * @param bitmap bitmap buffer
+ */
+void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb,
+                                struct ext4_bgroup *bg,
+                                void *bitmap);
+
+/**@brief   Free block from inode.
+ * @param   inode_ref inode reference
+ * @param   baddr block address
+ * @return  standard error code*/
+int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref,
+                          ext4_fsblk_t baddr);
+
+/**@brief   Free blocks from inode.
+ * @param   inode_ref inode reference
+ * @param   first block address
+ * @param   count block count
+ * @return  standard error code*/
+int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref,
+                           ext4_fsblk_t first, uint32_t count);
+
+/**@brief   Allocate block procedure.
+ * @param   inode_ref inode reference
+ * @param   goal
+ * @param   baddr allocated block address
+ * @return  standard error code*/
+int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref,
+                           ext4_fsblk_t goal,
+                           ext4_fsblk_t *baddr);
+
+/**@brief   Try allocate selected block.
+ * @param   inode_ref inode reference
+ * @param   baddr block address to allocate
+ * @param   free if baddr is not allocated
+ * @return  standard error code*/
+int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref,
+                               ext4_fsblk_t baddr, bool *free);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_BALLOC_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_bcache.h b/include/ext4_bcache.h
new file mode 100644 (file)
index 0000000..bceb75a
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "misc/tree.h"
+#include "misc/queue.h"
+
+#define EXT4_BLOCK_ZERO()      \
+       {.lb_id = 0, .data = 0}
+
+/**@brief   Single block descriptor*/
+struct ext4_block {
+       /**@brief   Logical block ID*/
+       uint64_t lb_id;
+
+       /**@brief   Buffer */
+       struct ext4_buf *buf;
+
+       /**@brief   Data buffer.*/
+       uint8_t *data;
+};
+
+struct ext4_bcache;
+
+/**@brief   Single block descriptor*/
+struct ext4_buf {
+       /**@brief   Flags*/
+       int flags;
+
+       /**@brief   Logical block address*/
+       uint64_t lba;
+
+       /**@brief   Data buffer.*/
+       uint8_t *data;
+
+       /**@brief   LRU priority. (unused) */
+       uint32_t lru_prio;
+
+       /**@brief   LRU id.*/
+       uint32_t lru_id;
+
+       /**@brief   Reference count table*/
+       uint32_t refctr;
+
+       /**@brief   The block cache this buffer belongs to. */
+       struct ext4_bcache *bc;
+
+       /**@brief   Whether or not buffer is on dirty list.*/
+       bool on_dirty_list;
+
+       /**@brief   LBA tree node*/
+       RB_ENTRY(ext4_buf) lba_node;
+
+       /**@brief   LRU tree node*/
+       RB_ENTRY(ext4_buf) lru_node;
+
+       /**@brief   Dirty list node*/
+       SLIST_ENTRY(ext4_buf) dirty_node;
+
+       /**@brief   Callback routine after a disk-write operation.
+        * @param   bc block cache descriptor
+        * @param   buf buffer descriptor
+        * @param   standard error code returned by bdev->bwrite()
+        * @param   arg argument passed to this routine*/
+       void (*end_write)(struct ext4_bcache *bc,
+                         struct ext4_buf *buf,
+                         int res,
+                         void *arg);
+
+       /**@brief   argument passed to end_write() callback.*/
+       void *end_write_arg;
+};
+
+/**@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   Currently referenced datablocks*/
+       uint32_t ref_blocks;
+
+       /**@brief   Maximum referenced datablocks*/
+       uint32_t max_ref_blocks;
+
+       /**@brief   The blockdev binded to this block cache*/
+       struct ext4_blockdev *bdev;
+
+       /**@brief   The cache should not be shaked */
+       bool dont_shake;
+
+       /**@brief   A tree holding all bufs*/
+       RB_HEAD(ext4_buf_lba, ext4_buf) lba_root;
+
+       /**@brief   A tree holding unreferenced bufs*/
+       RB_HEAD(ext4_buf_lru, ext4_buf) lru_root;
+
+       /**@brief   A singly-linked list holding dirty buffers*/
+       SLIST_HEAD(ext4_buf_dirty, ext4_buf) dirty_list;
+};
+
+/**@brief buffer state bits
+ *
+ *  - BC♡UPTODATE: Buffer contains valid data.
+ *  - BC_DIRTY: Buffer is dirty.
+ *  - BC_FLUSH: Buffer will be immediately flushed,
+ *              when no one references it.
+ *  - BC_TMP: Buffer will be dropped once its refctr
+ *            reaches zero.
+ */
+enum bcache_state_bits {
+       BC_UPTODATE,
+       BC_DIRTY,
+       BC_FLUSH,
+       BC_TMP
+};
+
+#define ext4_bcache_set_flag(buf, b)    \
+       (buf)->flags |= 1 << (b)
+
+#define ext4_bcache_clear_flag(buf, b)    \
+       (buf)->flags &= ~(1 << (b))
+
+#define ext4_bcache_test_flag(buf, b)    \
+       (((buf)->flags & (1 << (b))) >> (b))
+
+static inline void ext4_bcache_set_dirty(struct ext4_buf *buf) {
+       ext4_bcache_set_flag(buf, BC_UPTODATE);
+       ext4_bcache_set_flag(buf, BC_DIRTY);
+}
+
+static inline void ext4_bcache_clear_dirty(struct ext4_buf *buf) {
+       ext4_bcache_clear_flag(buf, BC_UPTODATE);
+       ext4_bcache_clear_flag(buf, BC_DIRTY);
+}
+
+/**@brief   Increment reference counter of buf by 1.*/
+#define ext4_bcache_inc_ref(buf) ((buf)->refctr++)
+
+/**@brief   Decrement reference counter of buf by 1.*/
+#define ext4_bcache_dec_ref(buf) ((buf)->refctr--)
+
+/**@brief   Insert buffer to dirty cache list
+ * @param   bc block cache descriptor
+ * @param   buf buffer descriptor */
+static inline void
+ext4_bcache_insert_dirty_node(struct ext4_bcache *bc, struct ext4_buf *buf) {
+       if (!buf->on_dirty_list) {
+               SLIST_INSERT_HEAD(&bc->dirty_list, buf, dirty_node);
+               buf->on_dirty_list = true;
+       }
+}
+
+/**@brief   Remove buffer to dirty cache list
+ * @param   bc block cache descriptor
+ * @param   buf buffer descriptor */
+static inline void
+ext4_bcache_remove_dirty_node(struct ext4_bcache *bc, struct ext4_buf *buf) {
+       if (buf->on_dirty_list) {
+               SLIST_REMOVE(&bc->dirty_list, buf, ext4_buf, dirty_node);
+               buf->on_dirty_list = false;
+       }
+}
+
+
+/**@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   Do cleanup works on block cache.
+ * @param   bc block cache descriptor.*/
+void ext4_bcache_cleanup(struct ext4_bcache *bc);
+
+/**@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   Get a buffer with the lowest LRU counter in bcache.
+ * @param   bc block cache descriptor
+ * @return  buffer with the lowest LRU counter*/
+struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc);
+
+/**@brief   Drop unreferenced buffer from bcache.
+ * @param   bc block cache descriptor
+ * @param   buf buffer*/
+void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf);
+
+/**@brief   Invalidate a range of buffers.
+ * @param   bc block cache descriptor
+ * @param   from starting lba
+ * @param   cnt block counts
+ * @param   buf buffer*/
+void ext4_bcache_invalidate_lba(struct ext4_bcache *bc,
+                               uint64_t from,
+                               uint32_t cnt);
+
+/**@brief   Find existing buffer 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   lba logical block address
+ * @return  block cache buffer */
+struct ext4_buf *
+ext4_bcache_find_get(struct ext4_bcache *bc, struct ext4_block *b,
+                    uint64_t lba);
+
+/**@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
+ * @return  standard error code*/
+int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b);
+
+/**@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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_BCACHE_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_bitmap.h b/include/ext4_bitmap.h
new file mode 100644 (file)
index 0000000..cb73b76
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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_bitmap.h
+ * @brief Block/inode bitmap allocator.
+ */
+
+#ifndef EXT4_BITMAP_H_
+#define EXT4_BITMAP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**@brief   Set bitmap bit.
+ * @param   bmap bitmap
+ * @param   bit bit to set*/
+static inline void ext4_bmap_bit_set(uint8_t *bmap, uint32_t bit)
+{
+       *(bmap + (bit >> 3)) |= (1 << (bit & 7));
+}
+
+/**@brief   Clear bitmap bit.
+ * @param   bmap bitmap buffer
+ * @param   bit bit to clear*/
+static inline void ext4_bmap_bit_clr(uint8_t *bmap, uint32_t bit)
+{
+       *(bmap + (bit >> 3)) &= ~(1 << (bit & 7));
+}
+
+/**@brief   Check if the bitmap bit is set.
+ * @param   bmap bitmap buffer
+ * @param   bit bit to check*/
+static inline bool ext4_bmap_is_bit_set(uint8_t *bmap, uint32_t bit)
+{
+       return (*(bmap + (bit >> 3)) & (1 << (bit & 7)));
+}
+
+/**@brief   Check if the bitmap bit is clear.
+ * @param   bmap bitmap buffer
+ * @param   bit bit to check*/
+static inline bool ext4_bmap_is_bit_clr(uint8_t *bmap, uint32_t bit)
+{
+       return !ext4_bmap_is_bit_set(bmap, bit);
+}
+
+/**@brief   Free range of bits in bitmap.
+ * @param   bmap bitmap buffer
+ * @param   sbit start bit
+ * @param   bcnt bit count*/
+void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt);
+
+/**@brief   Find first clear bit in bitmap.
+ * @param   sbit start bit of search
+ * @param   ebit end bit of search
+ * @param   bit_id output parameter (first free bit)
+ * @return  standard error code*/
+int ext4_bmap_bit_find_clr(uint8_t *bmap, uint32_t sbit, uint32_t ebit,
+                          uint32_t *bit_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_BITMAP_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_block_group.h b/include/ext4_block_group.h
new file mode 100644 (file)
index 0000000..715967e
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * 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_block_group.h
+ * @brief Block group function set.
+ */
+
+#ifndef EXT4_BLOCK_GROUP_H_
+#define EXT4_BLOCK_GROUP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_super.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**@brief Get address of block with data block bitmap.
+ * @param bg pointer to block group
+ * @param s pointer to superblock
+ * @return Address of block with block bitmap
+ */
+static inline uint64_t ext4_bg_get_block_bitmap(struct ext4_bgroup *bg,
+                                               struct ext4_sblock *s)
+{
+       uint64_t v = to_le32(bg->block_bitmap_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint64_t)to_le32(bg->block_bitmap_hi) << 32;
+
+       return v;
+}
+
+/**@brief Set address of block with data block bitmap.
+ * @param bg pointer to block group
+ * @param s pointer to superblock
+ * @param blk block to set
+ * @return Address of block with block bitmap
+ */
+static inline void ext4_bg_set_block_bitmap(struct ext4_bgroup *bg,
+                                           struct ext4_sblock *s, uint64_t blk)
+{
+
+       bg->block_bitmap_lo = to_le32((uint32_t)blk);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->block_bitmap_hi = to_le32(blk >> 32);
+
+}
+
+/**@brief Get address of block with i-node bitmap.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @return Address of block with i-node bitmap
+ */
+static inline uint64_t ext4_bg_get_inode_bitmap(struct ext4_bgroup *bg,
+                                               struct ext4_sblock *s)
+{
+
+       uint64_t v = to_le32(bg->inode_bitmap_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint64_t)to_le32(bg->inode_bitmap_hi) << 32;
+
+       return v;
+}
+
+/**@brief Set address of block with i-node bitmap.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @param blk block to set
+ * @return Address of block with i-node bitmap
+ */
+static inline void ext4_bg_set_inode_bitmap(struct ext4_bgroup *bg,
+                                           struct ext4_sblock *s, uint64_t blk)
+{
+       bg->inode_bitmap_lo = to_le32((uint32_t)blk);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->inode_bitmap_hi = to_le32(blk >> 32);
+
+}
+
+
+/**@brief Get address of the first block of the i-node table.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @return Address of first block of i-node table
+ */
+static inline uint64_t
+ext4_bg_get_inode_table_first_block(struct ext4_bgroup *bg,
+                                   struct ext4_sblock *s)
+{
+       uint64_t v = to_le32(bg->inode_table_first_block_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint64_t)to_le32(bg->inode_table_first_block_hi) << 32;
+
+       return v;
+}
+
+/**@brief Set address of the first block of the i-node table.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @param blk block to set
+ * @return Address of first block of i-node table
+ */
+static inline void
+ext4_bg_set_inode_table_first_block(struct ext4_bgroup *bg,
+                                   struct ext4_sblock *s, uint64_t blk)
+{
+       bg->inode_table_first_block_lo = to_le32((uint32_t)blk);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->inode_table_first_block_hi = to_le32(blk >> 32);
+}
+
+/**@brief Get number of free blocks in block group.
+ * @param bg Pointer to block group
+ * @param sb Pointer to superblock
+ * @return Number of free blocks in block group
+ */
+static inline uint32_t ext4_bg_get_free_blocks_count(struct ext4_bgroup *bg,
+                                                    struct ext4_sblock *s)
+{
+       uint32_t v = to_le16(bg->free_blocks_count_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint32_t)to_le16(bg->free_blocks_count_hi) << 16;
+
+       return v;
+}
+
+/**@brief Set number of free blocks in block group.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @param cnt Number of free blocks in block group
+ */
+static inline void ext4_bg_set_free_blocks_count(struct ext4_bgroup *bg,
+                                                struct ext4_sblock *s,
+                                                uint32_t cnt)
+{
+       bg->free_blocks_count_lo = to_le16((cnt << 16) >> 16);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->free_blocks_count_hi = to_le16(cnt >> 16);
+}
+
+/**@brief Get number of free i-nodes in block group.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @return Number of free i-nodes in block group
+ */
+static inline uint32_t ext4_bg_get_free_inodes_count(struct ext4_bgroup *bg,
+                                                    struct ext4_sblock *s)
+{
+       uint32_t v = to_le16(bg->free_inodes_count_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint32_t)to_le16(bg->free_inodes_count_hi) << 16;
+
+       return v;
+}
+
+/**@brief Set number of free i-nodes in block group.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @param cnt Number of free i-nodes in block group
+ */
+static inline void ext4_bg_set_free_inodes_count(struct ext4_bgroup *bg,
+                                                struct ext4_sblock *s,
+                                                uint32_t cnt)
+{
+       bg->free_inodes_count_lo = to_le16((cnt << 16) >> 16);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->free_inodes_count_hi = to_le16(cnt >> 16);
+}
+
+/**@brief Get number of used directories in block group.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @return Number of used directories in block group
+ */
+static inline uint32_t ext4_bg_get_used_dirs_count(struct ext4_bgroup *bg,
+                                                  struct ext4_sblock *s)
+{
+       uint32_t v = to_le16(bg->used_dirs_count_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint32_t)to_le16(bg->used_dirs_count_hi) << 16;
+
+       return v;
+}
+
+/**@brief Set number of used directories in block group.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @param cnt Number of used directories in block group
+ */
+static inline void ext4_bg_set_used_dirs_count(struct ext4_bgroup *bg,
+                                              struct ext4_sblock *s,
+                                              uint32_t cnt)
+{
+       bg->used_dirs_count_lo = to_le16((cnt << 16) >> 16);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->used_dirs_count_hi = to_le16(cnt >> 16);
+}
+
+/**@brief Get number of unused i-nodes.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @return Number of unused i-nodes
+ */
+static inline uint32_t ext4_bg_get_itable_unused(struct ext4_bgroup *bg,
+                                                struct ext4_sblock *s)
+{
+
+       uint32_t v = to_le16(bg->itable_unused_lo);
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               v |= (uint32_t)to_le16(bg->itable_unused_hi) << 16;
+
+       return v;
+}
+
+/**@brief Set number of unused i-nodes.
+ * @param bg Pointer to block group
+ * @param s Pointer to superblock
+ * @param cnt Number of unused i-nodes
+ */
+static inline void ext4_bg_set_itable_unused(struct ext4_bgroup *bg,
+                                            struct ext4_sblock *s,
+                                            uint32_t cnt)
+{
+       bg->itable_unused_lo = to_le16((cnt << 16) >> 16);
+       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->itable_unused_hi = to_le16(cnt >> 16);
+}
+
+/**@brief  Set checksum of block group.
+ * @param bg Pointer to block group
+ * @param crc Cheksum of block group
+ */
+static inline void ext4_bg_set_checksum(struct ext4_bgroup *bg, uint16_t crc)
+{
+       bg->checksum = to_le16(crc);
+}
+
+/**@brief Check if block group has a flag.
+ * @param bg Pointer to block group
+ * @param flag Flag to be checked
+ * @return True if flag is set to 1
+ */
+static inline bool ext4_bg_has_flag(struct ext4_bgroup *bg, uint32_t f)
+{
+       return to_le16(bg->flags) & f;
+}
+
+/**@brief Set flag of block group.
+ * @param bg Pointer to block group
+ * @param flag Flag to be set
+ */
+static inline void ext4_bg_set_flag(struct ext4_bgroup *bg, uint32_t f)
+{
+       uint16_t flags = to_le16(bg->flags);
+       flags |= f;
+       bg->flags = to_le16(flags);
+}
+
+/**@brief Clear flag of block group.
+ * @param bg Pointer to block group
+ * @param flag Flag to be cleared
+ */
+static inline void ext4_bg_clear_flag(struct ext4_bgroup *bg, uint32_t f)
+{
+       uint16_t flags = to_le16(bg->flags);
+       flags &= ~f;
+       bg->flags = to_le16(flags);
+}
+
+/**@brief Calculate CRC16 of the block group.
+ * @param crc Init value
+ * @param buffer Input buffer
+ * @param len Sizeof input buffer
+ * @return Computed CRC16*/
+uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len);
+
+#endif /* EXT4_BLOCK_GROUP_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_blockdev.h b/include/ext4_blockdev.h
new file mode 100644 (file)
index 0000000..f5329ec
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * 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_blockdev.h
+ * @brief Block device module.
+ */
+
+#ifndef EXT4_BLOCKDEV_H_
+#define EXT4_BLOCKDEV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_bcache.h"
+#include "ext4_trans.h"
+#include "ext4_debug.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct ext4_blockdev_iface {
+       /**@brief   Open device function
+        * @param   bdev block device.*/
+       int (*open)(struct ext4_blockdev *bdev);
+
+       /**@brief   Block read function.
+        * @param   bdev block device
+        * @param   buf output buffer
+        * @param   blk_id block id
+        * @param   blk_cnt block count*/
+       int (*bread)(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
+                    uint32_t blk_cnt);
+
+       /**@brief   Block write function.
+        * @param   buf input buffer
+        * @param   blk_id block id
+        * @param   blk_cnt block count*/
+       int (*bwrite)(struct ext4_blockdev *bdev, const void *buf,
+                     uint64_t blk_id, uint32_t blk_cnt);
+
+       /**@brief   Close device function.
+        * @param   bdev block device.*/
+       int (*close)(struct ext4_blockdev *bdev);
+
+       /**@brief   Lock block device. Required in multi partition mode
+        *          operations. Not mandatory field.
+        * @param   bdev block device.*/
+       int (*lock)(struct ext4_blockdev *bdev);
+
+       /**@brief   Unlock block device. Required in multi partition mode
+        *          operations. Not mandatory field.
+        * @param   bdev block device.*/
+       int (*unlock)(struct ext4_blockdev *bdev);
+
+       /**@brief   Block size (bytes): physical*/
+       uint32_t ph_bsize;
+
+       /**@brief   Block count: physical*/
+       uint64_t ph_bcnt;
+
+       /**@brief   Block size buffer: physical*/
+       uint8_t *ph_bbuf;
+
+       /**@brief   Reference counter to block device interface*/
+       uint32_t ph_refctr;
+
+       /**@brief   Physical read counter*/
+       uint32_t bread_ctr;
+
+       /**@brief   Physical write counter*/
+       uint32_t bwrite_ctr;
+};
+
+/**@brief   Definition of the simple block device.*/
+struct ext4_blockdev {
+       /**@brief Block device interface*/
+       struct ext4_blockdev_iface *bdif;
+
+       /**@brief Offset in bdif. For multi partition mode.*/
+       uint64_t part_offset;
+
+       /**@brief Part size in bdif. For multi partition mode.*/
+       uint64_t part_size;
+
+       /**@brief   Block cache.*/
+       struct ext4_bcache *bc;
+
+       /**@brief   Block size (bytes) logical*/
+       uint32_t lg_bsize;
+
+       /**@brief   Block count: logical*/
+       uint64_t lg_bcnt;
+
+       /**@brief   Cache write back mode reference counter*/
+       uint32_t cache_write_back;
+
+       /**@brief   The filesystem this block device belongs to. */
+       struct ext4_fs *fs;
+};
+
+/**@brief   Static initialization of the block device.*/
+#define EXT4_BLOCKDEV_STATIC_INSTANCE(__name, __bsize, __bcnt, __open, __bread,\
+                                     __bwrite, __close, __lock, __unlock)     \
+       static uint8_t __name##_ph_bbuf[(__bsize)];                            \
+       static struct ext4_blockdev_iface __name##_iface = {                   \
+               .open = __open,                                                \
+               .bread = __bread,                                              \
+               .bwrite = __bwrite,                                            \
+               .close = __close,                                              \
+               .lock = __lock,                                                \
+               .unlock = __unlock,                                            \
+               .ph_bsize = __bsize,                                           \
+               .ph_bcnt = __bcnt,                                             \
+               .ph_bbuf = __name##_ph_bbuf,                                   \
+       };                                                                     \
+       static struct ext4_blockdev __name = {                                 \
+               .bdif = &__name##_iface,                                       \
+               .part_offset = 0,                                              \
+               .part_size =  (__bcnt) * (__bsize),                            \
+       }
+
+/**@brief   Block device initialization.
+ * @param   bdev block device descriptor
+ * @param   bg_bsize logical block size
+ * @param   bdev block device descriptor
+ * @return  standard error code*/
+int ext4_block_init(struct ext4_blockdev *bdev);
+
+/**@brief   Binds a bcache to block device.
+ * @param   bdev block device descriptor
+ * @param   bc block cache descriptor
+ * @return  standard error code*/
+int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc);
+
+/**@brief   Close block device
+ * @param   bdev block device descriptor
+ * @return  standard error code*/
+int ext4_block_fini(struct ext4_blockdev *bdev);
+
+/**@brief   Flush data in given buffer to disk.
+ * @param   bdev block device descriptor
+ * @param   buf buffer
+ * @return  standard error code*/
+int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf);
+
+/**@brief   Flush data in buffer of given lba to disk,
+ *          if that buffer exists in block cache.
+ * @param   bdev block device descriptor
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_block_flush_lba(struct ext4_blockdev *bdev, uint64_t lba);
+
+/**@brief   Set logical block size in block device.
+ * @param   bdev block device descriptor
+ * @param   lb_size logical block size (in bytes)
+ * @return  standard error code*/
+void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint64_t lb_bsize);
+
+/**@brief   Block get function (through cache, don't read).
+ * @param   bdev block device descriptor
+ * @param   b block descriptor
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b,
+                         uint64_t lba);
+
+/**@brief   Block get function (through cache).
+ * @param   bdev block device descriptor
+ * @param   b block descriptor
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b,
+                  uint64_t lba);
+
+/**@brief   Block set procedure (through cache).
+ * @param   bdev block device descriptor
+ * @param   b block descriptor
+ * @return  standard error code*/
+int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b);
+
+/**@brief   Block read procedure (without cache)
+ * @param   bdev block device descriptor
+ * @param   buf output buffer
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba,
+                          uint32_t cnt);
+
+/**@brief   Block write procedure (without cache)
+ * @param   bdev block device descriptor
+ * @param   buf output buffer
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf,
+                          uint64_t lba, uint32_t cnt);
+
+/**@brief   Write to block device (by direct address).
+ * @param   bdev block device descriptor
+ * @param   off byte offset in block device
+ * @param   buf input buffer
+ * @param   len length of the write buffer
+ * @return  standard error code*/
+int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off,
+                         const void *buf, uint32_t len);
+
+/**@brief   Read freom block device (by direct address).
+ * @param   bdev block device descriptor
+ * @param   off byte offset in block device
+ * @param   buf input buffer
+ * @param   len length of the write buffer
+ * @return  standard error code*/
+int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf,
+                        uint32_t len);
+
+/**@brief   Flush all dirty buffers to disk
+ * @param   bdev block device descriptor
+ * @return  standard error code*/
+int ext4_block_cache_flush(struct ext4_blockdev *bdev);
+
+/**@brief   Enable/disable write back cache mode
+ * @param   bdev block device descriptor
+ * @param   on_off
+ *              !0 - ENABLE
+ *               0 - DISABLE (all delayed cache buffers will be flushed)
+ * @return  standard error code*/
+int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_BLOCKDEV_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_config.h b/include/ext4_config.h
new file mode 100644 (file)
index 0000000..3d856eb
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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_config.h
+ * @brief Configuration file.
+ */
+
+#ifndef EXT4_CONFIG_H_
+#define EXT4_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CONFIG_HAVE_OWN_CFG
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+
+#define F_SET_EXT2 2
+#define F_SET_EXT3 3
+#define F_SET_EXT4 4
+
+#ifndef CONFIG_EXT_FEATURE_SET_LVL
+#define CONFIG_EXT_FEATURE_SET_LVL F_SET_EXT4
+#endif
+
+/*****************************************************************************/
+
+#if CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT2
+/*Superblock features flag EXT2*/
+#define CONFIG_SUPPORTED_FCOM EXT2_SUPPORTED_FCOM
+#define CONFIG_SUPPORTED_FINCOM (EXT2_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
+#define CONFIG_SUPPORTED_FRO_COM EXT2_SUPPORTED_FRO_COM
+
+#elif CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT3
+/*Superblock features flag EXT3*/
+#define CONFIG_SUPPORTED_FCOM EXT3_SUPPORTED_FCOM
+#define CONFIG_SUPPORTED_FINCOM (EXT3_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
+#define CONFIG_SUPPORTED_FRO_COM EXT3_SUPPORTED_FRO_COM
+#elif CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT4
+/*Superblock features flag EXT4*/
+#define CONFIG_SUPPORTED_FCOM EXT4_SUPPORTED_FCOM
+#define CONFIG_SUPPORTED_FINCOM (EXT4_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
+#define CONFIG_SUPPORTED_FRO_COM EXT4_SUPPORTED_FRO_COM
+#else
+#define "Unsupported CONFIG_EXT_FEATURE_SET_LVL"
+#endif
+
+#define CONFIG_DIR_INDEX_ENABLE (CONFIG_SUPPORTED_FCOM & EXT4_FCOM_DIR_INDEX)
+#define CONFIG_EXTENT_ENABLE (CONFIG_SUPPORTED_FINCOM & EXT4_FINCOM_EXTENTS)
+#define CONFIG_META_CSUM_ENABLE (CONFIG_SUPPORTED_FRO_COM & EXT4_FRO_COM_METADATA_CSUM)
+
+/*****************************************************************************/
+
+/**@brief  Enable/disable journaling*/
+#ifndef CONFIG_JOURNALING_ENABLE
+#define CONFIG_JOURNALING_ENABLE 1
+#endif
+
+/**@brief   Enable directory indexing comb sort*/
+#ifndef CONFIG_DIR_INDEX_COMB_SORT
+#define CONFIG_DIR_INDEX_COMB_SORT 1
+#endif
+
+/**@brief   Include error codes from ext4_errno or standard library.*/
+#ifndef CONFIG_HAVE_OWN_ERRNO
+#define CONFIG_HAVE_OWN_ERRNO 0
+#endif
+
+/**@brief   Debug printf enable (stdout)*/
+#ifndef CONFIG_DEBUG_PRINTF
+#define CONFIG_DEBUG_PRINTF 1
+#endif
+
+/**@brief   Assert printf enable (stdout)*/
+#ifndef CONFIG_DEBUG_ASSERT
+#define CONFIG_DEBUG_ASSERT 1
+#endif
+
+/**@brief   Include assert codes from ext4_debug or standard library.*/
+#ifndef CONFIG_HAVE_OWN_ASSERT
+#define CONFIG_HAVE_OWN_ASSERT 1
+#endif
+
+/**@brief   Statistics of block device*/
+#ifndef CONFIG_BLOCK_DEV_ENABLE_STATS
+#define CONFIG_BLOCK_DEV_ENABLE_STATS 1
+#endif
+
+/**@brief   Cache size of block device.*/
+#ifndef CONFIG_BLOCK_DEV_CACHE_SIZE
+#define CONFIG_BLOCK_DEV_CACHE_SIZE 8
+#endif
+
+/**@brief   Maximum block device count*/
+#ifndef CONFIG_EXT4_BLOCKDEVS_COUNT
+#define CONFIG_EXT4_BLOCKDEVS_COUNT 2
+#endif
+
+/**@brief   Maximum mountpoint count*/
+#ifndef CONFIG_EXT4_MOUNTPOINTS_COUNT
+#define CONFIG_EXT4_MOUNTPOINTS_COUNT 2
+#endif
+
+/**@brief   Include open flags from ext4_errno or standard library.*/
+#ifndef CONFIG_HAVE_OWN_OFLAGS
+#define CONFIG_HAVE_OWN_OFLAGS 1
+#endif
+
+/**@brief Maximum single truncate size. Transactions must be limited to reduce
+ *        number of allocetions for single transaction*/
+#ifndef CONFIG_MAX_TRUNCATE_SIZE
+#define CONFIG_MAX_TRUNCATE_SIZE (16ul * 1024ul * 1024ul)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_CONFIG_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_crc32.h b/include/ext4_crc32.h
new file mode 100644 (file)
index 0000000..3dad1d1
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * All rights reserved.
+ *
+ * Based on FreeBSD.
+ *
+ * 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_crc32.h
+ * @brief Crc32c routine. Taken from FreeBSD kernel.
+ */
+
+#ifndef LWEXT4_EXT4_CRC32C_H_
+#define LWEXT4_EXT4_CRC32C_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+
+#include <stdint.h>
+
+/**@brief      CRC32 algorithm.
+ * @param      crc input feed
+ * @param      buf input buffer
+ * @param      size input buffer length (bytes)
+ * @return     updated crc32 value*/
+uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size);
+
+/**@brief      CRC32C algorithm.
+ * @param      crc input feed
+ * @param      buf input buffer
+ * @param      length input buffer length (bytes)
+ * @return     updated crc32c value*/
+uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWEXT4_EXT4_CRC32C_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_debug.h b/include/ext4_debug.h
new file mode 100644 (file)
index 0000000..2afc39c
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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_debug.c
+ * @brief Debug printf and assert macros.
+ */
+
+#ifndef EXT4_DEBUG_H_
+#define EXT4_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_errno.h"
+
+#if !CONFIG_HAVE_OWN_ASSERT
+#include <assert.h>
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif
+
+#ifndef PRId64
+#define PRId64 "lld"
+#endif
+
+
+#define DEBUG_BALLOC (1ul << 0)
+#define DEBUG_BCACHE (1ul << 1)
+#define DEBUG_BITMAP (1ul << 2)
+#define DEBUG_BLOCK_GROUP (1ul << 3)
+#define DEBUG_BLOCKDEV (1ul << 4)
+#define DEBUG_DIR_IDX (1ul << 5)
+#define DEBUG_DIR (1ul << 6)
+#define DEBUG_EXTENT (1ul << 7)
+#define DEBUG_FS (1ul << 8)
+#define DEBUG_HASH (1ul << 9)
+#define DEBUG_IALLOC (1ul << 10)
+#define DEBUG_INODE (1ul << 11)
+#define DEBUG_SUPER (1ul << 12)
+#define DEBUG_XATTR (1ul << 13)
+#define DEBUG_MKFS (1ul << 14)
+#define DEBUG_EXT4 (1ul << 15)
+#define DEBUG_JBD (1ul << 16)
+#define DEBUG_MBR (1ul << 17)
+
+#define DEBUG_NOPREFIX (1ul << 31)
+#define DEBUG_ALL (0xFFFFFFFF)
+
+static inline const char *ext4_dmask_id2str(uint32_t m)
+{
+       switch(m) {
+       case DEBUG_BALLOC:
+               return "ext4_balloc: ";
+       case DEBUG_BCACHE:
+               return "ext4_bcache: ";
+       case DEBUG_BITMAP:
+               return "ext4_bitmap: ";
+       case DEBUG_BLOCK_GROUP:
+               return "ext4_block_group: ";
+       case DEBUG_BLOCKDEV:
+               return "ext4_blockdev: ";
+       case DEBUG_DIR_IDX:
+               return "ext4_dir_idx: ";
+       case DEBUG_DIR:
+               return "ext4_dir: ";
+       case DEBUG_EXTENT:
+               return "ext4_extent: ";
+       case DEBUG_FS:
+               return "ext4_fs: ";
+       case DEBUG_HASH:
+               return "ext4_hash: ";
+       case DEBUG_IALLOC:
+               return "ext4_ialloc: ";
+       case DEBUG_INODE:
+               return "ext4_inode: ";
+       case DEBUG_SUPER:
+               return "ext4_super: ";
+       case DEBUG_XATTR:
+               return "ext4_xattr: ";
+       case DEBUG_MKFS:
+               return "ext4_mkfs: ";
+       case DEBUG_JBD:
+               return "ext4_jbd: ";
+       case DEBUG_MBR:
+               return "ext4_mbr: ";
+       case DEBUG_EXT4:
+               return "ext4: ";
+       }
+       return "";
+}
+#define DBG_NONE  ""
+#define DBG_INFO  "[info]  "
+#define DBG_WARN  "[warn]  "
+#define DBG_ERROR "[error] "
+
+/**@brief   Global mask debug set.
+ * @brief   m new debug mask.*/
+void ext4_dmask_set(uint32_t m);
+
+/**@brief   Global mask debug clear.
+ * @brief   m new debug mask.*/
+void ext4_dmask_clr(uint32_t m);
+
+/**@brief   Global debug mask get.
+ * @return  debug mask*/
+uint32_t ext4_dmask_get(void);
+
+#if CONFIG_DEBUG_PRINTF
+/**@brief   Debug printf.*/
+#define ext4_dbg(m, ...)                                                       \
+       do {                                                                   \
+               if ((m) & ext4_dmask_get()) {                                  \
+                       if (!((m) & DEBUG_NOPREFIX)) {                         \
+                               printf("%s", ext4_dmask_id2str(m));            \
+                               printf("l: %d   ", __LINE__);                  \
+                       }                                                      \
+                       printf(__VA_ARGS__);                                   \
+                       fflush(stdout);                                        \
+               }                                                              \
+       } while (0)
+#else
+#define ext4_dbg(m, ...) do { } while (0)
+#endif
+
+#if CONFIG_DEBUG_ASSERT
+/**@brief   Debug assertion.*/
+#if CONFIG_HAVE_OWN_ASSERT
+#define ext4_assert(_v)                                                        \
+       do {                                                                   \
+               if (!(_v)) {                                                   \
+                       printf("assertion failed:\nfile: %s\nline: %d\n",      \
+                              __FILE__, __LINE__);                            \
+                              while (1)                                       \
+                                      ;                                       \
+               }                                                              \
+       } while (0)
+#else
+#define ext4_assert(_v) assert(_v)
+#endif
+#else
+#define ext4_assert(_v) ((void)(_v))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_DEBUG_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_dir.h b/include/ext4_dir.h
new file mode 100644 (file)
index 0000000..37547ea
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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_en_get_inode(struct ext4_dir_en *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_en_set_inode(struct ext4_dir_en *de, uint32_t inode)
+{
+       de->inode = to_le32(inode);
+}
+
+/**@brief Set i-node number to directory entry. (For HTree root)
+ * @param de Directory entry
+ * @param inode I-node number
+ */
+static inline void
+ext4_dx_dot_en_set_inode(struct ext4_dir_idx_dot_en *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_en_get_entry_len(struct ext4_dir_en *de)
+{
+       return to_le16(de->entry_len);
+}
+
+/**@brief Set directory entry length.
+ * @param de     Directory entry
+ * @param length Entry length
+ */
+static inline void ext4_dir_en_set_entry_len(struct ext4_dir_en *de, uint16_t l)
+{
+       de->entry_len = to_le16(l);
+}
+
+/**@brief Get directory entry name length.
+ * @param sb Superblock
+ * @param de Directory entry
+ * @return Entry name length
+ */
+static inline uint16_t ext4_dir_en_get_name_len(struct ext4_sblock *sb,
+                                               struct ext4_dir_en *de)
+{
+       uint16_t v = de->name_len;
+
+       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_en_set_name_len(struct ext4_sblock *sb,
+                                           struct ext4_dir_en *de,
+                                           uint16_t len)
+{
+       de->name_len = (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_en_get_inode_type(struct ext4_sblock *sb,
+                                                struct ext4_dir_en *de)
+{
+       if ((ext4_get32(sb, rev_level) > 0) ||
+           (ext4_get32(sb, minor_rev_level) >= 5))
+               return de->in.inode_type;
+
+       return EXT4_DE_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_en_set_inode_type(struct ext4_sblock *sb,
+                                             struct ext4_dir_en *de, uint8_t t)
+{
+       if ((ext4_get32(sb, rev_level) > 0) ||
+           (ext4_get32(sb, minor_rev_level) >= 5))
+               de->in.inode_type = t;
+}
+
+/**@brief Verify checksum of a linear directory leaf block
+ * @param inode_ref Directory i-node
+ * @param dirent    Linear directory leaf block
+ * @return true means the block passed checksum verification
+ */
+bool ext4_dir_csum_verify(struct ext4_inode_ref *inode_ref,
+                         struct ext4_dir_en *dirent);
+
+/**@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_dir_iter *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_dir_iter *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_dir_iter *it);
+
+/**@brief Write directory entry to concrete data block.
+ * @param sb        Superblock
+ * @param en     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_dir_en *en,
+                         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_dir_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 inode_ref    Directory i-node
+ * @param dst_blk      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_inode_ref *inode_ref,
+                             struct ext4_block *dst_blk,
+                             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_dir_en **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_dir_search_result *result);
+
+void ext4_dir_set_csum(struct ext4_inode_ref *inode_ref,
+                      struct ext4_dir_en *dirent);
+
+
+void ext4_dir_init_entry_tail(struct ext4_dir_entry_tail *t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_DIR_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_dir_idx.h b/include/ext4_dir_idx.h
new file mode 100644 (file)
index 0000000..dd0067d
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define EXT4_DIR_DX_INIT_BCNT 2
+
+/**@brief Initialize index structure of new directory.
+ * @param dir Pointer to directory i-node
+ * @param dir Pointer to parent directory i-node
+ * @return Error code
+ */
+int ext4_dir_dx_init(struct ext4_inode_ref *dir,
+                    struct ext4_inode_ref *parent);
+
+/**@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_dir_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);
+
+/**@brief Add new entry to indexed directory
+ * @param dir           Directory i-node
+ * @param parent_inode  parent inode index
+ * @return Error code
+ */
+int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir,
+                                   uint32_t parent_inode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_DIR_IDX_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_errno.h b/include/ext4_errno.h
new file mode 100644 (file)
index 0000000..edf89a9
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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_errno.h
+ * @brief Error codes.
+ */
+#ifndef EXT4_ERRNO_H_
+#define EXT4_ERRNO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+
+#if !CONFIG_HAVE_OWN_ERRNO
+#include <errno.h>
+#else
+#define EPERM 1      /* Operation not permitted */
+#define ENOENT 2     /* No such file or directory */
+#define EIO 5        /* I/O error */
+#define ENXIO 6      /* No such device or address */
+#define E2BIG 7      /* Argument list too long */
+#define ENOMEM 12    /* Out of memory */
+#define EACCES 13    /* Permission denied */
+#define EFAULT 14    /* Bad address */
+#define EEXIST 17    /* File exists */
+#define ENODEV 19    /* No such device */
+#define ENOTDIR 20   /* Not a directory */
+#define EISDIR 21    /* Is a directory */
+#define EINVAL 22    /* Invalid argument */
+#define EFBIG 27     /* File too large */
+#define ENOSPC 28    /* No space left on device */
+#define EROFS 30     /* Read-only file system */
+#define EMLINK 31    /* Too many links */
+#define ERANGE 34    /* Math result not representable */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ENODATA 61   /* No data available */
+#define ENOTSUP 95   /* Not supported */
+#endif
+
+#ifndef ENODATA
+ #ifdef ENOATTR
+ #define ENODATA ENOATTR
+ #else
+ #define ENODATA 61
+ #endif
+#endif
+
+#ifndef ENOTSUP
+#define ENOTSUP 95
+#endif
+
+#ifndef EOK
+#define EOK 0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_ERRNO_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_extent.h b/include/ext4_extent.h
new file mode 100644 (file)
index 0000000..1d4ddf1
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_inode.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 */
+static inline uint32_t ext4_extent_get_first_block(struct ext4_extent *extent)
+{
+       return to_le32(extent->first_block);
+}
+
+/**@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 */
+static inline void ext4_extent_set_first_block(struct ext4_extent *extent,
+               uint32_t iblock)
+{
+       extent->first_block = to_le32(iblock);
+}
+
+/**@brief Get number of blocks covered by extent.
+ * @param extent Extent to load count from
+ * @return Number of blocks covered by extent */
+static inline uint16_t ext4_extent_get_block_count(struct ext4_extent *extent)
+{
+       if (EXT4_EXT_IS_UNWRITTEN(extent))
+               return EXT4_EXT_GET_LEN_UNWRITTEN(extent);
+       else
+               return EXT4_EXT_GET_LEN(extent);
+}
+/**@brief Set number of blocks covered by extent.
+ * @param extent Extent to load count from
+ * @param count  Number of blocks covered by extent
+ * @param unwritten Whether the extent is unwritten or not */
+static inline void ext4_extent_set_block_count(struct ext4_extent *extent,
+                                              uint16_t count, bool unwritten)
+{
+       EXT4_EXT_SET_LEN(extent, count);
+       if (unwritten)
+               EXT4_EXT_SET_UNWRITTEN(extent);
+}
+
+/**@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 */
+static inline 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));
+}
+
+
+/**@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 */
+static inline 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));
+}
+
+
+/**@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 */
+static inline uint32_t
+ext4_extent_index_get_first_block(struct ext4_extent_index *index)
+{
+       return to_le32(index->first_block);
+}
+
+/**@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 */
+static inline void
+ext4_extent_index_set_first_block(struct ext4_extent_index *index,
+                                 uint32_t iblock)
+{
+       index->first_block = to_le32(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 */
+static inline 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));
+}
+
+/**@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 */
+static inline 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));
+}
+
+/**@brief Get magic value from extent header.
+ * @param header Extent header to load value from
+ * @return Magic value of extent header */
+static inline uint16_t
+ext4_extent_header_get_magic(struct ext4_extent_header *header)
+{
+       return to_le16(header->magic);
+}
+
+/**@brief Set magic value to extent header.
+ * @param header Extent header to set value to
+ * @param magic  Magic value of extent header */
+static inline void ext4_extent_header_set_magic(struct ext4_extent_header *header,
+                                               uint16_t magic)
+{
+       header->magic = to_le16(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 */
+static inline uint16_t
+ext4_extent_header_get_entries_count(struct ext4_extent_header *header)
+{
+       return to_le16(header->entries_count);
+}
+
+/**@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 */
+static inline void
+ext4_extent_header_set_entries_count(struct ext4_extent_header *header,
+                                    uint16_t count)
+{
+       header->entries_count = to_le16(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 */
+static inline uint16_t
+ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header)
+{
+       return to_le16(header->max_entries_count);
+}
+
+/**@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 */
+static inline 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);
+}
+
+/**@brief Get depth of extent subtree.
+ * @param header Extent header to get value from
+ * @return Depth of extent subtree */
+static inline uint16_t
+ext4_extent_header_get_depth(struct ext4_extent_header *header)
+{
+       return to_le16(header->depth);
+}
+
+/**@brief Set depth of extent subtree.
+ * @param header Extent header to set value to
+ * @param depth  Depth of extent subtree */
+static inline void
+ext4_extent_header_set_depth(struct ext4_extent_header *header, uint16_t depth)
+{
+       header->depth = to_le16(depth);
+}
+
+/**@brief Get generation from extent header
+ * @param header Extent header to get value from
+ * @return Generation */
+static inline uint32_t
+ext4_extent_header_get_generation(struct ext4_extent_header *header)
+{
+       return to_le32(header->generation);
+}
+
+/**@brief Set generation to extent header
+ * @param header     Extent header to set value to
+ * @param generation Generation */
+static inline void
+ext4_extent_header_set_generation(struct ext4_extent_header *header,
+                                      uint32_t generation)
+{
+       header->generation = to_le32(generation);
+}
+
+/******************************************************************************/
+
+/**TODO:  */
+static inline void ext4_extent_tree_init(struct ext4_inode_ref *inode_ref)
+{
+       /* Initialize extent root header */
+       struct ext4_extent_header *header =
+                       ext4_inode_get_extent_header(inode_ref->inode);
+       ext4_extent_header_set_depth(header, 0);
+       ext4_extent_header_set_entries_count(header, 0);
+       ext4_extent_header_set_generation(header, 0);
+       ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
+
+       uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
+                       sizeof(struct ext4_extent_header)) /
+                                       sizeof(struct ext4_extent);
+
+       ext4_extent_header_set_max_entries_count(header, max_entries);
+       inode_ref->dirty  = true;
+}
+
+
+
+/**TODO:  */
+int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_lblk_t iblock,
+                          uint32_t max_blocks, ext4_fsblk_t *result, bool create,
+                          uint32_t *blocks_count);
+
+
+/**@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_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
+                            ext4_lblk_t to);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_EXTENT_H_ */
+/**
+* @}
+*/
diff --git a/include/ext4_fs.h b/include/ext4_fs.h
new file mode 100644 (file)
index 0000000..97e1d1d
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * 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_fs.c
+ * @brief More complex filesystem functions.
+ */
+
+#ifndef EXT4_FS_H_
+#define EXT4_FS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**@brief Convert block address to relative index in block group.
+ * @param sb Superblock pointer
+ * @param baddr Block number to convert
+ * @return Relative number of block
+ */
+static inline uint32_t ext4_fs_addr_to_idx_bg(struct ext4_sblock *s,
+                                                    ext4_fsblk_t baddr)
+{
+       if (ext4_get32(s, first_data_block))
+               baddr--;
+
+       return baddr % ext4_get32(s, blocks_per_group);
+}
+
+/**@brief Convert relative block address in group to absolute address.
+ * @param s Superblock pointer
+ * @param index Relative block address
+ * @param bgid Block group
+ * @return Absolute block address
+ */
+static inline ext4_fsblk_t ext4_fs_bg_idx_to_addr(struct ext4_sblock *s,
+                                                    uint32_t index,
+                                                    uint32_t bgid)
+{
+       if (ext4_get32(s, first_data_block))
+               index++;
+
+       return ext4_get32(s, blocks_per_group) * bgid + index;
+}
+
+/**@brief TODO: */
+static inline ext4_fsblk_t ext4_fs_first_bg_block_no(struct ext4_sblock *s,
+                                                uint32_t bgid)
+{
+       return (uint64_t)bgid * ext4_get32(s, blocks_per_group) +
+              ext4_get32(s, first_data_block);
+}
+
+/**@brief Initialize filesystem and read all needed data.
+ * @param fs Filesystem instance to be initialized
+ * @param bdev Identifier if device with the filesystem
+ * @return Error code
+ */
+int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev);
+
+/**@brief Destroy filesystem instance (used by unmount operation).
+ * @param fs Filesystem to be destroyed
+ * @return Error code
+ */
+int ext4_fs_fini(struct ext4_fs *fs);
+
+/**@brief Check filesystem's features, if supported by this driver
+ * Function can return EOK and set read_only flag. It mean's that
+ * there are some not-supported features, that can cause problems
+ * during some write operations.
+ * @param fs        Filesystem to be checked
+ * @param read_only Flag if filesystem should be mounted only for reading
+ * @return Error code
+ */
+int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only);
+
+/**@brief Get reference to block group specified by index.
+ * @param fs   Filesystem to find block group on
+ * @param bgid Index of block group to load
+ * @param ref  Output pointer for reference
+ * @return Error code
+ */
+int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid,
+                               struct ext4_block_group_ref *ref);
+
+/**@brief Put reference to block group.
+ * @param ref Pointer for reference to be put back
+ * @return Error code
+ */
+int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref);
+
+/**@brief Get reference to i-node specified by index.
+ * @param fs    Filesystem to find i-node on
+ * @param index Index of i-node to load
+ * @param ref   Output pointer for reference
+ * @return Error code
+ */
+int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
+                         struct ext4_inode_ref *ref);
+
+/**@brief Reset blocks field of i-node.
+ * @param fs        Filesystem to reset blocks field of i-inode on
+ * @param inode_ref ref Pointer for inode to be operated on
+ */
+void ext4_fs_inode_blocks_init(struct ext4_fs *fs,
+                              struct ext4_inode_ref *inode_ref);
+
+/**@brief Put reference to i-node.
+ * @param ref Pointer for reference to be put back
+ * @return Error code
+ */
+int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref);
+
+/**@brief Convert filetype to inode mode.
+ * @param filetype
+ * @return inode mode
+ */
+uint32_t ext4_fs_correspond_inode_mode(int filetype);
+
+/**@brief Allocate new i-node in the filesystem.
+ * @param fs        Filesystem to allocated i-node on
+ * @param inode_ref Output pointer to return reference to allocated i-node
+ * @param filetype  File type of newly created i-node
+ * @return Error code
+ */
+int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
+                       int filetype);
+
+/**@brief Release i-node and mark it as free.
+ * @param inode_ref I-node to be released
+ * @return Error code
+ */
+int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref);
+
+/**@brief Truncate i-node data blocks.
+ * @param inode_ref I-node to be truncated
+ * @param new_size  New size of inode (must be < current size)
+ * @return Error code
+ */
+int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size);
+
+/**@brief Compute 'goal' for inode index
+ * @param inode_ref Reference to inode, to allocate block for
+ * @return goal
+ */
+ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref);
+
+/**@brief Compute 'goal' for allocation algorithm (For blockmap).
+ * @param inode_ref Reference to inode, to allocate block for
+ * @param goal
+ * @return error code
+ */
+int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
+                               ext4_fsblk_t *goal);
+
+/**@brief Get physical block address by logical index of the block.
+ * @param inode_ref I-node to read block address from
+ * @param iblock            Logical index of block
+ * @param fblock            Output pointer for return physical
+ *                          block address
+ * @param support_unwritten Indicate whether unwritten block range
+ *                          is supported under the current context
+ * @return Error code
+ */
+int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
+                                uint64_t iblock, ext4_fsblk_t *fblock,
+                                bool support_unwritten);
+
+/**@brief Initialize a part of unwritten range of the inode.
+ * @param inode_ref I-node to proceed on.
+ * @param iblock    Logical index of block
+ * @param fblock    Output pointer for return physical block address
+ * @return Error code
+ */
+int ext4_fs_init_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
+                                 uint64_t iblock, ext4_fsblk_t *fblock);
+
+/**@brief Append following logical block to the i-node.
+ * @param inode_ref I-node to append block to
+ * @param fblock    Output physical block address of newly allocated block
+ * @param iblock    Output logical number of newly allocated block
+ * @return Error code
+ */
+int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref,
+                             ext4_fsblk_t *fblock, uint32_t *iblock);
+
+/**@brief   Increment inode link count.
+ * @param   inode none handle
+ */
+void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref);
+
+/**@brief   Decrement inode link count.
+ * @param   inode none handle
+ */
+void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_FS_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_hash.h b/include/ext4_hash.h
new file mode 100644 (file)
index 0000000..b7a9ac5
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_HASH_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_ialloc.h b/include/ext4_ialloc.h
new file mode 100644 (file)
index 0000000..cea3fe6
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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 Calculate and set checksum of inode bitmap.
+ * @param sb superblock pointer.
+ * @param bg block group
+ * @param bitmap bitmap buffer
+ */
+void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
+                                void *bitmap);
+
+/**@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_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_inode.h b/include/ext4_inode.h
new file mode 100644 (file)
index 0000000..90c5754
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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_modif_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_modif_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_del_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_del_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_cnt(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_cnt(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 extra I-node size field.
+ * @param inode      I-node
+ * @return extra I-node size
+ */
+uint16_t ext4_inode_get_extra_isize(struct ext4_inode *inode);
+
+/**@brief Set extra I-node size field.
+ * @param inode      I-node
+ * @param size       extra I-node size
+ */
+void ext4_inode_set_extra_isize(struct ext4_inode *inode, uint16_t size);
+
+/**@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 return the type of i-node
+ * @param sb    Superblock
+ * @param inode I-node to return the type of
+ * @return Result of check operation
+ */
+uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode);
+
+/**@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 Get inode checksum(crc32)
+ * @param sb    Superblock
+ * @param inode I-node to get checksum value from
+ */
+uint32_t
+ext4_inode_get_csum(struct ext4_sblock *sb, struct ext4_inode *inode);
+
+/**@brief Get inode checksum(crc32)
+ * @param sb    Superblock
+ * @param inode I-node to get checksum value from
+ */
+void
+ext4_inode_set_csum(struct ext4_sblock *sb, struct ext4_inode *inode,
+                       uint32_t checksum);
+
+/**@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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_INODE_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_journal.h b/include/ext4_journal.h
new file mode 100644 (file)
index 0000000..8e193cc
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
+ *
+ * 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_journal.h
+ * @brief Journal handle functions
+ */
+
+#ifndef EXT4_JOURNAL_H_
+#define EXT4_JOURNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+int jbd_get_fs(struct ext4_fs *fs,
+              struct jbd_fs *jbd_fs);
+int jbd_put_fs(struct jbd_fs *jbd_fs);
+int jbd_inode_bmap(struct jbd_fs *jbd_fs,
+                  ext4_lblk_t iblock,
+                  ext4_fsblk_t *fblock);
+int jbd_recover(struct jbd_fs *jbd_fs);
+int jbd_journal_start(struct jbd_fs *jbd_fs,
+                     struct jbd_journal *journal);
+int jbd_journal_stop(struct jbd_journal *journal);
+struct jbd_trans *jbd_journal_new_trans(struct jbd_journal *journal);
+int jbd_trans_get_access(struct jbd_journal *journal,
+                        struct jbd_trans *trans,
+                        struct ext4_block *block);
+int jbd_trans_set_block_dirty(struct jbd_trans *trans,
+                             struct ext4_block *block);
+int jbd_trans_revoke_block(struct jbd_trans *trans,
+                          ext4_fsblk_t lba);
+int jbd_trans_try_revoke_block(struct jbd_trans *trans,
+                              ext4_fsblk_t lba);
+void jbd_journal_free_trans(struct jbd_journal *journal,
+                           struct jbd_trans *trans,
+                           bool abort);
+int jbd_journal_commit_trans(struct jbd_journal *journal,
+                            struct jbd_trans *trans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_JOURNAL_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_mbr.h b/include/ext4_mbr.h
new file mode 100644 (file)
index 0000000..2fc91f2
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 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_mbr.h
+ * @brief Master boot record parser
+ */
+
+#ifndef EXT4_MBR_H_
+#define EXT4_MBR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_blockdev.h"
+
+/**@brief Master boot record block devices descriptor*/
+struct ext4_mbr_bdevs {
+       struct ext4_blockdev partitions[4];
+};
+
+int ext4_mbr_scan(struct ext4_blockdev *parent, struct ext4_mbr_bdevs *bdevs);
+
+/**@brief Master boot record partitions*/
+struct ext4_mbr_parts {
+       uint64_t size[4];
+};
+
+int ext4_mbr_write(struct ext4_blockdev *parent, struct ext4_mbr_parts *parts);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_MBR_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_mkfs.h b/include/ext4_mkfs.h
new file mode 100644 (file)
index 0000000..497bf3b
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 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_mkfs.h
+ * @brief
+ */
+
+#ifndef EXT4_MKFS_H_
+#define EXT4_MKFS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_blockdev.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+struct ext4_mkfs_info {
+       int64_t len;
+       uint32_t block_size;
+       uint32_t blocks_per_group;
+       uint32_t inodes_per_group;
+       uint32_t inode_size;
+       uint32_t inodes;
+       uint32_t journal_blocks;
+       uint16_t feat_ro_compat;
+       uint16_t feat_compat;
+       uint16_t feat_incompat;
+       uint32_t bg_desc_reserve_blocks;
+       uint16_t dsc_size;
+       uint8_t journal;
+       const char *label;
+};
+
+int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info);
+
+int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd,
+             struct ext4_mkfs_info *info, int fs_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_MKFS_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_oflags.h b/include/ext4_oflags.h
new file mode 100644 (file)
index 0000000..7f7be7e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@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_oflags.h
+ * @brief File opening & seeking flags.
+ */
+#ifndef EXT4_OFLAGS_H_
+#define EXT4_OFLAGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/********************************FILE OPEN FLAGS*****************************/
+
+#if CONFIG_HAVE_OWN_OFLAGS
+
+ #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
+
+#else
+ #include <unistd.h>
+ #include <fcntl.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_OFLAGS_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_super.h b/include/ext4_super.h
new file mode 100644 (file)
index 0000000..2172698
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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_super.c
+ * @brief Superblock operations.
+ */
+
+#ifndef EXT4_SUPER_H_
+#define EXT4_SUPER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+/**@brief   Blocks count get stored in superblock.
+ * @param   s superblock descriptor
+ * @return  count of blocks*/
+static inline uint64_t ext4_sb_get_blocks_cnt(struct ext4_sblock *s)
+{
+       return ((uint64_t)to_le32(s->blocks_count_hi) << 32) |
+              to_le32(s->blocks_count_lo);
+}
+
+/**@brief   Blocks count set  in superblock.
+ * @param   s superblock descriptor
+ * @return  count of blocks*/
+static inline void ext4_sb_set_blocks_cnt(struct ext4_sblock *s, uint64_t cnt)
+{
+       s->blocks_count_lo = to_le32((cnt << 32) >> 32);
+       s->blocks_count_hi = to_le32(cnt >> 32);
+}
+
+/**@brief   Free blocks count get stored in superblock.
+ * @param   s superblock descriptor
+ * @return  free blocks*/
+static inline uint64_t ext4_sb_get_free_blocks_cnt(struct ext4_sblock *s)
+{
+       return ((uint64_t)to_le32(s->free_blocks_count_hi) << 32) |
+              to_le32(s->free_blocks_count_lo);
+}
+
+/**@brief   Free blocks count set.
+ * @param   s superblock descriptor
+ * @param   cnt new value of free blocks*/
+static inline void ext4_sb_set_free_blocks_cnt(struct ext4_sblock *s,
+                                              uint64_t cnt)
+{
+       s->free_blocks_count_lo = to_le32((cnt << 32) >> 32);
+       s->free_blocks_count_hi = to_le32(cnt >> 32);
+}
+
+/**@brief   Block size get from superblock.
+ * @param   s superblock descriptor
+ * @return  block size in bytes*/
+static inline uint32_t ext4_sb_get_block_size(struct ext4_sblock *s)
+{
+       return 1024 << to_le32(s->log_block_size);
+}
+
+/**@brief   Block group descriptor size.
+ * @param   s superblock descriptor
+ * @return  block group descriptor size in bytes*/
+static inline uint16_t ext4_sb_get_desc_size(struct ext4_sblock *s)
+{
+       uint16_t size = to_le16(s->desc_size);
+
+       return size < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
+                  ? EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
+                  : size;
+}
+
+/*************************Flags and features*********************************/
+
+/**@brief   Support check of flag.
+ * @param   s superblock descriptor
+ * @param   v flag to check
+ * @return  true if flag is supported*/
+static inline bool ext4_sb_check_flag(struct ext4_sblock *s, uint32_t v)
+{
+       return to_le32(s->flags) & v;
+}
+
+/**@brief   Support check of feature compatible.
+ * @param   s superblock descriptor
+ * @param   v feature to check
+ * @return  true if feature is supported*/
+static inline bool ext4_sb_feature_com(struct ext4_sblock *s, uint32_t v)
+{
+       return to_le32(s->features_compatible) & v;
+}
+
+/**@brief   Support check of feature incompatible.
+ * @param   s superblock descriptor
+ * @param   v feature to check
+ * @return  true if feature is supported*/
+static inline bool ext4_sb_feature_incom(struct ext4_sblock *s, uint32_t v)
+{
+       return to_le32(s->features_incompatible) & v;
+}
+
+/**@brief   Support check of read only flag.
+ * @param   s superblock descriptor
+ * @param   v flag to check
+ * @return  true if flag is supported*/
+static inline bool ext4_sb_feature_ro_com(struct ext4_sblock *s, uint32_t v)
+{
+       return to_le32(s->features_read_only) & v;
+}
+
+/**@brief   Block group to flex group.
+ * @param   s superblock descriptor
+ * @param   block_group block group
+ * @return  flex group id*/
+static inline uint32_t ext4_sb_bg_to_flex(struct ext4_sblock *s,
+                                         uint32_t block_group)
+{
+       return block_group >> to_le32(s->log_groups_per_flex);
+}
+
+/**@brief   Flex block group size.
+ * @param   s superblock descriptor
+ * @return  flex bg size*/
+static inline uint32_t ext4_sb_flex_bg_size(struct ext4_sblock *s)
+{
+       return 1 << to_le32(s->log_groups_per_flex);
+}
+
+/**@brief   Return first meta block group id.
+ * @param   s superblock descriptor
+ * @return  first meta_bg id */
+static inline uint32_t ext4_sb_first_meta_bg(struct ext4_sblock *s)
+{
+       return to_le32(s->first_meta_bg);
+}
+
+/**************************More complex functions****************************/
+
+/**@brief   Returns a block group count.
+ * @param   s superblock descriptor
+ * @return  count of block groups*/
+uint32_t ext4_block_group_cnt(struct ext4_sblock *s);
+
+/**@brief   Returns block count in block group
+ *          (last block group may have less blocks)
+ * @param   s superblock descriptor
+ * @param   bgid block group id
+ * @return  blocks count*/
+uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid);
+
+/**@brief   Returns inodes count in block group
+ *          (last block group may have less inodes)
+ * @param   s superblock descriptor
+ * @param   bgid block group id
+ * @return  inodes count*/
+uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid);
+
+/***************************Read/write/check superblock**********************/
+
+/**@brief   Superblock write.
+ * @param   bdev block device descriptor.
+ * @param   s superblock descriptor
+ * @return  Standard error code */
+int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s);
+
+/**@brief   Superblock read.
+ * @param   bdev block device descriptor.
+ * @param   s superblock descriptor
+ * @return  Standard error code */
+int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s);
+
+/**@brief   Superblock simple validation.
+ * @param   s superblock descriptor
+ * @return  true if OK*/
+bool ext4_sb_check(struct ext4_sblock *s);
+
+/**@brief   Superblock presence in block group.
+ * @param   s superblock descriptor
+ * @param   block_group block group id
+ * @return  true if block group has superblock*/
+bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t block_group);
+
+/**@brief   TODO:*/
+bool ext4_sb_sparse(uint32_t group);
+
+/**@brief   TODO:*/
+uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group);
+
+/**@brief   TODO:*/
+uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
+                                    uint32_t block_group);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_SUPER_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_trans.h b/include/ext4_trans.h
new file mode 100644 (file)
index 0000000..e3cb28a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
+ *
+ * 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_trans.h
+ * @brief Transaction handle functions
+ */
+
+#ifndef EXT4_TRANS_H
+#define EXT4_TRANS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+
+/**@brief   Mark a buffer dirty and add it to the current transaction.
+ * @param   buf buffer
+ * @return  standard error code*/
+int ext4_trans_set_block_dirty(struct ext4_buf *buf);
+
+/**@brief   Block get function (through cache, don't read).
+ *          jbd_trans_get_access would be called in order to
+ *          get write access to the buffer.
+ * @param   bdev block device descriptor
+ * @param   b block descriptor
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_trans_block_get_noread(struct ext4_blockdev *bdev,
+                         struct ext4_block *b,
+                         uint64_t lba);
+
+/**@brief   Block get function (through cache).
+ *          jbd_trans_get_access would be called in order to
+ *          get write access to the buffer.
+ * @param   bdev block device descriptor
+ * @param   b block descriptor
+ * @param   lba logical block address
+ * @return  standard error code*/
+int ext4_trans_block_get(struct ext4_blockdev *bdev,
+                  struct ext4_block *b,
+                  uint64_t lba);
+
+/**@brief  Try to add block to be revoked to the current transaction.
+ * @param  bdev block device descriptor
+ * @param  lba logical block address
+ * @return standard error code*/
+int ext4_trans_try_revoke_block(struct ext4_blockdev *bdev,
+                              uint64_t lba);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_TRANS_H */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_types.h b/include/ext4_types.h
new file mode 100644 (file)
index 0000000..41cb33f
--- /dev/null
@@ -0,0 +1,1304 @@
+/*
+ * 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_types.h
+ * @brief Ext4 data structure definitions.
+ */
+
+#ifndef EXT4_TYPES_H_
+#define EXT4_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_blockdev.h"
+#include "misc/tree.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define EXT4_CHECKSUM_CRC32C 1
+
+#define UUID_SIZE 16
+
+#pragma pack(push, 1)
+
+/*
+ * Structure of the super block
+ */
+struct ext4_sblock {
+       uint32_t inodes_count;             /* I-nodes count */
+       uint32_t blocks_count_lo;         /* Blocks count */
+       uint32_t reserved_blocks_count_lo; /* Reserved blocks count */
+       uint32_t free_blocks_count_lo;     /* Free blocks count */
+       uint32_t free_inodes_count;     /* Free inodes count */
+       uint32_t first_data_block;       /* First Data Block */
+       uint32_t log_block_size;           /* Block size */
+       uint32_t log_cluster_size;       /* Obsoleted fragment size */
+       uint32_t blocks_per_group;       /* Number of blocks per group */
+       uint32_t frags_per_group;         /* Obsoleted fragments per group */
+       uint32_t inodes_per_group;       /* Number of inodes per group */
+       uint32_t mount_time;               /* Mount time */
+       uint32_t write_time;               /* Write time */
+       uint16_t mount_count;              /* Mount count */
+       uint16_t max_mount_count;         /* Maximal mount count */
+       uint16_t magic;                    /* Magic signature */
+       uint16_t state;                    /* File system state */
+       uint16_t errors;                   /* Behavior when detecting errors */
+       uint16_t minor_rev_level;         /* Minor revision level */
+       uint32_t last_check_time;         /* Time of last check */
+       uint32_t check_interval;           /* Maximum time between checks */
+       uint32_t creator_os;               /* Creator OS */
+       uint32_t rev_level;                /* Revision level */
+       uint16_t def_resuid;               /* Default uid for reserved blocks */
+       uint16_t def_resgid;               /* Default gid for reserved blocks */
+
+       /* Fields for EXT4_DYNAMIC_REV superblocks only. */
+       uint32_t first_inode;    /* First non-reserved inode */
+       uint16_t inode_size;      /* Size of inode structure */
+       uint16_t block_group_index;   /* Block group index of this superblock */
+       uint32_t features_compatible; /* Compatible feature set */
+       uint32_t features_incompatible;  /* Incompatible feature set */
+       uint32_t features_read_only;     /* Readonly-compatible feature set */
+       uint8_t uuid[UUID_SIZE];                 /* 128-bit uuid for volume */
+       char volume_name[16];            /* Volume name */
+       char last_mounted[64];           /* Directory where last mounted */
+       uint32_t algorithm_usage_bitmap; /* For compression */
+
+       /*
+        * Performance hints. Directory preallocation should only
+        * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
+        */
+       uint8_t s_prealloc_blocks; /* Number of blocks to try to preallocate */
+       uint8_t s_prealloc_dir_blocks;  /* Number to preallocate for dirs */
+       uint16_t s_reserved_gdt_blocks; /* Per group desc for online growth */
+
+       /*
+        * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
+        */
+       uint8_t journal_uuid[UUID_SIZE];      /* UUID of journal superblock */
+       uint32_t journal_inode_number; /* Inode number of journal file */
+       uint32_t journal_dev;     /* Device number of journal file */
+       uint32_t last_orphan;     /* Head of list of inodes to delete */
+       uint32_t hash_seed[4];   /* HTREE hash seed */
+       uint8_t default_hash_version;  /* Default hash version to use */
+       uint8_t journal_backup_type;
+       uint16_t desc_size;       /* Size of group descriptor */
+       uint32_t default_mount_opts; /* Default mount options */
+       uint32_t first_meta_bg;      /* First metablock block group */
+       uint32_t mkfs_time;       /* When the filesystem was created */
+       uint32_t journal_blocks[17]; /* Backup of the journal inode */
+
+       /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
+       uint32_t blocks_count_hi;         /* Blocks count */
+       uint32_t reserved_blocks_count_hi; /* Reserved blocks count */
+       uint32_t free_blocks_count_hi;     /* Free blocks count */
+       uint16_t min_extra_isize;    /* All inodes have at least # bytes */
+       uint16_t want_extra_isize;   /* New inodes should reserve # bytes */
+       uint32_t flags;              /* Miscellaneous flags */
+       uint16_t raid_stride;   /* RAID stride */
+       uint16_t mmp_interval;       /* # seconds to wait in MMP checking */
+       uint64_t mmp_block;       /* Block for multi-mount protection */
+       uint32_t raid_stripe_width;  /* Blocks on all data disks (N * stride) */
+       uint8_t log_groups_per_flex; /* FLEX_BG group size */
+       uint8_t checksum_type;
+       uint16_t reserved_pad;
+       uint64_t kbytes_written; /* Number of lifetime kilobytes written */
+       uint32_t snapshot_inum;  /* I-node number of active snapshot */
+       uint32_t snapshot_id;    /* Sequential ID of active snapshot */
+       uint64_t
+           snapshot_r_blocks_count; /* Reserved blocks for active snapshot's
+                                       future use */
+       uint32_t
+           snapshot_list; /* I-node number of the head of the on-disk snapshot
+                             list */
+       uint32_t error_count;    /* Number of file system errors */
+       uint32_t first_error_time;    /* First time an error happened */
+       uint32_t first_error_ino;     /* I-node involved in first error */
+       uint64_t first_error_block;   /* Block involved of first error */
+       uint8_t first_error_func[32]; /* Function where the error happened */
+       uint32_t first_error_line;    /* Line number where error happened */
+       uint32_t last_error_time;     /* Most recent time of an error */
+       uint32_t last_error_ino;      /* I-node involved in last error */
+       uint32_t last_error_line;     /* Line number where error happened */
+       uint64_t last_error_block;    /* Block involved of last error */
+       uint8_t last_error_func[32];  /* Function where the error happened */
+       uint8_t mount_opts[64];
+       uint32_t usr_quota_inum;        /* inode for tracking user quota */
+       uint32_t grp_quota_inum;        /* inode for tracking group quota */
+       uint32_t overhead_clusters;     /* overhead blocks/clusters in fs */
+       uint32_t backup_bgs[2]; /* groups with sparse_super2 SBs */
+       uint8_t  encrypt_algos[4];      /* Encryption algorithms in use  */
+       uint8_t  encrypt_pw_salt[16];   /* Salt used for string2key algorithm */
+       uint32_t lpf_ino;               /* Location of the lost+found inode */
+       uint32_t padding[100];  /* Padding to the end of the block */
+       uint32_t checksum;              /* crc32c(superblock) */
+};
+
+#pragma pack(pop)
+
+#define EXT4_SUPERBLOCK_MAGIC 0xEF53
+#define EXT4_SUPERBLOCK_SIZE 1024
+#define EXT4_SUPERBLOCK_OFFSET 1024
+
+#define EXT4_SUPERBLOCK_OS_LINUX 0
+#define EXT4_SUPERBLOCK_OS_HURD 1
+
+/*
+ * Misc. filesystem flags
+ */
+#define EXT4_SUPERBLOCK_FLAGS_SIGNED_HASH 0x0001
+#define EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH 0x0002
+#define EXT4_SUPERBLOCK_FLAGS_TEST_FILESYS 0x0004
+/*
+ * Filesystem states
+ */
+#define EXT4_SUPERBLOCK_STATE_VALID_FS 0x0001  /* Unmounted cleanly */
+#define EXT4_SUPERBLOCK_STATE_ERROR_FS 0x0002  /* Errors detected */
+#define EXT4_SUPERBLOCK_STATE_ORPHAN_FS 0x0004 /* Orphans being recovered */
+
+/*
+ * Behaviour when errors detected
+ */
+#define EXT4_SUPERBLOCK_ERRORS_CONTINUE 1 /* Continue execution */
+#define EXT4_SUPERBLOCK_ERRORS_RO 2       /* Remount fs read-only */
+#define EXT4_SUPERBLOCK_ERRORS_PANIC 3    /* Panic */
+#define EXT4_SUPERBLOCK_ERRORS_DEFAULT EXT4_ERRORS_CONTINUE
+
+/*
+ * Compatible features
+ */
+#define EXT4_FCOM_DIR_PREALLOC 0x0001
+#define EXT4_FCOM_IMAGIC_INODES 0x0002
+#define EXT4_FCOM_HAS_JOURNAL 0x0004
+#define EXT4_FCOM_EXT_ATTR 0x0008
+#define EXT4_FCOM_RESIZE_INODE 0x0010
+#define EXT4_FCOM_DIR_INDEX 0x0020
+
+/*
+ * Read-only compatible features
+ */
+#define EXT4_FRO_COM_SPARSE_SUPER 0x0001
+#define EXT4_FRO_COM_LARGE_FILE 0x0002
+#define EXT4_FRO_COM_BTREE_DIR 0x0004
+#define EXT4_FRO_COM_HUGE_FILE 0x0008
+#define EXT4_FRO_COM_GDT_CSUM 0x0010
+#define EXT4_FRO_COM_DIR_NLINK 0x0020
+#define EXT4_FRO_COM_EXTRA_ISIZE 0x0040
+#define EXT4_FRO_COM_QUOTA 0x0100
+#define EXT4_FRO_COM_BIGALLOC 0x0200
+#define EXT4_FRO_COM_METADATA_CSUM 0x0400
+
+/*
+ * Incompatible features
+ */
+#define EXT4_FINCOM_COMPRESSION 0x0001
+#define EXT4_FINCOM_FILETYPE 0x0002
+#define EXT4_FINCOM_RECOVER 0x0004     /* Needs recovery */
+#define EXT4_FINCOM_JOURNAL_DEV 0x0008 /* Journal device */
+#define EXT4_FINCOM_META_BG 0x0010
+#define EXT4_FINCOM_EXTENTS 0x0040 /* extents support */
+#define EXT4_FINCOM_64BIT 0x0080
+#define EXT4_FINCOM_MMP 0x0100
+#define EXT4_FINCOM_FLEX_BG 0x0200
+#define EXT4_FINCOM_EA_INODE 0x0400     /* EA in inode */
+#define EXT4_FINCOM_DIRDATA 0x1000       /* data in dirent */
+#define EXT4_FINCOM_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */
+#define EXT4_FINCOM_LARGEDIR 0x4000     /* >2GB or 3-lvl htree */
+#define EXT4_FINCOM_INLINE_DATA 0x8000      /* data in inode */
+
+/*
+ * EXT2 supported feature set
+ */
+#define EXT2_SUPPORTED_FCOM 0x0000
+
+#define EXT2_SUPPORTED_FINCOM                                   \
+       (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG)
+
+#define EXT2_SUPPORTED_FRO_COM                                  \
+       (EXT4_FRO_COM_SPARSE_SUPER |                            \
+        EXT4_FRO_COM_LARGE_FILE)
+
+/*
+ * EXT3 supported feature set
+ */
+#define EXT3_SUPPORTED_FCOM (EXT4_FCOM_DIR_INDEX)
+
+#define EXT3_SUPPORTED_FINCOM                                 \
+       (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG)
+
+#define EXT3_SUPPORTED_FRO_COM                                \
+       (EXT4_FRO_COM_SPARSE_SUPER | EXT4_FRO_COM_LARGE_FILE)
+
+/*
+ * EXT4 supported feature set
+ */
+#define EXT4_SUPPORTED_FCOM (EXT4_FCOM_DIR_INDEX)
+
+#define EXT4_SUPPORTED_FINCOM                              \
+       (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG |      \
+        EXT4_FINCOM_EXTENTS | EXT4_FINCOM_FLEX_BG |       \
+        EXT4_FINCOM_64BIT)
+
+#define EXT4_SUPPORTED_FRO_COM                             \
+       (EXT4_FRO_COM_SPARSE_SUPER |                       \
+        EXT4_FRO_COM_METADATA_CSUM |                      \
+        EXT4_FRO_COM_LARGE_FILE | EXT4_FRO_COM_GDT_CSUM | \
+        EXT4_FRO_COM_DIR_NLINK |                          \
+        EXT4_FRO_COM_EXTRA_ISIZE | EXT4_FRO_COM_HUGE_FILE)
+
+/*Ignored features:
+ * RECOVER - journaling in lwext4 is not supported
+ *           (probably won't be ever...)
+ * MMP - multi-mout protection (impossible scenario)
+ * */
+#define EXT_FINCOM_IGNORED                                 \
+       EXT4_FINCOM_RECOVER | EXT4_FINCOM_MMP
+
+#if 0
+/*TODO: Features incompatible to implement*/
+#define EXT4_SUPPORTED_FINCOM
+                     (EXT4_FINCOM_INLINE_DATA)
+
+/*TODO: Features read only to implement*/
+#define EXT4_SUPPORTED_FRO_COM
+                     EXT4_FRO_COM_BIGALLOC |\
+                     EXT4_FRO_COM_QUOTA)
+#endif
+
+struct ext4_fs {
+       struct ext4_blockdev *bdev;
+       struct ext4_sblock sb;
+
+       uint64_t inode_block_limits[4];
+       uint64_t inode_blocks_per_level[4];
+
+       uint32_t last_inode_bg_id;
+
+       struct jbd_fs *jbd_fs;
+       struct jbd_journal *jbd_journal;
+       struct jbd_trans *curr_trans;
+};
+
+/* Inode table/bitmap not in use */
+#define EXT4_BLOCK_GROUP_INODE_UNINIT 0x0001
+/* Block bitmap not in use */
+#define EXT4_BLOCK_GROUP_BLOCK_UNINIT 0x0002
+/* On-disk itable initialized to zero */
+#define EXT4_BLOCK_GROUP_ITABLE_ZEROED 0x0004
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext4_bgroup {
+       uint32_t block_bitmap_lo;           /* Blocks bitmap block */
+       uint32_t inode_bitmap_lo;           /* Inodes bitmap block */
+       uint32_t inode_table_first_block_lo; /* Inodes table block */
+       uint16_t free_blocks_count_lo;       /* Free blocks count */
+       uint16_t free_inodes_count_lo;       /* Free inodes count */
+       uint16_t used_dirs_count_lo;     /* Directories count */
+       uint16_t flags;                /* EXT4_BG_flags (INODE_UNINIT, etc) */
+       uint32_t exclude_bitmap_lo;    /* Exclude bitmap for snapshots */
+       uint16_t block_bitmap_csum_lo; /* crc32c(s_uuid+grp_num+bbitmap) LE */
+       uint16_t inode_bitmap_csum_lo; /* crc32c(s_uuid+grp_num+ibitmap) LE */
+       uint16_t itable_unused_lo;     /* Unused inodes count */
+       uint16_t checksum;           /* crc16(sb_uuid+group+desc) */
+
+       uint32_t block_bitmap_hi;           /* Blocks bitmap block MSB */
+       uint32_t inode_bitmap_hi;           /* I-nodes bitmap block MSB */
+       uint32_t inode_table_first_block_hi; /* I-nodes table block MSB */
+       uint16_t free_blocks_count_hi;       /* Free blocks count MSB */
+       uint16_t free_inodes_count_hi;       /* Free i-nodes count MSB */
+       uint16_t used_dirs_count_hi;     /* Directories count MSB */
+       uint16_t itable_unused_hi;         /* Unused inodes count MSB */
+       uint32_t exclude_bitmap_hi;       /* Exclude bitmap block MSB */
+       uint16_t block_bitmap_csum_hi; /* crc32c(s_uuid+grp_num+bbitmap) BE */
+       uint16_t inode_bitmap_csum_hi; /* crc32c(s_uuid+grp_num+ibitmap) BE */
+       uint32_t reserved;           /* Padding */
+};
+
+struct ext4_block_group_ref {
+       struct ext4_block block;
+       struct ext4_bgroup *block_group;
+       struct ext4_fs *fs;
+       uint32_t index;
+       bool dirty;
+};
+
+#define EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE 32
+#define EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE 64
+
+#define EXT4_MIN_BLOCK_SIZE 1024  /* 1 KiB */
+#define EXT4_MAX_BLOCK_SIZE 65536 /* 64 KiB */
+#define EXT4_REV0_INODE_SIZE 128
+
+#define EXT4_INODE_BLOCK_SIZE 512
+
+#define EXT4_INODE_DIRECT_BLOCK_COUNT 12
+#define EXT4_INODE_INDIRECT_BLOCK EXT4_INODE_DIRECT_BLOCK_COUNT
+#define EXT4_INODE_DOUBLE_INDIRECT_BLOCK (EXT4_INODE_INDIRECT_BLOCK + 1)
+#define EXT4_INODE_TRIPPLE_INDIRECT_BLOCK (EXT4_INODE_DOUBLE_INDIRECT_BLOCK + 1)
+#define EXT4_INODE_BLOCKS (EXT4_INODE_TRIPPLE_INDIRECT_BLOCK + 1)
+#define EXT4_INODE_INDIRECT_BLOCK_COUNT                                        \
+       (EXT4_INODE_BLOCKS - EXT4_INODE_DIRECT_BLOCK_COUNT)
+
+#pragma pack(push, 1)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext4_inode {
+       uint16_t mode;              /* File mode */
+       uint16_t uid;               /* Low 16 bits of owner uid */
+       uint32_t size_lo;          /* Size in bytes */
+       uint32_t access_time;       /* Access time */
+       uint32_t change_inode_time; /* I-node change time */
+       uint32_t modification_time; /* Modification time */
+       uint32_t deletion_time;     /* Deletion time */
+       uint16_t gid;               /* Low 16 bits of group id */
+       uint16_t links_count;       /* Links count */
+       uint32_t blocks_count_lo;   /* Blocks count */
+       uint32_t flags;             /* File flags */
+       uint32_t unused_osd1;       /* OS dependent - not used in HelenOS */
+       uint32_t blocks[EXT4_INODE_BLOCKS]; /* Pointers to blocks */
+       uint32_t generation;                /* File version (for NFS) */
+       uint32_t file_acl_lo;               /* File ACL */
+       uint32_t size_hi;
+       uint32_t obso_faddr; /* Obsoleted fragment address */
+
+       union {
+               struct {
+                       uint16_t blocks_high;
+                       uint16_t file_acl_high;
+                       uint16_t uid_high;
+                       uint16_t gid_high;
+                       uint16_t checksum_lo; /* crc32c(uuid+inum+inode) LE */
+                       uint16_t reserved2;
+               } linux2;
+               struct {
+                       uint16_t reserved1;
+                       uint16_t mode_high;
+                       uint16_t uid_high;
+                       uint16_t gid_high;
+                       uint32_t author;
+               } hurd2;
+       } osd2;
+
+       uint16_t extra_isize;
+       uint16_t checksum_hi;   /* crc32c(uuid+inum+inode) BE */
+       uint32_t ctime_extra; /* Extra change time (nsec << 2 | epoch) */
+       uint32_t mtime_extra; /* Extra Modification time (nsec << 2 | epoch) */
+       uint32_t atime_extra; /* Extra Access time (nsec << 2 | epoch) */
+       uint32_t crtime;      /* File creation time */
+       uint32_t
+           crtime_extra;    /* Extra file creation time (nsec << 2 | epoch) */
+       uint32_t version_hi; /* High 32 bits for 64-bit version */
+};
+
+#pragma pack(pop)
+
+#define EXT4_INODE_MODE_FIFO 0x1000
+#define EXT4_INODE_MODE_CHARDEV 0x2000
+#define EXT4_INODE_MODE_DIRECTORY 0x4000
+#define EXT4_INODE_MODE_BLOCKDEV 0x6000
+#define EXT4_INODE_MODE_FILE 0x8000
+#define EXT4_INODE_MODE_SOFTLINK 0xA000
+#define EXT4_INODE_MODE_SOCKET 0xC000
+#define EXT4_INODE_MODE_TYPE_MASK 0xF000
+
+/*
+ * Inode flags
+ */
+#define EXT4_INODE_FLAG_SECRM 0x00000001     /* Secure deletion */
+#define EXT4_INODE_FLAG_UNRM 0x00000002      /* Undelete */
+#define EXT4_INODE_FLAG_COMPR 0x00000004     /* Compress file */
+#define EXT4_INODE_FLAG_SYNC 0x00000008      /* Synchronous updates */
+#define EXT4_INODE_FLAG_IMMUTABLE 0x00000010 /* Immutable file */
+#define EXT4_INODE_FLAG_APPEND 0x00000020  /* writes to file may only append */
+#define EXT4_INODE_FLAG_NODUMP 0x00000040  /* do not dump file */
+#define EXT4_INODE_FLAG_NOATIME 0x00000080 /* do not update atime */
+
+/* Compression flags */
+#define EXT4_INODE_FLAG_DIRTY 0x00000100
+#define EXT4_INODE_FLAG_COMPRBLK                                               \
+       0x00000200                         /* One or more compressed clusters */
+#define EXT4_INODE_FLAG_NOCOMPR 0x00000400 /* Don't compress */
+#define EXT4_INODE_FLAG_ECOMPR 0x00000800  /* Compression error */
+
+#define EXT4_INODE_FLAG_INDEX 0x00001000  /* hash-indexed directory */
+#define EXT4_INODE_FLAG_IMAGIC 0x00002000 /* AFS directory */
+#define EXT4_INODE_FLAG_JOURNAL_DATA                                           \
+       0x00004000                        /* File data should be journaled */
+#define EXT4_INODE_FLAG_NOTAIL 0x00008000 /* File tail should not be merged */
+#define EXT4_INODE_FLAG_DIRSYNC                                                \
+       0x00010000 /* Dirsync behaviour (directories only) */
+#define EXT4_INODE_FLAG_TOPDIR 0x00020000    /* Top of directory hierarchies */
+#define EXT4_INODE_FLAG_HUGE_FILE 0x00040000 /* Set to each huge file */
+#define EXT4_INODE_FLAG_EXTENTS 0x00080000   /* Inode uses extents */
+#define EXT4_INODE_FLAG_EA_INODE 0x00200000  /* Inode used for large EA */
+#define EXT4_INODE_FLAG_EOFBLOCKS 0x00400000 /* Blocks allocated beyond EOF */
+#define EXT4_INODE_FLAG_RESERVED 0x80000000  /* reserved for ext4 lib */
+
+#define EXT4_INODE_ROOT_INDEX 2
+
+struct ext4_inode_ref {
+       struct ext4_block block;
+       struct ext4_inode *inode;
+       struct ext4_fs *fs;
+       uint32_t index;
+       bool dirty;
+};
+
+#define EXT4_DIRECTORY_FILENAME_LEN 255
+
+/**@brief   Directory entry types. */
+enum { EXT4_DE_UNKNOWN = 0,
+       EXT4_DE_REG_FILE,
+       EXT4_DE_DIR,
+       EXT4_DE_CHRDEV,
+       EXT4_DE_BLKDEV,
+       EXT4_DE_FIFO,
+       EXT4_DE_SOCK,
+       EXT4_DE_SYMLINK };
+
+#define EXT4_DIRENTRY_DIR_CSUM 0xDE
+
+#pragma pack(push, 1)
+
+union ext4_dir_en_internal {
+       uint8_t name_length_high; /* Higher 8 bits of name length */
+       uint8_t inode_type;       /* Type of referenced inode (in rev >= 0.5) */
+};
+
+/**
+ * Linked list directory entry structure
+ */
+struct ext4_dir_en {
+       uint32_t inode; /* I-node for the entry */
+       uint16_t entry_len; /* Distance to the next directory entry */
+       uint8_t name_len;   /* Lower 8 bits of name length */
+
+       union ext4_dir_en_internal in;
+
+       uint8_t name[EXT4_DIRECTORY_FILENAME_LEN]; /* Entry name */
+};
+
+struct ext4_dir_iter {
+       struct ext4_inode_ref *inode_ref;
+       struct ext4_block curr_blk;
+       uint64_t curr_off;
+       struct ext4_dir_en *curr;
+};
+
+struct ext4_dir_search_result {
+       struct ext4_block block;
+       struct ext4_dir_en *dentry;
+};
+
+/* Structures for indexed directory */
+
+struct ext4_dir_idx_climit {
+       uint16_t limit;
+       uint16_t count;
+};
+
+struct ext4_dir_idx_dot_en {
+       uint32_t inode;
+       uint16_t entry_length;
+       uint8_t name_length;
+       uint8_t inode_type;
+       uint8_t name[4];
+};
+
+struct ext4_dir_idx_rinfo {
+       uint32_t reserved_zero;
+       uint8_t hash_version;
+       uint8_t info_length;
+       uint8_t indirect_levels;
+       uint8_t unused_flags;
+};
+
+struct ext4_dir_idx_entry {
+       uint32_t hash;
+       uint32_t block;
+};
+
+struct ext4_dir_idx_root {
+       struct ext4_dir_idx_dot_en dots[2];
+       struct ext4_dir_idx_rinfo info;
+       struct ext4_dir_idx_entry en[];
+};
+
+struct ext4_fake_dir_entry {
+       uint32_t inode;
+       uint16_t entry_length;
+       uint8_t name_length;
+       uint8_t inode_type;
+};
+
+struct ext4_dir_idx_node {
+       struct ext4_fake_dir_entry fake;
+       struct ext4_dir_idx_entry entries[];
+};
+
+struct ext4_dir_idx_block {
+       struct ext4_block b;
+       struct ext4_dir_idx_entry *entries;
+       struct ext4_dir_idx_entry *position;
+};
+
+/*
+ * This goes at the end of each htree block.
+ */
+struct ext4_dir_idx_tail {
+       uint32_t reserved;
+       uint32_t checksum;      /* crc32c(uuid+inum+dirblock) */
+};
+
+/*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext4_dir_entry_tail {
+       uint32_t reserved_zero1;        /* Pretend to be unused */
+       uint16_t rec_len;               /* 12 */
+       uint8_t reserved_zero2; /* Zero name length */
+       uint8_t reserved_ft;    /* 0xDE, fake file type */
+       uint32_t checksum;              /* crc32c(uuid+inum+dirblock) */
+};
+
+#pragma pack(pop)
+
+#define EXT4_DIRENT_TAIL(block, blocksize) \
+       ((struct ext4_dir_entry_tail *)(((char *)(block)) + ((blocksize) - \
+                                       sizeof(struct ext4_dir_entry_tail))))
+
+#define EXT4_ERR_BAD_DX_DIR (-25000)
+
+#define EXT4_LINK_MAX 65000
+
+#define EXT4_BAD_INO 1
+#define EXT4_ROOT_INO 2
+#define EXT4_BOOT_LOADER_INO 5
+#define EXT4_UNDEL_DIR_INO 6
+#define EXT4_RESIZE_INO 7
+#define EXT4_JOURNAL_INO 8
+
+#define EXT4_GOOD_OLD_FIRST_INO 11
+
+#define EXT4_EXT_UNWRITTEN_MASK (1L << 15)
+
+#define EXT4_EXT_MAX_LEN_WRITTEN (1L << 15)
+#define EXT4_EXT_MAX_LEN_UNWRITTEN \
+       (EXT4_EXT_MAX_LEN_WRITTEN - 1)
+
+#define EXT4_EXT_GET_LEN(ex) to_le16((ex)->block_count)
+#define EXT4_EXT_GET_LEN_UNWRITTEN(ex) \
+       (EXT4_EXT_GET_LEN(ex) &= ~(EXT4_EXT_UNWRITTEN_MASK))
+#define EXT4_EXT_SET_LEN(ex, count) \
+       ((ex)->block_count = to_le16(count))
+
+#define EXT4_EXT_IS_UNWRITTEN(ex) \
+       (EXT4_EXT_GET_LEN(ex) > EXT4_EXT_MAX_LEN_WRITTEN)
+#define EXT4_EXT_SET_UNWRITTEN(ex) \
+       ((ex)->block_count |= to_le16(EXT4_EXT_UNWRITTEN_MASK))
+#define EXT4_EXT_SET_WRITTEN(ex) \
+       ((ex)->block_count &= ~(to_le16(EXT4_EXT_UNWRITTEN_MASK)))
+
+#pragma pack(push, 1)
+
+/*
+ * This is the extent tail on-disk structure.
+ * All other extent structures are 12 bytes long.  It turns out that
+ * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
+ * covers all valid ext4 block sizes.  Therefore, this tail structure can be
+ * crammed into the end of the block without having to rebalance the tree.
+ */
+struct ext4_extent_tail
+{
+       uint32_t et_checksum; /* crc32c(uuid+inum+extent_block) */
+};
+
+/*
+ * This is the extent on-disk structure.
+ * It's used at the bottom of the tree.
+ */
+struct ext4_extent {
+       uint32_t first_block; /* First logical block extent covers */
+       uint16_t block_count; /* Number of blocks covered by extent */
+       uint16_t start_hi;    /* High 16 bits of physical block */
+       uint32_t start_lo;    /* Low 32 bits of physical block */
+};
+
+/*
+ * This is index on-disk structure.
+ * It's used at all the levels except the bottom.
+ */
+struct ext4_extent_index {
+       uint32_t first_block; /* Index covers logical blocks from 'block' */
+
+       /**
+        * Pointer to the physical block of the next
+        * level. leaf or next index could be there
+        * high 16 bits of physical block
+        */
+       uint32_t leaf_lo;
+       uint16_t leaf_hi;
+       uint16_t padding;
+};
+
+/*
+ * Each block (leaves and indexes), even inode-stored has header.
+ */
+struct ext4_extent_header {
+       uint16_t magic;
+       uint16_t entries_count;     /* Number of valid entries */
+       uint16_t max_entries_count; /* Capacity of store in entries */
+       uint16_t depth;             /* Has tree real underlying blocks? */
+       uint32_t generation;    /* generation of the tree */
+};
+
+#pragma pack(pop)
+
+
+/*
+ * Types of blocks.
+ */
+typedef uint32_t ext4_lblk_t;
+typedef uint64_t ext4_fsblk_t;
+
+/*
+ * Array of ext4_ext_path contains path to some extent.
+ * Creation/lookup routines use it for traversal/splitting/etc.
+ * Truncate uses it to simulate recursive walking.
+ */
+struct ext4_extent_path {
+       ext4_fsblk_t p_block;
+       struct ext4_block block;
+       int32_t depth;
+       int32_t maxdepth;
+       struct ext4_extent_header *header;
+       struct ext4_extent_index *index;
+       struct ext4_extent *extent;
+
+};
+
+
+#define EXT4_EXTENT_MAGIC 0xF30A
+
+#define EXT4_EXTENT_FIRST(header)                                              \
+       ((struct ext4_extent *)(((char *)(header)) +                           \
+                               sizeof(struct ext4_extent_header)))
+
+#define EXT4_EXTENT_FIRST_INDEX(header)                                        \
+       ((struct ext4_extent_index *)(((char *)(header)) +                     \
+                                     sizeof(struct ext4_extent_header)))
+
+/*
+ * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an
+ * initialized extent. This is 2^15 and not (2^16 - 1), since we use the
+ * MSB of ee_len field in the extent datastructure to signify if this
+ * particular extent is an initialized extent or an uninitialized (i.e.
+ * preallocated).
+ * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an
+ * uninitialized extent.
+ * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an
+ * uninitialized one. In other words, if MSB of ee_len is set, it is an
+ * uninitialized extent with only one special scenario when ee_len = 0x8000.
+ * In this case we can not have an uninitialized extent of zero length and
+ * thus we make it as a special case of initialized extent with 0x8000 length.
+ * This way we get better extent-to-group alignment for initialized extents.
+ * Hence, the maximum number of blocks we can have in an *initialized*
+ * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767).
+ */
+#define EXT_INIT_MAX_LEN (1L << 15)
+#define EXT_UNWRITTEN_MAX_LEN (EXT_INIT_MAX_LEN - 1)
+
+#define EXT_EXTENT_SIZE sizeof(struct ext4_extent)
+#define EXT_INDEX_SIZE sizeof(struct ext4_extent_idx)
+
+#define EXT_FIRST_EXTENT(__hdr__)                                              \
+       ((struct ext4_extent *)(((char *)(__hdr__)) +                          \
+                               sizeof(struct ext4_extent_header)))
+#define EXT_FIRST_INDEX(__hdr__)                                               \
+       ((struct ext4_extent_index *)(((char *)(__hdr__)) +                    \
+                                   sizeof(struct ext4_extent_header)))
+#define EXT_HAS_FREE_INDEX(__path__)                                           \
+       ((__path__)->header->entries_count < (__path__)->header->max_entries_count)
+#define EXT_LAST_EXTENT(__hdr__)                                               \
+       (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->entries_count - 1)
+#define EXT_LAST_INDEX(__hdr__)                                                \
+       (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->entries_count - 1)
+#define EXT_MAX_EXTENT(__hdr__)                                                \
+       (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->max_entries_count - 1)
+#define EXT_MAX_INDEX(__hdr__)                                                 \
+       (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->max_entries_count - 1)
+
+#define EXT4_EXTENT_TAIL_OFFSET(hdr)                                           \
+       (sizeof(struct ext4_extent_header) +                                   \
+        (sizeof(struct ext4_extent) * (hdr)->max_entries_count))
+
+/*
+ * ext4_ext_next_allocated_block:
+ * returns allocated block in subsequent extent or EXT_MAX_BLOCKS.
+ * NOTE: it considers block number from index entry as
+ * allocated block. Thus, index entries have to be consistent
+ * with leaves.
+ */
+#define EXT_MAX_BLOCKS (ext4_lblk_t) (-1)
+
+#define IN_RANGE(b, first, len)        ((b) >= (first) && (b) <= (first) + (len) - 1)
+
+
+/******************************************************************************/
+
+/* EXT3 HTree directory indexing */
+#define EXT2_HTREE_LEGACY 0
+#define EXT2_HTREE_HALF_MD4 1
+#define EXT2_HTREE_TEA 2
+#define EXT2_HTREE_LEGACY_UNSIGNED 3
+#define EXT2_HTREE_HALF_MD4_UNSIGNED 4
+#define EXT2_HTREE_TEA_UNSIGNED 5
+
+#define EXT2_HTREE_EOF 0x7FFFFFFFUL
+
+struct ext4_hash_info {
+       uint32_t hash;
+       uint32_t minor_hash;
+       uint32_t hash_version;
+       const uint32_t *seed;
+};
+
+/* Extended Attribute(EA) */
+
+/* Magic value in attribute blocks */
+#define EXT4_XATTR_MAGIC               0xEA020000
+
+/* Maximum number of references to one attribute block */
+#define EXT4_XATTR_REFCOUNT_MAX                1024
+
+/* Name indexes */
+#define EXT4_XATTR_INDEX_USER                  1
+#define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS      2
+#define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT     3
+#define EXT4_XATTR_INDEX_TRUSTED               4
+#define        EXT4_XATTR_INDEX_LUSTRE                 5
+#define EXT4_XATTR_INDEX_SECURITY              6
+#define EXT4_XATTR_INDEX_SYSTEM                        7
+#define EXT4_XATTR_INDEX_RICHACL               8
+#define EXT4_XATTR_INDEX_ENCRYPTION            9
+
+#pragma pack(push, 1)
+
+struct ext4_xattr_header {
+       uint32_t h_magic;       /* magic number for identification */
+       uint32_t h_refcount;    /* reference count */
+       uint32_t h_blocks;      /* number of disk blocks used */
+       uint32_t h_hash;                /* hash value of all attributes */
+       uint32_t h_checksum;    /* crc32c(uuid+id+xattrblock) */
+                               /* id = inum if refcount=1, blknum otherwise */
+       uint32_t h_reserved[3]; /* zero right now */
+};
+
+struct ext4_xattr_ibody_header {
+       uint32_t h_magic;       /* magic number for identification */
+};
+
+struct ext4_xattr_entry {
+       uint8_t e_name_len;     /* length of name */
+       uint8_t e_name_index;   /* attribute name index */
+       uint16_t e_value_offs;  /* offset in disk block of value */
+       uint32_t e_value_block; /* disk block attribute is stored on (n/i) */
+       uint32_t e_value_size;  /* size of attribute value */
+       uint32_t e_hash;                /* hash value of name and value */
+};
+
+#pragma pack(pop)
+
+struct ext4_xattr_item {
+       /* This attribute should be stored in inode body */
+       bool in_inode;
+
+       uint8_t name_index;
+       char  *name;
+       size_t name_len;
+       void  *data;
+       size_t data_size;
+
+       RB_ENTRY(ext4_xattr_item) node;
+};
+
+struct ext4_xattr_ref {
+       bool block_loaded;
+       struct ext4_block block;
+       struct ext4_inode_ref *inode_ref;
+       bool   dirty;
+       size_t ea_size;
+       struct ext4_fs *fs;
+
+       void *iter_arg;
+       struct ext4_xattr_item *iter_from;
+
+       RB_HEAD(ext4_xattr_tree,
+               ext4_xattr_item) root;
+};
+
+#define EXT4_XATTR_ITERATE_CONT 0
+#define EXT4_XATTR_ITERATE_STOP 1
+#define EXT4_XATTR_ITERATE_PAUSE 2
+
+#define EXT4_GOOD_OLD_INODE_SIZE       128
+
+#define EXT4_XATTR_PAD_BITS            2
+#define EXT4_XATTR_PAD         (1<<EXT4_XATTR_PAD_BITS)
+#define EXT4_XATTR_ROUND               (EXT4_XATTR_PAD-1)
+#define EXT4_XATTR_LEN(name_len) \
+       (((name_len) + EXT4_XATTR_ROUND + \
+       sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
+#define EXT4_XATTR_NEXT(entry) \
+       ((struct ext4_xattr_entry *)( \
+        (char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)))
+#define EXT4_XATTR_SIZE(size) \
+       (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
+#define EXT4_XATTR_NAME(entry) \
+       ((char *)((entry) + 1))
+
+#define EXT4_XATTR_IHDR(raw_inode) \
+       ((struct ext4_xattr_ibody_header *) \
+               ((char *)raw_inode + \
+               EXT4_GOOD_OLD_INODE_SIZE + \
+               (raw_inode)->extra_isize))
+#define EXT4_XATTR_IFIRST(hdr) \
+       ((struct ext4_xattr_entry *)((hdr)+1))
+
+#define EXT4_XATTR_BHDR(block) \
+       ((struct ext4_xattr_header *)((block)->data))
+#define EXT4_XATTR_ENTRY(ptr) \
+       ((struct ext4_xattr_entry *)(ptr))
+#define EXT4_XATTR_BFIRST(block) \
+       EXT4_XATTR_ENTRY(EXT4_XATTR_BHDR(block)+1)
+#define EXT4_XATTR_IS_LAST_ENTRY(entry) \
+       (*(uint32_t *)(entry) == 0)
+
+#define EXT4_ZERO_XATTR_VALUE ((void *)-1)
+
+/*****************************************************************************/
+
+/*
+ * JBD stores integers in big endian.
+ */
+
+#define JBD_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JBD_DESCRIPTOR_BLOCK   1
+#define JBD_COMMIT_BLOCK       2
+#define JBD_SUPERBLOCK         3
+#define JBD_SUPERBLOCK_V2      4
+#define JBD_REVOKE_BLOCK       5
+
+#pragma pack(push, 1)
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+struct jbd_bhdr {
+       uint32_t                magic;
+       uint32_t                blocktype;
+       uint32_t                sequence;
+};
+
+#pragma pack(pop)
+
+/*
+ * Checksum types.
+ */
+#define JBD_CRC32_CHKSUM   1
+#define JBD_MD5_CHKSUM     2
+#define JBD_SHA1_CHKSUM    3
+#define JBD_CRC32C_CHKSUM  4
+
+#define JBD_CRC32_CHKSUM_SIZE 4
+
+#define JBD_CHECKSUM_BYTES (32 / sizeof(uint32_t))
+
+#pragma pack(push, 1)
+
+/*
+ * Commit block header for storing transactional checksums:
+ *
+ * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
+ * fields are used to store a checksum of the descriptor and data blocks.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
+ * field is used to store crc32c(uuid+commit_block).  Each journal metadata
+ * block gets its own checksum, and data block checksums are stored in
+ * journal_block_tag (in the descriptor).  The other h_chksum* fields are
+ * not used.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses
+ * journal_block_tag3_t to store a full 32-bit checksum.  Everything else
+ * is the same as v2.
+ *
+ * Checksum v1, v2, and v3 are mutually exclusive features.
+ */
+
+struct jbd_commit_header {
+       struct jbd_bhdr header;
+       uint8_t chksum_type;
+       uint8_t chksum_size;
+       uint8_t padding[2];
+       uint32_t                chksum[JBD_CHECKSUM_BYTES];
+       uint64_t                commit_sec;
+       uint32_t                commit_nsec;
+};
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+struct jbd_block_tag3 {
+       uint32_t                blocknr;        /* The on-disk block number */
+       uint32_t                flags;  /* See below */
+       uint32_t                blocknr_high; /* most-significant high 32bits. */
+       uint32_t                checksum;       /* crc32c(uuid+seq+block) */
+};
+
+struct jbd_block_tag {
+       uint32_t                blocknr;        /* The on-disk block number */
+       uint16_t                checksum;       /* truncated crc32c(uuid+seq+block) */
+       uint16_t                flags;  /* See below */
+       uint32_t                blocknr_high; /* most-significant high 32bits. */
+};
+
+#pragma pack(pop)
+
+/* Definitions for the journal tag flags word: */
+#define JBD_FLAG_ESCAPE                1       /* on-disk block is escaped */
+#define JBD_FLAG_SAME_UUID     2       /* block has same uuid as previous */
+#define JBD_FLAG_DELETED       4       /* block deleted by this transaction */
+#define JBD_FLAG_LAST_TAG      8       /* last tag in this descriptor block */
+
+#pragma pack(push, 1)
+
+/* Tail of descriptor block, for checksumming */
+struct jbd_block_tail {
+       uint32_t        checksum;
+};
+
+/*
+ * The revoke descriptor: used on disk to describe a series of blocks to
+ * be revoked from the log
+ */
+struct jbd_revoke_header {
+       struct jbd_bhdr  header;
+       uint32_t         count; /* Count of bytes used in the block */
+};
+
+/* Tail of revoke block, for checksumming */
+struct jbd_revoke_tail {
+       uint32_t                checksum;
+};
+
+#pragma pack(pop)
+
+#define JBD_USERS_MAX 48
+#define JBD_USERS_SIZE (UUID_SIZE * JBD_USERS_MAX)
+
+#pragma pack(push, 1)
+
+/*
+ * The journal superblock.  All fields are in big-endian byte order.
+ */
+struct jbd_sb {
+/* 0x0000 */
+       struct jbd_bhdr header;
+
+/* 0x000C */
+       /* Static information describing the journal */
+       uint32_t        blocksize;              /* journal device blocksize */
+       uint32_t        maxlen;         /* total blocks in journal file */
+       uint32_t        first;          /* first block of log information */
+
+/* 0x0018 */
+       /* Dynamic information describing the current state of the log */
+       uint32_t        sequence;               /* first commit ID expected in log */
+       uint32_t        start;          /* blocknr of start of log */
+
+/* 0x0020 */
+       /* Error value, as set by journal_abort(). */
+       int32_t         error_val;
+
+/* 0x0024 */
+       /* Remaining fields are only valid in a version-2 superblock */
+       uint32_t        feature_compat;         /* compatible feature set */
+       uint32_t        feature_incompat;       /* incompatible feature set */
+       uint32_t        feature_ro_compat;      /* readonly-compatible feature set */
+/* 0x0030 */
+       uint8_t         uuid[UUID_SIZE];                /* 128-bit uuid for journal */
+
+/* 0x0040 */
+       uint32_t        nr_users;               /* Nr of filesystems sharing log */
+
+       uint32_t        dynsuper;               /* Blocknr of dynamic superblock copy*/
+
+/* 0x0048 */
+       uint32_t        max_transaction;        /* Limit of journal blocks per trans.*/
+       uint32_t        max_trandata;   /* Limit of data blocks per trans. */
+
+/* 0x0050 */
+       uint8_t         checksum_type;  /* checksum type */
+       uint8_t         padding2[3];
+       uint32_t        padding[42];
+       uint32_t        checksum;               /* crc32c(superblock) */
+
+/* 0x0100 */
+       uint8_t         users[JBD_USERS_SIZE];          /* ids of all fs'es sharing the log */
+
+/* 0x0400 */
+};
+
+#pragma pack(pop)
+
+#define JBD_SUPERBLOCK_SIZE sizeof(struct jbd_sb)
+
+#define JBD_HAS_COMPAT_FEATURE(jsb,mask)                                       \
+       ((jsb)->header.blocktype >= to_be32(2) &&                               \
+        ((jsb)->feature_compat & to_be32((mask))))
+#define JBD_HAS_RO_COMPAT_FEATURE(jsb,mask)                            \
+       ((jsb)->header.blocktype >= to_be32(2) &&                               \
+        ((jsb)->feature_ro_compat & to_be32((mask))))
+#define JBD_HAS_INCOMPAT_FEATURE(jsb,mask)                             \
+       ((jsb)->header.blocktype >= to_be32(2) &&                               \
+        ((jsb)->feature_incompat & to_be32((mask))))
+
+#define JBD_FEATURE_COMPAT_CHECKSUM    0x00000001
+
+#define JBD_FEATURE_INCOMPAT_REVOKE            0x00000001
+#define JBD_FEATURE_INCOMPAT_64BIT             0x00000002
+#define JBD_FEATURE_INCOMPAT_ASYNC_COMMIT      0x00000004
+#define JBD_FEATURE_INCOMPAT_CSUM_V2           0x00000008
+#define JBD_FEATURE_INCOMPAT_CSUM_V3           0x00000010
+
+/* Features known to this kernel version: */
+#define JBD_KNOWN_COMPAT_FEATURES      0
+#define JBD_KNOWN_ROCOMPAT_FEATURES    0
+#define JBD_KNOWN_INCOMPAT_FEATURES    (JBD_FEATURE_INCOMPAT_REVOKE|\
+                                        JBD_FEATURE_INCOMPAT_ASYNC_COMMIT|\
+                                        JBD_FEATURE_INCOMPAT_64BIT|\
+                                        JBD_FEATURE_INCOMPAT_CSUM_V2|\
+                                        JBD_FEATURE_INCOMPAT_CSUM_V3)
+
+struct jbd_fs {
+       /* If journal block device is used, bdev will be non-null */
+       struct ext4_blockdev *bdev;
+       struct ext4_inode_ref inode_ref;
+       struct jbd_sb sb;
+
+       bool dirty;
+};
+
+struct jbd_buf {
+       uint64_t jbd_lba;
+       struct ext4_block block;
+       struct jbd_trans *trans;
+       struct jbd_block_rec *block_rec;
+       TAILQ_ENTRY(jbd_buf) buf_node;
+       TAILQ_ENTRY(jbd_buf) dirty_buf_node;
+};
+
+struct jbd_revoke_rec {
+       ext4_fsblk_t lba;
+       LIST_ENTRY(jbd_revoke_rec) revoke_node;
+};
+
+struct jbd_block_rec {
+       ext4_fsblk_t lba;
+       struct ext4_buf *buf;
+       struct jbd_trans *trans;
+       RB_ENTRY(jbd_block_rec) block_rec_node;
+       LIST_ENTRY(jbd_block_rec) tbrec_node;
+       TAILQ_HEAD(jbd_buf_dirty, jbd_buf) dirty_buf_queue;
+};
+
+struct jbd_trans {
+       uint32_t trans_id;
+
+       uint32_t start_iblock;
+       int alloc_blocks;
+       int data_cnt;
+       uint32_t data_csum;
+       int written_cnt;
+       int error;
+
+       struct jbd_journal *journal;
+
+       TAILQ_HEAD(jbd_trans_buf, jbd_buf) buf_queue;
+       LIST_HEAD(jbd_revoke_list, jbd_revoke_rec) revoke_list;
+       LIST_HEAD(jbd_trans_block_rec, jbd_block_rec) tbrec_list;
+       TAILQ_ENTRY(jbd_trans) trans_node;
+};
+
+struct jbd_journal {
+       uint32_t first;
+       uint32_t start;
+       uint32_t last;
+       uint32_t trans_id;
+       uint32_t alloc_trans_id;
+
+       uint32_t block_size;
+
+       TAILQ_HEAD(jbd_trans_queue, jbd_trans) trans_queue;
+       TAILQ_HEAD(jbd_cp_queue, jbd_trans) cp_queue;
+       RB_HEAD(jbd_block, jbd_block_rec) block_rec_root;
+
+       struct jbd_fs *jbd_fs;
+};
+
+/*****************************************************************************/
+
+#define EXT4_CRC32_INIT (0xFFFFFFFFUL)
+
+/*****************************************************************************/
+
+static inline uint64_t reorder64(uint64_t n)
+{
+       return  ((n & 0xff) << 56) |
+               ((n & 0xff00) << 40) |
+               ((n & 0xff0000) << 24) |
+               ((n & 0xff000000LL) << 8) |
+               ((n & 0xff00000000LL) >> 8) |
+               ((n & 0xff0000000000LL) >> 24) |
+               ((n & 0xff000000000000LL) >> 40) |
+               ((n & 0xff00000000000000LL) >> 56);
+}
+
+static inline uint32_t reorder32(uint32_t n)
+{
+       return  ((n & 0xff) << 24) |
+               ((n & 0xff00) << 8) |
+               ((n & 0xff0000) >> 8) |
+               ((n & 0xff000000) >> 24);
+}
+
+static inline uint16_t reorder16(uint16_t n)
+{
+       return  ((n & 0xff) << 8) |
+               ((n & 0xff00) >> 8);
+}
+
+#ifdef CONFIG_BIG_ENDIAN
+#define to_le64(_n) reorder64(_n)
+#define to_le32(_n) reorder32(_n)
+#define to_le16(_n) reorder16(_n)
+
+#define to_be64(_n) _n
+#define to_be32(_n) _n
+#define to_be16(_n) _n
+
+#else
+#define to_le64(_n) _n
+#define to_le32(_n) _n
+#define to_le16(_n) _n
+
+#define to_be64(_n) reorder64(_n)
+#define to_be32(_n) reorder32(_n)
+#define to_be16(_n) reorder16(_n)
+#endif
+
+/****************************Access macros to ext4 structures*****************/
+
+#define ext4_get32(s, f) to_le32((s)->f)
+#define ext4_get16(s, f) to_le16((s)->f)
+#define ext4_get8(s, f) (s)->f
+
+#define ext4_set32(s, f, v)                                                    \
+       do {                                                                   \
+               (s)->f = to_le32(v);                                           \
+       } while (0)
+#define ext4_set16(s, f, v)                                                    \
+       do {                                                                   \
+               (s)->f = to_le16(v);                                           \
+       } while (0)
+#define ext4_set8                                                              \
+       (s, f, v) do { (s)->f = (v); }                                         \
+       while (0)
+
+/****************************Access macros to jbd2 structures*****************/
+
+#define jbd_get32(s, f) to_be32((s)->f)
+#define jbd_get16(s, f) to_be16((s)->f)
+#define jbd_get8(s, f) (s)->f
+
+#define jbd_set32(s, f, v)                                                    \
+       do {                                                                   \
+               (s)->f = to_be32(v);                                           \
+       } while (0)
+#define jbd_set16(s, f, v)                                                    \
+       do {                                                                   \
+               (s)->f = to_be16(v);                                           \
+       } while (0)
+#define jbd_set8                                                              \
+       (s, f, v) do { (s)->f = (v); }                                         \
+       while (0)
+
+#ifdef __GNUC__
+ #ifndef __unused
+ #define __unused __attribute__ ((__unused__))
+ #endif
+#else
+ #define __unused
+#endif
+
+#ifndef offsetof
+#define offsetof(type, field)          \
+       ((size_t)(&(((type *)0)->field)))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXT4_TYPES_H_ */
+
+/**
+ * @}
+ */
diff --git a/include/ext4_xattr.h b/include/ext4_xattr.h
new file mode 100644 (file)
index 0000000..f6076d2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
+ *
+ * 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_xattr.h
+ * @brief Extended Attribute manipulation.
+ */
+
+#ifndef EXT4_XATTR_H_
+#define EXT4_XATTR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+
+int ext4_fs_get_xattr_ref(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
+                         struct ext4_xattr_ref *ref);
+
+void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref);
+
+int ext4_fs_set_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
+                     const char *name, size_t name_len, const void *data,
+                     size_t data_size, bool replace);
+
+int ext4_fs_remove_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
+                        const char *name, size_t name_len);
+
+int ext4_fs_get_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
+                     const char *name, size_t name_len, void *buf,
+                     size_t buf_size, size_t *data_size);
+
+void ext4_fs_xattr_iterate(struct ext4_xattr_ref *ref,
+                          int (*iter)(struct ext4_xattr_ref *ref,
+                                    struct ext4_xattr_item *item));
+
+void ext4_fs_xattr_iterate_reset(struct ext4_xattr_ref *ref);
+
+const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
+                             uint8_t *name_index, size_t *name_len);
+
+const char *ext4_get_xattr_name_prefix(uint8_t name_index,
+                                      size_t *ret_prefix_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/**
+ * @}
+ */
diff --git a/include/misc/queue.h b/include/misc/queue.h
new file mode 100644 (file)
index 0000000..6efd6f3
--- /dev/null
@@ -0,0 +1,702 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * 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.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../ext4_config.h"
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *                             SLIST   LIST    STAILQ  TAILQ
+ * _HEAD                       +       +       +       +
+ * _HEAD_INITIALIZER           +       +       +       +
+ * _ENTRY                      +       +       +       +
+ * _INIT                       +       +       +       +
+ * _EMPTY                      +       +       +       +
+ * _FIRST                      +       +       +       +
+ * _NEXT                       +       +       +       +
+ * _PREV                       -       +       -       +
+ * _LAST                       -       -       +       +
+ * _FOREACH                    +       +       +       +
+ * _FOREACH_FROM               +       +       +       +
+ * _FOREACH_SAFE               +       +       +       +
+ * _FOREACH_FROM_SAFE          +       +       +       +
+ * _FOREACH_REVERSE            -       -       -       +
+ * _FOREACH_REVERSE_FROM       -       -       -       +
+ * _FOREACH_REVERSE_SAFE       -       -       -       +
+ * _FOREACH_REVERSE_FROM_SAFE  -       -       -       +
+ * _INSERT_HEAD                        +       +       +       +
+ * _INSERT_BEFORE              -       +       -       +
+ * _INSERT_AFTER               +       +       +       +
+ * _INSERT_TAIL                        -       -       +       +
+ * _CONCAT                     -       -       +       +
+ * _REMOVE_AFTER               +       -       +       -
+ * _REMOVE_HEAD                        +       -       +       -
+ * _REMOVE                     +       +       +       +
+ * _SWAP                       +       +       +       +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+       unsigned long    lastline;
+       unsigned long    prevline;
+       const char      *lastfile;
+       const char      *prevfile;
+};
+
+#define        TRACEBUF        struct qm_trace trace;
+#define        TRACEBUF_INITIALIZER    { __LINE__, 0, __FILE__, NULL } ,
+#define        TRASHIT(x)      do {(x) = (void *)-1;} while (0)
+#define        QMD_SAVELINK(name, link)        void **name = (void *)&(link)
+
+#define        QMD_TRACE_HEAD(head) do {                                       \
+       (head)->trace.prevline = (head)->trace.lastline;                \
+       (head)->trace.prevfile = (head)->trace.lastfile;                \
+       (head)->trace.lastline = __LINE__;                              \
+       (head)->trace.lastfile = __FILE__;                              \
+} while (0)
+
+#define        QMD_TRACE_ELEM(elem) do {                                       \
+       (elem)->trace.prevline = (elem)->trace.lastline;                \
+       (elem)->trace.prevfile = (elem)->trace.lastfile;                \
+       (elem)->trace.lastline = __LINE__;                              \
+       (elem)->trace.lastfile = __FILE__;                              \
+} while (0)
+
+#else
+#define        QMD_TRACE_ELEM(elem)
+#define        QMD_TRACE_HEAD(head)
+#define        QMD_SAVELINK(name, link)
+#define        TRACEBUF
+#define        TRACEBUF_INITIALIZER
+#define        TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define        SLIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define        SLIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
+
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_FOREACH_FROM(var, head, field)                            \
+       for ((var) = ((var) ? (var) : SLIST_FIRST((head)));             \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        SLIST_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
+       for ((var) = ((var) ? (var) : SLIST_FIRST((head)));             \
+           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        SLIST_FOREACH_PREVPTR(var, varp, head, field)                   \
+       for ((varp) = &SLIST_FIRST((head));                             \
+           ((var) = *(varp)) != NULL;                                  \
+           (varp) = &SLIST_NEXT((var), field))
+
+#define        SLIST_INIT(head) do {                                           \
+       SLIST_FIRST((head)) = NULL;                                     \
+} while (0)
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
+       SLIST_NEXT((slistelm), field) = (elm);                          \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
+       SLIST_FIRST((head)) = (elm);                                    \
+} while (0)
+
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_REMOVE(head, elm, type, field) do {                       \
+       QMD_SAVELINK(oldnext, (elm)->field.sle_next);                   \
+       if (SLIST_FIRST((head)) == (elm)) {                             \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = SLIST_FIRST((head));              \
+               while (SLIST_NEXT(curelm, field) != (elm))              \
+                       curelm = SLIST_NEXT(curelm, field);             \
+               SLIST_REMOVE_AFTER(curelm, field);                      \
+       }                                                               \
+       TRASHIT(*oldnext);                                              \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do {                            \
+       SLIST_NEXT(elm, field) =                                        \
+           SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
+} while (0)
+
+#define SLIST_SWAP(head1, head2, type) do {                            \
+       struct type *swap_first = SLIST_FIRST(head1);                   \
+       SLIST_FIRST(head1) = SLIST_FIRST(head2);                        \
+       SLIST_FIRST(head2) = swap_first;                                \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define        STAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *stqh_first;/* first element */                     \
+       struct type **stqh_last;/* addr of last next element */         \
+}
+
+#define        STAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).stqh_first }
+
+#define        STAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *stqe_next; /* next element */                      \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define        STAILQ_CONCAT(head1, head2) do {                                \
+       if (!STAILQ_EMPTY((head2))) {                                   \
+               *(head1)->stqh_last = (head2)->stqh_first;              \
+               (head1)->stqh_last = (head2)->stqh_last;                \
+               STAILQ_INIT((head2));                                   \
+       }                                                               \
+} while (0)
+
+#define        STAILQ_EMPTY(head)      ((head)->stqh_first == NULL)
+
+#define        STAILQ_FIRST(head)      ((head)->stqh_first)
+
+#define        STAILQ_FOREACH(var, head, field)                                \
+       for((var) = STAILQ_FIRST((head));                               \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+#define        STAILQ_FOREACH_FROM(var, head, field)                           \
+       for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));            \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+#define        STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
+       for ((var) = STAILQ_FIRST((head));                              \
+           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
+           (var) = (tvar))
+
+#define        STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                \
+       for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));            \
+           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
+           (var) = (tvar))
+
+#define        STAILQ_INIT(head) do {                                          \
+       STAILQ_FIRST((head)) = NULL;                                    \
+       (head)->stqh_last = &STAILQ_FIRST((head));                      \
+} while (0)
+
+#define        STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_NEXT((tqelm), field) = (elm);                            \
+} while (0)
+
+#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_FIRST((head)) = (elm);                                   \
+} while (0)
+
+#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       STAILQ_NEXT((elm), field) = NULL;                               \
+       *(head)->stqh_last = (elm);                                     \
+       (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
+} while (0)
+
+#define        STAILQ_LAST(head, type, field)                                  \
+       (STAILQ_EMPTY((head)) ? NULL :                                  \
+           __containerof((head)->stqh_last, struct type, field.stqe_next))
+
+#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
+       QMD_SAVELINK(oldnext, (elm)->field.stqe_next);                  \
+       if (STAILQ_FIRST((head)) == (elm)) {                            \
+               STAILQ_REMOVE_HEAD((head), field);                      \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = STAILQ_FIRST((head));             \
+               while (STAILQ_NEXT(curelm, field) != (elm))             \
+                       curelm = STAILQ_NEXT(curelm, field);            \
+               STAILQ_REMOVE_AFTER(head, curelm, field);               \
+       }                                                               \
+       TRASHIT(*oldnext);                                              \
+} while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do {                     \
+       if ((STAILQ_NEXT(elm, field) =                                  \
+            STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)      \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
+       if ((STAILQ_FIRST((head)) =                                     \
+            STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do {                           \
+       struct type *swap_first = STAILQ_FIRST(head1);                  \
+       struct type **swap_last = (head1)->stqh_last;                   \
+       STAILQ_FIRST(head1) = STAILQ_FIRST(head2);                      \
+       (head1)->stqh_last = (head2)->stqh_last;                        \
+       STAILQ_FIRST(head2) = swap_first;                               \
+       (head2)->stqh_last = swap_last;                                 \
+       if (STAILQ_EMPTY(head1))                                        \
+               (head1)->stqh_last = &STAILQ_FIRST(head1);              \
+       if (STAILQ_EMPTY(head2))                                        \
+               (head2)->stqh_last = &STAILQ_FIRST(head2);              \
+} while (0)
+
+
+/*
+ * List declarations.
+ */
+#define        LIST_HEAD(name, type)                                           \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define        LIST_HEAD_INITIALIZER(head)                                     \
+       { NULL }
+
+#define        LIST_ENTRY(type)                                                \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define        QMD_LIST_CHECK_HEAD(head, field) do {                           \
+       if (LIST_FIRST((head)) != NULL &&                               \
+           LIST_FIRST((head))->field.le_prev !=                        \
+            &LIST_FIRST((head)))                                       \
+               panic("Bad list head %p first->prev != head", (head));  \
+} while (0)
+
+#define        QMD_LIST_CHECK_NEXT(elm, field) do {                            \
+       if (LIST_NEXT((elm), field) != NULL &&                          \
+           LIST_NEXT((elm), field)->field.le_prev !=                   \
+            &((elm)->field.le_next))                                   \
+               panic("Bad link elm %p next->prev != elm", (elm));      \
+} while (0)
+
+#define        QMD_LIST_CHECK_PREV(elm, field) do {                            \
+       if (*(elm)->field.le_prev != (elm))                             \
+               panic("Bad link elm %p prev->next != elm", (elm));      \
+} while (0)
+#else
+#define        QMD_LIST_CHECK_HEAD(head, field)
+#define        QMD_LIST_CHECK_NEXT(elm, field)
+#define        QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define        LIST_EMPTY(head)        ((head)->lh_first == NULL)
+
+#define        LIST_FIRST(head)        ((head)->lh_first)
+
+#define        LIST_FOREACH(var, head, field)                                  \
+       for ((var) = LIST_FIRST((head));                                \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_FOREACH_FROM(var, head, field)                             \
+       for ((var) = ((var) ? (var) : LIST_FIRST((head)));              \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_FOREACH_SAFE(var, head, field, tvar)                       \
+       for ((var) = LIST_FIRST((head));                                \
+           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
+           (var) = (tvar))
+
+#define        LIST_FOREACH_FROM_SAFE(var, head, field, tvar)                  \
+       for ((var) = ((var) ? (var) : LIST_FIRST((head)));              \
+           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
+           (var) = (tvar))
+
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST((head)) = NULL;                                      \
+} while (0)
+
+#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+       QMD_LIST_CHECK_NEXT(listelm, field);                            \
+       if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+               LIST_NEXT((listelm), field)->field.le_prev =            \
+                   &LIST_NEXT((elm), field);                           \
+       LIST_NEXT((listelm), field) = (elm);                            \
+       (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       QMD_LIST_CHECK_PREV(listelm, field);                            \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       LIST_NEXT((elm), field) = (listelm);                            \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
+} while (0)
+
+#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
+       QMD_LIST_CHECK_HEAD((head), field);                             \
+       if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
+               LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+       LIST_FIRST((head)) = (elm);                                     \
+       (elm)->field.le_prev = &LIST_FIRST((head));                     \
+} while (0)
+
+#define        LIST_NEXT(elm, field)   ((elm)->field.le_next)
+
+#define        LIST_PREV(elm, head, type, field)                               \
+       ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL :           \
+           __containerof((elm)->field.le_prev, struct type, field.le_next))
+
+#define        LIST_REMOVE(elm, field) do {                                    \
+       QMD_SAVELINK(oldnext, (elm)->field.le_next);                    \
+       QMD_SAVELINK(oldprev, (elm)->field.le_prev);                    \
+       QMD_LIST_CHECK_NEXT(elm, field);                                \
+       QMD_LIST_CHECK_PREV(elm, field);                                \
+       if (LIST_NEXT((elm), field) != NULL)                            \
+               LIST_NEXT((elm), field)->field.le_prev =                \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
+       TRASHIT(*oldnext);                                              \
+       TRASHIT(*oldprev);                                              \
+} while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do {                      \
+       struct type *swap_tmp = LIST_FIRST((head1));                    \
+       LIST_FIRST((head1)) = LIST_FIRST((head2));                      \
+       LIST_FIRST((head2)) = swap_tmp;                                 \
+       if ((swap_tmp = LIST_FIRST((head1))) != NULL)                   \
+               swap_tmp->field.le_prev = &LIST_FIRST((head1));         \
+       if ((swap_tmp = LIST_FIRST((head2))) != NULL)                   \
+               swap_tmp->field.le_prev = &LIST_FIRST((head2));         \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define        TAILQ_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+       TRACEBUF                                                        \
+}
+
+#define        TAILQ_HEAD_INITIALIZER(head)                                    \
+       { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
+
+#define        TAILQ_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+       TRACEBUF                                                        \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define        QMD_TAILQ_CHECK_HEAD(head, field) do {                          \
+       if (!TAILQ_EMPTY(head) &&                                       \
+           TAILQ_FIRST((head))->field.tqe_prev !=                      \
+            &TAILQ_FIRST((head)))                                      \
+               panic("Bad tailq head %p first->prev != head", (head)); \
+} while (0)
+
+#define        QMD_TAILQ_CHECK_TAIL(head, field) do {                          \
+       if (*(head)->tqh_last != NULL)                                  \
+               panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head));  \
+} while (0)
+
+#define        QMD_TAILQ_CHECK_NEXT(elm, field) do {                           \
+       if (TAILQ_NEXT((elm), field) != NULL &&                         \
+           TAILQ_NEXT((elm), field)->field.tqe_prev !=                 \
+            &((elm)->field.tqe_next))                                  \
+               panic("Bad link elm %p next->prev != elm", (elm));      \
+} while (0)
+
+#define        QMD_TAILQ_CHECK_PREV(elm, field) do {                           \
+       if (*(elm)->field.tqe_prev != (elm))                            \
+               panic("Bad link elm %p prev->next != elm", (elm));      \
+} while (0)
+#else
+#define        QMD_TAILQ_CHECK_HEAD(head, field)
+#define        QMD_TAILQ_CHECK_TAIL(head, headname)
+#define        QMD_TAILQ_CHECK_NEXT(elm, field)
+#define        QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define        TAILQ_CONCAT(head1, head2, field) do {                          \
+       if (!TAILQ_EMPTY(head2)) {                                      \
+               *(head1)->tqh_last = (head2)->tqh_first;                \
+               (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+               (head1)->tqh_last = (head2)->tqh_last;                  \
+               TAILQ_INIT((head2));                                    \
+               QMD_TRACE_HEAD(head1);                                  \
+               QMD_TRACE_HEAD(head2);                                  \
+       }                                                               \
+} while (0)
+
+#define        TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
+
+#define        TAILQ_FIRST(head)       ((head)->tqh_first)
+
+#define        TAILQ_FOREACH(var, head, field)                                 \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_FROM(var, head, field)                            \
+       for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));             \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
+       for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));             \
+           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field)          \
+       for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));    \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+       for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));    \
+           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
+           (var) = (tvar))
+
+#define        TAILQ_INIT(head) do {                                           \
+       TAILQ_FIRST((head)) = NULL;                                     \
+       (head)->tqh_last = &TAILQ_FIRST((head));                        \
+       QMD_TRACE_HEAD(head);                                           \
+} while (0)
+
+#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+       QMD_TAILQ_CHECK_NEXT(listelm, field);                           \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   &TAILQ_NEXT((elm), field);                          \
+       else {                                                          \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+               QMD_TRACE_HEAD(head);                                   \
+       }                                                               \
+       TAILQ_NEXT((listelm), field) = (elm);                           \
+       (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+       QMD_TRACE_ELEM(&(listelm)->field);                              \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       QMD_TAILQ_CHECK_PREV(listelm, field);                           \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       TAILQ_NEXT((elm), field) = (listelm);                           \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+       QMD_TRACE_ELEM(&(listelm)->field);                              \
+} while (0)
+
+#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+       QMD_TAILQ_CHECK_HEAD(head, field);                              \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
+               TAILQ_FIRST((head))->field.tqe_prev =                   \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_FIRST((head)) = (elm);                                    \
+       (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
+       QMD_TRACE_HEAD(head);                                           \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+       QMD_TAILQ_CHECK_TAIL(head, field);                              \
+       TAILQ_NEXT((elm), field) = NULL;                                \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
+       QMD_TRACE_HEAD(head);                                           \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define        TAILQ_LAST(head, headname)                                      \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define        TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define        TAILQ_PREV(elm, headname, field)                                \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define        TAILQ_REMOVE(head, elm, field) do {                             \
+       QMD_SAVELINK(oldnext, (elm)->field.tqe_next);                   \
+       QMD_SAVELINK(oldprev, (elm)->field.tqe_prev);                   \
+       QMD_TAILQ_CHECK_NEXT(elm, field);                               \
+       QMD_TAILQ_CHECK_PREV(elm, field);                               \
+       if ((TAILQ_NEXT((elm), field)) != NULL)                         \
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   (elm)->field.tqe_prev;                              \
+       else {                                                          \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+               QMD_TRACE_HEAD(head);                                   \
+       }                                                               \
+       *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
+       TRASHIT(*oldnext);                                              \
+       TRASHIT(*oldprev);                                              \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do {                     \
+       struct type *swap_first = (head1)->tqh_first;                   \
+       struct type **swap_last = (head1)->tqh_last;                    \
+       (head1)->tqh_first = (head2)->tqh_first;                        \
+       (head1)->tqh_last = (head2)->tqh_last;                          \
+       (head2)->tqh_first = swap_first;                                \
+       (head2)->tqh_last = swap_last;                                  \
+       if ((swap_first = (head1)->tqh_first) != NULL)                  \
+               swap_first->field.tqe_prev = &(head1)->tqh_first;       \
+       else                                                            \
+               (head1)->tqh_last = &(head1)->tqh_first;                \
+       if ((swap_first = (head2)->tqh_first) != NULL)                  \
+               swap_first->field.tqe_prev = &(head2)->tqh_first;       \
+       else                                                            \
+               (head2)->tqh_last = &(head2)->tqh_first;                \
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/include/misc/tree.h b/include/misc/tree.h
new file mode 100644 (file)
index 0000000..8ed5d41
--- /dev/null
@@ -0,0 +1,809 @@
+/*     $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $  */
+/*     $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $    */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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 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.
+ */
+
+#ifndef        _SYS_TREE_H_
+#define        _SYS_TREE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../ext4_config.h"
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *     - every search path from the root to a leaf consists of the
+ *       same number of black nodes,
+ *     - each red node (except for the root) has a black parent,
+ *     - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *sph_root; /* root of the tree */                   \
+}
+
+#define SPLAY_INITIALIZER(root)                                                \
+       { NULL }
+
+#define SPLAY_INIT(root) do {                                          \
+       (root)->sph_root = NULL;                                        \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *spe_left; /* left element */                       \
+       struct type *spe_right; /* right element */                     \
+}
+
+#define SPLAY_LEFT(elm, field)         (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)                (elm)->field.spe_right
+#define SPLAY_ROOT(head)               (head)->sph_root
+#define SPLAY_EMPTY(head)              (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                      \
+       SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);  \
+       SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \
+       (head)->sph_root = tmp;                                         \
+} while (/*CONSTCOND*/ 0)
+       
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                       \
+       SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);  \
+       SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \
+       (head)->sph_root = tmp;                                         \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {                          \
+       SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \
+       tmp = (head)->sph_root;                                         \
+       (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);         \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {                         \
+       SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \
+       tmp = (head)->sph_root;                                         \
+       (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);        \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {            \
+       SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+       SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+       SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+       SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)                                \
+void name##_SPLAY(struct name *, struct type *);                       \
+void name##_SPLAY_MINMAX(struct name *, int);                          \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);                \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);                \
+                                                                       \
+/* Finds the node with the same key as elm */                          \
+static __inline struct type *                                          \
+name##_SPLAY_FIND(struct name *head, struct type *elm)                 \
+{                                                                      \
+       if (SPLAY_EMPTY(head))                                          \
+               return(NULL);                                           \
+       name##_SPLAY(head, elm);                                        \
+       if ((cmp)(elm, (head)->sph_root) == 0)                          \
+               return (head->sph_root);                                \
+       return (NULL);                                                  \
+}                                                                      \
+                                                                       \
+static __inline struct type *                                          \
+name##_SPLAY_NEXT(struct name *head, struct type *elm)                 \
+{                                                                      \
+       name##_SPLAY(head, elm);                                        \
+       if (SPLAY_RIGHT(elm, field) != NULL) {                          \
+               elm = SPLAY_RIGHT(elm, field);                          \
+               while (SPLAY_LEFT(elm, field) != NULL) {                \
+                       elm = SPLAY_LEFT(elm, field);                   \
+               }                                                       \
+       } else                                                          \
+               elm = NULL;                                             \
+       return (elm);                                                   \
+}                                                                      \
+                                                                       \
+static __inline struct type *                                          \
+name##_SPLAY_MIN_MAX(struct name *head, int val)                       \
+{                                                                      \
+       name##_SPLAY_MINMAX(head, val);                                 \
+        return (SPLAY_ROOT(head));                                     \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)                         \
+struct type *                                                          \
+name##_SPLAY_INSERT(struct name *head, struct type *elm)               \
+{                                                                      \
+    if (SPLAY_EMPTY(head)) {                                           \
+           SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;    \
+    } else {                                                           \
+           int __comp;                                                 \
+           name##_SPLAY(head, elm);                                    \
+           __comp = (cmp)(elm, (head)->sph_root);                      \
+           if(__comp < 0) {                                            \
+                   SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+                   SPLAY_RIGHT(elm, field) = (head)->sph_root;         \
+                   SPLAY_LEFT((head)->sph_root, field) = NULL;         \
+           } else if (__comp > 0) {                                    \
+                   SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+                   SPLAY_LEFT(elm, field) = (head)->sph_root;          \
+                   SPLAY_RIGHT((head)->sph_root, field) = NULL;        \
+           } else                                                      \
+                   return ((head)->sph_root);                          \
+    }                                                                  \
+    (head)->sph_root = (elm);                                          \
+    return (NULL);                                                     \
+}                                                                      \
+                                                                       \
+struct type *                                                          \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)               \
+{                                                                      \
+       struct type *__tmp;                                             \
+       if (SPLAY_EMPTY(head))                                          \
+               return (NULL);                                          \
+       name##_SPLAY(head, elm);                                        \
+       if ((cmp)(elm, (head)->sph_root) == 0) {                        \
+               if (SPLAY_LEFT((head)->sph_root, field) == NULL) {      \
+                       (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+               } else {                                                \
+                       __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
+                       (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+                       name##_SPLAY(head, elm);                        \
+                       SPLAY_RIGHT((head)->sph_root, field) = __tmp;   \
+               }                                                       \
+               return (elm);                                           \
+       }                                                               \
+       return (NULL);                                                  \
+}                                                                      \
+                                                                       \
+void                                                                   \
+name##_SPLAY(struct name *head, struct type *elm)                      \
+{                                                                      \
+       struct type __node, *__left, *__right, *__tmp;                  \
+       int __comp;                                                     \
+\
+       SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+       __left = __right = &__node;                                     \
+\
+       while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {          \
+               if (__comp < 0) {                                       \
+                       __tmp = SPLAY_LEFT((head)->sph_root, field);    \
+                       if (__tmp == NULL)                              \
+                               break;                                  \
+                       if ((cmp)(elm, __tmp) < 0){                     \
+                               SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+                               if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+                                       break;                          \
+                       }                                               \
+                       SPLAY_LINKLEFT(head, __right, field);           \
+               } else if (__comp > 0) {                                \
+                       __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
+                       if (__tmp == NULL)                              \
+                               break;                                  \
+                       if ((cmp)(elm, __tmp) > 0){                     \
+                               SPLAY_ROTATE_LEFT(head, __tmp, field);  \
+                               if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+                                       break;                          \
+                       }                                               \
+                       SPLAY_LINKRIGHT(head, __left, field);           \
+               }                                                       \
+       }                                                               \
+       SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \
+}                                                                      \
+                                                                       \
+/* Splay with either the minimum or the maximum element                        \
+ * Used to find minimum or maximum element in tree.                    \
+ */                                                                    \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{                                                                      \
+       struct type __node, *__left, *__right, *__tmp;                  \
+\
+       SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+       __left = __right = &__node;                                     \
+\
+       while (1) {                                                     \
+               if (__comp < 0) {                                       \
+                       __tmp = SPLAY_LEFT((head)->sph_root, field);    \
+                       if (__tmp == NULL)                              \
+                               break;                                  \
+                       if (__comp < 0){                                \
+                               SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+                               if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+                                       break;                          \
+                       }                                               \
+                       SPLAY_LINKLEFT(head, __right, field);           \
+               } else if (__comp > 0) {                                \
+                       __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
+                       if (__tmp == NULL)                              \
+                               break;                                  \
+                       if (__comp > 0) {                               \
+                               SPLAY_ROTATE_LEFT(head, __tmp, field);  \
+                               if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+                                       break;                          \
+                       }                                               \
+                       SPLAY_LINKRIGHT(head, __left, field);           \
+               }                                                       \
+       }                                                               \
+       SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \
+}
+
+#define SPLAY_NEGINF   -1
+#define SPLAY_INF      1
+
+#define SPLAY_INSERT(name, x, y)       name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)       name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)         name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)         name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)             (SPLAY_EMPTY(x) ? NULL  \
+                                       : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)             (SPLAY_EMPTY(x) ? NULL  \
+                                       : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)                                   \
+       for ((x) = SPLAY_MIN(name, head);                               \
+            (x) != NULL;                                               \
+            (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)                                            \
+struct name {                                                          \
+       struct type *rbh_root; /* root of the tree */                   \
+}
+
+#define RB_INITIALIZER(root)                                           \
+       { NULL }
+
+#define RB_INIT(root) do {                                             \
+       (root)->rbh_root = NULL;                                        \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_BLACK       0
+#define RB_RED         1
+#define RB_ENTRY(type)                                                 \
+struct {                                                               \
+       struct type *rbe_left;          /* left element */              \
+       struct type *rbe_right;         /* right element */             \
+       struct type *rbe_parent;        /* parent element */            \
+       int rbe_color;                  /* node color */                \
+}
+
+#define RB_LEFT(elm, field)            (elm)->field.rbe_left
+#define RB_RIGHT(elm, field)           (elm)->field.rbe_right
+#define RB_PARENT(elm, field)          (elm)->field.rbe_parent
+#define RB_COLOR(elm, field)           (elm)->field.rbe_color
+#define RB_ROOT(head)                  (head)->rbh_root
+#define RB_EMPTY(head)                 (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {                                        \
+       RB_PARENT(elm, field) = parent;                                 \
+       RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;              \
+       RB_COLOR(elm, field) = RB_RED;                                  \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_SET_BLACKRED(black, red, field) do {                                \
+       RB_COLOR(black, field) = RB_BLACK;                              \
+       RB_COLOR(red, field) = RB_RED;                                  \
+} while (/*CONSTCOND*/ 0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)  do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {                     \
+       (tmp) = RB_RIGHT(elm, field);                                   \
+       if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {     \
+               RB_PARENT(RB_LEFT(tmp, field), field) = (elm);          \
+       }                                                               \
+       RB_AUGMENT(elm);                                                \
+       if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \
+               if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \
+                       RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \
+               else                                                    \
+                       RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+       } else                                                          \
+               (head)->rbh_root = (tmp);                               \
+       RB_LEFT(tmp, field) = (elm);                                    \
+       RB_PARENT(elm, field) = (tmp);                                  \
+       RB_AUGMENT(tmp);                                                \
+       if ((RB_PARENT(tmp, field)))                                    \
+               RB_AUGMENT(RB_PARENT(tmp, field));                      \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {                    \
+       (tmp) = RB_LEFT(elm, field);                                    \
+       if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {     \
+               RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);         \
+       }                                                               \
+       RB_AUGMENT(elm);                                                \
+       if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \
+               if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \
+                       RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \
+               else                                                    \
+                       RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+       } else                                                          \
+               (head)->rbh_root = (tmp);                               \
+       RB_RIGHT(tmp, field) = (elm);                                   \
+       RB_PARENT(elm, field) = (tmp);                                  \
+       RB_AUGMENT(tmp);                                                \
+       if ((RB_PARENT(tmp, field)))                                    \
+               RB_AUGMENT(RB_PARENT(tmp, field));                      \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define        RB_PROTOTYPE(name, type, field, cmp)                            \
+       RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define        RB_PROTOTYPE_STATIC(name, type, field, cmp)                     \
+       RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)            \
+       RB_PROTOTYPE_INSERT_COLOR(name, type, attr);                    \
+       RB_PROTOTYPE_REMOVE_COLOR(name, type, attr);                    \
+       RB_PROTOTYPE_INSERT(name, type, attr);                          \
+       RB_PROTOTYPE_REMOVE(name, type, attr);                          \
+       RB_PROTOTYPE_FIND(name, type, attr);                            \
+       RB_PROTOTYPE_NFIND(name, type, attr);                           \
+       RB_PROTOTYPE_NEXT(name, type, attr);                            \
+       RB_PROTOTYPE_PREV(name, type, attr);                            \
+       RB_PROTOTYPE_MINMAX(name, type, attr);
+#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr)                    \
+       attr void name##_RB_INSERT_COLOR(struct name *, struct type *)
+#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr)                    \
+       attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *)
+#define RB_PROTOTYPE_REMOVE(name, type, attr)                          \
+       attr struct type *name##_RB_REMOVE(struct name *, struct type *)
+#define RB_PROTOTYPE_INSERT(name, type, attr)                          \
+       attr struct type *name##_RB_INSERT(struct name *, struct type *)
+#define RB_PROTOTYPE_FIND(name, type, attr)                            \
+       attr struct type *name##_RB_FIND(struct name *, struct type *)
+#define RB_PROTOTYPE_NFIND(name, type, attr)                           \
+       attr struct type *name##_RB_NFIND(struct name *, struct type *)
+#define RB_PROTOTYPE_NEXT(name, type, attr)                            \
+       attr struct type *name##_RB_NEXT(struct type *)
+#define RB_PROTOTYPE_PREV(name, type, attr)                            \
+       attr struct type *name##_RB_PREV(struct type *)
+#define RB_PROTOTYPE_MINMAX(name, type, attr)                          \
+       attr struct type *name##_RB_MINMAX(struct name *, int)
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define        RB_GENERATE(name, type, field, cmp)                             \
+       RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define        RB_GENERATE_STATIC(name, type, field, cmp)                      \
+       RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)             \
+       RB_GENERATE_INSERT_COLOR(name, type, field, attr)               \
+       RB_GENERATE_REMOVE_COLOR(name, type, field, attr)               \
+       RB_GENERATE_INSERT(name, type, field, cmp, attr)                \
+       RB_GENERATE_REMOVE(name, type, field, attr)                     \
+       RB_GENERATE_FIND(name, type, field, cmp, attr)                  \
+       RB_GENERATE_NFIND(name, type, field, cmp, attr)                 \
+       RB_GENERATE_NEXT(name, type, field, attr)                       \
+       RB_GENERATE_PREV(name, type, field, attr)                       \
+       RB_GENERATE_MINMAX(name, type, field, attr)
+
+#define RB_GENERATE_INSERT_COLOR(name, type, field, attr)              \
+attr void                                                              \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)            \
+{                                                                      \
+       struct type *parent, *gparent, *tmp;                            \
+       while ((parent = RB_PARENT(elm, field)) != NULL &&              \
+           RB_COLOR(parent, field) == RB_RED) {                        \
+               gparent = RB_PARENT(parent, field);                     \
+               if (parent == RB_LEFT(gparent, field)) {                \
+                       tmp = RB_RIGHT(gparent, field);                 \
+                       if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \
+                               RB_COLOR(tmp, field) = RB_BLACK;        \
+                               RB_SET_BLACKRED(parent, gparent, field);\
+                               elm = gparent;                          \
+                               continue;                               \
+                       }                                               \
+                       if (RB_RIGHT(parent, field) == elm) {           \
+                               RB_ROTATE_LEFT(head, parent, tmp, field);\
+                               tmp = parent;                           \
+                               parent = elm;                           \
+                               elm = tmp;                              \
+                       }                                               \
+                       RB_SET_BLACKRED(parent, gparent, field);        \
+                       RB_ROTATE_RIGHT(head, gparent, tmp, field);     \
+               } else {                                                \
+                       tmp = RB_LEFT(gparent, field);                  \
+                       if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \
+                               RB_COLOR(tmp, field) = RB_BLACK;        \
+                               RB_SET_BLACKRED(parent, gparent, field);\
+                               elm = gparent;                          \
+                               continue;                               \
+                       }                                               \
+                       if (RB_LEFT(parent, field) == elm) {            \
+                               RB_ROTATE_RIGHT(head, parent, tmp, field);\
+                               tmp = parent;                           \
+                               parent = elm;                           \
+                               elm = tmp;                              \
+                       }                                               \
+                       RB_SET_BLACKRED(parent, gparent, field);        \
+                       RB_ROTATE_LEFT(head, gparent, tmp, field);      \
+               }                                                       \
+       }                                                               \
+       RB_COLOR(head->rbh_root, field) = RB_BLACK;                     \
+}
+
+#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr)              \
+attr void                                                              \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{                                                                      \
+       struct type *tmp;                                               \
+       while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&     \
+           elm != RB_ROOT(head)) {                                     \
+               if (RB_LEFT(parent, field) == elm) {                    \
+                       tmp = RB_RIGHT(parent, field);                  \
+                       if (RB_COLOR(tmp, field) == RB_RED) {           \
+                               RB_SET_BLACKRED(tmp, parent, field);    \
+                               RB_ROTATE_LEFT(head, parent, tmp, field);\
+                               tmp = RB_RIGHT(parent, field);          \
+                       }                                               \
+                       if ((RB_LEFT(tmp, field) == NULL ||             \
+                           RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+                           (RB_RIGHT(tmp, field) == NULL ||            \
+                           RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+                               RB_COLOR(tmp, field) = RB_RED;          \
+                               elm = parent;                           \
+                               parent = RB_PARENT(elm, field);         \
+                       } else {                                        \
+                               if (RB_RIGHT(tmp, field) == NULL ||     \
+                                   RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+                                       struct type *oleft;             \
+                                       if ((oleft = RB_LEFT(tmp, field)) \
+                                           != NULL)                    \
+                                               RB_COLOR(oleft, field) = RB_BLACK;\
+                                       RB_COLOR(tmp, field) = RB_RED;  \
+                                       RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+                                       tmp = RB_RIGHT(parent, field);  \
+                               }                                       \
+                               RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+                               RB_COLOR(parent, field) = RB_BLACK;     \
+                               if (RB_RIGHT(tmp, field))               \
+                                       RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+                               RB_ROTATE_LEFT(head, parent, tmp, field);\
+                               elm = RB_ROOT(head);                    \
+                               break;                                  \
+                       }                                               \
+               } else {                                                \
+                       tmp = RB_LEFT(parent, field);                   \
+                       if (RB_COLOR(tmp, field) == RB_RED) {           \
+                               RB_SET_BLACKRED(tmp, parent, field);    \
+                               RB_ROTATE_RIGHT(head, parent, tmp, field);\
+                               tmp = RB_LEFT(parent, field);           \
+                       }                                               \
+                       if ((RB_LEFT(tmp, field) == NULL ||             \
+                           RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+                           (RB_RIGHT(tmp, field) == NULL ||            \
+                           RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+                               RB_COLOR(tmp, field) = RB_RED;          \
+                               elm = parent;                           \
+                               parent = RB_PARENT(elm, field);         \
+                       } else {                                        \
+                               if (RB_LEFT(tmp, field) == NULL ||      \
+                                   RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+                                       struct type *oright;            \
+                                       if ((oright = RB_RIGHT(tmp, field)) \
+                                           != NULL)                    \
+                                               RB_COLOR(oright, field) = RB_BLACK;\
+                                       RB_COLOR(tmp, field) = RB_RED;  \
+                                       RB_ROTATE_LEFT(head, tmp, oright, field);\
+                                       tmp = RB_LEFT(parent, field);   \
+                               }                                       \
+                               RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+                               RB_COLOR(parent, field) = RB_BLACK;     \
+                               if (RB_LEFT(tmp, field))                \
+                                       RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+                               RB_ROTATE_RIGHT(head, parent, tmp, field);\
+                               elm = RB_ROOT(head);                    \
+                               break;                                  \
+                       }                                               \
+               }                                                       \
+       }                                                               \
+       if (elm)                                                        \
+               RB_COLOR(elm, field) = RB_BLACK;                        \
+}
+
+#define RB_GENERATE_REMOVE(name, type, field, attr)                    \
+attr struct type *                                                     \
+name##_RB_REMOVE(struct name *head, struct type *elm)                  \
+{                                                                      \
+       struct type *child, *parent, *old = elm;                        \
+       int color;                                                      \
+       if (RB_LEFT(elm, field) == NULL)                                \
+               child = RB_RIGHT(elm, field);                           \
+       else if (RB_RIGHT(elm, field) == NULL)                          \
+               child = RB_LEFT(elm, field);                            \
+       else {                                                          \
+               struct type *left;                                      \
+               elm = RB_RIGHT(elm, field);                             \
+               while ((left = RB_LEFT(elm, field)) != NULL)            \
+                       elm = left;                                     \
+               child = RB_RIGHT(elm, field);                           \
+               parent = RB_PARENT(elm, field);                         \
+               color = RB_COLOR(elm, field);                           \
+               if (child)                                              \
+                       RB_PARENT(child, field) = parent;               \
+               if (parent) {                                           \
+                       if (RB_LEFT(parent, field) == elm)              \
+                               RB_LEFT(parent, field) = child;         \
+                       else                                            \
+                               RB_RIGHT(parent, field) = child;        \
+                       RB_AUGMENT(parent);                             \
+               } else                                                  \
+                       RB_ROOT(head) = child;                          \
+               if (RB_PARENT(elm, field) == old)                       \
+                       parent = elm;                                   \
+               (elm)->field = (old)->field;                            \
+               if (RB_PARENT(old, field)) {                            \
+                       if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+                               RB_LEFT(RB_PARENT(old, field), field) = elm;\
+                       else                                            \
+                               RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+                       RB_AUGMENT(RB_PARENT(old, field));              \
+               } else                                                  \
+                       RB_ROOT(head) = elm;                            \
+               RB_PARENT(RB_LEFT(old, field), field) = elm;            \
+               if (RB_RIGHT(old, field))                               \
+                       RB_PARENT(RB_RIGHT(old, field), field) = elm;   \
+               if (parent) {                                           \
+                       left = parent;                                  \
+                       do {                                            \
+                               RB_AUGMENT(left);                       \
+                       } while ((left = RB_PARENT(left, field)) != NULL); \
+               }                                                       \
+               goto color;                                             \
+       }                                                               \
+       parent = RB_PARENT(elm, field);                                 \
+       color = RB_COLOR(elm, field);                                   \
+       if (child)                                                      \
+               RB_PARENT(child, field) = parent;                       \
+       if (parent) {                                                   \
+               if (RB_LEFT(parent, field) == elm)                      \
+                       RB_LEFT(parent, field) = child;                 \
+               else                                                    \
+                       RB_RIGHT(parent, field) = child;                \
+               RB_AUGMENT(parent);                                     \
+       } else                                                          \
+               RB_ROOT(head) = child;                                  \
+color:                                                                 \
+       if (color == RB_BLACK)                                          \
+               name##_RB_REMOVE_COLOR(head, parent, child);            \
+       return (old);                                                   \
+}                                                                      \
+
+#define RB_GENERATE_INSERT(name, type, field, cmp, attr)               \
+/* Inserts a node into the RB tree */                                  \
+attr struct type *                                                     \
+name##_RB_INSERT(struct name *head, struct type *elm)                  \
+{                                                                      \
+       struct type *tmp;                                               \
+       struct type *parent = NULL;                                     \
+       int comp = 0;                                                   \
+       tmp = RB_ROOT(head);                                            \
+       while (tmp) {                                                   \
+               parent = tmp;                                           \
+               comp = (cmp)(elm, parent);                              \
+               if (comp < 0)                                           \
+                       tmp = RB_LEFT(tmp, field);                      \
+               else if (comp > 0)                                      \
+                       tmp = RB_RIGHT(tmp, field);                     \
+               else                                                    \
+                       return (tmp);                                   \
+       }                                                               \
+       RB_SET(elm, parent, field);                                     \
+       if (parent != NULL) {                                           \
+               if (comp < 0)                                           \
+                       RB_LEFT(parent, field) = elm;                   \
+               else                                                    \
+                       RB_RIGHT(parent, field) = elm;                  \
+               RB_AUGMENT(parent);                                     \
+       } else                                                          \
+               RB_ROOT(head) = elm;                                    \
+       name##_RB_INSERT_COLOR(head, elm);                              \
+       return (NULL);                                                  \
+}
+
+#define RB_GENERATE_FIND(name, type, field, cmp, attr)                 \
+/* Finds the node with the same key as elm */                          \
+attr struct type *                                                     \
+name##_RB_FIND(struct name *head, struct type *elm)                    \
+{                                                                      \
+       struct type *tmp = RB_ROOT(head);                               \
+       int comp;                                                       \
+       while (tmp) {                                                   \
+               comp = cmp(elm, tmp);                                   \
+               if (comp < 0)                                           \
+                       tmp = RB_LEFT(tmp, field);                      \
+               else if (comp > 0)                                      \
+                       tmp = RB_RIGHT(tmp, field);                     \
+               else                                                    \
+                       return (tmp);                                   \
+       }                                                               \
+       return (NULL);                                                  \
+}
+
+#define RB_GENERATE_NFIND(name, type, field, cmp, attr)                        \
+/* Finds the first node greater than or equal to the search key */     \
+attr struct type *                                                     \
+name##_RB_NFIND(struct name *head, struct type *elm)                   \
+{                                                                      \
+       struct type *tmp = RB_ROOT(head);                               \
+       struct type *res = NULL;                                        \
+       int comp;                                                       \
+       while (tmp) {                                                   \
+               comp = cmp(elm, tmp);                                   \
+               if (comp < 0) {                                         \
+                       res = tmp;                                      \
+                       tmp = RB_LEFT(tmp, field);                      \
+               }                                                       \
+               else if (comp > 0)                                      \
+                       tmp = RB_RIGHT(tmp, field);                     \
+               else                                                    \
+                       return (tmp);                                   \
+       }                                                               \
+       return (res);                                                   \
+}
+
+#define RB_GENERATE_NEXT(name, type, field, attr)                      \
+/* ARGSUSED */                                                         \
+attr struct type *                                                     \
+name##_RB_NEXT(struct type *elm)                                       \
+{                                                                      \
+       if (RB_RIGHT(elm, field)) {                                     \
+               elm = RB_RIGHT(elm, field);                             \
+               while (RB_LEFT(elm, field))                             \
+                       elm = RB_LEFT(elm, field);                      \
+       } else {                                                        \
+               if (RB_PARENT(elm, field) &&                            \
+                   (elm == RB_LEFT(RB_PARENT(elm, field), field)))     \
+                       elm = RB_PARENT(elm, field);                    \
+               else {                                                  \
+                       while (RB_PARENT(elm, field) &&                 \
+                           (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+                               elm = RB_PARENT(elm, field);            \
+                       elm = RB_PARENT(elm, field);                    \
+               }                                                       \
+       }                                                               \
+       return (elm);                                                   \
+}
+
+#define RB_GENERATE_PREV(name, type, field, attr)                      \
+/* ARGSUSED */                                                         \
+attr struct type *                                                     \
+name##_RB_PREV(struct type *elm)                                       \
+{                                                                      \
+       if (RB_LEFT(elm, field)) {                                      \
+               elm = RB_LEFT(elm, field);                              \
+               while (RB_RIGHT(elm, field))                            \
+                       elm = RB_RIGHT(elm, field);                     \
+       } else {                                                        \
+               if (RB_PARENT(elm, field) &&                            \
+                   (elm == RB_RIGHT(RB_PARENT(elm, field), field)))    \
+                       elm = RB_PARENT(elm, field);                    \
+               else {                                                  \
+                       while (RB_PARENT(elm, field) &&                 \
+                           (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+                               elm = RB_PARENT(elm, field);            \
+                       elm = RB_PARENT(elm, field);                    \
+               }                                                       \
+       }                                                               \
+       return (elm);                                                   \
+}
+
+#define RB_GENERATE_MINMAX(name, type, field, attr)                    \
+attr struct type *                                                     \
+name##_RB_MINMAX(struct name *head, int val)                           \
+{                                                                      \
+       struct type *tmp = RB_ROOT(head);                               \
+       struct type *parent = NULL;                                     \
+       while (tmp) {                                                   \
+               parent = tmp;                                           \
+               if (val < 0)                                            \
+                       tmp = RB_LEFT(tmp, field);                      \
+               else                                                    \
+                       tmp = RB_RIGHT(tmp, field);                     \
+       }                                                               \
+       return (parent);                                                \
+}
+
+#define RB_NEGINF      -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y)  name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)  name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)    name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y)   name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y)    name##_RB_NEXT(y)
+#define RB_PREV(name, x, y)    name##_RB_PREV(y)
+#define RB_MIN(name, x)                name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)                name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)                                      \
+       for ((x) = RB_MIN(name, head);                                  \
+            (x) != NULL;                                               \
+            (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y)                                    \
+       for ((x) = (y);                                                 \
+           ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \
+            (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y)                              \
+       for ((x) = RB_MIN(name, head);                                  \
+           ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \
+            (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head)                              \
+       for ((x) = RB_MAX(name, head);                                  \
+            (x) != NULL;                                               \
+            (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y)                            \
+       for ((x) = (y);                                                 \
+           ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \
+            (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                      \
+       for ((x) = RB_MAX(name, head);                                  \
+           ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \
+            (x) = (y))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_TREE_H_ */
diff --git a/lwext4/CMakeLists.txt b/lwext4/CMakeLists.txt
deleted file mode 100644 (file)
index 78b233d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-#LIBRARY
-include_directories(.)
-aux_source_directory(. LWEXT4_SRC)
-add_library(lwext4  ${LWEXT4_SRC})
-if  (DEFINED SIZE)
-    add_custom_target(lib_size ALL DEPENDS lwext4 COMMAND ${SIZE} liblwext4.a)
-else()
-
-endif()
diff --git a/lwext4/ext4.c b/lwext4/ext4.c
deleted file mode 100644 (file)
index 38ec6d4..0000000
+++ /dev/null
@@ -1,2935 +0,0 @@
-/*
- * 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.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_xattr.h"
-#include "ext4_journal.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;
-
-       struct jbd_fs jbd_fs;
-       struct jbd_journal jbd_journal;
-};
-
-/**@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_sblock *sb = &enode->fs->sb;
-
-       /* Check if node is directory */
-       if (!ext4_inode_is_type(sb, enode->inode, EXT4_INODE_MODE_DIRECTORY)) {
-               *has_children = false;
-               return EOK;
-       }
-
-       struct ext4_dir_iter 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.curr != NULL) {
-               if (ext4_dir_en_get_inode(it.curr) != 0) {
-                       uint16_t nsize;
-                       nsize = ext4_dir_en_get_name_len(sb, it.curr);
-                       if (!ext4_is_dots(it.curr->name, nsize)) {
-                               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 *ch, const char *n,
-                    uint32_t len, bool rename)
-{
-       /* Check maximum name length */
-       if (len > EXT4_DIRECTORY_FILENAME_LEN)
-               return EINVAL;
-
-       /* Add entry to parent directory */
-       int r = ext4_dir_add_entry(parent, n, len, ch);
-       if (r != EOK)
-               return r;
-
-       /* Fill new dir -> add '.' and '..' entries.
-        * Also newly allocated inode should have 0 link count.
-        */
-
-       bool is_dir = ext4_inode_is_type(&mp->fs.sb, ch->inode,
-                              EXT4_INODE_MODE_DIRECTORY);
-       if (is_dir && !rename) {
-
-#if CONFIG_DIR_INDEX_ENABLE
-               /* Initialize directory index if supported */
-               if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
-                       r = ext4_dir_dx_init(ch, parent);
-                       if (r != EOK)
-                               return r;
-
-                       ext4_inode_set_flag(ch->inode, EXT4_INODE_FLAG_INDEX);
-                       ch->dirty = true;
-               } else
-#endif
-               {
-                       r = ext4_dir_add_entry(ch, ".", strlen("."), ch);
-                       if (r != EOK) {
-                               ext4_dir_remove_entry(parent, n, strlen(n));
-                               return r;
-                       }
-
-                       r = ext4_dir_add_entry(ch, "..", strlen(".."), parent);
-                       if (r != EOK) {
-                               ext4_dir_remove_entry(parent, n, strlen(n));
-                               ext4_dir_remove_entry(ch, ".", strlen("."));
-                               return r;
-                       }
-               }
-
-               /*New empty directory. Two links (. and ..) */
-               ext4_inode_set_links_cnt(ch->inode, 2);
-               ext4_fs_inode_links_count_inc(parent);
-               ch->dirty = true;
-               parent->dirty = true;
-               return r;
-       }
-       /*
-        * In case we want to rename a directory,
-        * we reset the original '..' pointer.
-        */
-       if (is_dir) {
-               bool idx;
-               idx = ext4_inode_has_flag(ch->inode, EXT4_INODE_FLAG_INDEX);
-               struct ext4_dir_search_result res;
-               if (!idx) {
-                       r = ext4_dir_find_entry(&res, ch, "..", strlen(".."));
-                       if (r != EOK)
-                               return EIO;
-
-                       ext4_dir_en_set_inode(res.dentry, parent->index);
-                       ext4_trans_set_block_dirty(res.block.buf);
-                       r = ext4_dir_destroy_result(ch, &res);
-                       if (r != EOK)
-                               return r;
-
-               } else {
-#if CONFIG_DIR_INDEX_ENABLE
-                       r = ext4_dir_dx_reset_parent_inode(ch, parent->index);
-                       if (r != EOK)
-                               return r;
-
-#endif
-               }
-
-               ext4_fs_inode_links_count_inc(parent);
-               parent->dirty = true;
-       }
-       if (!rename) {
-               ext4_fs_inode_links_count_inc(ch);
-               ch->dirty = true;
-       }
-
-       return r;
-}
-
-static int ext4_unlink(struct ext4_mountpoint *mp,
-                      struct ext4_inode_ref *parent,
-                      struct ext4_inode_ref *child, const char *name,
-                      uint32_t name_len)
-{
-       bool has_children;
-       int rc = ext4_has_children(&has_children, child);
-       if (rc != EOK)
-               return rc;
-
-       /* Cannot unlink non-empty node */
-       if (has_children)
-               return ENOTEMPTY;
-
-       /* 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,
-                                        EXT4_INODE_MODE_DIRECTORY);
-
-       /* If directory - handle links from parent */
-       if (is_dir) {
-               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,
-        *     (uint32_t) now);
-        */
-       if (ext4_inode_get_links_cnt(child->inode)) {
-               ext4_fs_inode_links_count_dec(child);
-               child->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_bcache_cleanup(bc);
-               ext4_block_fini(bd);
-               if (mp->cache_dynamic) {
-                       ext4_bcache_fini_dynamic(bc);
-                       free(bc);
-               }
-               return r;
-       }
-       bd->fs = &mp->fs;
-
-       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)
-               goto Finish;
-
-       mp->mounted = 0;
-
-       ext4_bcache_cleanup(mp->fs.bdev->bc);
-       if (mp->cache_dynamic) {
-               ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
-               free(mp->fs.bdev->bc);
-       }
-       r = ext4_block_fini(mp->fs.bdev);
-Finish:
-       mp->fs.bdev->fs = NULL;
-       return r;
-}
-
-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 NULL;
-}
-
-__unused
-static int __ext4_journal_start(const char *mount_point)
-{
-       int r = EOK;
-       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-       if (!mp)
-               return ENOENT;
-
-       if (ext4_sb_feature_com(&mp->fs.sb,
-                               EXT4_FCOM_HAS_JOURNAL)) {
-               r = jbd_get_fs(&mp->fs, &mp->jbd_fs);
-               if (r != EOK)
-                       goto Finish;
-
-               r = jbd_journal_start(&mp->jbd_fs, &mp->jbd_journal);
-               if (r != EOK) {
-                       mp->jbd_fs.dirty = false;
-                       jbd_put_fs(&mp->jbd_fs);
-                       goto Finish;
-               }
-               mp->fs.jbd_fs = &mp->jbd_fs;
-               mp->fs.jbd_journal = &mp->jbd_journal;
-       }
-Finish:
-       return r;
-}
-
-__unused
-static int __ext4_journal_stop(const char *mount_point)
-{
-       int r = EOK;
-       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-       if (!mp)
-               return ENOENT;
-
-       if (ext4_sb_feature_com(&mp->fs.sb,
-                               EXT4_FCOM_HAS_JOURNAL)) {
-               r = jbd_journal_stop(&mp->jbd_journal);
-               if (r != EOK) {
-                       mp->jbd_fs.dirty = false;
-                       jbd_put_fs(&mp->jbd_fs);
-                       mp->fs.jbd_journal = NULL;
-                       mp->fs.jbd_fs = NULL;
-                       goto Finish;
-               }
-
-               r = jbd_put_fs(&mp->jbd_fs);
-               if (r != EOK) {
-                       mp->fs.jbd_journal = NULL;
-                       mp->fs.jbd_fs = NULL;
-                       goto Finish;
-               }
-
-               mp->fs.jbd_journal = NULL;
-               mp->fs.jbd_fs = NULL;
-       }
-Finish:
-       return r;
-}
-
-__unused
-static int __ext4_recover(const char *mount_point)
-{
-       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-       if (!mp)
-               return ENOENT;
-
-       int r = ENOTSUP;
-       EXT4_MP_LOCK(mp);
-       if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL)) {
-               struct jbd_fs *jbd_fs = calloc(1, sizeof(struct jbd_fs));
-               if (!jbd_fs) {
-                        r = ENOMEM;
-                        goto Finish;
-               }
-
-
-               r = jbd_get_fs(&mp->fs, jbd_fs);
-               if (r != EOK) {
-                       free(jbd_fs);
-                       goto Finish;
-               }
-
-               r = jbd_recover(jbd_fs);
-               jbd_put_fs(jbd_fs);
-               free(jbd_fs);
-       }
-
-
-Finish:
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-__unused
-static int __ext4_trans_start(struct ext4_mountpoint *mp)
-{
-       int r = EOK;
-       if (mp->fs.jbd_journal && !mp->fs.curr_trans) {
-               struct jbd_journal *journal = mp->fs.jbd_journal;
-               struct jbd_trans *trans;
-               trans = jbd_journal_new_trans(journal);
-               if (!trans) {
-                       r = ENOMEM;
-                       goto Finish;
-               }
-               mp->fs.curr_trans = trans;
-       }
-Finish:
-       return r;
-}
-
-__unused
-static int __ext4_trans_stop(struct ext4_mountpoint *mp)
-{
-       int r = EOK;
-       if (mp->fs.jbd_journal && mp->fs.curr_trans) {
-               struct jbd_journal *journal = mp->fs.jbd_journal;
-               struct jbd_trans *trans = mp->fs.curr_trans;
-               r = jbd_journal_commit_trans(journal, trans);
-               mp->fs.curr_trans = NULL;
-       }
-       return r;
-}
-
-__unused
-static void __ext4_trans_abort(struct ext4_mountpoint *mp)
-{
-       if (mp->fs.jbd_journal && mp->fs.curr_trans) {
-               struct jbd_journal *journal = mp->fs.jbd_journal;
-               struct jbd_trans *trans = mp->fs.curr_trans;
-               jbd_journal_free_trans(journal, trans, true);
-               mp->fs.curr_trans = NULL;
-       }
-}
-
-int ext4_journal_start(const char *mount_point __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       r = __ext4_journal_start(mount_point);
-#endif
-       return r;
-}
-
-int ext4_journal_stop(const char *mount_point __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       r = __ext4_journal_stop(mount_point);
-#endif
-       return r;
-}
-
-int ext4_recover(const char *mount_point __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       r = __ext4_recover(mount_point);
-#endif
-       return r;
-}
-
-static int ext4_trans_start(struct ext4_mountpoint *mp __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       r = __ext4_trans_start(mp);
-#endif
-       return r;
-}
-
-static int ext4_trans_stop(struct ext4_mountpoint *mp __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       r = __ext4_trans_stop(mp);
-#endif
-       return r;
-}
-
-static void ext4_trans_abort(struct ext4_mountpoint *mp __unused)
-{
-#if CONFIG_JOURNALING_ENABLE
-       __ext4_trans_abort(mp);
-#endif
-}
-
-
-int ext4_mount_point_stats(const char *mount_point,
-                          struct ext4_mount_stats *stats)
-{
-       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-
-       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 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;
-}
-
-static int ext4_trunc_inode(struct ext4_mountpoint *mp,
-                           uint32_t index, uint64_t new_size)
-{
-       int r = EOK;
-       struct ext4_fs *const fs = &mp->fs;
-       struct ext4_inode_ref inode_ref;
-       uint64_t inode_size;
-       bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans;
-       r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
-       if (r != EOK)
-               return r;
-
-       inode_size = ext4_inode_get_size(&fs->sb, inode_ref.inode);
-       ext4_fs_put_inode_ref(&inode_ref);
-       if (has_trans)
-               ext4_trans_stop(mp);
-
-       while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) {
-
-               inode_size -= CONFIG_MAX_TRUNCATE_SIZE;
-
-               ext4_trans_start(mp);
-               r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
-               if (r != EOK) {
-                       ext4_trans_abort(mp);
-                       break;
-               }
-               r = ext4_fs_truncate_inode(&inode_ref, inode_size);
-               if (r != EOK)
-                       ext4_fs_put_inode_ref(&inode_ref);
-               else
-                       r = ext4_fs_put_inode_ref(&inode_ref);
-
-               if (r != EOK) {
-                       ext4_trans_abort(mp);
-                       goto Finish;
-               } else
-                       ext4_trans_stop(mp);
-       }
-
-       if (inode_size > new_size) {
-
-               inode_size = new_size;
-
-               ext4_trans_start(mp);
-               r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
-               if (r != EOK) {
-                       ext4_trans_abort(mp);
-                       goto Finish;
-               }
-               r = ext4_fs_truncate_inode(&inode_ref, inode_size);
-               if (r != EOK)
-                       ext4_fs_put_inode_ref(&inode_ref);
-               else
-                       r = ext4_fs_put_inode_ref(&inode_ref);
-
-               if (r != EOK)
-                       ext4_trans_abort(mp);
-               else
-                       ext4_trans_stop(mp);
-
-       }
-
-Finish:
-
-       if (has_trans)
-               ext4_trans_start(mp);
-
-       return r;
-}
-
-static int ext4_trunc_dir(struct ext4_mountpoint *mp,
-                         struct ext4_inode_ref *parent,
-                         struct ext4_inode_ref *dir)
-{
-       int r = EOK;
-       bool is_dir = ext4_inode_is_type(&mp->fs.sb, dir->inode,
-                       EXT4_INODE_MODE_DIRECTORY);
-       uint32_t block_size = ext4_sb_get_block_size(&mp->fs.sb);
-       if (!is_dir)
-               return EINVAL;
-
-#if CONFIG_DIR_INDEX_ENABLE
-       /* Initialize directory index if supported */
-       if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
-               r = ext4_dir_dx_init(dir, parent);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_trunc_inode(mp, dir->index,
-                                    EXT4_DIR_DX_INIT_BCNT * block_size);
-               if (r != EOK)
-                       return r;
-       } else
-#endif
-       {
-               r = ext4_trunc_inode(mp, dir->index, block_size);
-               if (r != EOK)
-                       return r;
-       }
-
-       return ext4_fs_truncate_inode(dir, 0);
-}
-
-/*
- * NOTICE: if filetype is equal to EXT4_DIRENTRY_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 ftype, uint32_t *parent_inode,
-                             uint32_t *name_off)
-{
-       bool is_goal = false;
-       uint32_t imode = EXT4_INODE_MODE_DIRECTORY;
-       uint32_t next_inode;
-
-       int r;
-       int len;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       struct ext4_dir_search_result result;
-       struct ext4_inode_ref ref;
-
-       f->mp = 0;
-
-       if (!mp)
-               return ENOENT;
-
-       struct ext4_fs *const fs = &mp->fs;
-       struct ext4_sblock *const sb = &mp->fs.sb;
-
-       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(fs, EXT4_INODE_ROOT_INDEX, &ref);
-       if (r != EOK)
-               return r;
-
-       if (parent_inode)
-               *parent_inode = ref.index;
-
-       if (flags & O_CREAT)
-               ext4_trans_start(mp);
-
-       len = ext4_path_check(path, &is_goal);
-       while (1) {
-
-               len = ext4_path_check(path, &is_goal);
-               if (!len) {
-                       /*If root open was request.*/
-                       if (ftype == EXT4_DE_DIR || ftype == EXT4_DE_UNKNOWN)
-                               if (is_goal)
-                                       break;
-
-                       r = ENOENT;
-                       break;
-               }
-
-               r = ext4_dir_find_entry(&result, &ref, path, len);
-               if (r != EOK) {
-
-                       /*Destroy last result*/
-                       ext4_dir_destroy_result(&ref, &result);
-                       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(fs, &child_ref,
-                                       is_goal ? ftype : EXT4_DE_DIR);
-                       if (r != EOK)
-                               break;
-
-
-                       /*Link with root dir.*/
-                       r = ext4_link(mp, &ref, &child_ref, path, len, false);
-                       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_en_get_inode(result.dentry);
-               if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) {
-                       uint8_t t;
-                       t = ext4_dir_en_get_inode_type(sb, result.dentry);
-                       imode = ext4_fs_correspond_inode_mode(t);
-               } else {
-                       struct ext4_inode_ref child_ref;
-                       r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref);
-                       if (r != EOK)
-                               break;
-
-                       imode = ext4_inode_type(sb, child_ref.inode);
-                       ext4_fs_put_inode_ref(&child_ref);
-               }
-
-               r = ext4_dir_destroy_result(&ref, &result);
-               if (r != EOK)
-                       break;
-
-               /*If expected file error*/
-               if (imode != EXT4_INODE_MODE_DIRECTORY && !is_goal) {
-                       r = ENOENT;
-                       break;
-               }
-               if (ftype != EXT4_DE_UNKNOWN) {
-                       bool df = imode != ext4_fs_correspond_inode_mode(ftype);
-                       if (df && is_goal) {
-                               r = ENOENT;
-                               break;
-                       }
-               }
-
-               r = ext4_fs_put_inode_ref(&ref);
-               if (r != EOK)
-                       break;
-
-               r = ext4_fs_get_inode_ref(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) && (imode == EXT4_INODE_MODE_FILE)) {
-                       r = ext4_trunc_inode(mp, ref.index, 0);
-                       if (r != EOK) {
-                               ext4_fs_put_inode_ref(&ref);
-                               return r;
-                       }
-               }
-
-               f->mp = mp;
-               f->fsize = ext4_inode_get_size(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);
-       if (flags & O_CREAT) {
-               if (r == EOK)
-                       ext4_trans_stop(mp);
-               else
-                       ext4_trans_abort(mp);
-
-       }
-
-       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_DE_REG_FILE;
-       else
-               filetype = EXT4_DE_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 rename)
-{
-       bool is_goal = false;
-       uint32_t inode_mode = EXT4_INODE_MODE_DIRECTORY;
-       uint32_t next_inode;
-
-       int r;
-       int len;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       struct ext4_dir_search_result result;
-       struct ext4_inode_ref ref;
-
-       if (!mp)
-               return ENOENT;
-
-       struct ext4_fs *const fs = &mp->fs;
-       struct ext4_sblock *const sb = &mp->fs.sb;
-
-       /*Skip mount point*/
-       path += strlen(mp->name);
-
-       /*Load root*/
-       r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref);
-       if (r != EOK)
-               return r;
-
-       len = ext4_path_check(path, &is_goal);
-       while (1) {
-
-               len = ext4_path_check(path, &is_goal);
-               if (!len) {
-                       /*If root open was request.*/
-                       r = is_goal ? EINVAL : ENOENT;
-                       break;
-               }
-
-               r = ext4_dir_find_entry(&result, &ref, path, len);
-               if (r != EOK) {
-
-                       /*Destroy last result*/
-                       ext4_dir_destroy_result(&ref, &result);
-
-                       if (r != ENOENT || !is_goal)
-                               break;
-
-                       /*Link with root dir.*/
-                       r = ext4_link(mp, &ref, child_ref, path, len, rename);
-                       break;
-               } else if (r == EOK && is_goal) {
-                       /*Destroy last result*/
-                       ext4_dir_destroy_result(&ref, &result);
-                       r = EEXIST;
-                       break;
-               }
-
-               next_inode = result.dentry->inode;
-               if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) {
-                       uint8_t t;
-                       t = ext4_dir_en_get_inode_type(sb, result.dentry);
-                       inode_mode = ext4_fs_correspond_inode_mode(t);
-               } else {
-                       struct ext4_inode_ref child_ref;
-                       r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref);
-                       if (r != EOK)
-                               break;
-
-                       inode_mode = ext4_inode_type(sb, child_ref.inode);
-                       ext4_fs_put_inode_ref(&child_ref);
-               }
-
-               r = ext4_dir_destroy_result(&ref, &result);
-               if (r != EOK)
-                       break;
-
-               if (inode_mode != EXT4_INODE_MODE_DIRECTORY) {
-                       r = is_goal ? EEXIST : ENOENT;
-                       break;
-               }
-
-               r = ext4_fs_put_inode_ref(&ref);
-               if (r != EOK)
-                       break;
-
-               r = ext4_fs_get_inode_ref(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_remove_orig_reference(const char *path, uint32_t name_off,
-                                     struct ext4_inode_ref *parent_ref,
-                                     struct ext4_inode_ref *child_ref)
-{
-       bool is_goal;
-       int r;
-       int len;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-
-       if (!mp)
-               return ENOENT;
-
-       /*Set path*/
-       path += name_off;
-
-       len = ext4_path_check(path, &is_goal);
-
-       /* Remove entry from parent directory */
-       r = ext4_dir_remove_entry(parent_ref, path, len);
-       if (r != EOK)
-               goto Finish;
-
-       if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode,
-                              EXT4_INODE_MODE_DIRECTORY)) {
-               ext4_fs_inode_links_count_dec(parent_ref);
-               parent_ref->dirty = true;
-       }
-Finish:
-       return r;
-}
-
-int ext4_flink(const char *path, const char *hardlink_path)
-{
-       int r;
-       ext4_file f;
-       uint32_t name_off;
-       bool child_loaded = false;
-       uint32_t parent_inode, child_inode;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path);
-       struct ext4_inode_ref child_ref;
-
-       if (!mp)
-               return ENOENT;
-
-       /* Will that happen? Anyway return EINVAL for such case. */
-       if (mp != target_mp)
-               return EINVAL;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
-                              &parent_inode, &name_off);
-       if (r != EOK)
-               goto Finish;
-
-       child_inode = f.inode;
-       ext4_fclose(&f);
-
-       /*We have file to unlink. Load it.*/
-       r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
-       if (r != EOK)
-               goto Finish;
-
-       child_loaded = true;
-
-       /* Creating hardlink for directory is not allowed. */
-       if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode,
-                              EXT4_INODE_MODE_DIRECTORY)) {
-               r = EINVAL;
-               goto Finish;
-       }
-
-       r = ext4_create_hardlink(hardlink_path, &child_ref, false);
-
-Finish:
-       if (child_loaded)
-               ext4_fs_put_inode_ref(&child_ref);
-
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-
-}
-
-int ext4_frename(const char *path, const char *new_path)
-{
-       int r;
-       ext4_file f;
-       uint32_t name_off;
-       bool parent_loaded = false, child_loaded = false;
-       uint32_t parent_inode, child_inode;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       struct ext4_inode_ref child_ref, parent_ref;
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
-                               &parent_inode, &name_off);
-       if (r != EOK)
-               goto Finish;
-
-       child_inode = f.inode;
-       ext4_fclose(&f);
-
-       /*Load parent*/
-       r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref);
-       if (r != EOK)
-               goto Finish;
-
-       parent_loaded = true;
-
-       /*We have file to unlink. Load it.*/
-       r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
-       if (r != EOK)
-               goto Finish;
-
-       child_loaded = true;
-
-       r = ext4_create_hardlink(new_path, &child_ref, true);
-       if (r != EOK)
-               goto Finish;
-
-       r = ext4_remove_orig_reference(path, name_off, &parent_ref, &child_ref);
-       if (r != EOK)
-               goto Finish;
-
-Finish:
-       if (parent_loaded)
-               ext4_fs_put_inode_ref(&parent_ref);
-
-       if (child_loaded)
-               ext4_fs_put_inode_ref(&child_ref);
-
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       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);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN,
-                              &parent_inode, &name_off);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       /*Load parent*/
-       r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               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_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-       /* We do not allow opening files here. */
-       if (ext4_inode_type(&mp->fs.sb, child.inode) ==
-           EXT4_INODE_MODE_DIRECTORY) {
-               ext4_fs_put_inode_ref(&parent);
-               ext4_fs_put_inode_ref(&child);
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       /*Link count will be zero, the inode should be freed. */
-       if (ext4_inode_get_links_cnt(child.inode) == 1) {
-               ext4_block_cache_write_back(mp->fs.bdev, 1);
-               r = ext4_trunc_inode(mp, child.index, 0);
-               if (r != EOK) {
-                       ext4_fs_put_inode_ref(&parent);
-                       ext4_fs_put_inode_ref(&child);
-                       ext4_trans_abort(mp);
-                       EXT4_MP_UNLOCK(mp);
-                       return r;
-               }
-               ext4_block_cache_write_back(mp->fs.bdev, 0);
-       }
-
-       /*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_cnt(child.inode)) {
-               ext4_inode_set_del_time(child.inode, -1L);
-
-               r = ext4_fs_free_inode(&child);
-               if (r != EOK)
-                       goto Finish;
-       }
-
-Finish:
-       ext4_fs_put_inode_ref(&child);
-       ext4_fs_put_inode_ref(&parent);
-
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_fill_raw_inode(const char *path, uint32_t *ret_ino,
-                       struct ext4_inode *inode)
-{
-       int r;
-       ext4_file f;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       uint32_t ino;
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK) {
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       ino = f.inode;
-       ext4_fclose(&f);
-
-       /*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);
-
-       if (ret_ino)
-               *ret_ino = ino;
-
-       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)
-{
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       int r;
-       int filetype;
-
-       if (!mp)
-               return ENOENT;
-
-        filetype = EXT4_DE_REG_FILE;
-
-       EXT4_MP_LOCK(mp);
-
-       ext4_block_cache_write_back(mp->fs.bdev, 1);
-       r = ext4_generic_open2(f, path, flags, filetype, NULL, NULL);
-       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;
-}
-
-static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
-{
-       struct ext4_inode_ref ref;
-       int r;
-
-
-       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_trunc_inode(f->mp, ref.index, 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);
-       return r;
-
-}
-
-int ext4_ftruncate(ext4_file *f, uint64_t size)
-{
-       int r;
-       ext4_assert(f && f->mp);
-
-       if (f->flags & O_RDONLY)
-               return EPERM;
-
-       EXT4_MP_LOCK(f->mp);
-
-       ext4_trans_start(f->mp);
-       r = ext4_ftruncate_no_lock(f, size);
-       if (r != EOK)
-               ext4_trans_abort(f->mp);
-       else
-               ext4_trans_stop(f->mp);
-
-       EXT4_MP_UNLOCK(f->mp);
-       return r;
-}
-
-int ext4_fread(ext4_file *f, void *buf, size_t size, size_t *rcnt)
-{
-       uint32_t unalg;
-       uint32_t iblock_idx;
-       uint32_t iblock_last;
-       uint32_t block_size;
-
-       ext4_fsblk_t fblock;
-       ext4_fsblk_t fblock_start;
-       uint32_t fblock_count;
-
-       uint8_t *u8_buf = buf;
-       int r;
-       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);
-
-       struct ext4_fs *const fs = &f->mp->fs;
-       struct ext4_sblock *const sb = &f->mp->fs.sb;
-
-       if (rcnt)
-               *rcnt = 0;
-
-       r = ext4_fs_get_inode_ref(fs, f->inode, &ref);
-       if (r != EOK) {
-               EXT4_MP_UNLOCK(f->mp);
-               return r;
-       }
-
-       /*Sync file size*/
-       f->fsize = ext4_inode_get_size(sb, ref.inode);
-
-       block_size = ext4_sb_get_block_size(sb);
-       size = ((uint64_t)size > (f->fsize - f->fpos))
-               ? ((size_t)(f->fsize - f->fpos)) : size;
-
-       iblock_idx = (uint32_t)((f->fpos) / block_size);
-       iblock_last = (uint32_t)((f->fpos + size) / block_size);
-       unalg = (f->fpos) % block_size;
-
-       /*If the size of symlink is smaller than 60 bytes*/
-       bool softlink;
-       softlink = ext4_inode_is_type(sb, ref.inode, EXT4_INODE_MODE_SOFTLINK);
-       if (softlink && f->fsize < sizeof(ref.inode->blocks)
-                    && !ext4_inode_get_blocks_count(sb, ref.inode)) {
-
-               char *content = (char *)ref.inode->blocks;
-               if (f->fpos < f->fsize) {
-                       size_t len = size;
-                       if (unalg + size > (uint32_t)f->fsize)
-                               len = (uint32_t)f->fsize - unalg;
-                       memcpy(buf, content + unalg, len);
-                       if (rcnt)
-                               *rcnt = len;
-
-               }
-
-               r = EOK;
-               goto Finish;
-       }
-
-       if (unalg) {
-               size_t len =  size;
-               if (size > (block_size - unalg))
-                       len = block_size - unalg;
-
-               r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true);
-               if (r != EOK)
-                       goto Finish;
-
-               /* Do we get an unwritten range? */
-               if (fblock != 0) {
-                       uint64_t off = fblock * block_size + unalg;
-                       r = ext4_block_readbytes(f->mp->fs.bdev, off, u8_buf, len);
-                       if (r != EOK)
-                               goto Finish;
-
-               } else {
-                       /* Yes, we do. */
-                       memset(u8_buf, 0, len);
-               }
-
-               u8_buf += len;
-               size -= len;
-               f->fpos += len;
-
-               if (rcnt)
-                       *rcnt += len;
-
-               iblock_idx++;
-       }
-
-       fblock_start = 0;
-       fblock_count = 0;
-       while (size >= block_size) {
-               while (iblock_idx < iblock_last) {
-                       r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx,
-                                                      &fblock, true);
-                       if (r != EOK)
-                               goto Finish;
-
-                       iblock_idx++;
-
-                       if (!fblock_start)
-                               fblock_start = fblock;
-
-                       if ((fblock_start + fblock_count) != fblock)
-                               break;
-
-                       fblock_count++;
-               }
-
-               r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
-                                          fblock_count);
-               if (r != EOK)
-                       goto Finish;
-
-               size -= block_size * fblock_count;
-               u8_buf += block_size * fblock_count;
-               f->fpos += block_size * fblock_count;
-
-               if (rcnt)
-                       *rcnt += block_size * fblock_count;
-
-               fblock_start = fblock;
-               fblock_count = 1;
-       }
-
-       if (size) {
-               uint64_t off;
-               r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true);
-               if (r != EOK)
-                       goto Finish;
-
-               off = fblock * block_size;
-               r = ext4_block_readbytes(f->mp->fs.bdev, off, u8_buf, size);
-               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, size_t size, size_t *wcnt)
-{
-       uint32_t unalg;
-       uint32_t iblk_idx;
-       uint32_t iblock_last;
-       uint32_t ifile_blocks;
-       uint32_t block_size;
-
-       uint32_t fblock_count;
-       ext4_fsblk_t fblk;
-       ext4_fsblk_t fblock_start;
-
-       struct ext4_inode_ref ref;
-       const uint8_t *u8_buf = buf;
-       int r, rr = EOK;
-
-       ext4_assert(f && f->mp);
-
-       if (f->flags & O_RDONLY)
-               return EPERM;
-
-       if (!size)
-               return EOK;
-
-       EXT4_MP_LOCK(f->mp);
-       ext4_trans_start(f->mp);
-
-       struct ext4_fs *const fs = &f->mp->fs;
-       struct ext4_sblock *const sb = &f->mp->fs.sb;
-
-       if (wcnt)
-               *wcnt = 0;
-
-       r = ext4_fs_get_inode_ref(fs, f->inode, &ref);
-       if (r != EOK) {
-               ext4_trans_abort(f->mp);
-               EXT4_MP_UNLOCK(f->mp);
-               return r;
-       }
-
-       /*Sync file size*/
-       f->fsize = ext4_inode_get_size(sb, ref.inode);
-       block_size = ext4_sb_get_block_size(sb);
-
-       iblock_last = (uint32_t)((f->fpos + size) / block_size);
-       iblk_idx = (uint32_t)(f->fpos / block_size);
-       ifile_blocks = (uint32_t)((f->fsize + block_size - 1) / block_size);
-
-       unalg = (f->fpos) % block_size;
-
-       if (unalg) {
-               size_t len =  size;
-               uint64_t off;
-               if (size > (block_size - unalg))
-                       len = block_size - unalg;
-
-               r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk);
-               if (r != EOK)
-                       goto Finish;
-
-               off = fblk * block_size + unalg;
-               r = ext4_block_writebytes(f->mp->fs.bdev, off, u8_buf, len);
-               if (r != EOK)
-                       goto Finish;
-
-               u8_buf += len;
-               size -= len;
-               f->fpos += len;
-
-               if (wcnt)
-                       *wcnt += len;
-
-               iblk_idx++;
-       }
-
-       /*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_count = 0;
-       while (size >= block_size) {
-
-               while (iblk_idx < iblock_last) {
-                       if (iblk_idx < ifile_blocks) {
-                               r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx,
-                                                               &fblk);
-                               if (r != EOK)
-                                       goto Finish;
-                       } else {
-                               rr = ext4_fs_append_inode_dblk(&ref, &fblk,
-                                                              &iblk_idx);
-                               if (rr != EOK) {
-                                       /* Unable to append more blocks. But
-                                        * some block might be allocated already
-                                        * */
-                                       break;
-                               }
-                       }
-
-                       iblk_idx++;
-
-                       if (!fblock_start) {
-                               fblock_start = fblk;
-                       }
-
-                       if ((fblock_start + fblock_count) != fblk)
-                               break;
-
-                       fblock_count++;
-               }
-
-               r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
-                                          fblock_count);
-               if (r != EOK)
-                       break;
-
-               size -= block_size * fblock_count;
-               u8_buf += block_size * fblock_count;
-               f->fpos += block_size * fblock_count;
-
-               if (wcnt)
-                       *wcnt += block_size * fblock_count;
-
-               fblock_start = fblk;
-               fblock_count = 1;
-
-               if (rr != EOK) {
-                       /*ext4_fs_append_inode_block has failed and no
-                        * more blocks might be written. But node size
-                        * should be updated.*/
-                       r = rr;
-                       goto out_fsize;
-               }
-       }
-
-       /*Stop write back cache mode*/
-       ext4_block_cache_write_back(f->mp->fs.bdev, 0);
-
-       if (r != EOK)
-               goto Finish;
-
-       if (size) {
-               uint64_t off;
-               if (iblk_idx < ifile_blocks) {
-                       r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk);
-                       if (r != EOK)
-                               goto Finish;
-               } else {
-                       r = ext4_fs_append_inode_dblk(&ref, &fblk, &iblk_idx);
-                       if (r != EOK)
-                               /*Node size sholud be updated.*/
-                               goto out_fsize;
-               }
-
-               off = fblk * block_size;
-               r = ext4_block_writebytes(f->mp->fs.bdev, off, u8_buf, size);
-               if (r != EOK)
-                       goto Finish;
-
-               f->fpos += size;
-
-               if (wcnt)
-                       *wcnt += size;
-       }
-
-out_fsize:
-       if (f->fpos > f->fsize) {
-               f->fsize = f->fpos;
-               ext4_inode_set_size(ref.inode, f->fsize);
-               ref.dirty = true;
-       }
-
-Finish:
-       r = ext4_fs_put_inode_ref(&ref);
-
-       if (r != EOK)
-               ext4_trans_abort(f->mp);
-       else
-               ext4_trans_stop(f->mp);
-
-       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_chmod(const char *path, uint32_t mode)
-{
-       int r;
-       uint32_t ino, orig_mode;
-       ext4_file f;
-       struct ext4_sblock *sb;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-       ino = f.inode;
-       sb = &mp->fs.sb;
-       ext4_fclose(&f);
-       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       orig_mode = ext4_inode_get_mode(sb, inode_ref.inode);
-       orig_mode &= ~0xFFF;
-       orig_mode |= mode & 0xFFF;
-       ext4_inode_set_mode(sb, inode_ref.inode, orig_mode);
-       inode_ref.dirty = true;
-
-       r = ext4_fs_put_inode_ref(&inode_ref);
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_chown(const char *path, uint32_t uid, uint32_t gid)
-{
-       int r;
-       ext4_file f;
-       uint32_t ino;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-       ino = f.inode;
-       ext4_fclose(&f);
-       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               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;
-
-       r = ext4_fs_put_inode_ref(&inode_ref);
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_file_set_atime(const char *path, uint32_t atime)
-{
-       int r;
-       ext4_file f;
-       uint32_t ino;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-       ino = f.inode;
-       ext4_fclose(&f);
-       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       ext4_inode_set_access_time(inode_ref.inode, atime);
-       inode_ref.dirty = true;
-
-       r = ext4_fs_put_inode_ref(&inode_ref);
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_file_set_mtime(const char *path, uint32_t mtime)
-{
-       int r;
-       ext4_file f;
-       uint32_t ino;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-       ino = f.inode;
-       ext4_fclose(&f);
-       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       ext4_inode_set_modif_time(inode_ref.inode, mtime);
-       inode_ref.dirty = true;
-
-       r = ext4_fs_put_inode_ref(&inode_ref);
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_file_set_ctime(const char *path, uint32_t ctime)
-{
-       int r;
-       ext4_file f;
-       uint32_t ino;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-
-       if (!mp)
-               return ENOENT;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-       ino = f.inode;
-       ext4_fclose(&f);
-       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
-       inode_ref.dirty = true;
-
-       r = ext4_fs_put_inode_ref(&inode_ref);
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
-{
-       struct ext4_inode_ref ref;
-       uint32_t sblock;
-       ext4_fsblk_t fblock;
-       uint32_t block_size;
-       int r;
-
-       ext4_assert(f && f->mp);
-
-       if (!size)
-               return EOK;
-
-       r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
-       if (r != EOK)
-               return r;
-
-       /*Sync file size*/
-       block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
-       if (size > block_size) {
-               r = EINVAL;
-               goto Finish;
-       }
-       r = ext4_ftruncate_no_lock(f, 0);
-       if (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;
-
-       /*If the size of symlink is smaller than 60 bytes*/
-       if (size < sizeof(ref.inode->blocks)) {
-               memset(ref.inode->blocks, 0, sizeof(ref.inode->blocks));
-               memcpy(ref.inode->blocks, buf, size);
-               ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
-       } else {
-               ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
-               r = ext4_fs_append_inode_dblk(&ref, &fblock, &sblock);
-               if (r != EOK)
-                       goto Finish;
-
-               r = ext4_block_writebytes(f->mp->fs.bdev, 0, buf, size);
-               if (r != EOK)
-                       goto Finish;
-
-       }
-
-       /*Stop write back cache mode*/
-       ext4_block_cache_write_back(f->mp->fs.bdev, 0);
-
-       if (r != EOK)
-               goto Finish;
-
-       ext4_inode_set_size(ref.inode, size);
-       ref.dirty = true;
-
-       f->fsize = size;
-       if (f->fpos > size)
-               f->fpos = size;
-
-Finish:
-       ext4_fs_put_inode_ref(&ref);
-       return r;
-}
-
-int ext4_fsymlink(const char *target, const char *path)
-{
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       int r;
-       ext4_file f;
-       int filetype;
-
-       if (!mp)
-               return ENOENT;
-
-       filetype = EXT4_DE_SYMLINK;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       ext4_block_cache_write_back(mp->fs.bdev, 1);
-       r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, NULL, NULL);
-       if (r == EOK)
-               r = ext4_fsymlink_set(&f, target, strlen(target));
-       else
-               goto Finish;
-
-       ext4_fclose(&f);
-
-Finish:
-       ext4_block_cache_write_back(mp->fs.bdev, 0);
-
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
-{
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       int r;
-       ext4_file f;
-       int filetype;
-
-       if (!mp)
-               return ENOENT;
-
-       if (!buf)
-               return EINVAL;
-
-       filetype = EXT4_DE_SYMLINK;
-
-       EXT4_MP_LOCK(mp);
-       ext4_block_cache_write_back(mp->fs.bdev, 1);
-       r = ext4_generic_open2(&f, path, O_RDONLY, filetype, NULL, NULL);
-       if (r == EOK)
-               r = ext4_fread(&f, buf, bufsize, rcnt);
-       else
-               goto Finish;
-
-       ext4_fclose(&f);
-
-Finish:
-       ext4_block_cache_write_back(mp->fs.bdev, 0);
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_setxattr(const char *path, const char *name, size_t name_len,
-                 const void *data, size_t data_size, bool replace)
-{
-       int r = EOK;
-       ext4_file f;
-       uint32_t inode;
-       uint8_t name_index;
-       const char *dissected_name = NULL;
-       size_t dissected_len = 0;
-       struct ext4_xattr_ref xattr_ref;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       if (!mp)
-               return ENOENT;
-
-       dissected_name = ext4_extract_xattr_name(name, name_len,
-                               &name_index, &dissected_len);
-       if (!dissected_len)
-               return EINVAL;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK)
-               goto Finish;
-       inode = f.inode;
-       ext4_fclose(&f);
-
-       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
-       if (r != EOK)
-               goto Finish;
-
-       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
-       if (r != EOK) {
-               ext4_fs_put_inode_ref(&inode_ref);
-               goto Finish;
-       }
-
-       r = ext4_fs_set_xattr(&xattr_ref, name_index, dissected_name,
-                       dissected_len, data, data_size, replace);
-
-       ext4_fs_put_xattr_ref(&xattr_ref);
-       ext4_fs_put_inode_ref(&inode_ref);
-Finish:
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-int ext4_getxattr(const char *path, const char *name, size_t name_len,
-                 void *buf, size_t buf_size, size_t *data_size)
-{
-       int r = EOK;
-       ext4_file f;
-       uint32_t inode;
-       uint8_t name_index;
-       const char *dissected_name = NULL;
-       size_t dissected_len = 0;
-       struct ext4_xattr_ref xattr_ref;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       if (!mp)
-               return ENOENT;
-
-       dissected_name = ext4_extract_xattr_name(name, name_len,
-                               &name_index, &dissected_len);
-       if (!dissected_len)
-               return EINVAL;
-
-       EXT4_MP_LOCK(mp);
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK)
-               goto Finish;
-       inode = f.inode;
-       ext4_fclose(&f);
-
-       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
-       if (r != EOK)
-               goto Finish;
-
-       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
-       if (r != EOK) {
-               ext4_fs_put_inode_ref(&inode_ref);
-               goto Finish;
-       }
-
-       r = ext4_fs_get_xattr(&xattr_ref, name_index, dissected_name,
-                               dissected_len, buf, buf_size, data_size);
-
-       ext4_fs_put_xattr_ref(&xattr_ref);
-       ext4_fs_put_inode_ref(&inode_ref);
-Finish:
-       EXT4_MP_UNLOCK(mp);
-       return r;
-}
-
-struct ext4_listxattr_iterator {
-       char *list;
-       char *list_ptr;
-       size_t size;
-       size_t ret_size;
-       bool list_too_small;
-       bool get_required_size;
-};
-
-static int ext4_iterate_ea_list(struct ext4_xattr_ref *ref,
-                               struct ext4_xattr_item *item)
-{
-       struct ext4_listxattr_iterator *lxi;
-       lxi = ref->iter_arg;
-       if (!lxi->get_required_size) {
-               size_t plen;
-               const char *prefix;
-               prefix = ext4_get_xattr_name_prefix(item->name_index, &plen);
-               if (lxi->ret_size + plen + item->name_len + 1 > lxi->size) {
-                       lxi->list_too_small = 1;
-                       return EXT4_XATTR_ITERATE_STOP;
-               }
-               if (prefix) {
-                       memcpy(lxi->list_ptr, prefix, plen);
-                       lxi->list_ptr += plen;
-                       lxi->ret_size += plen;
-               }
-               memcpy(lxi->list_ptr, item->name, item->name_len);
-               lxi->list_ptr[item->name_len] = 0;
-               lxi->list_ptr += item->name_len + 1;
-       }
-       lxi->ret_size += item->name_len + 1;
-       return EXT4_XATTR_ITERATE_CONT;
-}
-
-int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size)
-{
-       int r = EOK;
-       ext4_file f;
-       uint32_t inode;
-       struct ext4_xattr_ref xattr_ref;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_listxattr_iterator lxi;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       if (!mp)
-               return ENOENT;
-
-       lxi.list = list;
-       lxi.list_ptr = list;
-       lxi.size = size;
-       lxi.ret_size = 0;
-       lxi.list_too_small = false;
-       lxi.get_required_size = (!size) ? true : false;
-
-       EXT4_MP_LOCK(mp);
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK)
-               goto Finish;
-       inode = f.inode;
-       ext4_fclose(&f);
-
-       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
-       if (r != EOK)
-               goto Finish;
-
-       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
-       if (r != EOK) {
-               ext4_fs_put_inode_ref(&inode_ref);
-               goto Finish;
-       }
-
-       xattr_ref.iter_arg = &lxi;
-       ext4_fs_xattr_iterate(&xattr_ref, ext4_iterate_ea_list);
-       if (lxi.list_too_small)
-               r = ERANGE;
-
-       if (r == EOK) {
-               if (ret_size)
-                       *ret_size = lxi.ret_size;
-
-       }
-       ext4_fs_put_xattr_ref(&xattr_ref);
-       ext4_fs_put_inode_ref(&inode_ref);
-Finish:
-       EXT4_MP_UNLOCK(mp);
-       return r;
-
-}
-
-int ext4_removexattr(const char *path, const char *name, size_t name_len)
-{
-       int r = EOK;
-       ext4_file f;
-       uint32_t inode;
-       uint8_t name_index;
-       const char *dissected_name = NULL;
-       size_t dissected_len = 0;
-       struct ext4_xattr_ref xattr_ref;
-       struct ext4_inode_ref inode_ref;
-       struct ext4_mountpoint *mp = ext4_get_mount(path);
-       if (!mp)
-               return ENOENT;
-
-       dissected_name = ext4_extract_xattr_name(name, name_len,
-                                               &name_index, &dissected_len);
-       if (!dissected_len)
-               return EINVAL;
-
-       EXT4_MP_LOCK(mp);
-       ext4_trans_start(mp);
-
-       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
-       if (r != EOK)
-               goto Finish;
-       inode = f.inode;
-       ext4_fclose(&f);
-
-       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
-       if (r != EOK)
-               goto Finish;
-
-       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
-       if (r != EOK) {
-               ext4_fs_put_inode_ref(&inode_ref);
-               goto Finish;
-       }
-
-       r = ext4_fs_remove_xattr(&xattr_ref, name_index, dissected_name,
-                               dissected_len);
-
-       ext4_fs_put_xattr_ref(&xattr_ref);
-       ext4_fs_put_inode_ref(&inode_ref);
-Finish:
-       if (r != EOK)
-               ext4_trans_abort(mp);
-       else
-               ext4_trans_stop(mp);
-
-       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 act;
-       struct ext4_inode_ref child;
-       struct ext4_dir_iter 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);
-
-       struct ext4_fs *const fs = &mp->fs;
-
-       /*Check if exist.*/
-       r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       path += name_off;
-       len = ext4_path_check(path, &is_goal);
-
-       inode_current = f.inode;
-
-       ext4_block_cache_write_back(mp->fs.bdev, 1);
-
-       do {
-
-               uint64_t act_curr_pos = 0;
-               has_children = false;
-               dir_end = false;
-
-               while (r == EOK && !has_children && !dir_end) {
-
-                       /*Load directory node.*/
-                       r = ext4_fs_get_inode_ref(fs, inode_current, &act);
-                       if (r != EOK) {
-                               break;
-                       }
-
-                       /*Initialize iterator.*/
-                       r = ext4_dir_iterator_init(&it, &act, act_curr_pos);
-                       if (r != EOK) {
-                               ext4_fs_put_inode_ref(&act);
-                               break;
-                       }
-
-                       if (!it.curr) {
-                               dir_end = true;
-                               goto End;
-                       }
-
-                       ext4_trans_start(mp);
-
-                       /*Get up directory inode when ".." entry*/
-                       if ((it.curr->name_len == 2) &&
-                           ext4_is_dots(it.curr->name, it.curr->name_len)) {
-                               inode_up = ext4_dir_en_get_inode(it.curr);
-                       }
-
-                       /*If directory or file entry,  but not "." ".." entry*/
-                       if (!ext4_is_dots(it.curr->name, it.curr->name_len)) {
-
-                               /*Get child inode reference do unlink
-                                * directory/file.*/
-                               uint32_t cinode;
-                               uint32_t inode_type;
-                               cinode = ext4_dir_en_get_inode(it.curr);
-                               r = ext4_fs_get_inode_ref(fs, cinode, &child);
-                               if (r != EOK)
-                                       goto End;
-
-                               /*If directory with no leaf children*/
-                               r = ext4_has_children(&has_children, &child);
-                               if (r != EOK) {
-                                       ext4_fs_put_inode_ref(&child);
-                                       goto End;
-                               }
-
-                               if (has_children) {
-                                       /*Has directory children. Go into this
-                                        * directory.*/
-                                       inode_up = inode_current;
-                                       inode_current = cinode;
-                                       depth++;
-                                       ext4_fs_put_inode_ref(&child);
-                                       goto End;
-                               }
-                               inode_type = ext4_inode_type(&mp->fs.sb,
-                                               child.inode);
-
-                               /* Truncate */
-                               if (inode_type != EXT4_INODE_MODE_DIRECTORY)
-                                       r = ext4_trunc_inode(mp, child.index, 0);
-                               else
-                                       r = ext4_trunc_dir(mp, &act, &child);
-
-                               if (r != EOK) {
-                                       ext4_fs_put_inode_ref(&child);
-                                       goto End;
-                               }
-
-                               /*No children in child directory or file. Just
-                                * unlink.*/
-                               r = ext4_unlink(f.mp, &act, &child,
-                                               (char *)it.curr->name,
-                                               it.curr->name_len);
-                               if (r != EOK) {
-                                       ext4_fs_put_inode_ref(&child);
-                                       goto End;
-                               }
-
-                               ext4_inode_set_del_time(child.inode, -1L);
-                               ext4_inode_set_links_cnt(child.inode, 0);
-                               child.dirty = true;
-
-                               r = ext4_fs_free_inode(&child);
-                               if (r != EOK) {
-                                       ext4_fs_put_inode_ref(&child);
-                                       goto End;
-                               }
-
-                               r = ext4_fs_put_inode_ref(&child);
-                               if (r != EOK)
-                                       goto End;
-
-                       }
-
-                       r = ext4_dir_iterator_next(&it);
-                       if (r != EOK)
-                               goto End;
-
-                       act_curr_pos = it.curr_off;
-End:
-                       ext4_dir_iterator_fini(&it);
-                       if (r == EOK)
-                               r = ext4_fs_put_inode_ref(&act);
-                       else
-                               ext4_fs_put_inode_ref(&act);
-
-                       if (r != EOK)
-                               ext4_trans_abort(mp);
-                       else
-                               ext4_trans_stop(mp);
-               }
-
-               if (dir_end) {
-                       /*Directory iterator reached last entry*/
-                       depth--;
-                       if (depth)
-                               inode_current = inode_up;
-
-               }
-
-               if (r != EOK)
-                       break;
-
-       } while (depth);
-
-       /*Last unlink*/
-       if (r == EOK && !depth) {
-               /*Load parent.*/
-               struct ext4_inode_ref parent;
-               r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
-                               &parent);
-               if (r != EOK)
-                       goto Finish;
-               r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
-                               &act);
-               if (r != EOK) {
-                       ext4_fs_put_inode_ref(&act);
-                       goto Finish;
-               }
-
-               ext4_trans_start(mp);
-
-               /* In this place all directories should be
-                * unlinked.
-                * Last unlink from root of current directory*/
-               r = ext4_unlink(f.mp, &parent, &act,
-                               (char *)path, len);
-               if (r != EOK) {
-                       ext4_fs_put_inode_ref(&parent);
-                       ext4_fs_put_inode_ref(&act);
-                       goto Finish;
-               }
-
-               if (ext4_inode_get_links_cnt(act.inode) == 2) {
-                       ext4_inode_set_del_time(act.inode, -1L);
-                       ext4_inode_set_links_cnt(act.inode, 0);
-                       act.dirty = true;
-                       /*Truncate*/
-                       r = ext4_trunc_dir(mp, &parent, &act);
-                       if (r != EOK) {
-                               ext4_fs_put_inode_ref(&parent);
-                               ext4_fs_put_inode_ref(&act);
-                               goto Finish;
-                       }
-
-                       r = ext4_fs_free_inode(&act);
-                       if (r != EOK) {
-                               ext4_fs_put_inode_ref(&parent);
-                               ext4_fs_put_inode_ref(&act);
-                               goto Finish;
-                       }
-               }
-
-               r = ext4_fs_put_inode_ref(&parent);
-               if (r != EOK)
-                       goto Finish;
-
-               r = ext4_fs_put_inode_ref(&act);
-       Finish:
-               if (r != EOK)
-                       ext4_trans_abort(mp);
-               else
-                       ext4_trans_stop(mp);
-       }
-
-       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);
-       ext4_trans_start(mp);
-
-       /*Check if exist.*/
-       r = ext4_generic_open(&f, path, "r", false, 0, 0);
-       if (r == EOK) {
-               /*Directory already created*/
-               ext4_trans_stop(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       /*Create new dir*/
-       r = ext4_generic_open(&f, path, "w", false, 0, 0);
-       if (r != EOK) {
-               ext4_trans_abort(mp);
-               EXT4_MP_UNLOCK(mp);
-               return r;
-       }
-
-       ext4_trans_stop(mp);
-       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_dir_iter it;
-
-       EXT4_MP_LOCK(d->f.mp);
-
-       if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
-               EXT4_MP_UNLOCK(d->f.mp);
-               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.curr, sizeof(ext4_direntry));
-       de = &d->de;
-
-       ext4_dir_iterator_next(&it);
-
-       d->next_off = it.curr ? it.curr_off : 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;
-}
-
-void ext4_dir_entry_rewind(ext4_dir *d)
-{
-       d->next_off = 0;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4.h b/lwext4/ext4.h
deleted file mode 100644 (file)
index 4bf31ac..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-#include <stddef.h>
-
-#include "ext4_config.h"
-#include "ext4_errno.h"
-#include "ext4_oflags.h"
-#include "ext4_types.h"
-#include "ext4_blockdev.h"
-
-/********************************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 descriptor. Copy from ext4_types.h*/
-typedef struct ext4_direntry {
-       uint32_t inode;
-       uint16_t entry_length;
-       uint8_t name_length;
-       uint8_t inode_type;
-       uint8_t name[255];
-} ext4_direntry;
-
-typedef struct ext4_dir {
-       /**@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
- *          Block cache may by created automatically when bc parameter is NULL.
- * @param   bd block device
- * @param   bd block device cache
- * @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   Start journaling. Journaling start/stop functions are transparent
- *          and might be used on filesystems without journaling support.
- * @warning Usage:
- *              ext4_mount("sda1", "/");
- *              ext4_journal_start("/");
- *
- *              //File operations here...
- *
- *              ext4_journal_stop("/");
- *              ext4_umount("/");
- * @param   mount_point mount name
- * @return  standard error code */
-int ext4_journal_start(const char *mount_point);
-
-/**@brief   Stop journaling. Journaling start/stop functions are transparent
- *          and might be used on filesystems without journaling support.
- * @param   mount_point mount name
- * @return  standard error code */
-int ext4_journal_stop(const char *mount_point);
-
-/**@brief   Journal recovery.
- * @param   mount_point mount point
- * @warning Must be called after @ref ext4_mount
- * @return standard error code */
-int ext4_recover(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   create a hardlink for a file.
- * @param   path path to file
- * @param   hardlink_path path of hardlink
- * @return  standard error code */
-int ext4_flink(const char *path, const char *hardlink_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   path 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);
-
-/**@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   path fetch inode data of the path
- * @param   ret_ino the inode id of the path
- * @param   ext4_inode buffer
- * @return  standard error code*/
-int ext4_fill_raw_inode(const char *path, uint32_t *ret_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, size_t size, size_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, size_t size, size_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);
-
-/**@brief Change file/directory/link mode bits
- * @param path to file/dir/link
- * @param mode new mode bits (for example 0777)
- * @return  standard error code*/
-int ext4_chmod(const char *path, uint32_t mode);
-
-/**@brief Change file owner and group
- * @param path to file/dir/link
- * @param uid user id
- * @param gid group id
- * @return  standard error code*/
-int ext4_chown(const char *path, uint32_t uid, uint32_t gid);
-
-/**@brief Set file access time
- * @param path to file/dir/link
- * @param atime access timestamp
- * @return  standard error code*/
-int ext4_file_set_atime(const char *path, uint32_t atime);
-
-/**@brief Set file modify time
- * @param path to file/dir/link
- * @param mtime modify timestamp
- * @return  standard error code*/
-int ext4_file_set_mtime(const char *path, uint32_t mtime);
-
-/**@brief Set file change time
- * @param path to file/dir/link
- * @param ctime change timestamp
- * @return  standard error code*/
-int ext4_file_set_ctime(const char *path, uint32_t ctime);
-
-/**@brief Create symbolic link
- * @param target destination path
- * @param path source entry
- * @return standard error code*/
-int ext4_fsymlink(const char *target, const char *path);
-
-
-/**@brief Read symbolic link payload
- * @param path to symlink
- * @param buf output buffer
- * @param bufsize output buffer max size
- * @param rcnt bytes read
- * @return standard error code*/
-int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt);
-
-/**@brief Set extended attribute
- * @param path to file/directory
- * @param name name of the entry to add
- * @param name_len length of @name in bytes
- * @param data data of the entry to add
- * @param data_size size of data to add
- * @param replace whether existing entries should be replaced
- * @return standard error code*/
-int ext4_setxattr(const char *path, const char *name, size_t name_len,
-                 const void *data, size_t data_size, bool replace);
-
-/**@brief Get extended attribute
- * @param path to file/directory
- * @param name name of the entry to get
- * @param name_len length of @name in bytes
- * @param data data of the entry to get
- * @param data_size size of data to get
- * @return standard error code*/
-int ext4_getxattr(const char *path, const char *name, size_t name_len,
-                 void *buf, size_t buf_size, size_t *data_size);
-
-/**@brief List extended attributes
- * @param path to file/directory
- * @param list list to hold the name of entries
- * @param size size of @list in bytes
- * @param ret_size used bytes of @list
- * @return standard error code*/
-int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size);
-
-/**@brief Remove extended attribute
- * @param path to file/directory
- * @param name name of the entry to remove
- * @param name_len length of @name in bytes
- * @return standard error code*/
-int ext4_removexattr(const char *path, const char *name, size_t name_len);
-
-
-/*********************************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);
-
-/**@brief   Rewine directory entry offset.
- * @param   d directory handle*/
-void ext4_dir_entry_rewind(ext4_dir *d);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_balloc.c b/lwext4/ext4_balloc.c
deleted file mode 100644 (file)
index 2980e26..0000000
+++ /dev/null
@@ -1,654 +0,0 @@
-/*
- * 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_balloc.c
- * @brief Physical block allocator.
- */
-
-#include "ext4_config.h"
-#include "ext4_balloc.h"
-#include "ext4_super.h"
-#include "ext4_crc32.h"
-#include "ext4_block_group.h"
-#include "ext4_fs.h"
-#include "ext4_bitmap.h"
-#include "ext4_inode.h"
-
-/**@brief Compute number of block group from block address.
- * @param sb superblock pointer.
- * @param baddr Absolute address of block.
- * @return Block group index
- */
-uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s,
-                                      uint64_t baddr)
-{
-       if (ext4_get32(s, first_data_block) && baddr)
-               baddr--;
-
-       return (uint32_t)(baddr / ext4_get32(s, blocks_per_group));
-}
-
-/**@brief Compute the starting block address of a block group
- * @param sb   superblock pointer.
- * @param bgid block group index
- * @return Block address
- */
-uint64_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s,
-                                      uint32_t bgid)
-{
-       uint64_t baddr = 0;
-       if (ext4_get32(s, first_data_block))
-               baddr++;
-
-       baddr += bgid * ext4_get32(s, blocks_per_group);
-       return baddr;
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_balloc_bitmap_csum(struct ext4_sblock *sb,
-                                       void *bitmap)
-{
-       uint32_t checksum = 0;
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint32_t blocks_per_group = ext4_get32(sb, blocks_per_group);
-
-               /* First calculate crc32 checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
-                               sizeof(sb->uuid));
-               /* Then calculate crc32 checksum against block_group_desc */
-               checksum = ext4_crc32c(checksum, bitmap, blocks_per_group / 8);
-       }
-       return checksum;
-}
-#else
-#define ext4_balloc_bitmap_csum(...) 0
-#endif
-
-void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb,
-                                struct ext4_bgroup *bg,
-                                void *bitmap __unused)
-{
-       int desc_size = ext4_sb_get_desc_size(sb);
-       uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap);
-       uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
-                hi_checksum = to_le16(checksum >> 16);
-
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return;
-
-       /* See if we need to assign a 32bit checksum */
-       bg->block_bitmap_csum_lo = lo_checksum;
-       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->block_bitmap_csum_hi = hi_checksum;
-
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool
-ext4_balloc_verify_bitmap_csum(struct ext4_sblock *sb,
-                              struct ext4_bgroup *bg,
-                              void *bitmap __unused)
-{
-       int desc_size = ext4_sb_get_desc_size(sb);
-       uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap);
-       uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
-                hi_checksum = to_le16(checksum >> 16);
-
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return true;
-
-       if (bg->block_bitmap_csum_lo != lo_checksum)
-               return false;
-
-       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               if (bg->block_bitmap_csum_hi != hi_checksum)
-                       return false;
-
-       return true;
-}
-#else
-#define ext4_balloc_verify_bitmap_csum(...) true
-#endif
-
-int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref, ext4_fsblk_t baddr)
-{
-       struct ext4_fs *fs = inode_ref->fs;
-       struct ext4_sblock *sb = &fs->sb;
-
-       uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, baddr);
-       uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr);
-
-       /* Load block group reference */
-       struct ext4_block_group_ref bg_ref;
-       int rc = ext4_fs_get_block_group_ref(fs, bg_id, &bg_ref);
-       if (rc != EOK)
-               return rc;
-
-       struct ext4_bgroup *bg = bg_ref.block_group;
-
-       /* Load block with bitmap */
-       ext4_fsblk_t bitmap_block_addr =
-           ext4_bg_get_block_bitmap(bg, sb);
-
-       struct ext4_block bitmap_block;
-
-       rc = ext4_trans_block_get(fs->bdev, &bitmap_block, bitmap_block_addr);
-       if (rc != EOK) {
-               ext4_fs_put_block_group_ref(&bg_ref);
-               return rc;
-       }
-
-       if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap_block.data)) {
-               ext4_dbg(DEBUG_BALLOC,
-                       DBG_WARN "Bitmap checksum failed."
-                       "Group: %" PRIu32"\n",
-                       bg_ref.index);
-       }
-
-       /* Modify bitmap */
-       ext4_bmap_bit_clr(bitmap_block.data, index_in_group);
-       ext4_balloc_set_bitmap_csum(sb, bg, bitmap_block.data);
-       ext4_trans_set_block_dirty(bitmap_block.buf);
-
-       /* Release 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;
-       }
-
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-
-       /* Update superblock free blocks count */
-       uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
-       sb_free_blocks++;
-       ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
-
-       /* Update inode blocks count */
-       uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
-       ino_blocks -= block_size / EXT4_INODE_BLOCK_SIZE;
-       ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
-       inode_ref->dirty = true;
-
-       /* Update block group free blocks count */
-       uint32_t free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
-       free_blocks++;
-       ext4_bg_set_free_blocks_count(bg, sb, free_blocks);
-
-       bg_ref.dirty = true;
-
-       rc = ext4_trans_try_revoke_block(fs->bdev, baddr);
-       if (rc != EOK) {
-               bg_ref.dirty = false;
-               ext4_fs_put_block_group_ref(&bg_ref);
-               return rc;
-       }
-       ext4_bcache_invalidate_lba(fs->bdev->bc, baddr, 1);
-       /* Release block group reference */
-       return ext4_fs_put_block_group_ref(&bg_ref);
-}
-
-int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref,
-                           ext4_fsblk_t first, uint32_t count)
-{
-       int rc = EOK;
-       struct ext4_fs *fs = inode_ref->fs;
-       struct ext4_sblock *sb = &fs->sb;
-
-       /* Compute indexes */
-       uint32_t bg_first = ext4_balloc_get_bgid_of_block(sb, first);
-
-       /* Compute indexes */
-       uint32_t bg_last = ext4_balloc_get_bgid_of_block(sb, first + count - 1);
-
-       if (!ext4_sb_feature_incom(sb, EXT4_FINCOM_FLEX_BG)) {
-               /*It is not possible without flex_bg that blocks are continuous
-                * and and last block belongs to other bg.*/
-               if (bg_last != bg_first) {
-                       ext4_dbg(DEBUG_BALLOC, DBG_WARN "FLEX_BG: disabled & "
-                               "bg_last: %"PRIu32" bg_first: %"PRIu32"\n",
-                               bg_last, bg_first);
-               }
-       }
-
-       /* Load block group reference */
-       struct ext4_block_group_ref bg_ref;
-       while (bg_first <= bg_last) {
-
-               rc = ext4_fs_get_block_group_ref(fs, bg_first, &bg_ref);
-               if (rc != EOK)
-                       return rc;
-
-               struct ext4_bgroup *bg = bg_ref.block_group;
-
-               uint32_t idx_in_bg_first;
-               idx_in_bg_first = ext4_fs_addr_to_idx_bg(sb, first);
-
-               /* Load block with bitmap */
-               ext4_fsblk_t bitmap_blk = ext4_bg_get_block_bitmap(bg, sb);
-
-               struct ext4_block blk;
-               rc = ext4_trans_block_get(fs->bdev, &blk, bitmap_blk);
-               if (rc != EOK) {
-                       ext4_fs_put_block_group_ref(&bg_ref);
-                       return rc;
-               }
-
-               if (!ext4_balloc_verify_bitmap_csum(sb, bg, blk.data)) {
-                       ext4_dbg(DEBUG_BALLOC,
-                               DBG_WARN "Bitmap checksum failed."
-                               "Group: %" PRIu32"\n",
-                               bg_ref.index);
-               }
-               uint32_t free_cnt;
-               free_cnt = ext4_sb_get_block_size(sb) * 8 - idx_in_bg_first;
-
-               /*If last block, free only count blocks*/
-               free_cnt = count > free_cnt ? free_cnt : count;
-
-               /* Modify bitmap */
-               ext4_bmap_bits_free(blk.data, idx_in_bg_first, free_cnt);
-               ext4_balloc_set_bitmap_csum(sb, bg, blk.data);
-               ext4_trans_set_block_dirty(blk.buf);
-
-               count -= free_cnt;
-               first += free_cnt;
-
-               /* Release block with bitmap */
-               rc = ext4_block_set(fs->bdev, &blk);
-               if (rc != EOK) {
-                       ext4_fs_put_block_group_ref(&bg_ref);
-                       return rc;
-               }
-
-               uint32_t block_size = ext4_sb_get_block_size(sb);
-
-               /* Update superblock free blocks count */
-               uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
-               sb_free_blocks += free_cnt;
-               ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
-
-               /* Update inode blocks count */
-               uint64_t ino_blocks;
-               ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
-               ino_blocks -= free_cnt * (block_size / EXT4_INODE_BLOCK_SIZE);
-               ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
-               inode_ref->dirty = true;
-
-               /* Update block group free blocks count */
-               uint32_t free_blocks;
-               free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
-               free_blocks += free_cnt;
-               ext4_bg_set_free_blocks_count(bg, sb, free_blocks);
-               bg_ref.dirty = true;
-
-               /* Release block group reference */
-               rc = ext4_fs_put_block_group_ref(&bg_ref);
-               if (rc != EOK)
-                       break;
-
-               bg_first++;
-       }
-
-       uint32_t i;
-       for (i = 0;i < count;i++) {
-               rc = ext4_trans_try_revoke_block(fs->bdev, first + i);
-               if (rc != EOK)
-                       return rc;
-
-       }
-
-       ext4_bcache_invalidate_lba(fs->bdev->bc, first, count);
-       /*All blocks should be released*/
-       ext4_assert(count == 0);
-       return rc;
-}
-
-int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref,
-                           ext4_fsblk_t goal,
-                           ext4_fsblk_t *fblock)
-{
-       ext4_fsblk_t alloc = 0;
-       ext4_fsblk_t bmp_blk_adr;
-       uint32_t rel_blk_idx = 0;
-       uint64_t free_blocks;
-       int r;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       /* Load block group number for goal and relative index */
-       uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, goal);
-       uint32_t idx_in_bg = ext4_fs_addr_to_idx_bg(sb, goal);
-
-       struct ext4_block b;
-       struct ext4_block_group_ref bg_ref;
-
-       /* Load block group reference */
-       r = ext4_fs_get_block_group_ref(inode_ref->fs, bg_id, &bg_ref);
-       if (r != EOK)
-               return r;
-
-       struct ext4_bgroup *bg = bg_ref.block_group;
-
-       free_blocks = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb);
-       if (free_blocks == 0) {
-               /* This group has no free blocks */
-               goto goal_failed;
-       }
-
-       /* Compute indexes */
-       ext4_fsblk_t first_in_bg;
-       first_in_bg = ext4_balloc_get_block_of_bgid(sb, bg_ref.index);
-
-       uint32_t first_in_bg_index;
-       first_in_bg_index = ext4_fs_addr_to_idx_bg(sb, first_in_bg);
-
-       if (idx_in_bg < first_in_bg_index)
-               idx_in_bg = first_in_bg_index;
-
-       /* Load block with bitmap */
-       bmp_blk_adr = ext4_bg_get_block_bitmap(bg_ref.block_group, sb);
-
-       r = ext4_trans_block_get(inode_ref->fs->bdev, &b, bmp_blk_adr);
-       if (r != EOK) {
-               ext4_fs_put_block_group_ref(&bg_ref);
-               return r;
-       }
-
-       if (!ext4_balloc_verify_bitmap_csum(sb, bg, b.data)) {
-               ext4_dbg(DEBUG_BALLOC,
-                       DBG_WARN "Bitmap checksum failed."
-                       "Group: %" PRIu32"\n",
-                       bg_ref.index);
-       }
-
-       /* Check if goal is free */
-       if (ext4_bmap_is_bit_clr(b.data, idx_in_bg)) {
-               ext4_bmap_bit_set(b.data, idx_in_bg);
-               ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group,
-                                           b.data);
-               ext4_trans_set_block_dirty(b.buf);
-               r = ext4_block_set(inode_ref->fs->bdev, &b);
-               if (r != EOK) {
-                       ext4_fs_put_block_group_ref(&bg_ref);
-                       return r;
-               }
-
-               alloc = ext4_fs_bg_idx_to_addr(sb, idx_in_bg, bg_id);
-               goto success;
-       }
-
-       uint32_t blk_in_bg = ext4_blocks_in_group_cnt(sb, bg_id);
-
-       uint32_t end_idx = (idx_in_bg + 63) & ~63;
-       if (end_idx > blk_in_bg)
-               end_idx = blk_in_bg;
-
-       /* Try to find free block near to goal */
-       uint32_t tmp_idx;
-       for (tmp_idx = idx_in_bg + 1; tmp_idx < end_idx; ++tmp_idx) {
-               if (ext4_bmap_is_bit_clr(b.data, tmp_idx)) {
-                       ext4_bmap_bit_set(b.data, tmp_idx);
-
-                       ext4_balloc_set_bitmap_csum(sb, bg, b.data);
-                       ext4_trans_set_block_dirty(b.buf);
-                       r = ext4_block_set(inode_ref->fs->bdev, &b);
-                       if (r != EOK)
-                               return r;
-
-                       alloc = ext4_fs_bg_idx_to_addr(sb, tmp_idx, bg_id);
-                       goto success;
-               }
-       }
-
-       /* Find free bit in bitmap */
-       r = ext4_bmap_bit_find_clr(b.data, idx_in_bg, blk_in_bg, &rel_blk_idx);
-       if (r == EOK) {
-               ext4_bmap_bit_set(b.data, rel_blk_idx);
-               ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, b.data);
-               ext4_trans_set_block_dirty(b.buf);
-               r = ext4_block_set(inode_ref->fs->bdev, &b);
-               if (r != EOK)
-                       return r;
-
-               alloc = ext4_fs_bg_idx_to_addr(sb, rel_blk_idx, bg_id);
-               goto success;
-       }
-
-       /* No free block found yet */
-       r = ext4_block_set(inode_ref->fs->bdev, &b);
-       if (r != EOK) {
-               ext4_fs_put_block_group_ref(&bg_ref);
-               return r;
-       }
-
-goal_failed:
-
-       r = ext4_fs_put_block_group_ref(&bg_ref);
-       if (r != EOK)
-               return r;
-
-       /* Try other block groups */
-       uint32_t block_group_count = ext4_block_group_cnt(sb);
-       uint32_t bgid = (bg_id + 1) % block_group_count;
-       uint32_t count = block_group_count;
-
-       while (count > 0) {
-               r = ext4_fs_get_block_group_ref(inode_ref->fs, bgid, &bg_ref);
-               if (r != EOK)
-                       return r;
-
-               struct ext4_bgroup *bg = bg_ref.block_group;
-               free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
-               if (free_blocks == 0) {
-                       /* This group has no free blocks */
-                       goto next_group;
-               }
-
-               /* Load block with bitmap */
-               bmp_blk_adr = ext4_bg_get_block_bitmap(bg, sb);
-               r = ext4_trans_block_get(inode_ref->fs->bdev, &b, bmp_blk_adr);
-               if (r != EOK) {
-                       ext4_fs_put_block_group_ref(&bg_ref);
-                       return r;
-               }
-
-               if (!ext4_balloc_verify_bitmap_csum(sb, bg, b.data)) {
-                       ext4_dbg(DEBUG_BALLOC,
-                               DBG_WARN "Bitmap checksum failed."
-                               "Group: %" PRIu32"\n",
-                               bg_ref.index);
-               }
-
-               /* Compute indexes */
-               first_in_bg = ext4_balloc_get_block_of_bgid(sb, bgid);
-               idx_in_bg = ext4_fs_addr_to_idx_bg(sb, first_in_bg);
-               blk_in_bg = ext4_blocks_in_group_cnt(sb, bgid);
-               first_in_bg_index = ext4_fs_addr_to_idx_bg(sb, first_in_bg);
-
-               if (idx_in_bg < first_in_bg_index)
-                       idx_in_bg = first_in_bg_index;
-
-               r = ext4_bmap_bit_find_clr(b.data, idx_in_bg, blk_in_bg,
-                               &rel_blk_idx);
-               if (r == EOK) {
-                       ext4_bmap_bit_set(b.data, rel_blk_idx);
-                       ext4_balloc_set_bitmap_csum(sb, bg, b.data);
-                       ext4_trans_set_block_dirty(b.buf);
-                       r = ext4_block_set(inode_ref->fs->bdev, &b);
-                       if (r != EOK) {
-                               ext4_fs_put_block_group_ref(&bg_ref);
-                               return r;
-                       }
-
-                       alloc = ext4_fs_bg_idx_to_addr(sb, rel_blk_idx, bgid);
-                       goto success;
-               }
-
-               r = ext4_block_set(inode_ref->fs->bdev, &b);
-               if (r != EOK) {
-                       ext4_fs_put_block_group_ref(&bg_ref);
-                       return r;
-               }
-
-       next_group:
-               r = ext4_fs_put_block_group_ref(&bg_ref);
-               if (r != EOK) {
-                       return r;
-               }
-
-               /* Goto next group */
-               bgid = (bgid + 1) % block_group_count;
-               count--;
-       }
-
-       return ENOSPC;
-
-success:
-    /* Empty command - because of syntax */
-    ;
-
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-
-       /* Update superblock free blocks count */
-       uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
-       sb_free_blocks--;
-       ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
-
-       /* Update inode blocks (different block size!) count */
-       uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
-       ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE;
-       ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
-       inode_ref->dirty = true;
-
-       /* Update block group free blocks count */
-
-       uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb);
-       fb_cnt--;
-       ext4_bg_set_free_blocks_count(bg_ref.block_group, sb, fb_cnt);
-
-       bg_ref.dirty = true;
-       r = ext4_fs_put_block_group_ref(&bg_ref);
-
-       *fblock = alloc;
-       return r;
-}
-
-int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref,
-                               ext4_fsblk_t baddr, bool *free)
-{
-       int rc;
-
-       struct ext4_fs *fs = inode_ref->fs;
-       struct ext4_sblock *sb = &fs->sb;
-
-       /* Compute indexes */
-       uint32_t block_group = ext4_balloc_get_bgid_of_block(sb, baddr);
-       uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr);
-
-       /* Load block group reference */
-       struct ext4_block_group_ref bg_ref;
-       rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
-       if (rc != EOK)
-               return rc;
-
-       /* Load block with bitmap */
-       ext4_fsblk_t bmp_blk_addr;
-       bmp_blk_addr = ext4_bg_get_block_bitmap(bg_ref.block_group, sb);
-
-       struct ext4_block b;
-       rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_addr);
-       if (rc != EOK) {
-               ext4_fs_put_block_group_ref(&bg_ref);
-               return rc;
-       }
-
-       if (!ext4_balloc_verify_bitmap_csum(sb, bg_ref.block_group, b.data)) {
-               ext4_dbg(DEBUG_BALLOC,
-                       DBG_WARN "Bitmap checksum failed."
-                       "Group: %" PRIu32"\n",
-                       bg_ref.index);
-       }
-
-       /* Check if block is free */
-       *free = ext4_bmap_is_bit_clr(b.data, index_in_group);
-
-       /* Allocate block if possible */
-       if (*free) {
-               ext4_bmap_bit_set(b.data, index_in_group);
-               ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, b.data);
-               ext4_trans_set_block_dirty(b.buf);
-       }
-
-       /* Release block with bitmap */
-       rc = ext4_block_set(fs->bdev, &b);
-       if (rc != EOK) {
-               /* Error in saving bitmap */
-               ext4_fs_put_block_group_ref(&bg_ref);
-               return rc;
-       }
-
-       /* If block is not free, return */
-       if (!(*free))
-               goto terminate;
-
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-
-       /* Update superblock free blocks count */
-       uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
-       sb_free_blocks--;
-       ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
-
-       /* Update inode blocks count */
-       uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
-       ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE;
-       ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
-       inode_ref->dirty = true;
-
-       /* Update block group free blocks count */
-       uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb);
-       fb_cnt--;
-       ext4_bg_set_free_blocks_count(bg_ref.block_group, sb, fb_cnt);
-
-       bg_ref.dirty = true;
-
-terminate:
-       return ext4_fs_put_block_group_ref(&bg_ref);
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_balloc.h b/lwext4/ext4_balloc.h
deleted file mode 100644 (file)
index f2c3dc9..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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_balloc.h
- * @brief Physical block allocator.
- */
-
-#ifndef EXT4_BALLOC_H_
-#define EXT4_BALLOC_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/**@brief Compute number of block group from block address.
- * @param sb superblock pointer.
- * @param baddr Absolute address of block.
- * @return Block group index
- */
-uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s,
-                                      ext4_fsblk_t baddr);
-
-/**@brief Compute the starting block address of a block group
- * @param sb   superblock pointer.
- * @param bgid block group index
- * @return Block address
- */
-ext4_fsblk_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s,
-                                          uint32_t bgid);
-
-/**@brief Calculate and set checksum of block bitmap.
- * @param sb superblock pointer.
- * @param bg block group
- * @param bitmap bitmap buffer
- */
-void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb,
-                                struct ext4_bgroup *bg,
-                                void *bitmap);
-
-/**@brief   Free block from inode.
- * @param   inode_ref inode reference
- * @param   baddr block address
- * @return  standard error code*/
-int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref,
-                          ext4_fsblk_t baddr);
-
-/**@brief   Free blocks from inode.
- * @param   inode_ref inode reference
- * @param   first block address
- * @param   count block count
- * @return  standard error code*/
-int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref,
-                           ext4_fsblk_t first, uint32_t count);
-
-/**@brief   Allocate block procedure.
- * @param   inode_ref inode reference
- * @param   goal
- * @param   baddr allocated block address
- * @return  standard error code*/
-int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref,
-                           ext4_fsblk_t goal,
-                           ext4_fsblk_t *baddr);
-
-/**@brief   Try allocate selected block.
- * @param   inode_ref inode reference
- * @param   baddr block address to allocate
- * @param   free if baddr is not allocated
- * @return  standard error code*/
-int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref,
-                               ext4_fsblk_t baddr, bool *free);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_BALLOC_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_bcache.c b/lwext4/ext4_bcache.c
deleted file mode 100644 (file)
index 1a1766a..0000000
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * 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_blockdev.h"
-#include "ext4_debug.h"
-#include "ext4_errno.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-static int ext4_bcache_lba_compare(struct ext4_buf *a, struct ext4_buf *b)
-{
-        if (a->lba > b->lba)
-                return 1;
-        else if (a->lba < b->lba)
-                return -1;
-        return 0;
-}
-
-static int ext4_bcache_lru_compare(struct ext4_buf *a, struct ext4_buf *b)
-{
-       if (a->lru_id > b->lru_id)
-               return 1;
-       else if (a->lru_id < b->lru_id)
-               return -1;
-       return 0;
-}
-
-RB_GENERATE_INTERNAL(ext4_buf_lba, ext4_buf, lba_node,
-                    ext4_bcache_lba_compare, static inline)
-RB_GENERATE_INTERNAL(ext4_buf_lru, ext4_buf, lru_node,
-                    ext4_bcache_lru_compare, static inline)
-
-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->cnt = cnt;
-       bc->itemsize = itemsize;
-       bc->ref_blocks = 0;
-       bc->max_ref_blocks = 0;
-
-       return EOK;
-}
-
-void ext4_bcache_cleanup(struct ext4_bcache *bc)
-{
-       struct ext4_buf *buf, *tmp;
-       RB_FOREACH_SAFE(buf, ext4_buf_lba, &bc->lba_root, tmp) {
-               ext4_block_flush_buf(bc->bdev, buf);
-               ext4_bcache_drop_buf(bc, buf);
-       }
-}
-
-int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
-{
-       memset(bc, 0, sizeof(struct ext4_bcache));
-       return EOK;
-}
-
-/**@brief:
- *
- *  This is ext4_bcache, the module handling basic buffer-cache stuff.
- *
- *  Buffers in a bcache are sorted by their LBA and stored in a
- *  RB-Tree(lba_root).
- *
- *  Bcache also maintains another RB-Tree(lru_root) right now, where
- *  buffers are sorted by their LRU id.
- *
- *  A singly-linked list is used to track those dirty buffers which are
- *  ready to be flushed. (Those buffers which are dirty but also referenced
- *  are not considered ready to be flushed.)
- *
- *  When a buffer is not referenced, it will be stored in both lba_root
- *  and lru_root, while it will only be stored in lba_root when it is
- *  referenced.
- */
-
-static struct ext4_buf *
-ext4_buf_alloc(struct ext4_bcache *bc, uint64_t lba)
-{
-       void *data;
-       struct ext4_buf *buf;
-       data = malloc(bc->itemsize);
-       if (!data)
-               return NULL;
-
-       buf = calloc(1, sizeof(struct ext4_buf));
-       if (!buf) {
-               free(data);
-               return NULL;
-       }
-
-       buf->lba = lba;
-       buf->data = data;
-       buf->bc = bc;
-       return buf;
-}
-
-static void ext4_buf_free(struct ext4_buf *buf)
-{
-       free(buf->data);
-       free(buf);
-}
-
-static struct ext4_buf *
-ext4_buf_lookup(struct ext4_bcache *bc, uint64_t lba)
-{
-       struct ext4_buf tmp = {
-               .lba = lba
-       };
-
-       return RB_FIND(ext4_buf_lba, &bc->lba_root, &tmp);
-}
-
-struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc)
-{
-       return RB_MIN(ext4_buf_lru, &bc->lru_root);
-}
-
-void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf)
-{
-       /* Warn on dropping any referenced buffers.*/
-       if (buf->refctr) {
-               ext4_dbg(DEBUG_BCACHE, DBG_WARN "Buffer is still referenced. "
-                               "lba: %" PRIu64 ", refctr: %" PRIu32 "\n",
-                               buf->lba, buf->refctr);
-       } else
-               RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
-
-       RB_REMOVE(ext4_buf_lba, &bc->lba_root, buf);
-
-       /*Forcibly drop dirty buffer.*/
-       if (ext4_bcache_test_flag(buf, BC_DIRTY))
-               ext4_bcache_remove_dirty_node(bc, buf);
-
-       ext4_buf_free(buf);
-       bc->ref_blocks--;
-}
-
-void ext4_bcache_invalidate_lba(struct ext4_bcache *bc,
-                               uint64_t from,
-                               uint32_t cnt)
-{
-       uint64_t end = from + cnt - 1;
-       struct ext4_buf *tmp = ext4_buf_lookup(bc, from), *buf;
-       RB_FOREACH_FROM(buf, ext4_buf_lba, tmp) {
-               if (buf->lba > end)
-                       break;
-
-               /* Clear both dirty and up-to-date flags. */
-               if (ext4_bcache_test_flag(buf, BC_DIRTY))
-                       ext4_bcache_remove_dirty_node(bc, buf);
-
-               buf->end_write = NULL;
-               buf->end_write_arg = NULL;
-               ext4_bcache_clear_dirty(buf);
-       }
-}
-
-struct ext4_buf *
-ext4_bcache_find_get(struct ext4_bcache *bc, struct ext4_block *b,
-                    uint64_t lba)
-{
-       struct ext4_buf *buf = ext4_buf_lookup(bc, lba);
-       if (buf) {
-               /* If buffer is not referenced. */
-               if (!buf->refctr) {
-                       /* Assign new value to LRU id and increment LRU counter
-                        * by 1*/
-                       buf->lru_id = ++bc->lru_ctr;
-                       RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
-                       if (ext4_bcache_test_flag(buf, BC_DIRTY))
-                               ext4_bcache_remove_dirty_node(bc, buf);
-
-               }
-
-               ext4_bcache_inc_ref(buf);
-
-               b->lb_id = lba;
-               b->buf = buf;
-               b->data = buf->data;
-       }
-       return buf;
-}
-
-int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
-                     bool *is_new)
-{
-       /* Try to search the buffer with exaxt LBA. */
-       struct ext4_buf *buf = ext4_bcache_find_get(bc, b, b->lb_id);
-       if (buf) {
-               *is_new = false;
-               return EOK;
-       }
-
-       /* We need to allocate one buffer.*/
-       buf = ext4_buf_alloc(bc, b->lb_id);
-       if (!buf)
-               return ENOMEM;
-
-       RB_INSERT(ext4_buf_lba, &bc->lba_root, buf);
-       /* One more buffer in bcache now. :-) */
-       bc->ref_blocks++;
-
-       /*Calc ref blocks max depth*/
-       if (bc->max_ref_blocks < bc->ref_blocks)
-               bc->max_ref_blocks = bc->ref_blocks;
-
-
-       ext4_bcache_inc_ref(buf);
-       /* Assign new value to LRU id and increment LRU counter
-        * by 1*/
-       buf->lru_id = ++bc->lru_ctr;
-
-       b->buf = buf;
-       b->data = buf->data;
-
-       *is_new = true;
-       return EOK;
-}
-
-int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b)
-{
-       struct ext4_buf *buf = b->buf;
-
-       ext4_assert(bc && b);
-
-       /*Check if valid.*/
-       ext4_assert(b->lb_id);
-
-       /*Block should have a valid pointer to ext4_buf.*/
-       ext4_assert(buf);
-
-       /*Check if someone don't try free unreferenced block cache.*/
-       ext4_assert(buf->refctr);
-
-       /*Just decrease reference counter*/
-       ext4_bcache_dec_ref(buf);
-
-       /* We are the last one touching this buffer, do the cleanups. */
-       if (!buf->refctr) {
-               RB_INSERT(ext4_buf_lru, &bc->lru_root, buf);
-               /* This buffer is ready to be flushed. */
-               if (ext4_bcache_test_flag(buf, BC_DIRTY) &&
-                   ext4_bcache_test_flag(buf, BC_UPTODATE)) {
-                       if (bc->bdev->cache_write_back &&
-                           !ext4_bcache_test_flag(buf, BC_FLUSH) &&
-                           !ext4_bcache_test_flag(buf, BC_TMP))
-                               ext4_bcache_insert_dirty_node(bc, buf);
-                       else {
-                               ext4_block_flush_buf(bc->bdev, buf);
-                               ext4_bcache_clear_flag(buf, BC_FLUSH);
-                       }
-               }
-
-               /* The buffer is invalidated...drop it. */
-               if (!ext4_bcache_test_flag(buf, BC_UPTODATE) ||
-                   ext4_bcache_test_flag(buf, BC_TMP))
-                       ext4_bcache_drop_buf(bc, buf);
-
-       }
-
-       b->lb_id = 0;
-       b->data = 0;
-
-       return EOK;
-}
-
-bool ext4_bcache_is_full(struct ext4_bcache *bc)
-{
-       return (bc->cnt <= bc->ref_blocks);
-}
-
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_bcache.h b/lwext4/ext4_bcache.h
deleted file mode 100644 (file)
index bceb75a..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "misc/tree.h"
-#include "misc/queue.h"
-
-#define EXT4_BLOCK_ZERO()      \
-       {.lb_id = 0, .data = 0}
-
-/**@brief   Single block descriptor*/
-struct ext4_block {
-       /**@brief   Logical block ID*/
-       uint64_t lb_id;
-
-       /**@brief   Buffer */
-       struct ext4_buf *buf;
-
-       /**@brief   Data buffer.*/
-       uint8_t *data;
-};
-
-struct ext4_bcache;
-
-/**@brief   Single block descriptor*/
-struct ext4_buf {
-       /**@brief   Flags*/
-       int flags;
-
-       /**@brief   Logical block address*/
-       uint64_t lba;
-
-       /**@brief   Data buffer.*/
-       uint8_t *data;
-
-       /**@brief   LRU priority. (unused) */
-       uint32_t lru_prio;
-
-       /**@brief   LRU id.*/
-       uint32_t lru_id;
-
-       /**@brief   Reference count table*/
-       uint32_t refctr;
-
-       /**@brief   The block cache this buffer belongs to. */
-       struct ext4_bcache *bc;
-
-       /**@brief   Whether or not buffer is on dirty list.*/
-       bool on_dirty_list;
-
-       /**@brief   LBA tree node*/
-       RB_ENTRY(ext4_buf) lba_node;
-
-       /**@brief   LRU tree node*/
-       RB_ENTRY(ext4_buf) lru_node;
-
-       /**@brief   Dirty list node*/
-       SLIST_ENTRY(ext4_buf) dirty_node;
-
-       /**@brief   Callback routine after a disk-write operation.
-        * @param   bc block cache descriptor
-        * @param   buf buffer descriptor
-        * @param   standard error code returned by bdev->bwrite()
-        * @param   arg argument passed to this routine*/
-       void (*end_write)(struct ext4_bcache *bc,
-                         struct ext4_buf *buf,
-                         int res,
-                         void *arg);
-
-       /**@brief   argument passed to end_write() callback.*/
-       void *end_write_arg;
-};
-
-/**@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   Currently referenced datablocks*/
-       uint32_t ref_blocks;
-
-       /**@brief   Maximum referenced datablocks*/
-       uint32_t max_ref_blocks;
-
-       /**@brief   The blockdev binded to this block cache*/
-       struct ext4_blockdev *bdev;
-
-       /**@brief   The cache should not be shaked */
-       bool dont_shake;
-
-       /**@brief   A tree holding all bufs*/
-       RB_HEAD(ext4_buf_lba, ext4_buf) lba_root;
-
-       /**@brief   A tree holding unreferenced bufs*/
-       RB_HEAD(ext4_buf_lru, ext4_buf) lru_root;
-
-       /**@brief   A singly-linked list holding dirty buffers*/
-       SLIST_HEAD(ext4_buf_dirty, ext4_buf) dirty_list;
-};
-
-/**@brief buffer state bits
- *
- *  - BC♡UPTODATE: Buffer contains valid data.
- *  - BC_DIRTY: Buffer is dirty.
- *  - BC_FLUSH: Buffer will be immediately flushed,
- *              when no one references it.
- *  - BC_TMP: Buffer will be dropped once its refctr
- *            reaches zero.
- */
-enum bcache_state_bits {
-       BC_UPTODATE,
-       BC_DIRTY,
-       BC_FLUSH,
-       BC_TMP
-};
-
-#define ext4_bcache_set_flag(buf, b)    \
-       (buf)->flags |= 1 << (b)
-
-#define ext4_bcache_clear_flag(buf, b)    \
-       (buf)->flags &= ~(1 << (b))
-
-#define ext4_bcache_test_flag(buf, b)    \
-       (((buf)->flags & (1 << (b))) >> (b))
-
-static inline void ext4_bcache_set_dirty(struct ext4_buf *buf) {
-       ext4_bcache_set_flag(buf, BC_UPTODATE);
-       ext4_bcache_set_flag(buf, BC_DIRTY);
-}
-
-static inline void ext4_bcache_clear_dirty(struct ext4_buf *buf) {
-       ext4_bcache_clear_flag(buf, BC_UPTODATE);
-       ext4_bcache_clear_flag(buf, BC_DIRTY);
-}
-
-/**@brief   Increment reference counter of buf by 1.*/
-#define ext4_bcache_inc_ref(buf) ((buf)->refctr++)
-
-/**@brief   Decrement reference counter of buf by 1.*/
-#define ext4_bcache_dec_ref(buf) ((buf)->refctr--)
-
-/**@brief   Insert buffer to dirty cache list
- * @param   bc block cache descriptor
- * @param   buf buffer descriptor */
-static inline void
-ext4_bcache_insert_dirty_node(struct ext4_bcache *bc, struct ext4_buf *buf) {
-       if (!buf->on_dirty_list) {
-               SLIST_INSERT_HEAD(&bc->dirty_list, buf, dirty_node);
-               buf->on_dirty_list = true;
-       }
-}
-
-/**@brief   Remove buffer to dirty cache list
- * @param   bc block cache descriptor
- * @param   buf buffer descriptor */
-static inline void
-ext4_bcache_remove_dirty_node(struct ext4_bcache *bc, struct ext4_buf *buf) {
-       if (buf->on_dirty_list) {
-               SLIST_REMOVE(&bc->dirty_list, buf, ext4_buf, dirty_node);
-               buf->on_dirty_list = false;
-       }
-}
-
-
-/**@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   Do cleanup works on block cache.
- * @param   bc block cache descriptor.*/
-void ext4_bcache_cleanup(struct ext4_bcache *bc);
-
-/**@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   Get a buffer with the lowest LRU counter in bcache.
- * @param   bc block cache descriptor
- * @return  buffer with the lowest LRU counter*/
-struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc);
-
-/**@brief   Drop unreferenced buffer from bcache.
- * @param   bc block cache descriptor
- * @param   buf buffer*/
-void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf);
-
-/**@brief   Invalidate a range of buffers.
- * @param   bc block cache descriptor
- * @param   from starting lba
- * @param   cnt block counts
- * @param   buf buffer*/
-void ext4_bcache_invalidate_lba(struct ext4_bcache *bc,
-                               uint64_t from,
-                               uint32_t cnt);
-
-/**@brief   Find existing buffer 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   lba logical block address
- * @return  block cache buffer */
-struct ext4_buf *
-ext4_bcache_find_get(struct ext4_bcache *bc, struct ext4_block *b,
-                    uint64_t lba);
-
-/**@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
- * @return  standard error code*/
-int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b);
-
-/**@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);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_BCACHE_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_bitmap.c b/lwext4/ext4_bitmap.c
deleted file mode 100644 (file)
index 1320033..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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_bitmap.c
- * @brief Block/inode bitmap allocator.
- */
-
-#include "ext4_config.h"
-#include "ext4_bitmap.h"
-
-#include "ext4_errno.h"
-
-void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt)
-{
-       uint32_t i = sbit;
-
-       while (i & 7) {
-
-               if (!bcnt)
-                       return;
-
-               ext4_bmap_bit_clr(bmap, i);
-
-               bcnt--;
-               i++;
-       }
-       sbit = i;
-       bmap += (sbit >> 3);
-
-       while (bcnt >= 32) {
-               *(uint32_t *)bmap = 0;
-               bmap += 4;
-               bcnt -= 32;
-               sbit += 32;
-       }
-
-       while (bcnt >= 16) {
-               *(uint16_t *)bmap = 0;
-               bmap += 2;
-               bcnt -= 16;
-               sbit += 16;
-       }
-
-       while (bcnt >= 8) {
-               *bmap = 0;
-               bmap += 1;
-               bcnt -= 8;
-               sbit += 8;
-       }
-
-       for (i = 0; i < bcnt; ++i) {
-               ext4_bmap_bit_clr(bmap, i);
-       }
-}
-
-int ext4_bmap_bit_find_clr(uint8_t *bmap, uint32_t sbit, uint32_t ebit,
-                          uint32_t *bit_id)
-{
-       uint32_t i;
-       uint32_t bcnt = ebit - sbit;
-
-       i = sbit;
-
-       while (i & 7) {
-
-               if (!bcnt)
-                       return ENOSPC;
-
-               if (ext4_bmap_is_bit_clr(bmap, i)) {
-                       *bit_id = sbit;
-                       return EOK;
-               }
-
-               i++;
-               bcnt--;
-       }
-
-       sbit = i;
-       bmap += (sbit >> 3);
-
-       while (bcnt >= 32) {
-               if (*(uint32_t *)bmap != 0xFFFFFFFF)
-                       goto finish_it;
-
-               bmap += 4;
-               bcnt -= 32;
-               sbit += 32;
-       }
-
-       while (bcnt >= 16) {
-               if (*(uint16_t *)bmap != 0xFFFF)
-                       goto finish_it;
-
-               bmap += 2;
-               bcnt -= 16;
-               sbit += 16;
-       }
-
-finish_it:
-       while (bcnt >= 8) {
-               if (*bmap != 0xFF) {
-                       for (i = 0; i < 8; ++i) {
-                               if (ext4_bmap_is_bit_clr(bmap, i)) {
-                                       *bit_id = sbit + i;
-                                       return EOK;
-                               }
-                       }
-               }
-
-               bmap += 1;
-               bcnt -= 8;
-               sbit += 8;
-       }
-
-       for (i = 0; i < bcnt; ++i) {
-               if (ext4_bmap_is_bit_clr(bmap, i)) {
-                       *bit_id = sbit + i;
-                       return EOK;
-               }
-       }
-
-       return ENOSPC;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_bitmap.h b/lwext4/ext4_bitmap.h
deleted file mode 100644 (file)
index cb73b76..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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_bitmap.h
- * @brief Block/inode bitmap allocator.
- */
-
-#ifndef EXT4_BITMAP_H_
-#define EXT4_BITMAP_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/**@brief   Set bitmap bit.
- * @param   bmap bitmap
- * @param   bit bit to set*/
-static inline void ext4_bmap_bit_set(uint8_t *bmap, uint32_t bit)
-{
-       *(bmap + (bit >> 3)) |= (1 << (bit & 7));
-}
-
-/**@brief   Clear bitmap bit.
- * @param   bmap bitmap buffer
- * @param   bit bit to clear*/
-static inline void ext4_bmap_bit_clr(uint8_t *bmap, uint32_t bit)
-{
-       *(bmap + (bit >> 3)) &= ~(1 << (bit & 7));
-}
-
-/**@brief   Check if the bitmap bit is set.
- * @param   bmap bitmap buffer
- * @param   bit bit to check*/
-static inline bool ext4_bmap_is_bit_set(uint8_t *bmap, uint32_t bit)
-{
-       return (*(bmap + (bit >> 3)) & (1 << (bit & 7)));
-}
-
-/**@brief   Check if the bitmap bit is clear.
- * @param   bmap bitmap buffer
- * @param   bit bit to check*/
-static inline bool ext4_bmap_is_bit_clr(uint8_t *bmap, uint32_t bit)
-{
-       return !ext4_bmap_is_bit_set(bmap, bit);
-}
-
-/**@brief   Free range of bits in bitmap.
- * @param   bmap bitmap buffer
- * @param   sbit start bit
- * @param   bcnt bit count*/
-void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt);
-
-/**@brief   Find first clear bit in bitmap.
- * @param   sbit start bit of search
- * @param   ebit end bit of search
- * @param   bit_id output parameter (first free bit)
- * @return  standard error code*/
-int ext4_bmap_bit_find_clr(uint8_t *bmap, uint32_t sbit, uint32_t ebit,
-                          uint32_t *bit_id);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_BITMAP_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_block_group.c b/lwext4/ext4_block_group.c
deleted file mode 100644 (file)
index 7f068b9..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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_block_group.c
- * @brief Block group function set.
- */
-
-#include "ext4_config.h"
-#include "ext4_block_group.h"
-
-/**@brief CRC-16 look up table*/
-static uint16_t const crc16_tab[256] = {
-    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601,
-    0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0,
-    0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81,
-    0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941,
-    0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01,
-    0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0,
-    0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081,
-    0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
-    0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00,
-    0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0,
-    0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981,
-    0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41,
-    0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700,
-    0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0,
-    0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281,
-    0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
-    0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01,
-    0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1,
-    0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80,
-    0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541,
-    0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101,
-    0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0,
-    0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481,
-    0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
-    0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801,
-    0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1,
-    0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581,
-    0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341,
-    0x4100, 0x81C1, 0x8081, 0x4040};
-
-uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len)
-{
-       while (len--)
-
-               crc = (((crc >> 8) & 0xffU) ^
-                      crc16_tab[(crc ^ *buffer++) & 0xffU]) &
-                     0x0000ffffU;
-       return crc;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_block_group.h b/lwext4/ext4_block_group.h
deleted file mode 100644 (file)
index 715967e..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * 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_block_group.h
- * @brief Block group function set.
- */
-
-#ifndef EXT4_BLOCK_GROUP_H_
-#define EXT4_BLOCK_GROUP_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_super.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/**@brief Get address of block with data block bitmap.
- * @param bg pointer to block group
- * @param s pointer to superblock
- * @return Address of block with block bitmap
- */
-static inline uint64_t ext4_bg_get_block_bitmap(struct ext4_bgroup *bg,
-                                               struct ext4_sblock *s)
-{
-       uint64_t v = to_le32(bg->block_bitmap_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint64_t)to_le32(bg->block_bitmap_hi) << 32;
-
-       return v;
-}
-
-/**@brief Set address of block with data block bitmap.
- * @param bg pointer to block group
- * @param s pointer to superblock
- * @param blk block to set
- * @return Address of block with block bitmap
- */
-static inline void ext4_bg_set_block_bitmap(struct ext4_bgroup *bg,
-                                           struct ext4_sblock *s, uint64_t blk)
-{
-
-       bg->block_bitmap_lo = to_le32((uint32_t)blk);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->block_bitmap_hi = to_le32(blk >> 32);
-
-}
-
-/**@brief Get address of block with i-node bitmap.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @return Address of block with i-node bitmap
- */
-static inline uint64_t ext4_bg_get_inode_bitmap(struct ext4_bgroup *bg,
-                                               struct ext4_sblock *s)
-{
-
-       uint64_t v = to_le32(bg->inode_bitmap_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint64_t)to_le32(bg->inode_bitmap_hi) << 32;
-
-       return v;
-}
-
-/**@brief Set address of block with i-node bitmap.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @param blk block to set
- * @return Address of block with i-node bitmap
- */
-static inline void ext4_bg_set_inode_bitmap(struct ext4_bgroup *bg,
-                                           struct ext4_sblock *s, uint64_t blk)
-{
-       bg->inode_bitmap_lo = to_le32((uint32_t)blk);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->inode_bitmap_hi = to_le32(blk >> 32);
-
-}
-
-
-/**@brief Get address of the first block of the i-node table.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @return Address of first block of i-node table
- */
-static inline uint64_t
-ext4_bg_get_inode_table_first_block(struct ext4_bgroup *bg,
-                                   struct ext4_sblock *s)
-{
-       uint64_t v = to_le32(bg->inode_table_first_block_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint64_t)to_le32(bg->inode_table_first_block_hi) << 32;
-
-       return v;
-}
-
-/**@brief Set address of the first block of the i-node table.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @param blk block to set
- * @return Address of first block of i-node table
- */
-static inline void
-ext4_bg_set_inode_table_first_block(struct ext4_bgroup *bg,
-                                   struct ext4_sblock *s, uint64_t blk)
-{
-       bg->inode_table_first_block_lo = to_le32((uint32_t)blk);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->inode_table_first_block_hi = to_le32(blk >> 32);
-}
-
-/**@brief Get number of free blocks in block group.
- * @param bg Pointer to block group
- * @param sb Pointer to superblock
- * @return Number of free blocks in block group
- */
-static inline uint32_t ext4_bg_get_free_blocks_count(struct ext4_bgroup *bg,
-                                                    struct ext4_sblock *s)
-{
-       uint32_t v = to_le16(bg->free_blocks_count_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint32_t)to_le16(bg->free_blocks_count_hi) << 16;
-
-       return v;
-}
-
-/**@brief Set number of free blocks in block group.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @param cnt Number of free blocks in block group
- */
-static inline void ext4_bg_set_free_blocks_count(struct ext4_bgroup *bg,
-                                                struct ext4_sblock *s,
-                                                uint32_t cnt)
-{
-       bg->free_blocks_count_lo = to_le16((cnt << 16) >> 16);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->free_blocks_count_hi = to_le16(cnt >> 16);
-}
-
-/**@brief Get number of free i-nodes in block group.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @return Number of free i-nodes in block group
- */
-static inline uint32_t ext4_bg_get_free_inodes_count(struct ext4_bgroup *bg,
-                                                    struct ext4_sblock *s)
-{
-       uint32_t v = to_le16(bg->free_inodes_count_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint32_t)to_le16(bg->free_inodes_count_hi) << 16;
-
-       return v;
-}
-
-/**@brief Set number of free i-nodes in block group.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @param cnt Number of free i-nodes in block group
- */
-static inline void ext4_bg_set_free_inodes_count(struct ext4_bgroup *bg,
-                                                struct ext4_sblock *s,
-                                                uint32_t cnt)
-{
-       bg->free_inodes_count_lo = to_le16((cnt << 16) >> 16);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->free_inodes_count_hi = to_le16(cnt >> 16);
-}
-
-/**@brief Get number of used directories in block group.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @return Number of used directories in block group
- */
-static inline uint32_t ext4_bg_get_used_dirs_count(struct ext4_bgroup *bg,
-                                                  struct ext4_sblock *s)
-{
-       uint32_t v = to_le16(bg->used_dirs_count_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint32_t)to_le16(bg->used_dirs_count_hi) << 16;
-
-       return v;
-}
-
-/**@brief Set number of used directories in block group.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @param cnt Number of used directories in block group
- */
-static inline void ext4_bg_set_used_dirs_count(struct ext4_bgroup *bg,
-                                              struct ext4_sblock *s,
-                                              uint32_t cnt)
-{
-       bg->used_dirs_count_lo = to_le16((cnt << 16) >> 16);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->used_dirs_count_hi = to_le16(cnt >> 16);
-}
-
-/**@brief Get number of unused i-nodes.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @return Number of unused i-nodes
- */
-static inline uint32_t ext4_bg_get_itable_unused(struct ext4_bgroup *bg,
-                                                struct ext4_sblock *s)
-{
-
-       uint32_t v = to_le16(bg->itable_unused_lo);
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               v |= (uint32_t)to_le16(bg->itable_unused_hi) << 16;
-
-       return v;
-}
-
-/**@brief Set number of unused i-nodes.
- * @param bg Pointer to block group
- * @param s Pointer to superblock
- * @param cnt Number of unused i-nodes
- */
-static inline void ext4_bg_set_itable_unused(struct ext4_bgroup *bg,
-                                            struct ext4_sblock *s,
-                                            uint32_t cnt)
-{
-       bg->itable_unused_lo = to_le16((cnt << 16) >> 16);
-       if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->itable_unused_hi = to_le16(cnt >> 16);
-}
-
-/**@brief  Set checksum of block group.
- * @param bg Pointer to block group
- * @param crc Cheksum of block group
- */
-static inline void ext4_bg_set_checksum(struct ext4_bgroup *bg, uint16_t crc)
-{
-       bg->checksum = to_le16(crc);
-}
-
-/**@brief Check if block group has a flag.
- * @param bg Pointer to block group
- * @param flag Flag to be checked
- * @return True if flag is set to 1
- */
-static inline bool ext4_bg_has_flag(struct ext4_bgroup *bg, uint32_t f)
-{
-       return to_le16(bg->flags) & f;
-}
-
-/**@brief Set flag of block group.
- * @param bg Pointer to block group
- * @param flag Flag to be set
- */
-static inline void ext4_bg_set_flag(struct ext4_bgroup *bg, uint32_t f)
-{
-       uint16_t flags = to_le16(bg->flags);
-       flags |= f;
-       bg->flags = to_le16(flags);
-}
-
-/**@brief Clear flag of block group.
- * @param bg Pointer to block group
- * @param flag Flag to be cleared
- */
-static inline void ext4_bg_clear_flag(struct ext4_bgroup *bg, uint32_t f)
-{
-       uint16_t flags = to_le16(bg->flags);
-       flags &= ~f;
-       bg->flags = to_le16(flags);
-}
-
-/**@brief Calculate CRC16 of the block group.
- * @param crc Init value
- * @param buffer Input buffer
- * @param len Sizeof input buffer
- * @return Computed CRC16*/
-uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len);
-
-#endif /* EXT4_BLOCK_GROUP_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_blockdev.c b/lwext4/ext4_blockdev.c
deleted file mode 100644 (file)
index 23ad9ee..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * 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_blockdev.c
- * @brief Block device module.
- */
-
-#include "ext4_config.h"
-#include "ext4_blockdev.h"
-#include "ext4_errno.h"
-#include "ext4_debug.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-static void ext4_bdif_lock(struct ext4_blockdev *bdev)
-{
-       if (!bdev->bdif->lock)
-               return;
-
-       int r = bdev->bdif->lock(bdev);
-       ext4_assert(r == EOK);
-}
-
-static void ext4_bdif_unlock(struct ext4_blockdev *bdev)
-{
-       if (!bdev->bdif->unlock)
-               return;
-
-       int r = bdev->bdif->unlock(bdev);
-       ext4_assert(r == EOK);
-}
-
-static int ext4_bdif_bread(struct ext4_blockdev *bdev, void *buf,
-                          uint64_t blk_id, uint32_t blk_cnt)
-{
-       ext4_bdif_lock(bdev);
-       int r = bdev->bdif->bread(bdev, buf, blk_id, blk_cnt);
-       bdev->bdif->bread_ctr++;
-       ext4_bdif_unlock(bdev);
-       return r;
-}
-
-static int ext4_bdif_bwrite(struct ext4_blockdev *bdev, const void *buf,
-                           uint64_t blk_id, uint32_t blk_cnt)
-{
-       ext4_bdif_lock(bdev);
-       int r = bdev->bdif->bwrite(bdev, buf, blk_id, blk_cnt);
-       bdev->bdif->bwrite_ctr++;
-       ext4_bdif_unlock(bdev);
-       return r;
-}
-
-int ext4_block_init(struct ext4_blockdev *bdev)
-{
-       int rc;
-       ext4_assert(bdev);
-       ext4_assert(bdev->bdif);
-       ext4_assert(bdev->bdif->open &&
-                  bdev->bdif->close &&
-                  bdev->bdif->bread &&
-                  bdev->bdif->bwrite);
-
-       if (bdev->bdif->ph_refctr) {
-               bdev->bdif->ph_refctr++;
-               return EOK;
-       }
-
-       /*Low level block init*/
-       rc = bdev->bdif->open(bdev);
-       if (rc != EOK)
-               return rc;
-
-       bdev->bdif->ph_refctr = 1;
-       return EOK;
-}
-
-int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc)
-{
-       ext4_assert(bdev && bc);
-       bdev->bc = bc;
-       bc->bdev = bdev;
-       return EOK;
-}
-
-void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint64_t lb_bsize)
-{
-       /*Logical block size has to be multiply of physical */
-       ext4_assert(!(lb_bsize % bdev->bdif->ph_bsize));
-
-       bdev->lg_bsize = lb_bsize;
-       bdev->lg_bcnt = bdev->part_size / lb_bsize;
-}
-
-int ext4_block_fini(struct ext4_blockdev *bdev)
-{
-       ext4_assert(bdev);
-
-       if (!bdev->bdif->ph_refctr)
-               return EOK;
-
-       bdev->bdif->ph_refctr--;
-       if (bdev->bdif->ph_refctr)
-               return EOK;
-
-       /*Low level block fini*/
-       return bdev->bdif->close(bdev);
-}
-
-int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf)
-{
-       int r;
-       struct ext4_bcache *bc = bdev->bc;
-
-       if (ext4_bcache_test_flag(buf, BC_DIRTY) &&
-           ext4_bcache_test_flag(buf, BC_UPTODATE)) {
-               r = ext4_blocks_set_direct(bdev, buf->data, buf->lba, 1);
-               if (r) {
-                       if (buf->end_write) {
-                               bc->dont_shake = true;
-                               buf->end_write(bc, buf, r, buf->end_write_arg);
-                               bc->dont_shake = false;
-                       }
-
-                       return r;
-               }
-
-               ext4_bcache_remove_dirty_node(bc, buf);
-               ext4_bcache_clear_flag(buf, BC_DIRTY);
-               if (buf->end_write) {
-                       bc->dont_shake = true;
-                       buf->end_write(bc, buf, r, buf->end_write_arg);
-                       bc->dont_shake = false;
-               }
-       }
-       return EOK;
-}
-
-int ext4_block_flush_lba(struct ext4_blockdev *bdev, uint64_t lba)
-{
-       int r = EOK;
-       struct ext4_buf *buf;
-       struct ext4_block b;
-       buf = ext4_bcache_find_get(bdev->bc, &b, lba);
-       if (buf) {
-               r = ext4_block_flush_buf(bdev, buf);
-               ext4_bcache_free(bdev->bc, &b);
-       }
-       return r;
-}
-
-int ext4_block_cache_shake(struct ext4_blockdev *bdev)
-{
-       int r = EOK;
-       struct ext4_buf *buf;
-       if (bdev->bc->dont_shake)
-               return EOK;
-
-       while (!RB_EMPTY(&bdev->bc->lru_root) &&
-               ext4_bcache_is_full(bdev->bc)) {
-
-               buf = ext4_buf_lowest_lru(bdev->bc);
-               ext4_assert(buf);
-               if (ext4_bcache_test_flag(buf, BC_DIRTY)) {
-                       r = ext4_block_flush_buf(bdev, buf);
-                       if (r != EOK)
-                               break;
-
-               }
-
-               ext4_bcache_drop_buf(bdev->bc, buf);
-       }
-       return r;
-}
-
-int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b,
-                         uint64_t lba)
-{
-       bool is_new;
-       int r;
-
-       ext4_assert(bdev && b);
-
-       if (!bdev->bdif->ph_refctr)
-               return EIO;
-
-       if (!(lba < bdev->lg_bcnt))
-               return ERANGE;
-
-       b->lb_id = lba;
-
-       /*If cache is full we have to (flush and) drop it anyway :(*/
-       r = ext4_block_cache_shake(bdev);
-       if (r != EOK)
-               return r;
-
-       r = ext4_bcache_alloc(bdev->bc, b, &is_new);
-       if (r != EOK)
-               return r;
-
-       if (!b->data)
-               return ENOMEM;
-
-       return EOK;
-}
-
-int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b,
-                  uint64_t lba)
-{
-       int r = ext4_block_get_noread(bdev, b, lba);
-       if (r != EOK)
-               return r;
-
-       if (ext4_bcache_test_flag(b->buf, BC_UPTODATE)) {
-               /* Data in the cache is up-to-date.
-                * Reading from physical device is not required */
-               return EOK;
-       }
-
-       r = ext4_blocks_get_direct(bdev, b->data, lba, 1);
-       if (r != EOK) {
-               ext4_bcache_free(bdev->bc, b);
-               b->lb_id = 0;
-               return r;
-       }
-
-       /* Mark buffer up-to-date, since
-        * fresh data is read from physical device just now. */
-       ext4_bcache_set_flag(b->buf, BC_UPTODATE);
-       return EOK;
-}
-
-int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b)
-{
-       ext4_assert(bdev && b);
-       ext4_assert(b->buf);
-
-       if (!bdev->bdif->ph_refctr)
-               return EIO;
-
-       return ext4_bcache_free(bdev->bc, b);
-}
-
-int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba,
-                          uint32_t cnt)
-{
-       uint64_t pba;
-       uint32_t pb_cnt;
-
-       ext4_assert(bdev && buf);
-
-       pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize;
-       pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize;
-
-       return ext4_bdif_bread(bdev, buf, pba, pb_cnt * cnt);
-}
-
-int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf,
-                          uint64_t lba, uint32_t cnt)
-{
-       uint64_t pba;
-       uint32_t pb_cnt;
-
-       ext4_assert(bdev && buf);
-
-       pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize;
-       pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize;
-
-       return ext4_bdif_bwrite(bdev, buf, pba, pb_cnt * cnt);
-}
-
-int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off,
-                         const void *buf, uint32_t len)
-{
-       uint64_t block_idx;
-       uint32_t blen;
-       uint32_t unalg;
-       int r = EOK;
-
-       const uint8_t *p = (void *)buf;
-
-       ext4_assert(bdev && buf);
-
-       if (!bdev->bdif->ph_refctr)
-               return EIO;
-
-       if (off + len > bdev->part_size)
-               return EINVAL; /*Ups. Out of range operation*/
-
-       block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize);
-
-       /*OK lets deal with the first possible unaligned block*/
-       unalg = (off & (bdev->bdif->ph_bsize - 1));
-       if (unalg) {
-
-               uint32_t wlen = (bdev->bdif->ph_bsize - unalg) > len
-                                   ? len
-                                   : (bdev->bdif->ph_bsize - unalg);
-
-               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
-               if (r != EOK)
-                       return r;
-
-               memcpy(bdev->bdif->ph_bbuf + unalg, p, wlen);
-               r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
-               if (r != EOK)
-                       return r;
-
-               p += wlen;
-               len -= wlen;
-               block_idx++;
-       }
-
-       /*Aligned data*/
-       blen = len / bdev->bdif->ph_bsize;
-       r = ext4_bdif_bwrite(bdev, p, block_idx, blen);
-       if (r != EOK)
-               return r;
-
-       p += bdev->bdif->ph_bsize * blen;
-       len -= bdev->bdif->ph_bsize * blen;
-
-       block_idx += blen;
-
-       /*Rest of the data*/
-       if (len) {
-               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
-               if (r != EOK)
-                       return r;
-
-               memcpy(bdev->bdif->ph_bbuf, p, len);
-               r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
-               if (r != EOK)
-                       return r;
-       }
-
-       return r;
-}
-
-int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf,
-                        uint32_t len)
-{
-       uint64_t block_idx;
-       uint32_t blen;
-       uint32_t unalg;
-       int r = EOK;
-
-       uint8_t *p = (void *)buf;
-
-       ext4_assert(bdev && buf);
-
-       if (!bdev->bdif->ph_refctr)
-               return EIO;
-
-       if (off + len > bdev->part_size)
-               return EINVAL; /*Ups. Out of range operation*/
-
-       block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize);
-
-       /*OK lets deal with the first possible unaligned block*/
-       unalg = (off & (bdev->bdif->ph_bsize - 1));
-       if (unalg) {
-
-               uint32_t rlen = (bdev->bdif->ph_bsize - unalg) > len
-                                   ? len
-                                   : (bdev->bdif->ph_bsize - unalg);
-
-               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
-               if (r != EOK)
-                       return r;
-
-               memcpy(p, bdev->bdif->ph_bbuf + unalg, rlen);
-
-               p += rlen;
-               len -= rlen;
-               block_idx++;
-       }
-
-       /*Aligned data*/
-       blen = len / bdev->bdif->ph_bsize;
-
-       r = ext4_bdif_bread(bdev, p, block_idx, blen);
-       if (r != EOK)
-               return r;
-
-       p += bdev->bdif->ph_bsize * blen;
-       len -= bdev->bdif->ph_bsize * blen;
-
-       block_idx += blen;
-
-       /*Rest of the data*/
-       if (len) {
-               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
-               if (r != EOK)
-                       return r;
-
-               memcpy(p, bdev->bdif->ph_bbuf, len);
-       }
-
-       return r;
-}
-
-int ext4_block_cache_flush(struct ext4_blockdev *bdev)
-{
-       while (!SLIST_EMPTY(&bdev->bc->dirty_list)) {
-               int r;
-               struct ext4_buf *buf = SLIST_FIRST(&bdev->bc->dirty_list);
-               ext4_assert(buf);
-               r = ext4_block_flush_buf(bdev, buf);
-               if (r != EOK)
-                       return r;
-
-       }
-       return EOK;
-}
-
-int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off)
-{
-       if (on_off)
-               bdev->cache_write_back++;
-
-       if (!on_off && bdev->cache_write_back)
-               bdev->cache_write_back--;
-
-       if (bdev->cache_write_back)
-               return EOK;
-
-       /*Flush data in all delayed cache blocks*/
-       return ext4_block_cache_flush(bdev);
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_blockdev.h b/lwext4/ext4_blockdev.h
deleted file mode 100644 (file)
index f5329ec..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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_blockdev.h
- * @brief Block device module.
- */
-
-#ifndef EXT4_BLOCKDEV_H_
-#define EXT4_BLOCKDEV_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_bcache.h"
-#include "ext4_trans.h"
-#include "ext4_debug.h"
-
-#include <stdbool.h>
-#include <stdint.h>
-
-struct ext4_blockdev_iface {
-       /**@brief   Open device function
-        * @param   bdev block device.*/
-       int (*open)(struct ext4_blockdev *bdev);
-
-       /**@brief   Block read function.
-        * @param   bdev block device
-        * @param   buf output buffer
-        * @param   blk_id block id
-        * @param   blk_cnt block count*/
-       int (*bread)(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
-                    uint32_t blk_cnt);
-
-       /**@brief   Block write function.
-        * @param   buf input buffer
-        * @param   blk_id block id
-        * @param   blk_cnt block count*/
-       int (*bwrite)(struct ext4_blockdev *bdev, const void *buf,
-                     uint64_t blk_id, uint32_t blk_cnt);
-
-       /**@brief   Close device function.
-        * @param   bdev block device.*/
-       int (*close)(struct ext4_blockdev *bdev);
-
-       /**@brief   Lock block device. Required in multi partition mode
-        *          operations. Not mandatory field.
-        * @param   bdev block device.*/
-       int (*lock)(struct ext4_blockdev *bdev);
-
-       /**@brief   Unlock block device. Required in multi partition mode
-        *          operations. Not mandatory field.
-        * @param   bdev block device.*/
-       int (*unlock)(struct ext4_blockdev *bdev);
-
-       /**@brief   Block size (bytes): physical*/
-       uint32_t ph_bsize;
-
-       /**@brief   Block count: physical*/
-       uint64_t ph_bcnt;
-
-       /**@brief   Block size buffer: physical*/
-       uint8_t *ph_bbuf;
-
-       /**@brief   Reference counter to block device interface*/
-       uint32_t ph_refctr;
-
-       /**@brief   Physical read counter*/
-       uint32_t bread_ctr;
-
-       /**@brief   Physical write counter*/
-       uint32_t bwrite_ctr;
-};
-
-/**@brief   Definition of the simple block device.*/
-struct ext4_blockdev {
-       /**@brief Block device interface*/
-       struct ext4_blockdev_iface *bdif;
-
-       /**@brief Offset in bdif. For multi partition mode.*/
-       uint64_t part_offset;
-
-       /**@brief Part size in bdif. For multi partition mode.*/
-       uint64_t part_size;
-
-       /**@brief   Block cache.*/
-       struct ext4_bcache *bc;
-
-       /**@brief   Block size (bytes) logical*/
-       uint32_t lg_bsize;
-
-       /**@brief   Block count: logical*/
-       uint64_t lg_bcnt;
-
-       /**@brief   Cache write back mode reference counter*/
-       uint32_t cache_write_back;
-
-       /**@brief   The filesystem this block device belongs to. */
-       struct ext4_fs *fs;
-};
-
-/**@brief   Static initialization of the block device.*/
-#define EXT4_BLOCKDEV_STATIC_INSTANCE(__name, __bsize, __bcnt, __open, __bread,\
-                                     __bwrite, __close, __lock, __unlock)     \
-       static uint8_t __name##_ph_bbuf[(__bsize)];                            \
-       static struct ext4_blockdev_iface __name##_iface = {                   \
-               .open = __open,                                                \
-               .bread = __bread,                                              \
-               .bwrite = __bwrite,                                            \
-               .close = __close,                                              \
-               .lock = __lock,                                                \
-               .unlock = __unlock,                                            \
-               .ph_bsize = __bsize,                                           \
-               .ph_bcnt = __bcnt,                                             \
-               .ph_bbuf = __name##_ph_bbuf,                                   \
-       };                                                                     \
-       static struct ext4_blockdev __name = {                                 \
-               .bdif = &__name##_iface,                                       \
-               .part_offset = 0,                                              \
-               .part_size =  (__bcnt) * (__bsize),                            \
-       }
-
-/**@brief   Block device initialization.
- * @param   bdev block device descriptor
- * @param   bg_bsize logical block size
- * @param   bdev block device descriptor
- * @return  standard error code*/
-int ext4_block_init(struct ext4_blockdev *bdev);
-
-/**@brief   Binds a bcache to block device.
- * @param   bdev block device descriptor
- * @param   bc block cache descriptor
- * @return  standard error code*/
-int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc);
-
-/**@brief   Close block device
- * @param   bdev block device descriptor
- * @return  standard error code*/
-int ext4_block_fini(struct ext4_blockdev *bdev);
-
-/**@brief   Flush data in given buffer to disk.
- * @param   bdev block device descriptor
- * @param   buf buffer
- * @return  standard error code*/
-int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf);
-
-/**@brief   Flush data in buffer of given lba to disk,
- *          if that buffer exists in block cache.
- * @param   bdev block device descriptor
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_block_flush_lba(struct ext4_blockdev *bdev, uint64_t lba);
-
-/**@brief   Set logical block size in block device.
- * @param   bdev block device descriptor
- * @param   lb_size logical block size (in bytes)
- * @return  standard error code*/
-void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint64_t lb_bsize);
-
-/**@brief   Block get function (through cache, don't read).
- * @param   bdev block device descriptor
- * @param   b block descriptor
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b,
-                         uint64_t lba);
-
-/**@brief   Block get function (through cache).
- * @param   bdev block device descriptor
- * @param   b block descriptor
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b,
-                  uint64_t lba);
-
-/**@brief   Block set procedure (through cache).
- * @param   bdev block device descriptor
- * @param   b block descriptor
- * @return  standard error code*/
-int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b);
-
-/**@brief   Block read procedure (without cache)
- * @param   bdev block device descriptor
- * @param   buf output buffer
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba,
-                          uint32_t cnt);
-
-/**@brief   Block write procedure (without cache)
- * @param   bdev block device descriptor
- * @param   buf output buffer
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf,
-                          uint64_t lba, uint32_t cnt);
-
-/**@brief   Write to block device (by direct address).
- * @param   bdev block device descriptor
- * @param   off byte offset in block device
- * @param   buf input buffer
- * @param   len length of the write buffer
- * @return  standard error code*/
-int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off,
-                         const void *buf, uint32_t len);
-
-/**@brief   Read freom block device (by direct address).
- * @param   bdev block device descriptor
- * @param   off byte offset in block device
- * @param   buf input buffer
- * @param   len length of the write buffer
- * @return  standard error code*/
-int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf,
-                        uint32_t len);
-
-/**@brief   Flush all dirty buffers to disk
- * @param   bdev block device descriptor
- * @return  standard error code*/
-int ext4_block_cache_flush(struct ext4_blockdev *bdev);
-
-/**@brief   Enable/disable write back cache mode
- * @param   bdev block device descriptor
- * @param   on_off
- *              !0 - ENABLE
- *               0 - DISABLE (all delayed cache buffers will be flushed)
- * @return  standard error code*/
-int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_BLOCKDEV_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_config.h b/lwext4/ext4_config.h
deleted file mode 100644 (file)
index 3d856eb..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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_config.h
- * @brief Configuration file.
- */
-
-#ifndef EXT4_CONFIG_H_
-#define EXT4_CONFIG_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef CONFIG_HAVE_OWN_CFG
-#include <config.h>
-#endif
-
-/*****************************************************************************/
-
-#define F_SET_EXT2 2
-#define F_SET_EXT3 3
-#define F_SET_EXT4 4
-
-#ifndef CONFIG_EXT_FEATURE_SET_LVL
-#define CONFIG_EXT_FEATURE_SET_LVL F_SET_EXT4
-#endif
-
-/*****************************************************************************/
-
-#if CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT2
-/*Superblock features flag EXT2*/
-#define CONFIG_SUPPORTED_FCOM EXT2_SUPPORTED_FCOM
-#define CONFIG_SUPPORTED_FINCOM (EXT2_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
-#define CONFIG_SUPPORTED_FRO_COM EXT2_SUPPORTED_FRO_COM
-
-#elif CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT3
-/*Superblock features flag EXT3*/
-#define CONFIG_SUPPORTED_FCOM EXT3_SUPPORTED_FCOM
-#define CONFIG_SUPPORTED_FINCOM (EXT3_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
-#define CONFIG_SUPPORTED_FRO_COM EXT3_SUPPORTED_FRO_COM
-#elif CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT4
-/*Superblock features flag EXT4*/
-#define CONFIG_SUPPORTED_FCOM EXT4_SUPPORTED_FCOM
-#define CONFIG_SUPPORTED_FINCOM (EXT4_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
-#define CONFIG_SUPPORTED_FRO_COM EXT4_SUPPORTED_FRO_COM
-#else
-#define "Unsupported CONFIG_EXT_FEATURE_SET_LVL"
-#endif
-
-#define CONFIG_DIR_INDEX_ENABLE (CONFIG_SUPPORTED_FCOM & EXT4_FCOM_DIR_INDEX)
-#define CONFIG_EXTENT_ENABLE (CONFIG_SUPPORTED_FINCOM & EXT4_FINCOM_EXTENTS)
-#define CONFIG_META_CSUM_ENABLE (CONFIG_SUPPORTED_FRO_COM & EXT4_FRO_COM_METADATA_CSUM)
-
-/*****************************************************************************/
-
-/**@brief  Enable/disable journaling*/
-#ifndef CONFIG_JOURNALING_ENABLE
-#define CONFIG_JOURNALING_ENABLE 1
-#endif
-
-/**@brief   Enable directory indexing comb sort*/
-#ifndef CONFIG_DIR_INDEX_COMB_SORT
-#define CONFIG_DIR_INDEX_COMB_SORT 1
-#endif
-
-/**@brief   Include error codes from ext4_errno or standard library.*/
-#ifndef CONFIG_HAVE_OWN_ERRNO
-#define CONFIG_HAVE_OWN_ERRNO 0
-#endif
-
-/**@brief   Debug printf enable (stdout)*/
-#ifndef CONFIG_DEBUG_PRINTF
-#define CONFIG_DEBUG_PRINTF 1
-#endif
-
-/**@brief   Assert printf enable (stdout)*/
-#ifndef CONFIG_DEBUG_ASSERT
-#define CONFIG_DEBUG_ASSERT 1
-#endif
-
-/**@brief   Include assert codes from ext4_debug or standard library.*/
-#ifndef CONFIG_HAVE_OWN_ASSERT
-#define CONFIG_HAVE_OWN_ASSERT 1
-#endif
-
-/**@brief   Statistics of block device*/
-#ifndef CONFIG_BLOCK_DEV_ENABLE_STATS
-#define CONFIG_BLOCK_DEV_ENABLE_STATS 1
-#endif
-
-/**@brief   Cache size of block device.*/
-#ifndef CONFIG_BLOCK_DEV_CACHE_SIZE
-#define CONFIG_BLOCK_DEV_CACHE_SIZE 8
-#endif
-
-/**@brief   Maximum block device count*/
-#ifndef CONFIG_EXT4_BLOCKDEVS_COUNT
-#define CONFIG_EXT4_BLOCKDEVS_COUNT 2
-#endif
-
-/**@brief   Maximum mountpoint count*/
-#ifndef CONFIG_EXT4_MOUNTPOINTS_COUNT
-#define CONFIG_EXT4_MOUNTPOINTS_COUNT 2
-#endif
-
-/**@brief   Include open flags from ext4_errno or standard library.*/
-#ifndef CONFIG_HAVE_OWN_OFLAGS
-#define CONFIG_HAVE_OWN_OFLAGS 1
-#endif
-
-/**@brief Maximum single truncate size. Transactions must be limited to reduce
- *        number of allocetions for single transaction*/
-#ifndef CONFIG_MAX_TRUNCATE_SIZE
-#define CONFIG_MAX_TRUNCATE_SIZE (16ul * 1024ul * 1024ul)
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_CONFIG_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_crc32.c b/lwext4/ext4_crc32.c
deleted file mode 100644 (file)
index a6ee6b0..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * All rights reserved.
- *
- * Based on FreeBSD.
- *
- * 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_crc32c.c
- * @brief Crc32c routine. Taken from FreeBSD kernel.
- */
-
-#include "ext4_config.h"
-#include "ext4_crc32.h"
-
-static const uint32_t crc32_tab[] = {
-       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
-       0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
-       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
-       0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
-       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
-       0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
-       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
-       0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
-       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
-       0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
-       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
-       0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
-       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
-       0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
-       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
-       0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
-       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
-       0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
-       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
-       0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
-       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
-       0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
-       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
-       0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
-       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
-       0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
-       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
-       0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
-       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
-       0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
-       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
-       0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
-       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
-       0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
-       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
-       0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
-       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
-       0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
-       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
-       0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
-       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
-       0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/* */
-/* CRC LOOKUP TABLE */
-/* ================ */
-/* The following CRC lookup table was generated automagically */
-/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
-/* Program V1.0 using the following model parameters: */
-/* */
-/* Width : 4 bytes. */
-/* Poly : 0x1EDC6F41L */
-/* Reverse : TRUE. */
-/* */
-/* For more information on the Rocksoft^tm Model CRC Algorithm, */
-/* see the document titled "A Painless Guide to CRC Error */
-/* Detection Algorithms" by Ross Williams */
-/* (ross@guest.adelaide.edu.au.). This document is likely to be */
-/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
-/* */
-static const uint32_t crc32c_tab[256] = {
-    0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL,
-    0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL,
-    0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L,
-    0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
-    0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L,
-    0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL,
-    0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L,
-    0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
-    0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL,
-    0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L,
-    0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L,
-    0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
-    0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL,
-    0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L,
-    0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L,
-    0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
-    0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL,
-    0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL,
-    0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L,
-    0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
-    0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL,
-    0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L,
-    0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL,
-    0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
-    0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL,
-    0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL,
-    0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L,
-    0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
-    0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L,
-    0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL,
-    0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL,
-    0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
-    0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L,
-    0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL,
-    0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L,
-    0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
-    0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L,
-    0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL,
-    0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L,
-    0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
-    0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L,
-    0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L,
-    0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L,
-    0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
-    0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL,
-    0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L,
-    0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L,
-    0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
-    0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL,
-    0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L,
-    0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L,
-    0xAD7D5351L};
-
-static inline uint32_t crc32(uint32_t crc, const void *buf, uint32_t size,
-                            const uint32_t *tab)
-{
-       const uint8_t *p = (const uint8_t *)buf;
-
-       while (size--)
-               crc = tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
-
-       return (crc);
-}
-
-uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size)
-{
-       return crc32(crc, buf, size, crc32_tab);
-}
-
-uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size)
-{
-       return crc32(crc, buf, size, crc32c_tab);
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_crc32.h b/lwext4/ext4_crc32.h
deleted file mode 100644 (file)
index 3dad1d1..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * All rights reserved.
- *
- * Based on FreeBSD.
- *
- * 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_crc32.h
- * @brief Crc32c routine. Taken from FreeBSD kernel.
- */
-
-#ifndef LWEXT4_EXT4_CRC32C_H_
-#define LWEXT4_EXT4_CRC32C_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-
-#include <stdint.h>
-
-/**@brief      CRC32 algorithm.
- * @param      crc input feed
- * @param      buf input buffer
- * @param      size input buffer length (bytes)
- * @return     updated crc32 value*/
-uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size);
-
-/**@brief      CRC32C algorithm.
- * @param      crc input feed
- * @param      buf input buffer
- * @param      length input buffer length (bytes)
- * @return     updated crc32c value*/
-uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWEXT4_EXT4_CRC32C_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_debug.c b/lwext4/ext4_debug.c
deleted file mode 100644 (file)
index b38593b..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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_debug.c
- * @brief Debug printf and assert macros.
- */
-
-#include "ext4_config.h"
-#include "ext4_debug.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-static uint32_t debug_mask;
-
-void ext4_dmask_set(uint32_t m)
-{
-       debug_mask |= m;
-}
-
-void ext4_dmask_clr(uint32_t m)
-{
-       debug_mask &= ~m;
-}
-
-uint32_t ext4_dmask_get(void)
-{
-       return debug_mask;
-}
-
-
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_debug.h b/lwext4/ext4_debug.h
deleted file mode 100644 (file)
index 2afc39c..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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_debug.c
- * @brief Debug printf and assert macros.
- */
-
-#ifndef EXT4_DEBUG_H_
-#define EXT4_DEBUG_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_errno.h"
-
-#if !CONFIG_HAVE_OWN_ASSERT
-#include <assert.h>
-#endif
-
-#include <stdint.h>
-#include <stdio.h>
-#include <inttypes.h>
-
-#ifndef PRIu64
-#define PRIu64 "llu"
-#endif
-
-#ifndef PRId64
-#define PRId64 "lld"
-#endif
-
-
-#define DEBUG_BALLOC (1ul << 0)
-#define DEBUG_BCACHE (1ul << 1)
-#define DEBUG_BITMAP (1ul << 2)
-#define DEBUG_BLOCK_GROUP (1ul << 3)
-#define DEBUG_BLOCKDEV (1ul << 4)
-#define DEBUG_DIR_IDX (1ul << 5)
-#define DEBUG_DIR (1ul << 6)
-#define DEBUG_EXTENT (1ul << 7)
-#define DEBUG_FS (1ul << 8)
-#define DEBUG_HASH (1ul << 9)
-#define DEBUG_IALLOC (1ul << 10)
-#define DEBUG_INODE (1ul << 11)
-#define DEBUG_SUPER (1ul << 12)
-#define DEBUG_XATTR (1ul << 13)
-#define DEBUG_MKFS (1ul << 14)
-#define DEBUG_EXT4 (1ul << 15)
-#define DEBUG_JBD (1ul << 16)
-#define DEBUG_MBR (1ul << 17)
-
-#define DEBUG_NOPREFIX (1ul << 31)
-#define DEBUG_ALL (0xFFFFFFFF)
-
-static inline const char *ext4_dmask_id2str(uint32_t m)
-{
-       switch(m) {
-       case DEBUG_BALLOC:
-               return "ext4_balloc: ";
-       case DEBUG_BCACHE:
-               return "ext4_bcache: ";
-       case DEBUG_BITMAP:
-               return "ext4_bitmap: ";
-       case DEBUG_BLOCK_GROUP:
-               return "ext4_block_group: ";
-       case DEBUG_BLOCKDEV:
-               return "ext4_blockdev: ";
-       case DEBUG_DIR_IDX:
-               return "ext4_dir_idx: ";
-       case DEBUG_DIR:
-               return "ext4_dir: ";
-       case DEBUG_EXTENT:
-               return "ext4_extent: ";
-       case DEBUG_FS:
-               return "ext4_fs: ";
-       case DEBUG_HASH:
-               return "ext4_hash: ";
-       case DEBUG_IALLOC:
-               return "ext4_ialloc: ";
-       case DEBUG_INODE:
-               return "ext4_inode: ";
-       case DEBUG_SUPER:
-               return "ext4_super: ";
-       case DEBUG_XATTR:
-               return "ext4_xattr: ";
-       case DEBUG_MKFS:
-               return "ext4_mkfs: ";
-       case DEBUG_JBD:
-               return "ext4_jbd: ";
-       case DEBUG_MBR:
-               return "ext4_mbr: ";
-       case DEBUG_EXT4:
-               return "ext4: ";
-       }
-       return "";
-}
-#define DBG_NONE  ""
-#define DBG_INFO  "[info]  "
-#define DBG_WARN  "[warn]  "
-#define DBG_ERROR "[error] "
-
-/**@brief   Global mask debug set.
- * @brief   m new debug mask.*/
-void ext4_dmask_set(uint32_t m);
-
-/**@brief   Global mask debug clear.
- * @brief   m new debug mask.*/
-void ext4_dmask_clr(uint32_t m);
-
-/**@brief   Global debug mask get.
- * @return  debug mask*/
-uint32_t ext4_dmask_get(void);
-
-#if CONFIG_DEBUG_PRINTF
-/**@brief   Debug printf.*/
-#define ext4_dbg(m, ...)                                                       \
-       do {                                                                   \
-               if ((m) & ext4_dmask_get()) {                                  \
-                       if (!((m) & DEBUG_NOPREFIX)) {                         \
-                               printf("%s", ext4_dmask_id2str(m));            \
-                               printf("l: %d   ", __LINE__);                  \
-                       }                                                      \
-                       printf(__VA_ARGS__);                                   \
-                       fflush(stdout);                                        \
-               }                                                              \
-       } while (0)
-#else
-#define ext4_dbg(m, ...) do { } while (0)
-#endif
-
-#if CONFIG_DEBUG_ASSERT
-/**@brief   Debug assertion.*/
-#if CONFIG_HAVE_OWN_ASSERT
-#define ext4_assert(_v)                                                        \
-       do {                                                                   \
-               if (!(_v)) {                                                   \
-                       printf("assertion failed:\nfile: %s\nline: %d\n",      \
-                              __FILE__, __LINE__);                            \
-                              while (1)                                       \
-                                      ;                                       \
-               }                                                              \
-       } while (0)
-#else
-#define ext4_assert(_v) assert(_v)
-#endif
-#else
-#define ext4_assert(_v) ((void)(_v))
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_DEBUG_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_dir.c b/lwext4/ext4_dir.c
deleted file mode 100644 (file)
index f26dc6d..0000000
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * 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_crc32.h"
-#include "ext4_inode.h"
-#include "ext4_fs.h"
-
-#include <string.h>
-
-/****************************************************************************/
-
-/* Walk through a dirent block to find a checksum "dirent" at the tail */
-static struct ext4_dir_entry_tail *
-ext4_dir_get_tail(struct ext4_inode_ref *inode_ref,
-               struct ext4_dir_en *de)
-{
-       struct ext4_dir_entry_tail *t;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       t = EXT4_DIRENT_TAIL(de, ext4_sb_get_block_size(sb));
-
-       if (t->reserved_zero1 || t->reserved_zero2)
-               return NULL;
-       if (to_le16(t->rec_len) != sizeof(struct ext4_dir_entry_tail))
-               return NULL;
-       if (t->reserved_ft != EXT4_DIRENTRY_DIR_CSUM)
-               return NULL;
-
-       return t;
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_dir_csum(struct ext4_inode_ref *inode_ref,
-                             struct ext4_dir_en *dirent, int size)
-{
-       uint32_t csum;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint32_t ino_index = to_le32(inode_ref->index);
-       uint32_t ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode));
-
-       /* First calculate crc32 checksum against fs uuid */
-       csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
-       /* Then calculate crc32 checksum against inode number
-        * and inode generation */
-       csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index));
-       csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen));
-       /* Finally calculate crc32 checksum against directory entries */
-       csum = ext4_crc32c(csum, dirent, size);
-       return csum;
-}
-#else
-#define ext4_dir_csum(...) 0
-#endif
-
-bool ext4_dir_csum_verify(struct ext4_inode_ref *inode_ref,
-                             struct ext4_dir_en *dirent)
-{
-#ifdef CONFIG_META_CSUM_ENABLE
-       struct ext4_dir_entry_tail *t;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       /* Compute the checksum only if the filesystem supports it */
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               t = ext4_dir_get_tail(inode_ref, dirent);
-               if (!t) {
-                       /* There is no space to hold the checksum */
-                       return false;
-               }
-
-               ptrdiff_t __unused diff = (char *)t - (char *)dirent;
-               uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff);
-               if (t->checksum != to_le32(csum))
-                       return false;
-
-       }
-#endif
-       return true;
-}
-
-void ext4_dir_init_entry_tail(struct ext4_dir_entry_tail *t)
-{
-       memset(t, 0, sizeof(struct ext4_dir_entry_tail));
-       t->rec_len = to_le16(sizeof(struct ext4_dir_entry_tail));
-       t->reserved_ft = EXT4_DIRENTRY_DIR_CSUM;
-}
-
-void ext4_dir_set_csum(struct ext4_inode_ref *inode_ref,
-                          struct ext4_dir_en *dirent)
-{
-       struct ext4_dir_entry_tail *t;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       /* Compute the checksum only if the filesystem supports it */
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               t = ext4_dir_get_tail(inode_ref, dirent);
-               if (!t) {
-                       /* There is no space to hold the checksum */
-                       return;
-               }
-
-               ptrdiff_t __unused diff = (char *)t - (char *)dirent;
-               uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff);
-               t->checksum = to_le32(csum);
-       }
-}
-
-/**@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_dir_iter *it,
-                                uint32_t block_size)
-{
-       uint32_t off_in_block = it->curr_off % block_size;
-       struct ext4_sblock *sb = &it->inode_ref->fs->sb;
-
-       it->curr = NULL;
-
-       /* Ensure proper alignment */
-       if ((off_in_block % 4) != 0)
-               return EIO;
-
-       /* Ensure that the core of the entry does not overflow the block */
-       if (off_in_block > block_size - 8)
-               return EIO;
-
-       struct ext4_dir_en *en;
-       en = (void *)(it->curr_blk.data + off_in_block);
-
-       /* Ensure that the whole entry does not overflow the block */
-       uint16_t length = ext4_dir_en_get_entry_len(en);
-       if (off_in_block + length > block_size)
-               return EIO;
-
-       /* Ensure the name length is not too large */
-       if (ext4_dir_en_get_name_len(sb, en) > length - 8)
-               return EIO;
-
-       /* Everything OK - "publish" the entry */
-       it->curr = en;
-       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_dir_iter *it, uint64_t pos)
-{
-       struct ext4_sblock *sb = &it->inode_ref->fs->sb;
-       struct ext4_inode *inode = it->inode_ref->inode;
-       struct ext4_blockdev *bdev = it->inode_ref->fs->bdev;
-       uint64_t size = ext4_inode_get_size(sb, inode);
-       int r;
-
-       /* The iterator is not valid until we seek to the desired position */
-       it->curr = NULL;
-
-       /* Are we at the end? */
-       if (pos >= size) {
-               if (it->curr_blk.lb_id) {
-
-                       r = ext4_block_set(bdev, &it->curr_blk);
-                       it->curr_blk.lb_id = 0;
-                       if (r != EOK)
-                               return r;
-               }
-
-               it->curr_off = pos;
-               return EOK;
-       }
-
-       /* Compute next block address */
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint64_t current_blk_idx = it->curr_off / block_size;
-       uint32_t next_blk_idx = (uint32_t)(pos / block_size);
-
-       /*
-        * If we don't have a block or are moving across block boundary,
-        * we need to get another block
-        */
-       if ((it->curr_blk.lb_id == 0) ||
-           (current_blk_idx != next_blk_idx)) {
-               if (it->curr_blk.lb_id) {
-                       r = ext4_block_set(bdev, &it->curr_blk);
-                       it->curr_blk.lb_id = 0;
-
-                       if (r != EOK)
-                               return r;
-               }
-
-               ext4_fsblk_t next_blk;
-               r = ext4_fs_get_inode_dblk_idx(it->inode_ref, next_blk_idx,
-                                              &next_blk, false);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_trans_block_get(bdev, &it->curr_blk, next_blk);
-               if (r != EOK) {
-                       it->curr_blk.lb_id = 0;
-                       return r;
-               }
-       }
-
-       it->curr_off = pos;
-       return ext4_dir_iterator_set(it, block_size);
-}
-
-int ext4_dir_iterator_init(struct ext4_dir_iter *it,
-                          struct ext4_inode_ref *inode_ref, uint64_t pos)
-{
-       it->inode_ref = inode_ref;
-       it->curr = 0;
-       it->curr_off = 0;
-       it->curr_blk.lb_id = 0;
-
-       return ext4_dir_iterator_seek(it, pos);
-}
-
-int ext4_dir_iterator_next(struct ext4_dir_iter *it)
-{
-       int r = EOK;
-       uint16_t skip;
-
-       while (r == EOK) {
-               skip = ext4_dir_en_get_entry_len(it->curr);
-               r = ext4_dir_iterator_seek(it, it->curr_off + skip);
-
-               if (!it->curr)
-                       break;
-               /*Skip NULL referenced entry*/
-               if (ext4_dir_en_get_inode(it->curr) != 0)
-                       break;
-       }
-
-       return r;
-}
-
-int ext4_dir_iterator_fini(struct ext4_dir_iter *it)
-{
-       it->curr = 0;
-
-       if (it->curr_blk.lb_id)
-               return ext4_block_set(it->inode_ref->fs->bdev, &it->curr_blk);
-
-       return EOK;
-}
-
-void ext4_dir_write_entry(struct ext4_sblock *sb, struct ext4_dir_en *en,
-                         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 type of entry */
-       switch (ext4_inode_type(sb, child->inode)) {
-       case EXT4_INODE_MODE_DIRECTORY:
-               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_DIR);
-               break;
-       case EXT4_INODE_MODE_FILE:
-               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_REG_FILE);
-               break;
-       case EXT4_INODE_MODE_SOFTLINK:
-               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SYMLINK);
-               break;
-       default:
-               /* FIXME: right now we only support 3 inode type. */
-               ext4_assert(0);
-       }
-
-       /* Set basic attributes */
-       ext4_dir_en_set_inode(en, child->index);
-       ext4_dir_en_set_entry_len(en, entry_len);
-       ext4_dir_en_set_name_len(sb, en, (uint16_t)name_len);
-
-       /* Write name */
-       memcpy(en->name, name, name_len);
-}
-
-int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
-                      uint32_t name_len, struct ext4_inode_ref *child)
-{
-       int r;
-       struct ext4_fs *fs = parent->fs;
-       struct ext4_sblock *sb = &parent->fs->sb;
-
-#if CONFIG_DIR_INDEX_ENABLE
-       /* Index adding (if allowed) */
-       if ((ext4_sb_feature_com(sb, EXT4_FCOM_DIR_INDEX)) &&
-           (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
-               r = ext4_dir_dx_add_entry(parent, child, name);
-
-               /* Check if index is not corrupted */
-               if (r != EXT4_ERR_BAD_DX_DIR) {
-                       if (r != EOK)
-                               return r;
-
-                       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;
-       ext4_fsblk_t fblock = 0;
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint64_t inode_size = ext4_inode_get_size(sb, parent->inode);
-       uint32_t total_blocks = (uint32_t)(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) {
-               r = ext4_fs_get_inode_dblk_idx(parent, iblock, &fblock, false);
-               if (r != EOK)
-                       return r;
-
-               struct ext4_block block;
-               r = ext4_trans_block_get(fs->bdev, &block, fblock);
-               if (r != EOK)
-                       return r;
-
-               if (!ext4_dir_csum_verify(parent, (void *)block.data)) {
-                       ext4_dbg(DEBUG_DIR,
-                                DBG_WARN "Leaf block checksum failed."
-                                "Inode: %" PRIu32", "
-                                "Block: %" PRIu32"\n",
-                                parent->index,
-                                iblock);
-               }
-
-               /* If adding is successful, function can finish */
-               r = ext4_dir_try_insert_entry(sb, parent, &block, child,
-                                               name, name_len);
-               if (r == EOK)
-                       success = true;
-
-               r = ext4_block_set(fs->bdev, &block);
-               if (r != EOK)
-                       return r;
-
-               if (success)
-                       return EOK;
-       }
-
-       /* No free block found - needed to allocate next data block */
-
-       iblock = 0;
-       fblock = 0;
-       r = ext4_fs_append_inode_dblk(parent, &fblock, &iblock);
-       if (r != EOK)
-               return r;
-
-       /* Load new block */
-       struct ext4_block b;
-
-       r = ext4_trans_block_get_noread(fs->bdev, &b, fblock);
-       if (r != EOK)
-               return r;
-
-       /* Fill block with zeroes */
-       memset(b.data, 0, block_size);
-       struct ext4_dir_en *blk_en = (void *)b.data;
-
-       /* Save new block */
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint16_t el = block_size - sizeof(struct ext4_dir_entry_tail);
-               ext4_dir_write_entry(sb, blk_en, el, child, name, name_len);
-               ext4_dir_init_entry_tail(EXT4_DIRENT_TAIL(b.data, block_size));
-       } else {
-               ext4_dir_write_entry(sb, blk_en, block_size, child, name,
-                               name_len);
-       }
-
-       ext4_dir_set_csum(parent, (void *)b.data);
-       ext4_trans_set_block_dirty(b.buf);
-       r = ext4_block_set(fs->bdev, &b);
-
-       return r;
-}
-
-int ext4_dir_find_entry(struct ext4_dir_search_result *result,
-                       struct ext4_inode_ref *parent, const char *name,
-                       uint32_t name_len)
-{
-       int r;
-       struct ext4_sblock *sb = &parent->fs->sb;
-
-       /* Entry clear */
-       result->block.lb_id = 0;
-       result->dentry = NULL;
-
-#if CONFIG_DIR_INDEX_ENABLE
-       /* Index search */
-       if ((ext4_sb_feature_com(sb, EXT4_FCOM_DIR_INDEX)) &&
-           (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
-               r = ext4_dir_dx_find_entry(result, parent, name_len, name);
-               /* Check if index is not corrupted */
-               if (r != EXT4_ERR_BAD_DX_DIR) {
-                       if (r != EOK)
-                               return r;
-
-                       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;
-       ext4_fsblk_t fblock;
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint64_t inode_size = ext4_inode_get_size(sb, parent->inode);
-       uint32_t total_blocks = (uint32_t)(inode_size / block_size);
-
-       /* Walk through all data blocks */
-       for (iblock = 0; iblock < total_blocks; ++iblock) {
-               /* Load block address */
-               r = ext4_fs_get_inode_dblk_idx(parent, iblock, &fblock, false);
-               if (r != EOK)
-                       return r;
-
-               /* Load data block */
-               struct ext4_block b;
-               r = ext4_trans_block_get(parent->fs->bdev, &b, fblock);
-               if (r != EOK)
-                       return r;
-
-               if (!ext4_dir_csum_verify(parent, (void *)b.data)) {
-                       ext4_dbg(DEBUG_DIR,
-                                DBG_WARN "Leaf block checksum failed."
-                                "Inode: %" PRIu32", "
-                                "Block: %" PRIu32"\n",
-                                parent->index,
-                                iblock);
-               }
-
-               /* Try to find entry in block */
-               struct ext4_dir_en *res_entry;
-               r = ext4_dir_find_in_block(&b, sb, name_len, name, &res_entry);
-               if (r == EOK) {
-                       result->block = b;
-                       result->dentry = res_entry;
-                       return EOK;
-               }
-
-               /* Entry not found - put block and continue to the next block */
-
-               r = ext4_block_set(parent->fs->bdev, &b);
-               if (r != EOK)
-                       return r;
-       }
-
-       return ENOENT;
-}
-
-int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name,
-                         uint32_t name_len)
-{
-       struct ext4_sblock *sb = &parent->fs->sb;
-       /* Check if removing from directory */
-       if (!ext4_inode_is_type(sb, parent->inode, EXT4_INODE_MODE_DIRECTORY))
-               return ENOTDIR;
-
-       /* Try to find entry */
-       struct ext4_dir_search_result result;
-       int rc = ext4_dir_find_entry(&result, parent, name, name_len);
-       if (rc != EOK)
-               return rc;
-
-       /* Invalidate entry */
-       ext4_dir_en_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_dir_en *tmp_de =(void *)result.block.data;
-               uint16_t de_len = ext4_dir_en_get_entry_len(tmp_de);
-
-               /* Find direct predecessor of removed entry */
-               while ((offset + de_len) < pos) {
-                       offset += ext4_dir_en_get_entry_len(tmp_de);
-                       tmp_de = (void *)(result.block.data + offset);
-                       de_len = ext4_dir_en_get_entry_len(tmp_de);
-               }
-
-               ext4_assert(de_len + offset == pos);
-
-               /* Add to removed entry length to predecessor's length */
-               uint16_t del_len;
-               del_len = ext4_dir_en_get_entry_len(result.dentry);
-               ext4_dir_en_set_entry_len(tmp_de, de_len + del_len);
-       }
-
-       ext4_dir_set_csum(parent,
-                       (struct ext4_dir_en *)result.block.data);
-       ext4_trans_set_block_dirty(result.block.buf);
-
-       return ext4_dir_destroy_result(parent, &result);
-}
-
-int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
-                             struct ext4_inode_ref *inode_ref,
-                             struct ext4_block *dst_blk,
-                             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_dir_entry) + name_len;
-
-       if ((required_len % 4) != 0)
-               required_len += 4 - (required_len % 4);
-
-       /* Initialize pointers, stop means to upper bound */
-       struct ext4_dir_en *start = (void *)dst_blk->data;
-       struct ext4_dir_en *stop = (void *)(dst_blk->data + block_size);
-
-       /*
-        * Walk through the block and check for invalid entries
-        * or entries with free space for new entry
-        */
-       while (start < stop) {
-               uint32_t inode = ext4_dir_en_get_inode(start);
-               uint16_t rec_len = ext4_dir_en_get_entry_len(start);
-               uint8_t itype = ext4_dir_en_get_inode_type(sb, start);
-
-               /* If invalid and large enough entry, use it */
-               if ((inode == 0) && (itype != EXT4_DIRENTRY_DIR_CSUM) &&
-                   (rec_len >= required_len)) {
-                       ext4_dir_write_entry(sb, start, rec_len, child, name,
-                                            name_len);
-                       ext4_dir_set_csum(inode_ref, (void *)dst_blk->data);
-                       ext4_trans_set_block_dirty(dst_blk->buf);
-
-                       return EOK;
-               }
-
-               /* Valid entry, try to split it */
-               if (inode != 0) {
-                       uint16_t used_len;
-                       used_len = ext4_dir_en_get_name_len(sb, start);
-
-                       uint16_t sz;
-                       sz = sizeof(struct ext4_fake_dir_entry) + used_len;
-
-                       if ((used_len % 4) != 0)
-                               sz += 4 - (used_len % 4);
-
-                       uint16_t free_space = rec_len - sz;
-
-                       /* There is free space for new entry */
-                       if (free_space >= required_len) {
-                               /* Cut tail of current entry */
-                               struct ext4_dir_en * new_entry;
-                               new_entry = (void *)((uint8_t *)start + sz);
-                               ext4_dir_en_set_entry_len(start, sz);
-                               ext4_dir_write_entry(sb, new_entry, free_space,
-                                                    child, name, name_len);
-
-                               ext4_dir_set_csum(inode_ref,
-                                                 (void *)dst_blk->data);
-                               ext4_trans_set_block_dirty(dst_blk->buf);
-                               return EOK;
-                       }
-               }
-
-               /* Jump to the next entry */
-               start = (void *)((uint8_t *)start + 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_dir_en **res_entry)
-{
-       /* Start from the first entry in block */
-       struct ext4_dir_en *de = (struct ext4_dir_en *)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 *)de < addr_limit) {
-               /* Termination condition */
-               if ((uint8_t *)de + name_len > addr_limit)
-                       break;
-
-               /* Valid entry - check it */
-               if (ext4_dir_en_get_inode(de) != 0) {
-                       /* For more efficient compare only lengths firstly*/
-                       uint16_t el = ext4_dir_en_get_name_len(sb, de);
-                       if (el == name_len) {
-                               /* Compare names */
-                               if (memcmp(name, de->name, name_len) == 0) {
-                                       *res_entry = de;
-                                       return EOK;
-                               }
-                       }
-               }
-
-               uint16_t de_len = ext4_dir_en_get_entry_len(de);
-
-               /* Corrupted entry */
-               if (de_len == 0)
-                       return EINVAL;
-
-               /* Jump to next entry */
-               de = (struct ext4_dir_en *)((uint8_t *)de + de_len);
-       }
-
-       /* Entry not found */
-       return ENOENT;
-}
-
-int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
-                           struct ext4_dir_search_result *result)
-{
-       if (result->block.lb_id)
-               return ext4_block_set(parent->fs->bdev, &result->block);
-
-       return EOK;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_dir.h b/lwext4/ext4_dir.h
deleted file mode 100644 (file)
index 37547ea..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#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_en_get_inode(struct ext4_dir_en *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_en_set_inode(struct ext4_dir_en *de, uint32_t inode)
-{
-       de->inode = to_le32(inode);
-}
-
-/**@brief Set i-node number to directory entry. (For HTree root)
- * @param de Directory entry
- * @param inode I-node number
- */
-static inline void
-ext4_dx_dot_en_set_inode(struct ext4_dir_idx_dot_en *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_en_get_entry_len(struct ext4_dir_en *de)
-{
-       return to_le16(de->entry_len);
-}
-
-/**@brief Set directory entry length.
- * @param de     Directory entry
- * @param length Entry length
- */
-static inline void ext4_dir_en_set_entry_len(struct ext4_dir_en *de, uint16_t l)
-{
-       de->entry_len = to_le16(l);
-}
-
-/**@brief Get directory entry name length.
- * @param sb Superblock
- * @param de Directory entry
- * @return Entry name length
- */
-static inline uint16_t ext4_dir_en_get_name_len(struct ext4_sblock *sb,
-                                               struct ext4_dir_en *de)
-{
-       uint16_t v = de->name_len;
-
-       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_en_set_name_len(struct ext4_sblock *sb,
-                                           struct ext4_dir_en *de,
-                                           uint16_t len)
-{
-       de->name_len = (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_en_get_inode_type(struct ext4_sblock *sb,
-                                                struct ext4_dir_en *de)
-{
-       if ((ext4_get32(sb, rev_level) > 0) ||
-           (ext4_get32(sb, minor_rev_level) >= 5))
-               return de->in.inode_type;
-
-       return EXT4_DE_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_en_set_inode_type(struct ext4_sblock *sb,
-                                             struct ext4_dir_en *de, uint8_t t)
-{
-       if ((ext4_get32(sb, rev_level) > 0) ||
-           (ext4_get32(sb, minor_rev_level) >= 5))
-               de->in.inode_type = t;
-}
-
-/**@brief Verify checksum of a linear directory leaf block
- * @param inode_ref Directory i-node
- * @param dirent    Linear directory leaf block
- * @return true means the block passed checksum verification
- */
-bool ext4_dir_csum_verify(struct ext4_inode_ref *inode_ref,
-                         struct ext4_dir_en *dirent);
-
-/**@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_dir_iter *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_dir_iter *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_dir_iter *it);
-
-/**@brief Write directory entry to concrete data block.
- * @param sb        Superblock
- * @param en     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_dir_en *en,
-                         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_dir_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 inode_ref    Directory i-node
- * @param dst_blk      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_inode_ref *inode_ref,
-                             struct ext4_block *dst_blk,
-                             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_dir_en **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_dir_search_result *result);
-
-void ext4_dir_set_csum(struct ext4_inode_ref *inode_ref,
-                      struct ext4_dir_en *dirent);
-
-
-void ext4_dir_init_entry_tail(struct ext4_dir_entry_tail *t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_DIR_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_dir_idx.c b/lwext4/ext4_dir_idx.c
deleted file mode 100644 (file)
index 81da0de..0000000
+++ /dev/null
@@ -1,1439 +0,0 @@
-/*
- * 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_inode.h"
-#include "ext4_crc32.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_rinfo_get_hash_version(struct ext4_dir_idx_rinfo *ri)
-{
-       return ri->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_rinfo_set_hash_version(struct ext4_dir_idx_rinfo *ri, uint8_t v)
-{
-       ri->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_rinfo_get_info_length(struct ext4_dir_idx_rinfo *ri)
-{
-       return ri->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_dir_idx_rinfo *ri,
-                                     uint8_t len)
-{
-       ri->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_rinfo_get_indirect_levels(struct ext4_dir_idx_rinfo *ri)
-{
-       return ri->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_rinfo_set_indirect_levels(struct ext4_dir_idx_rinfo *ri, uint8_t l)
-{
-       ri->indirect_levels = l;
-}
-
-/**@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_climit_get_limit(struct ext4_dir_idx_climit *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_climit_set_limit(struct ext4_dir_idx_climit *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_climit_get_count(struct ext4_dir_idx_climit *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_climit_set_count(struct ext4_dir_idx_climit *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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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);
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_dir_dx_checksum(struct ext4_inode_ref *inode_ref, void *de,
-                                    int count_offset, int count,
-                                    struct ext4_dir_idx_tail *t)
-{
-       uint32_t orig_cum, csum = 0;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       int sz;
-
-       /* Compute the checksum only if the filesystem supports it */
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint32_t ino_index = to_le32(inode_ref->index);
-               uint32_t ino_gen;
-               ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode));
-
-               sz = count_offset + (count * sizeof(struct ext4_dir_idx_tail));
-               orig_cum = t->checksum;
-               t->checksum = 0;
-               /* First calculate crc32 checksum against fs uuid */
-               csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
-               /* Then calculate crc32 checksum against inode number
-                * and inode generation */
-               csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index));
-               csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen));
-               /* After that calculate crc32 checksum against all the dx_entry */
-               csum = ext4_crc32c(csum, de, sz);
-               /* Finally calculate crc32 checksum for dx_tail */
-               csum = ext4_crc32c(csum, t, sizeof(struct ext4_dir_idx_tail));
-               t->checksum = orig_cum;
-       }
-       return csum;
-}
-
-static struct ext4_dir_idx_climit *
-ext4_dir_dx_get_climit(struct ext4_inode_ref *inode_ref,
-                          struct ext4_dir_en *dirent, int *offset)
-{
-       struct ext4_dir_en *dp;
-       struct ext4_dir_idx_root *root;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint16_t entry_len = ext4_dir_en_get_entry_len(dirent);
-       int count_offset;
-
-
-       if (entry_len == 12) {
-               root = (struct ext4_dir_idx_root *)dirent;
-               dp = (struct ext4_dir_en *)&root->dots[1];
-               if (ext4_dir_en_get_entry_len(dp) != (block_size - 12))
-                       return NULL;
-               if (root->info.reserved_zero)
-                       return NULL;
-               if (root->info.info_length != sizeof(struct ext4_dir_idx_rinfo))
-                       return NULL;
-               count_offset = 32;
-       } else if (entry_len == block_size) {
-               count_offset = 8;
-       } else {
-               return NULL;
-       }
-
-       if (offset)
-               *offset = count_offset;
-       return (struct ext4_dir_idx_climit *)(((char *)dirent) + count_offset);
-}
-
-/*
- * BIG FAT NOTES:
- *       Currently we do not verify the checksum of HTree node.
- */
-static bool ext4_dir_dx_csum_verify(struct ext4_inode_ref *inode_ref,
-                                   struct ext4_dir_en *de)
-{
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       int coff, limit, cnt;
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               struct ext4_dir_idx_climit *climit;
-               climit = ext4_dir_dx_get_climit(inode_ref, de, &coff);
-               if (!climit) {
-                       /* Directory seems corrupted. */
-                       return true;
-               }
-               struct ext4_dir_idx_tail *t;
-               limit = ext4_dir_dx_climit_get_limit(climit);
-               cnt = ext4_dir_dx_climit_get_count(climit);
-               if (coff + (limit * sizeof(struct ext4_dir_idx_entry)) >
-                   (block_size - sizeof(struct ext4_dir_idx_tail))) {
-                       /* There is no space to hold the checksum */
-                       return true;
-               }
-               t = (void *)(((struct ext4_dir_idx_entry *)climit) + limit);
-
-               uint32_t c;
-               c = to_le32(ext4_dir_dx_checksum(inode_ref, de, coff, cnt, t));
-               if (t->checksum != c)
-                       return false;
-       }
-       return true;
-}
-
-
-static void ext4_dir_set_dx_csum(struct ext4_inode_ref *inode_ref,
-                                struct ext4_dir_en *dirent)
-{
-       int coff, limit, count;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               struct ext4_dir_idx_climit *climit;
-               climit = ext4_dir_dx_get_climit(inode_ref, dirent, &coff);
-               if (!climit) {
-                       /* Directory seems corrupted. */
-                       return;
-               }
-               struct ext4_dir_idx_tail *t;
-               limit = ext4_dir_dx_climit_get_limit(climit);
-               count = ext4_dir_dx_climit_get_count(climit);
-               if (coff + (limit * sizeof(struct ext4_dir_idx_entry)) >
-                  (block_size - sizeof(struct ext4_dir_idx_tail))) {
-                       /* There is no space to hold the checksum */
-                       return;
-               }
-
-               t = (void *)(((struct ext4_dir_idx_entry *)climit) + limit);
-               t->checksum = to_le32(ext4_dir_dx_checksum(inode_ref, dirent,
-                                       coff, count, t));
-       }
-}
-#else
-#define ext4_dir_dx_csum_verify(...) true
-#define ext4_dir_set_dx_csum(...)
-#endif
-
-/****************************************************************************/
-
-int ext4_dir_dx_init(struct ext4_inode_ref *dir, struct ext4_inode_ref *parent)
-{
-       /* Load block 0, where will be index root located */
-       ext4_fsblk_t fblock;
-       uint32_t iblock = 0;
-       bool need_append =
-               (ext4_inode_get_size(&dir->fs->sb, dir->inode)
-                       < EXT4_DIR_DX_INIT_BCNT)
-               ? true : false;
-       struct ext4_sblock *sb = &dir->fs->sb;
-       uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb);
-       struct ext4_block block;
-
-       int rc;
-
-       if (!need_append)
-               rc = ext4_fs_init_inode_dblk_idx(dir, iblock, &fblock);
-       else
-               rc = ext4_fs_append_inode_dblk(dir, &fblock, &iblock);
-
-       if (rc != EOK)
-               return rc;
-
-       rc = ext4_trans_block_get_noread(dir->fs->bdev, &block, fblock);
-       if (rc != EOK)
-               return rc;
-
-       /* Initialize pointers to data structures */
-       struct ext4_dir_idx_root *root = (void *)block.data;
-       struct ext4_dir_idx_rinfo *info = &(root->info);
-
-       memset(root, 0, sizeof(struct ext4_dir_idx_root));
-       struct ext4_dir_en *de;
-
-       /* Initialize dot entries */
-       de = (struct ext4_dir_en *)root->dots;
-       ext4_dir_write_entry(sb, de, 12, dir, ".", strlen("."));
-
-       de = (struct ext4_dir_en *)(root->dots + 1);
-       uint16_t elen = block_size - 12;
-       ext4_dir_write_entry(sb, de, elen, parent, "..", strlen(".."));
-
-       /* Initialize root info structure */
-       uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version);
-
-       ext4_dir_dx_rinfo_set_hash_version(info, hash_version);
-       ext4_dir_dx_rinfo_set_indirect_levels(info, 0);
-       ext4_dir_dx_root_info_set_info_length(info, 8);
-
-       /* Set limit and current number of entries */
-       struct ext4_dir_idx_climit *climit;
-       climit = (struct ext4_dir_idx_climit *)&root->en;
-
-       ext4_dir_dx_climit_set_count(climit, 1);
-
-       uint32_t entry_space;
-       entry_space = block_size - 2 * sizeof(struct ext4_dir_idx_dot_en) -
-                       sizeof(struct ext4_dir_idx_rinfo);
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               entry_space -= sizeof(struct ext4_dir_idx_tail);
-
-       uint16_t root_limit = entry_space / sizeof(struct ext4_dir_idx_entry);
-       ext4_dir_dx_climit_set_limit(climit, root_limit);
-
-       /* Append new block, where will be new entries inserted in the future */
-       iblock++;
-       if (!need_append)
-               rc = ext4_fs_init_inode_dblk_idx(dir, iblock, &fblock);
-       else
-               rc = ext4_fs_append_inode_dblk(dir, &fblock, &iblock);
-
-       if (rc != EOK) {
-               ext4_block_set(dir->fs->bdev, &block);
-               return rc;
-       }
-
-       struct ext4_block new_block;
-       rc = ext4_trans_block_get_noread(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_dir_en *be = (void *)new_block.data;
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint16_t len = block_size - sizeof(struct ext4_dir_entry_tail);
-               ext4_dir_en_set_entry_len(be, len);
-               ext4_dir_en_set_name_len(sb, be, 0);
-               ext4_dir_en_set_inode_type(sb, be, EXT4_DE_UNKNOWN);
-               ext4_dir_init_entry_tail(EXT4_DIRENT_TAIL(be, block_size));
-               ext4_dir_set_csum(dir, be);
-       } else {
-               ext4_dir_en_set_entry_len(be, block_size);
-       }
-
-       ext4_dir_en_set_inode(be, 0);
-
-       ext4_trans_set_block_dirty(new_block.buf);
-       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_dir_idx_entry *entry = root->en;
-       ext4_dir_dx_entry_set_block(entry, iblock);
-
-       ext4_dir_set_dx_csum(dir, (struct ext4_dir_en *)block.data);
-       ext4_trans_set_block_dirty(block.buf);
-
-       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_dir_idx_root *root;
-
-       root = (struct ext4_dir_idx_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_dir_idx_dot_en);
-       entry_space -= sizeof(struct ext4_dir_idx_rinfo);
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               entry_space -= sizeof(struct ext4_dir_idx_tail);
-       entry_space = entry_space / sizeof(struct ext4_dir_idx_entry);
-
-       struct ext4_dir_idx_climit *climit = (void *)&root->en;
-       uint16_t limit = ext4_dir_dx_climit_get_limit(climit);
-       if (limit != entry_space)
-               return EXT4_ERR_BAD_DX_DIR;
-
-       /* Check hash version and modify if necessary */
-       hinfo->hash_version = ext4_dir_dx_rinfo_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_dir_idx_block **dx_block,
-                               struct ext4_dir_idx_block *dx_blocks)
-{
-       struct ext4_dir_idx_root *root;
-       struct ext4_dir_idx_entry *entries;
-       struct ext4_dir_idx_entry *p;
-       struct ext4_dir_idx_entry *q;
-       struct ext4_dir_idx_entry *m;
-       struct ext4_dir_idx_entry *at;
-       ext4_fsblk_t fblk;
-       uint32_t block_size;
-       uint16_t limit;
-       uint16_t entry_space;
-       uint8_t ind_level;
-       int r;
-
-       struct ext4_dir_idx_block *tmp_dx_blk = dx_blocks;
-       struct ext4_block *tmp_blk = root_block;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       block_size = ext4_sb_get_block_size(sb);
-       root = (struct ext4_dir_idx_root *)root_block->data;
-       entries = (struct ext4_dir_idx_entry *)&root->en;
-       limit = ext4_dir_dx_climit_get_limit((void *)entries);
-       ind_level = ext4_dir_dx_rinfo_get_indirect_levels(&root->info);
-
-       /* Walk through the index tree */
-       while (true) {
-               uint16_t cnt = ext4_dir_dx_climit_get_count((void *)entries);
-               if ((cnt == 0) || (cnt > limit))
-                       return EXT4_ERR_BAD_DX_DIR;
-
-               /* Do binary search in every node */
-               p = entries + 1;
-               q = entries + cnt - 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_blk->b, tmp_blk, sizeof(struct ext4_block));
-               tmp_dx_blk->entries = entries;
-               tmp_dx_blk->position = at;
-
-               /* Is algorithm in the leaf? */
-               if (ind_level == 0) {
-                       *dx_block = tmp_dx_blk;
-                       return EOK;
-               }
-
-               /* Goto child node */
-               uint32_t n_blk = ext4_dir_dx_entry_get_block(at);
-
-               ind_level--;
-
-               r = ext4_fs_get_inode_dblk_idx(inode_ref, n_blk, &fblk, false);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_trans_block_get(inode_ref->fs->bdev, tmp_blk, fblk);
-               if (r != EOK)
-                       return r;
-
-               entries = ((struct ext4_dir_idx_node *)tmp_blk->data)->entries;
-               limit = ext4_dir_dx_climit_get_limit((void *)entries);
-
-               entry_space = block_size - sizeof(struct ext4_fake_dir_entry);
-               if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-                       entry_space -= sizeof(struct ext4_dir_idx_tail);
-
-               entry_space = entry_space / sizeof(struct ext4_dir_idx_entry);
-
-               if (limit != entry_space) {
-                       ext4_block_set(inode_ref->fs->bdev, tmp_blk);
-                       return EXT4_ERR_BAD_DX_DIR;
-               }
-
-               if (!ext4_dir_dx_csum_verify(inode_ref, (void *)tmp_blk->data)) {
-                       ext4_dbg(DEBUG_DIR_IDX,
-                                       DBG_WARN "HTree checksum failed."
-                                       "Inode: %" PRIu32", "
-                                       "Block: %" PRIu32"\n",
-                                       inode_ref->index,
-                                       n_blk);
-               }
-
-               ++tmp_dx_blk;
-       }
-
-       /* 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_dir_idx_block *dx_block,
-                                 struct ext4_dir_idx_block *dx_blocks)
-{
-       int r;
-       uint32_t num_handles = 0;
-       ext4_fsblk_t blk_adr;
-       struct ext4_dir_idx_block *p = dx_block;
-
-       /* Try to find data block with next bunch of entries */
-       while (true) {
-               uint16_t cnt = ext4_dir_dx_climit_get_count((void *)p->entries);
-
-               p->position++;
-               if (p->position < p->entries + cnt)
-                       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 blk = ext4_dir_dx_entry_get_block(p->position);
-               r = ext4_fs_get_inode_dblk_idx(inode_ref, blk, &blk_adr, false);
-               if (r != EOK)
-                       return r;
-
-               struct ext4_block b;
-               r = ext4_trans_block_get(inode_ref->fs->bdev, &b, blk_adr);
-               if (r != EOK)
-                       return r;
-
-               if (!ext4_dir_dx_csum_verify(inode_ref, (void *)b.data)) {
-                       ext4_dbg(DEBUG_DIR_IDX,
-                                       DBG_WARN "HTree checksum failed."
-                                       "Inode: %" PRIu32", "
-                                       "Block: %" PRIu32"\n",
-                                       inode_ref->index,
-                                       blk);
-               }
-
-               p++;
-
-               /* Don't forget to put old block (prevent memory leak) */
-               r = ext4_block_set(inode_ref->fs->bdev, &p->b);
-               if (r != EOK)
-                       return r;
-
-               memcpy(&p->b, &b, sizeof(b));
-               p->entries = ((struct ext4_dir_idx_node *)b.data)->entries;
-               p->position = p->entries;
-       }
-
-       return ENOENT;
-}
-
-int ext4_dir_dx_find_entry(struct ext4_dir_search_result *result,
-                          struct ext4_inode_ref *inode_ref, size_t name_len,
-                          const char *name)
-{
-       /* Load direct block 0 (index root) */
-       ext4_fsblk_t root_block_addr;
-       int rc2;
-       int rc;
-       rc = ext4_fs_get_inode_dblk_idx(inode_ref,  0, &root_block_addr, false);
-       if (rc != EOK)
-               return rc;
-
-       struct ext4_fs *fs = inode_ref->fs;
-
-       struct ext4_block root_block;
-       rc = ext4_trans_block_get(fs->bdev, &root_block, root_block_addr);
-       if (rc != EOK)
-               return rc;
-
-       if (!ext4_dir_dx_csum_verify(inode_ref, (void *)root_block.data)) {
-               ext4_dbg(DEBUG_DIR_IDX,
-                        DBG_WARN "HTree root checksum failed."
-                        "Inode: %" PRIu32", "
-                        "Block: %" PRIu32"\n",
-                        inode_ref->index,
-                        (uint32_t)0);
-       }
-
-       /* 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_dir_idx_block dx_blocks[2];
-       struct ext4_dir_idx_block *dx_block;
-       struct ext4_dir_idx_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_blk_idx;
-               ext4_fsblk_t leaf_block_addr;
-               struct ext4_block b;
-
-               leaf_blk_idx = ext4_dir_dx_entry_get_block(dx_block->position);
-               rc = ext4_fs_get_inode_dblk_idx(inode_ref, leaf_blk_idx,
-                                               &leaf_block_addr, false);
-               if (rc != EOK)
-                       goto cleanup;
-
-               rc = ext4_trans_block_get(fs->bdev, &b, leaf_block_addr);
-               if (rc != EOK)
-                       goto cleanup;
-
-               if (!ext4_dir_csum_verify(inode_ref, (void *)b.data)) {
-                       ext4_dbg(DEBUG_DIR_IDX,
-                                DBG_WARN "HTree leaf block checksum failed."
-                                "Inode: %" PRIu32", "
-                                "Block: %" PRIu32"\n",
-                                inode_ref->index,
-                                leaf_blk_idx);
-               }
-
-               /* Linear search inside block */
-               struct ext4_dir_en *de;
-               rc = ext4_dir_find_in_block(&b, &fs->sb, name_len, name, &de);
-
-               /* Found => return it */
-               if (rc == EOK) {
-                       result->block = b;
-                       result->dentry = de;
-                       goto cleanup;
-               }
-
-               /* Not found, leave untouched */
-               rc2 = ext4_block_set(fs->bdev, &b);
-               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->b);
-               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 inode_ref   Directory i-node
- * @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_inode_ref *inode_ref __unused,
-                        struct ext4_dir_idx_block *index_block,
-                        uint32_t hash, uint32_t iblock)
-{
-       struct ext4_dir_idx_entry *old_index_entry = index_block->position;
-       struct ext4_dir_idx_entry *new_index_entry = old_index_entry + 1;
-       struct ext4_dir_idx_climit *climit = (void *)index_block->entries;
-       struct ext4_dir_idx_entry *start_index = index_block->entries;
-       uint32_t count = ext4_dir_dx_climit_get_count(climit);
-
-       size_t bytes;
-       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_climit_set_count(climit, count + 1);
-       ext4_dir_set_dx_csum(inode_ref, (void *)index_block->b.data);
-       ext4_trans_set_block_dirty(index_block->b.buf);
-}
-
-/**@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_dir_idx_block *index_block,
-                                 struct ext4_block *new_data_block)
-{
-       int rc = EOK;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
-
-       /* Allocate buffer for directory entries */
-       uint8_t *entry_buffer = malloc(block_size);
-       if (entry_buffer == NULL)
-               return ENOMEM;
-
-       /* dot entry has the smallest size available */
-       uint32_t max_ecnt = block_size / sizeof(struct ext4_dir_idx_dot_en);
-
-       /* Allocate sort entry */
-       struct ext4_dx_sort_entry *sort;
-
-       sort = malloc(max_ecnt * sizeof(struct ext4_dx_sort_entry));
-       if (sort == NULL) {
-               free(entry_buffer);
-               return ENOMEM;
-       }
-
-       uint32_t idx = 0;
-       uint32_t real_size = 0;
-
-       /* Initialize hinfo */
-       struct ext4_hash_info hinfo_tmp;
-       memcpy(&hinfo_tmp, hinfo, sizeof(struct ext4_hash_info));
-
-       /* Load all valid entries to the buffer */
-       struct ext4_dir_en *de = (void *)old_data_block->data;
-       uint8_t *entry_buffer_ptr = entry_buffer;
-       while ((void *)de < (void *)(old_data_block->data + block_size)) {
-               /* Read only valid entries */
-               if (ext4_dir_en_get_inode(de) && de->name_len) {
-                       uint16_t len = ext4_dir_en_get_name_len(sb, de);
-                       rc = ext4_dir_dx_hash_string(&hinfo_tmp, len,
-                                                    (char *)de->name);
-                       if (rc != EOK) {
-                               free(sort);
-                               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, de, rec_len);
-
-                       sort[idx].dentry = entry_buffer_ptr;
-                       sort[idx].rec_len = rec_len;
-                       sort[idx].hash = hinfo_tmp.hash;
-
-                       entry_buffer_ptr += rec_len;
-                       real_size += rec_len;
-                       idx++;
-               }
-
-               size_t elen = ext4_dir_en_get_entry_len(de);
-               de = (void *)((uint8_t *)de + elen);
-       }
-
-/* Sort all entries */
-#if CONFIG_DIR_INDEX_COMB_SORT
-       comb_sort(sort, idx);
-#else
-       qsort(sort, idx, sizeof(struct ext4_dx_sort_entry),
-             ext4_dir_dx_entry_comparator);
-#endif
-       /* Allocate new block for store the second part of entries */
-       ext4_fsblk_t new_fblock;
-       uint32_t new_iblock;
-       rc = ext4_fs_append_inode_dblk(inode_ref, &new_fblock, &new_iblock);
-       if (rc != EOK) {
-               free(sort);
-               free(entry_buffer);
-               return rc;
-       }
-
-       /* Load new block */
-       struct ext4_block new_data_block_tmp;
-       rc = ext4_trans_block_get_noread(inode_ref->fs->bdev, &new_data_block_tmp,
-                                  new_fblock);
-       if (rc != EOK) {
-               free(sort);
-               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[i].rec_len) > (block_size / 2)) {
-                       new_hash = sort[i].hash;
-                       mid = i;
-                       break;
-               }
-
-               current_size += sort[i].rec_len;
-       }
-
-       /* Check hash collision */
-       uint32_t continued = 0;
-       if (new_hash == sort[mid - 1].hash)
-               continued = 1;
-
-       uint32_t off = 0;
-       void *ptr;
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               block_size -= sizeof(struct ext4_dir_entry_tail);
-
-       /* First part - to the old block */
-       for (i = 0; i < mid; ++i) {
-               ptr = old_data_block->data + off;
-               memcpy(ptr, sort[i].dentry, sort[i].rec_len);
-
-               struct ext4_dir_en *t = ptr;
-               if (i < (mid - 1))
-                       ext4_dir_en_set_entry_len(t, sort[i].rec_len);
-               else
-                       ext4_dir_en_set_entry_len(t, block_size - off);
-
-               off += sort[i].rec_len;
-       }
-
-       /* Second part - to the new block */
-       off = 0;
-       for (i = mid; i < idx; ++i) {
-               ptr = new_data_block_tmp.data + off;
-               memcpy(ptr, sort[i].dentry, sort[i].rec_len);
-
-               struct ext4_dir_en *t = ptr;
-               if (i < (idx - 1))
-                       ext4_dir_en_set_entry_len(t, sort[i].rec_len);
-               else
-                       ext4_dir_en_set_entry_len(t, block_size - off);
-
-               off += sort[i].rec_len;
-       }
-
-       block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
-
-       /* Do some steps to finish operation */
-       sb = &inode_ref->fs->sb;
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               struct ext4_dir_entry_tail *t;
-
-               t = EXT4_DIRENT_TAIL(old_data_block->data, block_size);
-               ext4_dir_init_entry_tail(t);
-               t = EXT4_DIRENT_TAIL(new_data_block_tmp.data, block_size);
-               ext4_dir_init_entry_tail(t);
-       }
-       ext4_dir_set_csum(inode_ref, (void *)old_data_block->data);
-       ext4_dir_set_csum(inode_ref, (void *)new_data_block_tmp.data);
-       ext4_trans_set_block_dirty(old_data_block->buf);
-       ext4_trans_set_block_dirty(new_data_block_tmp.buf);
-
-       free(sort);
-       free(entry_buffer);
-
-       ext4_dir_dx_insert_entry(inode_ref, 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 *ino_ref,
-                       struct ext4_dir_idx_block *dx_blks,
-                       struct ext4_dir_idx_block *dxb,
-                       struct ext4_dir_idx_block **new_dx_block)
-{
-       struct ext4_sblock *sb = &ino_ref->fs->sb;
-       struct ext4_dir_idx_entry *e;
-       int r;
-
-       uint32_t block_size = ext4_sb_get_block_size(&ino_ref->fs->sb);
-       uint32_t entry_space = block_size - sizeof(struct ext4_fake_dir_entry);
-       uint32_t node_limit =  entry_space / sizeof(struct ext4_dir_idx_entry);
-
-       bool meta_csum = ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM);
-
-       if (dxb == dx_blks)
-               e = ((struct ext4_dir_idx_root *)dxb->b.data)->en;
-       else
-               e = ((struct ext4_dir_idx_node *)dxb->b.data)->entries;
-
-       struct ext4_dir_idx_climit *climit = (struct ext4_dir_idx_climit *)e;
-
-       uint16_t leaf_limit = ext4_dir_dx_climit_get_limit(climit);
-       uint16_t leaf_count = ext4_dir_dx_climit_get_count(climit);
-
-       /* Check if is necessary to split index block */
-       if (leaf_limit == leaf_count) {
-               struct ext4_dir_idx_entry *ren;
-               ptrdiff_t levels = dxb - dx_blks;
-
-               ren = ((struct ext4_dir_idx_root *)dx_blks[0].b.data)->en;
-               struct ext4_dir_idx_climit *rclimit = (void *)ren;
-               uint16_t root_limit = ext4_dir_dx_climit_get_limit(rclimit);
-               uint16_t root_count = ext4_dir_dx_climit_get_count(rclimit);
-
-
-               /* Linux limitation */
-               if ((levels > 0) && (root_limit == root_count))
-                       return ENOSPC;
-
-               /* Add new block to directory */
-               ext4_fsblk_t new_fblk;
-               uint32_t new_iblk;
-               r = ext4_fs_append_inode_dblk(ino_ref, &new_fblk, &new_iblk);
-               if (r != EOK)
-                       return r;
-
-               /* load new block */
-               struct ext4_block b;
-               r = ext4_trans_block_get_noread(ino_ref->fs->bdev, &b, new_fblk);
-               if (r != EOK)
-                       return r;
-
-               struct ext4_dir_idx_node *new_node = (void *)b.data;
-               struct ext4_dir_idx_entry *new_en = new_node->entries;
-
-               memset(&new_node->fake, 0, sizeof(struct ext4_fake_dir_entry));
-               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;
-                       size_t sz;
-
-                       struct ext4_dir_idx_climit *left_climit;
-                       struct ext4_dir_idx_climit *right_climit;
-
-                       hash_right = ext4_dir_dx_entry_get_hash(e + count_left);
-                       /* Copy data to new node */
-                       sz = count_right * sizeof(struct ext4_dir_idx_entry);
-                       memcpy(new_en, e + count_left, sz);
-
-                       /* Initialize new node */
-                       left_climit = (struct ext4_dir_idx_climit *)e;
-                       right_climit = (struct ext4_dir_idx_climit *)new_en;
-
-                       ext4_dir_dx_climit_set_count(left_climit, count_left);
-                       ext4_dir_dx_climit_set_count(right_climit, count_right);
-
-                       if (meta_csum)
-                               entry_space -= sizeof(struct ext4_dir_idx_tail);
-
-                       ext4_dir_dx_climit_set_limit(right_climit, node_limit);
-
-                       /* Which index block is target for new entry */
-                       uint32_t position_index =
-                           (dxb->position - dxb->entries);
-                       if (position_index >= count_left) {
-                               ext4_dir_set_dx_csum(
-                                               ino_ref,
-                                               (struct ext4_dir_en *)
-                                               dxb->b.data);
-                               ext4_trans_set_block_dirty(dxb->b.buf);
-
-                               struct ext4_block block_tmp = dxb->b;
-
-                               dxb->b = b;
-
-                               dxb->position =
-                                   new_en + position_index - count_left;
-                               dxb->entries = new_en;
-
-                               b = block_tmp;
-                       }
-
-                       /* Finally insert new entry */
-                       ext4_dir_dx_insert_entry(ino_ref, dx_blks, hash_right,
-                                                new_iblk);
-                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[0].b.data);
-                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[1].b.data);
-                       ext4_trans_set_block_dirty(dx_blks[0].b.buf);
-                       ext4_trans_set_block_dirty(dx_blks[1].b.buf);
-
-                       ext4_dir_set_dx_csum(ino_ref, (void *)b.data);
-                       ext4_trans_set_block_dirty(b.buf);
-                       return ext4_block_set(ino_ref->fs->bdev, &b);
-               } else {
-                       size_t sz;
-                       /* Copy data from root to child block */
-                       sz = leaf_count * sizeof(struct ext4_dir_idx_entry);
-                       memcpy(new_en, e, sz);
-
-                       struct ext4_dir_idx_climit *new_climit = (void*)new_en;
-                       if (meta_csum)
-                               entry_space -= sizeof(struct ext4_dir_idx_tail);
-
-                       ext4_dir_dx_climit_set_limit(new_climit, node_limit);
-
-                       /* Set values in root node */
-                       struct ext4_dir_idx_climit *new_root_climit = (void *)e;
-
-                       ext4_dir_dx_climit_set_count(new_root_climit, 1);
-                       ext4_dir_dx_entry_set_block(e, new_iblk);
-
-                       struct ext4_dir_idx_root *r = (void *)dx_blks[0].b.data;
-                       r->info.indirect_levels = 1;
-
-                       /* Add new entry to the path */
-                       dxb = dx_blks + 1;
-                       dxb->position = dx_blks->position - e + new_en;
-                       dxb->entries = new_en;
-                       dxb->b = b;
-                       *new_dx_block = dxb;
-
-                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[0].b.data);
-                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[1].b.data);
-                       ext4_trans_set_block_dirty(dx_blks[0].b.buf);
-                       ext4_trans_set_block_dirty(dx_blks[1].b.buf);
-               }
-       }
-
-       return EOK;
-}
-
-int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
-                         struct ext4_inode_ref *child, const char *name)
-{
-       int rc2 = EOK;
-       int r;
-       /* Get direct block 0 (index root) */
-       ext4_fsblk_t rblock_addr;
-       r =  ext4_fs_get_inode_dblk_idx(parent, 0, &rblock_addr, false);
-       if (r != EOK)
-               return r;
-
-       struct ext4_fs *fs = parent->fs;
-       struct ext4_block root_blk;
-
-       r = ext4_trans_block_get(fs->bdev, &root_blk, rblock_addr);
-       if (r != EOK)
-               return r;
-
-       if (!ext4_dir_dx_csum_verify(parent, (void*)root_blk.data)) {
-               ext4_dbg(DEBUG_DIR_IDX,
-                        DBG_WARN "HTree root checksum failed."
-                        "Inode: %" PRIu32", "
-                        "Block: %" PRIu32"\n",
-                        parent->index,
-                        (uint32_t)0);
-       }
-
-       /* Initialize hinfo structure (mainly compute hash) */
-       uint32_t name_len = strlen(name);
-       struct ext4_hash_info hinfo;
-       r = ext4_dir_hinfo_init(&hinfo, &root_blk, &fs->sb, name_len, name);
-       if (r != EOK) {
-               ext4_block_set(fs->bdev, &root_blk);
-               return EXT4_ERR_BAD_DX_DIR;
-       }
-
-       /*
-        * Hardcoded number 2 means maximum height of index
-        * tree defined in Linux.
-        */
-       struct ext4_dir_idx_block dx_blks[2];
-       struct ext4_dir_idx_block *dx_blk;
-       struct ext4_dir_idx_block *dx_it;
-
-       r = ext4_dir_dx_get_leaf(&hinfo, parent, &root_blk, &dx_blk, dx_blks);
-       if (r != EOK) {
-               r = 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_blk->position);
-       ext4_fsblk_t leaf_block_addr;
-       r = ext4_fs_get_inode_dblk_idx(parent, leaf_block_idx,
-                                               &leaf_block_addr, false);
-       if (r != EOK)
-               goto release_index;
-
-       /*
-        * Check if there is needed to split index node
-        * (and recursively also parent nodes)
-        */
-       r = ext4_dir_dx_split_index(parent, dx_blks, dx_blk, &dx_blk);
-       if (r != EOK)
-               goto release_target_index;
-
-       struct ext4_block target_block;
-       r = ext4_trans_block_get(fs->bdev, &target_block, leaf_block_addr);
-       if (r != EOK)
-               goto release_index;
-
-       if (!ext4_dir_csum_verify(parent,(void *)target_block.data)) {
-               ext4_dbg(DEBUG_DIR_IDX,
-                               DBG_WARN "HTree leaf block checksum failed."
-                               "Inode: %" PRIu32", "
-                               "Block: %" PRIu32"\n",
-                               parent->index,
-                               leaf_block_idx);
-       }
-
-       /* Check if insert operation passed */
-       r = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child,
-                                       name, name_len);
-       if (r == EOK)
-               goto release_target_index;
-
-       /* Split entries to two blocks (includes sorting by hash value) */
-       struct ext4_block new_block;
-       r = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_blk,
-                                   &new_block);
-       if (r != EOK) {
-               rc2 = r;
-               goto release_target_index;
-       }
-
-       /* Where to save new entry */
-       uint32_t blk_hash = ext4_dir_dx_entry_get_hash(dx_blk->position + 1);
-       if (hinfo.hash >= blk_hash)
-               r = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block,
-                                               child, name, name_len);
-       else
-               r = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block,
-                                               child, name, name_len);
-
-       /* Cleanup */
-       r = ext4_block_set(fs->bdev, &new_block);
-       if (r != EOK)
-               return r;
-
-/* Cleanup operations */
-
-release_target_index:
-       rc2 = r;
-
-       r = ext4_block_set(fs->bdev, &target_block);
-       if (r != EOK)
-               return r;
-
-release_index:
-       if (r != EOK)
-               rc2 = r;
-
-       dx_it = dx_blks;
-
-       while (dx_it <= dx_blk) {
-               r = ext4_block_set(fs->bdev, &dx_it->b);
-               if (r != EOK)
-                       return r;
-
-               dx_it++;
-       }
-
-       return rc2;
-}
-
-int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir,
-                                   uint32_t parent_inode)
-{
-       /* Load block 0, where will be index root located */
-       ext4_fsblk_t fblock;
-       int rc = ext4_fs_get_inode_dblk_idx(dir, 0, &fblock, false);
-       if (rc != EOK)
-               return rc;
-
-       struct ext4_block block;
-       rc = ext4_trans_block_get(dir->fs->bdev, &block, fblock);
-       if (rc != EOK)
-               return rc;
-
-       if (!ext4_dir_dx_csum_verify(dir, (void *)block.data)) {
-               ext4_dbg(DEBUG_DIR_IDX,
-                        DBG_WARN "HTree root checksum failed."
-                        "Inode: %" PRIu32", "
-                        "Block: %" PRIu32"\n",
-                        dir->index,
-                        (uint32_t)0);
-       }
-
-       /* Initialize pointers to data structures */
-       struct ext4_dir_idx_root *root = (void *)block.data;
-
-       /* Fill the inode field with a new parent ino. */
-       ext4_dx_dot_en_set_inode(&root->dots[1], parent_inode);
-
-       ext4_dir_set_dx_csum(dir, (void *)block.data);
-       ext4_trans_set_block_dirty(block.buf);
-
-       return ext4_block_set(dir->fs->bdev, &block);
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_dir_idx.h b/lwext4/ext4_dir_idx.h
deleted file mode 100644 (file)
index dd0067d..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#define EXT4_DIR_DX_INIT_BCNT 2
-
-/**@brief Initialize index structure of new directory.
- * @param dir Pointer to directory i-node
- * @param dir Pointer to parent directory i-node
- * @return Error code
- */
-int ext4_dir_dx_init(struct ext4_inode_ref *dir,
-                    struct ext4_inode_ref *parent);
-
-/**@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_dir_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);
-
-/**@brief Add new entry to indexed directory
- * @param dir           Directory i-node
- * @param parent_inode  parent inode index
- * @return Error code
- */
-int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir,
-                                   uint32_t parent_inode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_DIR_IDX_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_errno.h b/lwext4/ext4_errno.h
deleted file mode 100644 (file)
index edf89a9..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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_errno.h
- * @brief Error codes.
- */
-#ifndef EXT4_ERRNO_H_
-#define EXT4_ERRNO_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-
-#if !CONFIG_HAVE_OWN_ERRNO
-#include <errno.h>
-#else
-#define EPERM 1      /* Operation not permitted */
-#define ENOENT 2     /* No such file or directory */
-#define EIO 5        /* I/O error */
-#define ENXIO 6      /* No such device or address */
-#define E2BIG 7      /* Argument list too long */
-#define ENOMEM 12    /* Out of memory */
-#define EACCES 13    /* Permission denied */
-#define EFAULT 14    /* Bad address */
-#define EEXIST 17    /* File exists */
-#define ENODEV 19    /* No such device */
-#define ENOTDIR 20   /* Not a directory */
-#define EISDIR 21    /* Is a directory */
-#define EINVAL 22    /* Invalid argument */
-#define EFBIG 27     /* File too large */
-#define ENOSPC 28    /* No space left on device */
-#define EROFS 30     /* Read-only file system */
-#define EMLINK 31    /* Too many links */
-#define ERANGE 34    /* Math result not representable */
-#define ENOTEMPTY 39 /* Directory not empty */
-#define ENODATA 61   /* No data available */
-#define ENOTSUP 95   /* Not supported */
-#endif
-
-#ifndef ENODATA
- #ifdef ENOATTR
- #define ENODATA ENOATTR
- #else
- #define ENODATA 61
- #endif
-#endif
-
-#ifndef ENOTSUP
-#define ENOTSUP 95
-#endif
-
-#ifndef EOK
-#define EOK 0
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_ERRNO_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_extent.c b/lwext4/ext4_extent.c
deleted file mode 100644 (file)
index f53733d..0000000
+++ /dev/null
@@ -1,1859 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
- *
- * 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.
- */
-
-#include "ext4_config.h"
-#include "ext4_blockdev.h"
-#include "ext4_fs.h"
-#include "ext4_super.h"
-#include "ext4_crc32.h"
-#include "ext4_balloc.h"
-#include "ext4_debug.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-#include <stddef.h>
-
-#include "ext4_extent.h"
-
-/*
- * used by extent splitting.
- */
-#define EXT4_EXT_MARK_UNWRIT1 0x02 /* mark first half unwritten */
-#define EXT4_EXT_MARK_UNWRIT2 0x04 /* mark second half unwritten */
-#define EXT4_EXT_DATA_VALID1 0x08  /* first half contains valid data */
-#define EXT4_EXT_DATA_VALID2 0x10  /* second half contains valid data */
-#define EXT4_EXT_NO_COMBINE 0x20   /* do not combine two extents */
-
-static struct ext4_extent_tail *
-find_ext4_extent_tail(struct ext4_extent_header *eh)
-{
-       return (struct ext4_extent_tail *)(((char *)eh) +
-                                          EXT4_EXTENT_TAIL_OFFSET(eh));
-}
-
-static struct ext4_extent_header *ext_inode_hdr(struct ext4_inode *inode)
-{
-       return (struct ext4_extent_header *)inode->blocks;
-}
-
-static struct ext4_extent_header *ext_block_hdr(struct ext4_block *block)
-{
-       return (struct ext4_extent_header *)block->data;
-}
-
-static uint16_t ext_depth(struct ext4_inode *inode)
-{
-       return to_le16(ext_inode_hdr(inode)->depth);
-}
-
-static uint16_t ext4_ext_get_actual_len(struct ext4_extent *ext)
-{
-       return (to_le16(ext->block_count) <= EXT_INIT_MAX_LEN
-                   ? to_le16(ext->block_count)
-                   : (to_le16(ext->block_count) - EXT_INIT_MAX_LEN));
-}
-
-static void ext4_ext_mark_initialized(struct ext4_extent *ext)
-{
-       ext->block_count = to_le16(ext4_ext_get_actual_len(ext));
-}
-
-static void ext4_ext_mark_unwritten(struct ext4_extent *ext)
-{
-       ext->block_count |= to_le16(EXT_INIT_MAX_LEN);
-}
-
-static int ext4_ext_is_unwritten(struct ext4_extent *ext)
-{
-       /* Extent with ee_len of 0x8000 is treated as an initialized extent */
-       return (to_le16(ext->block_count) > EXT_INIT_MAX_LEN);
-}
-
-/*
- * ext4_ext_pblock:
- * combine low and high parts of physical block number into ext4_fsblk_t
- */
-static ext4_fsblk_t ext4_ext_pblock(struct ext4_extent *ex)
-{
-       ext4_fsblk_t block;
-
-       block = to_le32(ex->start_lo);
-       block |= ((ext4_fsblk_t)to_le16(ex->start_hi) << 31) << 1;
-       return block;
-}
-
-/*
- * ext4_idx_pblock:
- * combine low and high parts of a leaf physical block number into ext4_fsblk_t
- */
-static ext4_fsblk_t ext4_idx_pblock(struct ext4_extent_index *ix)
-{
-       ext4_fsblk_t block;
-
-       block = to_le32(ix->leaf_lo);
-       block |= ((ext4_fsblk_t)to_le16(ix->leaf_hi) << 31) << 1;
-       return block;
-}
-
-/*
- * ext4_ext_store_pblock:
- * stores a large physical block number into an extent struct,
- * breaking it into parts
- */
-static void ext4_ext_store_pblock(struct ext4_extent *ex, ext4_fsblk_t pb)
-{
-       ex->start_lo = to_le32((uint32_t)(pb & 0xffffffff));
-       ex->start_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff);
-}
-
-/*
- * ext4_idx_store_pblock:
- * stores a large physical block number into an index struct,
- * breaking it into parts
- */
-static void ext4_idx_store_pblock(struct ext4_extent_index *ix, ext4_fsblk_t pb)
-{
-       ix->leaf_lo = to_le32((uint32_t)(pb & 0xffffffff));
-       ix->leaf_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff);
-}
-
-static int ext4_allocate_single_block(struct ext4_inode_ref *inode_ref,
-                                     ext4_fsblk_t goal,
-                                     ext4_fsblk_t *blockp)
-{
-       return ext4_balloc_alloc_block(inode_ref, goal, blockp);
-}
-
-static ext4_fsblk_t ext4_new_meta_blocks(struct ext4_inode_ref *inode_ref,
-                                        ext4_fsblk_t goal,
-                                        uint32_t flags __unused,
-                                        uint32_t *count, int *errp)
-{
-       ext4_fsblk_t block = 0;
-
-       *errp = ext4_allocate_single_block(inode_ref, goal, &block);
-       if (count)
-               *count = 1;
-       return block;
-}
-
-static void ext4_ext_free_blocks(struct ext4_inode_ref *inode_ref,
-                                ext4_fsblk_t block, uint32_t count,
-                                uint32_t flags __unused)
-{
-       ext4_balloc_free_blocks(inode_ref, block, count);
-}
-
-static uint16_t ext4_ext_space_block(struct ext4_inode_ref *inode_ref)
-{
-       uint16_t size;
-       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
-
-       size = (block_size - sizeof(struct ext4_extent_header)) /
-              sizeof(struct ext4_extent);
-#ifdef AGGRESSIVE_TEST
-       if (size > 6)
-               size = 6;
-#endif
-       return size;
-}
-
-static uint16_t ext4_ext_space_block_idx(struct ext4_inode_ref *inode_ref)
-{
-       uint16_t size;
-       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
-
-       size = (block_size - sizeof(struct ext4_extent_header)) /
-              sizeof(struct ext4_extent_index);
-#ifdef AGGRESSIVE_TEST
-       if (size > 5)
-               size = 5;
-#endif
-       return size;
-}
-
-static uint16_t ext4_ext_space_root(struct ext4_inode_ref *inode_ref)
-{
-       uint16_t size;
-
-       size = sizeof(inode_ref->inode->blocks);
-       size -= sizeof(struct ext4_extent_header);
-       size /= sizeof(struct ext4_extent);
-#ifdef AGGRESSIVE_TEST
-       if (size > 3)
-               size = 3;
-#endif
-       return size;
-}
-
-static uint16_t ext4_ext_space_root_idx(struct ext4_inode_ref *inode_ref)
-{
-       uint16_t size;
-
-       size = sizeof(inode_ref->inode->blocks);
-       size -= sizeof(struct ext4_extent_header);
-       size /= sizeof(struct ext4_extent_index);
-#ifdef AGGRESSIVE_TEST
-       if (size > 4)
-               size = 4;
-#endif
-       return size;
-}
-
-static uint16_t ext4_ext_max_entries(struct ext4_inode_ref *inode_ref,
-                                  uint32_t depth)
-{
-       uint16_t max;
-
-       if (depth == ext_depth(inode_ref->inode)) {
-               if (depth == 0)
-                       max = ext4_ext_space_root(inode_ref);
-               else
-                       max = ext4_ext_space_root_idx(inode_ref);
-       } else {
-               if (depth == 0)
-                       max = ext4_ext_space_block(inode_ref);
-               else
-                       max = ext4_ext_space_block_idx(inode_ref);
-       }
-
-       return max;
-}
-
-static ext4_fsblk_t ext4_ext_find_goal(struct ext4_inode_ref *inode_ref,
-                                      struct ext4_extent_path *path,
-                                      ext4_lblk_t block)
-{
-       if (path) {
-               uint32_t depth = path->depth;
-               struct ext4_extent *ex;
-
-               /*
-                * Try to predict block placement assuming that we are
-                * filling in a file which will eventually be
-                * non-sparse --- i.e., in the case of libbfd writing
-                * an ELF object sections out-of-order but in a way
-                * the eventually results in a contiguous object or
-                * executable file, or some database extending a table
-                * space file.  However, this is actually somewhat
-                * non-ideal if we are writing a sparse file such as
-                * qemu or KVM writing a raw image file that is going
-                * to stay fairly sparse, since it will end up
-                * fragmenting the file system's free space.  Maybe we
-                * should have some hueristics or some way to allow
-                * userspace to pass a hint to file system,
-                * especially if the latter case turns out to be
-                * common.
-                */
-               ex = path[depth].extent;
-               if (ex) {
-                       ext4_fsblk_t ext_pblk = ext4_ext_pblock(ex);
-                       ext4_lblk_t ext_block = to_le32(ex->first_block);
-
-                       if (block > ext_block)
-                               return ext_pblk + (block - ext_block);
-                       else
-                               return ext_pblk - (ext_block - block);
-               }
-
-               /* it looks like index is empty;
-                * try to find starting block from index itself */
-               if (path[depth].block.lb_id)
-                       return path[depth].block.lb_id;
-       }
-
-       /* OK. use inode's group */
-       return ext4_fs_inode_to_goal_block(inode_ref);
-}
-
-/*
- * Allocation for a meta data block
- */
-static ext4_fsblk_t ext4_ext_new_meta_block(struct ext4_inode_ref *inode_ref,
-                                           struct ext4_extent_path *path,
-                                           struct ext4_extent *ex, int *err,
-                                           uint32_t flags)
-{
-       ext4_fsblk_t goal, newblock;
-
-       goal = ext4_ext_find_goal(inode_ref, path, to_le32(ex->first_block));
-       newblock = ext4_new_meta_blocks(inode_ref, goal, flags, NULL, err);
-       return newblock;
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_ext_block_csum(struct ext4_inode_ref *inode_ref,
-                                   struct ext4_extent_header *eh)
-{
-       uint32_t checksum = 0;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint32_t ino_index = to_le32(inode_ref->index);
-               uint32_t ino_gen =
-                       to_le32(ext4_inode_get_generation(inode_ref->inode));
-               /* First calculate crc32 checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
-                               sizeof(sb->uuid));
-               /* Then calculate crc32 checksum against inode number
-                * and inode generation */
-               checksum = ext4_crc32c(checksum, &ino_index,
-                                    sizeof(ino_index));
-               checksum = ext4_crc32c(checksum, &ino_gen,
-                                    sizeof(ino_gen));
-               /* Finally calculate crc32 checksum against 
-                * the entire extent block up to the checksum field */
-               checksum = ext4_crc32c(checksum, eh,
-                               EXT4_EXTENT_TAIL_OFFSET(eh));
-       }
-       return checksum;
-}
-#else
-#define ext4_ext_block_csum(...) 0
-#endif
-
-static void ext4_extent_block_csum_set(struct ext4_inode_ref *inode_ref __unused,
-                                      struct ext4_extent_header *eh)
-{
-       struct ext4_extent_tail *tail;
-
-       tail = find_ext4_extent_tail(eh);
-       tail->et_checksum = to_le32(ext4_ext_block_csum(inode_ref, eh));
-}
-
-static int ext4_ext_dirty(struct ext4_inode_ref *inode_ref,
-                         struct ext4_extent_path *path)
-{
-       if (path->block.lb_id)
-               ext4_trans_set_block_dirty(path->block.buf);
-       else
-               inode_ref->dirty = true;
-
-       return EOK;
-}
-
-static void ext4_ext_drop_refs(struct ext4_inode_ref *inode_ref,
-                              struct ext4_extent_path *path, bool keep_other)
-{
-       int32_t depth, i;
-
-       if (!path)
-               return;
-       if (keep_other)
-               depth = 0;
-       else
-               depth = path->depth;
-
-       for (i = 0; i <= depth; i++, path++) {
-               if (path->block.lb_id) {
-                       if (ext4_bcache_test_flag(path->block.buf, BC_DIRTY))
-                               ext4_extent_block_csum_set(inode_ref,
-                                               path->header);
-
-                       ext4_block_set(inode_ref->fs->bdev, &path->block);
-               }
-       }
-}
-
-/*
- * Check that whether the basic information inside the extent header
- * is correct or not.
- */
-static int ext4_ext_check(struct ext4_inode_ref *inode_ref,
-                         struct ext4_extent_header *eh, uint16_t depth,
-                         ext4_fsblk_t pblk __unused)
-{
-       struct ext4_extent_tail *tail;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       const char *error_msg;
-       (void)error_msg;
-
-       if (to_le16(eh->magic) != EXT4_EXTENT_MAGIC) {
-               error_msg = "invalid magic";
-               goto corrupted;
-       }
-       if (to_le16(eh->depth) != depth) {
-               error_msg = "unexpected eh_depth";
-               goto corrupted;
-       }
-       if (eh->max_entries_count == 0) {
-               error_msg = "invalid eh_max";
-               goto corrupted;
-       }
-       if (to_le16(eh->entries_count) > to_le16(eh->max_entries_count)) {
-               error_msg = "invalid eh_entries";
-               goto corrupted;
-       }
-
-       tail = find_ext4_extent_tail(eh);
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               if (tail->et_checksum != to_le32(ext4_ext_block_csum(inode_ref, eh))) {
-                       ext4_dbg(DEBUG_EXTENT,
-                                DBG_WARN "Extent block checksum failed."
-                                "Blocknr: %" PRIu64"\n",
-                                pblk);
-
-               }
-       }
-
-       return EOK;
-
-corrupted:
-       ext4_dbg(DEBUG_EXTENT, "Bad extents B+ tree block: %s. "
-                              "Blocknr: %" PRId64 "\n",
-                error_msg, pblk);
-       return EIO;
-}
-
-static int read_extent_tree_block(struct ext4_inode_ref *inode_ref,
-                                 ext4_fsblk_t pblk, int32_t depth,
-                                 struct ext4_block *bh,
-                                 uint32_t flags __unused)
-{
-       int err;
-
-       err = ext4_trans_block_get(inode_ref->fs->bdev, bh, pblk);
-       if (err != EOK)
-               goto errout;
-
-       err = ext4_ext_check(inode_ref, ext_block_hdr(bh), depth, pblk);
-       if (err != EOK)
-               goto errout;
-
-       return EOK;
-errout:
-       if (bh->lb_id)
-               ext4_block_set(inode_ref->fs->bdev, bh);
-
-       return err;
-}
-
-/*
- * ext4_ext_binsearch_idx:
- * binary search for the closest index of the given block
- * the header must be checked before calling this
- */
-static void ext4_ext_binsearch_idx(struct ext4_extent_path *path,
-                                  ext4_lblk_t block)
-{
-       struct ext4_extent_header *eh = path->header;
-       struct ext4_extent_index *r, *l, *m;
-
-       l = EXT_FIRST_INDEX(eh) + 1;
-       r = EXT_LAST_INDEX(eh);
-       while (l <= r) {
-               m = l + (r - l) / 2;
-               if (block < to_le32(m->first_block))
-                       r = m - 1;
-               else
-                       l = m + 1;
-       }
-
-       path->index = l - 1;
-}
-
-/*
- * ext4_ext_binsearch:
- * binary search for closest extent of the given block
- * the header must be checked before calling this
- */
-static void ext4_ext_binsearch(struct ext4_extent_path *path, ext4_lblk_t block)
-{
-       struct ext4_extent_header *eh = path->header;
-       struct ext4_extent *r, *l, *m;
-
-       if (eh->entries_count == 0) {
-               /*
-                * this leaf is empty:
-                * we get such a leaf in split/add case
-                */
-               return;
-       }
-
-       l = EXT_FIRST_EXTENT(eh) + 1;
-       r = EXT_LAST_EXTENT(eh);
-
-       while (l <= r) {
-               m = l + (r - l) / 2;
-               if (block < to_le32(m->first_block))
-                       r = m - 1;
-               else
-                       l = m + 1;
-       }
-
-       path->extent = l - 1;
-}
-
-static int ext4_find_extent(struct ext4_inode_ref *inode_ref, ext4_lblk_t block,
-                           struct ext4_extent_path **orig_path, uint32_t flags)
-{
-       struct ext4_extent_header *eh;
-       struct ext4_block bh = EXT4_BLOCK_ZERO();
-       ext4_fsblk_t buf_block = 0;
-       struct ext4_extent_path *path = *orig_path;
-       int32_t depth, ppos = 0;
-       int32_t i;
-       int ret;
-
-       eh = ext_inode_hdr(inode_ref->inode);
-       depth = ext_depth(inode_ref->inode);
-
-       if (path) {
-               ext4_ext_drop_refs(inode_ref, path, 0);
-               if (depth > path[0].maxdepth) {
-                       free(path);
-                       *orig_path = path = NULL;
-               }
-       }
-       if (!path) {
-               int32_t path_depth = depth + 1;
-               /* account possible depth increase */
-               path = calloc(1, sizeof(struct ext4_extent_path) *
-                                    (path_depth + 1));
-               if (!path)
-                       return ENOMEM;
-               path[0].maxdepth = path_depth;
-       }
-       path[0].header = eh;
-       path[0].block = bh;
-
-       i = depth;
-       /* walk through the tree */
-       while (i) {
-               ext4_ext_binsearch_idx(path + ppos, block);
-               path[ppos].p_block = ext4_idx_pblock(path[ppos].index);
-               path[ppos].depth = i;
-               path[ppos].extent = NULL;
-               buf_block = path[ppos].p_block;
-
-               i--;
-               ppos++;
-               if (!path[ppos].block.lb_id ||
-                   path[ppos].block.lb_id != buf_block) {
-                       ret = read_extent_tree_block(inode_ref, buf_block, i,
-                                                    &bh, flags);
-                       if (ret != EOK) {
-                               goto err;
-                       }
-                       if (ppos > depth) {
-                               ext4_block_set(inode_ref->fs->bdev, &bh);
-                               ret = EIO;
-                               goto err;
-                       }
-
-                       eh = ext_block_hdr(&bh);
-                       path[ppos].block = bh;
-                       path[ppos].header = eh;
-               }
-       }
-
-       path[ppos].depth = i;
-       path[ppos].extent = NULL;
-       path[ppos].index = NULL;
-
-       /* find extent */
-       ext4_ext_binsearch(path + ppos, block);
-       /* if not an empty leaf */
-       if (path[ppos].extent)
-               path[ppos].p_block = ext4_ext_pblock(path[ppos].extent);
-
-       *orig_path = path;
-
-       ret = EOK;
-       return ret;
-
-err:
-       ext4_ext_drop_refs(inode_ref, path, 0);
-       free(path);
-       if (orig_path)
-               *orig_path = NULL;
-       return ret;
-}
-
-static void ext4_ext_init_header(struct ext4_inode_ref *inode_ref,
-                                struct ext4_extent_header *eh, int32_t depth)
-{
-       eh->entries_count = 0;
-       eh->max_entries_count = to_le16(ext4_ext_max_entries(inode_ref, depth));
-       eh->magic = to_le16(EXT4_EXTENT_MAGIC);
-       eh->depth = depth;
-}
-
-/*
- * Be cautious, the buffer_head returned is not yet mark dirtied. */
-static int ext4_ext_split_node(struct ext4_inode_ref *inode_ref,
-                              struct ext4_extent_path *path, int32_t at,
-                              struct ext4_extent *newext,
-                              ext4_fsblk_t *sibling, struct ext4_block *new_bh)
-{
-       int ret;
-       ext4_fsblk_t newblock;
-       struct ext4_block bh = EXT4_BLOCK_ZERO();
-       int32_t depth = ext_depth(inode_ref->inode);
-
-       ext4_assert(sibling);
-
-       /* FIXME: currently we split at the point after the current extent. */
-       newblock = ext4_ext_new_meta_block(inode_ref, path, newext, &ret, 0);
-       if (ret)
-               goto cleanup;
-
-       /*  For write access.# */
-       ret = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, newblock);
-       if (ret != EOK)
-               goto cleanup;
-
-       if (at == depth) {
-               /* start copy from next extent */
-               ptrdiff_t m = EXT_MAX_EXTENT(path[at].header) - path[at].extent;
-               struct ext4_extent_header *neh;
-               neh = ext_block_hdr(&bh);
-               ext4_ext_init_header(inode_ref, neh, 0);
-               if (m) {
-                       struct ext4_extent *ex;
-                       ex = EXT_FIRST_EXTENT(neh);
-                       memmove(ex, path[at].extent + 1,
-                               sizeof(struct ext4_extent) * m);
-                       neh->entries_count =
-                           to_le16(to_le16(neh->entries_count) + m);
-                       path[at].header->entries_count = to_le16(
-                           to_le16(path[at].header->entries_count) - m);
-                       ret = ext4_ext_dirty(inode_ref, path + at);
-                       if (ret)
-                               goto cleanup;
-               }
-       } else {
-               ptrdiff_t m = EXT_MAX_INDEX(path[at].header) - path[at].index;
-               struct ext4_extent_header *neh;
-               neh = ext_block_hdr(&bh);
-               ext4_ext_init_header(inode_ref, neh, depth - at);
-               if (m) {
-                       struct ext4_extent_index *ix;
-                       ix = EXT_FIRST_INDEX(neh);
-                       memmove(ix, path[at].index + 1,
-                               sizeof(struct ext4_extent) * m);
-                       neh->entries_count =
-                           to_le16(to_le16(neh->entries_count) + m);
-                       path[at].header->entries_count = to_le16(
-                           to_le16(path[at].header->entries_count) - m);
-                       ret = ext4_ext_dirty(inode_ref, path + at);
-                       if (ret)
-                               goto cleanup;
-               }
-       }
-cleanup:
-       if (ret) {
-               if (bh.lb_id) {
-                       ext4_block_set(inode_ref->fs->bdev, &bh);
-               }
-               if (newblock)
-                       ext4_ext_free_blocks(inode_ref, newblock, 1, 0);
-
-               newblock = 0;
-       }
-       *sibling = newblock;
-       *new_bh = bh;
-       return ret;
-}
-
-static ext4_lblk_t ext4_ext_block_index(struct ext4_extent_header *eh)
-{
-       if (eh->depth)
-               return to_le32(EXT_FIRST_INDEX(eh)->first_block);
-
-       return to_le32(EXT_FIRST_EXTENT(eh)->first_block);
-}
-
-struct ext_split_trans {
-       ext4_fsblk_t ptr;
-       struct ext4_extent_path path;
-       int switch_to;
-};
-
-static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref,
-                                struct ext4_extent_path *path,
-                                int32_t at,
-                                struct ext4_extent *newext,
-                                ext4_lblk_t insert_index,
-                                ext4_fsblk_t insert_block,
-                                struct ext_split_trans *spt,
-                                bool *need_grow)
-{
-       struct ext4_extent_index *ix;
-       struct ext4_extent_path *curp = path + at;
-       struct ext4_block bh = EXT4_BLOCK_ZERO();
-       int32_t len;
-       int err;
-       struct ext4_extent_header *eh;
-
-       *need_grow = false;
-
-       if (curp->index && insert_index == to_le32(curp->index->first_block))
-               return EIO;
-
-       if (to_le16(curp->header->entries_count) ==
-           to_le16(curp->header->max_entries_count)) {
-               if (at) {
-                       struct ext4_extent_header *neh;
-                       err = ext4_ext_split_node(inode_ref, path, at, newext,
-                                                 &spt->ptr, &bh);
-                       if (err != EOK)
-                               goto out;
-
-                       neh = ext_block_hdr(&bh);
-                       if (insert_index > to_le32(curp->index->first_block)) {
-                               /* Make decision which node should be used to
-                                * insert the index.*/
-                               if (to_le16(neh->entries_count) >
-                                   to_le16(curp->header->entries_count)) {
-                                       eh = curp->header;
-                                       /* insert after */
-                                       ix = EXT_LAST_INDEX(eh) + 1;
-                               } else {
-                                       eh = neh;
-                                       ix = EXT_FIRST_INDEX(eh);
-                               }
-                       } else {
-                               eh = curp->header;
-                               /* insert before */
-                               ix = EXT_LAST_INDEX(eh);
-                       }
-               } else {
-                       err = EOK;
-                       *need_grow = true;
-                       goto out;
-               }
-       } else {
-               eh = curp->header;
-               if (curp->index == NULL) {
-                       ix = EXT_FIRST_INDEX(eh);
-                       curp->index = ix;
-               } else if (insert_index > to_le32(curp->index->first_block)) {
-                       /* insert after */
-                       ix = curp->index + 1;
-               } else {
-                       /* insert before */
-                       ix = curp->index;
-               }
-       }
-
-       len = EXT_LAST_INDEX(eh) - ix + 1;
-       ext4_assert(len >= 0);
-       if (len > 0)
-               memmove(ix + 1, ix, len * sizeof(struct ext4_extent_index));
-
-       if (ix > EXT_MAX_INDEX(eh)) {
-               err = EIO;
-               goto out;
-       }
-
-       ix->first_block = to_le32(insert_index);
-       ext4_idx_store_pblock(ix, insert_block);
-       eh->entries_count = to_le16(to_le16(eh->entries_count) + 1);
-
-       if (ix > EXT_LAST_INDEX(eh)) {
-               err = EIO;
-               goto out;
-       }
-
-       if (eh == curp->header)
-               err = ext4_ext_dirty(inode_ref, curp);
-       else
-               err = EOK;
-
-out:
-       if (err != EOK || *need_grow) {
-               if (bh.lb_id)
-                       ext4_block_set(inode_ref->fs->bdev, &bh);
-
-               spt->ptr = 0;
-       } else if (bh.lb_id) {
-               /* If we got a sibling leaf. */
-               ext4_extent_block_csum_set(inode_ref, ext_block_hdr(&bh));
-               ext4_trans_set_block_dirty(bh.buf);
-
-               spt->path.p_block = ext4_idx_pblock(ix);
-               spt->path.depth = to_le16(eh->depth);
-               spt->path.maxdepth = 0;
-               spt->path.extent = NULL;
-               spt->path.index = ix;
-               spt->path.header = eh;
-               spt->path.block = bh;
-
-               /*
-                * If newext->ee_block can be included into the
-                * right sub-tree.
-                */
-               if (to_le32(newext->first_block) >=
-                   ext4_ext_block_index(ext_block_hdr(&bh)))
-                       spt->switch_to = 1;
-               else {
-                       curp->index = ix;
-                       curp->p_block = ext4_idx_pblock(ix);
-               }
-
-       } else {
-               spt->ptr = 0;
-               curp->index = ix;
-               curp->p_block = ext4_idx_pblock(ix);
-       }
-       return err;
-}
-
-/*
- * ext4_ext_correct_indexes:
- * if leaf gets modified and modified extent is first in the leaf,
- * then we have to correct all indexes above.
- */
-static int ext4_ext_correct_indexes(struct ext4_inode_ref *inode_ref,
-                                   struct ext4_extent_path *path)
-{
-       struct ext4_extent_header *eh;
-       int32_t depth = ext_depth(inode_ref->inode);
-       struct ext4_extent *ex;
-       uint32_t border;
-       int32_t k;
-       int err = EOK;
-
-       eh = path[depth].header;
-       ex = path[depth].extent;
-
-       if (ex == NULL || eh == NULL)
-               return EIO;
-
-       if (depth == 0) {
-               /* there is no tree at all */
-               return EOK;
-       }
-
-       if (ex != EXT_FIRST_EXTENT(eh)) {
-               /* we correct tree if first leaf got modified only */
-               return EOK;
-       }
-
-       k = depth - 1;
-       border = path[depth].extent->first_block;
-       path[k].index->first_block = border;
-       err = ext4_ext_dirty(inode_ref, path + k);
-       if (err != EOK)
-               return err;
-
-       while (k--) {
-               /* change all left-side indexes */
-               if (path[k + 1].index != EXT_FIRST_INDEX(path[k + 1].header))
-                       break;
-               path[k].index->first_block = border;
-               err = ext4_ext_dirty(inode_ref, path + k);
-               if (err != EOK)
-                       break;
-       }
-
-       return err;
-}
-
-static bool ext4_ext_can_prepend(struct ext4_extent *ex1,
-                                struct ext4_extent *ex2)
-{
-       if (ext4_ext_pblock(ex2) + ext4_ext_get_actual_len(ex2) !=
-           ext4_ext_pblock(ex1))
-               return false;
-
-#ifdef AGGRESSIVE_TEST
-       if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4)
-               return 0;
-#else
-       if (ext4_ext_is_unwritten(ex1)) {
-               if (ext4_ext_get_actual_len(ex1) +
-                       ext4_ext_get_actual_len(ex2) >
-                   EXT_UNWRITTEN_MAX_LEN)
-                       return false;
-       } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) >
-                  EXT_INIT_MAX_LEN)
-               return false;
-#endif
-
-       if (to_le32(ex2->first_block) + ext4_ext_get_actual_len(ex2) !=
-           to_le32(ex1->first_block))
-               return false;
-
-       return true;
-}
-
-static bool ext4_ext_can_append(struct ext4_extent *ex1,
-                               struct ext4_extent *ex2)
-{
-       if (ext4_ext_pblock(ex1) + ext4_ext_get_actual_len(ex1) !=
-           ext4_ext_pblock(ex2))
-               return false;
-
-#ifdef AGGRESSIVE_TEST
-       if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4)
-               return 0;
-#else
-       if (ext4_ext_is_unwritten(ex1)) {
-               if (ext4_ext_get_actual_len(ex1) +
-                       ext4_ext_get_actual_len(ex2) >
-                   EXT_UNWRITTEN_MAX_LEN)
-                       return false;
-       } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) >
-                  EXT_INIT_MAX_LEN)
-               return false;
-#endif
-
-       if (to_le32(ex1->first_block) + ext4_ext_get_actual_len(ex1) !=
-           to_le32(ex2->first_block))
-               return false;
-
-       return true;
-}
-
-static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref,
-                               struct ext4_extent_path *path,
-                               int32_t at,
-                               struct ext4_extent *newext,
-                               struct ext_split_trans *spt,
-                               uint32_t flags,
-                               bool *need_grow)
-{
-       struct ext4_extent_path *curp = path + at;
-       struct ext4_extent *ex = curp->extent;
-       struct ext4_block bh = EXT4_BLOCK_ZERO();
-       int32_t len;
-       int err = EOK;
-       int unwritten;
-       struct ext4_extent_header *eh = NULL;
-
-       *need_grow = false;
-
-       if (curp->extent &&
-           to_le32(newext->first_block) == to_le32(curp->extent->first_block))
-               return EIO;
-
-       if (!(flags & EXT4_EXT_NO_COMBINE)) {
-               if (curp->extent && ext4_ext_can_append(curp->extent, newext)) {
-                       unwritten = ext4_ext_is_unwritten(curp->extent);
-                       curp->extent->block_count =
-                           to_le16(ext4_ext_get_actual_len(curp->extent) +
-                                   ext4_ext_get_actual_len(newext));
-                       if (unwritten)
-                               ext4_ext_mark_unwritten(curp->extent);
-                       err = ext4_ext_dirty(inode_ref, curp);
-                       goto out;
-               }
-
-               if (curp->extent &&
-                   ext4_ext_can_prepend(curp->extent, newext)) {
-                       unwritten = ext4_ext_is_unwritten(curp->extent);
-                       curp->extent->first_block = newext->first_block;
-                       curp->extent->block_count =
-                           to_le16(ext4_ext_get_actual_len(curp->extent) +
-                                   ext4_ext_get_actual_len(newext));
-                       if (unwritten)
-                               ext4_ext_mark_unwritten(curp->extent);
-                       err = ext4_ext_dirty(inode_ref, curp);
-                       goto out;
-               }
-       }
-
-       if (to_le16(curp->header->entries_count) ==
-           to_le16(curp->header->max_entries_count)) {
-               if (at) {
-                       struct ext4_extent_header *neh;
-                       err = ext4_ext_split_node(inode_ref, path, at, newext,
-                                                 &spt->ptr, &bh);
-                       if (err != EOK)
-                               goto out;
-
-                       neh = ext_block_hdr(&bh);
-                       if (to_le32(newext->first_block) >
-                           to_le32(curp->extent->first_block)) {
-                               if (to_le16(neh->entries_count) >
-                                   to_le16(curp->header->entries_count)) {
-                                       eh = curp->header;
-                                       /* insert after */
-                                       ex = EXT_LAST_EXTENT(eh) + 1;
-                               } else {
-                                       eh = neh;
-                                       ex = EXT_FIRST_EXTENT(eh);
-                               }
-                       } else {
-                               eh = curp->header;
-                               /* insert before */
-                               ex = EXT_LAST_EXTENT(eh);
-                       }
-               } else {
-                       err = EOK;
-                       *need_grow = true;
-                       goto out;
-               }
-       } else {
-               eh = curp->header;
-               if (curp->extent == NULL) {
-                       ex = EXT_FIRST_EXTENT(eh);
-                       curp->extent = ex;
-               } else if (to_le32(newext->first_block) >
-                          to_le32(curp->extent->first_block)) {
-                       /* insert after */
-                       ex = curp->extent + 1;
-               } else {
-                       /* insert before */
-                       ex = curp->extent;
-               }
-       }
-
-       len = EXT_LAST_EXTENT(eh) - ex + 1;
-       ext4_assert(len >= 0);
-       if (len > 0)
-               memmove(ex + 1, ex, len * sizeof(struct ext4_extent));
-
-       if (ex > EXT_MAX_EXTENT(eh)) {
-               err = EIO;
-               goto out;
-       }
-
-       ex->first_block = newext->first_block;
-       ex->block_count = newext->block_count;
-       ext4_ext_store_pblock(ex, ext4_ext_pblock(newext));
-       eh->entries_count = to_le16(to_le16(eh->entries_count) + 1);
-
-       if (ex > EXT_LAST_EXTENT(eh)) {
-               err = EIO;
-               goto out;
-       }
-
-       if (eh == curp->header) {
-               err = ext4_ext_correct_indexes(inode_ref, path);
-               if (err != EOK)
-                       goto out;
-               err = ext4_ext_dirty(inode_ref, curp);
-       } else
-               err = EOK;
-
-out:
-       if (err != EOK || *need_grow) {
-               if (bh.lb_id)
-                       ext4_block_set(inode_ref->fs->bdev, &bh);
-
-               spt->ptr = 0;
-       } else if (bh.lb_id) {
-               /* If we got a sibling leaf. */
-               ext4_extent_block_csum_set(inode_ref, ext_block_hdr(&bh));
-               ext4_trans_set_block_dirty(bh.buf);
-
-               spt->path.p_block = ext4_ext_pblock(ex);
-               spt->path.depth = to_le16(eh->depth);
-               spt->path.maxdepth = 0;
-               spt->path.extent = ex;
-               spt->path.index = NULL;
-               spt->path.header = eh;
-               spt->path.block = bh;
-
-               /*
-                * If newext->ee_block can be included into the
-                * right sub-tree.
-                */
-               if (to_le32(newext->first_block) >=
-                   ext4_ext_block_index(ext_block_hdr(&bh)))
-                       spt->switch_to = 1;
-               else {
-                       curp->extent = ex;
-                       curp->p_block = ext4_ext_pblock(ex);
-               }
-
-       } else {
-               spt->ptr = 0;
-               curp->extent = ex;
-               curp->p_block = ext4_ext_pblock(ex);
-       }
-
-       return err;
-}
-
-/*
- * ext4_ext_grow_indepth:
- * implements tree growing procedure:
- * - allocates new block
- * - moves top-level data (index block or leaf) into the new block
- * - initializes new top-level, creating index that points to the
- *   just created block
- */
-static int ext4_ext_grow_indepth(struct ext4_inode_ref *inode_ref,
-                                uint32_t flags)
-{
-       struct ext4_extent_header *neh;
-       struct ext4_block bh = EXT4_BLOCK_ZERO();
-       ext4_fsblk_t newblock, goal = 0;
-       int err = EOK;
-
-       /* Try to prepend new index to old one */
-       if (ext_depth(inode_ref->inode))
-               goal = ext4_idx_pblock(
-                   EXT_FIRST_INDEX(ext_inode_hdr(inode_ref->inode)));
-       else
-               goal = ext4_fs_inode_to_goal_block(inode_ref);
-
-       newblock = ext4_new_meta_blocks(inode_ref, goal, flags, NULL, &err);
-       if (newblock == 0)
-               return err;
-
-       /* # */
-       err = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, newblock);
-       if (err != EOK) {
-               ext4_ext_free_blocks(inode_ref, newblock, 1, 0);
-               return err;
-       }
-
-       /* move top-level index/leaf into new block */
-       memmove(bh.data, inode_ref->inode->blocks,
-               sizeof(inode_ref->inode->blocks));
-
-       /* set size of new block */
-       neh = ext_block_hdr(&bh);
-       /* old root could have indexes or leaves
-        * so calculate e_max right way */
-       if (ext_depth(inode_ref->inode))
-               neh->max_entries_count =
-                   to_le16(ext4_ext_space_block_idx(inode_ref));
-       else
-               neh->max_entries_count =
-                   to_le16(ext4_ext_space_block(inode_ref));
-
-       neh->magic = to_le16(EXT4_EXTENT_MAGIC);
-       ext4_extent_block_csum_set(inode_ref, neh);
-
-       /* Update top-level index: num,max,pointer */
-       neh = ext_inode_hdr(inode_ref->inode);
-       neh->entries_count = to_le16(1);
-       ext4_idx_store_pblock(EXT_FIRST_INDEX(neh), newblock);
-       if (neh->depth == 0) {
-               /* Root extent block becomes index block */
-               neh->max_entries_count =
-                   to_le16(ext4_ext_space_root_idx(inode_ref));
-               EXT_FIRST_INDEX(neh)
-                   ->first_block = EXT_FIRST_EXTENT(neh)->first_block;
-       }
-       neh->depth = to_le16(to_le16(neh->depth) + 1);
-
-       ext4_trans_set_block_dirty(bh.buf);
-       inode_ref->dirty = true;
-       ext4_block_set(inode_ref->fs->bdev, &bh);
-
-       return err;
-}
-
-__unused static void print_path(struct ext4_extent_path *path)
-{
-       int32_t i = path->depth;
-       while (i >= 0) {
-
-               ptrdiff_t a =
-                   (path->extent)
-                       ? (path->extent - EXT_FIRST_EXTENT(path->header))
-                       : 0;
-               ptrdiff_t b =
-                   (path->index)
-                       ? (path->index - EXT_FIRST_INDEX(path->header))
-                       : 0;
-
-               (void)a;
-               (void)b;
-               ext4_dbg(DEBUG_EXTENT,
-                        "depth %" PRId32 ", p_block: %" PRIu64 ","
-                        "p_ext offset: %td, p_idx offset: %td\n",
-                        i, path->p_block, a, b);
-               i--;
-               path++;
-       }
-}
-
-static void ext4_ext_replace_path(struct ext4_inode_ref *inode_ref,
-                                 struct ext4_extent_path *path,
-                                 struct ext_split_trans *spt,
-                                 int32_t level)
-{
-       int32_t depth = ext_depth(inode_ref->inode);
-       int32_t i = depth - level;
-       ext4_ext_drop_refs(inode_ref, path + i, 1);
-       path[i] = spt[level].path;
-}
-
-static int ext4_ext_insert_extent(struct ext4_inode_ref *inode_ref,
-                                 struct ext4_extent_path **ppath,
-                                 struct ext4_extent *newext, uint32_t flags)
-{
-       int32_t i, depth, level;
-       int ret = EOK;
-       ext4_fsblk_t ptr = 0;
-       bool need_grow = false;
-       struct ext4_extent_path *path = *ppath;
-       struct ext_split_trans *spt = NULL;
-       struct ext_split_trans newblock;
-
-       memset(&newblock, 0, sizeof(newblock));
-
-       depth = ext_depth(inode_ref->inode);
-       for (i = depth, level = 0; i >= 0; i--, level++)
-               if (EXT_HAS_FREE_INDEX(path + i))
-                       break;
-
-       if (level) {
-               spt = calloc(1, sizeof(struct ext_split_trans) * (level));
-               if (!spt) {
-                       ret = ENOMEM;
-                       goto out;
-               }
-       }
-       i = 0;
-again:
-       depth = ext_depth(inode_ref->inode);
-
-       do {
-               if (!i) {
-                       ret = ext4_ext_insert_leaf(inode_ref, path, depth - i,
-                                                  newext, &newblock, flags,
-                                                  &need_grow);
-               } else {
-                       ret = ext4_ext_insert_index(
-                           inode_ref, path, depth - i, newext,
-                           ext4_ext_block_index(
-                               ext_block_hdr(&spt[i - 1].path.block)),
-                           spt[i - 1].ptr, &newblock,
-                           &need_grow);
-               }
-               ptr = newblock.ptr;
-
-               if (ret != EOK)
-                       goto out;
-
-               else if (spt && ptr && !ret) {
-                       /* Prepare for the next iteration after splitting. */
-                       spt[i] = newblock;
-               }
-
-               i++;
-       } while (ptr != 0 && i <= depth);
-
-       if (need_grow) {
-               ret = ext4_ext_grow_indepth(inode_ref, 0);
-               if (ret)
-                       goto out;
-               ret = ext4_find_extent(inode_ref, to_le32(newext->first_block),
-                                      ppath, 0);
-               if (ret)
-                       goto out;
-               i = depth;
-               path = *ppath;
-               goto again;
-       }
-out:
-       if (ret) {
-               if (path)
-                       ext4_ext_drop_refs(inode_ref, path, 0);
-
-               while (--level >= 0 && spt) {
-                       if (spt[level].ptr) {
-                               ext4_ext_free_blocks(inode_ref, spt[level].ptr,
-                                                    1, 0);
-                               ext4_ext_drop_refs(inode_ref, &spt[level].path,
-                                                  1);
-                       }
-               }
-       } else {
-               while (--level >= 0 && spt) {
-                       if (spt[level].switch_to)
-                               ext4_ext_replace_path(inode_ref, path, spt,
-                                                     level);
-                       else if (spt[level].ptr)
-                               ext4_ext_drop_refs(inode_ref, &spt[level].path,
-                                                  1);
-               }
-       }
-       if (spt)
-               free(spt);
-
-       return ret;
-}
-
-static void ext4_ext_remove_blocks(struct ext4_inode_ref *inode_ref,
-                                  struct ext4_extent *ex, ext4_lblk_t from,
-                                  ext4_lblk_t to)
-{
-       ext4_lblk_t len = to - from + 1;
-       ext4_lblk_t num;
-       ext4_fsblk_t start;
-       num = from - to_le32(ex->first_block);
-       start = ext4_ext_pblock(ex) + num;
-       ext4_dbg(DEBUG_EXTENT,
-                "Freeing %" PRIu32 " at %" PRIu64 ", %" PRIu32 "\n", from,
-                start, len);
-
-       ext4_ext_free_blocks(inode_ref, start, len, 0);
-}
-
-static int ext4_ext_remove_idx(struct ext4_inode_ref *inode_ref,
-                              struct ext4_extent_path *path, int32_t depth)
-{
-       int err = EOK;
-       int32_t i = depth;
-       ext4_fsblk_t leaf;
-
-       /* free index block */
-       leaf = ext4_idx_pblock(path[i].index);
-
-       if (path[i].index != EXT_LAST_INDEX(path[i].header)) {
-               ptrdiff_t len = EXT_LAST_INDEX(path[i].header) - path[i].index;
-               memmove(path[i].index, path[i].index + 1,
-                       len * sizeof(struct ext4_extent_index));
-       }
-
-       path[i].header->entries_count =
-           to_le16(to_le16(path[i].header->entries_count) - 1);
-       err = ext4_ext_dirty(inode_ref, path + i);
-       if (err != EOK)
-               return err;
-
-       ext4_dbg(DEBUG_EXTENT, "IDX: Freeing %" PRIu32 " at %" PRIu64 ", %d\n",
-                to_le32(path[i].index->first_block), leaf, 1);
-       ext4_ext_free_blocks(inode_ref, leaf, 1, 0);
-
-       while (i > 0) {
-               if (path[i].index != EXT_FIRST_INDEX(path[i].header))
-                       break;
-
-               path[i - 1].index->first_block = path[i].index->first_block;
-               err = ext4_ext_dirty(inode_ref, path + i - 1);
-               if (err != EOK)
-                       break;
-
-               i--;
-       }
-       return err;
-}
-
-static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref,
-                               struct ext4_extent_path *path, ext4_lblk_t from,
-                               ext4_lblk_t to)
-{
-
-       int32_t depth = ext_depth(inode_ref->inode);
-       struct ext4_extent *ex = path[depth].extent;
-       struct ext4_extent *start_ex, *ex2 = NULL;
-       struct ext4_extent_header *eh = path[depth].header;
-       int32_t len;
-       int err = EOK;
-       uint16_t new_entries;
-
-       start_ex = ex;
-       new_entries = to_le16(eh->entries_count);
-       while (ex <= EXT_LAST_EXTENT(path[depth].header) &&
-              to_le32(ex->first_block) <= to) {
-               int32_t new_len = 0;
-               int unwritten;
-               ext4_lblk_t start, new_start;
-               ext4_fsblk_t newblock;
-               new_start = start = to_le32(ex->first_block);
-               len = ext4_ext_get_actual_len(ex);
-               newblock = ext4_ext_pblock(ex);
-               if (start < from) {
-                       len -= from - start;
-                       new_len = from - start;
-                       start = from;
-                       start_ex++;
-               } else {
-                       if (start + len - 1 > to) {
-                               len -= start + len - 1 - to;
-                               new_len = start + len - 1 - to;
-                               new_start = to + 1;
-                               newblock += to + 1 - start;
-                               ex2 = ex;
-                       }
-               }
-
-               ext4_ext_remove_blocks(inode_ref, ex, start, start + len - 1);
-               ex->first_block = to_le32(new_start);
-               if (!new_len)
-                       new_entries--;
-               else {
-                       unwritten = ext4_ext_is_unwritten(ex);
-                       ex->block_count = to_le16(new_len);
-                       ext4_ext_store_pblock(ex, newblock);
-                       if (unwritten)
-                               ext4_ext_mark_unwritten(ex);
-               }
-
-               ex += 1;
-       }
-
-       if (ex2 == NULL)
-               ex2 = ex;
-
-       if (ex2 <= EXT_LAST_EXTENT(eh))
-               memmove(start_ex, ex2, EXT_LAST_EXTENT(eh) - ex2 + 1);
-
-       eh->entries_count = to_le16(new_entries);
-       ext4_ext_dirty(inode_ref, path + depth);
-       if (path[depth].extent == EXT_FIRST_EXTENT(eh) && eh->entries_count)
-               err = ext4_ext_correct_indexes(inode_ref, path);
-
-       /* if this leaf is free, then we should
-        * remove it from index block above */
-       if (err == EOK && eh->entries_count == 0 && path[depth].block.lb_id)
-               err = ext4_ext_remove_idx(inode_ref, path, depth - 1);
-       else if (depth > 0)
-               path[depth - 1].index++;
-
-       return err;
-}
-
-static bool ext4_ext_more_to_rm(struct ext4_extent_path *path, ext4_lblk_t to)
-{
-       if (!to_le16(path->header->entries_count))
-               return false;
-
-       if (path->index > EXT_LAST_INDEX(path->header))
-               return false;
-
-       if (to_le32(path->index->first_block) > to)
-               return false;
-
-       return true;
-}
-
-int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
-                         ext4_lblk_t to)
-{
-       struct ext4_extent_path *path = NULL;
-       int ret = EOK;
-       int32_t depth = ext_depth(inode_ref->inode);
-       int32_t i;
-
-       ret = ext4_find_extent(inode_ref, from, &path, 0);
-       if (ret)
-               goto out;
-
-       if (!path[depth].extent) {
-               ret = EOK;
-               goto out;
-       }
-
-       bool in_range = IN_RANGE(from, to_le32(path[depth].extent->first_block),
-                       ext4_ext_get_actual_len(path[depth].extent));
-
-       if (!in_range) {
-               ret = EOK;
-               goto out;
-       }
-
-       /* If we do remove_space inside the range of an extent */
-       if ((to_le32(path[depth].extent->first_block) < from) &&
-           (to < to_le32(path[depth].extent->first_block) +
-                       ext4_ext_get_actual_len(path[depth].extent) - 1)) {
-
-               struct ext4_extent *ex = path[depth].extent, newex;
-               int unwritten = ext4_ext_is_unwritten(ex);
-               ext4_lblk_t ee_block = to_le32(ex->first_block);
-               int32_t len = ext4_ext_get_actual_len(ex);
-               ext4_fsblk_t newblock =
-                       to + 1 - ee_block + ext4_ext_pblock(ex);
-
-               ex->block_count = to_le16(from - ee_block);
-               if (unwritten)
-                       ext4_ext_mark_unwritten(ex);
-
-               ext4_ext_dirty(inode_ref, path + depth);
-
-               newex.first_block = to_le32(to + 1);
-               newex.block_count = to_le16(ee_block + len - 1 - to);
-               ext4_ext_store_pblock(&newex, newblock);
-               if (unwritten)
-                       ext4_ext_mark_unwritten(&newex);
-
-               ret = ext4_ext_insert_extent(inode_ref, &path, &newex, 0);
-               goto out;
-       }
-
-       i = depth;
-       while (i >= 0) {
-               if (i == depth) {
-                       struct ext4_extent_header *eh;
-                       struct ext4_extent *first_ex, *last_ex;
-                       ext4_lblk_t leaf_from, leaf_to;
-                       eh = path[i].header;
-                       ext4_assert(to_le16(eh->entries_count) > 0);
-                       first_ex = EXT_FIRST_EXTENT(eh);
-                       last_ex = EXT_LAST_EXTENT(eh);
-                       leaf_from = to_le32(first_ex->first_block);
-                       leaf_to = to_le32(last_ex->first_block) +
-                                 ext4_ext_get_actual_len(last_ex) - 1;
-                       if (leaf_from < from)
-                               leaf_from = from;
-
-                       if (leaf_to > to)
-                               leaf_to = to;
-
-                       ext4_ext_remove_leaf(inode_ref, path, leaf_from,
-                                       leaf_to);
-                       ext4_ext_drop_refs(inode_ref, path + i, 0);
-                       i--;
-                       continue;
-               }
-
-               struct ext4_extent_header *eh;
-               eh = path[i].header;
-               if (ext4_ext_more_to_rm(path + i, to)) {
-                       struct ext4_block bh = EXT4_BLOCK_ZERO();
-                       if (path[i + 1].block.lb_id)
-                               ext4_ext_drop_refs(inode_ref, path + i + 1, 0);
-
-                       ret = read_extent_tree_block(inode_ref,
-                                       ext4_idx_pblock(path[i].index),
-                                       depth - i - 1, &bh, 0);
-                       if (ret)
-                               goto out;
-
-                       path[i].p_block =
-                                       ext4_idx_pblock(path[i].index);
-                       path[i + 1].block = bh;
-                       path[i + 1].header = ext_block_hdr(&bh);
-                       path[i + 1].depth = depth - i - 1;
-                       if (i + 1 == depth)
-                               path[i + 1].extent = EXT_FIRST_EXTENT(
-                                       path[i + 1].header);
-                       else
-                               path[i + 1].index =
-                                       EXT_FIRST_INDEX(path[i + 1].header);
-
-                       i++;
-               } else {
-                       if (i > 0) {
-                               if (!eh->entries_count)
-                                       ret = ext4_ext_remove_idx(inode_ref, path,
-                                                       i - 1);
-                               else
-                                       path[i - 1].index++;
-
-                       }
-
-                       if (i)
-                               ext4_block_set(inode_ref->fs->bdev,
-                                               &path[i].block);
-
-
-                       i--;
-               }
-
-       }
-
-       /* TODO: flexible tree reduction should be here */
-       if (path->header->entries_count == 0) {
-               /*
-                * truncate to zero freed all the tree,
-                * so we need to correct eh_depth
-                */
-               ext_inode_hdr(inode_ref->inode)->depth = 0;
-               ext_inode_hdr(inode_ref->inode)->max_entries_count =
-                   to_le16(ext4_ext_space_root(inode_ref));
-               ret = ext4_ext_dirty(inode_ref, path);
-       }
-
-out:
-       ext4_ext_drop_refs(inode_ref, path, 0);
-       free(path);
-       path = NULL;
-       return ret;
-}
-
-static int ext4_ext_split_extent_at(struct ext4_inode_ref *inode_ref,
-                                   struct ext4_extent_path **ppath,
-                                   ext4_lblk_t split, uint32_t split_flag)
-{
-       struct ext4_extent *ex, newex;
-       ext4_fsblk_t newblock;
-       ext4_lblk_t ee_block;
-       int32_t ee_len;
-       int32_t depth = ext_depth(inode_ref->inode);
-       int err = EOK;
-
-       ex = (*ppath)[depth].extent;
-       ee_block = to_le32(ex->first_block);
-       ee_len = ext4_ext_get_actual_len(ex);
-       newblock = split - ee_block + ext4_ext_pblock(ex);
-
-       if (split == ee_block) {
-               /*
-                * case b: block @split is the block that the extent begins with
-                * then we just change the state of the extent, and splitting
-                * is not needed.
-                */
-               if (split_flag & EXT4_EXT_MARK_UNWRIT2)
-                       ext4_ext_mark_unwritten(ex);
-               else
-                       ext4_ext_mark_initialized(ex);
-
-               err = ext4_ext_dirty(inode_ref, *ppath + depth);
-               goto out;
-       }
-
-       ex->block_count = to_le16(split - ee_block);
-       if (split_flag & EXT4_EXT_MARK_UNWRIT1)
-               ext4_ext_mark_unwritten(ex);
-
-       err = ext4_ext_dirty(inode_ref, *ppath + depth);
-       if (err != EOK)
-               goto out;
-
-       newex.first_block = to_le32(split);
-       newex.block_count = to_le16(ee_len - (split - ee_block));
-       ext4_ext_store_pblock(&newex, newblock);
-       if (split_flag & EXT4_EXT_MARK_UNWRIT2)
-               ext4_ext_mark_unwritten(&newex);
-       err = ext4_ext_insert_extent(inode_ref, ppath, &newex,
-                                    EXT4_EXT_NO_COMBINE);
-       if (err != EOK)
-               goto restore_extent_len;
-
-out:
-       return err;
-restore_extent_len:
-       ex->block_count = to_le16(ee_len);
-       err = ext4_ext_dirty(inode_ref, *ppath + depth);
-       return err;
-}
-
-static int ext4_ext_convert_to_initialized(struct ext4_inode_ref *inode_ref,
-                                          struct ext4_extent_path **ppath,
-                                          ext4_lblk_t split, uint32_t blocks)
-{
-       int32_t depth = ext_depth(inode_ref->inode), err = EOK;
-       struct ext4_extent *ex = (*ppath)[depth].extent;
-
-       ext4_assert(to_le32(ex->first_block) <= split);
-
-       if (split + blocks ==
-           to_le32(ex->first_block) + ext4_ext_get_actual_len(ex)) {
-               /* split and initialize right part */
-               err = ext4_ext_split_extent_at(inode_ref, ppath, split,
-                                              EXT4_EXT_MARK_UNWRIT1);
-       } else if (to_le32(ex->first_block) == split) {
-               /* split and initialize left part */
-               err = ext4_ext_split_extent_at(inode_ref, ppath, split + blocks,
-                                              EXT4_EXT_MARK_UNWRIT2);
-       } else {
-               /* split 1 extent to 3 and initialize the 2nd */
-               err = ext4_ext_split_extent_at(inode_ref, ppath, split + blocks,
-                                              EXT4_EXT_MARK_UNWRIT1 |
-                                                  EXT4_EXT_MARK_UNWRIT2);
-               if (!err) {
-                       err = ext4_ext_split_extent_at(inode_ref, ppath, split,
-                                                      EXT4_EXT_MARK_UNWRIT1);
-               }
-       }
-
-       return err;
-}
-
-static ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_extent_path *path)
-{
-       int32_t depth;
-
-       depth = path->depth;
-
-       if (depth == 0 && path->extent == NULL)
-               return EXT_MAX_BLOCKS;
-
-       while (depth >= 0) {
-               if (depth == path->depth) {
-                       /* leaf */
-                       if (path[depth].extent &&
-                           path[depth].extent !=
-                               EXT_LAST_EXTENT(path[depth].header))
-                               return to_le32(
-                                   path[depth].extent[1].first_block);
-               } else {
-                       /* index */
-                       if (path[depth].index !=
-                           EXT_LAST_INDEX(path[depth].header))
-                               return to_le32(
-                                   path[depth].index[1].first_block);
-               }
-               depth--;
-       }
-
-       return EXT_MAX_BLOCKS;
-}
-
-static int ext4_ext_zero_unwritten_range(struct ext4_inode_ref *inode_ref,
-                                        ext4_fsblk_t block,
-                                        uint32_t blocks_count)
-{
-       int err = EOK;
-       uint32_t i;
-       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
-       for (i = 0; i < blocks_count; i++) {
-               struct ext4_block bh = EXT4_BLOCK_ZERO();
-               err = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, block + i);
-               if (err != EOK)
-                       break;
-
-               memset(bh.data, 0, block_size);
-               ext4_trans_set_block_dirty(bh.buf);
-               err = ext4_block_set(inode_ref->fs->bdev, &bh);
-               if (err != EOK)
-                       break;
-       }
-       return err;
-}
-
-int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_lblk_t iblock,
-                       uint32_t max_blocks, ext4_fsblk_t *result, bool create,
-                       uint32_t *blocks_count)
-{
-       struct ext4_extent_path *path = NULL;
-       struct ext4_extent newex, *ex;
-       ext4_fsblk_t goal;
-       int err = EOK;
-       int32_t depth;
-       uint32_t allocated = 0;
-       ext4_lblk_t next;
-       ext4_fsblk_t newblock;
-
-       if (result)
-               *result = 0;
-
-       if (blocks_count)
-               *blocks_count = 0;
-
-       /* find extent for this block */
-       err = ext4_find_extent(inode_ref, iblock, &path, 0);
-       if (err != EOK) {
-               path = NULL;
-               goto out2;
-       }
-
-       depth = ext_depth(inode_ref->inode);
-
-       /*
-        * consistent leaf must not be empty
-        * this situations is possible, though, _during_ tree modification
-        * this is why assert can't be put in ext4_ext_find_extent()
-        */
-       ex = path[depth].extent;
-       if (ex) {
-               ext4_lblk_t ee_block = to_le32(ex->first_block);
-               ext4_fsblk_t ee_start = ext4_ext_pblock(ex);
-               uint16_t ee_len = ext4_ext_get_actual_len(ex);
-               /* if found exent covers block, simple return it */
-               if (IN_RANGE(iblock, ee_block, ee_len)) {
-                       /* number of remain blocks in the extent */
-                       allocated = ee_len - (iblock - ee_block);
-
-                       if (!ext4_ext_is_unwritten(ex)) {
-                               newblock = iblock - ee_block + ee_start;
-                               goto out;
-                       }
-
-                       if (!create) {
-                               newblock = 0;
-                               goto out;
-                       }
-
-                       uint32_t zero_range;
-                       zero_range = allocated;
-                       if (zero_range > max_blocks)
-                               zero_range = max_blocks;
-
-                       newblock = iblock - ee_block + ee_start;
-                       err = ext4_ext_zero_unwritten_range(inode_ref, newblock,
-                                       zero_range);
-                       if (err != EOK)
-                               goto out2;
-
-                       err = ext4_ext_convert_to_initialized(inode_ref, &path,
-                                       iblock, zero_range);
-                       if (err != EOK)
-                               goto out2;
-
-                       goto out;
-               }
-       }
-
-       /*
-        * requested block isn't allocated yet
-        * we couldn't try to create block if create flag is zero
-        */
-       if (!create) {
-               goto out2;
-       }
-
-       /* find next allocated block so that we know how many
-        * blocks we can allocate without ovelapping next extent */
-       next = ext4_ext_next_allocated_block(path);
-       allocated = next - iblock;
-       if (allocated > max_blocks)
-               allocated = max_blocks;
-
-       /* allocate new block */
-       goal = ext4_ext_find_goal(inode_ref, path, iblock);
-       newblock = ext4_new_meta_blocks(inode_ref, goal, 0, &allocated, &err);
-       if (!newblock)
-               goto out2;
-
-       /* try to insert new extent into found leaf and return */
-       newex.first_block = to_le32(iblock);
-       ext4_ext_store_pblock(&newex, newblock);
-       newex.block_count = to_le16(allocated);
-       err = ext4_ext_insert_extent(inode_ref, &path, &newex, 0);
-       if (err != EOK) {
-               /* free data blocks we just allocated */
-               ext4_ext_free_blocks(inode_ref, ext4_ext_pblock(&newex),
-                                    to_le16(newex.block_count), 0);
-               goto out2;
-       }
-
-       /* previous routine could use block we allocated */
-       newblock = ext4_ext_pblock(&newex);
-
-out:
-       if (allocated > max_blocks)
-               allocated = max_blocks;
-
-       if (result)
-               *result = newblock;
-
-       if (blocks_count)
-               *blocks_count = allocated;
-
-out2:
-       if (path) {
-               ext4_ext_drop_refs(inode_ref, path, 0);
-               free(path);
-       }
-
-       return err;
-}
diff --git a/lwext4/ext4_extent.h b/lwext4/ext4_extent.h
deleted file mode 100644 (file)
index 1d4ddf1..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_inode.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 */
-static inline uint32_t ext4_extent_get_first_block(struct ext4_extent *extent)
-{
-       return to_le32(extent->first_block);
-}
-
-/**@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 */
-static inline void ext4_extent_set_first_block(struct ext4_extent *extent,
-               uint32_t iblock)
-{
-       extent->first_block = to_le32(iblock);
-}
-
-/**@brief Get number of blocks covered by extent.
- * @param extent Extent to load count from
- * @return Number of blocks covered by extent */
-static inline uint16_t ext4_extent_get_block_count(struct ext4_extent *extent)
-{
-       if (EXT4_EXT_IS_UNWRITTEN(extent))
-               return EXT4_EXT_GET_LEN_UNWRITTEN(extent);
-       else
-               return EXT4_EXT_GET_LEN(extent);
-}
-/**@brief Set number of blocks covered by extent.
- * @param extent Extent to load count from
- * @param count  Number of blocks covered by extent
- * @param unwritten Whether the extent is unwritten or not */
-static inline void ext4_extent_set_block_count(struct ext4_extent *extent,
-                                              uint16_t count, bool unwritten)
-{
-       EXT4_EXT_SET_LEN(extent, count);
-       if (unwritten)
-               EXT4_EXT_SET_UNWRITTEN(extent);
-}
-
-/**@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 */
-static inline 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));
-}
-
-
-/**@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 */
-static inline 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));
-}
-
-
-/**@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 */
-static inline uint32_t
-ext4_extent_index_get_first_block(struct ext4_extent_index *index)
-{
-       return to_le32(index->first_block);
-}
-
-/**@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 */
-static inline void
-ext4_extent_index_set_first_block(struct ext4_extent_index *index,
-                                 uint32_t iblock)
-{
-       index->first_block = to_le32(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 */
-static inline 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));
-}
-
-/**@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 */
-static inline 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));
-}
-
-/**@brief Get magic value from extent header.
- * @param header Extent header to load value from
- * @return Magic value of extent header */
-static inline uint16_t
-ext4_extent_header_get_magic(struct ext4_extent_header *header)
-{
-       return to_le16(header->magic);
-}
-
-/**@brief Set magic value to extent header.
- * @param header Extent header to set value to
- * @param magic  Magic value of extent header */
-static inline void ext4_extent_header_set_magic(struct ext4_extent_header *header,
-                                               uint16_t magic)
-{
-       header->magic = to_le16(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 */
-static inline uint16_t
-ext4_extent_header_get_entries_count(struct ext4_extent_header *header)
-{
-       return to_le16(header->entries_count);
-}
-
-/**@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 */
-static inline void
-ext4_extent_header_set_entries_count(struct ext4_extent_header *header,
-                                    uint16_t count)
-{
-       header->entries_count = to_le16(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 */
-static inline uint16_t
-ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header)
-{
-       return to_le16(header->max_entries_count);
-}
-
-/**@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 */
-static inline 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);
-}
-
-/**@brief Get depth of extent subtree.
- * @param header Extent header to get value from
- * @return Depth of extent subtree */
-static inline uint16_t
-ext4_extent_header_get_depth(struct ext4_extent_header *header)
-{
-       return to_le16(header->depth);
-}
-
-/**@brief Set depth of extent subtree.
- * @param header Extent header to set value to
- * @param depth  Depth of extent subtree */
-static inline void
-ext4_extent_header_set_depth(struct ext4_extent_header *header, uint16_t depth)
-{
-       header->depth = to_le16(depth);
-}
-
-/**@brief Get generation from extent header
- * @param header Extent header to get value from
- * @return Generation */
-static inline uint32_t
-ext4_extent_header_get_generation(struct ext4_extent_header *header)
-{
-       return to_le32(header->generation);
-}
-
-/**@brief Set generation to extent header
- * @param header     Extent header to set value to
- * @param generation Generation */
-static inline void
-ext4_extent_header_set_generation(struct ext4_extent_header *header,
-                                      uint32_t generation)
-{
-       header->generation = to_le32(generation);
-}
-
-/******************************************************************************/
-
-/**TODO:  */
-static inline void ext4_extent_tree_init(struct ext4_inode_ref *inode_ref)
-{
-       /* Initialize extent root header */
-       struct ext4_extent_header *header =
-                       ext4_inode_get_extent_header(inode_ref->inode);
-       ext4_extent_header_set_depth(header, 0);
-       ext4_extent_header_set_entries_count(header, 0);
-       ext4_extent_header_set_generation(header, 0);
-       ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
-
-       uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
-                       sizeof(struct ext4_extent_header)) /
-                                       sizeof(struct ext4_extent);
-
-       ext4_extent_header_set_max_entries_count(header, max_entries);
-       inode_ref->dirty  = true;
-}
-
-
-
-/**TODO:  */
-int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_lblk_t iblock,
-                          uint32_t max_blocks, ext4_fsblk_t *result, bool create,
-                          uint32_t *blocks_count);
-
-
-/**@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_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
-                            ext4_lblk_t to);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_EXTENT_H_ */
-/**
-* @}
-*/
diff --git a/lwext4/ext4_fs.c b/lwext4/ext4_fs.c
deleted file mode 100644 (file)
index 392728d..0000000
+++ /dev/null
@@ -1,1713 +0,0 @@
-/*
- * 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_fs.c
- * @brief More complex filesystem functions.
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_fs.h"
-#include "ext4_errno.h"
-#include "ext4_blockdev.h"
-#include "ext4_super.h"
-#include "ext4_crc32.h"
-#include "ext4_debug.h"
-#include "ext4_block_group.h"
-#include "ext4_balloc.h"
-#include "ext4_bitmap.h"
-#include "ext4_inode.h"
-#include "ext4_ialloc.h"
-#include "ext4_extent.h"
-
-#include <string.h>
-
-int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev)
-{
-       int r, i;
-       uint16_t tmp;
-       uint32_t bsize;
-       bool read_only = false;
-
-       ext4_assert(fs && bdev);
-
-       fs->bdev = bdev;
-
-       r = ext4_sb_read(fs->bdev, &fs->sb);
-       if (r != EOK)
-               return r;
-
-       if (!ext4_sb_check(&fs->sb))
-               return ENOTSUP;
-
-       bsize = ext4_sb_get_block_size(&fs->sb);
-       if (bsize > EXT4_MAX_BLOCK_SIZE)
-               return ENXIO;
-
-       r = ext4_fs_check_features(fs, &read_only);
-       if (r != EOK)
-               return r;
-
-       if (read_only)
-               return ENOTSUP;
-
-       /* Compute limits for indirect block levels */
-       uint32_t blocks_id = bsize / sizeof(uint32_t);
-
-       fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
-       fs->inode_blocks_per_level[0] = 1;
-
-       for (i = 1; i < 4; i++) {
-               fs->inode_blocks_per_level[i] =
-                   fs->inode_blocks_per_level[i - 1] * blocks_id;
-               fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] +
-                                           fs->inode_blocks_per_level[i];
-       }
-
-       /*Validate FS*/
-       tmp = ext4_get16(&fs->sb, state);
-       if (tmp & EXT4_SUPERBLOCK_STATE_ERROR_FS)
-               ext4_dbg(DEBUG_FS, DBG_WARN
-                               "last umount error: superblock fs_error flag\n");
-
-
-       /* Mark system as mounted */
-       ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_ERROR_FS);
-       r = ext4_sb_write(fs->bdev, &fs->sb);
-       if (r != EOK)
-               return r;
-
-       /*Update mount count*/
-       ext4_set16(&fs->sb, mount_count, ext4_get16(&fs->sb, mount_count) + 1);
-
-       return r;
-}
-
-int ext4_fs_fini(struct ext4_fs *fs)
-{
-       ext4_assert(fs);
-
-       /*Set superblock state*/
-       ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_VALID_FS);
-
-       return ext4_sb_write(fs->bdev, &fs->sb);
-}
-
-static void ext4_fs_debug_features_inc(uint32_t features_incompatible)
-{
-       if (features_incompatible & EXT4_FINCOM_COMPRESSION)
-               ext4_dbg(DEBUG_FS, DBG_NONE "compression\n");
-       if (features_incompatible & EXT4_FINCOM_FILETYPE)
-               ext4_dbg(DEBUG_FS, DBG_NONE "filetype\n");
-       if (features_incompatible & EXT4_FINCOM_RECOVER)
-               ext4_dbg(DEBUG_FS, DBG_NONE "recover\n");
-       if (features_incompatible & EXT4_FINCOM_JOURNAL_DEV)
-               ext4_dbg(DEBUG_FS, DBG_NONE "journal_dev\n");
-       if (features_incompatible & EXT4_FINCOM_META_BG)
-               ext4_dbg(DEBUG_FS, DBG_NONE "meta_bg\n");
-       if (features_incompatible & EXT4_FINCOM_EXTENTS)
-               ext4_dbg(DEBUG_FS, DBG_NONE "extents\n");
-       if (features_incompatible & EXT4_FINCOM_64BIT)
-               ext4_dbg(DEBUG_FS, DBG_NONE "64bit\n");
-       if (features_incompatible & EXT4_FINCOM_MMP)
-               ext4_dbg(DEBUG_FS, DBG_NONE "mnp\n");
-       if (features_incompatible & EXT4_FINCOM_FLEX_BG)
-               ext4_dbg(DEBUG_FS, DBG_NONE "flex_bg\n");
-       if (features_incompatible & EXT4_FINCOM_EA_INODE)
-               ext4_dbg(DEBUG_FS, DBG_NONE "ea_inode\n");
-       if (features_incompatible & EXT4_FINCOM_DIRDATA)
-               ext4_dbg(DEBUG_FS, DBG_NONE "dirdata\n");
-       if (features_incompatible & EXT4_FINCOM_BG_USE_META_CSUM)
-               ext4_dbg(DEBUG_FS, DBG_NONE "meta_csum\n");
-       if (features_incompatible & EXT4_FINCOM_LARGEDIR)
-               ext4_dbg(DEBUG_FS, DBG_NONE "largedir\n");
-       if (features_incompatible & EXT4_FINCOM_INLINE_DATA)
-               ext4_dbg(DEBUG_FS, DBG_NONE "inline_data\n");
-}
-static void ext4_fs_debug_features_comp(uint32_t features_compatible)
-{
-       if (features_compatible & EXT4_FCOM_DIR_PREALLOC)
-               ext4_dbg(DEBUG_FS, DBG_NONE "dir_prealloc\n");
-       if (features_compatible & EXT4_FCOM_IMAGIC_INODES)
-               ext4_dbg(DEBUG_FS, DBG_NONE "imagic_inodes\n");
-       if (features_compatible & EXT4_FCOM_HAS_JOURNAL)
-               ext4_dbg(DEBUG_FS, DBG_NONE "has_journal\n");
-       if (features_compatible & EXT4_FCOM_EXT_ATTR)
-               ext4_dbg(DEBUG_FS, DBG_NONE "ext_attr\n");
-       if (features_compatible & EXT4_FCOM_RESIZE_INODE)
-               ext4_dbg(DEBUG_FS, DBG_NONE "resize_inode\n");
-       if (features_compatible & EXT4_FCOM_DIR_INDEX)
-               ext4_dbg(DEBUG_FS, DBG_NONE "dir_index\n");
-}
-
-static void ext4_fs_debug_features_ro(uint32_t features_ro)
-{
-       if (features_ro & EXT4_FRO_COM_SPARSE_SUPER)
-               ext4_dbg(DEBUG_FS, DBG_NONE "sparse_super\n");
-       if (features_ro & EXT4_FRO_COM_LARGE_FILE)
-               ext4_dbg(DEBUG_FS, DBG_NONE "large_file\n");
-       if (features_ro & EXT4_FRO_COM_BTREE_DIR)
-               ext4_dbg(DEBUG_FS, DBG_NONE "btree_dir\n");
-       if (features_ro & EXT4_FRO_COM_HUGE_FILE)
-               ext4_dbg(DEBUG_FS, DBG_NONE "huge_file\n");
-       if (features_ro & EXT4_FRO_COM_GDT_CSUM)
-               ext4_dbg(DEBUG_FS, DBG_NONE "gtd_csum\n");
-       if (features_ro & EXT4_FRO_COM_DIR_NLINK)
-               ext4_dbg(DEBUG_FS, DBG_NONE "dir_nlink\n");
-       if (features_ro & EXT4_FRO_COM_EXTRA_ISIZE)
-               ext4_dbg(DEBUG_FS, DBG_NONE "extra_isize\n");
-       if (features_ro & EXT4_FRO_COM_QUOTA)
-               ext4_dbg(DEBUG_FS, DBG_NONE "quota\n");
-       if (features_ro & EXT4_FRO_COM_BIGALLOC)
-               ext4_dbg(DEBUG_FS, DBG_NONE "bigalloc\n");
-       if (features_ro & EXT4_FRO_COM_METADATA_CSUM)
-               ext4_dbg(DEBUG_FS, DBG_NONE "metadata_csum\n");
-}
-
-int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only)
-{
-       ext4_assert(fs && read_only);
-       uint32_t v;
-       if (ext4_get32(&fs->sb, rev_level) == 0) {
-               *read_only = false;
-               return EOK;
-       }
-
-       ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_incompatible:\n");
-       ext4_fs_debug_features_inc(ext4_get32(&fs->sb, features_incompatible));
-
-       ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_compatible:\n");
-       ext4_fs_debug_features_comp(ext4_get32(&fs->sb, features_compatible));
-
-       ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_read_only:\n");
-       ext4_fs_debug_features_ro(ext4_get32(&fs->sb, features_read_only));
-
-       /*Check features_incompatible*/
-       v = (ext4_get32(&fs->sb, features_incompatible) &
-            (~CONFIG_SUPPORTED_FINCOM));
-       if (v) {
-               ext4_dbg(DEBUG_FS, DBG_ERROR
-                               "sblock has unsupported features incompatible:\n");
-               ext4_fs_debug_features_inc(v);
-               return ENOTSUP;
-       }
-
-       /*Check features_read_only*/
-       v = ext4_get32(&fs->sb, features_read_only);
-       v &= ~CONFIG_SUPPORTED_FRO_COM;
-       if (v) {
-               ext4_dbg(DEBUG_FS, DBG_WARN
-                       "sblock has unsupported features read only:\n");
-               ext4_fs_debug_features_ro(v);
-               *read_only = true;
-               return EOK;
-       }
-       *read_only = false;
-
-       return EOK;
-}
-
-/**@brief Determine whether the block is inside the group.
- * @param baddr   block address
- * @param bgid    block group id
- * @return Error code
- */
-static bool ext4_block_in_group(struct ext4_sblock *s, ext4_fsblk_t baddr,
-                               uint32_t bgid)
-{
-       uint32_t actual_bgid;
-       actual_bgid = ext4_balloc_get_bgid_of_block(s, baddr);
-       if (actual_bgid == bgid)
-               return true;
-       return false;
-}
-
-/**@brief   To avoid calling the atomic setbit hundreds or thousands of times, we only
- *          need to use it within a single byte (to ensure we get endianness right).
- *          We can use memset for the rest of the bitmap as there are no other users.
- */
-static void ext4_fs_mark_bitmap_end(int start_bit, int end_bit, void *bitmap)
-{
-       int i;
-
-       if (start_bit >= end_bit)
-               return;
-
-       for (i = start_bit; (unsigned)i < ((start_bit + 7) & ~7UL); i++)
-               ext4_bmap_bit_set(bitmap, i);
-
-       if (i < end_bit)
-               memset((char *)bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
-}
-
-/**@brief Initialize block bitmap in block group.
- * @param bg_ref Reference to block group
- * @return Error code
- */
-static int ext4_fs_init_block_bitmap(struct ext4_block_group_ref *bg_ref)
-{
-       struct ext4_sblock *sb = &bg_ref->fs->sb;
-       struct ext4_bgroup *bg = bg_ref->block_group;
-       int rc;
-
-       uint32_t i, bit, bit_max;
-       uint32_t group_blocks;
-       uint16_t inode_size = ext4_get16(sb, inode_size);
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
-
-       ext4_fsblk_t bmp_blk = ext4_bg_get_block_bitmap(bg, sb);
-       ext4_fsblk_t bmp_inode = ext4_bg_get_inode_bitmap(bg, sb);
-       ext4_fsblk_t inode_table = ext4_bg_get_inode_table_first_block(bg, sb);
-       ext4_fsblk_t first_bg = ext4_balloc_get_block_of_bgid(sb, bg_ref->index);
-
-       uint32_t dsc_per_block =  block_size / ext4_sb_get_desc_size(sb);
-
-       bool flex_bg = ext4_sb_feature_incom(sb, EXT4_FINCOM_FLEX_BG);
-       bool meta_bg = ext4_sb_feature_incom(sb, EXT4_FINCOM_META_BG);
-
-       uint32_t inode_table_bcnt = inodes_per_group * inode_size / block_size;
-
-       struct ext4_block block_bitmap;
-       rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &block_bitmap, bmp_blk);
-       if (rc != EOK)
-               return rc;
-
-       memset(block_bitmap.data, 0, block_size);
-       bit_max = ext4_sb_is_super_in_bg(sb, bg_ref->index);
-
-       uint32_t count = ext4_sb_first_meta_bg(sb) * dsc_per_block;
-       if (!meta_bg || bg_ref->index < count) {
-               if (bit_max) {
-                       bit_max += ext4_bg_num_gdb(sb, bg_ref->index);
-                       bit_max += ext4_get16(sb, s_reserved_gdt_blocks);
-               }
-       } else { /* For META_BG_BLOCK_GROUPS */
-               bit_max += ext4_bg_num_gdb(sb, bg_ref->index);
-       }
-       for (bit = 0; bit < bit_max; bit++)
-               ext4_bmap_bit_set(block_bitmap.data, bit);
-
-       if (bg_ref->index == ext4_block_group_cnt(sb) - 1) {
-               /*
-                * Even though mke2fs always initialize first and last group
-                * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need
-                * to make sure we calculate the right free blocks
-                */
-
-               group_blocks = (uint32_t)(ext4_sb_get_blocks_cnt(sb) -
-                                         ext4_get32(sb, first_data_block) -
-                                         ext4_get32(sb, blocks_per_group) *
-                                         (ext4_block_group_cnt(sb) - 1));
-       } else {
-               group_blocks = ext4_get32(sb, blocks_per_group);
-       }
-
-       bool in_bg;
-       in_bg = ext4_block_in_group(sb, bmp_blk, bg_ref->index);
-       if (!flex_bg || in_bg)
-               ext4_bmap_bit_set(block_bitmap.data,
-                                 (uint32_t)(bmp_blk - first_bg));
-
-       in_bg = ext4_block_in_group(sb, bmp_inode, bg_ref->index);
-       if (!flex_bg || in_bg)
-               ext4_bmap_bit_set(block_bitmap.data,
-                                 (uint32_t)(bmp_inode - first_bg));
-
-        for (i = inode_table; i < inode_table + inode_table_bcnt; i++) {
-               in_bg = ext4_block_in_group(sb, i, bg_ref->index);
-               if (!flex_bg || in_bg)
-                       ext4_bmap_bit_set(block_bitmap.data,
-                                         (uint32_t)(i - first_bg));
-       }
-        /*
-         * Also if the number of blocks within the group is
-         * less than the blocksize * 8 ( which is the size
-         * of bitmap ), set rest of the block bitmap to 1
-         */
-        ext4_fs_mark_bitmap_end(group_blocks, block_size * 8, block_bitmap.data);
-       ext4_trans_set_block_dirty(block_bitmap.buf);
-
-       ext4_balloc_set_bitmap_csum(sb, bg_ref->block_group, block_bitmap.data);
-       bg_ref->dirty = true;
-
-       /* Save bitmap */
-       return ext4_block_set(bg_ref->fs->bdev, &block_bitmap);
-}
-
-/**@brief Initialize i-node bitmap in block group.
- * @param bg_ref Reference to block group
- * @return Error code
- */
-static int ext4_fs_init_inode_bitmap(struct ext4_block_group_ref *bg_ref)
-{
-       int rc;
-       struct ext4_sblock *sb = &bg_ref->fs->sb;
-       struct ext4_bgroup *bg = bg_ref->block_group;
-
-       /* Load bitmap */
-       ext4_fsblk_t bitmap_block_addr = ext4_bg_get_inode_bitmap(bg, sb);
-
-       struct ext4_block b;
-       rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &b, bitmap_block_addr);
-       if (rc != EOK)
-               return rc;
-
-       /* Initialize all bitmap bits to zero */
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
-
-       memset(b.data, 0, (inodes_per_group + 7) / 8);
-
-       uint32_t start_bit = inodes_per_group;
-       uint32_t end_bit = block_size * 8;
-
-       uint32_t i;
-       for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++)
-               ext4_bmap_bit_set(b.data, i);
-
-       if (i < end_bit)
-               memset(b.data + (i >> 3), 0xff, (end_bit - i) >> 3);
-
-       ext4_trans_set_block_dirty(b.buf);
-
-       ext4_ialloc_set_bitmap_csum(sb, bg, b.data);
-       bg_ref->dirty = true;
-
-       /* Save bitmap */
-       return ext4_block_set(bg_ref->fs->bdev, &b);
-}
-
-/**@brief Initialize i-node table in block group.
- * @param bg_ref Reference to block group
- * @return Error code
- */
-static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref)
-{
-       struct ext4_sblock *sb = &bg_ref->fs->sb;
-       struct ext4_bgroup *bg = bg_ref->block_group;
-
-       uint32_t inode_size = ext4_get32(sb, inode_size);
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint32_t inodes_per_block = block_size / inode_size;
-       uint32_t inodes_in_group = ext4_inodes_in_group_cnt(sb, bg_ref->index);
-       uint32_t table_blocks = inodes_in_group / inodes_per_block;
-       ext4_fsblk_t fblock;
-
-       if (inodes_in_group % inodes_per_block)
-               table_blocks++;
-
-       /* Compute initialization bounds */
-       ext4_fsblk_t first_block = ext4_bg_get_inode_table_first_block(bg, sb);
-
-       ext4_fsblk_t last_block = first_block + table_blocks - 1;
-
-       /* Initialization of all itable blocks */
-       for (fblock = first_block; fblock <= last_block; ++fblock) {
-               struct ext4_block b;
-               int rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &b, fblock);
-               if (rc != EOK)
-                       return rc;
-
-               memset(b.data, 0, block_size);
-               ext4_trans_set_block_dirty(b.buf);
-
-               ext4_block_set(bg_ref->fs->bdev, &b);
-               if (rc != EOK)
-                       return rc;
-       }
-
-       return EOK;
-}
-
-static ext4_fsblk_t ext4_fs_get_descriptor_block(struct ext4_sblock *s,
-                                            uint32_t bgid,
-                                            uint32_t dsc_per_block)
-{
-       uint32_t first_meta_bg, dsc_id;
-       int has_super = 0;
-       dsc_id = bgid / dsc_per_block;
-       first_meta_bg = ext4_sb_first_meta_bg(s);
-
-       bool meta_bg = ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG);
-
-       if (!meta_bg || dsc_id < first_meta_bg)
-               return ext4_get32(s, first_data_block) + dsc_id + 1;
-
-       if (ext4_sb_is_super_in_bg(s, bgid))
-               has_super = 1;
-
-       return (has_super + ext4_fs_first_bg_block_no(s, bgid));
-}
-
-/**@brief  Compute checksum of block group descriptor.
- * @param sb   Superblock
- * @param bgid Index of block group in the filesystem
- * @param bg   Block group to compute checksum for
- * @return Checksum value
- */
-static uint16_t ext4_fs_bg_checksum(struct ext4_sblock *sb, uint32_t bgid,
-                                   struct ext4_bgroup *bg)
-{
-       /* If checksum not supported, 0 will be returned */
-       uint16_t crc = 0;
-#if CONFIG_META_CSUM_ENABLE
-       /* Compute the checksum only if the filesystem supports it */
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               /* Use metadata_csum algorithm instead */
-               uint32_t le32_bgid = to_le32(bgid);
-               uint32_t orig_checksum, checksum;
-
-               /* Preparation: temporarily set bg checksum to 0 */
-               orig_checksum = bg->checksum;
-               bg->checksum = 0;
-
-               /* First calculate crc32 checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
-                               sizeof(sb->uuid));
-               /* Then calculate crc32 checksum against bgid */
-               checksum = ext4_crc32c(checksum, &le32_bgid, sizeof(bgid));
-               /* Finally calculate crc32 checksum against block_group_desc */
-               checksum = ext4_crc32c(checksum, bg, ext4_sb_get_desc_size(sb));
-               bg->checksum = orig_checksum;
-
-               crc = checksum & 0xFFFF;
-               return crc;
-       }
-#endif
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_GDT_CSUM)) {
-               uint8_t *base = (uint8_t *)bg;
-               uint8_t *checksum = (uint8_t *)&bg->checksum;
-
-               uint32_t offset = (uint32_t)(checksum - base);
-
-               /* Convert block group index to little endian */
-               uint32_t group = to_le32(bgid);
-
-               /* Initialization */
-               crc = ext4_bg_crc16(~0, sb->uuid, sizeof(sb->uuid));
-
-               /* Include index of block group */
-               crc = ext4_bg_crc16(crc, (uint8_t *)&group, sizeof(group));
-
-               /* Compute crc from the first part (stop before checksum field)
-                */
-               crc = ext4_bg_crc16(crc, (uint8_t *)bg, offset);
-
-               /* Skip checksum */
-               offset += sizeof(bg->checksum);
-
-               /* Checksum of the rest of block group descriptor */
-               if ((ext4_sb_feature_incom(sb, EXT4_FINCOM_64BIT)) &&
-                   (offset < ext4_sb_get_desc_size(sb))) {
-
-                       const uint8_t *start = ((uint8_t *)bg) + offset;
-                       size_t len = ext4_sb_get_desc_size(sb) - offset;
-                       crc = ext4_bg_crc16(crc, start, len);
-               }
-       }
-       return crc;
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool ext4_fs_verify_bg_csum(struct ext4_sblock *sb,
-                                  uint32_t bgid,
-                                  struct ext4_bgroup *bg)
-{
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return true;
-
-       return ext4_fs_bg_checksum(sb, bgid, bg) == to_le16(bg->checksum);
-}
-#else
-#define ext4_fs_verify_bg_csum(...) true
-#endif
-
-int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid,
-                               struct ext4_block_group_ref *ref)
-{
-       /* Compute number of descriptors, that fits in one data block */
-       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
-       uint32_t dsc_cnt = block_size / ext4_sb_get_desc_size(&fs->sb);
-
-       /* Block group descriptor table starts at the next block after
-        * superblock */
-       uint64_t block_id = ext4_fs_get_descriptor_block(&fs->sb, bgid, dsc_cnt);
-
-       uint32_t offset = (bgid % dsc_cnt) * ext4_sb_get_desc_size(&fs->sb);
-
-       int rc = ext4_trans_block_get(fs->bdev, &ref->block, block_id);
-       if (rc != EOK)
-               return rc;
-
-       ref->block_group = (void *)(ref->block.data + offset);
-       ref->fs = fs;
-       ref->index = bgid;
-       ref->dirty = false;
-       struct ext4_bgroup *bg = ref->block_group;
-
-       if (!ext4_fs_verify_bg_csum(&fs->sb, bgid, bg)) {
-               ext4_dbg(DEBUG_FS,
-                        DBG_WARN "Block group descriptor checksum failed."
-                        "Block group index: %" PRIu32"\n",
-                        bgid);
-       }
-
-       if (ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_BLOCK_UNINIT)) {
-               rc = ext4_fs_init_block_bitmap(ref);
-               if (rc != EOK) {
-                       ext4_block_set(fs->bdev, &ref->block);
-                       return rc;
-               }
-               ext4_bg_clear_flag(bg, EXT4_BLOCK_GROUP_BLOCK_UNINIT);
-               ref->dirty = true;
-       }
-
-       if (ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_INODE_UNINIT)) {
-               rc = ext4_fs_init_inode_bitmap(ref);
-               if (rc != EOK) {
-                       ext4_block_set(ref->fs->bdev, &ref->block);
-                       return rc;
-               }
-
-               ext4_bg_clear_flag(bg, EXT4_BLOCK_GROUP_INODE_UNINIT);
-
-               if (!ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
-                       rc = ext4_fs_init_inode_table(ref);
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &ref->block);
-                               return rc;
-                       }
-
-                       ext4_bg_set_flag(bg, EXT4_BLOCK_GROUP_ITABLE_ZEROED);
-               }
-
-               ref->dirty = true;
-       }
-
-       return EOK;
-}
-
-int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref)
-{
-       /* Check if reference modified */
-       if (ref->dirty) {
-               /* Compute new checksum of block group */
-               uint16_t cs;
-               cs = ext4_fs_bg_checksum(&ref->fs->sb, ref->index,
-                                        ref->block_group);
-               ref->block_group->checksum = to_le16(cs);
-
-               /* Mark block dirty for writing changes to physical device */
-               ext4_trans_set_block_dirty(ref->block.buf);
-       }
-
-       /* Put back block, that contains block group descriptor */
-       return ext4_block_set(ref->fs->bdev, &ref->block);
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_fs_inode_checksum(struct ext4_inode_ref *inode_ref)
-{
-       uint32_t checksum = 0;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint16_t inode_size = ext4_get16(sb, inode_size);
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint32_t orig_checksum;
-
-               uint32_t ino_index = to_le32(inode_ref->index);
-               uint32_t ino_gen =
-                       to_le32(ext4_inode_get_generation(inode_ref->inode));
-
-               /* Preparation: temporarily set bg checksum to 0 */
-               orig_checksum = ext4_inode_get_csum(sb, inode_ref->inode);
-               ext4_inode_set_csum(sb, inode_ref->inode, 0);
-
-               /* First calculate crc32 checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
-                                      sizeof(sb->uuid));
-               /* Then calculate crc32 checksum against inode number
-                * and inode generation */
-               checksum = ext4_crc32c(checksum, &ino_index, sizeof(ino_index));
-               checksum = ext4_crc32c(checksum, &ino_gen, sizeof(ino_gen));
-               /* Finally calculate crc32 checksum against
-                * the entire inode */
-               checksum = ext4_crc32c(checksum, inode_ref->inode, inode_size);
-               ext4_inode_set_csum(sb, inode_ref->inode, orig_checksum);
-
-               /* If inode size is not large enough to hold the
-                * upper 16bit of the checksum */
-               if (inode_size == EXT4_GOOD_OLD_INODE_SIZE)
-                       checksum &= 0xFFFF;
-
-       }
-       return checksum;
-}
-#else
-#define ext4_fs_inode_checksum(...) 0
-#endif
-
-static void ext4_fs_set_inode_checksum(struct ext4_inode_ref *inode_ref)
-{
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return;
-
-       uint32_t csum = ext4_fs_inode_checksum(inode_ref);
-       ext4_inode_set_csum(sb, inode_ref->inode, csum);
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool ext4_fs_verify_inode_csum(struct ext4_inode_ref *inode_ref)
-{
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return true;
-
-       return ext4_inode_get_csum(sb, inode_ref->inode) ==
-               ext4_fs_inode_checksum(inode_ref);
-}
-#else
-#define ext4_fs_verify_inode_csum(...) true
-#endif
-
-static int
-__ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
-                       struct ext4_inode_ref *ref,
-                       bool initialized)
-{
-       /* Compute number of i-nodes, that fits in one data block */
-       uint32_t inodes_per_group = ext4_get32(&fs->sb, inodes_per_group);
-
-       /*
-        * Inode numbers are 1-based, but it is simpler to work with 0-based
-        * when computing indices
-        */
-       index -= 1;
-       uint32_t block_group = index / inodes_per_group;
-       uint32_t offset_in_group = index % inodes_per_group;
-
-       /* Load block group, where i-node is located */
-       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 block address, where i-node table is located */
-       ext4_fsblk_t inode_table_start =
-           ext4_bg_get_inode_table_first_block(bg_ref.block_group, &fs->sb);
-
-       /* Put back block group reference (not needed more) */
-       rc = ext4_fs_put_block_group_ref(&bg_ref);
-       if (rc != EOK) {
-               return rc;
-       }
-
-       /* Compute position of i-node in the block group */
-       uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
-       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
-       uint32_t byte_offset_in_group = offset_in_group * inode_size;
-
-       /* Compute block address */
-       ext4_fsblk_t block_id =
-           inode_table_start + (byte_offset_in_group / block_size);
-
-       rc = ext4_trans_block_get(fs->bdev, &ref->block, block_id);
-       if (rc != EOK) {
-               return rc;
-       }
-
-       /* Compute position of i-node in the data block */
-       uint32_t offset_in_block = byte_offset_in_group % block_size;
-       ref->inode = (struct ext4_inode *)(ref->block.data + offset_in_block);
-
-       /* We need to store the original value of index in the reference */
-       ref->index = index + 1;
-       ref->fs = fs;
-       ref->dirty = false;
-
-       if (initialized && !ext4_fs_verify_inode_csum(ref)) {
-               ext4_dbg(DEBUG_FS,
-                       DBG_WARN "Inode checksum failed."
-                       "Inode: %" PRIu32"\n",
-                       ref->index);
-       }
-
-       return EOK;
-}
-
-int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
-                         struct ext4_inode_ref *ref)
-{
-       return __ext4_fs_get_inode_ref(fs, index, ref, true);
-}
-
-int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref)
-{
-       /* Check if reference modified */
-       if (ref->dirty) {
-               /* Mark block dirty for writing changes to physical device */
-               ext4_fs_set_inode_checksum(ref);
-               ext4_trans_set_block_dirty(ref->block.buf);
-       }
-
-       /* Put back block, that contains i-node */
-       return ext4_block_set(ref->fs->bdev, &ref->block);
-}
-
-void ext4_fs_inode_blocks_init(struct ext4_fs *fs,
-                              struct ext4_inode_ref *inode_ref)
-{
-       int i;
-       struct ext4_inode *inode = inode_ref->inode;
-
-       for (i = 0; i < EXT4_INODE_BLOCKS; i++)
-               inode->blocks[i] = 0;
-
-       (void)fs;
-#if CONFIG_EXTENT_ENABLE
-       /* Initialize extents if needed */
-       if (ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) {
-               ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
-
-               /* Initialize extent root header */
-               ext4_extent_tree_init(inode_ref);
-       }
-#endif
-}
-
-uint32_t ext4_fs_correspond_inode_mode(int filetype)
-{
-       switch (filetype) {
-       case EXT4_DE_DIR:
-               return EXT4_INODE_MODE_DIRECTORY;
-       case EXT4_DE_REG_FILE:
-               return EXT4_INODE_MODE_FILE;
-       case EXT4_DE_SYMLINK:
-               return EXT4_INODE_MODE_SOFTLINK;
-       default:
-               /* FIXME: right now we only support 3 file type. */
-               ext4_assert(0);
-       }
-       return 0;
-}
-
-int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
-                       int filetype)
-{
-       /* Check if newly allocated i-node will be a directory */
-       bool is_dir;
-       uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
-
-       is_dir = (filetype == EXT4_DE_DIR);
-
-       /* Allocate inode by allocation algorithm */
-       uint32_t index;
-       int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
-       if (rc != EOK)
-               return rc;
-
-       /* Load i-node from on-disk i-node table */
-       rc = __ext4_fs_get_inode_ref(fs, index, inode_ref, false);
-       if (rc != EOK) {
-               ext4_ialloc_free_inode(fs, index, is_dir);
-               return rc;
-       }
-
-       /* Initialize i-node */
-       struct ext4_inode *inode = inode_ref->inode;
-
-       uint32_t mode;
-       if (is_dir) {
-               /*
-                * Default directory permissions to be compatible with other
-                * systems
-                * 0777 (octal) == rwxrwxrwx
-                */
-
-               mode = 0777;
-               mode |= EXT4_INODE_MODE_DIRECTORY;
-       } else {
-               /*
-                * Default file permissions to be compatible with other systems
-                * 0666 (octal) == rw-rw-rw-
-                */
-
-               mode = 0666;
-               mode |= ext4_fs_correspond_inode_mode(filetype);
-       }
-       ext4_inode_set_mode(&fs->sb, inode, mode);
-
-       ext4_inode_set_links_cnt(inode, 0);
-       ext4_inode_set_uid(inode, 0);
-       ext4_inode_set_gid(inode, 0);
-       ext4_inode_set_size(inode, 0);
-       ext4_inode_set_access_time(inode, 0);
-       ext4_inode_set_change_inode_time(inode, 0);
-       ext4_inode_set_modif_time(inode, 0);
-       ext4_inode_set_del_time(inode, 0);
-       ext4_inode_set_blocks_count(&fs->sb, inode, 0);
-       ext4_inode_set_flags(inode, 0);
-       ext4_inode_set_generation(inode, 0);
-       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
-               uint16_t off = offsetof(struct ext4_inode, extra_isize);
-               uint16_t size = sizeof(struct ext4_inode) - off;
-               ext4_inode_set_extra_isize(inode, size);
-       }
-
-       /* Reset blocks array. For symbolic link inode, just
-        * fill in blocks with 0 */
-       if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
-               for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
-                       inode->blocks[i] = 0;
-
-       } else {
-               ext4_fs_inode_blocks_init(fs, inode_ref);
-       }
-
-       inode_ref->dirty = true;
-
-       return EOK;
-}
-
-int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref)
-{
-       struct ext4_fs *fs = inode_ref->fs;
-       uint32_t offset;
-       uint32_t suboff;
-       int rc;
-#if CONFIG_EXTENT_ENABLE
-       /* For extents must be data block destroyed by other way */
-       if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) &&
-           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
-               /* Data structures are released during truncate operation... */
-               goto finish;
-       }
-#endif
-       /* Release all indirect (no data) blocks */
-
-       /* 1) Single indirect */
-       ext4_fsblk_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
-       if (fblock != 0) {
-               int rc = ext4_balloc_free_block(inode_ref, fblock);
-               if (rc != EOK)
-                       return rc;
-
-               ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
-       }
-
-       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
-       uint32_t count = block_size / sizeof(uint32_t);
-
-       struct ext4_block block;
-
-       /* 2) Double indirect */
-       fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
-       if (fblock != 0) {
-               int rc = ext4_trans_block_get(fs->bdev, &block, fblock);
-               if (rc != EOK)
-                       return rc;
-
-               ext4_fsblk_t ind_block;
-               for (offset = 0; offset < count; ++offset) {
-                       ind_block = to_le32(((uint32_t *)block.data)[offset]);
-
-                       if (ind_block == 0)
-                               continue;
-                       rc = ext4_balloc_free_block(inode_ref, ind_block);
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &block);
-                               return rc;
-                       }
-
-               }
-
-               ext4_block_set(fs->bdev, &block);
-               rc = ext4_balloc_free_block(inode_ref, fblock);
-               if (rc != EOK)
-                       return rc;
-
-               ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
-       }
-
-       /* 3) Tripple indirect */
-       struct ext4_block subblock;
-       fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
-       if (fblock == 0)
-               goto finish;
-       rc = ext4_trans_block_get(fs->bdev, &block, fblock);
-       if (rc != EOK)
-               return rc;
-
-       ext4_fsblk_t ind_block;
-       for (offset = 0; offset < count; ++offset) {
-               ind_block = to_le32(((uint32_t *)block.data)[offset]);
-
-               if (ind_block == 0)
-                       continue;
-               rc = ext4_trans_block_get(fs->bdev, &subblock,
-                               ind_block);
-               if (rc != EOK) {
-                       ext4_block_set(fs->bdev, &block);
-                       return rc;
-               }
-
-               ext4_fsblk_t ind_subblk;
-               for (suboff = 0; suboff < count; ++suboff) {
-                       ind_subblk = to_le32(((uint32_t *)subblock.data)[suboff]);
-
-                       if (ind_subblk == 0)
-                               continue;
-                       rc = ext4_balloc_free_block(inode_ref, ind_subblk);
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &subblock);
-                               ext4_block_set(fs->bdev, &block);
-                               return rc;
-                       }
-
-               }
-
-               ext4_block_set(fs->bdev, &subblock);
-
-               rc = ext4_balloc_free_block(inode_ref,
-                               ind_block);
-               if (rc != EOK) {
-                       ext4_block_set(fs->bdev, &block);
-                       return rc;
-               }
-
-       }
-
-       ext4_block_set(fs->bdev, &block);
-       rc = ext4_balloc_free_block(inode_ref, fblock);
-       if (rc != EOK)
-               return rc;
-
-       ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
-finish:
-       /* Mark inode dirty for writing to the physical device */
-       inode_ref->dirty = true;
-
-       /* Free block with extended attributes if present */
-       ext4_fsblk_t xattr_block =
-           ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
-       if (xattr_block) {
-               int rc = ext4_balloc_free_block(inode_ref, xattr_block);
-               if (rc != EOK)
-                       return rc;
-
-               ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0);
-       }
-
-       /* Free inode by allocator */
-       if (ext4_inode_is_type(&fs->sb, inode_ref->inode,
-                              EXT4_INODE_MODE_DIRECTORY))
-               rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
-       else
-               rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
-
-       return rc;
-}
-
-
-/**@brief Release data block from i-node
- * @param inode_ref I-node to release block from
- * @param iblock    Logical block to be released
- * @return Error code
- */
-static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
-                               uint32_t iblock)
-{
-       ext4_fsblk_t fblock;
-
-       struct ext4_fs *fs = inode_ref->fs;
-
-       /* Extents are handled otherwise = there is not support in this function
-        */
-       ext4_assert(!(
-           ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS) &&
-           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
-
-       struct ext4_inode *inode = inode_ref->inode;
-
-       /* Handle simple case when we are dealing with direct reference */
-       if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
-               fblock = ext4_inode_get_direct_block(inode, iblock);
-
-               /* Sparse file */
-               if (fblock == 0)
-                       return EOK;
-
-               ext4_inode_set_direct_block(inode, iblock, 0);
-               return ext4_balloc_free_block(inode_ref, fblock);
-       }
-
-       /* Determine the indirection level needed to get the desired block */
-       unsigned int level = 0;
-       unsigned int i;
-       for (i = 1; i < 4; i++) {
-               if (iblock < fs->inode_block_limits[i]) {
-                       level = i;
-                       break;
-               }
-       }
-
-       if (level == 0)
-               return EIO;
-
-       /* Compute offsets for the topmost level */
-       uint32_t block_offset_in_level =
-           iblock - fs->inode_block_limits[level - 1];
-       ext4_fsblk_t current_block =
-           ext4_inode_get_indirect_block(inode, level - 1);
-       uint32_t offset_in_block =
-           block_offset_in_level / fs->inode_blocks_per_level[level - 1];
-
-       /*
-        * Navigate through other levels, until we find the block number
-        * or find null reference meaning we are dealing with sparse file
-        */
-       struct ext4_block block;
-
-       while (level > 0) {
-
-               /* Sparse check */
-               if (current_block == 0)
-                       return EOK;
-
-               int rc = ext4_trans_block_get(fs->bdev, &block, current_block);
-               if (rc != EOK)
-                       return rc;
-
-               current_block =
-                   to_le32(((uint32_t *)block.data)[offset_in_block]);
-
-               /* Set zero if physical data block address found */
-               if (level == 1) {
-                       ((uint32_t *)block.data)[offset_in_block] = to_le32(0);
-                       ext4_trans_set_block_dirty(block.buf);
-               }
-
-               rc = ext4_block_set(fs->bdev, &block);
-               if (rc != EOK)
-                       return rc;
-
-               level--;
-
-               /*
-                * If we are on the last level, break here as
-                * there is no next level to visit
-                */
-               if (level == 0)
-                       break;
-
-               /* Visit the next level */
-               block_offset_in_level %= fs->inode_blocks_per_level[level];
-               offset_in_block = block_offset_in_level /
-                                 fs->inode_blocks_per_level[level - 1];
-       }
-
-       fblock = current_block;
-       if (fblock == 0)
-               return EOK;
-
-       /* Physical block is not referenced, it can be released */
-       return ext4_balloc_free_block(inode_ref, fblock);
-}
-
-int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
-{
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       uint32_t i;
-       int r;
-
-       /* Check flags, if i-node can be truncated */
-       if (!ext4_inode_can_truncate(sb, inode_ref->inode))
-               return EINVAL;
-
-       /* If sizes are equal, nothing has to be done. */
-       uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
-       if (old_size == new_size)
-               return EOK;
-
-       /* It's not supported to make the larger file by truncate operation */
-       if (old_size < new_size)
-               return EINVAL;
-
-       bool v;
-       v = ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK);
-       if (v && old_size < sizeof(inode_ref->inode->blocks) &&
-           !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
-               char *content = (char *)inode_ref->inode->blocks + new_size;
-               memset(content, 0,
-                      sizeof(inode_ref->inode->blocks) - (uint32_t)new_size);
-               ext4_inode_set_size(inode_ref->inode, new_size);
-               inode_ref->dirty = true;
-
-               return EOK;
-       }
-
-       /* Compute how many blocks will be released */
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint32_t new_blocks_cnt = (uint32_t)((new_size + block_size - 1) / block_size);
-       uint32_t old_blocks_cnt = (uint32_t)((old_size + block_size - 1) / block_size);
-       uint32_t diff_blocks_cnt = old_blocks_cnt - new_blocks_cnt;
-#if CONFIG_EXTENT_ENABLE
-       if ((ext4_sb_feature_incom(sb, EXT4_FINCOM_EXTENTS)) &&
-           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
-
-               /* Extents require special operation */
-               if (diff_blocks_cnt) {
-                       r = ext4_extent_remove_space(inode_ref, new_blocks_cnt,
-                                                    EXT_MAX_BLOCKS);
-                       if (r != EOK)
-                               return r;
-
-               }
-       } else
-#endif
-       {
-               /* Release data blocks from the end of file */
-
-               /* Starting from 1 because of logical blocks are numbered from 0
-                */
-               for (i = 0; i < diff_blocks_cnt; ++i) {
-                       r = ext4_fs_release_inode_block(inode_ref,
-                                                       new_blocks_cnt + i);
-                       if (r != EOK)
-                               return r;
-               }
-       }
-
-       /* Update i-node */
-       ext4_inode_set_size(inode_ref->inode, new_size);
-       inode_ref->dirty = true;
-
-       return EOK;
-}
-
-/**@brief Compute 'goal' for inode index
- * @param inode_ref Reference to inode, to allocate block for
- * @return goal
- */
-ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref)
-{
-       uint32_t grp_inodes = ext4_get32(&inode_ref->fs->sb, inodes_per_group);
-       return (inode_ref->index - 1) / grp_inodes;
-}
-
-/**@brief Compute 'goal' for allocation algorithm (For blockmap).
- * @param inode_ref Reference to inode, to allocate block for
- * @param goal
- * @return error code
- */
-int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
-                              ext4_fsblk_t *goal)
-{
-       int r;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       *goal = 0;
-
-       uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-       uint32_t iblock_cnt = (uint32_t)(inode_size / block_size);
-
-       if (inode_size % block_size != 0)
-               iblock_cnt++;
-
-       /* If inode has some blocks, get last block address + 1 */
-       if (iblock_cnt > 0) {
-               r = ext4_fs_get_inode_dblk_idx(inode_ref, iblock_cnt - 1,
-                                              goal, false);
-               if (r != EOK)
-                       return r;
-
-               if (*goal != 0) {
-                       (*goal)++;
-                       return r;
-               }
-
-               /* If goal == 0, sparse file -> continue */
-       }
-
-       /* Identify block group of inode */
-
-       uint32_t inodes_per_bg = ext4_get32(sb, inodes_per_group);
-       uint32_t block_group = (inode_ref->index - 1) / inodes_per_bg;
-       block_size = ext4_sb_get_block_size(sb);
-
-       /* Load block group reference */
-       struct ext4_block_group_ref bg_ref;
-       r = ext4_fs_get_block_group_ref(inode_ref->fs, block_group, &bg_ref);
-       if (r != EOK)
-               return r;
-
-       struct ext4_bgroup *bg = bg_ref.block_group;
-
-       /* Compute indexes */
-       uint32_t bg_count = ext4_block_group_cnt(sb);
-       ext4_fsblk_t itab_first_block = ext4_bg_get_inode_table_first_block(bg, sb);
-       uint16_t itab_item_size = ext4_get16(sb, inode_size);
-       uint32_t itab_bytes;
-
-       /* Check for last block group */
-       if (block_group < bg_count - 1) {
-               itab_bytes = inodes_per_bg * itab_item_size;
-       } else {
-               /* Last block group could be smaller */
-               uint32_t inodes_cnt = ext4_get32(sb, inodes_count);
-
-               itab_bytes = (inodes_cnt - ((bg_count - 1) * inodes_per_bg));
-               itab_bytes *= itab_item_size;
-       }
-
-       ext4_fsblk_t inode_table_blocks = itab_bytes / block_size;
-
-       if (itab_bytes % block_size)
-               inode_table_blocks++;
-
-       *goal = itab_first_block + inode_table_blocks;
-
-       return ext4_fs_put_block_group_ref(&bg_ref);
-}
-
-static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref,
-                                      uint64_t iblock, ext4_fsblk_t *fblock,
-                                      bool extent_create,
-                                      bool support_unwritten __unused)
-{
-       struct ext4_fs *fs = inode_ref->fs;
-
-       /* For empty file is situation simple */
-       if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) {
-               *fblock = 0;
-               return EOK;
-       }
-
-       ext4_fsblk_t current_block;
-
-       (void)extent_create;
-#if CONFIG_EXTENT_ENABLE
-       /* Handle i-node using extents */
-       if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) &&
-           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
-
-               ext4_fsblk_t current_fsblk;
-               int rc = ext4_extent_get_blocks(inode_ref, iblock, 1,
-                               &current_fsblk, extent_create, NULL);
-               if (rc != EOK)
-                       return rc;
-
-               current_block = current_fsblk;
-               *fblock = current_block;
-
-               ext4_assert(*fblock || support_unwritten);
-               return EOK;
-       }
-#endif
-
-       struct ext4_inode *inode = inode_ref->inode;
-
-       /* Direct block are read directly from array in i-node structure */
-       if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
-               current_block =
-                   ext4_inode_get_direct_block(inode, (uint32_t)iblock);
-               *fblock = current_block;
-               return EOK;
-       }
-
-       /* Determine indirection level of the target block */
-       unsigned int l = 0;
-       unsigned int i;
-       for (i = 1; i < 4; i++) {
-               if (iblock < fs->inode_block_limits[i]) {
-                       l = i;
-                       break;
-               }
-       }
-
-       if (l == 0)
-               return EIO;
-
-       /* Compute offsets for the topmost level */
-       uint32_t blk_off_in_lvl =  iblock - fs->inode_block_limits[l - 1];
-       current_block = ext4_inode_get_indirect_block(inode, l - 1);
-       uint32_t off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
-
-       /* Sparse file */
-       if (current_block == 0) {
-               *fblock = 0;
-               return EOK;
-       }
-
-       struct ext4_block block;
-
-       /*
-        * Navigate through other levels, until we find the block number
-        * or find null reference meaning we are dealing with sparse file
-        */
-       while (l > 0) {
-               /* Load indirect block */
-               int rc = ext4_trans_block_get(fs->bdev, &block, current_block);
-               if (rc != EOK)
-                       return rc;
-
-               /* Read block address from indirect block */
-               current_block =
-                   to_le32(((uint32_t *)block.data)[off_in_blk]);
-
-               /* Put back indirect block untouched */
-               rc = ext4_block_set(fs->bdev, &block);
-               if (rc != EOK)
-                       return rc;
-
-               /* Check for sparse file */
-               if (current_block == 0) {
-                       *fblock = 0;
-                       return EOK;
-               }
-
-               /* Jump to the next level */
-               l--;
-
-               /* Termination condition - we have address of data block loaded
-                */
-               if (l == 0)
-                       break;
-
-               /* Visit the next level */
-               blk_off_in_lvl %= fs->inode_blocks_per_level[l];
-               off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
-       }
-
-       *fblock = current_block;
-
-       return EOK;
-}
-
-
-int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
-                              uint64_t iblock, ext4_fsblk_t *fblock,
-                              bool support_unwritten)
-{
-       return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock,
-                                                  false, support_unwritten);
-}
-
-int ext4_fs_init_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
-                               uint64_t iblock, ext4_fsblk_t *fblock)
-{
-       return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock,
-                                                  true, true);
-}
-
-static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
-                                      uint32_t iblock, ext4_fsblk_t fblock)
-{
-       struct ext4_fs *fs = inode_ref->fs;
-
-#if CONFIG_EXTENT_ENABLE
-       /* Handle inode using extents */
-       if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) &&
-           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
-               /* Not reachable */
-               return ENOTSUP;
-       }
-#endif
-
-       /* Handle simple case when we are dealing with direct reference */
-       if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
-               ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock,
-                                           (uint32_t)fblock);
-               inode_ref->dirty = true;
-
-               return EOK;
-       }
-
-       /* Determine the indirection level needed to get the desired block */
-       unsigned int l = 0;
-       unsigned int i;
-       for (i = 1; i < 4; i++) {
-               if (iblock < fs->inode_block_limits[i]) {
-                       l = i;
-                       break;
-               }
-       }
-
-       if (l == 0)
-               return EIO;
-
-       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
-
-       /* Compute offsets for the topmost level */
-       uint32_t blk_off_in_lvl =  iblock - fs->inode_block_limits[l - 1];
-       ext4_fsblk_t current_block =
-                       ext4_inode_get_indirect_block(inode_ref->inode, l - 1);
-       uint32_t off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
-
-       ext4_fsblk_t new_blk;
-
-       struct ext4_block block;
-       struct ext4_block new_block;
-
-       /* Is needed to allocate indirect block on the i-node level */
-       if (current_block == 0) {
-               /* Allocate new indirect block */
-               ext4_fsblk_t goal;
-               int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
-               if (rc != EOK)
-                       return rc;
-
-               rc = ext4_balloc_alloc_block(inode_ref, goal, &new_blk);
-               if (rc != EOK)
-                       return rc;
-
-               /* Update i-node */
-               ext4_inode_set_indirect_block(inode_ref->inode, l - 1,
-                               (uint32_t)new_blk);
-               inode_ref->dirty = true;
-
-               /* Load newly allocated block */
-               rc = ext4_trans_block_get_noread(fs->bdev, &new_block, new_blk);
-               if (rc != EOK) {
-                       ext4_balloc_free_block(inode_ref, new_blk);
-                       return rc;
-               }
-
-               /* Initialize new block */
-               memset(new_block.data, 0, block_size);
-               ext4_trans_set_block_dirty(new_block.buf);
-
-               /* Put back the allocated block */
-               rc = ext4_block_set(fs->bdev, &new_block);
-               if (rc != EOK)
-                       return rc;
-
-               current_block = new_blk;
-       }
-
-       /*
-        * Navigate through other levels, until we find the block number
-        * or find null reference meaning we are dealing with sparse file
-        */
-       while (l > 0) {
-               int rc = ext4_trans_block_get(fs->bdev, &block, current_block);
-               if (rc != EOK)
-                       return rc;
-
-               current_block = to_le32(((uint32_t *)block.data)[off_in_blk]);
-               if ((l > 1) && (current_block == 0)) {
-                       ext4_fsblk_t goal;
-                       rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &block);
-                               return rc;
-                       }
-
-                       /* Allocate new block */
-                       rc =
-                           ext4_balloc_alloc_block(inode_ref, goal, &new_blk);
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &block);
-                               return rc;
-                       }
-
-                       /* Load newly allocated block */
-                       rc = ext4_trans_block_get_noread(fs->bdev, &new_block,
-                                           new_blk);
-
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &block);
-                               return rc;
-                       }
-
-                       /* Initialize allocated block */
-                       memset(new_block.data, 0, block_size);
-                       ext4_trans_set_block_dirty(new_block.buf);
-
-                       rc = ext4_block_set(fs->bdev, &new_block);
-                       if (rc != EOK) {
-                               ext4_block_set(fs->bdev, &block);
-                               return rc;
-                       }
-
-                       /* Write block address to the parent */
-                       uint32_t * p = (uint32_t * )block.data;
-                       p[off_in_blk] = to_le32((uint32_t)new_blk);
-                       ext4_trans_set_block_dirty(block.buf);
-                       current_block = new_blk;
-               }
-
-               /* Will be finished, write the fblock address */
-               if (l == 1) {
-                       uint32_t * p = (uint32_t * )block.data;
-                       p[off_in_blk] = to_le32((uint32_t)fblock);
-                       ext4_trans_set_block_dirty(block.buf);
-               }
-
-               rc = ext4_block_set(fs->bdev, &block);
-               if (rc != EOK)
-                       return rc;
-
-               l--;
-
-               /*
-                * If we are on the last level, break here as
-                * there is no next level to visit
-                */
-               if (l == 0)
-                       break;
-
-               /* Visit the next level */
-               blk_off_in_lvl %= fs->inode_blocks_per_level[l];
-               off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
-       }
-
-       return EOK;
-}
-
-
-int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref,
-                             ext4_fsblk_t *fblock, uint32_t *iblock)
-{
-#if CONFIG_EXTENT_ENABLE
-       /* Handle extents separately */
-       if ((ext4_sb_feature_incom(&inode_ref->fs->sb, EXT4_FINCOM_EXTENTS)) &&
-           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
-               int rc;
-               ext4_fsblk_t current_fsblk;
-               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);
-               *iblock = (uint32_t)((inode_size + block_size - 1) / block_size);
-
-               rc = ext4_extent_get_blocks(inode_ref, *iblock, 1,
-                                               &current_fsblk, true, NULL);
-
-               *fblock = current_fsblk;
-               ext4_assert(*fblock);
-
-               ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
-               inode_ref->dirty = true;
-
-
-               return rc;
-       }
-#endif
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       /* Compute next block index and allocate data block */
-       uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
-       uint32_t block_size = ext4_sb_get_block_size(sb);
-
-       /* Align size i-node size */
-       if ((inode_size % block_size) != 0)
-               inode_size += block_size - (inode_size % block_size);
-
-       /* Logical blocks are numbered from 0 */
-       uint32_t new_block_idx = (uint32_t)(inode_size / block_size);
-
-       /* Allocate new physical block */
-       ext4_fsblk_t goal, phys_block;
-       int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
-       if (rc != EOK)
-               return rc;
-
-       rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block);
-       if (rc != EOK)
-               return rc;
-
-       /* Add physical block address to the i-node */
-       rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx,
-                                               phys_block);
-       if (rc != EOK) {
-               ext4_balloc_free_block(inode_ref, phys_block);
-               return rc;
-       }
-
-       /* Update i-node */
-       ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
-       inode_ref->dirty = true;
-
-       *fblock = phys_block;
-       *iblock = new_block_idx;
-
-       return EOK;
-}
-
-void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
-{
-       uint16_t link;
-       bool is_dx;
-       link = ext4_inode_get_links_cnt(inode_ref->inode);
-       link++;
-       ext4_inode_set_links_cnt(inode_ref->inode, link);
-
-       is_dx = ext4_sb_feature_com(&inode_ref->fs->sb, EXT4_FCOM_DIR_INDEX) &&
-               ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX);
-
-       if (is_dx && link > 1) {
-               if (link >= EXT4_LINK_MAX || link == 2) {
-                       ext4_inode_set_links_cnt(inode_ref->inode, 1);
-
-                       uint32_t v;
-                       v = ext4_get32(&inode_ref->fs->sb, features_read_only);
-                       v |= EXT4_FRO_COM_DIR_NLINK;
-                       ext4_set32(&inode_ref->fs->sb, features_read_only, v);
-               }
-       }
-}
-
-void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
-{
-       uint16_t links = ext4_inode_get_links_cnt(inode_ref->inode);
-       if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
-                               EXT4_INODE_MODE_DIRECTORY)) {
-               if (links > 0)
-                       ext4_inode_set_links_cnt(inode_ref->inode, links - 1);
-               return;
-       }
-
-       if (links > 2)
-               ext4_inode_set_links_cnt(inode_ref->inode, links - 1);
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_fs.h b/lwext4/ext4_fs.h
deleted file mode 100644 (file)
index 97e1d1d..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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_fs.c
- * @brief More complex filesystem functions.
- */
-
-#ifndef EXT4_FS_H_
-#define EXT4_FS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/**@brief Convert block address to relative index in block group.
- * @param sb Superblock pointer
- * @param baddr Block number to convert
- * @return Relative number of block
- */
-static inline uint32_t ext4_fs_addr_to_idx_bg(struct ext4_sblock *s,
-                                                    ext4_fsblk_t baddr)
-{
-       if (ext4_get32(s, first_data_block))
-               baddr--;
-
-       return baddr % ext4_get32(s, blocks_per_group);
-}
-
-/**@brief Convert relative block address in group to absolute address.
- * @param s Superblock pointer
- * @param index Relative block address
- * @param bgid Block group
- * @return Absolute block address
- */
-static inline ext4_fsblk_t ext4_fs_bg_idx_to_addr(struct ext4_sblock *s,
-                                                    uint32_t index,
-                                                    uint32_t bgid)
-{
-       if (ext4_get32(s, first_data_block))
-               index++;
-
-       return ext4_get32(s, blocks_per_group) * bgid + index;
-}
-
-/**@brief TODO: */
-static inline ext4_fsblk_t ext4_fs_first_bg_block_no(struct ext4_sblock *s,
-                                                uint32_t bgid)
-{
-       return (uint64_t)bgid * ext4_get32(s, blocks_per_group) +
-              ext4_get32(s, first_data_block);
-}
-
-/**@brief Initialize filesystem and read all needed data.
- * @param fs Filesystem instance to be initialized
- * @param bdev Identifier if device with the filesystem
- * @return Error code
- */
-int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev);
-
-/**@brief Destroy filesystem instance (used by unmount operation).
- * @param fs Filesystem to be destroyed
- * @return Error code
- */
-int ext4_fs_fini(struct ext4_fs *fs);
-
-/**@brief Check filesystem's features, if supported by this driver
- * Function can return EOK and set read_only flag. It mean's that
- * there are some not-supported features, that can cause problems
- * during some write operations.
- * @param fs        Filesystem to be checked
- * @param read_only Flag if filesystem should be mounted only for reading
- * @return Error code
- */
-int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only);
-
-/**@brief Get reference to block group specified by index.
- * @param fs   Filesystem to find block group on
- * @param bgid Index of block group to load
- * @param ref  Output pointer for reference
- * @return Error code
- */
-int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid,
-                               struct ext4_block_group_ref *ref);
-
-/**@brief Put reference to block group.
- * @param ref Pointer for reference to be put back
- * @return Error code
- */
-int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref);
-
-/**@brief Get reference to i-node specified by index.
- * @param fs    Filesystem to find i-node on
- * @param index Index of i-node to load
- * @param ref   Output pointer for reference
- * @return Error code
- */
-int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
-                         struct ext4_inode_ref *ref);
-
-/**@brief Reset blocks field of i-node.
- * @param fs        Filesystem to reset blocks field of i-inode on
- * @param inode_ref ref Pointer for inode to be operated on
- */
-void ext4_fs_inode_blocks_init(struct ext4_fs *fs,
-                              struct ext4_inode_ref *inode_ref);
-
-/**@brief Put reference to i-node.
- * @param ref Pointer for reference to be put back
- * @return Error code
- */
-int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref);
-
-/**@brief Convert filetype to inode mode.
- * @param filetype
- * @return inode mode
- */
-uint32_t ext4_fs_correspond_inode_mode(int filetype);
-
-/**@brief Allocate new i-node in the filesystem.
- * @param fs        Filesystem to allocated i-node on
- * @param inode_ref Output pointer to return reference to allocated i-node
- * @param filetype  File type of newly created i-node
- * @return Error code
- */
-int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
-                       int filetype);
-
-/**@brief Release i-node and mark it as free.
- * @param inode_ref I-node to be released
- * @return Error code
- */
-int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref);
-
-/**@brief Truncate i-node data blocks.
- * @param inode_ref I-node to be truncated
- * @param new_size  New size of inode (must be < current size)
- * @return Error code
- */
-int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size);
-
-/**@brief Compute 'goal' for inode index
- * @param inode_ref Reference to inode, to allocate block for
- * @return goal
- */
-ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref);
-
-/**@brief Compute 'goal' for allocation algorithm (For blockmap).
- * @param inode_ref Reference to inode, to allocate block for
- * @param goal
- * @return error code
- */
-int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
-                               ext4_fsblk_t *goal);
-
-/**@brief Get physical block address by logical index of the block.
- * @param inode_ref I-node to read block address from
- * @param iblock            Logical index of block
- * @param fblock            Output pointer for return physical
- *                          block address
- * @param support_unwritten Indicate whether unwritten block range
- *                          is supported under the current context
- * @return Error code
- */
-int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
-                                uint64_t iblock, ext4_fsblk_t *fblock,
-                                bool support_unwritten);
-
-/**@brief Initialize a part of unwritten range of the inode.
- * @param inode_ref I-node to proceed on.
- * @param iblock    Logical index of block
- * @param fblock    Output pointer for return physical block address
- * @return Error code
- */
-int ext4_fs_init_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
-                                 uint64_t iblock, ext4_fsblk_t *fblock);
-
-/**@brief Append following logical block to the i-node.
- * @param inode_ref I-node to append block to
- * @param fblock    Output physical block address of newly allocated block
- * @param iblock    Output logical number of newly allocated block
- * @return Error code
- */
-int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref,
-                             ext4_fsblk_t *fblock, uint32_t *iblock);
-
-/**@brief   Increment inode link count.
- * @param   inode none handle
- */
-void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref);
-
-/**@brief   Decrement inode link count.
- * @param   inode none handle
- */
-void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_FS_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_hash.c b/lwext4/ext4_hash.c
deleted file mode 100644 (file)
index 4f97eac..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * 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;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_hash.h b/lwext4/ext4_hash.h
deleted file mode 100644 (file)
index b7a9ac5..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#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);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_HASH_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_ialloc.c b/lwext4/ext4_ialloc.c
deleted file mode 100644 (file)
index bbdcd4d..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * 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_crc32.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_inode_to_bgidx(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_bgidx_to_inode(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;
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_ialloc_bitmap_csum(struct ext4_sblock *sb,        void *bitmap)
-{
-       uint32_t csum = 0;
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint32_t inodes_per_group =
-                       ext4_get32(sb, inodes_per_group);
-
-               /* First calculate crc32 checksum against fs uuid */
-               csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
-               /* Then calculate crc32 checksum against inode bitmap */
-               csum = ext4_crc32c(csum, bitmap, (inodes_per_group + 7) / 8);
-       }
-       return csum;
-}
-#else
-#define ext4_ialloc_bitmap_csum(...) 0
-#endif
-
-void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
-                                void *bitmap __unused)
-{
-       int desc_size = ext4_sb_get_desc_size(sb);
-       uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap);
-       uint16_t lo_csum = to_le16(csum & 0xFFFF),
-                hi_csum = to_le16(csum >> 16);
-
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return;
-
-       /* See if we need to assign a 32bit checksum */
-       bg->inode_bitmap_csum_lo = lo_csum;
-       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               bg->inode_bitmap_csum_hi = hi_csum;
-
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool
-ext4_ialloc_verify_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
-                              void *bitmap __unused)
-{
-
-       int desc_size = ext4_sb_get_desc_size(sb);
-       uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap);
-       uint16_t lo_csum = to_le16(csum & 0xFFFF),
-                hi_csum = to_le16(csum >> 16);
-
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return true;
-
-       if (bg->inode_bitmap_csum_lo != lo_csum)
-               return false;
-
-       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               if (bg->inode_bitmap_csum_hi != hi_csum)
-                       return false;
-
-       return true;
-}
-#else
-#define ext4_ialloc_verify_bitmap_csum(...) true
-#endif
-
-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;
-
-       struct ext4_bgroup *bg = bg_ref.block_group;
-
-       /* Load i-node bitmap */
-       ext4_fsblk_t bitmap_block_addr =
-           ext4_bg_get_inode_bitmap(bg, sb);
-
-       struct ext4_block b;
-       rc = ext4_trans_block_get(fs->bdev, &b, bitmap_block_addr);
-       if (rc != EOK)
-               return rc;
-
-       if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) {
-               ext4_dbg(DEBUG_IALLOC,
-                       DBG_WARN "Bitmap checksum failed."
-                       "Group: %" PRIu32"\n",
-                       bg_ref.index);
-       }
-
-       /* Free i-node in the bitmap */
-       uint32_t index_in_group = ext4_ialloc_inode_to_bgidx(sb, index);
-       ext4_bmap_bit_clr(b.data, index_in_group);
-       ext4_ialloc_set_bitmap_csum(sb, bg, b.data);
-       ext4_trans_set_block_dirty(b.buf);
-
-       /* Put back the block with bitmap */
-       rc = ext4_block_set(fs->bdev, &b);
-       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, sb);
-               bg_used_dirs--;
-               ext4_bg_set_used_dirs_count(bg, sb, bg_used_dirs);
-       }
-
-       /* Update block group free inodes count */
-       uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
-       free_inodes++;
-       ext4_bg_set_free_inodes_count(bg, 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 *idx, 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 */
-                       ext4_fsblk_t bmp_blk_add = ext4_bg_get_inode_bitmap(bg, sb);
-
-                       struct ext4_block b;
-                       rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_add);
-                       if (rc != EOK) {
-                               ext4_fs_put_block_group_ref(&bg_ref);
-                               return rc;
-                       }
-
-                       if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) {
-                               ext4_dbg(DEBUG_IALLOC,
-                                       DBG_WARN "Bitmap checksum failed."
-                                       "Group: %" PRIu32"\n",
-                                       bg_ref.index);
-                       }
-
-                       /* Try to allocate i-node in the bitmap */
-                       uint32_t inodes_in_bg;
-                       uint32_t idx_in_bg;
-
-                       inodes_in_bg = ext4_inodes_in_group_cnt(sb, bgid);
-                       rc = ext4_bmap_bit_find_clr(b.data, 0, inodes_in_bg,
-                                                   &idx_in_bg);
-                       /* Block group has not any free i-node */
-                       if (rc == ENOSPC) {
-                               rc = ext4_block_set(fs->bdev, &b);
-                               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(b.data, idx_in_bg);
-
-                       /* Free i-node found, save the bitmap */
-                       ext4_ialloc_set_bitmap_csum(sb,bg,
-                                                   b.data);
-                       ext4_trans_set_block_dirty(b.buf);
-
-                       ext4_block_set(fs->bdev, &b);
-                       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 */
-                       uint32_t unused =
-                           ext4_bg_get_itable_unused(bg, sb);
-
-                       uint32_t free = inodes_in_bg - unused;
-
-                       if (idx_in_bg >= free) {
-                               unused = inodes_in_bg - (idx_in_bg + 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 */
-                       *idx = ext4_ialloc_bgidx_to_inode(sb, idx_in_bg, 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;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_ialloc.h b/lwext4/ext4_ialloc.h
deleted file mode 100644 (file)
index cea3fe6..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 Calculate and set checksum of inode bitmap.
- * @param sb superblock pointer.
- * @param bg block group
- * @param bitmap bitmap buffer
- */
-void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
-                                void *bitmap);
-
-/**@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_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_inode.c b/lwext4/ext4_inode.c
deleted file mode 100644 (file)
index 9b6eb87..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * 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_csum(struct ext4_sblock *sb, struct ext4_inode *inode)
-{
-       uint16_t inode_size = ext4_get16(sb, inode_size);
-       uint32_t v = to_le16(inode->osd2.linux2.checksum_lo);
-
-       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE)
-               v |= ((uint32_t)to_le16(inode->checksum_hi)) << 16;
-
-       return v;
-}
-
-void ext4_inode_set_csum(struct ext4_sblock *sb, struct ext4_inode *inode,
-                       uint32_t checksum)
-{
-       uint16_t inode_size = ext4_get16(sb, inode_size);
-       inode->osd2.linux2.checksum_lo =
-               to_le16((checksum << 16) >> 16);
-
-       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE)
-               inode->checksum_hi = to_le16(checksum >> 16);
-
-}
-
-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_modif_time(struct ext4_inode *inode)
-{
-       return to_le32(inode->modification_time);
-}
-
-void ext4_inode_set_modif_time(struct ext4_inode *inode, uint32_t time)
-{
-       inode->modification_time = to_le32(time);
-}
-
-uint32_t ext4_inode_get_del_time(struct ext4_inode *inode)
-{
-       return to_le32(inode->deletion_time);
-}
-
-void ext4_inode_set_del_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_cnt(struct ext4_inode *inode)
-{
-       return to_le16(inode->links_count);
-}
-void ext4_inode_set_links_cnt(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 cnt = to_le32(inode->blocks_count_lo);
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE)) {
-
-               /* 48-bit field */
-               cnt |= (uint64_t)to_le16(inode->osd2.linux2.blocks_high) << 32;
-
-               if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) {
-
-                       uint32_t block_count = ext4_sb_get_block_size(sb);
-                       uint32_t b = ext4_inode_block_bits_count(block_count);
-                       return cnt << (b - 9);
-               }
-       }
-
-       return cnt;
-}
-
-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((uint32_t)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_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE))
-               return EINVAL;
-
-       /* 48-bit maximum */
-       max = 0;
-       max = ~max >> 16;
-
-       if (count <= max) {
-               inode->blocks_count_lo = to_le32((uint32_t)count);
-               inode->osd2.linux2.blocks_high = to_le16((uint16_t)(count >> 32));
-               ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
-       } else {
-               uint32_t block_count = ext4_sb_get_block_size(sb);
-               uint32_t block_bits =ext4_inode_block_bits_count(block_count);
-
-               ext4_inode_set_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
-               count = count >> (block_bits - 9);
-               inode->blocks_count_lo = to_le32((uint32_t)count);
-               inode->osd2.linux2.blocks_high = to_le16((uint16_t)(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);
-}
-
-uint16_t ext4_inode_get_extra_isize(struct ext4_inode *inode)
-{
-       return to_le16(inode->extra_isize);
-}
-
-void ext4_inode_set_extra_isize(struct ext4_inode *inode, uint16_t size)
-{
-       inode->extra_isize = to_le16(size);
-}
-
-uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode,
-                                struct ext4_sblock *sb)
-{
-       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)
-{
-       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((uint16_t)(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);
-}
-
-uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode)
-{
-       return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK);
-}
-
-bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
-                       uint32_t type)
-{
-       return ext4_inode_type(sb, inode) == 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)) ||
-           (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_SOFTLINK)))
-               return true;
-
-       return false;
-}
-
-struct ext4_extent_header *
-ext4_inode_get_extent_header(struct ext4_inode *inode)
-{
-       return (struct ext4_extent_header *)inode->blocks;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_inode.h b/lwext4/ext4_inode.h
deleted file mode 100644 (file)
index 90c5754..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * 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_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#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_modif_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_modif_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_del_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_del_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_cnt(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_cnt(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 extra I-node size field.
- * @param inode      I-node
- * @return extra I-node size
- */
-uint16_t ext4_inode_get_extra_isize(struct ext4_inode *inode);
-
-/**@brief Set extra I-node size field.
- * @param inode      I-node
- * @param size       extra I-node size
- */
-void ext4_inode_set_extra_isize(struct ext4_inode *inode, uint16_t size);
-
-/**@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 return the type of i-node
- * @param sb    Superblock
- * @param inode I-node to return the type of
- * @return Result of check operation
- */
-uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode);
-
-/**@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 Get inode checksum(crc32)
- * @param sb    Superblock
- * @param inode I-node to get checksum value from
- */
-uint32_t
-ext4_inode_get_csum(struct ext4_sblock *sb, struct ext4_inode *inode);
-
-/**@brief Get inode checksum(crc32)
- * @param sb    Superblock
- * @param inode I-node to get checksum value from
- */
-void
-ext4_inode_set_csum(struct ext4_sblock *sb, struct ext4_inode *inode,
-                       uint32_t checksum);
-
-/**@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);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_INODE_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_journal.c b/lwext4/ext4_journal.c
deleted file mode 100644 (file)
index 73a84bf..0000000
+++ /dev/null
@@ -1,2158 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@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_journal.c
- * @brief Journal handle functions
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_fs.h"
-#include "ext4_super.h"
-#include "ext4_journal.h"
-#include "ext4_errno.h"
-#include "ext4_blockdev.h"
-#include "ext4_crc32.h"
-#include "ext4_debug.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-/**@brief  Revoke entry during journal replay.*/
-struct revoke_entry {
-       /**@brief  Block number not to be replayed.*/
-       ext4_fsblk_t block;
-
-       /**@brief  For any transaction id smaller
-        *         than trans_id, records of @block
-        *         in those transactions should not
-        *         be replayed.*/
-       uint32_t trans_id;
-
-       /**@brief  Revoke tree node.*/
-       RB_ENTRY(revoke_entry) revoke_node;
-};
-
-/**@brief  Valid journal replay information.*/
-struct recover_info {
-       /**@brief  Starting transaction id.*/
-       uint32_t start_trans_id;
-
-       /**@brief  Ending transaction id.*/
-       uint32_t last_trans_id;
-
-       /**@brief  Used as internal argument.*/
-       uint32_t this_trans_id;
-
-       /**@brief  No of transactions went through.*/
-       uint32_t trans_cnt;
-
-       /**@brief  RB-Tree storing revoke entries.*/
-       RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
-};
-
-/**@brief  Journal replay internal arguments.*/
-struct replay_arg {
-       /**@brief  Journal replay information.*/
-       struct recover_info *info;
-
-       /**@brief  Current block we are on.*/
-       uint32_t *this_block;
-
-       /**@brief  Current trans_id we are on.*/
-       uint32_t this_trans_id;
-};
-
-static int
-jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
-{
-       if (a->block > b->block)
-               return 1;
-       else if (a->block < b->block)
-               return -1;
-       return 0;
-}
-
-static int
-jbd_block_rec_cmp(struct jbd_block_rec *a, struct jbd_block_rec *b)
-{
-       if (a->lba > b->lba)
-               return 1;
-       else if (a->lba < b->lba)
-               return -1;
-       return 0;
-}
-
-RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
-                    jbd_revoke_entry_cmp, static inline)
-RB_GENERATE_INTERNAL(jbd_block, jbd_block_rec, block_rec_node,
-                    jbd_block_rec_cmp, static inline)
-
-#define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
-#define jbd_free_revoke_entry(addr) free(addr)
-
-static int jbd_has_csum(struct jbd_sb *jbd_sb)
-{
-       if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V2))
-               return 2;
-
-       if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V3))
-               return 3;
-
-       return 0;
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t jbd_sb_csum(struct jbd_sb *jbd_sb)
-{
-       uint32_t checksum = 0;
-
-       if (jbd_has_csum(jbd_sb)) {
-               uint32_t orig_checksum = jbd_sb->checksum;
-               jbd_set32(jbd_sb, checksum, 0);
-               /* Calculate crc32c checksum against tho whole superblock */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_sb,
-                               JBD_SUPERBLOCK_SIZE);
-               jbd_sb->checksum = orig_checksum;
-       }
-       return checksum;
-}
-#else
-#define jbd_sb_csum(...) 0
-#endif
-
-static void jbd_sb_csum_set(struct jbd_sb *jbd_sb)
-{
-       if (!jbd_has_csum(jbd_sb))
-               return;
-
-       jbd_set32(jbd_sb, checksum, jbd_sb_csum(jbd_sb));
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool
-jbd_verify_sb_csum(struct jbd_sb *jbd_sb)
-{
-       if (!jbd_has_csum(jbd_sb))
-               return true;
-
-       return jbd_sb_csum(jbd_sb) == jbd_get32(jbd_sb, checksum);
-}
-#else
-#define jbd_verify_sb_csum(...) true
-#endif
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t jbd_meta_csum(struct jbd_fs *jbd_fs,
-                             struct jbd_bhdr *bhdr)
-{
-       uint32_t checksum = 0;
-
-       if (jbd_has_csum(&jbd_fs->sb)) {
-               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
-               struct jbd_block_tail *tail =
-                       (struct jbd_block_tail *)((char *)bhdr + block_size -
-                               sizeof(struct jbd_block_tail));
-               uint32_t orig_checksum = tail->checksum;
-               tail->checksum = 0;
-
-               /* First calculate crc32c checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
-                                      sizeof(jbd_fs->sb.uuid));
-               /* Calculate crc32c checksum against tho whole block */
-               checksum = ext4_crc32c(checksum, bhdr,
-                               block_size);
-               tail->checksum = orig_checksum;
-       }
-       return checksum;
-}
-#else
-#define jbd_meta_csum(...) 0
-#endif
-
-static void jbd_meta_csum_set(struct jbd_fs *jbd_fs,
-                             struct jbd_bhdr *bhdr)
-{
-       uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
-       struct jbd_block_tail *tail = (struct jbd_block_tail *)
-                               ((char *)bhdr + block_size -
-                               sizeof(struct jbd_block_tail));
-       if (!jbd_has_csum(&jbd_fs->sb))
-               return;
-
-       tail->checksum = to_be32(jbd_meta_csum(jbd_fs, bhdr));
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool
-jbd_verify_meta_csum(struct jbd_fs *jbd_fs,
-                    struct jbd_bhdr *bhdr)
-{
-       uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
-       struct jbd_block_tail *tail = (struct jbd_block_tail *)
-                               ((char *)bhdr + block_size -
-                               sizeof(struct jbd_block_tail));
-       if (!jbd_has_csum(&jbd_fs->sb))
-               return true;
-
-       return jbd_meta_csum(jbd_fs, bhdr) == to_be32(tail->checksum);
-}
-#else
-#define jbd_verify_meta_csum(...) true
-#endif
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t jbd_commit_csum(struct jbd_fs *jbd_fs,
-                             struct jbd_commit_header *header)
-{
-       uint32_t checksum = 0;
-
-       if (jbd_has_csum(&jbd_fs->sb)) {
-               uint32_t orig_checksum_type = header->chksum_type,
-                        orig_checksum_size = header->chksum_size,
-                        orig_checksum = header->chksum[0];
-               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
-               header->chksum_type = 0;
-               header->chksum_size = 0;
-               header->chksum[0] = 0;
-
-               /* First calculate crc32c checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
-                                      sizeof(jbd_fs->sb.uuid));
-               /* Calculate crc32c checksum against tho whole block */
-               checksum = ext4_crc32c(checksum, header,
-                               block_size);
-
-               header->chksum_type = orig_checksum_type;
-               header->chksum_size = orig_checksum_size;
-               header->chksum[0] = orig_checksum;
-       }
-       return checksum;
-}
-#else
-#define jbd_commit_csum(...) 0
-#endif
-
-static void jbd_commit_csum_set(struct jbd_fs *jbd_fs,
-                             struct jbd_commit_header *header)
-{
-       if (!jbd_has_csum(&jbd_fs->sb))
-               return;
-
-       header->chksum_type = 0;
-       header->chksum_size = 0;
-       header->chksum[0] = jbd_commit_csum(jbd_fs, header);
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static bool jbd_verify_commit_csum(struct jbd_fs *jbd_fs,
-                                  struct jbd_commit_header *header)
-{
-       if (!jbd_has_csum(&jbd_fs->sb))
-               return true;
-
-       return header->chksum[0] == to_be32(jbd_commit_csum(jbd_fs,
-                                           header));
-}
-#else
-#define jbd_verify_commit_csum(...) true
-#endif
-
-#if CONFIG_META_CSUM_ENABLE
-/*
- * NOTE: We only make use of @csum parameter when
- *       JBD_FEATURE_COMPAT_CHECKSUM is enabled.
- */
-static uint32_t jbd_block_csum(struct jbd_fs *jbd_fs, const void *buf,
-                              uint32_t csum,
-                              uint32_t sequence)
-{
-       uint32_t checksum = 0;
-
-       if (jbd_has_csum(&jbd_fs->sb)) {
-               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
-               /* First calculate crc32c checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
-                                      sizeof(jbd_fs->sb.uuid));
-               /* Then calculate crc32c checksum against sequence no. */
-               checksum = ext4_crc32c(checksum, &sequence,
-                               sizeof(uint32_t));
-               /* Calculate crc32c checksum against tho whole block */
-               checksum = ext4_crc32c(checksum, buf,
-                               block_size);
-       } else if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_COMPAT_CHECKSUM)) {
-               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
-               /* Calculate crc32c checksum against tho whole block */
-               checksum = ext4_crc32(csum, buf,
-                               block_size);
-       }
-       return checksum;
-}
-#else
-#define jbd_block_csum(...) 0
-#endif
-
-static void jbd_block_tag_csum_set(struct jbd_fs *jbd_fs, void *__tag,
-                                  uint32_t checksum)
-{
-       int ver = jbd_has_csum(&jbd_fs->sb);
-       if (!ver)
-               return;
-
-       if (ver == 2) {
-               struct jbd_block_tag *tag = __tag;
-               tag->checksum = (uint16_t)to_be32(checksum);
-       } else {
-               struct jbd_block_tag3 *tag = __tag;
-               tag->checksum = to_be32(checksum);
-       }
-}
-
-/**@brief  Write jbd superblock to disk.
- * @param  jbd_fs jbd filesystem
- * @param  s jbd superblock
- * @return standard error code*/
-static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
-{
-       int rc;
-       struct ext4_fs *fs = jbd_fs->inode_ref.fs;
-       uint64_t offset;
-       ext4_fsblk_t fblock;
-       rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
-       if (rc != EOK)
-               return rc;
-
-       jbd_sb_csum_set(s);
-       offset = fblock * ext4_sb_get_block_size(&fs->sb);
-       return ext4_block_writebytes(fs->bdev, offset, s,
-                                    EXT4_SUPERBLOCK_SIZE);
-}
-
-/**@brief  Read jbd superblock from disk.
- * @param  jbd_fs jbd filesystem
- * @param  s jbd superblock
- * @return standard error code*/
-static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
-{
-       int rc;
-       struct ext4_fs *fs = jbd_fs->inode_ref.fs;
-       uint64_t offset;
-       ext4_fsblk_t fblock;
-       rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
-       if (rc != EOK)
-               return rc;
-
-       offset = fblock * ext4_sb_get_block_size(&fs->sb);
-       return ext4_block_readbytes(fs->bdev, offset, s,
-                                   EXT4_SUPERBLOCK_SIZE);
-}
-
-/**@brief  Verify jbd superblock.
- * @param  sb jbd superblock
- * @return true if jbd superblock is valid */
-static bool jbd_verify_sb(struct jbd_sb *sb)
-{
-       struct jbd_bhdr *header = &sb->header;
-       if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
-               return false;
-
-       if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
-           jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
-               return false;
-
-       return jbd_verify_sb_csum(sb);
-}
-
-/**@brief  Write back dirty jbd superblock to disk.
- * @param  jbd_fs jbd filesystem
- * @return standard error code*/
-static int jbd_write_sb(struct jbd_fs *jbd_fs)
-{
-       int rc = EOK;
-       if (jbd_fs->dirty) {
-               rc = jbd_sb_write(jbd_fs, &jbd_fs->sb);
-               if (rc != EOK)
-                       return rc;
-
-               jbd_fs->dirty = false;
-       }
-       return rc;
-}
-
-/**@brief  Get reference to jbd filesystem.
- * @param  fs Filesystem to load journal of
- * @param  jbd_fs jbd filesystem
- * @return standard error code*/
-int jbd_get_fs(struct ext4_fs *fs,
-              struct jbd_fs *jbd_fs)
-{
-       int rc;
-       uint32_t journal_ino;
-
-       memset(jbd_fs, 0, sizeof(struct jbd_fs));
-       /* See if there is journal inode on this filesystem.*/
-       /* FIXME: detection on existance ofbkejournal bdev is
-        *        missing.*/
-       journal_ino = ext4_get32(&fs->sb, journal_inode_number);
-
-       rc = ext4_fs_get_inode_ref(fs,
-                                  journal_ino,
-                                  &jbd_fs->inode_ref);
-       if (rc != EOK) {
-               memset(jbd_fs, 0, sizeof(struct jbd_fs));
-               return rc;
-       }
-       rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
-       if (rc != EOK) {
-               memset(jbd_fs, 0, sizeof(struct jbd_fs));
-               ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
-               return rc;
-       }
-       if (!jbd_verify_sb(&jbd_fs->sb)) {
-               memset(jbd_fs, 0, sizeof(struct jbd_fs));
-               ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
-               rc = EIO;
-       }
-
-       return rc;
-}
-
-/**@brief  Put reference of jbd filesystem.
- * @param  jbd_fs jbd filesystem
- * @return standard error code*/
-int jbd_put_fs(struct jbd_fs *jbd_fs)
-{
-       int rc = EOK;
-       rc = jbd_write_sb(jbd_fs);
-
-       ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
-       return rc;
-}
-
-/**@brief  Data block lookup helper.
- * @param  jbd_fs jbd filesystem
- * @param  iblock block index
- * @param  fblock logical block address
- * @return standard error code*/
-int jbd_inode_bmap(struct jbd_fs *jbd_fs,
-                  ext4_lblk_t iblock,
-                  ext4_fsblk_t *fblock)
-{
-       int rc = ext4_fs_get_inode_dblk_idx(
-                       &jbd_fs->inode_ref,
-                       iblock,
-                       fblock,
-                       false);
-       return rc;
-}
-
-/**@brief   jbd block get function (through cache).
- * @param   jbd_fs jbd filesystem
- * @param   block block descriptor
- * @param   fblock jbd logical block address
- * @return  standard error code*/
-static int jbd_block_get(struct jbd_fs *jbd_fs,
-                 struct ext4_block *block,
-                 ext4_fsblk_t fblock)
-{
-       /* TODO: journal device. */
-       int rc;
-       ext4_lblk_t iblock = (ext4_lblk_t)fblock;
-
-       /* Lookup the logical block address of
-        * fblock.*/
-       rc = jbd_inode_bmap(jbd_fs, iblock,
-                           &fblock);
-       if (rc != EOK)
-               return rc;
-
-       struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
-       rc = ext4_block_get(bdev, block, fblock);
-
-       /* If succeeded, mark buffer as BC_FLUSH to indicate
-        * that data should be written to disk immediately.*/
-       if (rc == EOK) {
-               ext4_bcache_set_flag(block->buf, BC_FLUSH);
-               /* As we don't want to occupy too much space
-                * in block cache, we set this buffer BC_TMP.*/
-               ext4_bcache_set_flag(block->buf, BC_TMP);
-       }
-
-       return rc;
-}
-
-/**@brief   jbd block get function (through cache, don't read).
- * @param   jbd_fs jbd filesystem
- * @param   block block descriptor
- * @param   fblock jbd logical block address
- * @return  standard error code*/
-static int jbd_block_get_noread(struct jbd_fs *jbd_fs,
-                        struct ext4_block *block,
-                        ext4_fsblk_t fblock)
-{
-       /* TODO: journal device. */
-       int rc;
-       ext4_lblk_t iblock = (ext4_lblk_t)fblock;
-       rc = jbd_inode_bmap(jbd_fs, iblock,
-                           &fblock);
-       if (rc != EOK)
-               return rc;
-
-       struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
-       rc = ext4_block_get_noread(bdev, block, fblock);
-       if (rc == EOK)
-               ext4_bcache_set_flag(block->buf, BC_FLUSH);
-
-       return rc;
-}
-
-/**@brief   jbd block set procedure (through cache).
- * @param   jbd_fs jbd filesystem
- * @param   block block descriptor
- * @return  standard error code*/
-static int jbd_block_set(struct jbd_fs *jbd_fs,
-                 struct ext4_block *block)
-{
-       return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
-                             block);
-}
-
-/**@brief  helper functions to calculate
- *         block tag size, not including UUID part.
- * @param  jbd_fs jbd filesystem
- * @return tag size in bytes*/
-static int jbd_tag_bytes(struct jbd_fs *jbd_fs)
-{
-       int size;
-
-       /* It is very easy to deal with the case which
-        * JBD_FEATURE_INCOMPAT_CSUM_V3 is enabled.*/
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_CSUM_V3))
-               return sizeof(struct jbd_block_tag3);
-
-       size = sizeof(struct jbd_block_tag);
-
-       /* If JBD_FEATURE_INCOMPAT_CSUM_V2 is enabled,
-        * add 2 bytes to size.*/
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_CSUM_V2))
-               size += sizeof(uint16_t);
-
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_64BIT))
-               return size;
-
-       /* If block number is 4 bytes in size,
-        * minus 4 bytes from size */
-       return size - sizeof(uint32_t);
-}
-
-/**@brief  Tag information. */
-struct tag_info {
-       /**@brief  Tag size in bytes, including UUID part.*/
-       int tag_bytes;
-
-       /**@brief  block number stored in this tag.*/
-       ext4_fsblk_t block;
-
-       /**@brief  whether UUID part exists or not.*/
-       bool uuid_exist;
-
-       /**@brief  UUID content if UUID part exists.*/
-       uint8_t uuid[UUID_SIZE];
-
-       /**@brief  Is this the last tag? */
-       bool last_tag;
-
-       /**@brief  crc32c checksum. */
-       uint32_t checksum;
-};
-
-/**@brief  Extract information from a block tag.
- * @param  __tag pointer to the block tag
- * @param  tag_bytes block tag size of this jbd filesystem
- * @param  remaining size in buffer containing the block tag
- * @param  tag_info information of this tag.
- * @return  EOK when succeed, otherwise return EINVAL.*/
-static int
-jbd_extract_block_tag(struct jbd_fs *jbd_fs,
-                     void *__tag,
-                     int tag_bytes,
-                     int32_t remain_buf_size,
-                     struct tag_info *tag_info)
-{
-       char *uuid_start;
-       tag_info->tag_bytes = tag_bytes;
-       tag_info->uuid_exist = false;
-       tag_info->last_tag = false;
-
-       /* See whether it is possible to hold a valid block tag.*/
-       if (remain_buf_size - tag_bytes < 0)
-               return EINVAL;
-
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_CSUM_V3)) {
-               struct jbd_block_tag3 *tag = __tag;
-               tag_info->block = jbd_get32(tag, blocknr);
-               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                            JBD_FEATURE_INCOMPAT_64BIT))
-                        tag_info->block |=
-                                (uint64_t)jbd_get32(tag, blocknr_high) << 32;
-
-               if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
-                       tag_info->block = 0;
-
-               if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
-                       /* See whether it is possible to hold UUID part.*/
-                       if (remain_buf_size - tag_bytes < UUID_SIZE)
-                               return EINVAL;
-
-                       uuid_start = (char *)tag + tag_bytes;
-                       tag_info->uuid_exist = true;
-                       tag_info->tag_bytes += UUID_SIZE;
-                       memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
-               }
-
-               if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
-                       tag_info->last_tag = true;
-
-       } else {
-               struct jbd_block_tag *tag = __tag;
-               tag_info->block = jbd_get32(tag, blocknr);
-               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                            JBD_FEATURE_INCOMPAT_64BIT))
-                        tag_info->block |=
-                                (uint64_t)jbd_get32(tag, blocknr_high) << 32;
-
-               if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
-                       tag_info->block = 0;
-
-               if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
-                       /* See whether it is possible to hold UUID part.*/
-                       if (remain_buf_size - tag_bytes < UUID_SIZE)
-                               return EINVAL;
-
-                       uuid_start = (char *)tag + tag_bytes;
-                       tag_info->uuid_exist = true;
-                       tag_info->tag_bytes += UUID_SIZE;
-                       memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
-               }
-
-               if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
-                       tag_info->last_tag = true;
-
-       }
-       return EOK;
-}
-
-/**@brief  Write information to a block tag.
- * @param  __tag pointer to the block tag
- * @param  remaining size in buffer containing the block tag
- * @param  tag_info information of this tag.
- * @return  EOK when succeed, otherwise return EINVAL.*/
-static int
-jbd_write_block_tag(struct jbd_fs *jbd_fs,
-                   void *__tag,
-                   int32_t remain_buf_size,
-                   struct tag_info *tag_info)
-{
-       char *uuid_start;
-       int tag_bytes = jbd_tag_bytes(jbd_fs);
-
-       tag_info->tag_bytes = tag_bytes;
-
-       /* See whether it is possible to hold a valid block tag.*/
-       if (remain_buf_size - tag_bytes < 0)
-               return EINVAL;
-
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_CSUM_V3)) {
-               struct jbd_block_tag3 *tag = __tag;
-               memset(tag, 0, sizeof(struct jbd_block_tag3));
-               jbd_set32(tag, blocknr, (uint32_t)tag_info->block);
-               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                            JBD_FEATURE_INCOMPAT_64BIT))
-                       jbd_set32(tag, blocknr_high, tag_info->block >> 32);
-
-               if (tag_info->uuid_exist) {
-                       /* See whether it is possible to hold UUID part.*/
-                       if (remain_buf_size - tag_bytes < UUID_SIZE)
-                               return EINVAL;
-
-                       uuid_start = (char *)tag + tag_bytes;
-                       tag_info->tag_bytes += UUID_SIZE;
-                       memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
-               } else
-                       jbd_set32(tag, flags,
-                                 jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID);
-
-               jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum);
-
-               if (tag_info->last_tag)
-                       jbd_set32(tag, flags,
-                                 jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG);
-
-       } else {
-               struct jbd_block_tag *tag = __tag;
-               memset(tag, 0, sizeof(struct jbd_block_tag));
-               jbd_set32(tag, blocknr, (uint32_t)tag_info->block);
-               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                            JBD_FEATURE_INCOMPAT_64BIT))
-                       jbd_set32(tag, blocknr_high, tag_info->block >> 32);
-
-               if (tag_info->uuid_exist) {
-                       /* See whether it is possible to hold UUID part.*/
-                       if (remain_buf_size - tag_bytes < UUID_SIZE)
-                               return EINVAL;
-
-                       uuid_start = (char *)tag + tag_bytes;
-                       tag_info->tag_bytes += UUID_SIZE;
-                       memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
-               } else
-                       jbd_set16(tag, flags,
-                                 jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID);
-
-               jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum);
-
-               if (tag_info->last_tag)
-                       jbd_set16(tag, flags,
-                                 jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG);
-
-       }
-       return EOK;
-}
-
-/**@brief  Iterate all block tags in a block.
- * @param  jbd_fs jbd filesystem
- * @param  __tag_start pointer to the block
- * @param  tag_tbl_size size of the block
- * @param  func callback routine to indicate that
- *         a block tag is found
- * @param  arg additional argument to be passed to func */
-static void
-jbd_iterate_block_table(struct jbd_fs *jbd_fs,
-                       void *__tag_start,
-                       int32_t tag_tbl_size,
-                       void (*func)(struct jbd_fs * jbd_fs,
-                                       ext4_fsblk_t block,
-                                       uint8_t *uuid,
-                                       void *arg),
-                       void *arg)
-{
-       char *tag_start, *tag_ptr;
-       int tag_bytes = jbd_tag_bytes(jbd_fs);
-       tag_start = __tag_start;
-       tag_ptr = tag_start;
-
-       /* Cut off the size of block tail storing checksum. */
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_CSUM_V2) ||
-           JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_CSUM_V3))
-               tag_tbl_size -= sizeof(struct jbd_block_tail);
-
-       while (tag_tbl_size) {
-               struct tag_info tag_info;
-               int rc = jbd_extract_block_tag(jbd_fs,
-                                     tag_ptr,
-                                     tag_bytes,
-                                     tag_tbl_size,
-                                     &tag_info);
-               if (rc != EOK)
-                       break;
-
-               if (func)
-                       func(jbd_fs, tag_info.block, tag_info.uuid, arg);
-
-               /* Stop the iteration when we reach the last tag. */
-               if (tag_info.last_tag)
-                       break;
-
-               tag_ptr += tag_info.tag_bytes;
-               tag_tbl_size -= tag_info.tag_bytes;
-       }
-}
-
-static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
-                                  ext4_fsblk_t block,
-                                  uint8_t *uuid,
-                                  void *arg)
-{
-       uint32_t *iblock = arg;
-       ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
-       (*iblock)++;
-       (void)jbd_fs;
-       (void)uuid;
-       return;
-}
-
-static struct revoke_entry *
-jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
-{
-       struct revoke_entry tmp = {
-               .block = block
-       };
-
-       return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
-}
-
-/**@brief  Replay a block in a transaction.
- * @param  jbd_fs jbd filesystem
- * @param  block  block address to be replayed.*/
-static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
-                                 ext4_fsblk_t block,
-                                 uint8_t *uuid __unused,
-                                 void *__arg)
-{
-       int r;
-       struct replay_arg *arg = __arg;
-       struct recover_info *info = arg->info;
-       uint32_t *this_block = arg->this_block;
-       struct revoke_entry *revoke_entry;
-       struct ext4_block journal_block, ext4_block;
-       struct ext4_fs *fs = jbd_fs->inode_ref.fs;
-
-       (*this_block)++;
-
-       /* We replay this block only if the current transaction id
-        * is equal or greater than that in revoke entry.*/
-       revoke_entry = jbd_revoke_entry_lookup(info, block);
-       if (revoke_entry &&
-           arg->this_trans_id < revoke_entry->trans_id)
-               return;
-
-       ext4_dbg(DEBUG_JBD,
-                "Replaying block in block_tag: %" PRIu64 "\n",
-                block);
-
-       r = jbd_block_get(jbd_fs, &journal_block, *this_block);
-       if (r != EOK)
-               return;
-
-       /* We need special treatment for ext4 superblock. */
-       if (block) {
-               r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
-               if (r != EOK) {
-                       jbd_block_set(jbd_fs, &journal_block);
-                       return;
-               }
-
-               memcpy(ext4_block.data,
-                       journal_block.data,
-                       jbd_get32(&jbd_fs->sb, blocksize));
-
-               ext4_bcache_set_dirty(ext4_block.buf);
-               ext4_block_set(fs->bdev, &ext4_block);
-       } else {
-               uint16_t mount_count, state;
-               mount_count = ext4_get16(&fs->sb, mount_count);
-               state = ext4_get16(&fs->sb, state);
-
-               memcpy(&fs->sb,
-                       journal_block.data + EXT4_SUPERBLOCK_OFFSET,
-                       EXT4_SUPERBLOCK_SIZE);
-
-               /* Mark system as mounted */
-               ext4_set16(&fs->sb, state, state);
-               r = ext4_sb_write(fs->bdev, &fs->sb);
-               if (r != EOK)
-                       return;
-
-               /*Update mount count*/
-               ext4_set16(&fs->sb, mount_count, mount_count);
-       }
-
-       jbd_block_set(jbd_fs, &journal_block);
-       
-       return;
-}
-
-/**@brief  Add block address to revoke tree, along with
- *         its transaction id.
- * @param  info  journal replay info
- * @param  block  block address to be replayed.*/
-static void jbd_add_revoke_block_tags(struct recover_info *info,
-                                     ext4_fsblk_t block)
-{
-       struct revoke_entry *revoke_entry;
-
-       ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
-       /* If the revoke entry with respect to the block address
-        * exists already, update its transaction id.*/
-       revoke_entry = jbd_revoke_entry_lookup(info, block);
-       if (revoke_entry) {
-               revoke_entry->trans_id = info->this_trans_id;
-               return;
-       }
-
-       revoke_entry = jbd_alloc_revoke_entry();
-       ext4_assert(revoke_entry);
-       revoke_entry->block = block;
-       revoke_entry->trans_id = info->this_trans_id;
-       RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
-
-       return;
-}
-
-static void jbd_destroy_revoke_tree(struct recover_info *info)
-{
-       while (!RB_EMPTY(&info->revoke_root)) {
-               struct revoke_entry *revoke_entry =
-                       RB_MIN(jbd_revoke, &info->revoke_root);
-               ext4_assert(revoke_entry);
-               RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
-               jbd_free_revoke_entry(revoke_entry);
-       }
-}
-
-/* Make sure we wrap around the log correctly! */
-#define wrap(sb, var)                                          \
-do {                                                                   \
-       if (var >= jbd_get32((sb), maxlen))                                     \
-               var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first));      \
-} while (0)
-
-#define ACTION_SCAN 0
-#define ACTION_REVOKE 1
-#define ACTION_RECOVER 2
-
-/**@brief  Add entries in a revoke block to revoke tree.
- * @param  jbd_fs jbd filesystem
- * @param  header revoke block header
- * @param  recover_info  journal replay info*/
-static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
-                                 struct jbd_bhdr *header,
-                                 struct recover_info *info)
-{
-       char *blocks_entry;
-       struct jbd_revoke_header *revoke_hdr =
-               (struct jbd_revoke_header *)header;
-       uint32_t i, nr_entries, record_len = 4;
-
-       /* If we are working on a 64bit jbd filesystem, */
-       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_64BIT))
-               record_len = 8;
-
-       nr_entries = (jbd_get32(revoke_hdr, count) -
-                       sizeof(struct jbd_revoke_header)) /
-                       record_len;
-
-       blocks_entry = (char *)(revoke_hdr + 1);
-
-       for (i = 0;i < nr_entries;i++) {
-               if (record_len == 8) {
-                       uint64_t *blocks =
-                               (uint64_t *)blocks_entry;
-                       jbd_add_revoke_block_tags(info, to_be64(*blocks));
-               } else {
-                       uint32_t *blocks =
-                               (uint32_t *)blocks_entry;
-                       jbd_add_revoke_block_tags(info, to_be32(*blocks));
-               }
-               blocks_entry += record_len;
-       }
-}
-
-static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
-                                      struct jbd_bhdr *header,
-                                      uint32_t *iblock)
-{
-       jbd_iterate_block_table(jbd_fs,
-                               header + 1,
-                               jbd_get32(&jbd_fs->sb, blocksize) -
-                                       sizeof(struct jbd_bhdr),
-                               jbd_display_block_tags,
-                               iblock);
-}
-
-static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
-                                       struct jbd_bhdr *header,
-                                       struct replay_arg *arg)
-{
-       jbd_iterate_block_table(jbd_fs,
-                               header + 1,
-                               jbd_get32(&jbd_fs->sb, blocksize) -
-                                       sizeof(struct jbd_bhdr),
-                               jbd_replay_block_tags,
-                               arg);
-}
-
-/**@brief  The core routine of journal replay.
- * @param  jbd_fs jbd filesystem
- * @param  recover_info  journal replay info
- * @param  action action needed to be taken
- * @return standard error code*/
-static int jbd_iterate_log(struct jbd_fs *jbd_fs,
-                          struct recover_info *info,
-                          int action)
-{
-       int r = EOK;
-       bool log_end = false;
-       struct jbd_sb *sb = &jbd_fs->sb;
-       uint32_t start_trans_id, this_trans_id;
-       uint32_t start_block, this_block;
-
-       /* We start iterating valid blocks in the whole journal.*/
-       start_trans_id = this_trans_id = jbd_get32(sb, sequence);
-       start_block = this_block = jbd_get32(sb, start);
-       if (action == ACTION_SCAN)
-               info->trans_cnt = 0;
-       else if (!info->trans_cnt)
-               log_end = true;
-
-       ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
-                           start_trans_id);
-
-       while (!log_end) {
-               struct ext4_block block;
-               struct jbd_bhdr *header;
-               /* If we are not scanning for the last
-                * valid transaction in the journal,
-                * we will stop when we reach the end of
-                * the journal.*/
-               if (action != ACTION_SCAN)
-                       if (this_trans_id > info->last_trans_id) {
-                               log_end = true;
-                               continue;
-                       }
-
-               r = jbd_block_get(jbd_fs, &block, this_block);
-               if (r != EOK)
-                       break;
-
-               header = (struct jbd_bhdr *)block.data;
-               /* This block does not have a valid magic number,
-                * so we have reached the end of the journal.*/
-               if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
-                       jbd_block_set(jbd_fs, &block);
-                       log_end = true;
-                       continue;
-               }
-
-               /* If the transaction id we found is not expected,
-                * we may have reached the end of the journal.
-                *
-                * If we are not scanning the journal, something
-                * bad might have taken place. :-( */
-               if (jbd_get32(header, sequence) != this_trans_id) {
-                       if (action != ACTION_SCAN)
-                               r = EIO;
-
-                       jbd_block_set(jbd_fs, &block);
-                       log_end = true;
-                       continue;
-               }
-
-               switch (jbd_get32(header, blocktype)) {
-               case JBD_DESCRIPTOR_BLOCK:
-                       if (!jbd_verify_meta_csum(jbd_fs, header)) {
-                               ext4_dbg(DEBUG_JBD,
-                                       DBG_WARN "Descriptor block checksum failed."
-                                               "Journal block: %" PRIu32"\n",
-                                               this_block);
-                               log_end = true;
-                               break;
-                       }
-                       ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
-                                           "trans_id: %" PRIu32"\n",
-                                           this_block, this_trans_id);
-                       if (action == ACTION_RECOVER) {
-                               struct replay_arg replay_arg;
-                               replay_arg.info = info;
-                               replay_arg.this_block = &this_block;
-                               replay_arg.this_trans_id = this_trans_id;
-
-                               jbd_replay_descriptor_block(jbd_fs,
-                                               header, &replay_arg);
-                       } else
-                               jbd_debug_descriptor_block(jbd_fs,
-                                               header, &this_block);
-
-                       break;
-               case JBD_COMMIT_BLOCK:
-                       if (!jbd_verify_commit_csum(jbd_fs,
-                                       (struct jbd_commit_header *)header)) {
-                               ext4_dbg(DEBUG_JBD,
-                                       DBG_WARN "Commit block checksum failed."
-                                               "Journal block: %" PRIu32"\n",
-                                               this_block);
-                               log_end = true;
-                               break;
-                       }
-                       ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
-                                           "trans_id: %" PRIu32"\n",
-                                           this_block, this_trans_id);
-                       /* This is the end of a transaction,
-                        * we may now proceed to the next transaction.
-                        */
-                       this_trans_id++;
-                       info->trans_cnt++;
-                       break;
-               case JBD_REVOKE_BLOCK:
-                       if (!jbd_verify_meta_csum(jbd_fs, header)) {
-                               ext4_dbg(DEBUG_JBD,
-                                       DBG_WARN "Revoke block checksum failed."
-                                               "Journal block: %" PRIu32"\n",
-                                               this_block);
-                               log_end = true;
-                               break;
-                       }
-                       ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
-                                           "trans_id: %" PRIu32"\n",
-                                           this_block, this_trans_id);
-                       if (action == ACTION_REVOKE) {
-                               info->this_trans_id = this_trans_id;
-                               jbd_build_revoke_tree(jbd_fs,
-                                               header, info);
-                       }
-                       break;
-               default:
-                       log_end = true;
-                       break;
-               }
-               jbd_block_set(jbd_fs, &block);
-               this_block++;
-               wrap(sb, this_block);
-               if (this_block == start_block)
-                       log_end = true;
-
-       }
-       ext4_dbg(DEBUG_JBD, "End of journal.\n");
-       if (r == EOK && action == ACTION_SCAN) {
-               /* We have finished scanning the journal. */
-               info->start_trans_id = start_trans_id;
-               if (this_trans_id > start_trans_id)
-                       info->last_trans_id = this_trans_id - 1;
-               else
-                       info->last_trans_id = this_trans_id;
-       }
-
-       return r;
-}
-
-/**@brief  Replay journal.
- * @param  jbd_fs jbd filesystem
- * @return standard error code*/
-int jbd_recover(struct jbd_fs *jbd_fs)
-{
-       int r;
-       struct recover_info info;
-       struct jbd_sb *sb = &jbd_fs->sb;
-       if (!sb->start)
-               return EOK;
-
-       RB_INIT(&info.revoke_root);
-
-       r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
-       if (r != EOK)
-               return r;
-
-       r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
-       if (r != EOK)
-               return r;
-
-       r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
-       if (r == EOK) {
-               /* If we successfully replay the journal,
-                * clear EXT4_FINCOM_RECOVER flag on the
-                * ext4 superblock, and set the start of
-                * journal to 0.*/
-               uint32_t features_incompatible =
-                       ext4_get32(&jbd_fs->inode_ref.fs->sb,
-                                  features_incompatible);
-               jbd_set32(&jbd_fs->sb, start, 0);
-               features_incompatible &= ~EXT4_FINCOM_RECOVER;
-               ext4_set32(&jbd_fs->inode_ref.fs->sb,
-                          features_incompatible,
-                          features_incompatible);
-               jbd_fs->dirty = true;
-               r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
-                                 &jbd_fs->inode_ref.fs->sb);
-       }
-       jbd_destroy_revoke_tree(&info);
-       return r;
-}
-
-static void jbd_journal_write_sb(struct jbd_journal *journal)
-{
-       struct jbd_fs *jbd_fs = journal->jbd_fs;
-       jbd_set32(&jbd_fs->sb, start, journal->start);
-       jbd_set32(&jbd_fs->sb, sequence, journal->trans_id);
-       jbd_fs->dirty = true;
-}
-
-/**@brief  Start accessing the journal.
- * @param  jbd_fs jbd filesystem
- * @param  journal current journal session
- * @return standard error code*/
-int jbd_journal_start(struct jbd_fs *jbd_fs,
-                     struct jbd_journal *journal)
-{
-       int r;
-       uint32_t features_incompatible =
-                       ext4_get32(&jbd_fs->inode_ref.fs->sb,
-                                  features_incompatible);
-       struct ext4_block block = EXT4_BLOCK_ZERO();
-       features_incompatible |= EXT4_FINCOM_RECOVER;
-       ext4_set32(&jbd_fs->inode_ref.fs->sb,
-                       features_incompatible,
-                       features_incompatible);
-       r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
-                       &jbd_fs->inode_ref.fs->sb);
-       if (r != EOK)
-               return r;
-
-       journal->first = jbd_get32(&jbd_fs->sb, first);
-       journal->start = journal->first;
-       journal->last = journal->first;
-       journal->trans_id = 1;
-       journal->alloc_trans_id = 1;
-
-       journal->block_size = jbd_get32(&jbd_fs->sb, blocksize);
-
-       r = jbd_block_get_noread(jbd_fs,
-                        &block,
-                        journal->start);
-       if (r != EOK) {
-               memset(journal, 0, sizeof(struct jbd_journal));
-               return r;
-       }
-       memset(block.data, 0, journal->block_size);
-       ext4_bcache_set_dirty(block.buf);
-       r = jbd_block_set(jbd_fs, &block);
-       if (r != EOK) {
-               memset(journal, 0, sizeof(struct jbd_journal));
-               return r;
-       }
-
-       TAILQ_INIT(&journal->trans_queue);
-       TAILQ_INIT(&journal->cp_queue);
-       RB_INIT(&journal->block_rec_root);
-       journal->jbd_fs = jbd_fs;
-       jbd_journal_write_sb(journal);
-       return jbd_write_sb(jbd_fs);
-}
-
-static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
-                         struct ext4_buf *buf __unused,
-                         int res,
-                         void *arg);
-
-static void jbd_journal_flush_trans(struct jbd_trans *trans)
-{
-       struct jbd_buf *jbd_buf, *tmp;
-       struct jbd_journal *journal = trans->journal;
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       void *tmp_data = malloc(journal->block_size);
-       ext4_assert(tmp_data);
-
-       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
-                       tmp) {
-               struct ext4_buf *buf = jbd_buf->block_rec->buf;
-               /* The buffer in memory is still dirty. */
-               if (buf) {
-                       if (jbd_buf->block_rec->trans != trans) {
-                               int r;
-                               struct ext4_block jbd_block = EXT4_BLOCK_ZERO();
-                               ext4_assert(ext4_block_get(fs->bdev,
-                                                       &jbd_block,
-                                                       jbd_buf->jbd_lba) == EOK);
-                               memcpy(tmp_data, jbd_block.data,
-                                               journal->block_size);
-                               ext4_block_set(fs->bdev, &jbd_block);
-                               r = ext4_blocks_set_direct(fs->bdev, tmp_data,
-                                               buf->lba, 1);
-                               jbd_trans_end_write(fs->bdev->bc, buf, r, jbd_buf);
-                       } else
-                               ext4_block_flush_buf(fs->bdev, buf);
-
-               }
-       }
-
-       free(tmp_data);
-}
-
-static void
-jbd_journal_skip_pure_revoke(struct jbd_journal *journal,
-                            struct jbd_trans *trans)
-{
-       journal->start = trans->start_iblock +
-               trans->alloc_blocks;
-       wrap(&journal->jbd_fs->sb, journal->start);
-       journal->trans_id = trans->trans_id + 1;
-       jbd_journal_free_trans(journal,
-                       trans, false);
-       jbd_journal_write_sb(journal);
-}
-
-static void
-jbd_journal_purge_cp_trans(struct jbd_journal *journal,
-                          bool flush)
-{
-       struct jbd_trans *trans;
-       while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
-               if (!trans->data_cnt) {
-                       TAILQ_REMOVE(&journal->cp_queue,
-                                       trans,
-                                       trans_node);
-                       jbd_journal_skip_pure_revoke(journal, trans);
-               } else {
-                       if (trans->data_cnt ==
-                                       trans->written_cnt) {
-                               journal->start =
-                                       trans->start_iblock +
-                                       trans->alloc_blocks;
-                               wrap(&journal->jbd_fs->sb,
-                                               journal->start);
-                               journal->trans_id =
-                                       trans->trans_id + 1;
-                               TAILQ_REMOVE(&journal->cp_queue,
-                                               trans,
-                                               trans_node);
-                               jbd_journal_free_trans(journal,
-                                               trans,
-                                               false);
-                               jbd_journal_write_sb(journal);
-                       } else if (!flush) {
-                               journal->start =
-                                       trans->start_iblock;
-                               wrap(&journal->jbd_fs->sb,
-                                               journal->start);
-                               journal->trans_id =
-                                       trans->trans_id;
-                               jbd_journal_write_sb(journal);
-                               break;
-                       } else
-                               jbd_journal_flush_trans(trans);
-               }
-       }
-}
-
-/**@brief  Stop accessing the journal.
- * @param  journal current journal session
- * @return standard error code*/
-int jbd_journal_stop(struct jbd_journal *journal)
-{
-       int r;
-       struct jbd_fs *jbd_fs = journal->jbd_fs;
-       uint32_t features_incompatible;
-
-       /* Make sure that journalled content have reached
-        * the disk.*/
-       jbd_journal_purge_cp_trans(journal, true);
-
-       /* There should be no block record in this journal
-        * session. */
-       if (!RB_EMPTY(&journal->block_rec_root))
-               ext4_dbg(DEBUG_JBD,
-                        DBG_WARN "There are still block records "
-                                 "in this journal session!\n");
-
-       features_incompatible =
-               ext4_get32(&jbd_fs->inode_ref.fs->sb,
-                          features_incompatible);
-       features_incompatible &= ~EXT4_FINCOM_RECOVER;
-       ext4_set32(&jbd_fs->inode_ref.fs->sb,
-                       features_incompatible,
-                       features_incompatible);
-       r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
-                       &jbd_fs->inode_ref.fs->sb);
-       if (r != EOK)
-               return r;
-
-       journal->start = 0;
-       journal->trans_id = 0;
-       jbd_journal_write_sb(journal);
-       return jbd_write_sb(journal->jbd_fs);
-}
-
-/**@brief  Allocate a block in the journal.
- * @param  journal current journal session
- * @param  trans transaction
- * @return allocated block address*/
-static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal,
-                                       struct jbd_trans *trans)
-{
-       uint32_t start_block;
-
-       start_block = journal->last++;
-       trans->alloc_blocks++;
-       wrap(&journal->jbd_fs->sb, journal->last);
-       
-       /* If there is no space left, flush all journalled
-        * blocks to disk first.*/
-       if (journal->last == journal->start)
-               jbd_journal_purge_cp_trans(journal, true);
-
-       return start_block;
-}
-
-/**@brief  Allocate a new transaction
- * @param  journal current journal session
- * @return transaction allocated*/
-struct jbd_trans *
-jbd_journal_new_trans(struct jbd_journal *journal)
-{
-       struct jbd_trans *trans = calloc(1, sizeof(struct jbd_trans));
-       if (!trans)
-               return NULL;
-
-       /* We will assign a trans_id to this transaction,
-        * once it has been committed.*/
-       trans->journal = journal;
-       trans->data_csum = EXT4_CRC32_INIT;
-       trans->error = EOK;
-       TAILQ_INIT(&trans->buf_queue);
-       return trans;
-}
-
-/**@brief  gain access to it before making any modications.
- * @param  journal current journal session
- * @param  trans transaction
- * @param  block descriptor
- * @return standard error code.*/
-int jbd_trans_get_access(struct jbd_journal *journal,
-                        struct jbd_trans *trans,
-                        struct ext4_block *block)
-{
-       int r = EOK;
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       struct jbd_buf *jbd_buf = block->buf->end_write_arg;
-
-       /* If the buffer has already been modified, we should
-        * flush dirty data in this buffer to disk.*/
-       if (ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
-           block->buf->end_write == jbd_trans_end_write) {
-               ext4_assert(jbd_buf);
-               if (jbd_buf->trans != trans)
-                       r = ext4_block_flush_buf(fs->bdev, block->buf);
-
-       }
-       return r;
-}
-
-static struct jbd_block_rec *
-jbd_trans_block_rec_lookup(struct jbd_journal *journal,
-                          ext4_fsblk_t lba)
-{
-       struct jbd_block_rec tmp = {
-               .lba = lba
-       };
-
-       return RB_FIND(jbd_block,
-                      &journal->block_rec_root,
-                      &tmp);
-}
-
-static void
-jbd_trans_change_ownership(struct jbd_block_rec *block_rec,
-                          struct jbd_trans *new_trans,
-                          struct ext4_buf *new_buf)
-{
-       LIST_REMOVE(block_rec, tbrec_node);
-       /* Now this block record belongs to this transaction. */
-       LIST_INSERT_HEAD(&new_trans->tbrec_list, block_rec, tbrec_node);
-       block_rec->trans = new_trans;
-       block_rec->buf = new_buf;
-}
-
-static inline struct jbd_block_rec *
-jbd_trans_insert_block_rec(struct jbd_trans *trans,
-                          ext4_fsblk_t lba,
-                          struct ext4_buf *buf)
-{
-       struct jbd_block_rec *block_rec;
-       block_rec = jbd_trans_block_rec_lookup(trans->journal, lba);
-       if (block_rec) {
-               jbd_trans_change_ownership(block_rec, trans, buf);
-               return block_rec;
-       }
-       block_rec = calloc(1, sizeof(struct jbd_block_rec));
-       if (!block_rec)
-               return NULL;
-
-       block_rec->lba = lba;
-       block_rec->buf = buf;
-       block_rec->trans = trans;
-       TAILQ_INIT(&block_rec->dirty_buf_queue);
-       LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
-       RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec);
-       return block_rec;
-}
-
-static void
-jbd_trans_finish_callback(struct jbd_journal *journal,
-                         const struct jbd_trans *trans,
-                         struct jbd_block_rec *block_rec,
-                         bool abort)
-{
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       if (block_rec->trans != trans)
-               return;
-
-       if (!abort) {
-               struct jbd_buf *jbd_buf, *tmp;
-               TAILQ_FOREACH_SAFE(jbd_buf,
-                               &block_rec->dirty_buf_queue,
-                               dirty_buf_node,
-                               tmp) {
-                       /* All we need is a fake ext4_buf. */
-                       struct ext4_buf buf;
-
-                       jbd_trans_end_write(fs->bdev->bc,
-                                       &buf,
-                                       EOK,
-                                       jbd_buf);
-               }
-       } else {
-               struct jbd_buf *jbd_buf;
-               struct ext4_block jbd_block = EXT4_BLOCK_ZERO(),
-                                 block = EXT4_BLOCK_ZERO();
-               jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue,
-                               jbd_buf_dirty);
-               if (jbd_buf) {
-                       ext4_assert(ext4_block_get(fs->bdev,
-                                               &jbd_block,
-                                               jbd_buf->jbd_lba) == EOK);
-                       ext4_assert(ext4_block_get_noread(fs->bdev,
-                                               &block,
-                                               block_rec->lba) == EOK);
-                       memcpy(block.data, jbd_block.data,
-                                       journal->block_size);
-
-                       jbd_trans_change_ownership(block_rec,
-                                       jbd_buf->trans, block.buf);
-
-                       block.buf->end_write = jbd_trans_end_write;
-                       block.buf->end_write_arg = jbd_buf;
-
-                       ext4_bcache_set_flag(jbd_block.buf, BC_TMP);
-                       ext4_bcache_set_dirty(block.buf);
-
-                       ext4_block_set(fs->bdev, &jbd_block);
-                       ext4_block_set(fs->bdev, &block);
-                       return;
-               }
-       }
-}
-
-static inline void
-jbd_trans_remove_block_rec(struct jbd_journal *journal,
-                          struct jbd_block_rec *block_rec,
-                          struct jbd_trans *trans)
-{
-       /* If this block record doesn't belong to this transaction,
-        * give up.*/
-       if (block_rec->trans == trans) {
-               LIST_REMOVE(block_rec, tbrec_node);
-               RB_REMOVE(jbd_block,
-                               &journal->block_rec_root,
-                               block_rec);
-               free(block_rec);
-       }
-}
-
-/**@brief  Add block to a transaction and mark it dirty.
- * @param  trans transaction
- * @param  block block descriptor
- * @return standard error code*/
-int jbd_trans_set_block_dirty(struct jbd_trans *trans,
-                             struct ext4_block *block)
-{
-       struct jbd_buf *buf;
-
-       struct jbd_block_rec *block_rec;
-       if (block->buf->end_write == jbd_trans_end_write) {
-               buf = block->buf->end_write_arg;
-               if (buf && buf->trans == trans)
-                       return EOK;
-       }
-       buf = calloc(1, sizeof(struct jbd_buf));
-       if (!buf)
-               return ENOMEM;
-
-       if ((block_rec = jbd_trans_insert_block_rec(trans,
-                                       block->lb_id,
-                                       block->buf)) == NULL) {
-               free(buf);
-               return ENOMEM;
-       }
-
-       TAILQ_INSERT_TAIL(&block_rec->dirty_buf_queue,
-                       buf,
-                       dirty_buf_node);
-
-       buf->block_rec = block_rec;
-       buf->trans = trans;
-       buf->block = *block;
-       ext4_bcache_inc_ref(block->buf);
-
-       /* If the content reach the disk, notify us
-        * so that we may do a checkpoint. */
-       block->buf->end_write = jbd_trans_end_write;
-       block->buf->end_write_arg = buf;
-
-       trans->data_cnt++;
-       TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
-
-       ext4_bcache_set_dirty(block->buf);
-       return EOK;
-}
-
-/**@brief  Add block to be revoked to a transaction
- * @param  trans transaction
- * @param  lba logical block address
- * @return standard error code*/
-int jbd_trans_revoke_block(struct jbd_trans *trans,
-                          ext4_fsblk_t lba)
-{
-       struct jbd_revoke_rec *rec =
-               calloc(1, sizeof(struct jbd_revoke_rec));
-       if (!rec)
-               return ENOMEM;
-
-       rec->lba = lba;
-       LIST_INSERT_HEAD(&trans->revoke_list, rec, revoke_node);
-       return EOK;
-}
-
-/**@brief  Try to add block to be revoked to a transaction.
- *         If @lba still remains in an transaction on checkpoint
- *         queue, add @lba as a revoked block to the transaction.
- * @param  trans transaction
- * @param  lba logical block address
- * @return standard error code*/
-int jbd_trans_try_revoke_block(struct jbd_trans *trans,
-                              ext4_fsblk_t lba)
-{
-       int r = EOK;
-       struct jbd_journal *journal = trans->journal;
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       struct jbd_block_rec *block_rec =
-               jbd_trans_block_rec_lookup(journal, lba);
-
-       /* Make sure we don't flush any buffers belong to this transaction. */
-       if (block_rec && block_rec->trans != trans) {
-               /* If the buffer has not been flushed yet, flush it now. */
-               if (block_rec->buf) {
-                       r = ext4_block_flush_buf(fs->bdev, block_rec->buf);
-                       if (r != EOK)
-                               return r;
-
-               }
-
-               jbd_trans_revoke_block(trans, lba);
-       }
-
-       return EOK;
-}
-
-/**@brief  Free a transaction
- * @param  journal current journal session
- * @param  trans transaction
- * @param  abort discard all the modifications on the block?
- * @return standard error code*/
-void jbd_journal_free_trans(struct jbd_journal *journal,
-                           struct jbd_trans *trans,
-                           bool abort)
-{
-       struct jbd_buf *jbd_buf, *tmp;
-       struct jbd_revoke_rec *rec, *tmp2;
-       struct jbd_block_rec *block_rec, *tmp3;
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
-                         tmp) {
-               block_rec = jbd_buf->block_rec;
-               if (abort) {
-                       jbd_buf->block.buf->end_write = NULL;
-                       jbd_buf->block.buf->end_write_arg = NULL;
-                       ext4_bcache_clear_dirty(jbd_buf->block.buf);
-                       ext4_block_set(fs->bdev, &jbd_buf->block);
-               }
-
-               TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
-                       jbd_buf,
-                       dirty_buf_node);
-               jbd_trans_finish_callback(journal,
-                               trans,
-                               block_rec,
-                               abort);
-               TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
-               free(jbd_buf);
-       }
-       LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
-                         tmp2) {
-               LIST_REMOVE(rec, revoke_node);
-               free(rec);
-       }
-       LIST_FOREACH_SAFE(block_rec, &trans->tbrec_list, tbrec_node,
-                         tmp3) {
-               jbd_trans_remove_block_rec(journal, block_rec, trans);
-       }
-
-       free(trans);
-}
-
-/**@brief  Write commit block for a transaction
- * @param  trans transaction
- * @return standard error code*/
-static int jbd_trans_write_commit_block(struct jbd_trans *trans)
-{
-       int rc;
-       struct jbd_commit_header *header;
-       uint32_t commit_iblock = 0;
-       struct ext4_block commit_block;
-       struct jbd_journal *journal = trans->journal;
-
-       commit_iblock = jbd_journal_alloc_block(journal, trans);
-       rc = jbd_block_get_noread(journal->jbd_fs,
-                       &commit_block, commit_iblock);
-       if (rc != EOK)
-               return rc;
-
-       header = (struct jbd_commit_header *)commit_block.data;
-       jbd_set32(&header->header, magic, JBD_MAGIC_NUMBER);
-       jbd_set32(&header->header, blocktype, JBD_COMMIT_BLOCK);
-       jbd_set32(&header->header, sequence, trans->trans_id);
-
-       if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
-                               JBD_FEATURE_COMPAT_CHECKSUM)) {
-               jbd_set32(header, chksum_type, JBD_CRC32_CHKSUM);
-               jbd_set32(header, chksum_size, JBD_CRC32_CHKSUM_SIZE);
-               jbd_set32(header, chksum[0], trans->data_csum);
-       }
-       jbd_commit_csum_set(journal->jbd_fs, header);
-       ext4_bcache_set_dirty(commit_block.buf);
-       rc = jbd_block_set(journal->jbd_fs, &commit_block);
-       if (rc != EOK)
-               return rc;
-
-       return EOK;
-}
-
-/**@brief  Write descriptor block for a transaction
- * @param  journal current journal session
- * @param  trans transaction
- * @return standard error code*/
-static int jbd_journal_prepare(struct jbd_journal *journal,
-                              struct jbd_trans *trans)
-{
-       int rc = EOK, i = 0;
-       int32_t tag_tbl_size;
-       uint32_t desc_iblock = 0;
-       uint32_t data_iblock = 0;
-       char *tag_start = NULL, *tag_ptr = NULL;
-       struct jbd_buf *jbd_buf, *tmp;
-       struct ext4_block desc_block, data_block;
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       uint32_t checksum = EXT4_CRC32_INIT;
-
-       /* Try to remove any non-dirty buffers from the tail of
-        * buf_queue. */
-       TAILQ_FOREACH_REVERSE_SAFE(jbd_buf, &trans->buf_queue,
-                       jbd_trans_buf, buf_node, tmp) {
-               /* We stop the iteration when we find a dirty buffer. */
-               if (ext4_bcache_test_flag(jbd_buf->block.buf,
-                                       BC_DIRTY))
-                       break;
-       
-               TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
-                       jbd_buf,
-                       dirty_buf_node);
-
-               jbd_buf->block.buf->end_write = NULL;
-               jbd_buf->block.buf->end_write_arg = NULL;
-               jbd_trans_finish_callback(journal,
-                               trans,
-                               jbd_buf->block_rec,
-                               true);
-
-               /* The buffer has not been modified, just release
-                * that jbd_buf. */
-               jbd_trans_remove_block_rec(journal,
-                               jbd_buf->block_rec, trans);
-               trans->data_cnt--;
-
-               ext4_block_set(fs->bdev, &jbd_buf->block);
-               TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
-               free(jbd_buf);
-       }
-
-       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, tmp) {
-               struct tag_info tag_info;
-               bool uuid_exist = false;
-               if (!ext4_bcache_test_flag(jbd_buf->block.buf,
-                                          BC_DIRTY)) {
-                       TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
-                                       jbd_buf,
-                                       dirty_buf_node);
-
-                       jbd_buf->block.buf->end_write = NULL;
-                       jbd_buf->block.buf->end_write_arg = NULL;
-                       jbd_trans_finish_callback(journal,
-                                       trans,
-                                       jbd_buf->block_rec,
-                                       true);
-
-                       /* The buffer has not been modified, just release
-                        * that jbd_buf. */
-                       jbd_trans_remove_block_rec(journal,
-                                       jbd_buf->block_rec, trans);
-                       trans->data_cnt--;
-
-                       ext4_block_set(fs->bdev, &jbd_buf->block);
-                       TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
-                       free(jbd_buf);
-                       continue;
-               }
-               checksum = jbd_block_csum(journal->jbd_fs,
-                                         jbd_buf->block.data,
-                                         checksum,
-                                         trans->trans_id);
-again:
-               if (!desc_iblock) {
-                       struct jbd_bhdr *bhdr;
-                       desc_iblock = jbd_journal_alloc_block(journal, trans);
-                       rc = jbd_block_get_noread(journal->jbd_fs,
-                                          &desc_block, desc_iblock);
-                       if (rc != EOK)
-                               break;
-
-                       ext4_bcache_set_dirty(desc_block.buf);
-
-                       bhdr = (struct jbd_bhdr *)desc_block.data;
-                       jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
-                       jbd_set32(bhdr, blocktype, JBD_DESCRIPTOR_BLOCK);
-                       jbd_set32(bhdr, sequence, trans->trans_id);
-
-                       tag_start = (char *)(bhdr + 1);
-                       tag_ptr = tag_start;
-                       uuid_exist = true;
-                       tag_tbl_size = journal->block_size -
-                               sizeof(struct jbd_bhdr);
-
-                       if (jbd_has_csum(&journal->jbd_fs->sb))
-                               tag_tbl_size -= sizeof(struct jbd_block_tail);
-
-                       if (!trans->start_iblock)
-                               trans->start_iblock = desc_iblock;
-
-               }
-               tag_info.block = jbd_buf->block.lb_id;
-               tag_info.uuid_exist = uuid_exist;
-               if (i == trans->data_cnt - 1)
-                       tag_info.last_tag = true;
-               else
-                       tag_info.last_tag = false;
-
-               tag_info.checksum = checksum;
-
-               if (uuid_exist)
-                       memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid,
-                                       UUID_SIZE);
-
-               rc = jbd_write_block_tag(journal->jbd_fs,
-                               tag_ptr,
-                               tag_tbl_size,
-                               &tag_info);
-               if (rc != EOK) {
-                       jbd_meta_csum_set(journal->jbd_fs,
-                                       (struct jbd_bhdr *)desc_block.data);
-                       jbd_block_set(journal->jbd_fs, &desc_block);
-                       desc_iblock = 0;
-                       goto again;
-               }
-
-               data_iblock = jbd_journal_alloc_block(journal, trans);
-               rc = jbd_block_get_noread(journal->jbd_fs,
-                               &data_block, data_iblock);
-               if (rc != EOK)
-                       break;
-
-               ext4_bcache_set_dirty(data_block.buf);
-
-               memcpy(data_block.data, jbd_buf->block.data,
-                       journal->block_size);
-               jbd_buf->jbd_lba = data_block.lb_id;
-
-               rc = jbd_block_set(journal->jbd_fs, &data_block);
-               if (rc != EOK)
-                       break;
-
-               tag_ptr += tag_info.tag_bytes;
-               tag_tbl_size -= tag_info.tag_bytes;
-
-               i++;
-       }
-       if (rc == EOK && desc_iblock) {
-               jbd_meta_csum_set(journal->jbd_fs,
-                               (struct jbd_bhdr *)desc_block.data);
-               trans->data_csum = checksum;
-               jbd_block_set(journal->jbd_fs, &desc_block);
-       }
-
-       return rc;
-}
-
-/**@brief  Write revoke block for a transaction
- * @param  journal current journal session
- * @param  trans transaction
- * @return standard error code*/
-static int
-jbd_journal_prepare_revoke(struct jbd_journal *journal,
-                          struct jbd_trans *trans)
-{
-       int rc = EOK, i = 0;
-       int32_t tag_tbl_size;
-       uint32_t desc_iblock = 0;
-       char *blocks_entry = NULL;
-       struct jbd_revoke_rec *rec, *tmp;
-       struct ext4_block desc_block;
-       struct jbd_revoke_header *header = NULL;
-       int32_t record_len = 4;
-
-       if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
-                                    JBD_FEATURE_INCOMPAT_64BIT))
-               record_len = 8;
-
-       LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
-                         tmp) {
-again:
-               if (!desc_iblock) {
-                       struct jbd_bhdr *bhdr;
-                       desc_iblock = jbd_journal_alloc_block(journal, trans);
-                       rc = jbd_block_get_noread(journal->jbd_fs,
-                                          &desc_block, desc_iblock);
-                       if (rc != EOK) {
-                               break;
-                       }
-
-                       ext4_bcache_set_dirty(desc_block.buf);
-
-                       bhdr = (struct jbd_bhdr *)desc_block.data;
-                       jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
-                       jbd_set32(bhdr, blocktype, JBD_REVOKE_BLOCK);
-                       jbd_set32(bhdr, sequence, trans->trans_id);
-                       
-                       header = (struct jbd_revoke_header *)bhdr;
-                       blocks_entry = (char *)(header + 1);
-                       tag_tbl_size = journal->block_size -
-                               sizeof(struct jbd_revoke_header);
-
-                       if (jbd_has_csum(&journal->jbd_fs->sb))
-                               tag_tbl_size -= sizeof(struct jbd_block_tail);
-
-                       if (!trans->start_iblock)
-                               trans->start_iblock = desc_iblock;
-
-               }
-
-               if (tag_tbl_size < record_len) {
-                       jbd_set32(header, count,
-                                 journal->block_size - tag_tbl_size);
-                       jbd_meta_csum_set(journal->jbd_fs,
-                                       (struct jbd_bhdr *)desc_block.data);
-                       jbd_block_set(journal->jbd_fs, &desc_block);
-                       desc_iblock = 0;
-                       header = NULL;
-                       goto again;
-               }
-               if (record_len == 8) {
-                       uint64_t *blocks =
-                               (uint64_t *)blocks_entry;
-                       *blocks = to_be64(rec->lba);
-               } else {
-                       uint32_t *blocks =
-                               (uint32_t *)blocks_entry;
-                       *blocks = to_be32((uint32_t)rec->lba);
-               }
-               blocks_entry += record_len;
-               tag_tbl_size -= record_len;
-
-               i++;
-       }
-       if (rc == EOK && desc_iblock) {
-               if (header != NULL)
-                       jbd_set32(header, count,
-                                 journal->block_size - tag_tbl_size);
-
-               jbd_meta_csum_set(journal->jbd_fs,
-                               (struct jbd_bhdr *)desc_block.data);
-               jbd_block_set(journal->jbd_fs, &desc_block);
-       }
-
-       return rc;
-}
-
-/**@brief  Put references of block descriptors in a transaction.
- * @param  journal current journal session
- * @param  trans transaction*/
-void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans)
-{
-       struct jbd_buf *jbd_buf, *tmp;
-       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
-       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
-                       tmp) {
-               struct ext4_block block = jbd_buf->block;
-               ext4_block_set(fs->bdev, &block);
-       }
-}
-
-/**@brief  Update the start block of the journal when
- *         all the contents in a transaction reach the disk.*/
-static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
-                         struct ext4_buf *buf,
-                         int res,
-                         void *arg)
-{
-       struct jbd_buf *jbd_buf = arg;
-       struct jbd_trans *trans = jbd_buf->trans;
-       struct jbd_block_rec *block_rec = jbd_buf->block_rec;
-       struct jbd_journal *journal = trans->journal;
-       bool first_in_queue =
-               trans == TAILQ_FIRST(&journal->cp_queue);
-       if (res != EOK)
-               trans->error = res;
-
-       TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
-       TAILQ_REMOVE(&block_rec->dirty_buf_queue,
-                       jbd_buf,
-                       dirty_buf_node);
-
-       jbd_trans_finish_callback(journal,
-                       trans,
-                       jbd_buf->block_rec,
-                       false);
-       if (block_rec->trans == trans) {
-               block_rec->buf = NULL;
-               /* Clear the end_write and end_write_arg fields. */
-               buf->end_write = NULL;
-               buf->end_write_arg = NULL;
-       }
-
-       free(jbd_buf);
-
-       trans->written_cnt++;
-       if (trans->written_cnt == trans->data_cnt) {
-               /* If it is the first transaction on checkpoint queue,
-                * we will shift the start of the journal to the next
-                * transaction, and remove subsequent written
-                * transactions from checkpoint queue until we find
-                * an unwritten one. */
-               if (first_in_queue) {
-                       journal->start = trans->start_iblock +
-                               trans->alloc_blocks;
-                       wrap(&journal->jbd_fs->sb, journal->start);
-                       journal->trans_id = trans->trans_id + 1;
-                       TAILQ_REMOVE(&journal->cp_queue, trans, trans_node);
-                       jbd_journal_free_trans(journal, trans, false);
-
-                       jbd_journal_purge_cp_trans(journal, false);
-                       jbd_journal_write_sb(journal);
-                       jbd_write_sb(journal->jbd_fs);
-               }
-       }
-}
-
-/**@brief  Commit a transaction to the journal immediately.
- * @param  journal current journal session
- * @param  trans transaction
- * @return standard error code*/
-int jbd_journal_commit_trans(struct jbd_journal *journal,
-                            struct jbd_trans *trans)
-{
-       int rc = EOK;
-       uint32_t last = journal->last;
-
-       trans->trans_id = journal->alloc_trans_id;
-       rc = jbd_journal_prepare(journal, trans);
-       if (rc != EOK)
-               goto Finish;
-
-       rc = jbd_journal_prepare_revoke(journal, trans);
-       if (rc != EOK)
-               goto Finish;
-
-       if (TAILQ_EMPTY(&trans->buf_queue) &&
-           LIST_EMPTY(&trans->revoke_list)) {
-               /* Since there are no entries in both buffer list
-                * and revoke entry list, we do not consider trans as
-                * complete transaction and just return EOK.*/
-               jbd_journal_free_trans(journal, trans, false);
-               goto Finish;
-       }
-
-       rc = jbd_trans_write_commit_block(trans);
-       if (rc != EOK)
-               goto Finish;
-
-       journal->alloc_trans_id++;
-       if (TAILQ_EMPTY(&journal->cp_queue)) {
-               if (trans->data_cnt) {
-                       journal->start = trans->start_iblock;
-                       wrap(&journal->jbd_fs->sb, journal->start);
-                       journal->trans_id = trans->trans_id;
-                       jbd_journal_write_sb(journal);
-                       jbd_write_sb(journal->jbd_fs);
-                       TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
-                                       trans_node);
-                       jbd_journal_cp_trans(journal, trans);
-               } else {
-                       journal->start = trans->start_iblock +
-                               trans->alloc_blocks;
-                       wrap(&journal->jbd_fs->sb, journal->start);
-                       journal->trans_id = trans->trans_id + 1;
-                       jbd_journal_write_sb(journal);
-                       jbd_journal_free_trans(journal, trans, false);
-               }
-       } else {
-               TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
-                               trans_node);
-               if (trans->data_cnt)
-                       jbd_journal_cp_trans(journal, trans);
-
-       }
-Finish:
-       if (rc != EOK) {
-               journal->last = last;
-               jbd_journal_free_trans(journal, trans, true);
-       }
-       return rc;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_journal.h b/lwext4/ext4_journal.h
deleted file mode 100644 (file)
index 8e193cc..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
- *
- * 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_journal.h
- * @brief Journal handle functions
- */
-
-#ifndef EXT4_JOURNAL_H_
-#define EXT4_JOURNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-int jbd_get_fs(struct ext4_fs *fs,
-              struct jbd_fs *jbd_fs);
-int jbd_put_fs(struct jbd_fs *jbd_fs);
-int jbd_inode_bmap(struct jbd_fs *jbd_fs,
-                  ext4_lblk_t iblock,
-                  ext4_fsblk_t *fblock);
-int jbd_recover(struct jbd_fs *jbd_fs);
-int jbd_journal_start(struct jbd_fs *jbd_fs,
-                     struct jbd_journal *journal);
-int jbd_journal_stop(struct jbd_journal *journal);
-struct jbd_trans *jbd_journal_new_trans(struct jbd_journal *journal);
-int jbd_trans_get_access(struct jbd_journal *journal,
-                        struct jbd_trans *trans,
-                        struct ext4_block *block);
-int jbd_trans_set_block_dirty(struct jbd_trans *trans,
-                             struct ext4_block *block);
-int jbd_trans_revoke_block(struct jbd_trans *trans,
-                          ext4_fsblk_t lba);
-int jbd_trans_try_revoke_block(struct jbd_trans *trans,
-                              ext4_fsblk_t lba);
-void jbd_journal_free_trans(struct jbd_journal *journal,
-                           struct jbd_trans *trans,
-                           bool abort);
-int jbd_journal_commit_trans(struct jbd_journal *journal,
-                            struct jbd_trans *trans);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_JOURNAL_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_mbr.c b/lwext4/ext4_mbr.c
deleted file mode 100644 (file)
index 52df852..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2015 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_mbr.c
- * @brief Master boot record parser
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_debug.h"
-#include "ext4_mbr.h"
-
-#include <inttypes.h>
-#include <string.h>
-
-#define MBR_SIGNATURE 0xAA55
-
-#pragma pack(push, 1)
-
-struct ext4_part_entry {
-       uint8_t status;
-       uint8_t chs1[3];
-       uint8_t type;
-       uint8_t chs2[3];
-       uint32_t first_lba;
-       uint32_t sectors;
-};
-
-struct ext4_mbr {
-       uint8_t bootstrap[446];
-       struct ext4_part_entry part_entry[4];
-       uint16_t signature;
-};
-
-#pragma pack(pop)
-
-int ext4_mbr_scan(struct ext4_blockdev *parent, struct ext4_mbr_bdevs *bdevs)
-{
-       int r;
-       size_t i;
-
-       ext4_dbg(DEBUG_MBR, DBG_INFO "ext4_mbr_scan\n");
-       memset(bdevs, 0, sizeof(struct ext4_mbr_bdevs));
-       r = ext4_block_init(parent);
-       if (r != EOK)
-               return r;
-
-       r = ext4_block_readbytes(parent, 0, parent->bdif->ph_bbuf, 512);
-       if (r != EOK) {
-               goto blockdev_fini;
-       }
-
-       const struct ext4_mbr *mbr = (void *)parent->bdif->ph_bbuf;
-
-       if (to_le16(mbr->signature) != MBR_SIGNATURE) {
-               ext4_dbg(DEBUG_MBR, DBG_ERROR "ext4_mbr_scan: unknown "
-                        "signature: 0x%x\n", to_le16(mbr->signature));
-               r = ENOENT;
-               goto blockdev_fini;
-       }
-
-       /*Show bootstrap code*/
-       ext4_dbg(DEBUG_MBR, "mbr_part: bootstrap:");
-       for (i = 0; i < sizeof(mbr->bootstrap); ++i) {
-               if (!(i & 0xF))
-                               ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "\n");
-               ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "%02x, ", mbr->bootstrap[i]);
-       }
-
-       ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "\n\n");
-       for (i = 0; i < 4; ++i) {
-               const struct ext4_part_entry *pe = &mbr->part_entry[i];
-               ext4_dbg(DEBUG_MBR, "mbr_part: %d\n", (int)i);
-               ext4_dbg(DEBUG_MBR, "\tstatus: 0x%x\n", pe->status);
-               ext4_dbg(DEBUG_MBR, "\ttype 0x%x:\n", pe->type);
-               ext4_dbg(DEBUG_MBR, "\tfirst_lba: 0x%"PRIx32"\n", pe->first_lba);
-               ext4_dbg(DEBUG_MBR, "\tsectors: 0x%"PRIx32"\n", pe->sectors);
-
-               if (!pe->sectors)
-                       continue; /*Empty entry*/
-
-               if (pe->type != 0x83)
-                       continue; /*Unsupported entry. 0x83 - linux native*/
-
-               bdevs->partitions[i].bdif = parent->bdif;
-               bdevs->partitions[i].part_offset =
-                       (uint64_t)pe->first_lba * parent->bdif->ph_bsize;
-               bdevs->partitions[i].part_size =
-                       (uint64_t)pe->sectors * parent->bdif->ph_bsize;
-       }
-
-       blockdev_fini:
-       ext4_block_fini(parent);
-       return r;
-}
-
-/**
- * @}
- */
-
diff --git a/lwext4/ext4_mbr.h b/lwext4/ext4_mbr.h
deleted file mode 100644 (file)
index 2fc91f2..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2015 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_mbr.h
- * @brief Master boot record parser
- */
-
-#ifndef EXT4_MBR_H_
-#define EXT4_MBR_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_blockdev.h"
-
-/**@brief Master boot record block devices descriptor*/
-struct ext4_mbr_bdevs {
-       struct ext4_blockdev partitions[4];
-};
-
-int ext4_mbr_scan(struct ext4_blockdev *parent, struct ext4_mbr_bdevs *bdevs);
-
-/**@brief Master boot record partitions*/
-struct ext4_mbr_parts {
-       uint64_t size[4];
-};
-
-int ext4_mbr_write(struct ext4_blockdev *parent, struct ext4_mbr_parts *parts);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_MBR_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_mkfs.c b/lwext4/ext4_mkfs.c
deleted file mode 100644 (file)
index 5712ef3..0000000
+++ /dev/null
@@ -1,774 +0,0 @@
-/*
- * Copyright (c) 2015 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_mkfs.c
- * @brief
- */
-
-#include "ext4_config.h"
-#include "ext4_super.h"
-#include "ext4_block_group.h"
-#include "ext4_dir.h"
-#include "ext4_dir_idx.h"
-#include "ext4_fs.h"
-#include "ext4_inode.h"
-#include "ext4_debug.h"
-#include "ext4_ialloc.h"
-#include "ext4_mkfs.h"
-
-#include <inttypes.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define EXT4_ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
-
-struct fs_aux_info {
-       struct ext4_sblock *sb;
-       struct ext4_bgroup *bg_desc;
-       struct xattr_list_element *xattrs;
-       uint32_t first_data_block;
-       uint64_t len_blocks;
-       uint32_t inode_table_blocks;
-       uint32_t groups;
-       uint32_t bg_desc_blocks;
-       uint32_t default_i_flags;
-       uint32_t blocks_per_ind;
-       uint32_t blocks_per_dind;
-       uint32_t blocks_per_tind;
-};
-
-static inline int log_2(int j)
-{
-       int i;
-
-       for (i = 0; j > 0; i++)
-               j >>= 1;
-
-       return i - 1;
-}
-
-static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info)
-{
-        if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC)
-                return EINVAL;
-
-       info->block_size = 1024 << to_le32(sb->log_block_size);
-       info->blocks_per_group = to_le32(sb->blocks_per_group);
-       info->inodes_per_group = to_le32(sb->inodes_per_group);
-       info->inode_size = to_le16(sb->inode_size);
-       info->inodes = to_le32(sb->inodes_count);
-       info->feat_ro_compat = to_le32(sb->features_read_only);
-       info->feat_compat = to_le32(sb->features_compatible);
-       info->feat_incompat = to_le32(sb->features_incompatible);
-       info->bg_desc_reserve_blocks = to_le16(sb->s_reserved_gdt_blocks);
-       info->label = sb->volume_name;
-       info->len = (uint64_t)info->block_size * ext4_sb_get_blocks_cnt(sb);
-       info->dsc_size = to_le16(sb->desc_size);
-
-       return EOK;
-}
-
-static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info)
-{
-       return info->block_size * 8;
-}
-
-static uint32_t compute_inodes(struct ext4_mkfs_info *info)
-{
-       return (uint32_t)DIV_ROUND_UP(info->len, info->block_size) / 4;
-}
-
-static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info)
-{
-       uint32_t blocks = (uint32_t)DIV_ROUND_UP(info->len, info->block_size);
-       uint32_t block_groups = DIV_ROUND_UP(blocks, info->blocks_per_group);
-       uint32_t inodes = DIV_ROUND_UP(info->inodes, block_groups);
-       inodes = EXT4_ALIGN(inodes, (info->block_size / info->inode_size));
-
-       /* After properly rounding up the number of inodes/group,
-        * make sure to update the total inodes field in the info struct.
-        */
-       info->inodes = inodes * block_groups;
-
-       return inodes;
-}
-
-
-static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info)
-{
-       uint32_t journal_blocks = (uint32_t)DIV_ROUND_UP(info->len,
-                                                info->block_size) / 64;
-       if (journal_blocks < 1024)
-               journal_blocks = 1024;
-       if (journal_blocks > 32768)
-               journal_blocks = 32768;
-       return journal_blocks;
-}
-
-static bool has_superblock(struct ext4_mkfs_info *info, uint32_t bgid)
-{
-       if (!(info->feat_ro_compat & EXT4_FRO_COM_SPARSE_SUPER))
-               return true;
-
-       return ext4_sb_sparse(bgid);
-}
-
-static int create_fs_aux_info(struct fs_aux_info *aux_info,
-                             struct ext4_mkfs_info *info)
-{
-       aux_info->first_data_block = (info->block_size > 1024) ? 0 : 1;
-       aux_info->len_blocks = info->len / info->block_size;
-       aux_info->inode_table_blocks = DIV_ROUND_UP(info->inodes_per_group *
-                       info->inode_size, info->block_size);
-       aux_info->groups = (uint32_t)DIV_ROUND_UP(aux_info->len_blocks -
-                       aux_info->first_data_block, info->blocks_per_group);
-       aux_info->blocks_per_ind = info->block_size / sizeof(uint32_t);
-       aux_info->blocks_per_dind =
-                       aux_info->blocks_per_ind * aux_info->blocks_per_ind;
-       aux_info->blocks_per_tind =
-                       aux_info->blocks_per_dind * aux_info->blocks_per_dind;
-
-       aux_info->bg_desc_blocks =
-               DIV_ROUND_UP(aux_info->groups * info->dsc_size,
-                       info->block_size);
-
-       aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME;
-
-       uint32_t last_group_size = aux_info->len_blocks % info->blocks_per_group;
-       uint32_t last_header_size = 2 + aux_info->inode_table_blocks;
-       if (has_superblock(info, aux_info->groups - 1))
-               last_header_size += 1 + aux_info->bg_desc_blocks +
-                       info->bg_desc_reserve_blocks;
-
-       if (last_group_size > 0 && last_group_size < last_header_size) {
-               aux_info->groups--;
-               aux_info->len_blocks -= last_group_size;
-       }
-
-       aux_info->sb = calloc(1, EXT4_SUPERBLOCK_SIZE);
-       if (!aux_info->sb)
-               return ENOMEM;
-
-       aux_info->bg_desc = calloc(aux_info->groups, sizeof(struct ext4_bgroup));
-       if (!aux_info->bg_desc)
-               return ENOMEM;
-
-       aux_info->xattrs = NULL;
-
-
-       ext4_dbg(DEBUG_MKFS, DBG_INFO "create_fs_aux_info\n");
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "first_data_block: %"PRIu32"\n",
-                       aux_info->first_data_block);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "len_blocks: %"PRIu64"\n",
-                       aux_info->len_blocks);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "inode_table_blocks: %"PRIu32"\n",
-                       aux_info->inode_table_blocks);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "groups: %"PRIu32"\n",
-                       aux_info->groups);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "bg_desc_blocks: %"PRIu32"\n",
-                       aux_info->bg_desc_blocks);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "default_i_flags: %"PRIu32"\n",
-                       aux_info->default_i_flags);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_ind: %"PRIu32"\n",
-                       aux_info->blocks_per_ind);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_dind: %"PRIu32"\n",
-                       aux_info->blocks_per_dind);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_tind: %"PRIu32"\n",
-                       aux_info->blocks_per_tind);
-
-       return EOK;
-}
-
-static void release_fs_aux_info(struct fs_aux_info *aux_info)
-{
-       if (aux_info->sb)
-               free(aux_info->sb);
-       if (aux_info->bg_desc)
-               free(aux_info->bg_desc);
-}
-
-
-/* Fill in the superblock memory buffer based on the filesystem parameters */
-static void fill_in_sb(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info)
-{
-       struct ext4_sblock *sb = aux_info->sb;
-
-       sb->inodes_count = to_le32(info->inodes_per_group * aux_info->groups);
-
-       ext4_sb_set_blocks_cnt(sb, aux_info->len_blocks);
-       ext4_sb_set_free_blocks_cnt(sb, aux_info->len_blocks);
-       sb->free_inodes_count = to_le32(info->inodes_per_group * aux_info->groups);
-
-       sb->reserved_blocks_count_lo = to_le32(0);
-       sb->first_data_block = to_le32(aux_info->first_data_block);
-       sb->log_block_size = to_le32(log_2(info->block_size / 1024));
-       sb->log_cluster_size = to_le32(log_2(info->block_size / 1024));
-       sb->blocks_per_group = to_le32(info->blocks_per_group);
-       sb->frags_per_group = to_le32(info->blocks_per_group);
-       sb->inodes_per_group = to_le32(info->inodes_per_group);
-       sb->mount_time = to_le32(0);
-       sb->write_time = to_le32(0);
-       sb->mount_count = to_le16(0);
-       sb->max_mount_count = to_le16(0xFFFF);
-       sb->magic = to_le16(EXT4_SUPERBLOCK_MAGIC);
-       sb->state = to_le16(EXT4_SUPERBLOCK_STATE_VALID_FS);
-       sb->errors = to_le16(EXT4_SUPERBLOCK_ERRORS_RO);
-       sb->minor_rev_level = to_le16(0);
-       sb->last_check_time = to_le32(0);
-       sb->check_interval = to_le32(0);
-       sb->creator_os = to_le32(EXT4_SUPERBLOCK_OS_LINUX);
-       sb->rev_level = to_le32(1);
-       sb->def_resuid = to_le16(0);
-       sb->def_resgid = to_le16(0);
-
-       sb->first_inode = to_le32(EXT4_GOOD_OLD_FIRST_INO);
-       sb->inode_size = to_le16(info->inode_size);
-       sb->block_group_index = to_le16(0);
-
-       sb->features_compatible = to_le32(info->feat_compat);
-       sb->features_incompatible = to_le32(info->feat_incompat);
-       sb->features_read_only = to_le32(info->feat_ro_compat);
-
-       memset(sb->uuid, 0, sizeof(sb->uuid));
-
-       memset(sb->volume_name, 0, sizeof(sb->volume_name));
-       strncpy(sb->volume_name, info->label, sizeof(sb->volume_name));
-       memset(sb->last_mounted, 0, sizeof(sb->last_mounted));
-
-       sb->algorithm_usage_bitmap = to_le32(0);
-       sb->s_prealloc_blocks = 0;
-       sb->s_prealloc_dir_blocks = 0;
-       sb->s_reserved_gdt_blocks = to_le16(info->bg_desc_reserve_blocks);
-
-       if (info->feat_compat & EXT4_FCOM_HAS_JOURNAL)
-               sb->journal_inode_number = to_le32(EXT4_JOURNAL_INO);
-       sb->journal_dev = to_le32(0);
-       sb->last_orphan = to_le32(0);
-       sb->hash_seed[0] = to_le32(0x11111111);
-       sb->hash_seed[1] = to_le32(0x22222222);
-       sb->hash_seed[2] = to_le32(0x33333333);
-       sb->hash_seed[3] = to_le32(0x44444444);
-       sb->default_hash_version = EXT2_HTREE_HALF_MD4;
-       sb->checksum_type = 1;
-       sb->desc_size = to_le16(info->dsc_size);
-       sb->default_mount_opts = to_le32(0);
-       sb->first_meta_bg = to_le32(0);
-       sb->mkfs_time = to_le32(0);
-
-       sb->reserved_blocks_count_hi = to_le32(0);
-       sb->min_extra_isize = to_le32(sizeof(struct ext4_inode) -
-               EXT4_GOOD_OLD_INODE_SIZE);
-       sb->want_extra_isize = to_le32(sizeof(struct ext4_inode) -
-               EXT4_GOOD_OLD_INODE_SIZE);
-       sb->flags = to_le32(EXT4_SUPERBLOCK_FLAGS_SIGNED_HASH);
-}
-
-static void fill_bgroups(struct fs_aux_info *aux_info,
-                        struct ext4_mkfs_info *info)
-{
-       uint32_t i;
-
-       uint32_t bg_free_blk = 0;
-       uint64_t sb_free_blk = 0;
-
-       for (i = 0; i < aux_info->groups; i++) {
-
-               uint64_t bg_start_block = aux_info->first_data_block +
-                       aux_info->first_data_block + i * info->blocks_per_group;
-               uint32_t blk_off = 0;
-
-               bg_free_blk = info->blocks_per_group -
-                               aux_info->inode_table_blocks;
-
-               bg_free_blk -= 2;
-               blk_off += aux_info->bg_desc_blocks;
-
-               if (i == (aux_info->groups - 1))
-                       bg_free_blk -= aux_info->first_data_block;
-
-               if (has_superblock(info, i)) {
-                       bg_start_block++;
-                       blk_off += info->bg_desc_reserve_blocks;
-                       bg_free_blk -= info->bg_desc_reserve_blocks + 1;
-                       bg_free_blk -= aux_info->bg_desc_blocks;
-               }
-
-               ext4_bg_set_block_bitmap(&aux_info->bg_desc[i], aux_info->sb,
-                               bg_start_block + blk_off + 1);
-
-               ext4_bg_set_inode_bitmap(&aux_info->bg_desc[i], aux_info->sb,
-                               bg_start_block + blk_off + 2);
-
-               ext4_bg_set_inode_table_first_block(&aux_info->bg_desc[i],
-                               aux_info->sb,
-                               bg_start_block + blk_off + 3);
-
-               ext4_bg_set_free_blocks_count(&aux_info->bg_desc[i],
-                               aux_info->sb, bg_free_blk);
-
-               ext4_bg_set_free_inodes_count(&aux_info->bg_desc[i],
-                               aux_info->sb, aux_info->sb->inodes_per_group);
-
-               ext4_bg_set_used_dirs_count(&aux_info->bg_desc[i], aux_info->sb,
-                                           0);
-
-               ext4_bg_set_flag(&aux_info->bg_desc[i],
-                               EXT4_BLOCK_GROUP_BLOCK_UNINIT |
-                               EXT4_BLOCK_GROUP_INODE_UNINIT);
-
-               sb_free_blk += bg_free_blk;
-       }
-
-       ext4_sb_set_free_blocks_cnt(aux_info->sb, sb_free_blk);
-}
-
-
-static int write_bgroups(struct ext4_blockdev *bd, struct fs_aux_info *aux_info,
-                        struct ext4_mkfs_info *info)
-{
-       int r;
-       uint32_t i;
-       struct ext4_block b;
-       for (i = 0; i < aux_info->groups; i++) {
-               uint64_t bg_start_block = aux_info->first_data_block +
-                       + i * info->blocks_per_group;
-               uint32_t blk_off = 0;
-
-               blk_off += aux_info->bg_desc_blocks;
-               if (has_superblock(info, i)) {
-                       bg_start_block++;
-                       blk_off += info->bg_desc_reserve_blocks;
-               }
-
-               uint32_t block_size = ext4_sb_get_block_size(aux_info->sb);
-               uint32_t dsc_pos = 0;
-               uint32_t dsc_id = 0;
-               uint32_t dsc_size = ext4_sb_get_desc_size(aux_info->sb);
-               uint32_t dsc_blk_cnt = aux_info->bg_desc_blocks;
-               uint64_t dsc_blk = bg_start_block;
-
-               while (dsc_blk_cnt--) {
-                       r = ext4_block_get(bd, &b, dsc_blk++);
-                       if (r != EOK)
-                               return r;
-
-                       dsc_pos = 0;
-                       while (dsc_pos + dsc_size <= block_size) {
-                               memcpy(b.data + dsc_pos,
-                                      &aux_info->bg_desc[dsc_id],
-                                      dsc_size);
-
-                               dsc_pos += dsc_size;
-                               dsc_id++;
-
-                               if (dsc_id == aux_info->groups)
-                                       break;
-                       }
-
-                       ext4_bcache_set_dirty(b.buf);
-                       r = ext4_block_set(bd, &b);
-                       if (r != EOK)
-                               return r;
-
-                       if (dsc_id == aux_info->groups)
-                               break;
-               }
-
-               r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 1);
-               if (r != EOK)
-                       return r;
-               memset(b.data, 0, block_size);
-               ext4_bcache_set_dirty(b.buf);
-               r = ext4_block_set(bd, &b);
-               if (r != EOK)
-                       return r;
-               r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 2);
-               if (r != EOK)
-                       return r;
-               memset(b.data, 0, block_size);
-               ext4_bcache_set_dirty(b.buf);
-               r = ext4_block_set(bd, &b);
-               if (r != EOK)
-                       return r;
-       }
-
-
-       return r;
-}
-
-static int write_sblocks(struct ext4_blockdev *bd, struct fs_aux_info *aux_info,
-                         struct ext4_mkfs_info *info)
-{
-       uint64_t offset;
-       uint32_t i;
-       int r;
-
-       /* write out the backup superblocks */
-       for (i = 1; i < aux_info->groups; i++) {
-               if (has_superblock(info, i)) {
-                       offset = info->block_size * (aux_info->first_data_block
-                               + i * info->blocks_per_group);
-
-                       aux_info->sb->block_group_index = to_le16(i);
-                       r = ext4_block_writebytes(bd, offset, aux_info->sb,
-                                                 EXT4_SUPERBLOCK_SIZE);
-                       if (r != EOK)
-                               return r;
-               }
-       }
-
-       /* write out the primary superblock */
-       aux_info->sb->block_group_index = to_le16(0);
-       return ext4_block_writebytes(bd, 1024, aux_info->sb,
-                       EXT4_SUPERBLOCK_SIZE);
-}
-
-
-int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
-{
-       int r;
-       struct ext4_sblock *sb = NULL;
-       r = ext4_block_init(bd);
-       if (r != EOK)
-               return r;
-
-       sb = malloc(EXT4_SUPERBLOCK_SIZE);
-       if (!sb)
-               goto Finish;
-
-
-       r = ext4_sb_read(bd, sb);
-       if (r != EOK)
-               goto Finish;
-
-       r = sb2info(sb, info);
-
-Finish:
-       if (sb)
-               free(sb);
-       ext4_block_fini(bd);
-       return r;
-}
-
-static int mkfs_initial(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
-{
-       int r;
-       struct fs_aux_info aux_info;
-       memset(&aux_info, 0, sizeof(struct fs_aux_info));
-
-       r = create_fs_aux_info(&aux_info, info);
-       if (r != EOK)
-               goto Finish;
-
-       fill_in_sb(&aux_info, info);
-       fill_bgroups(&aux_info, info);
-
-
-       r = write_bgroups(bd, &aux_info, info);
-       if (r != EOK)
-               goto Finish;
-
-       r = write_sblocks(bd, &aux_info, info);
-       if (r != EOK)
-               goto Finish;
-
-       Finish:
-       release_fs_aux_info(&aux_info);
-       return r;
-}
-
-static int init_bgs(struct ext4_fs *fs)
-{
-       int r = EOK;
-       struct ext4_block_group_ref ref;
-       uint32_t i;
-       uint32_t bg_count = ext4_block_group_cnt(&fs->sb);
-       for (i = 0; i < bg_count; ++i) {
-               r = ext4_fs_get_block_group_ref(fs, i, &ref);
-               if (r != EOK)
-                       break;
-
-               r = ext4_fs_put_block_group_ref(&ref);
-               if (r != EOK)
-                       break;
-       }
-       return r;
-}
-
-static int alloc_inodes(struct ext4_fs *fs)
-{
-       int r = EOK;
-       int i;
-       struct ext4_inode_ref inode_ref;
-       for (i = 1; i < 12; ++i) {
-               int filetype = EXT4_DE_REG_FILE;
-
-               switch (i) {
-               case EXT4_ROOT_INO:
-               case EXT4_GOOD_OLD_FIRST_INO:
-                       filetype = EXT4_DE_DIR;
-                       break;
-               }
-
-               r = ext4_fs_alloc_inode(fs, &inode_ref, filetype);
-               if (r != EOK)
-                       return r;
-
-               ext4_inode_set_mode(&fs->sb, inode_ref.inode, 0);
-               ext4_fs_put_inode_ref(&inode_ref);
-       }
-
-       return r;
-}
-
-static int create_dirs(struct ext4_fs *fs)
-{
-       int r = EOK;
-       struct ext4_inode_ref root;
-       struct ext4_inode_ref child;
-
-       r = ext4_fs_get_inode_ref(fs, EXT4_ROOT_INO, &root);
-       if (r != EOK)
-               return r;
-
-       r = ext4_fs_get_inode_ref(fs, EXT4_GOOD_OLD_FIRST_INO, &child);
-       if (r != EOK)
-               return r;
-
-       ext4_inode_set_mode(&fs->sb, child.inode,
-                       EXT4_INODE_MODE_DIRECTORY | 0777);
-
-       ext4_inode_set_mode(&fs->sb, root.inode,
-                       EXT4_INODE_MODE_DIRECTORY | 0777);
-
-#if CONFIG_DIR_INDEX_ENABLE
-       /* Initialize directory index if supported */
-       if (ext4_sb_feature_com(&fs->sb, EXT4_FCOM_DIR_INDEX)) {
-               r = ext4_dir_dx_init(&root, &root);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_dir_dx_init(&child, &root);
-               if (r != EOK)
-                       return r;
-
-               ext4_inode_set_flag(root.inode, EXT4_INODE_FLAG_INDEX);
-               ext4_inode_set_flag(child.inode, EXT4_INODE_FLAG_INDEX);
-       } else
-#endif
-       {
-               r = ext4_dir_add_entry(&root, ".", strlen("."), &root);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_dir_add_entry(&root, "..", strlen(".."), &root);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_dir_add_entry(&child, ".", strlen("."), &child);
-               if (r != EOK)
-                       return r;
-
-               r = ext4_dir_add_entry(&child, "..", strlen(".."), &root);
-               if (r != EOK)
-                       return r;
-       }
-
-       r = ext4_dir_add_entry(&root, "lost+found", strlen("lost+found"), &child);
-       if (r != EOK)
-               return r;
-
-       ext4_inode_set_links_cnt(root.inode, 3);
-       ext4_inode_set_links_cnt(child.inode, 2);
-
-       child.dirty = true;
-       root.dirty = true;
-       ext4_fs_put_inode_ref(&child);
-       ext4_fs_put_inode_ref(&root);
-       return r;
-}
-
-int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd,
-             struct ext4_mkfs_info *info, int fs_type)
-{
-       int r;
-
-       r = ext4_block_init(bd);
-       if (r != EOK)
-               return r;
-
-       bd->fs = fs;
-
-       if (info->len == 0)
-               info->len = bd->bdif->ph_bcnt * bd->bdif->ph_bsize;
-
-       if (info->block_size == 0)
-               info->block_size = 4096; /*Set block size to default value*/
-
-       /* Round down the filesystem length to be a multiple of the block size */
-       info->len &= ~((uint64_t)info->block_size - 1);
-
-       if (info->journal_blocks == 0)
-               info->journal_blocks = compute_journal_blocks(info);
-
-       if (info->blocks_per_group == 0)
-               info->blocks_per_group = compute_blocks_per_group(info);
-
-       if (info->inodes == 0)
-               info->inodes = compute_inodes(info);
-
-       if (info->inode_size == 0)
-               info->inode_size = 256;
-
-       if (info->label == NULL)
-               info->label = "";
-
-       info->inodes_per_group = compute_inodes_per_group(info);
-
-       switch (fs_type) {
-       case F_SET_EXT2:
-               info->feat_compat = EXT2_SUPPORTED_FCOM;
-               info->feat_ro_compat = EXT2_SUPPORTED_FRO_COM;
-               info->feat_incompat = EXT2_SUPPORTED_FINCOM;
-               break;
-       case F_SET_EXT3:
-               info->feat_compat = EXT3_SUPPORTED_FCOM;
-               info->feat_ro_compat = EXT3_SUPPORTED_FRO_COM;
-               info->feat_incompat = EXT3_SUPPORTED_FINCOM;
-               break;
-       case F_SET_EXT4:
-               info->feat_compat = EXT4_SUPPORTED_FCOM;
-               info->feat_ro_compat = EXT4_SUPPORTED_FRO_COM;
-               info->feat_incompat = EXT4_SUPPORTED_FINCOM;
-               break;
-       }
-
-       /*TODO: handle this features*/
-       info->feat_incompat &= ~EXT4_FINCOM_META_BG;
-       info->feat_incompat &= ~EXT4_FINCOM_FLEX_BG;
-       info->feat_ro_compat &= ~EXT4_FRO_COM_METADATA_CSUM;
-
-       /*TODO: handle journal feature & inode*/
-       if (info->journal == 0)
-               info->feat_compat |= 0;
-
-       if (info->dsc_size == 0) {
-
-               if (info->feat_incompat & EXT4_FINCOM_64BIT)
-                       info->dsc_size = EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE;
-               else
-                       info->dsc_size = EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE;
-       }
-
-       info->bg_desc_reserve_blocks = 0;
-
-       ext4_dbg(DEBUG_MKFS, DBG_INFO "Creating filesystem with parameters:\n");
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Size: %"PRIu64"\n", info->len);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Block size: %"PRIu32"\n",
-                       info->block_size);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Blocks per group: %"PRIu32"\n",
-                       info->blocks_per_group);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes per group: %"PRIu32"\n",
-                       info->inodes_per_group);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Inode size: %"PRIu32"\n",
-                       info->inode_size);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes: %"PRIu32"\n", info->inodes);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Journal blocks: %"PRIu32"\n",
-                       info->journal_blocks);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Features ro_compat: 0x%x\n",
-                       info->feat_ro_compat);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Features compat: 0x%x\n",
-                       info->feat_compat);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Features incompat: 0x%x\n",
-                       info->feat_incompat);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "BG desc reserve: %"PRIu32"\n",
-                       info->bg_desc_reserve_blocks);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Descriptor size: %"PRIu16"\n",
-                       info->dsc_size);
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "journal: %s\n",
-                       info->journal ? "yes" : "no");
-       ext4_dbg(DEBUG_MKFS, DBG_NONE "Label: %s\n", info->label);
-
-       struct ext4_bcache bc;
-       memset(&bc, 0, sizeof(struct ext4_bcache));
-       ext4_block_set_lb_size(bd, info->block_size);
-       r = ext4_bcache_init_dynamic(&bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
-                                     info->block_size);
-       if (r != EOK)
-               goto block_fini;
-
-       /*Bind block cache to block device*/
-       r = ext4_block_bind_bcache(bd, &bc);
-       if (r != EOK)
-               goto cache_fini;
-
-       r = ext4_block_cache_write_back(bd, 1);
-       if (r != EOK)
-               goto cache_fini;
-
-       r = mkfs_initial(bd, info);
-       if (r != EOK)
-               goto cache_fini;
-
-       r = ext4_fs_init(fs, bd);
-       if (r != EOK)
-               goto cache_fini;
-
-       r = init_bgs(fs);
-       if (r != EOK)
-               goto fs_fini;
-
-       r = alloc_inodes(fs);
-       if (r != EOK)
-               goto fs_fini;
-
-       r = create_dirs(fs);
-       if (r != EOK)
-               goto fs_fini;
-
-       fs_fini:
-       ext4_fs_fini(fs);
-
-       cache_fini:
-       ext4_block_cache_write_back(bd, 0);
-       ext4_bcache_fini_dynamic(&bc);
-
-       block_fini:
-       ext4_block_fini(bd);
-
-       return r;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_mkfs.h b/lwext4/ext4_mkfs.h
deleted file mode 100644 (file)
index 497bf3b..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2015 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_mkfs.h
- * @brief
- */
-
-#ifndef EXT4_MKFS_H_
-#define EXT4_MKFS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_blockdev.h"
-
-#include <stdbool.h>
-#include <stdint.h>
-
-
-struct ext4_mkfs_info {
-       int64_t len;
-       uint32_t block_size;
-       uint32_t blocks_per_group;
-       uint32_t inodes_per_group;
-       uint32_t inode_size;
-       uint32_t inodes;
-       uint32_t journal_blocks;
-       uint16_t feat_ro_compat;
-       uint16_t feat_compat;
-       uint16_t feat_incompat;
-       uint32_t bg_desc_reserve_blocks;
-       uint16_t dsc_size;
-       uint8_t journal;
-       const char *label;
-};
-
-int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info);
-
-int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd,
-             struct ext4_mkfs_info *info, int fs_type);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_MKFS_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_oflags.h b/lwext4/ext4_oflags.h
deleted file mode 100644 (file)
index 7f7be7e..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@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_oflags.h
- * @brief File opening & seeking flags.
- */
-#ifndef EXT4_OFLAGS_H_
-#define EXT4_OFLAGS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/********************************FILE OPEN FLAGS*****************************/
-
-#if CONFIG_HAVE_OWN_OFLAGS
-
- #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
-
-#else
- #include <unistd.h>
- #include <fcntl.h>
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_OFLAGS_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_super.c b/lwext4/ext4_super.c
deleted file mode 100644 (file)
index 582d1fb..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * 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_super.h
- * @brief Superblock operations.
- */
-
-#include "ext4_config.h"
-#include "ext4_super.h"
-#include "ext4_crc32.h"
-
-uint32_t ext4_block_group_cnt(struct ext4_sblock *s)
-{
-       uint64_t blocks_count = ext4_sb_get_blocks_cnt(s);
-       uint32_t blocks_per_group = ext4_get32(s, blocks_per_group);
-
-       uint32_t block_groups_count = (uint32_t)(blocks_count / blocks_per_group);
-
-       if (blocks_count % blocks_per_group)
-               block_groups_count++;
-
-       return block_groups_count;
-}
-
-uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid)
-{
-       uint32_t block_group_count = ext4_block_group_cnt(s);
-       uint32_t blocks_per_group = ext4_get32(s, blocks_per_group);
-       uint64_t total_blocks = ext4_sb_get_blocks_cnt(s);
-
-       if (bgid < block_group_count - 1)
-               return blocks_per_group;
-
-       return (uint32_t)(total_blocks - ((block_group_count - 1) * blocks_per_group));
-}
-
-uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid)
-{
-       uint32_t block_group_count = ext4_block_group_cnt(s);
-       uint32_t inodes_per_group = ext4_get32(s, inodes_per_group);
-       uint32_t total_inodes = ext4_get32(s, inodes_count);
-
-       if (bgid < block_group_count - 1)
-               return inodes_per_group;
-
-       return (total_inodes - ((block_group_count - 1) * inodes_per_group));
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t ext4_sb_csum(struct ext4_sblock *s)
-{
-
-       return ext4_crc32c(EXT4_CRC32_INIT, s,
-                       offsetof(struct ext4_sblock, checksum));
-}
-#else
-#define ext4_sb_csum(...) 0
-#endif
-
-static bool ext4_sb_verify_csum(struct ext4_sblock *s)
-{
-       if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM))
-               return true;
-
-       if (s->checksum_type != to_le32(EXT4_CHECKSUM_CRC32C))
-               return false;
-
-       return s->checksum == to_le32(ext4_sb_csum(s));
-}
-
-static void ext4_sb_set_csum(struct ext4_sblock *s)
-{
-       if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM))
-               return;
-
-       s->checksum = to_le32(ext4_sb_csum(s));
-}
-
-int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s)
-{
-       ext4_sb_set_csum(s);
-       return ext4_block_writebytes(bdev, EXT4_SUPERBLOCK_OFFSET, s,
-                                    EXT4_SUPERBLOCK_SIZE);
-}
-
-int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s)
-{
-       return ext4_block_readbytes(bdev, EXT4_SUPERBLOCK_OFFSET, s,
-                                   EXT4_SUPERBLOCK_SIZE);
-}
-
-bool ext4_sb_check(struct ext4_sblock *s)
-{
-       if (ext4_get16(s, magic) != EXT4_SUPERBLOCK_MAGIC)
-               return false;
-
-       if (ext4_get32(s, inodes_count) == 0)
-               return false;
-
-       if (ext4_sb_get_blocks_cnt(s) == 0)
-               return false;
-
-       if (ext4_get32(s, blocks_per_group) == 0)
-               return false;
-
-       if (ext4_get32(s, inodes_per_group) == 0)
-               return false;
-
-       if (ext4_get16(s, inode_size) < 128)
-               return false;
-
-       if (ext4_get32(s, first_inode) < 11)
-               return false;
-
-       if (ext4_sb_get_desc_size(s) < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               return false;
-
-       if (ext4_sb_get_desc_size(s) > EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
-               return false;
-
-       if (!ext4_sb_verify_csum(s))
-               return false;
-
-       return true;
-}
-
-static inline int is_power_of(uint32_t a, uint32_t b)
-{
-       while (1) {
-               if (a < b)
-                       return 0;
-               if (a == b)
-                       return 1;
-               if ((a % b) != 0)
-                       return 0;
-               a = a / b;
-       }
-}
-
-bool ext4_sb_sparse(uint32_t group)
-{
-       if (group <= 1)
-               return 1;
-
-       if (!(group & 1))
-               return 0;
-
-       return (is_power_of(group, 7) || is_power_of(group, 5) ||
-               is_power_of(group, 3));
-}
-
-bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t group)
-{
-       if (ext4_sb_feature_ro_com(s, EXT4_FRO_COM_SPARSE_SUPER) &&
-           !ext4_sb_sparse(group))
-               return false;
-       return true;
-}
-
-static uint32_t ext4_bg_num_gdb_meta(struct ext4_sblock *s, uint32_t group)
-{
-       uint32_t dsc_per_block =
-           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
-
-       uint32_t metagroup = group / dsc_per_block;
-       uint32_t first = metagroup * dsc_per_block;
-       uint32_t last = first + dsc_per_block - 1;
-
-       if (group == first || group == first + 1 || group == last)
-               return 1;
-       return 0;
-}
-
-static uint32_t ext4_bg_num_gdb_nometa(struct ext4_sblock *s, uint32_t group)
-{
-       if (!ext4_sb_is_super_in_bg(s, group))
-               return 0;
-       uint32_t dsc_per_block =
-           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
-
-       uint32_t db_count =
-           (ext4_block_group_cnt(s) + dsc_per_block - 1) / dsc_per_block;
-
-       if (ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG))
-               return ext4_sb_first_meta_bg(s);
-
-       return db_count;
-}
-
-uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group)
-{
-       uint32_t dsc_per_block =
-           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
-       uint32_t first_meta_bg = ext4_sb_first_meta_bg(s);
-       uint32_t metagroup = group / dsc_per_block;
-
-       if (!ext4_sb_feature_incom(s,EXT4_FINCOM_META_BG) ||
-           metagroup < first_meta_bg)
-               return ext4_bg_num_gdb_nometa(s, group);
-
-       return ext4_bg_num_gdb_meta(s, group);
-}
-
-uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
-                                    uint32_t block_group)
-{
-       uint32_t num;
-       uint32_t dsc_per_block =
-           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
-
-       num = ext4_sb_is_super_in_bg(s, block_group);
-
-       if (!ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG) ||
-           block_group < ext4_sb_first_meta_bg(s) * dsc_per_block) {
-               if (num) {
-                       num += ext4_bg_num_gdb(s, block_group);
-                       num += ext4_get16(s, s_reserved_gdt_blocks);
-               }
-       } else {
-               num += ext4_bg_num_gdb(s, block_group);
-       }
-
-       uint32_t clustersize = 1024 << ext4_get32(s, log_cluster_size);
-       uint32_t cluster_ratio = clustersize / ext4_sb_get_block_size(s);
-       uint32_t v =
-           (num + cluster_ratio - 1) >> ext4_get32(s, log_cluster_size);
-
-       return v;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_super.h b/lwext4/ext4_super.h
deleted file mode 100644 (file)
index 2172698..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * 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_super.c
- * @brief Superblock operations.
- */
-
-#ifndef EXT4_SUPER_H_
-#define EXT4_SUPER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-/**@brief   Blocks count get stored in superblock.
- * @param   s superblock descriptor
- * @return  count of blocks*/
-static inline uint64_t ext4_sb_get_blocks_cnt(struct ext4_sblock *s)
-{
-       return ((uint64_t)to_le32(s->blocks_count_hi) << 32) |
-              to_le32(s->blocks_count_lo);
-}
-
-/**@brief   Blocks count set  in superblock.
- * @param   s superblock descriptor
- * @return  count of blocks*/
-static inline void ext4_sb_set_blocks_cnt(struct ext4_sblock *s, uint64_t cnt)
-{
-       s->blocks_count_lo = to_le32((cnt << 32) >> 32);
-       s->blocks_count_hi = to_le32(cnt >> 32);
-}
-
-/**@brief   Free blocks count get stored in superblock.
- * @param   s superblock descriptor
- * @return  free blocks*/
-static inline uint64_t ext4_sb_get_free_blocks_cnt(struct ext4_sblock *s)
-{
-       return ((uint64_t)to_le32(s->free_blocks_count_hi) << 32) |
-              to_le32(s->free_blocks_count_lo);
-}
-
-/**@brief   Free blocks count set.
- * @param   s superblock descriptor
- * @param   cnt new value of free blocks*/
-static inline void ext4_sb_set_free_blocks_cnt(struct ext4_sblock *s,
-                                              uint64_t cnt)
-{
-       s->free_blocks_count_lo = to_le32((cnt << 32) >> 32);
-       s->free_blocks_count_hi = to_le32(cnt >> 32);
-}
-
-/**@brief   Block size get from superblock.
- * @param   s superblock descriptor
- * @return  block size in bytes*/
-static inline uint32_t ext4_sb_get_block_size(struct ext4_sblock *s)
-{
-       return 1024 << to_le32(s->log_block_size);
-}
-
-/**@brief   Block group descriptor size.
- * @param   s superblock descriptor
- * @return  block group descriptor size in bytes*/
-static inline uint16_t ext4_sb_get_desc_size(struct ext4_sblock *s)
-{
-       uint16_t size = to_le16(s->desc_size);
-
-       return size < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
-                  ? EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
-                  : size;
-}
-
-/*************************Flags and features*********************************/
-
-/**@brief   Support check of flag.
- * @param   s superblock descriptor
- * @param   v flag to check
- * @return  true if flag is supported*/
-static inline bool ext4_sb_check_flag(struct ext4_sblock *s, uint32_t v)
-{
-       return to_le32(s->flags) & v;
-}
-
-/**@brief   Support check of feature compatible.
- * @param   s superblock descriptor
- * @param   v feature to check
- * @return  true if feature is supported*/
-static inline bool ext4_sb_feature_com(struct ext4_sblock *s, uint32_t v)
-{
-       return to_le32(s->features_compatible) & v;
-}
-
-/**@brief   Support check of feature incompatible.
- * @param   s superblock descriptor
- * @param   v feature to check
- * @return  true if feature is supported*/
-static inline bool ext4_sb_feature_incom(struct ext4_sblock *s, uint32_t v)
-{
-       return to_le32(s->features_incompatible) & v;
-}
-
-/**@brief   Support check of read only flag.
- * @param   s superblock descriptor
- * @param   v flag to check
- * @return  true if flag is supported*/
-static inline bool ext4_sb_feature_ro_com(struct ext4_sblock *s, uint32_t v)
-{
-       return to_le32(s->features_read_only) & v;
-}
-
-/**@brief   Block group to flex group.
- * @param   s superblock descriptor
- * @param   block_group block group
- * @return  flex group id*/
-static inline uint32_t ext4_sb_bg_to_flex(struct ext4_sblock *s,
-                                         uint32_t block_group)
-{
-       return block_group >> to_le32(s->log_groups_per_flex);
-}
-
-/**@brief   Flex block group size.
- * @param   s superblock descriptor
- * @return  flex bg size*/
-static inline uint32_t ext4_sb_flex_bg_size(struct ext4_sblock *s)
-{
-       return 1 << to_le32(s->log_groups_per_flex);
-}
-
-/**@brief   Return first meta block group id.
- * @param   s superblock descriptor
- * @return  first meta_bg id */
-static inline uint32_t ext4_sb_first_meta_bg(struct ext4_sblock *s)
-{
-       return to_le32(s->first_meta_bg);
-}
-
-/**************************More complex functions****************************/
-
-/**@brief   Returns a block group count.
- * @param   s superblock descriptor
- * @return  count of block groups*/
-uint32_t ext4_block_group_cnt(struct ext4_sblock *s);
-
-/**@brief   Returns block count in block group
- *          (last block group may have less blocks)
- * @param   s superblock descriptor
- * @param   bgid block group id
- * @return  blocks count*/
-uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid);
-
-/**@brief   Returns inodes count in block group
- *          (last block group may have less inodes)
- * @param   s superblock descriptor
- * @param   bgid block group id
- * @return  inodes count*/
-uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid);
-
-/***************************Read/write/check superblock**********************/
-
-/**@brief   Superblock write.
- * @param   bdev block device descriptor.
- * @param   s superblock descriptor
- * @return  Standard error code */
-int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s);
-
-/**@brief   Superblock read.
- * @param   bdev block device descriptor.
- * @param   s superblock descriptor
- * @return  Standard error code */
-int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s);
-
-/**@brief   Superblock simple validation.
- * @param   s superblock descriptor
- * @return  true if OK*/
-bool ext4_sb_check(struct ext4_sblock *s);
-
-/**@brief   Superblock presence in block group.
- * @param   s superblock descriptor
- * @param   block_group block group id
- * @return  true if block group has superblock*/
-bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t block_group);
-
-/**@brief   TODO:*/
-bool ext4_sb_sparse(uint32_t group);
-
-/**@brief   TODO:*/
-uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group);
-
-/**@brief   TODO:*/
-uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
-                                    uint32_t block_group);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_SUPER_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_trans.c b/lwext4/ext4_trans.c
deleted file mode 100644 (file)
index 9f32fb8..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@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_trans.c
- * @brief Ext4 transaction buffer operations.
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_journal.h"
-
-static int ext4_trans_get_write_access(struct ext4_fs *fs __unused,
-                               struct ext4_block *block __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       if (fs->jbd_journal && fs->curr_trans) {
-               struct jbd_journal *journal = fs->jbd_journal;
-               struct jbd_trans *trans = fs->curr_trans;
-               r = jbd_trans_get_access(journal, trans, block);
-       }
-#endif
-       return r;
-}
-
-int ext4_trans_set_block_dirty(struct ext4_buf *buf)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       struct ext4_fs *fs = buf->bc->bdev->fs;
-       struct ext4_block block = {
-               .lb_id = buf->lba,
-               .data = buf->data,
-               .buf = buf
-       };
-
-       if (fs->jbd_journal && fs->curr_trans) {
-               struct jbd_trans *trans = fs->curr_trans;
-               return jbd_trans_set_block_dirty(trans, &block);
-       }
-#endif
-       ext4_bcache_set_dirty(buf);
-       return r;
-}
-
-int ext4_trans_block_get_noread(struct ext4_blockdev *bdev,
-                         struct ext4_block *b,
-                         uint64_t lba)
-{
-       int r = ext4_block_get_noread(bdev, b, lba);
-       if (r != EOK)
-               return r;
-
-       r = ext4_trans_get_write_access(bdev->fs, b);
-       if (r != EOK)
-               ext4_block_set(bdev, b);
-
-       return r;
-}
-
-int ext4_trans_block_get(struct ext4_blockdev *bdev,
-                  struct ext4_block *b,
-                  uint64_t lba)
-{
-       int r = ext4_block_get(bdev, b, lba);
-       if (r != EOK)
-               return r;
-
-       r = ext4_trans_get_write_access(bdev->fs, b);
-       if (r != EOK)
-               ext4_block_set(bdev, b);
-
-       return r;
-}
-
-int ext4_trans_try_revoke_block(struct ext4_blockdev *bdev __unused,
-                               uint64_t lba __unused)
-{
-       int r = EOK;
-#if CONFIG_JOURNALING_ENABLE
-       struct ext4_fs *fs = bdev->fs;
-       if (fs->jbd_journal && fs->curr_trans) {
-               struct jbd_trans *trans = fs->curr_trans;
-               r = jbd_trans_try_revoke_block(trans, lba);
-       } else if (fs->jbd_journal) {
-               r = ext4_block_flush_lba(fs->bdev, lba);
-       }
-#endif
-       return r;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_trans.h b/lwext4/ext4_trans.h
deleted file mode 100644 (file)
index e3cb28a..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
- *
- * 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_trans.h
- * @brief Transaction handle functions
- */
-
-#ifndef EXT4_TRANS_H
-#define EXT4_TRANS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-
-/**@brief   Mark a buffer dirty and add it to the current transaction.
- * @param   buf buffer
- * @return  standard error code*/
-int ext4_trans_set_block_dirty(struct ext4_buf *buf);
-
-/**@brief   Block get function (through cache, don't read).
- *          jbd_trans_get_access would be called in order to
- *          get write access to the buffer.
- * @param   bdev block device descriptor
- * @param   b block descriptor
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_trans_block_get_noread(struct ext4_blockdev *bdev,
-                         struct ext4_block *b,
-                         uint64_t lba);
-
-/**@brief   Block get function (through cache).
- *          jbd_trans_get_access would be called in order to
- *          get write access to the buffer.
- * @param   bdev block device descriptor
- * @param   b block descriptor
- * @param   lba logical block address
- * @return  standard error code*/
-int ext4_trans_block_get(struct ext4_blockdev *bdev,
-                  struct ext4_block *b,
-                  uint64_t lba);
-
-/**@brief  Try to add block to be revoked to the current transaction.
- * @param  bdev block device descriptor
- * @param  lba logical block address
- * @return standard error code*/
-int ext4_trans_try_revoke_block(struct ext4_blockdev *bdev,
-                              uint64_t lba);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_TRANS_H */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_types.h b/lwext4/ext4_types.h
deleted file mode 100644 (file)
index 41cb33f..0000000
+++ /dev/null
@@ -1,1304 +0,0 @@
-/*
- * 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_types.h
- * @brief Ext4 data structure definitions.
- */
-
-#ifndef EXT4_TYPES_H_
-#define EXT4_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_blockdev.h"
-#include "misc/tree.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#define EXT4_CHECKSUM_CRC32C 1
-
-#define UUID_SIZE 16
-
-#pragma pack(push, 1)
-
-/*
- * Structure of the super block
- */
-struct ext4_sblock {
-       uint32_t inodes_count;             /* I-nodes count */
-       uint32_t blocks_count_lo;         /* Blocks count */
-       uint32_t reserved_blocks_count_lo; /* Reserved blocks count */
-       uint32_t free_blocks_count_lo;     /* Free blocks count */
-       uint32_t free_inodes_count;     /* Free inodes count */
-       uint32_t first_data_block;       /* First Data Block */
-       uint32_t log_block_size;           /* Block size */
-       uint32_t log_cluster_size;       /* Obsoleted fragment size */
-       uint32_t blocks_per_group;       /* Number of blocks per group */
-       uint32_t frags_per_group;         /* Obsoleted fragments per group */
-       uint32_t inodes_per_group;       /* Number of inodes per group */
-       uint32_t mount_time;               /* Mount time */
-       uint32_t write_time;               /* Write time */
-       uint16_t mount_count;              /* Mount count */
-       uint16_t max_mount_count;         /* Maximal mount count */
-       uint16_t magic;                    /* Magic signature */
-       uint16_t state;                    /* File system state */
-       uint16_t errors;                   /* Behavior when detecting errors */
-       uint16_t minor_rev_level;         /* Minor revision level */
-       uint32_t last_check_time;         /* Time of last check */
-       uint32_t check_interval;           /* Maximum time between checks */
-       uint32_t creator_os;               /* Creator OS */
-       uint32_t rev_level;                /* Revision level */
-       uint16_t def_resuid;               /* Default uid for reserved blocks */
-       uint16_t def_resgid;               /* Default gid for reserved blocks */
-
-       /* Fields for EXT4_DYNAMIC_REV superblocks only. */
-       uint32_t first_inode;    /* First non-reserved inode */
-       uint16_t inode_size;      /* Size of inode structure */
-       uint16_t block_group_index;   /* Block group index of this superblock */
-       uint32_t features_compatible; /* Compatible feature set */
-       uint32_t features_incompatible;  /* Incompatible feature set */
-       uint32_t features_read_only;     /* Readonly-compatible feature set */
-       uint8_t uuid[UUID_SIZE];                 /* 128-bit uuid for volume */
-       char volume_name[16];            /* Volume name */
-       char last_mounted[64];           /* Directory where last mounted */
-       uint32_t algorithm_usage_bitmap; /* For compression */
-
-       /*
-        * Performance hints. Directory preallocation should only
-        * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
-        */
-       uint8_t s_prealloc_blocks; /* Number of blocks to try to preallocate */
-       uint8_t s_prealloc_dir_blocks;  /* Number to preallocate for dirs */
-       uint16_t s_reserved_gdt_blocks; /* Per group desc for online growth */
-
-       /*
-        * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
-        */
-       uint8_t journal_uuid[UUID_SIZE];      /* UUID of journal superblock */
-       uint32_t journal_inode_number; /* Inode number of journal file */
-       uint32_t journal_dev;     /* Device number of journal file */
-       uint32_t last_orphan;     /* Head of list of inodes to delete */
-       uint32_t hash_seed[4];   /* HTREE hash seed */
-       uint8_t default_hash_version;  /* Default hash version to use */
-       uint8_t journal_backup_type;
-       uint16_t desc_size;       /* Size of group descriptor */
-       uint32_t default_mount_opts; /* Default mount options */
-       uint32_t first_meta_bg;      /* First metablock block group */
-       uint32_t mkfs_time;       /* When the filesystem was created */
-       uint32_t journal_blocks[17]; /* Backup of the journal inode */
-
-       /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
-       uint32_t blocks_count_hi;         /* Blocks count */
-       uint32_t reserved_blocks_count_hi; /* Reserved blocks count */
-       uint32_t free_blocks_count_hi;     /* Free blocks count */
-       uint16_t min_extra_isize;    /* All inodes have at least # bytes */
-       uint16_t want_extra_isize;   /* New inodes should reserve # bytes */
-       uint32_t flags;              /* Miscellaneous flags */
-       uint16_t raid_stride;   /* RAID stride */
-       uint16_t mmp_interval;       /* # seconds to wait in MMP checking */
-       uint64_t mmp_block;       /* Block for multi-mount protection */
-       uint32_t raid_stripe_width;  /* Blocks on all data disks (N * stride) */
-       uint8_t log_groups_per_flex; /* FLEX_BG group size */
-       uint8_t checksum_type;
-       uint16_t reserved_pad;
-       uint64_t kbytes_written; /* Number of lifetime kilobytes written */
-       uint32_t snapshot_inum;  /* I-node number of active snapshot */
-       uint32_t snapshot_id;    /* Sequential ID of active snapshot */
-       uint64_t
-           snapshot_r_blocks_count; /* Reserved blocks for active snapshot's
-                                       future use */
-       uint32_t
-           snapshot_list; /* I-node number of the head of the on-disk snapshot
-                             list */
-       uint32_t error_count;    /* Number of file system errors */
-       uint32_t first_error_time;    /* First time an error happened */
-       uint32_t first_error_ino;     /* I-node involved in first error */
-       uint64_t first_error_block;   /* Block involved of first error */
-       uint8_t first_error_func[32]; /* Function where the error happened */
-       uint32_t first_error_line;    /* Line number where error happened */
-       uint32_t last_error_time;     /* Most recent time of an error */
-       uint32_t last_error_ino;      /* I-node involved in last error */
-       uint32_t last_error_line;     /* Line number where error happened */
-       uint64_t last_error_block;    /* Block involved of last error */
-       uint8_t last_error_func[32];  /* Function where the error happened */
-       uint8_t mount_opts[64];
-       uint32_t usr_quota_inum;        /* inode for tracking user quota */
-       uint32_t grp_quota_inum;        /* inode for tracking group quota */
-       uint32_t overhead_clusters;     /* overhead blocks/clusters in fs */
-       uint32_t backup_bgs[2]; /* groups with sparse_super2 SBs */
-       uint8_t  encrypt_algos[4];      /* Encryption algorithms in use  */
-       uint8_t  encrypt_pw_salt[16];   /* Salt used for string2key algorithm */
-       uint32_t lpf_ino;               /* Location of the lost+found inode */
-       uint32_t padding[100];  /* Padding to the end of the block */
-       uint32_t checksum;              /* crc32c(superblock) */
-};
-
-#pragma pack(pop)
-
-#define EXT4_SUPERBLOCK_MAGIC 0xEF53
-#define EXT4_SUPERBLOCK_SIZE 1024
-#define EXT4_SUPERBLOCK_OFFSET 1024
-
-#define EXT4_SUPERBLOCK_OS_LINUX 0
-#define EXT4_SUPERBLOCK_OS_HURD 1
-
-/*
- * Misc. filesystem flags
- */
-#define EXT4_SUPERBLOCK_FLAGS_SIGNED_HASH 0x0001
-#define EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH 0x0002
-#define EXT4_SUPERBLOCK_FLAGS_TEST_FILESYS 0x0004
-/*
- * Filesystem states
- */
-#define EXT4_SUPERBLOCK_STATE_VALID_FS 0x0001  /* Unmounted cleanly */
-#define EXT4_SUPERBLOCK_STATE_ERROR_FS 0x0002  /* Errors detected */
-#define EXT4_SUPERBLOCK_STATE_ORPHAN_FS 0x0004 /* Orphans being recovered */
-
-/*
- * Behaviour when errors detected
- */
-#define EXT4_SUPERBLOCK_ERRORS_CONTINUE 1 /* Continue execution */
-#define EXT4_SUPERBLOCK_ERRORS_RO 2       /* Remount fs read-only */
-#define EXT4_SUPERBLOCK_ERRORS_PANIC 3    /* Panic */
-#define EXT4_SUPERBLOCK_ERRORS_DEFAULT EXT4_ERRORS_CONTINUE
-
-/*
- * Compatible features
- */
-#define EXT4_FCOM_DIR_PREALLOC 0x0001
-#define EXT4_FCOM_IMAGIC_INODES 0x0002
-#define EXT4_FCOM_HAS_JOURNAL 0x0004
-#define EXT4_FCOM_EXT_ATTR 0x0008
-#define EXT4_FCOM_RESIZE_INODE 0x0010
-#define EXT4_FCOM_DIR_INDEX 0x0020
-
-/*
- * Read-only compatible features
- */
-#define EXT4_FRO_COM_SPARSE_SUPER 0x0001
-#define EXT4_FRO_COM_LARGE_FILE 0x0002
-#define EXT4_FRO_COM_BTREE_DIR 0x0004
-#define EXT4_FRO_COM_HUGE_FILE 0x0008
-#define EXT4_FRO_COM_GDT_CSUM 0x0010
-#define EXT4_FRO_COM_DIR_NLINK 0x0020
-#define EXT4_FRO_COM_EXTRA_ISIZE 0x0040
-#define EXT4_FRO_COM_QUOTA 0x0100
-#define EXT4_FRO_COM_BIGALLOC 0x0200
-#define EXT4_FRO_COM_METADATA_CSUM 0x0400
-
-/*
- * Incompatible features
- */
-#define EXT4_FINCOM_COMPRESSION 0x0001
-#define EXT4_FINCOM_FILETYPE 0x0002
-#define EXT4_FINCOM_RECOVER 0x0004     /* Needs recovery */
-#define EXT4_FINCOM_JOURNAL_DEV 0x0008 /* Journal device */
-#define EXT4_FINCOM_META_BG 0x0010
-#define EXT4_FINCOM_EXTENTS 0x0040 /* extents support */
-#define EXT4_FINCOM_64BIT 0x0080
-#define EXT4_FINCOM_MMP 0x0100
-#define EXT4_FINCOM_FLEX_BG 0x0200
-#define EXT4_FINCOM_EA_INODE 0x0400     /* EA in inode */
-#define EXT4_FINCOM_DIRDATA 0x1000       /* data in dirent */
-#define EXT4_FINCOM_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */
-#define EXT4_FINCOM_LARGEDIR 0x4000     /* >2GB or 3-lvl htree */
-#define EXT4_FINCOM_INLINE_DATA 0x8000      /* data in inode */
-
-/*
- * EXT2 supported feature set
- */
-#define EXT2_SUPPORTED_FCOM 0x0000
-
-#define EXT2_SUPPORTED_FINCOM                                   \
-       (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG)
-
-#define EXT2_SUPPORTED_FRO_COM                                  \
-       (EXT4_FRO_COM_SPARSE_SUPER |                            \
-        EXT4_FRO_COM_LARGE_FILE)
-
-/*
- * EXT3 supported feature set
- */
-#define EXT3_SUPPORTED_FCOM (EXT4_FCOM_DIR_INDEX)
-
-#define EXT3_SUPPORTED_FINCOM                                 \
-       (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG)
-
-#define EXT3_SUPPORTED_FRO_COM                                \
-       (EXT4_FRO_COM_SPARSE_SUPER | EXT4_FRO_COM_LARGE_FILE)
-
-/*
- * EXT4 supported feature set
- */
-#define EXT4_SUPPORTED_FCOM (EXT4_FCOM_DIR_INDEX)
-
-#define EXT4_SUPPORTED_FINCOM                              \
-       (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG |      \
-        EXT4_FINCOM_EXTENTS | EXT4_FINCOM_FLEX_BG |       \
-        EXT4_FINCOM_64BIT)
-
-#define EXT4_SUPPORTED_FRO_COM                             \
-       (EXT4_FRO_COM_SPARSE_SUPER |                       \
-        EXT4_FRO_COM_METADATA_CSUM |                      \
-        EXT4_FRO_COM_LARGE_FILE | EXT4_FRO_COM_GDT_CSUM | \
-        EXT4_FRO_COM_DIR_NLINK |                          \
-        EXT4_FRO_COM_EXTRA_ISIZE | EXT4_FRO_COM_HUGE_FILE)
-
-/*Ignored features:
- * RECOVER - journaling in lwext4 is not supported
- *           (probably won't be ever...)
- * MMP - multi-mout protection (impossible scenario)
- * */
-#define EXT_FINCOM_IGNORED                                 \
-       EXT4_FINCOM_RECOVER | EXT4_FINCOM_MMP
-
-#if 0
-/*TODO: Features incompatible to implement*/
-#define EXT4_SUPPORTED_FINCOM
-                     (EXT4_FINCOM_INLINE_DATA)
-
-/*TODO: Features read only to implement*/
-#define EXT4_SUPPORTED_FRO_COM
-                     EXT4_FRO_COM_BIGALLOC |\
-                     EXT4_FRO_COM_QUOTA)
-#endif
-
-struct ext4_fs {
-       struct ext4_blockdev *bdev;
-       struct ext4_sblock sb;
-
-       uint64_t inode_block_limits[4];
-       uint64_t inode_blocks_per_level[4];
-
-       uint32_t last_inode_bg_id;
-
-       struct jbd_fs *jbd_fs;
-       struct jbd_journal *jbd_journal;
-       struct jbd_trans *curr_trans;
-};
-
-/* Inode table/bitmap not in use */
-#define EXT4_BLOCK_GROUP_INODE_UNINIT 0x0001
-/* Block bitmap not in use */
-#define EXT4_BLOCK_GROUP_BLOCK_UNINIT 0x0002
-/* On-disk itable initialized to zero */
-#define EXT4_BLOCK_GROUP_ITABLE_ZEROED 0x0004
-
-/*
- * Structure of a blocks group descriptor
- */
-struct ext4_bgroup {
-       uint32_t block_bitmap_lo;           /* Blocks bitmap block */
-       uint32_t inode_bitmap_lo;           /* Inodes bitmap block */
-       uint32_t inode_table_first_block_lo; /* Inodes table block */
-       uint16_t free_blocks_count_lo;       /* Free blocks count */
-       uint16_t free_inodes_count_lo;       /* Free inodes count */
-       uint16_t used_dirs_count_lo;     /* Directories count */
-       uint16_t flags;                /* EXT4_BG_flags (INODE_UNINIT, etc) */
-       uint32_t exclude_bitmap_lo;    /* Exclude bitmap for snapshots */
-       uint16_t block_bitmap_csum_lo; /* crc32c(s_uuid+grp_num+bbitmap) LE */
-       uint16_t inode_bitmap_csum_lo; /* crc32c(s_uuid+grp_num+ibitmap) LE */
-       uint16_t itable_unused_lo;     /* Unused inodes count */
-       uint16_t checksum;           /* crc16(sb_uuid+group+desc) */
-
-       uint32_t block_bitmap_hi;           /* Blocks bitmap block MSB */
-       uint32_t inode_bitmap_hi;           /* I-nodes bitmap block MSB */
-       uint32_t inode_table_first_block_hi; /* I-nodes table block MSB */
-       uint16_t free_blocks_count_hi;       /* Free blocks count MSB */
-       uint16_t free_inodes_count_hi;       /* Free i-nodes count MSB */
-       uint16_t used_dirs_count_hi;     /* Directories count MSB */
-       uint16_t itable_unused_hi;         /* Unused inodes count MSB */
-       uint32_t exclude_bitmap_hi;       /* Exclude bitmap block MSB */
-       uint16_t block_bitmap_csum_hi; /* crc32c(s_uuid+grp_num+bbitmap) BE */
-       uint16_t inode_bitmap_csum_hi; /* crc32c(s_uuid+grp_num+ibitmap) BE */
-       uint32_t reserved;           /* Padding */
-};
-
-struct ext4_block_group_ref {
-       struct ext4_block block;
-       struct ext4_bgroup *block_group;
-       struct ext4_fs *fs;
-       uint32_t index;
-       bool dirty;
-};
-
-#define EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE 32
-#define EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE 64
-
-#define EXT4_MIN_BLOCK_SIZE 1024  /* 1 KiB */
-#define EXT4_MAX_BLOCK_SIZE 65536 /* 64 KiB */
-#define EXT4_REV0_INODE_SIZE 128
-
-#define EXT4_INODE_BLOCK_SIZE 512
-
-#define EXT4_INODE_DIRECT_BLOCK_COUNT 12
-#define EXT4_INODE_INDIRECT_BLOCK EXT4_INODE_DIRECT_BLOCK_COUNT
-#define EXT4_INODE_DOUBLE_INDIRECT_BLOCK (EXT4_INODE_INDIRECT_BLOCK + 1)
-#define EXT4_INODE_TRIPPLE_INDIRECT_BLOCK (EXT4_INODE_DOUBLE_INDIRECT_BLOCK + 1)
-#define EXT4_INODE_BLOCKS (EXT4_INODE_TRIPPLE_INDIRECT_BLOCK + 1)
-#define EXT4_INODE_INDIRECT_BLOCK_COUNT                                        \
-       (EXT4_INODE_BLOCKS - EXT4_INODE_DIRECT_BLOCK_COUNT)
-
-#pragma pack(push, 1)
-
-/*
- * Structure of an inode on the disk
- */
-struct ext4_inode {
-       uint16_t mode;              /* File mode */
-       uint16_t uid;               /* Low 16 bits of owner uid */
-       uint32_t size_lo;          /* Size in bytes */
-       uint32_t access_time;       /* Access time */
-       uint32_t change_inode_time; /* I-node change time */
-       uint32_t modification_time; /* Modification time */
-       uint32_t deletion_time;     /* Deletion time */
-       uint16_t gid;               /* Low 16 bits of group id */
-       uint16_t links_count;       /* Links count */
-       uint32_t blocks_count_lo;   /* Blocks count */
-       uint32_t flags;             /* File flags */
-       uint32_t unused_osd1;       /* OS dependent - not used in HelenOS */
-       uint32_t blocks[EXT4_INODE_BLOCKS]; /* Pointers to blocks */
-       uint32_t generation;                /* File version (for NFS) */
-       uint32_t file_acl_lo;               /* File ACL */
-       uint32_t size_hi;
-       uint32_t obso_faddr; /* Obsoleted fragment address */
-
-       union {
-               struct {
-                       uint16_t blocks_high;
-                       uint16_t file_acl_high;
-                       uint16_t uid_high;
-                       uint16_t gid_high;
-                       uint16_t checksum_lo; /* crc32c(uuid+inum+inode) LE */
-                       uint16_t reserved2;
-               } linux2;
-               struct {
-                       uint16_t reserved1;
-                       uint16_t mode_high;
-                       uint16_t uid_high;
-                       uint16_t gid_high;
-                       uint32_t author;
-               } hurd2;
-       } osd2;
-
-       uint16_t extra_isize;
-       uint16_t checksum_hi;   /* crc32c(uuid+inum+inode) BE */
-       uint32_t ctime_extra; /* Extra change time (nsec << 2 | epoch) */
-       uint32_t mtime_extra; /* Extra Modification time (nsec << 2 | epoch) */
-       uint32_t atime_extra; /* Extra Access time (nsec << 2 | epoch) */
-       uint32_t crtime;      /* File creation time */
-       uint32_t
-           crtime_extra;    /* Extra file creation time (nsec << 2 | epoch) */
-       uint32_t version_hi; /* High 32 bits for 64-bit version */
-};
-
-#pragma pack(pop)
-
-#define EXT4_INODE_MODE_FIFO 0x1000
-#define EXT4_INODE_MODE_CHARDEV 0x2000
-#define EXT4_INODE_MODE_DIRECTORY 0x4000
-#define EXT4_INODE_MODE_BLOCKDEV 0x6000
-#define EXT4_INODE_MODE_FILE 0x8000
-#define EXT4_INODE_MODE_SOFTLINK 0xA000
-#define EXT4_INODE_MODE_SOCKET 0xC000
-#define EXT4_INODE_MODE_TYPE_MASK 0xF000
-
-/*
- * Inode flags
- */
-#define EXT4_INODE_FLAG_SECRM 0x00000001     /* Secure deletion */
-#define EXT4_INODE_FLAG_UNRM 0x00000002      /* Undelete */
-#define EXT4_INODE_FLAG_COMPR 0x00000004     /* Compress file */
-#define EXT4_INODE_FLAG_SYNC 0x00000008      /* Synchronous updates */
-#define EXT4_INODE_FLAG_IMMUTABLE 0x00000010 /* Immutable file */
-#define EXT4_INODE_FLAG_APPEND 0x00000020  /* writes to file may only append */
-#define EXT4_INODE_FLAG_NODUMP 0x00000040  /* do not dump file */
-#define EXT4_INODE_FLAG_NOATIME 0x00000080 /* do not update atime */
-
-/* Compression flags */
-#define EXT4_INODE_FLAG_DIRTY 0x00000100
-#define EXT4_INODE_FLAG_COMPRBLK                                               \
-       0x00000200                         /* One or more compressed clusters */
-#define EXT4_INODE_FLAG_NOCOMPR 0x00000400 /* Don't compress */
-#define EXT4_INODE_FLAG_ECOMPR 0x00000800  /* Compression error */
-
-#define EXT4_INODE_FLAG_INDEX 0x00001000  /* hash-indexed directory */
-#define EXT4_INODE_FLAG_IMAGIC 0x00002000 /* AFS directory */
-#define EXT4_INODE_FLAG_JOURNAL_DATA                                           \
-       0x00004000                        /* File data should be journaled */
-#define EXT4_INODE_FLAG_NOTAIL 0x00008000 /* File tail should not be merged */
-#define EXT4_INODE_FLAG_DIRSYNC                                                \
-       0x00010000 /* Dirsync behaviour (directories only) */
-#define EXT4_INODE_FLAG_TOPDIR 0x00020000    /* Top of directory hierarchies */
-#define EXT4_INODE_FLAG_HUGE_FILE 0x00040000 /* Set to each huge file */
-#define EXT4_INODE_FLAG_EXTENTS 0x00080000   /* Inode uses extents */
-#define EXT4_INODE_FLAG_EA_INODE 0x00200000  /* Inode used for large EA */
-#define EXT4_INODE_FLAG_EOFBLOCKS 0x00400000 /* Blocks allocated beyond EOF */
-#define EXT4_INODE_FLAG_RESERVED 0x80000000  /* reserved for ext4 lib */
-
-#define EXT4_INODE_ROOT_INDEX 2
-
-struct ext4_inode_ref {
-       struct ext4_block block;
-       struct ext4_inode *inode;
-       struct ext4_fs *fs;
-       uint32_t index;
-       bool dirty;
-};
-
-#define EXT4_DIRECTORY_FILENAME_LEN 255
-
-/**@brief   Directory entry types. */
-enum { EXT4_DE_UNKNOWN = 0,
-       EXT4_DE_REG_FILE,
-       EXT4_DE_DIR,
-       EXT4_DE_CHRDEV,
-       EXT4_DE_BLKDEV,
-       EXT4_DE_FIFO,
-       EXT4_DE_SOCK,
-       EXT4_DE_SYMLINK };
-
-#define EXT4_DIRENTRY_DIR_CSUM 0xDE
-
-#pragma pack(push, 1)
-
-union ext4_dir_en_internal {
-       uint8_t name_length_high; /* Higher 8 bits of name length */
-       uint8_t inode_type;       /* Type of referenced inode (in rev >= 0.5) */
-};
-
-/**
- * Linked list directory entry structure
- */
-struct ext4_dir_en {
-       uint32_t inode; /* I-node for the entry */
-       uint16_t entry_len; /* Distance to the next directory entry */
-       uint8_t name_len;   /* Lower 8 bits of name length */
-
-       union ext4_dir_en_internal in;
-
-       uint8_t name[EXT4_DIRECTORY_FILENAME_LEN]; /* Entry name */
-};
-
-struct ext4_dir_iter {
-       struct ext4_inode_ref *inode_ref;
-       struct ext4_block curr_blk;
-       uint64_t curr_off;
-       struct ext4_dir_en *curr;
-};
-
-struct ext4_dir_search_result {
-       struct ext4_block block;
-       struct ext4_dir_en *dentry;
-};
-
-/* Structures for indexed directory */
-
-struct ext4_dir_idx_climit {
-       uint16_t limit;
-       uint16_t count;
-};
-
-struct ext4_dir_idx_dot_en {
-       uint32_t inode;
-       uint16_t entry_length;
-       uint8_t name_length;
-       uint8_t inode_type;
-       uint8_t name[4];
-};
-
-struct ext4_dir_idx_rinfo {
-       uint32_t reserved_zero;
-       uint8_t hash_version;
-       uint8_t info_length;
-       uint8_t indirect_levels;
-       uint8_t unused_flags;
-};
-
-struct ext4_dir_idx_entry {
-       uint32_t hash;
-       uint32_t block;
-};
-
-struct ext4_dir_idx_root {
-       struct ext4_dir_idx_dot_en dots[2];
-       struct ext4_dir_idx_rinfo info;
-       struct ext4_dir_idx_entry en[];
-};
-
-struct ext4_fake_dir_entry {
-       uint32_t inode;
-       uint16_t entry_length;
-       uint8_t name_length;
-       uint8_t inode_type;
-};
-
-struct ext4_dir_idx_node {
-       struct ext4_fake_dir_entry fake;
-       struct ext4_dir_idx_entry entries[];
-};
-
-struct ext4_dir_idx_block {
-       struct ext4_block b;
-       struct ext4_dir_idx_entry *entries;
-       struct ext4_dir_idx_entry *position;
-};
-
-/*
- * This goes at the end of each htree block.
- */
-struct ext4_dir_idx_tail {
-       uint32_t reserved;
-       uint32_t checksum;      /* crc32c(uuid+inum+dirblock) */
-};
-
-/*
- * This is a bogus directory entry at the end of each leaf block that
- * records checksums.
- */
-struct ext4_dir_entry_tail {
-       uint32_t reserved_zero1;        /* Pretend to be unused */
-       uint16_t rec_len;               /* 12 */
-       uint8_t reserved_zero2; /* Zero name length */
-       uint8_t reserved_ft;    /* 0xDE, fake file type */
-       uint32_t checksum;              /* crc32c(uuid+inum+dirblock) */
-};
-
-#pragma pack(pop)
-
-#define EXT4_DIRENT_TAIL(block, blocksize) \
-       ((struct ext4_dir_entry_tail *)(((char *)(block)) + ((blocksize) - \
-                                       sizeof(struct ext4_dir_entry_tail))))
-
-#define EXT4_ERR_BAD_DX_DIR (-25000)
-
-#define EXT4_LINK_MAX 65000
-
-#define EXT4_BAD_INO 1
-#define EXT4_ROOT_INO 2
-#define EXT4_BOOT_LOADER_INO 5
-#define EXT4_UNDEL_DIR_INO 6
-#define EXT4_RESIZE_INO 7
-#define EXT4_JOURNAL_INO 8
-
-#define EXT4_GOOD_OLD_FIRST_INO 11
-
-#define EXT4_EXT_UNWRITTEN_MASK (1L << 15)
-
-#define EXT4_EXT_MAX_LEN_WRITTEN (1L << 15)
-#define EXT4_EXT_MAX_LEN_UNWRITTEN \
-       (EXT4_EXT_MAX_LEN_WRITTEN - 1)
-
-#define EXT4_EXT_GET_LEN(ex) to_le16((ex)->block_count)
-#define EXT4_EXT_GET_LEN_UNWRITTEN(ex) \
-       (EXT4_EXT_GET_LEN(ex) &= ~(EXT4_EXT_UNWRITTEN_MASK))
-#define EXT4_EXT_SET_LEN(ex, count) \
-       ((ex)->block_count = to_le16(count))
-
-#define EXT4_EXT_IS_UNWRITTEN(ex) \
-       (EXT4_EXT_GET_LEN(ex) > EXT4_EXT_MAX_LEN_WRITTEN)
-#define EXT4_EXT_SET_UNWRITTEN(ex) \
-       ((ex)->block_count |= to_le16(EXT4_EXT_UNWRITTEN_MASK))
-#define EXT4_EXT_SET_WRITTEN(ex) \
-       ((ex)->block_count &= ~(to_le16(EXT4_EXT_UNWRITTEN_MASK)))
-
-#pragma pack(push, 1)
-
-/*
- * This is the extent tail on-disk structure.
- * All other extent structures are 12 bytes long.  It turns out that
- * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
- * covers all valid ext4 block sizes.  Therefore, this tail structure can be
- * crammed into the end of the block without having to rebalance the tree.
- */
-struct ext4_extent_tail
-{
-       uint32_t et_checksum; /* crc32c(uuid+inum+extent_block) */
-};
-
-/*
- * This is the extent on-disk structure.
- * It's used at the bottom of the tree.
- */
-struct ext4_extent {
-       uint32_t first_block; /* First logical block extent covers */
-       uint16_t block_count; /* Number of blocks covered by extent */
-       uint16_t start_hi;    /* High 16 bits of physical block */
-       uint32_t start_lo;    /* Low 32 bits of physical block */
-};
-
-/*
- * This is index on-disk structure.
- * It's used at all the levels except the bottom.
- */
-struct ext4_extent_index {
-       uint32_t first_block; /* Index covers logical blocks from 'block' */
-
-       /**
-        * Pointer to the physical block of the next
-        * level. leaf or next index could be there
-        * high 16 bits of physical block
-        */
-       uint32_t leaf_lo;
-       uint16_t leaf_hi;
-       uint16_t padding;
-};
-
-/*
- * Each block (leaves and indexes), even inode-stored has header.
- */
-struct ext4_extent_header {
-       uint16_t magic;
-       uint16_t entries_count;     /* Number of valid entries */
-       uint16_t max_entries_count; /* Capacity of store in entries */
-       uint16_t depth;             /* Has tree real underlying blocks? */
-       uint32_t generation;    /* generation of the tree */
-};
-
-#pragma pack(pop)
-
-
-/*
- * Types of blocks.
- */
-typedef uint32_t ext4_lblk_t;
-typedef uint64_t ext4_fsblk_t;
-
-/*
- * Array of ext4_ext_path contains path to some extent.
- * Creation/lookup routines use it for traversal/splitting/etc.
- * Truncate uses it to simulate recursive walking.
- */
-struct ext4_extent_path {
-       ext4_fsblk_t p_block;
-       struct ext4_block block;
-       int32_t depth;
-       int32_t maxdepth;
-       struct ext4_extent_header *header;
-       struct ext4_extent_index *index;
-       struct ext4_extent *extent;
-
-};
-
-
-#define EXT4_EXTENT_MAGIC 0xF30A
-
-#define EXT4_EXTENT_FIRST(header)                                              \
-       ((struct ext4_extent *)(((char *)(header)) +                           \
-                               sizeof(struct ext4_extent_header)))
-
-#define EXT4_EXTENT_FIRST_INDEX(header)                                        \
-       ((struct ext4_extent_index *)(((char *)(header)) +                     \
-                                     sizeof(struct ext4_extent_header)))
-
-/*
- * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an
- * initialized extent. This is 2^15 and not (2^16 - 1), since we use the
- * MSB of ee_len field in the extent datastructure to signify if this
- * particular extent is an initialized extent or an uninitialized (i.e.
- * preallocated).
- * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an
- * uninitialized extent.
- * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an
- * uninitialized one. In other words, if MSB of ee_len is set, it is an
- * uninitialized extent with only one special scenario when ee_len = 0x8000.
- * In this case we can not have an uninitialized extent of zero length and
- * thus we make it as a special case of initialized extent with 0x8000 length.
- * This way we get better extent-to-group alignment for initialized extents.
- * Hence, the maximum number of blocks we can have in an *initialized*
- * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767).
- */
-#define EXT_INIT_MAX_LEN (1L << 15)
-#define EXT_UNWRITTEN_MAX_LEN (EXT_INIT_MAX_LEN - 1)
-
-#define EXT_EXTENT_SIZE sizeof(struct ext4_extent)
-#define EXT_INDEX_SIZE sizeof(struct ext4_extent_idx)
-
-#define EXT_FIRST_EXTENT(__hdr__)                                              \
-       ((struct ext4_extent *)(((char *)(__hdr__)) +                          \
-                               sizeof(struct ext4_extent_header)))
-#define EXT_FIRST_INDEX(__hdr__)                                               \
-       ((struct ext4_extent_index *)(((char *)(__hdr__)) +                    \
-                                   sizeof(struct ext4_extent_header)))
-#define EXT_HAS_FREE_INDEX(__path__)                                           \
-       ((__path__)->header->entries_count < (__path__)->header->max_entries_count)
-#define EXT_LAST_EXTENT(__hdr__)                                               \
-       (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->entries_count - 1)
-#define EXT_LAST_INDEX(__hdr__)                                                \
-       (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->entries_count - 1)
-#define EXT_MAX_EXTENT(__hdr__)                                                \
-       (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->max_entries_count - 1)
-#define EXT_MAX_INDEX(__hdr__)                                                 \
-       (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->max_entries_count - 1)
-
-#define EXT4_EXTENT_TAIL_OFFSET(hdr)                                           \
-       (sizeof(struct ext4_extent_header) +                                   \
-        (sizeof(struct ext4_extent) * (hdr)->max_entries_count))
-
-/*
- * ext4_ext_next_allocated_block:
- * returns allocated block in subsequent extent or EXT_MAX_BLOCKS.
- * NOTE: it considers block number from index entry as
- * allocated block. Thus, index entries have to be consistent
- * with leaves.
- */
-#define EXT_MAX_BLOCKS (ext4_lblk_t) (-1)
-
-#define IN_RANGE(b, first, len)        ((b) >= (first) && (b) <= (first) + (len) - 1)
-
-
-/******************************************************************************/
-
-/* EXT3 HTree directory indexing */
-#define EXT2_HTREE_LEGACY 0
-#define EXT2_HTREE_HALF_MD4 1
-#define EXT2_HTREE_TEA 2
-#define EXT2_HTREE_LEGACY_UNSIGNED 3
-#define EXT2_HTREE_HALF_MD4_UNSIGNED 4
-#define EXT2_HTREE_TEA_UNSIGNED 5
-
-#define EXT2_HTREE_EOF 0x7FFFFFFFUL
-
-struct ext4_hash_info {
-       uint32_t hash;
-       uint32_t minor_hash;
-       uint32_t hash_version;
-       const uint32_t *seed;
-};
-
-/* Extended Attribute(EA) */
-
-/* Magic value in attribute blocks */
-#define EXT4_XATTR_MAGIC               0xEA020000
-
-/* Maximum number of references to one attribute block */
-#define EXT4_XATTR_REFCOUNT_MAX                1024
-
-/* Name indexes */
-#define EXT4_XATTR_INDEX_USER                  1
-#define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS      2
-#define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT     3
-#define EXT4_XATTR_INDEX_TRUSTED               4
-#define        EXT4_XATTR_INDEX_LUSTRE                 5
-#define EXT4_XATTR_INDEX_SECURITY              6
-#define EXT4_XATTR_INDEX_SYSTEM                        7
-#define EXT4_XATTR_INDEX_RICHACL               8
-#define EXT4_XATTR_INDEX_ENCRYPTION            9
-
-#pragma pack(push, 1)
-
-struct ext4_xattr_header {
-       uint32_t h_magic;       /* magic number for identification */
-       uint32_t h_refcount;    /* reference count */
-       uint32_t h_blocks;      /* number of disk blocks used */
-       uint32_t h_hash;                /* hash value of all attributes */
-       uint32_t h_checksum;    /* crc32c(uuid+id+xattrblock) */
-                               /* id = inum if refcount=1, blknum otherwise */
-       uint32_t h_reserved[3]; /* zero right now */
-};
-
-struct ext4_xattr_ibody_header {
-       uint32_t h_magic;       /* magic number for identification */
-};
-
-struct ext4_xattr_entry {
-       uint8_t e_name_len;     /* length of name */
-       uint8_t e_name_index;   /* attribute name index */
-       uint16_t e_value_offs;  /* offset in disk block of value */
-       uint32_t e_value_block; /* disk block attribute is stored on (n/i) */
-       uint32_t e_value_size;  /* size of attribute value */
-       uint32_t e_hash;                /* hash value of name and value */
-};
-
-#pragma pack(pop)
-
-struct ext4_xattr_item {
-       /* This attribute should be stored in inode body */
-       bool in_inode;
-
-       uint8_t name_index;
-       char  *name;
-       size_t name_len;
-       void  *data;
-       size_t data_size;
-
-       RB_ENTRY(ext4_xattr_item) node;
-};
-
-struct ext4_xattr_ref {
-       bool block_loaded;
-       struct ext4_block block;
-       struct ext4_inode_ref *inode_ref;
-       bool   dirty;
-       size_t ea_size;
-       struct ext4_fs *fs;
-
-       void *iter_arg;
-       struct ext4_xattr_item *iter_from;
-
-       RB_HEAD(ext4_xattr_tree,
-               ext4_xattr_item) root;
-};
-
-#define EXT4_XATTR_ITERATE_CONT 0
-#define EXT4_XATTR_ITERATE_STOP 1
-#define EXT4_XATTR_ITERATE_PAUSE 2
-
-#define EXT4_GOOD_OLD_INODE_SIZE       128
-
-#define EXT4_XATTR_PAD_BITS            2
-#define EXT4_XATTR_PAD         (1<<EXT4_XATTR_PAD_BITS)
-#define EXT4_XATTR_ROUND               (EXT4_XATTR_PAD-1)
-#define EXT4_XATTR_LEN(name_len) \
-       (((name_len) + EXT4_XATTR_ROUND + \
-       sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
-#define EXT4_XATTR_NEXT(entry) \
-       ((struct ext4_xattr_entry *)( \
-        (char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)))
-#define EXT4_XATTR_SIZE(size) \
-       (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
-#define EXT4_XATTR_NAME(entry) \
-       ((char *)((entry) + 1))
-
-#define EXT4_XATTR_IHDR(raw_inode) \
-       ((struct ext4_xattr_ibody_header *) \
-               ((char *)raw_inode + \
-               EXT4_GOOD_OLD_INODE_SIZE + \
-               (raw_inode)->extra_isize))
-#define EXT4_XATTR_IFIRST(hdr) \
-       ((struct ext4_xattr_entry *)((hdr)+1))
-
-#define EXT4_XATTR_BHDR(block) \
-       ((struct ext4_xattr_header *)((block)->data))
-#define EXT4_XATTR_ENTRY(ptr) \
-       ((struct ext4_xattr_entry *)(ptr))
-#define EXT4_XATTR_BFIRST(block) \
-       EXT4_XATTR_ENTRY(EXT4_XATTR_BHDR(block)+1)
-#define EXT4_XATTR_IS_LAST_ENTRY(entry) \
-       (*(uint32_t *)(entry) == 0)
-
-#define EXT4_ZERO_XATTR_VALUE ((void *)-1)
-
-/*****************************************************************************/
-
-/*
- * JBD stores integers in big endian.
- */
-
-#define JBD_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
-
-/*
- * Descriptor block types:
- */
-
-#define JBD_DESCRIPTOR_BLOCK   1
-#define JBD_COMMIT_BLOCK       2
-#define JBD_SUPERBLOCK         3
-#define JBD_SUPERBLOCK_V2      4
-#define JBD_REVOKE_BLOCK       5
-
-#pragma pack(push, 1)
-
-/*
- * Standard header for all descriptor blocks:
- */
-struct jbd_bhdr {
-       uint32_t                magic;
-       uint32_t                blocktype;
-       uint32_t                sequence;
-};
-
-#pragma pack(pop)
-
-/*
- * Checksum types.
- */
-#define JBD_CRC32_CHKSUM   1
-#define JBD_MD5_CHKSUM     2
-#define JBD_SHA1_CHKSUM    3
-#define JBD_CRC32C_CHKSUM  4
-
-#define JBD_CRC32_CHKSUM_SIZE 4
-
-#define JBD_CHECKSUM_BYTES (32 / sizeof(uint32_t))
-
-#pragma pack(push, 1)
-
-/*
- * Commit block header for storing transactional checksums:
- *
- * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
- * fields are used to store a checksum of the descriptor and data blocks.
- *
- * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
- * field is used to store crc32c(uuid+commit_block).  Each journal metadata
- * block gets its own checksum, and data block checksums are stored in
- * journal_block_tag (in the descriptor).  The other h_chksum* fields are
- * not used.
- *
- * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses
- * journal_block_tag3_t to store a full 32-bit checksum.  Everything else
- * is the same as v2.
- *
- * Checksum v1, v2, and v3 are mutually exclusive features.
- */
-
-struct jbd_commit_header {
-       struct jbd_bhdr header;
-       uint8_t chksum_type;
-       uint8_t chksum_size;
-       uint8_t padding[2];
-       uint32_t                chksum[JBD_CHECKSUM_BYTES];
-       uint64_t                commit_sec;
-       uint32_t                commit_nsec;
-};
-
-/*
- * The block tag: used to describe a single buffer in the journal
- */
-struct jbd_block_tag3 {
-       uint32_t                blocknr;        /* The on-disk block number */
-       uint32_t                flags;  /* See below */
-       uint32_t                blocknr_high; /* most-significant high 32bits. */
-       uint32_t                checksum;       /* crc32c(uuid+seq+block) */
-};
-
-struct jbd_block_tag {
-       uint32_t                blocknr;        /* The on-disk block number */
-       uint16_t                checksum;       /* truncated crc32c(uuid+seq+block) */
-       uint16_t                flags;  /* See below */
-       uint32_t                blocknr_high; /* most-significant high 32bits. */
-};
-
-#pragma pack(pop)
-
-/* Definitions for the journal tag flags word: */
-#define JBD_FLAG_ESCAPE                1       /* on-disk block is escaped */
-#define JBD_FLAG_SAME_UUID     2       /* block has same uuid as previous */
-#define JBD_FLAG_DELETED       4       /* block deleted by this transaction */
-#define JBD_FLAG_LAST_TAG      8       /* last tag in this descriptor block */
-
-#pragma pack(push, 1)
-
-/* Tail of descriptor block, for checksumming */
-struct jbd_block_tail {
-       uint32_t        checksum;
-};
-
-/*
- * The revoke descriptor: used on disk to describe a series of blocks to
- * be revoked from the log
- */
-struct jbd_revoke_header {
-       struct jbd_bhdr  header;
-       uint32_t         count; /* Count of bytes used in the block */
-};
-
-/* Tail of revoke block, for checksumming */
-struct jbd_revoke_tail {
-       uint32_t                checksum;
-};
-
-#pragma pack(pop)
-
-#define JBD_USERS_MAX 48
-#define JBD_USERS_SIZE (UUID_SIZE * JBD_USERS_MAX)
-
-#pragma pack(push, 1)
-
-/*
- * The journal superblock.  All fields are in big-endian byte order.
- */
-struct jbd_sb {
-/* 0x0000 */
-       struct jbd_bhdr header;
-
-/* 0x000C */
-       /* Static information describing the journal */
-       uint32_t        blocksize;              /* journal device blocksize */
-       uint32_t        maxlen;         /* total blocks in journal file */
-       uint32_t        first;          /* first block of log information */
-
-/* 0x0018 */
-       /* Dynamic information describing the current state of the log */
-       uint32_t        sequence;               /* first commit ID expected in log */
-       uint32_t        start;          /* blocknr of start of log */
-
-/* 0x0020 */
-       /* Error value, as set by journal_abort(). */
-       int32_t         error_val;
-
-/* 0x0024 */
-       /* Remaining fields are only valid in a version-2 superblock */
-       uint32_t        feature_compat;         /* compatible feature set */
-       uint32_t        feature_incompat;       /* incompatible feature set */
-       uint32_t        feature_ro_compat;      /* readonly-compatible feature set */
-/* 0x0030 */
-       uint8_t         uuid[UUID_SIZE];                /* 128-bit uuid for journal */
-
-/* 0x0040 */
-       uint32_t        nr_users;               /* Nr of filesystems sharing log */
-
-       uint32_t        dynsuper;               /* Blocknr of dynamic superblock copy*/
-
-/* 0x0048 */
-       uint32_t        max_transaction;        /* Limit of journal blocks per trans.*/
-       uint32_t        max_trandata;   /* Limit of data blocks per trans. */
-
-/* 0x0050 */
-       uint8_t         checksum_type;  /* checksum type */
-       uint8_t         padding2[3];
-       uint32_t        padding[42];
-       uint32_t        checksum;               /* crc32c(superblock) */
-
-/* 0x0100 */
-       uint8_t         users[JBD_USERS_SIZE];          /* ids of all fs'es sharing the log */
-
-/* 0x0400 */
-};
-
-#pragma pack(pop)
-
-#define JBD_SUPERBLOCK_SIZE sizeof(struct jbd_sb)
-
-#define JBD_HAS_COMPAT_FEATURE(jsb,mask)                                       \
-       ((jsb)->header.blocktype >= to_be32(2) &&                               \
-        ((jsb)->feature_compat & to_be32((mask))))
-#define JBD_HAS_RO_COMPAT_FEATURE(jsb,mask)                            \
-       ((jsb)->header.blocktype >= to_be32(2) &&                               \
-        ((jsb)->feature_ro_compat & to_be32((mask))))
-#define JBD_HAS_INCOMPAT_FEATURE(jsb,mask)                             \
-       ((jsb)->header.blocktype >= to_be32(2) &&                               \
-        ((jsb)->feature_incompat & to_be32((mask))))
-
-#define JBD_FEATURE_COMPAT_CHECKSUM    0x00000001
-
-#define JBD_FEATURE_INCOMPAT_REVOKE            0x00000001
-#define JBD_FEATURE_INCOMPAT_64BIT             0x00000002
-#define JBD_FEATURE_INCOMPAT_ASYNC_COMMIT      0x00000004
-#define JBD_FEATURE_INCOMPAT_CSUM_V2           0x00000008
-#define JBD_FEATURE_INCOMPAT_CSUM_V3           0x00000010
-
-/* Features known to this kernel version: */
-#define JBD_KNOWN_COMPAT_FEATURES      0
-#define JBD_KNOWN_ROCOMPAT_FEATURES    0
-#define JBD_KNOWN_INCOMPAT_FEATURES    (JBD_FEATURE_INCOMPAT_REVOKE|\
-                                        JBD_FEATURE_INCOMPAT_ASYNC_COMMIT|\
-                                        JBD_FEATURE_INCOMPAT_64BIT|\
-                                        JBD_FEATURE_INCOMPAT_CSUM_V2|\
-                                        JBD_FEATURE_INCOMPAT_CSUM_V3)
-
-struct jbd_fs {
-       /* If journal block device is used, bdev will be non-null */
-       struct ext4_blockdev *bdev;
-       struct ext4_inode_ref inode_ref;
-       struct jbd_sb sb;
-
-       bool dirty;
-};
-
-struct jbd_buf {
-       uint64_t jbd_lba;
-       struct ext4_block block;
-       struct jbd_trans *trans;
-       struct jbd_block_rec *block_rec;
-       TAILQ_ENTRY(jbd_buf) buf_node;
-       TAILQ_ENTRY(jbd_buf) dirty_buf_node;
-};
-
-struct jbd_revoke_rec {
-       ext4_fsblk_t lba;
-       LIST_ENTRY(jbd_revoke_rec) revoke_node;
-};
-
-struct jbd_block_rec {
-       ext4_fsblk_t lba;
-       struct ext4_buf *buf;
-       struct jbd_trans *trans;
-       RB_ENTRY(jbd_block_rec) block_rec_node;
-       LIST_ENTRY(jbd_block_rec) tbrec_node;
-       TAILQ_HEAD(jbd_buf_dirty, jbd_buf) dirty_buf_queue;
-};
-
-struct jbd_trans {
-       uint32_t trans_id;
-
-       uint32_t start_iblock;
-       int alloc_blocks;
-       int data_cnt;
-       uint32_t data_csum;
-       int written_cnt;
-       int error;
-
-       struct jbd_journal *journal;
-
-       TAILQ_HEAD(jbd_trans_buf, jbd_buf) buf_queue;
-       LIST_HEAD(jbd_revoke_list, jbd_revoke_rec) revoke_list;
-       LIST_HEAD(jbd_trans_block_rec, jbd_block_rec) tbrec_list;
-       TAILQ_ENTRY(jbd_trans) trans_node;
-};
-
-struct jbd_journal {
-       uint32_t first;
-       uint32_t start;
-       uint32_t last;
-       uint32_t trans_id;
-       uint32_t alloc_trans_id;
-
-       uint32_t block_size;
-
-       TAILQ_HEAD(jbd_trans_queue, jbd_trans) trans_queue;
-       TAILQ_HEAD(jbd_cp_queue, jbd_trans) cp_queue;
-       RB_HEAD(jbd_block, jbd_block_rec) block_rec_root;
-
-       struct jbd_fs *jbd_fs;
-};
-
-/*****************************************************************************/
-
-#define EXT4_CRC32_INIT (0xFFFFFFFFUL)
-
-/*****************************************************************************/
-
-static inline uint64_t reorder64(uint64_t n)
-{
-       return  ((n & 0xff) << 56) |
-               ((n & 0xff00) << 40) |
-               ((n & 0xff0000) << 24) |
-               ((n & 0xff000000LL) << 8) |
-               ((n & 0xff00000000LL) >> 8) |
-               ((n & 0xff0000000000LL) >> 24) |
-               ((n & 0xff000000000000LL) >> 40) |
-               ((n & 0xff00000000000000LL) >> 56);
-}
-
-static inline uint32_t reorder32(uint32_t n)
-{
-       return  ((n & 0xff) << 24) |
-               ((n & 0xff00) << 8) |
-               ((n & 0xff0000) >> 8) |
-               ((n & 0xff000000) >> 24);
-}
-
-static inline uint16_t reorder16(uint16_t n)
-{
-       return  ((n & 0xff) << 8) |
-               ((n & 0xff00) >> 8);
-}
-
-#ifdef CONFIG_BIG_ENDIAN
-#define to_le64(_n) reorder64(_n)
-#define to_le32(_n) reorder32(_n)
-#define to_le16(_n) reorder16(_n)
-
-#define to_be64(_n) _n
-#define to_be32(_n) _n
-#define to_be16(_n) _n
-
-#else
-#define to_le64(_n) _n
-#define to_le32(_n) _n
-#define to_le16(_n) _n
-
-#define to_be64(_n) reorder64(_n)
-#define to_be32(_n) reorder32(_n)
-#define to_be16(_n) reorder16(_n)
-#endif
-
-/****************************Access macros to ext4 structures*****************/
-
-#define ext4_get32(s, f) to_le32((s)->f)
-#define ext4_get16(s, f) to_le16((s)->f)
-#define ext4_get8(s, f) (s)->f
-
-#define ext4_set32(s, f, v)                                                    \
-       do {                                                                   \
-               (s)->f = to_le32(v);                                           \
-       } while (0)
-#define ext4_set16(s, f, v)                                                    \
-       do {                                                                   \
-               (s)->f = to_le16(v);                                           \
-       } while (0)
-#define ext4_set8                                                              \
-       (s, f, v) do { (s)->f = (v); }                                         \
-       while (0)
-
-/****************************Access macros to jbd2 structures*****************/
-
-#define jbd_get32(s, f) to_be32((s)->f)
-#define jbd_get16(s, f) to_be16((s)->f)
-#define jbd_get8(s, f) (s)->f
-
-#define jbd_set32(s, f, v)                                                    \
-       do {                                                                   \
-               (s)->f = to_be32(v);                                           \
-       } while (0)
-#define jbd_set16(s, f, v)                                                    \
-       do {                                                                   \
-               (s)->f = to_be16(v);                                           \
-       } while (0)
-#define jbd_set8                                                              \
-       (s, f, v) do { (s)->f = (v); }                                         \
-       while (0)
-
-#ifdef __GNUC__
- #ifndef __unused
- #define __unused __attribute__ ((__unused__))
- #endif
-#else
- #define __unused
-#endif
-
-#ifndef offsetof
-#define offsetof(type, field)          \
-       ((size_t)(&(((type *)0)->field)))
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* EXT4_TYPES_H_ */
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_xattr.c b/lwext4/ext4_xattr.c
deleted file mode 100644 (file)
index 54d457d..0000000
+++ /dev/null
@@ -1,942 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
- *
- * 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_xattr.c
- * @brief Extended Attribute manipulation.
- */
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-#include "ext4_fs.h"
-#include "ext4_errno.h"
-#include "ext4_blockdev.h"
-#include "ext4_super.h"
-#include "ext4_crc32.h"
-#include "ext4_debug.h"
-#include "ext4_block_group.h"
-#include "ext4_balloc.h"
-#include "ext4_inode.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-/**
- * @file  ext4_xattr.c
- * @brief Extended Attribute Manipulation
- */
-
-#define NAME_HASH_SHIFT 5
-#define VALUE_HASH_SHIFT 16
-
-static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header,
-                                          struct ext4_xattr_entry *entry)
-{
-       uint32_t hash = 0;
-       char *name = EXT4_XATTR_NAME(entry);
-       int n;
-
-       for (n = 0; n < entry->e_name_len; n++) {
-               hash = (hash << NAME_HASH_SHIFT) ^
-                      (hash >> (8 * sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++;
-       }
-
-       if (entry->e_value_block == 0 && entry->e_value_size != 0) {
-               uint32_t *value =
-                   (uint32_t *)((char *)header + to_le16(entry->e_value_offs));
-               for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >>
-                        EXT4_XATTR_PAD_BITS;
-                    n; n--) {
-                       hash = (hash << VALUE_HASH_SHIFT) ^
-                              (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^
-                              to_le32(*value++);
-               }
-       }
-       entry->e_hash = to_le32(hash);
-}
-
-#define BLOCK_HASH_SHIFT 16
-
-/*
- * ext4_xattr_rehash()
- *
- * Re-compute the extended attribute hash value after an entry has changed.
- */
-static void ext4_xattr_rehash(struct ext4_xattr_header *header,
-                             struct ext4_xattr_entry *entry)
-{
-       struct ext4_xattr_entry *here;
-       uint32_t hash = 0;
-
-       ext4_xattr_compute_hash(header, entry);
-       here = EXT4_XATTR_ENTRY(header + 1);
-       while (!EXT4_XATTR_IS_LAST_ENTRY(here)) {
-               if (!here->e_hash) {
-                       /* Block is not shared if an entry's hash value == 0 */
-                       hash = 0;
-                       break;
-               }
-               hash = (hash << BLOCK_HASH_SHIFT) ^
-                      (hash >> (8 * sizeof(hash) - BLOCK_HASH_SHIFT)) ^
-                      to_le32(here->e_hash);
-               here = EXT4_XATTR_NEXT(here);
-       }
-       header->h_hash = to_le32(hash);
-}
-
-#if CONFIG_META_CSUM_ENABLE
-static uint32_t
-ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref,
-                         ext4_fsblk_t blocknr,
-                         struct ext4_xattr_header *header)
-{
-       uint32_t checksum = 0;
-       uint64_t le64_blocknr = blocknr;
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-
-       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
-               uint32_t orig_checksum;
-
-               /* Preparation: temporarily set bg checksum to 0 */
-               orig_checksum = header->h_checksum;
-               header->h_checksum = 0;
-               /* First calculate crc32 checksum against fs uuid */
-               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
-                               sizeof(sb->uuid));
-               /* Then calculate crc32 checksum block number */
-               checksum = ext4_crc32c(checksum, &le64_blocknr,
-                                    sizeof(le64_blocknr));
-               /* Finally calculate crc32 checksum against 
-                * the entire xattr block */
-               checksum = ext4_crc32c(checksum, header,
-                                  ext4_sb_get_block_size(sb));
-               header->h_checksum = orig_checksum;
-       }
-       return checksum;
-}
-#else
-#define ext4_xattr_block_checksum(...) 0
-#endif
-
-static void
-ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref,
-                             ext4_fsblk_t blocknr __unused,
-                             struct ext4_xattr_header *header)
-{
-       struct ext4_sblock *sb = &inode_ref->fs->sb;
-       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
-               return;
-
-       header->h_checksum =
-               ext4_xattr_block_checksum(inode_ref, blocknr, header);
-}
-
-static int ext4_xattr_item_cmp(struct ext4_xattr_item *a,
-                              struct ext4_xattr_item *b)
-{
-       int result;
-       if (a->in_inode && !b->in_inode)
-               return -1;
-       
-       if (!a->in_inode && b->in_inode)
-               return 1;
-
-       result = a->name_index - b->name_index;
-       if (result)
-               return result;
-
-       result = a->name_len - b->name_len;
-       if (result)
-               return result;
-
-       return memcmp(a->name, b->name, a->name_len);
-}
-
-RB_GENERATE_INTERNAL(ext4_xattr_tree, ext4_xattr_item, node,
-                    ext4_xattr_item_cmp, static inline)
-
-static struct ext4_xattr_item *
-ext4_xattr_item_alloc(uint8_t name_index, const char *name, size_t name_len)
-{
-       struct ext4_xattr_item *item;
-       item = malloc(sizeof(struct ext4_xattr_item) + name_len);
-       if (!item)
-               return NULL;
-
-       item->name_index = name_index;
-       item->name = (char *)(item + 1);
-       item->name_len = name_len;
-       item->data = NULL;
-       item->data_size = 0;
-
-       memset(&item->node, 0, sizeof(item->node));
-       memcpy(item->name, name, name_len);
-
-       if (name_index == EXT4_XATTR_INDEX_SYSTEM &&
-           name_len == 4 &&
-           !memcmp(name, "data", 4))
-               item->in_inode = true;
-       else
-               item->in_inode = false;
-
-       return item;
-}
-
-static int ext4_xattr_item_alloc_data(struct ext4_xattr_item *item,
-                                     const void *orig_data, size_t data_size)
-{
-       void *data = NULL;
-       ext4_assert(!item->data);
-       data = malloc(data_size);
-       if (!data)
-               return ENOMEM;
-
-       if (orig_data)
-               memcpy(data, orig_data, data_size);
-
-       item->data = data;
-       item->data_size = data_size;
-       return EOK;
-}
-
-static void ext4_xattr_item_free_data(struct ext4_xattr_item *item)
-{
-       ext4_assert(item->data);
-       free(item->data);
-       item->data = NULL;
-       item->data_size = 0;
-}
-
-static int ext4_xattr_item_resize_data(struct ext4_xattr_item *item,
-                                      size_t new_data_size)
-{
-       if (new_data_size != item->data_size) {
-               void *new_data;
-               new_data = realloc(item->data, new_data_size);
-               if (!new_data)
-                       return ENOMEM;
-
-               item->data = new_data;
-               item->data_size = new_data_size;
-       }
-       return EOK;
-}
-
-static void ext4_xattr_item_free(struct ext4_xattr_item *item)
-{
-       if (item->data)
-               ext4_xattr_item_free_data(item);
-
-       free(item);
-}
-
-static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref,
-                                  struct ext4_xattr_entry *entry,
-                                  bool in_inode)
-{
-       char *ret;
-       if (in_inode) {
-               struct ext4_xattr_ibody_header *header;
-               struct ext4_xattr_entry *first_entry;
-               int16_t inode_size =
-                   ext4_get16(&xattr_ref->fs->sb, inode_size);
-               header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
-               first_entry = EXT4_XATTR_IFIRST(header);
-
-               ret = ((char *)first_entry + to_le16(entry->e_value_offs));
-               if (ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size)) -
-                       (char *)xattr_ref->inode_ref->inode > inode_size)
-                       ret = NULL;
-
-               return ret;
-
-       }
-       int32_t block_size = ext4_sb_get_block_size(&xattr_ref->fs->sb);
-       ret = ((char *)xattr_ref->block.data + to_le16(entry->e_value_offs));
-       if (ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size)) -
-                       (char *)xattr_ref->block.data > block_size)
-               ret = NULL;
-       return ret;
-}
-
-static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref)
-{
-       int ret = EOK;
-       size_t size_rem;
-       void *data;
-       struct ext4_xattr_entry *entry = NULL;
-
-       ext4_assert(xattr_ref->block.data);
-       entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
-
-       size_rem = ext4_sb_get_block_size(&xattr_ref->fs->sb);
-       for (; size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
-            entry = EXT4_XATTR_NEXT(entry),
-            size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
-               struct ext4_xattr_item *item;
-               char *e_name = EXT4_XATTR_NAME(entry);
-
-               data = ext4_xattr_entry_data(xattr_ref, entry, false);
-               if (!data) {
-                       ret = EIO;
-                       goto Finish;
-               }
-
-               item = ext4_xattr_item_alloc(entry->e_name_index, e_name,
-                                            (size_t)entry->e_name_len);
-               if (!item) {
-                       ret = ENOMEM;
-                       goto Finish;
-               }
-               if (ext4_xattr_item_alloc_data(
-                       item, data, to_le32(entry->e_value_size)) != EOK) {
-                       ext4_xattr_item_free(item);
-                       ret = ENOMEM;
-                       goto Finish;
-               }
-               RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
-               xattr_ref->ea_size += EXT4_XATTR_SIZE(item->data_size) +
-                                     EXT4_XATTR_LEN(item->name_len);
-       }
-
-Finish:
-       return ret;
-}
-
-static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref)
-{
-       void *data;
-       size_t size_rem;
-       int ret = EOK;
-       struct ext4_xattr_ibody_header *header = NULL;
-       struct ext4_xattr_entry *entry = NULL;
-       uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
-
-       header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
-       entry = EXT4_XATTR_IFIRST(header);
-
-       size_rem = inode_size - EXT4_GOOD_OLD_INODE_SIZE -
-                  xattr_ref->inode_ref->inode->extra_isize;
-       for (; size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
-            entry = EXT4_XATTR_NEXT(entry),
-            size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
-               struct ext4_xattr_item *item;
-               char *e_name = EXT4_XATTR_NAME(entry);
-
-               data = ext4_xattr_entry_data(xattr_ref, entry, true);
-               if (!data) {
-                       ret = EIO;
-                       goto Finish;
-               }
-
-               item = ext4_xattr_item_alloc(entry->e_name_index, e_name,
-                                            (size_t)entry->e_name_len);
-               if (!item) {
-                       ret = ENOMEM;
-                       goto Finish;
-               }
-               if (ext4_xattr_item_alloc_data(
-                       item, data, to_le32(entry->e_value_size)) != EOK) {
-                       ext4_xattr_item_free(item);
-                       ret = ENOMEM;
-                       goto Finish;
-               }
-               RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
-               xattr_ref->ea_size += EXT4_XATTR_SIZE(item->data_size) +
-                                     EXT4_XATTR_LEN(item->name_len);
-       }
-
-Finish:
-       return ret;
-}
-
-static size_t ext4_xattr_inode_space(struct ext4_xattr_ref *xattr_ref)
-{
-       uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
-       uint16_t size_rem = inode_size - EXT4_GOOD_OLD_INODE_SIZE -
-                           xattr_ref->inode_ref->inode->extra_isize;
-       return size_rem;
-}
-
-static size_t ext4_xattr_block_space(struct ext4_xattr_ref *xattr_ref)
-{
-       return ext4_sb_get_block_size(&xattr_ref->fs->sb);
-}
-
-static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref)
-{
-       int ret = EOK;
-       uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
-       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
-               ret = ext4_xattr_inode_fetch(xattr_ref);
-               if (ret != EOK)
-                       return ret;
-       }
-
-       if (xattr_ref->block_loaded)
-               ret = ext4_xattr_block_fetch(xattr_ref);
-
-       xattr_ref->dirty = false;
-       return ret;
-}
-
-static struct ext4_xattr_item *
-ext4_xattr_lookup_item(struct ext4_xattr_ref *xattr_ref, uint8_t name_index,
-                      const char *name, size_t name_len)
-{
-       struct ext4_xattr_item tmp = {
-               .name_index = name_index,
-               .name = (char *)name, /*RB_FIND - won't touch this string*/
-               .name_len = name_len,
-       };
-       if (name_index == EXT4_XATTR_INDEX_SYSTEM &&
-           name_len == 4 &&
-           !memcmp(name, "data", 4))
-               tmp.in_inode = true;
-
-       return RB_FIND(ext4_xattr_tree, &xattr_ref->root, &tmp);
-}
-
-static struct ext4_xattr_item *
-ext4_xattr_insert_item(struct ext4_xattr_ref *xattr_ref, uint8_t name_index,
-                      const char *name, size_t name_len, const void *data,
-                      size_t data_size)
-{
-       struct ext4_xattr_item *item;
-       item = ext4_xattr_item_alloc(name_index, name, name_len);
-       if (!item)
-               return NULL;
-
-       if ((xattr_ref->ea_size + EXT4_XATTR_SIZE(data_size) +
-               EXT4_XATTR_LEN(item->name_len)
-                       >
-           ext4_xattr_inode_space(xattr_ref) -
-               sizeof(struct ext4_xattr_ibody_header))
-               &&
-           (xattr_ref->ea_size + EXT4_XATTR_SIZE(data_size) +
-               EXT4_XATTR_LEN(item->name_len) >
-           ext4_xattr_block_space(xattr_ref) -
-               sizeof(struct ext4_xattr_header))) {
-               ext4_xattr_item_free(item);
-
-               return NULL;
-       }
-       if (ext4_xattr_item_alloc_data(item, data, data_size) != EOK) {
-               ext4_xattr_item_free(item);
-               return NULL;
-       }
-       RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
-       xattr_ref->ea_size +=
-           EXT4_XATTR_SIZE(item->data_size) + EXT4_XATTR_LEN(item->name_len);
-       xattr_ref->dirty = true;
-       return item;
-}
-
-static int ext4_xattr_remove_item(struct ext4_xattr_ref *xattr_ref,
-                                 uint8_t name_index, const char *name,
-                                 size_t name_len)
-{
-       int ret = ENOENT;
-       struct ext4_xattr_item *item =
-           ext4_xattr_lookup_item(xattr_ref, name_index, name, name_len);
-       if (item) {
-               if (item == xattr_ref->iter_from)
-                       xattr_ref->iter_from =
-                           RB_NEXT(ext4_xattr_tree, &xattr_ref->root, item);
-
-               xattr_ref->ea_size -= EXT4_XATTR_SIZE(item->data_size) +
-                                     EXT4_XATTR_LEN(item->name_len);
-
-               RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
-               ext4_xattr_item_free(item);
-               xattr_ref->dirty = true;
-               ret = EOK;
-       }
-       return ret;
-}
-
-static int ext4_xattr_resize_item(struct ext4_xattr_ref *xattr_ref,
-                                 struct ext4_xattr_item *item,
-                                 size_t new_data_size)
-{
-       int ret = EOK;
-       size_t old_data_size = item->data_size;
-       if ((xattr_ref->ea_size - EXT4_XATTR_SIZE(old_data_size) +
-               EXT4_XATTR_SIZE(new_data_size)
-                       >
-           ext4_xattr_inode_space(xattr_ref) -
-               sizeof(struct ext4_xattr_ibody_header))
-               &&
-           (xattr_ref->ea_size - EXT4_XATTR_SIZE(old_data_size) +
-               EXT4_XATTR_SIZE(new_data_size)
-                       >
-           ext4_xattr_block_space(xattr_ref) -
-               sizeof(struct ext4_xattr_header))) {
-
-               return ENOSPC;
-       }
-       ret = ext4_xattr_item_resize_data(item, new_data_size);
-       if (ret != EOK) {
-               return ret;
-       }
-       xattr_ref->ea_size =
-           xattr_ref->ea_size -
-           EXT4_XATTR_SIZE(old_data_size) +
-           EXT4_XATTR_SIZE(new_data_size);
-       xattr_ref->dirty = true;
-       return ret;
-}
-
-static void ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
-{
-       struct ext4_xattr_item *item, *save_item;
-       RB_FOREACH_SAFE(item, ext4_xattr_tree, &xattr_ref->root, save_item)
-       {
-               RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
-               ext4_xattr_item_free(item);
-       }
-       xattr_ref->ea_size = 0;
-}
-
-static int ext4_xattr_try_alloc_block(struct ext4_xattr_ref *xattr_ref)
-{
-       int ret = EOK;
-
-       ext4_fsblk_t xattr_block = 0;
-       xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
-                                             &xattr_ref->fs->sb);
-       if (!xattr_block) {
-               ext4_fsblk_t goal =
-                       ext4_fs_inode_to_goal_block(xattr_ref->inode_ref);
-
-               ret = ext4_balloc_alloc_block(xattr_ref->inode_ref,
-                                             goal,
-                                             &xattr_block);
-               if (ret != EOK)
-                       goto Finish;
-
-               ret = ext4_trans_block_get(xattr_ref->fs->bdev, &xattr_ref->block,
-                                    xattr_block);
-               if (ret != EOK) {
-                       ext4_balloc_free_block(xattr_ref->inode_ref,
-                                              xattr_block);
-                       goto Finish;
-               }
-
-               ext4_inode_set_file_acl(xattr_ref->inode_ref->inode,
-                                       &xattr_ref->fs->sb, xattr_block);
-               xattr_ref->inode_ref->dirty = true;
-               xattr_ref->block_loaded = true;
-       }
-
-Finish:
-       return ret;
-}
-
-static void ext4_xattr_try_free_block(struct ext4_xattr_ref *xattr_ref)
-{
-       ext4_fsblk_t xattr_block;
-       xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
-                                             &xattr_ref->fs->sb);
-       ext4_inode_set_file_acl(xattr_ref->inode_ref->inode, &xattr_ref->fs->sb,
-                               0);
-       ext4_block_set(xattr_ref->fs->bdev, &xattr_ref->block);
-       ext4_balloc_free_block(xattr_ref->inode_ref, xattr_block);
-       xattr_ref->inode_ref->dirty = true;
-       xattr_ref->block_loaded = false;
-}
-
-static void ext4_xattr_set_block_header(struct ext4_xattr_ref *xattr_ref)
-{
-       struct ext4_xattr_header *block_header = NULL;
-       block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
-
-       memset(block_header, 0, sizeof(struct ext4_xattr_header));
-       block_header->h_magic = EXT4_XATTR_MAGIC;
-       block_header->h_refcount = to_le32(1);
-       block_header->h_blocks = to_le32(1);
-}
-
-static void
-ext4_xattr_set_inode_entry(struct ext4_xattr_item *item,
-                          struct ext4_xattr_ibody_header *ibody_header,
-                          struct ext4_xattr_entry *entry, void *ibody_data_ptr)
-{
-       entry->e_name_len = (uint8_t)item->name_len;
-       entry->e_name_index = item->name_index;
-       entry->e_value_offs =
-           (char *)ibody_data_ptr - (char *)EXT4_XATTR_IFIRST(ibody_header);
-       entry->e_value_block = 0;
-       entry->e_value_size = item->data_size;
-}
-
-static void ext4_xattr_set_block_entry(struct ext4_xattr_item *item,
-                                      struct ext4_xattr_header *block_header,
-                                      struct ext4_xattr_entry *block_entry,
-                                      void *block_data_ptr)
-{
-       block_entry->e_name_len = (uint8_t)item->name_len;
-       block_entry->e_name_index = item->name_index;
-       block_entry->e_value_offs =
-           (char *)block_data_ptr - (char *)block_header;
-       block_entry->e_value_block = 0;
-       block_entry->e_value_size = item->data_size;
-}
-
-static int ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
-{
-       int ret = EOK;
-       bool block_modified = false;
-       void *ibody_data = NULL;
-       void *block_data = NULL;
-       struct ext4_xattr_item *item, *save_item;
-       size_t inode_size_rem, block_size_rem;
-       struct ext4_xattr_ibody_header *ibody_header = NULL;
-       struct ext4_xattr_header *block_header = NULL;
-       struct ext4_xattr_entry *entry = NULL;
-       struct ext4_xattr_entry *block_entry = NULL;
-
-       inode_size_rem = ext4_xattr_inode_space(xattr_ref);
-       block_size_rem = ext4_xattr_block_space(xattr_ref);
-       if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
-               ibody_header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
-               entry = EXT4_XATTR_IFIRST(ibody_header);
-       }
-
-       if (!xattr_ref->dirty)
-               goto Finish;
-       /* If there are enough spaces in the ibody EA table.*/
-       if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
-               memset(ibody_header, 0, inode_size_rem);
-               ibody_header->h_magic = EXT4_XATTR_MAGIC;
-               ibody_data = (char *)ibody_header + inode_size_rem;
-               inode_size_rem -= sizeof(struct ext4_xattr_ibody_header);
-
-               xattr_ref->inode_ref->dirty = true;
-       }
-       /* If we need an extra block to hold the EA entries*/
-       if (xattr_ref->ea_size > inode_size_rem) {
-               if (!xattr_ref->block_loaded) {
-                       ret = ext4_xattr_try_alloc_block(xattr_ref);
-                       if (ret != EOK)
-                               goto Finish;
-               }
-               block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
-               block_entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
-               ext4_xattr_set_block_header(xattr_ref);
-               block_data = (char *)block_header + block_size_rem;
-               block_size_rem -= sizeof(struct ext4_xattr_header);
-
-               ext4_trans_set_block_dirty(xattr_ref->block.buf);
-       } else {
-               /* We don't need an extra block.*/
-               if (xattr_ref->block_loaded) {
-                       block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
-                       block_header->h_refcount =
-                           to_le32(to_le32(block_header->h_refcount) - 1);
-                       if (!block_header->h_refcount) {
-                               ext4_xattr_try_free_block(xattr_ref);
-                               block_header = NULL;
-                       } else {
-                               block_entry =
-                                   EXT4_XATTR_BFIRST(&xattr_ref->block);
-                               block_data =
-                                   (char *)block_header + block_size_rem;
-                               block_size_rem -=
-                                   sizeof(struct ext4_xattr_header);
-                               ext4_inode_set_file_acl(
-                                   xattr_ref->inode_ref->inode,
-                                   &xattr_ref->fs->sb, 0);
-
-                               xattr_ref->inode_ref->dirty = true;
-                               ext4_trans_set_block_dirty(xattr_ref->block.buf);
-                       }
-               }
-       }
-       RB_FOREACH_SAFE(item, ext4_xattr_tree, &xattr_ref->root, save_item)
-       {
-               if (EXT4_XATTR_SIZE(item->data_size) +
-                       EXT4_XATTR_LEN(item->name_len) <=
-                   inode_size_rem) {
-                       ibody_data = (char *)ibody_data -
-                                    EXT4_XATTR_SIZE(item->data_size);
-                       ext4_xattr_set_inode_entry(item, ibody_header, entry,
-                                                  ibody_data);
-                       memcpy(EXT4_XATTR_NAME(entry), item->name,
-                              item->name_len);
-                       memcpy(ibody_data, item->data, item->data_size);
-                       entry = EXT4_XATTR_NEXT(entry);
-                       inode_size_rem -= EXT4_XATTR_SIZE(item->data_size) +
-                                         EXT4_XATTR_LEN(item->name_len);
-
-                       xattr_ref->inode_ref->dirty = true;
-                       continue;
-               }
-               if (EXT4_XATTR_SIZE(item->data_size) +
-                       EXT4_XATTR_LEN(item->name_len) >
-                   block_size_rem) {
-                       ret = ENOSPC;
-                       goto Finish;
-               }
-               block_data =
-                   (char *)block_data - EXT4_XATTR_SIZE(item->data_size);
-               ext4_xattr_set_block_entry(item, block_header, block_entry,
-                                          block_data);
-               memcpy(EXT4_XATTR_NAME(block_entry), item->name,
-                      item->name_len);
-               memcpy(block_data, item->data, item->data_size);
-               block_entry = EXT4_XATTR_NEXT(block_entry);
-               block_size_rem -= EXT4_XATTR_SIZE(item->data_size) +
-                                 EXT4_XATTR_LEN(item->name_len);
-
-               block_modified = true;
-       }
-       xattr_ref->dirty = false;
-       if (block_modified) {
-               ext4_xattr_rehash(block_header,
-                                 EXT4_XATTR_BFIRST(&xattr_ref->block));
-               ext4_xattr_set_block_checksum(xattr_ref->inode_ref,
-                                             xattr_ref->block.lb_id,
-                                             block_header);
-               ext4_trans_set_block_dirty(xattr_ref->block.buf);
-       }
-
-Finish:
-       return ret;
-}
-
-void ext4_fs_xattr_iterate(struct ext4_xattr_ref *ref,
-                          int (*iter)(struct ext4_xattr_ref *ref,
-                                    struct ext4_xattr_item *item))
-{
-       struct ext4_xattr_item *item;
-       if (!ref->iter_from)
-               ref->iter_from = RB_MIN(ext4_xattr_tree, &ref->root);
-
-       RB_FOREACH_FROM(item, ext4_xattr_tree, ref->iter_from)
-       {
-               int ret = EXT4_XATTR_ITERATE_CONT;
-               if (iter)
-                       iter(ref, item);
-
-               if (ret != EXT4_XATTR_ITERATE_CONT) {
-                       if (ret == EXT4_XATTR_ITERATE_STOP)
-                               ref->iter_from = NULL;
-
-                       break;
-               }
-       }
-}
-
-void ext4_fs_xattr_iterate_reset(struct ext4_xattr_ref *ref)
-{
-       ref->iter_from = NULL;
-}
-
-int ext4_fs_set_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
-                     const char *name, size_t name_len, const void *data,
-                     size_t data_size, bool replace)
-{
-       int ret = EOK;
-       struct ext4_xattr_item *item =
-           ext4_xattr_lookup_item(ref, name_index, name, name_len);
-       if (replace) {
-               if (!item) {
-                       ret = ENODATA;
-                       goto Finish;
-               }
-               if (item->data_size != data_size)
-                       ret = ext4_xattr_resize_item(ref, item, data_size);
-
-               if (ret != EOK) {
-                       goto Finish;
-               }
-               memcpy(item->data, data, data_size);
-       } else {
-               if (item) {
-                       ret = EEXIST;
-                       goto Finish;
-               }
-               item = ext4_xattr_insert_item(ref, name_index, name, name_len,
-                                             data, data_size);
-               if (!item)
-                       ret = ENOMEM;
-       }
-Finish:
-       return ret;
-}
-
-int ext4_fs_remove_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
-                        const char *name, size_t name_len)
-{
-       return ext4_xattr_remove_item(ref, name_index, name, name_len);
-}
-
-int ext4_fs_get_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
-                     const char *name, size_t name_len, void *buf,
-                     size_t buf_size, size_t *data_size)
-{
-       int ret = EOK;
-       size_t item_size = 0;
-       struct ext4_xattr_item *item =
-           ext4_xattr_lookup_item(ref, name_index, name, name_len);
-
-       if (!item) {
-               ret = ENODATA;
-               goto Finish;
-       }
-       item_size = item->data_size;
-       if (buf_size > item_size)
-               buf_size = item_size;
-
-       if (buf)
-               memcpy(buf, item->data, buf_size);
-
-Finish:
-       if (data_size)
-               *data_size = item_size;
-
-       return ret;
-}
-
-int ext4_fs_get_xattr_ref(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
-                         struct ext4_xattr_ref *ref)
-{
-       int rc;
-       ext4_fsblk_t xattr_block;
-       xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
-       RB_INIT(&ref->root);
-       ref->ea_size = 0;
-       ref->iter_from = NULL;
-       if (xattr_block) {
-               rc = ext4_trans_block_get(fs->bdev, &ref->block, xattr_block);
-               if (rc != EOK)
-                       return EIO;
-
-               ref->block_loaded = true;
-       } else
-               ref->block_loaded = false;
-
-       ref->inode_ref = inode_ref;
-       ref->fs = fs;
-
-       rc = ext4_xattr_fetch(ref);
-       if (rc != EOK) {
-               ext4_xattr_purge_items(ref);
-               if (xattr_block)
-                       ext4_block_set(fs->bdev, &inode_ref->block);
-
-               ref->block_loaded = false;
-               return rc;
-       }
-       return EOK;
-}
-
-void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref)
-{
-       ext4_xattr_write_to_disk(ref);
-       if (ref->block_loaded) {
-               ext4_block_set(ref->fs->bdev, &ref->block);
-               ref->block_loaded = false;
-       }
-       ext4_xattr_purge_items(ref);
-       ref->inode_ref = NULL;
-       ref->fs = NULL;
-}
-
-struct xattr_prefix {
-       const char *prefix;
-       uint8_t name_index;
-};
-
-static const struct xattr_prefix prefix_tbl[] = {
-    {"user.", EXT4_XATTR_INDEX_USER},
-    {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS},
-    {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT},
-    {"trusted.", EXT4_XATTR_INDEX_TRUSTED},
-    {"security.", EXT4_XATTR_INDEX_SECURITY},
-    {"system.", EXT4_XATTR_INDEX_SYSTEM},
-    {"system.richacl", EXT4_XATTR_INDEX_RICHACL},
-    {NULL, 0},
-};
-
-const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
-                             uint8_t *name_index, size_t *name_len)
-{
-       int i;
-       ext4_assert(name_index);
-       if (!full_name_len) {
-               if (name_len)
-                       *name_len = 0;
-
-               return NULL;
-       }
-
-       for (i = 0; prefix_tbl[i].prefix; i++) {
-               size_t prefix_len = strlen(prefix_tbl[i].prefix);
-               if (full_name_len >= prefix_len &&
-                   !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) {
-                       *name_index = prefix_tbl[i].name_index;
-                       if (name_len)
-                               *name_len = full_name_len - prefix_len;
-
-                       return full_name + prefix_len;
-               }
-       }
-       if (name_len)
-               *name_len = 0;
-
-       return NULL;
-}
-
-const char *ext4_get_xattr_name_prefix(uint8_t name_index,
-                                      size_t *ret_prefix_len)
-{
-       int i;
-
-       for (i = 0; prefix_tbl[i].prefix; i++) {
-               size_t prefix_len = strlen(prefix_tbl[i].prefix);
-               if (prefix_tbl[i].name_index == name_index) {
-                       if (ret_prefix_len)
-                               *ret_prefix_len = prefix_len;
-
-                       return prefix_tbl[i].prefix;
-               }
-       }
-       if (ret_prefix_len)
-               *ret_prefix_len = 0;
-
-       return NULL;
-}
-
-/**
- * @}
- */
diff --git a/lwext4/ext4_xattr.h b/lwext4/ext4_xattr.h
deleted file mode 100644 (file)
index f6076d2..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
- * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
- *
- * 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_xattr.h
- * @brief Extended Attribute manipulation.
- */
-
-#ifndef EXT4_XATTR_H_
-#define EXT4_XATTR_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ext4_config.h"
-#include "ext4_types.h"
-
-int ext4_fs_get_xattr_ref(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
-                         struct ext4_xattr_ref *ref);
-
-void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref);
-
-int ext4_fs_set_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
-                     const char *name, size_t name_len, const void *data,
-                     size_t data_size, bool replace);
-
-int ext4_fs_remove_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
-                        const char *name, size_t name_len);
-
-int ext4_fs_get_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
-                     const char *name, size_t name_len, void *buf,
-                     size_t buf_size, size_t *data_size);
-
-void ext4_fs_xattr_iterate(struct ext4_xattr_ref *ref,
-                          int (*iter)(struct ext4_xattr_ref *ref,
-                                    struct ext4_xattr_item *item));
-
-void ext4_fs_xattr_iterate_reset(struct ext4_xattr_ref *ref);
-
-const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
-                             uint8_t *name_index, size_t *name_len);
-
-const char *ext4_get_xattr_name_prefix(uint8_t name_index,
-                                      size_t *ret_prefix_len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-/**
- * @}
- */
diff --git a/lwext4/misc/queue.h b/lwext4/misc/queue.h
deleted file mode 100644 (file)
index 6efd6f3..0000000
+++ /dev/null
@@ -1,702 +0,0 @@
-/*-
- * Copyright (c) 1991, 1993
- *     The Regents of the University of California.  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.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * 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.
- *
- *     @(#)queue.h     8.5 (Berkeley) 8/20/94
- * $FreeBSD$
- */
-
-#ifndef _SYS_QUEUE_H_
-#define        _SYS_QUEUE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "../ext4_config.h"
-
-/*
- * This file defines four types of data structures: singly-linked lists,
- * singly-linked tail queues, lists and tail queues.
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction.  Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A singly-linked tail queue is headed by a pair of pointers, one to the
- * head of the list and the other to the tail of the list. The elements are
- * singly linked for minimum space and pointer manipulation overhead at the
- * expense of O(n) removal for arbitrary elements. New elements can be added
- * to the list after an existing element, at the head of the list, or at the
- * end of the list. Elements being removed from the head of the tail queue
- * should use the explicit macro for this purpose for optimum efficiency.
- * A singly-linked tail queue may only be traversed in the forward direction.
- * Singly-linked tail queues are ideal for applications with large datasets
- * and few or no removals or for implementing a FIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may be traversed in either direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- *
- *
- *                             SLIST   LIST    STAILQ  TAILQ
- * _HEAD                       +       +       +       +
- * _HEAD_INITIALIZER           +       +       +       +
- * _ENTRY                      +       +       +       +
- * _INIT                       +       +       +       +
- * _EMPTY                      +       +       +       +
- * _FIRST                      +       +       +       +
- * _NEXT                       +       +       +       +
- * _PREV                       -       +       -       +
- * _LAST                       -       -       +       +
- * _FOREACH                    +       +       +       +
- * _FOREACH_FROM               +       +       +       +
- * _FOREACH_SAFE               +       +       +       +
- * _FOREACH_FROM_SAFE          +       +       +       +
- * _FOREACH_REVERSE            -       -       -       +
- * _FOREACH_REVERSE_FROM       -       -       -       +
- * _FOREACH_REVERSE_SAFE       -       -       -       +
- * _FOREACH_REVERSE_FROM_SAFE  -       -       -       +
- * _INSERT_HEAD                        +       +       +       +
- * _INSERT_BEFORE              -       +       -       +
- * _INSERT_AFTER               +       +       +       +
- * _INSERT_TAIL                        -       -       +       +
- * _CONCAT                     -       -       +       +
- * _REMOVE_AFTER               +       -       +       -
- * _REMOVE_HEAD                        +       -       +       -
- * _REMOVE                     +       +       +       +
- * _SWAP                       +       +       +       +
- *
- */
-#ifdef QUEUE_MACRO_DEBUG
-/* Store the last 2 places the queue element or head was altered */
-struct qm_trace {
-       unsigned long    lastline;
-       unsigned long    prevline;
-       const char      *lastfile;
-       const char      *prevfile;
-};
-
-#define        TRACEBUF        struct qm_trace trace;
-#define        TRACEBUF_INITIALIZER    { __LINE__, 0, __FILE__, NULL } ,
-#define        TRASHIT(x)      do {(x) = (void *)-1;} while (0)
-#define        QMD_SAVELINK(name, link)        void **name = (void *)&(link)
-
-#define        QMD_TRACE_HEAD(head) do {                                       \
-       (head)->trace.prevline = (head)->trace.lastline;                \
-       (head)->trace.prevfile = (head)->trace.lastfile;                \
-       (head)->trace.lastline = __LINE__;                              \
-       (head)->trace.lastfile = __FILE__;                              \
-} while (0)
-
-#define        QMD_TRACE_ELEM(elem) do {                                       \
-       (elem)->trace.prevline = (elem)->trace.lastline;                \
-       (elem)->trace.prevfile = (elem)->trace.lastfile;                \
-       (elem)->trace.lastline = __LINE__;                              \
-       (elem)->trace.lastfile = __FILE__;                              \
-} while (0)
-
-#else
-#define        QMD_TRACE_ELEM(elem)
-#define        QMD_TRACE_HEAD(head)
-#define        QMD_SAVELINK(name, link)
-#define        TRACEBUF
-#define        TRACEBUF_INITIALIZER
-#define        TRASHIT(x)
-#endif /* QUEUE_MACRO_DEBUG */
-
-/*
- * Singly-linked List declarations.
- */
-#define        SLIST_HEAD(name, type)                                          \
-struct name {                                                          \
-       struct type *slh_first; /* first element */                     \
-}
-
-#define        SLIST_HEAD_INITIALIZER(head)                                    \
-       { NULL }
-
-#define        SLIST_ENTRY(type)                                               \
-struct {                                                               \
-       struct type *sle_next;  /* next element */                      \
-}
-
-/*
- * Singly-linked List functions.
- */
-#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
-
-#define        SLIST_FIRST(head)       ((head)->slh_first)
-
-#define        SLIST_FOREACH(var, head, field)                                 \
-       for ((var) = SLIST_FIRST((head));                               \
-           (var);                                                      \
-           (var) = SLIST_NEXT((var), field))
-
-#define        SLIST_FOREACH_FROM(var, head, field)                            \
-       for ((var) = ((var) ? (var) : SLIST_FIRST((head)));             \
-           (var);                                                      \
-           (var) = SLIST_NEXT((var), field))
-
-#define        SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
-       for ((var) = SLIST_FIRST((head));                               \
-           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
-           (var) = (tvar))
-
-#define        SLIST_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
-       for ((var) = ((var) ? (var) : SLIST_FIRST((head)));             \
-           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
-           (var) = (tvar))
-
-#define        SLIST_FOREACH_PREVPTR(var, varp, head, field)                   \
-       for ((varp) = &SLIST_FIRST((head));                             \
-           ((var) = *(varp)) != NULL;                                  \
-           (varp) = &SLIST_NEXT((var), field))
-
-#define        SLIST_INIT(head) do {                                           \
-       SLIST_FIRST((head)) = NULL;                                     \
-} while (0)
-
-#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
-       SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
-       SLIST_NEXT((slistelm), field) = (elm);                          \
-} while (0)
-
-#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
-       SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
-       SLIST_FIRST((head)) = (elm);                                    \
-} while (0)
-
-#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
-
-#define        SLIST_REMOVE(head, elm, type, field) do {                       \
-       QMD_SAVELINK(oldnext, (elm)->field.sle_next);                   \
-       if (SLIST_FIRST((head)) == (elm)) {                             \
-               SLIST_REMOVE_HEAD((head), field);                       \
-       }                                                               \
-       else {                                                          \
-               struct type *curelm = SLIST_FIRST((head));              \
-               while (SLIST_NEXT(curelm, field) != (elm))              \
-                       curelm = SLIST_NEXT(curelm, field);             \
-               SLIST_REMOVE_AFTER(curelm, field);                      \
-       }                                                               \
-       TRASHIT(*oldnext);                                              \
-} while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) do {                            \
-       SLIST_NEXT(elm, field) =                                        \
-           SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \
-} while (0)
-
-#define        SLIST_REMOVE_HEAD(head, field) do {                             \
-       SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
-} while (0)
-
-#define SLIST_SWAP(head1, head2, type) do {                            \
-       struct type *swap_first = SLIST_FIRST(head1);                   \
-       SLIST_FIRST(head1) = SLIST_FIRST(head2);                        \
-       SLIST_FIRST(head2) = swap_first;                                \
-} while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define        STAILQ_HEAD(name, type)                                         \
-struct name {                                                          \
-       struct type *stqh_first;/* first element */                     \
-       struct type **stqh_last;/* addr of last next element */         \
-}
-
-#define        STAILQ_HEAD_INITIALIZER(head)                                   \
-       { NULL, &(head).stqh_first }
-
-#define        STAILQ_ENTRY(type)                                              \
-struct {                                                               \
-       struct type *stqe_next; /* next element */                      \
-}
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define        STAILQ_CONCAT(head1, head2) do {                                \
-       if (!STAILQ_EMPTY((head2))) {                                   \
-               *(head1)->stqh_last = (head2)->stqh_first;              \
-               (head1)->stqh_last = (head2)->stqh_last;                \
-               STAILQ_INIT((head2));                                   \
-       }                                                               \
-} while (0)
-
-#define        STAILQ_EMPTY(head)      ((head)->stqh_first == NULL)
-
-#define        STAILQ_FIRST(head)      ((head)->stqh_first)
-
-#define        STAILQ_FOREACH(var, head, field)                                \
-       for((var) = STAILQ_FIRST((head));                               \
-          (var);                                                       \
-          (var) = STAILQ_NEXT((var), field))
-
-#define        STAILQ_FOREACH_FROM(var, head, field)                           \
-       for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));            \
-          (var);                                                       \
-          (var) = STAILQ_NEXT((var), field))
-
-#define        STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
-       for ((var) = STAILQ_FIRST((head));                              \
-           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
-           (var) = (tvar))
-
-#define        STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                \
-       for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));            \
-           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
-           (var) = (tvar))
-
-#define        STAILQ_INIT(head) do {                                          \
-       STAILQ_FIRST((head)) = NULL;                                    \
-       (head)->stqh_last = &STAILQ_FIRST((head));                      \
-} while (0)
-
-#define        STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
-       if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
-               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
-       STAILQ_NEXT((tqelm), field) = (elm);                            \
-} while (0)
-
-#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
-       if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
-               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
-       STAILQ_FIRST((head)) = (elm);                                   \
-} while (0)
-
-#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
-       STAILQ_NEXT((elm), field) = NULL;                               \
-       *(head)->stqh_last = (elm);                                     \
-       (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
-} while (0)
-
-#define        STAILQ_LAST(head, type, field)                                  \
-       (STAILQ_EMPTY((head)) ? NULL :                                  \
-           __containerof((head)->stqh_last, struct type, field.stqe_next))
-
-#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
-       QMD_SAVELINK(oldnext, (elm)->field.stqe_next);                  \
-       if (STAILQ_FIRST((head)) == (elm)) {                            \
-               STAILQ_REMOVE_HEAD((head), field);                      \
-       }                                                               \
-       else {                                                          \
-               struct type *curelm = STAILQ_FIRST((head));             \
-               while (STAILQ_NEXT(curelm, field) != (elm))             \
-                       curelm = STAILQ_NEXT(curelm, field);            \
-               STAILQ_REMOVE_AFTER(head, curelm, field);               \
-       }                                                               \
-       TRASHIT(*oldnext);                                              \
-} while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) do {                     \
-       if ((STAILQ_NEXT(elm, field) =                                  \
-            STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)      \
-               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
-} while (0)
-
-#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
-       if ((STAILQ_FIRST((head)) =                                     \
-            STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
-               (head)->stqh_last = &STAILQ_FIRST((head));              \
-} while (0)
-
-#define STAILQ_SWAP(head1, head2, type) do {                           \
-       struct type *swap_first = STAILQ_FIRST(head1);                  \
-       struct type **swap_last = (head1)->stqh_last;                   \
-       STAILQ_FIRST(head1) = STAILQ_FIRST(head2);                      \
-       (head1)->stqh_last = (head2)->stqh_last;                        \
-       STAILQ_FIRST(head2) = swap_first;                               \
-       (head2)->stqh_last = swap_last;                                 \
-       if (STAILQ_EMPTY(head1))                                        \
-               (head1)->stqh_last = &STAILQ_FIRST(head1);              \
-       if (STAILQ_EMPTY(head2))                                        \
-               (head2)->stqh_last = &STAILQ_FIRST(head2);              \
-} while (0)
-
-
-/*
- * List declarations.
- */
-#define        LIST_HEAD(name, type)                                           \
-struct name {                                                          \
-       struct type *lh_first;  /* first element */                     \
-}
-
-#define        LIST_HEAD_INITIALIZER(head)                                     \
-       { NULL }
-
-#define        LIST_ENTRY(type)                                                \
-struct {                                                               \
-       struct type *le_next;   /* next element */                      \
-       struct type **le_prev;  /* address of previous next element */  \
-}
-
-/*
- * List functions.
- */
-
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define        QMD_LIST_CHECK_HEAD(head, field) do {                           \
-       if (LIST_FIRST((head)) != NULL &&                               \
-           LIST_FIRST((head))->field.le_prev !=                        \
-            &LIST_FIRST((head)))                                       \
-               panic("Bad list head %p first->prev != head", (head));  \
-} while (0)
-
-#define        QMD_LIST_CHECK_NEXT(elm, field) do {                            \
-       if (LIST_NEXT((elm), field) != NULL &&                          \
-           LIST_NEXT((elm), field)->field.le_prev !=                   \
-            &((elm)->field.le_next))                                   \
-               panic("Bad link elm %p next->prev != elm", (elm));      \
-} while (0)
-
-#define        QMD_LIST_CHECK_PREV(elm, field) do {                            \
-       if (*(elm)->field.le_prev != (elm))                             \
-               panic("Bad link elm %p prev->next != elm", (elm));      \
-} while (0)
-#else
-#define        QMD_LIST_CHECK_HEAD(head, field)
-#define        QMD_LIST_CHECK_NEXT(elm, field)
-#define        QMD_LIST_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define        LIST_EMPTY(head)        ((head)->lh_first == NULL)
-
-#define        LIST_FIRST(head)        ((head)->lh_first)
-
-#define        LIST_FOREACH(var, head, field)                                  \
-       for ((var) = LIST_FIRST((head));                                \
-           (var);                                                      \
-           (var) = LIST_NEXT((var), field))
-
-#define        LIST_FOREACH_FROM(var, head, field)                             \
-       for ((var) = ((var) ? (var) : LIST_FIRST((head)));              \
-           (var);                                                      \
-           (var) = LIST_NEXT((var), field))
-
-#define        LIST_FOREACH_SAFE(var, head, field, tvar)                       \
-       for ((var) = LIST_FIRST((head));                                \
-           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
-           (var) = (tvar))
-
-#define        LIST_FOREACH_FROM_SAFE(var, head, field, tvar)                  \
-       for ((var) = ((var) ? (var) : LIST_FIRST((head)));              \
-           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
-           (var) = (tvar))
-
-#define        LIST_INIT(head) do {                                            \
-       LIST_FIRST((head)) = NULL;                                      \
-} while (0)
-
-#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
-       QMD_LIST_CHECK_NEXT(listelm, field);                            \
-       if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
-               LIST_NEXT((listelm), field)->field.le_prev =            \
-                   &LIST_NEXT((elm), field);                           \
-       LIST_NEXT((listelm), field) = (elm);                            \
-       (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
-} while (0)
-
-#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
-       QMD_LIST_CHECK_PREV(listelm, field);                            \
-       (elm)->field.le_prev = (listelm)->field.le_prev;                \
-       LIST_NEXT((elm), field) = (listelm);                            \
-       *(listelm)->field.le_prev = (elm);                              \
-       (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
-} while (0)
-
-#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
-       QMD_LIST_CHECK_HEAD((head), field);                             \
-       if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
-               LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
-       LIST_FIRST((head)) = (elm);                                     \
-       (elm)->field.le_prev = &LIST_FIRST((head));                     \
-} while (0)
-
-#define        LIST_NEXT(elm, field)   ((elm)->field.le_next)
-
-#define        LIST_PREV(elm, head, type, field)                               \
-       ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL :           \
-           __containerof((elm)->field.le_prev, struct type, field.le_next))
-
-#define        LIST_REMOVE(elm, field) do {                                    \
-       QMD_SAVELINK(oldnext, (elm)->field.le_next);                    \
-       QMD_SAVELINK(oldprev, (elm)->field.le_prev);                    \
-       QMD_LIST_CHECK_NEXT(elm, field);                                \
-       QMD_LIST_CHECK_PREV(elm, field);                                \
-       if (LIST_NEXT((elm), field) != NULL)                            \
-               LIST_NEXT((elm), field)->field.le_prev =                \
-                   (elm)->field.le_prev;                               \
-       *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
-       TRASHIT(*oldnext);                                              \
-       TRASHIT(*oldprev);                                              \
-} while (0)
-
-#define LIST_SWAP(head1, head2, type, field) do {                      \
-       struct type *swap_tmp = LIST_FIRST((head1));                    \
-       LIST_FIRST((head1)) = LIST_FIRST((head2));                      \
-       LIST_FIRST((head2)) = swap_tmp;                                 \
-       if ((swap_tmp = LIST_FIRST((head1))) != NULL)                   \
-               swap_tmp->field.le_prev = &LIST_FIRST((head1));         \
-       if ((swap_tmp = LIST_FIRST((head2))) != NULL)                   \
-               swap_tmp->field.le_prev = &LIST_FIRST((head2));         \
-} while (0)
-
-/*
- * Tail queue declarations.
- */
-#define        TAILQ_HEAD(name, type)                                          \
-struct name {                                                          \
-       struct type *tqh_first; /* first element */                     \
-       struct type **tqh_last; /* addr of last next element */         \
-       TRACEBUF                                                        \
-}
-
-#define        TAILQ_HEAD_INITIALIZER(head)                                    \
-       { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
-
-#define        TAILQ_ENTRY(type)                                               \
-struct {                                                               \
-       struct type *tqe_next;  /* next element */                      \
-       struct type **tqe_prev; /* address of previous next element */  \
-       TRACEBUF                                                        \
-}
-
-/*
- * Tail queue functions.
- */
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define        QMD_TAILQ_CHECK_HEAD(head, field) do {                          \
-       if (!TAILQ_EMPTY(head) &&                                       \
-           TAILQ_FIRST((head))->field.tqe_prev !=                      \
-            &TAILQ_FIRST((head)))                                      \
-               panic("Bad tailq head %p first->prev != head", (head)); \
-} while (0)
-
-#define        QMD_TAILQ_CHECK_TAIL(head, field) do {                          \
-       if (*(head)->tqh_last != NULL)                                  \
-               panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head));  \
-} while (0)
-
-#define        QMD_TAILQ_CHECK_NEXT(elm, field) do {                           \
-       if (TAILQ_NEXT((elm), field) != NULL &&                         \
-           TAILQ_NEXT((elm), field)->field.tqe_prev !=                 \
-            &((elm)->field.tqe_next))                                  \
-               panic("Bad link elm %p next->prev != elm", (elm));      \
-} while (0)
-
-#define        QMD_TAILQ_CHECK_PREV(elm, field) do {                           \
-       if (*(elm)->field.tqe_prev != (elm))                            \
-               panic("Bad link elm %p prev->next != elm", (elm));      \
-} while (0)
-#else
-#define        QMD_TAILQ_CHECK_HEAD(head, field)
-#define        QMD_TAILQ_CHECK_TAIL(head, headname)
-#define        QMD_TAILQ_CHECK_NEXT(elm, field)
-#define        QMD_TAILQ_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define        TAILQ_CONCAT(head1, head2, field) do {                          \
-       if (!TAILQ_EMPTY(head2)) {                                      \
-               *(head1)->tqh_last = (head2)->tqh_first;                \
-               (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
-               (head1)->tqh_last = (head2)->tqh_last;                  \
-               TAILQ_INIT((head2));                                    \
-               QMD_TRACE_HEAD(head1);                                  \
-               QMD_TRACE_HEAD(head2);                                  \
-       }                                                               \
-} while (0)
-
-#define        TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
-
-#define        TAILQ_FIRST(head)       ((head)->tqh_first)
-
-#define        TAILQ_FOREACH(var, head, field)                                 \
-       for ((var) = TAILQ_FIRST((head));                               \
-           (var);                                                      \
-           (var) = TAILQ_NEXT((var), field))
-
-#define        TAILQ_FOREACH_FROM(var, head, field)                            \
-       for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));             \
-           (var);                                                      \
-           (var) = TAILQ_NEXT((var), field))
-
-#define        TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
-       for ((var) = TAILQ_FIRST((head));                               \
-           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
-           (var) = (tvar))
-
-#define        TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
-       for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));             \
-           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
-           (var) = (tvar))
-
-#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
-       for ((var) = TAILQ_LAST((head), headname);                      \
-           (var);                                                      \
-           (var) = TAILQ_PREV((var), headname, field))
-
-#define        TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field)          \
-       for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));    \
-           (var);                                                      \
-           (var) = TAILQ_PREV((var), headname, field))
-
-#define        TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
-       for ((var) = TAILQ_LAST((head), headname);                      \
-           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
-           (var) = (tvar))
-
-#define        TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
-       for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));    \
-           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
-           (var) = (tvar))
-
-#define        TAILQ_INIT(head) do {                                           \
-       TAILQ_FIRST((head)) = NULL;                                     \
-       (head)->tqh_last = &TAILQ_FIRST((head));                        \
-       QMD_TRACE_HEAD(head);                                           \
-} while (0)
-
-#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
-       QMD_TAILQ_CHECK_NEXT(listelm, field);                           \
-       if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
-               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
-                   &TAILQ_NEXT((elm), field);                          \
-       else {                                                          \
-               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
-               QMD_TRACE_HEAD(head);                                   \
-       }                                                               \
-       TAILQ_NEXT((listelm), field) = (elm);                           \
-       (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
-       QMD_TRACE_ELEM(&(elm)->field);                                  \
-       QMD_TRACE_ELEM(&(listelm)->field);                              \
-} while (0)
-
-#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
-       QMD_TAILQ_CHECK_PREV(listelm, field);                           \
-       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
-       TAILQ_NEXT((elm), field) = (listelm);                           \
-       *(listelm)->field.tqe_prev = (elm);                             \
-       (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
-       QMD_TRACE_ELEM(&(elm)->field);                                  \
-       QMD_TRACE_ELEM(&(listelm)->field);                              \
-} while (0)
-
-#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
-       QMD_TAILQ_CHECK_HEAD(head, field);                              \
-       if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
-               TAILQ_FIRST((head))->field.tqe_prev =                   \
-                   &TAILQ_NEXT((elm), field);                          \
-       else                                                            \
-               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
-       TAILQ_FIRST((head)) = (elm);                                    \
-       (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
-       QMD_TRACE_HEAD(head);                                           \
-       QMD_TRACE_ELEM(&(elm)->field);                                  \
-} while (0)
-
-#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
-       QMD_TAILQ_CHECK_TAIL(head, field);                              \
-       TAILQ_NEXT((elm), field) = NULL;                                \
-       (elm)->field.tqe_prev = (head)->tqh_last;                       \
-       *(head)->tqh_last = (elm);                                      \
-       (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
-       QMD_TRACE_HEAD(head);                                           \
-       QMD_TRACE_ELEM(&(elm)->field);                                  \
-} while (0)
-
-#define        TAILQ_LAST(head, headname)                                      \
-       (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define        TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define        TAILQ_PREV(elm, headname, field)                                \
-       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define        TAILQ_REMOVE(head, elm, field) do {                             \
-       QMD_SAVELINK(oldnext, (elm)->field.tqe_next);                   \
-       QMD_SAVELINK(oldprev, (elm)->field.tqe_prev);                   \
-       QMD_TAILQ_CHECK_NEXT(elm, field);                               \
-       QMD_TAILQ_CHECK_PREV(elm, field);                               \
-       if ((TAILQ_NEXT((elm), field)) != NULL)                         \
-               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
-                   (elm)->field.tqe_prev;                              \
-       else {                                                          \
-               (head)->tqh_last = (elm)->field.tqe_prev;               \
-               QMD_TRACE_HEAD(head);                                   \
-       }                                                               \
-       *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
-       TRASHIT(*oldnext);                                              \
-       TRASHIT(*oldprev);                                              \
-       QMD_TRACE_ELEM(&(elm)->field);                                  \
-} while (0)
-
-#define TAILQ_SWAP(head1, head2, type, field) do {                     \
-       struct type *swap_first = (head1)->tqh_first;                   \
-       struct type **swap_last = (head1)->tqh_last;                    \
-       (head1)->tqh_first = (head2)->tqh_first;                        \
-       (head1)->tqh_last = (head2)->tqh_last;                          \
-       (head2)->tqh_first = swap_first;                                \
-       (head2)->tqh_last = swap_last;                                  \
-       if ((swap_first = (head1)->tqh_first) != NULL)                  \
-               swap_first->field.tqe_prev = &(head1)->tqh_first;       \
-       else                                                            \
-               (head1)->tqh_last = &(head1)->tqh_first;                \
-       if ((swap_first = (head2)->tqh_first) != NULL)                  \
-               swap_first->field.tqe_prev = &(head2)->tqh_first;       \
-       else                                                            \
-               (head2)->tqh_last = &(head2)->tqh_first;                \
-} while (0)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !_SYS_QUEUE_H_ */
diff --git a/lwext4/misc/tree.h b/lwext4/misc/tree.h
deleted file mode 100644 (file)
index 8ed5d41..0000000
+++ /dev/null
@@ -1,809 +0,0 @@
-/*     $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $  */
-/*     $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $    */
-/* $FreeBSD$ */
-
-/*-
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * 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 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.
- */
-
-#ifndef        _SYS_TREE_H_
-#define        _SYS_TREE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "../ext4_config.h"
-
-/*
- * This file defines data structures for different types of trees:
- * splay trees and red-black trees.
- *
- * A splay tree is a self-organizing data structure.  Every operation
- * on the tree causes a splay to happen.  The splay moves the requested
- * node to the root of the tree and partly rebalances it.
- *
- * This has the benefit that request locality causes faster lookups as
- * the requested nodes move to the top of the tree.  On the other hand,
- * every lookup causes memory writes.
- *
- * The Balance Theorem bounds the total access time for m operations
- * and n inserts on an initially empty tree as O((m + n)lg n).  The
- * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
- *
- * A red-black tree is a binary search tree with the node color as an
- * extra attribute.  It fulfills a set of conditions:
- *     - every search path from the root to a leaf consists of the
- *       same number of black nodes,
- *     - each red node (except for the root) has a black parent,
- *     - each leaf node is black.
- *
- * Every operation on a red-black tree is bounded as O(lg n).
- * The maximum height of a red-black tree is 2lg (n+1).
- */
-
-#define SPLAY_HEAD(name, type)                                         \
-struct name {                                                          \
-       struct type *sph_root; /* root of the tree */                   \
-}
-
-#define SPLAY_INITIALIZER(root)                                                \
-       { NULL }
-
-#define SPLAY_INIT(root) do {                                          \
-       (root)->sph_root = NULL;                                        \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_ENTRY(type)                                              \
-struct {                                                               \
-       struct type *spe_left; /* left element */                       \
-       struct type *spe_right; /* right element */                     \
-}
-
-#define SPLAY_LEFT(elm, field)         (elm)->field.spe_left
-#define SPLAY_RIGHT(elm, field)                (elm)->field.spe_right
-#define SPLAY_ROOT(head)               (head)->sph_root
-#define SPLAY_EMPTY(head)              (SPLAY_ROOT(head) == NULL)
-
-/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
-#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                      \
-       SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);  \
-       SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \
-       (head)->sph_root = tmp;                                         \
-} while (/*CONSTCOND*/ 0)
-       
-#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                       \
-       SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);  \
-       SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \
-       (head)->sph_root = tmp;                                         \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_LINKLEFT(head, tmp, field) do {                          \
-       SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \
-       tmp = (head)->sph_root;                                         \
-       (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);         \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_LINKRIGHT(head, tmp, field) do {                         \
-       SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \
-       tmp = (head)->sph_root;                                         \
-       (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);        \
-} while (/*CONSTCOND*/ 0)
-
-#define SPLAY_ASSEMBLE(head, node, left, right, field) do {            \
-       SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
-       SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
-       SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
-       SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
-} while (/*CONSTCOND*/ 0)
-
-/* Generates prototypes and inline functions */
-
-#define SPLAY_PROTOTYPE(name, type, field, cmp)                                \
-void name##_SPLAY(struct name *, struct type *);                       \
-void name##_SPLAY_MINMAX(struct name *, int);                          \
-struct type *name##_SPLAY_INSERT(struct name *, struct type *);                \
-struct type *name##_SPLAY_REMOVE(struct name *, struct type *);                \
-                                                                       \
-/* Finds the node with the same key as elm */                          \
-static __inline struct type *                                          \
-name##_SPLAY_FIND(struct name *head, struct type *elm)                 \
-{                                                                      \
-       if (SPLAY_EMPTY(head))                                          \
-               return(NULL);                                           \
-       name##_SPLAY(head, elm);                                        \
-       if ((cmp)(elm, (head)->sph_root) == 0)                          \
-               return (head->sph_root);                                \
-       return (NULL);                                                  \
-}                                                                      \
-                                                                       \
-static __inline struct type *                                          \
-name##_SPLAY_NEXT(struct name *head, struct type *elm)                 \
-{                                                                      \
-       name##_SPLAY(head, elm);                                        \
-       if (SPLAY_RIGHT(elm, field) != NULL) {                          \
-               elm = SPLAY_RIGHT(elm, field);                          \
-               while (SPLAY_LEFT(elm, field) != NULL) {                \
-                       elm = SPLAY_LEFT(elm, field);                   \
-               }                                                       \
-       } else                                                          \
-               elm = NULL;                                             \
-       return (elm);                                                   \
-}                                                                      \
-                                                                       \
-static __inline struct type *                                          \
-name##_SPLAY_MIN_MAX(struct name *head, int val)                       \
-{                                                                      \
-       name##_SPLAY_MINMAX(head, val);                                 \
-        return (SPLAY_ROOT(head));                                     \
-}
-
-/* Main splay operation.
- * Moves node close to the key of elm to top
- */
-#define SPLAY_GENERATE(name, type, field, cmp)                         \
-struct type *                                                          \
-name##_SPLAY_INSERT(struct name *head, struct type *elm)               \
-{                                                                      \
-    if (SPLAY_EMPTY(head)) {                                           \
-           SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;    \
-    } else {                                                           \
-           int __comp;                                                 \
-           name##_SPLAY(head, elm);                                    \
-           __comp = (cmp)(elm, (head)->sph_root);                      \
-           if(__comp < 0) {                                            \
-                   SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
-                   SPLAY_RIGHT(elm, field) = (head)->sph_root;         \
-                   SPLAY_LEFT((head)->sph_root, field) = NULL;         \
-           } else if (__comp > 0) {                                    \
-                   SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
-                   SPLAY_LEFT(elm, field) = (head)->sph_root;          \
-                   SPLAY_RIGHT((head)->sph_root, field) = NULL;        \
-           } else                                                      \
-                   return ((head)->sph_root);                          \
-    }                                                                  \
-    (head)->sph_root = (elm);                                          \
-    return (NULL);                                                     \
-}                                                                      \
-                                                                       \
-struct type *                                                          \
-name##_SPLAY_REMOVE(struct name *head, struct type *elm)               \
-{                                                                      \
-       struct type *__tmp;                                             \
-       if (SPLAY_EMPTY(head))                                          \
-               return (NULL);                                          \
-       name##_SPLAY(head, elm);                                        \
-       if ((cmp)(elm, (head)->sph_root) == 0) {                        \
-               if (SPLAY_LEFT((head)->sph_root, field) == NULL) {      \
-                       (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
-               } else {                                                \
-                       __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
-                       (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
-                       name##_SPLAY(head, elm);                        \
-                       SPLAY_RIGHT((head)->sph_root, field) = __tmp;   \
-               }                                                       \
-               return (elm);                                           \
-       }                                                               \
-       return (NULL);                                                  \
-}                                                                      \
-                                                                       \
-void                                                                   \
-name##_SPLAY(struct name *head, struct type *elm)                      \
-{                                                                      \
-       struct type __node, *__left, *__right, *__tmp;                  \
-       int __comp;                                                     \
-\
-       SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
-       __left = __right = &__node;                                     \
-\
-       while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {          \
-               if (__comp < 0) {                                       \
-                       __tmp = SPLAY_LEFT((head)->sph_root, field);    \
-                       if (__tmp == NULL)                              \
-                               break;                                  \
-                       if ((cmp)(elm, __tmp) < 0){                     \
-                               SPLAY_ROTATE_RIGHT(head, __tmp, field); \
-                               if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
-                                       break;                          \
-                       }                                               \
-                       SPLAY_LINKLEFT(head, __right, field);           \
-               } else if (__comp > 0) {                                \
-                       __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
-                       if (__tmp == NULL)                              \
-                               break;                                  \
-                       if ((cmp)(elm, __tmp) > 0){                     \
-                               SPLAY_ROTATE_LEFT(head, __tmp, field);  \
-                               if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
-                                       break;                          \
-                       }                                               \
-                       SPLAY_LINKRIGHT(head, __left, field);           \
-               }                                                       \
-       }                                                               \
-       SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \
-}                                                                      \
-                                                                       \
-/* Splay with either the minimum or the maximum element                        \
- * Used to find minimum or maximum element in tree.                    \
- */                                                                    \
-void name##_SPLAY_MINMAX(struct name *head, int __comp) \
-{                                                                      \
-       struct type __node, *__left, *__right, *__tmp;                  \
-\
-       SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
-       __left = __right = &__node;                                     \
-\
-       while (1) {                                                     \
-               if (__comp < 0) {                                       \
-                       __tmp = SPLAY_LEFT((head)->sph_root, field);    \
-                       if (__tmp == NULL)                              \
-                               break;                                  \
-                       if (__comp < 0){                                \
-                               SPLAY_ROTATE_RIGHT(head, __tmp, field); \
-                               if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
-                                       break;                          \
-                       }                                               \
-                       SPLAY_LINKLEFT(head, __right, field);           \
-               } else if (__comp > 0) {                                \
-                       __tmp = SPLAY_RIGHT((head)->sph_root, field);   \
-                       if (__tmp == NULL)                              \
-                               break;                                  \
-                       if (__comp > 0) {                               \
-                               SPLAY_ROTATE_LEFT(head, __tmp, field);  \
-                               if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
-                                       break;                          \
-                       }                                               \
-                       SPLAY_LINKRIGHT(head, __left, field);           \
-               }                                                       \
-       }                                                               \
-       SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \
-}
-
-#define SPLAY_NEGINF   -1
-#define SPLAY_INF      1
-
-#define SPLAY_INSERT(name, x, y)       name##_SPLAY_INSERT(x, y)
-#define SPLAY_REMOVE(name, x, y)       name##_SPLAY_REMOVE(x, y)
-#define SPLAY_FIND(name, x, y)         name##_SPLAY_FIND(x, y)
-#define SPLAY_NEXT(name, x, y)         name##_SPLAY_NEXT(x, y)
-#define SPLAY_MIN(name, x)             (SPLAY_EMPTY(x) ? NULL  \
-                                       : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
-#define SPLAY_MAX(name, x)             (SPLAY_EMPTY(x) ? NULL  \
-                                       : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
-
-#define SPLAY_FOREACH(x, name, head)                                   \
-       for ((x) = SPLAY_MIN(name, head);                               \
-            (x) != NULL;                                               \
-            (x) = SPLAY_NEXT(name, head, x))
-
-/* Macros that define a red-black tree */
-#define RB_HEAD(name, type)                                            \
-struct name {                                                          \
-       struct type *rbh_root; /* root of the tree */                   \
-}
-
-#define RB_INITIALIZER(root)                                           \
-       { NULL }
-
-#define RB_INIT(root) do {                                             \
-       (root)->rbh_root = NULL;                                        \
-} while (/*CONSTCOND*/ 0)
-
-#define RB_BLACK       0
-#define RB_RED         1
-#define RB_ENTRY(type)                                                 \
-struct {                                                               \
-       struct type *rbe_left;          /* left element */              \
-       struct type *rbe_right;         /* right element */             \
-       struct type *rbe_parent;        /* parent element */            \
-       int rbe_color;                  /* node color */                \
-}
-
-#define RB_LEFT(elm, field)            (elm)->field.rbe_left
-#define RB_RIGHT(elm, field)           (elm)->field.rbe_right
-#define RB_PARENT(elm, field)          (elm)->field.rbe_parent
-#define RB_COLOR(elm, field)           (elm)->field.rbe_color
-#define RB_ROOT(head)                  (head)->rbh_root
-#define RB_EMPTY(head)                 (RB_ROOT(head) == NULL)
-
-#define RB_SET(elm, parent, field) do {                                        \
-       RB_PARENT(elm, field) = parent;                                 \
-       RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;              \
-       RB_COLOR(elm, field) = RB_RED;                                  \
-} while (/*CONSTCOND*/ 0)
-
-#define RB_SET_BLACKRED(black, red, field) do {                                \
-       RB_COLOR(black, field) = RB_BLACK;                              \
-       RB_COLOR(red, field) = RB_RED;                                  \
-} while (/*CONSTCOND*/ 0)
-
-#ifndef RB_AUGMENT
-#define RB_AUGMENT(x)  do {} while (0)
-#endif
-
-#define RB_ROTATE_LEFT(head, elm, tmp, field) do {                     \
-       (tmp) = RB_RIGHT(elm, field);                                   \
-       if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {     \
-               RB_PARENT(RB_LEFT(tmp, field), field) = (elm);          \
-       }                                                               \
-       RB_AUGMENT(elm);                                                \
-       if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \
-               if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \
-                       RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \
-               else                                                    \
-                       RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
-       } else                                                          \
-               (head)->rbh_root = (tmp);                               \
-       RB_LEFT(tmp, field) = (elm);                                    \
-       RB_PARENT(elm, field) = (tmp);                                  \
-       RB_AUGMENT(tmp);                                                \
-       if ((RB_PARENT(tmp, field)))                                    \
-               RB_AUGMENT(RB_PARENT(tmp, field));                      \
-} while (/*CONSTCOND*/ 0)
-
-#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {                    \
-       (tmp) = RB_LEFT(elm, field);                                    \
-       if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {     \
-               RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);         \
-       }                                                               \
-       RB_AUGMENT(elm);                                                \
-       if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \
-               if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \
-                       RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \
-               else                                                    \
-                       RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
-       } else                                                          \
-               (head)->rbh_root = (tmp);                               \
-       RB_RIGHT(tmp, field) = (elm);                                   \
-       RB_PARENT(elm, field) = (tmp);                                  \
-       RB_AUGMENT(tmp);                                                \
-       if ((RB_PARENT(tmp, field)))                                    \
-               RB_AUGMENT(RB_PARENT(tmp, field));                      \
-} while (/*CONSTCOND*/ 0)
-
-/* Generates prototypes and inline functions */
-#define        RB_PROTOTYPE(name, type, field, cmp)                            \
-       RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
-#define        RB_PROTOTYPE_STATIC(name, type, field, cmp)                     \
-       RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
-#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)            \
-       RB_PROTOTYPE_INSERT_COLOR(name, type, attr);                    \
-       RB_PROTOTYPE_REMOVE_COLOR(name, type, attr);                    \
-       RB_PROTOTYPE_INSERT(name, type, attr);                          \
-       RB_PROTOTYPE_REMOVE(name, type, attr);                          \
-       RB_PROTOTYPE_FIND(name, type, attr);                            \
-       RB_PROTOTYPE_NFIND(name, type, attr);                           \
-       RB_PROTOTYPE_NEXT(name, type, attr);                            \
-       RB_PROTOTYPE_PREV(name, type, attr);                            \
-       RB_PROTOTYPE_MINMAX(name, type, attr);
-#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr)                    \
-       attr void name##_RB_INSERT_COLOR(struct name *, struct type *)
-#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr)                    \
-       attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *)
-#define RB_PROTOTYPE_REMOVE(name, type, attr)                          \
-       attr struct type *name##_RB_REMOVE(struct name *, struct type *)
-#define RB_PROTOTYPE_INSERT(name, type, attr)                          \
-       attr struct type *name##_RB_INSERT(struct name *, struct type *)
-#define RB_PROTOTYPE_FIND(name, type, attr)                            \
-       attr struct type *name##_RB_FIND(struct name *, struct type *)
-#define RB_PROTOTYPE_NFIND(name, type, attr)                           \
-       attr struct type *name##_RB_NFIND(struct name *, struct type *)
-#define RB_PROTOTYPE_NEXT(name, type, attr)                            \
-       attr struct type *name##_RB_NEXT(struct type *)
-#define RB_PROTOTYPE_PREV(name, type, attr)                            \
-       attr struct type *name##_RB_PREV(struct type *)
-#define RB_PROTOTYPE_MINMAX(name, type, attr)                          \
-       attr struct type *name##_RB_MINMAX(struct name *, int)
-
-/* Main rb operation.
- * Moves node close to the key of elm to top
- */
-#define        RB_GENERATE(name, type, field, cmp)                             \
-       RB_GENERATE_INTERNAL(name, type, field, cmp,)
-#define        RB_GENERATE_STATIC(name, type, field, cmp)                      \
-       RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
-#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)             \
-       RB_GENERATE_INSERT_COLOR(name, type, field, attr)               \
-       RB_GENERATE_REMOVE_COLOR(name, type, field, attr)               \
-       RB_GENERATE_INSERT(name, type, field, cmp, attr)                \
-       RB_GENERATE_REMOVE(name, type, field, attr)                     \
-       RB_GENERATE_FIND(name, type, field, cmp, attr)                  \
-       RB_GENERATE_NFIND(name, type, field, cmp, attr)                 \
-       RB_GENERATE_NEXT(name, type, field, attr)                       \
-       RB_GENERATE_PREV(name, type, field, attr)                       \
-       RB_GENERATE_MINMAX(name, type, field, attr)
-
-#define RB_GENERATE_INSERT_COLOR(name, type, field, attr)              \
-attr void                                                              \
-name##_RB_INSERT_COLOR(struct name *head, struct type *elm)            \
-{                                                                      \
-       struct type *parent, *gparent, *tmp;                            \
-       while ((parent = RB_PARENT(elm, field)) != NULL &&              \
-           RB_COLOR(parent, field) == RB_RED) {                        \
-               gparent = RB_PARENT(parent, field);                     \
-               if (parent == RB_LEFT(gparent, field)) {                \
-                       tmp = RB_RIGHT(gparent, field);                 \
-                       if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \
-                               RB_COLOR(tmp, field) = RB_BLACK;        \
-                               RB_SET_BLACKRED(parent, gparent, field);\
-                               elm = gparent;                          \
-                               continue;                               \
-                       }                                               \
-                       if (RB_RIGHT(parent, field) == elm) {           \
-                               RB_ROTATE_LEFT(head, parent, tmp, field);\
-                               tmp = parent;                           \
-                               parent = elm;                           \
-                               elm = tmp;                              \
-                       }                                               \
-                       RB_SET_BLACKRED(parent, gparent, field);        \
-                       RB_ROTATE_RIGHT(head, gparent, tmp, field);     \
-               } else {                                                \
-                       tmp = RB_LEFT(gparent, field);                  \
-                       if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \
-                               RB_COLOR(tmp, field) = RB_BLACK;        \
-                               RB_SET_BLACKRED(parent, gparent, field);\
-                               elm = gparent;                          \
-                               continue;                               \
-                       }                                               \
-                       if (RB_LEFT(parent, field) == elm) {            \
-                               RB_ROTATE_RIGHT(head, parent, tmp, field);\
-                               tmp = parent;                           \
-                               parent = elm;                           \
-                               elm = tmp;                              \
-                       }                                               \
-                       RB_SET_BLACKRED(parent, gparent, field);        \
-                       RB_ROTATE_LEFT(head, gparent, tmp, field);      \
-               }                                                       \
-       }                                                               \
-       RB_COLOR(head->rbh_root, field) = RB_BLACK;                     \
-}
-
-#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr)              \
-attr void                                                              \
-name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
-{                                                                      \
-       struct type *tmp;                                               \
-       while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&     \
-           elm != RB_ROOT(head)) {                                     \
-               if (RB_LEFT(parent, field) == elm) {                    \
-                       tmp = RB_RIGHT(parent, field);                  \
-                       if (RB_COLOR(tmp, field) == RB_RED) {           \
-                               RB_SET_BLACKRED(tmp, parent, field);    \
-                               RB_ROTATE_LEFT(head, parent, tmp, field);\
-                               tmp = RB_RIGHT(parent, field);          \
-                       }                                               \
-                       if ((RB_LEFT(tmp, field) == NULL ||             \
-                           RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
-                           (RB_RIGHT(tmp, field) == NULL ||            \
-                           RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
-                               RB_COLOR(tmp, field) = RB_RED;          \
-                               elm = parent;                           \
-                               parent = RB_PARENT(elm, field);         \
-                       } else {                                        \
-                               if (RB_RIGHT(tmp, field) == NULL ||     \
-                                   RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
-                                       struct type *oleft;             \
-                                       if ((oleft = RB_LEFT(tmp, field)) \
-                                           != NULL)                    \
-                                               RB_COLOR(oleft, field) = RB_BLACK;\
-                                       RB_COLOR(tmp, field) = RB_RED;  \
-                                       RB_ROTATE_RIGHT(head, tmp, oleft, field);\
-                                       tmp = RB_RIGHT(parent, field);  \
-                               }                                       \
-                               RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
-                               RB_COLOR(parent, field) = RB_BLACK;     \
-                               if (RB_RIGHT(tmp, field))               \
-                                       RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
-                               RB_ROTATE_LEFT(head, parent, tmp, field);\
-                               elm = RB_ROOT(head);                    \
-                               break;                                  \
-                       }                                               \
-               } else {                                                \
-                       tmp = RB_LEFT(parent, field);                   \
-                       if (RB_COLOR(tmp, field) == RB_RED) {           \
-                               RB_SET_BLACKRED(tmp, parent, field);    \
-                               RB_ROTATE_RIGHT(head, parent, tmp, field);\
-                               tmp = RB_LEFT(parent, field);           \
-                       }                                               \
-                       if ((RB_LEFT(tmp, field) == NULL ||             \
-                           RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
-                           (RB_RIGHT(tmp, field) == NULL ||            \
-                           RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
-                               RB_COLOR(tmp, field) = RB_RED;          \
-                               elm = parent;                           \
-                               parent = RB_PARENT(elm, field);         \
-                       } else {                                        \
-                               if (RB_LEFT(tmp, field) == NULL ||      \
-                                   RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
-                                       struct type *oright;            \
-                                       if ((oright = RB_RIGHT(tmp, field)) \
-                                           != NULL)                    \
-                                               RB_COLOR(oright, field) = RB_BLACK;\
-                                       RB_COLOR(tmp, field) = RB_RED;  \
-                                       RB_ROTATE_LEFT(head, tmp, oright, field);\
-                                       tmp = RB_LEFT(parent, field);   \
-                               }                                       \
-                               RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
-                               RB_COLOR(parent, field) = RB_BLACK;     \
-                               if (RB_LEFT(tmp, field))                \
-                                       RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
-                               RB_ROTATE_RIGHT(head, parent, tmp, field);\
-                               elm = RB_ROOT(head);                    \
-                               break;                                  \
-                       }                                               \
-               }                                                       \
-       }                                                               \
-       if (elm)                                                        \
-               RB_COLOR(elm, field) = RB_BLACK;                        \
-}
-
-#define RB_GENERATE_REMOVE(name, type, field, attr)                    \
-attr struct type *                                                     \
-name##_RB_REMOVE(struct name *head, struct type *elm)                  \
-{                                                                      \
-       struct type *child, *parent, *old = elm;                        \
-       int color;                                                      \
-       if (RB_LEFT(elm, field) == NULL)                                \
-               child = RB_RIGHT(elm, field);                           \
-       else if (RB_RIGHT(elm, field) == NULL)                          \
-               child = RB_LEFT(elm, field);                            \
-       else {                                                          \
-               struct type *left;                                      \
-               elm = RB_RIGHT(elm, field);                             \
-               while ((left = RB_LEFT(elm, field)) != NULL)            \
-                       elm = left;                                     \
-               child = RB_RIGHT(elm, field);                           \
-               parent = RB_PARENT(elm, field);                         \
-               color = RB_COLOR(elm, field);                           \
-               if (child)                                              \
-                       RB_PARENT(child, field) = parent;               \
-               if (parent) {                                           \
-                       if (RB_LEFT(parent, field) == elm)              \
-                               RB_LEFT(parent, field) = child;         \
-                       else                                            \
-                               RB_RIGHT(parent, field) = child;        \
-                       RB_AUGMENT(parent);                             \
-               } else                                                  \
-                       RB_ROOT(head) = child;                          \
-               if (RB_PARENT(elm, field) == old)                       \
-                       parent = elm;                                   \
-               (elm)->field = (old)->field;                            \
-               if (RB_PARENT(old, field)) {                            \
-                       if (RB_LEFT(RB_PARENT(old, field), field) == old)\
-                               RB_LEFT(RB_PARENT(old, field), field) = elm;\
-                       else                                            \
-                               RB_RIGHT(RB_PARENT(old, field), field) = elm;\
-                       RB_AUGMENT(RB_PARENT(old, field));              \
-               } else                                                  \
-                       RB_ROOT(head) = elm;                            \
-               RB_PARENT(RB_LEFT(old, field), field) = elm;            \
-               if (RB_RIGHT(old, field))                               \
-                       RB_PARENT(RB_RIGHT(old, field), field) = elm;   \
-               if (parent) {                                           \
-                       left = parent;                                  \
-                       do {                                            \
-                               RB_AUGMENT(left);                       \
-                       } while ((left = RB_PARENT(left, field)) != NULL); \
-               }                                                       \
-               goto color;                                             \
-       }                                                               \
-       parent = RB_PARENT(elm, field);                                 \
-       color = RB_COLOR(elm, field);                                   \
-       if (child)                                                      \
-               RB_PARENT(child, field) = parent;                       \
-       if (parent) {                                                   \
-               if (RB_LEFT(parent, field) == elm)                      \
-                       RB_LEFT(parent, field) = child;                 \
-               else                                                    \
-                       RB_RIGHT(parent, field) = child;                \
-               RB_AUGMENT(parent);                                     \
-       } else                                                          \
-               RB_ROOT(head) = child;                                  \
-color:                                                                 \
-       if (color == RB_BLACK)                                          \
-               name##_RB_REMOVE_COLOR(head, parent, child);            \
-       return (old);                                                   \
-}                                                                      \
-
-#define RB_GENERATE_INSERT(name, type, field, cmp, attr)               \
-/* Inserts a node into the RB tree */                                  \
-attr struct type *                                                     \
-name##_RB_INSERT(struct name *head, struct type *elm)                  \
-{                                                                      \
-       struct type *tmp;                                               \
-       struct type *parent = NULL;                                     \
-       int comp = 0;                                                   \
-       tmp = RB_ROOT(head);                                            \
-       while (tmp) {                                                   \
-               parent = tmp;                                           \
-               comp = (cmp)(elm, parent);                              \
-               if (comp < 0)                                           \
-                       tmp = RB_LEFT(tmp, field);                      \
-               else if (comp > 0)                                      \
-                       tmp = RB_RIGHT(tmp, field);                     \
-               else                                                    \
-                       return (tmp);                                   \
-       }                                                               \
-       RB_SET(elm, parent, field);                                     \
-       if (parent != NULL) {                                           \
-               if (comp < 0)                                           \
-                       RB_LEFT(parent, field) = elm;                   \
-               else                                                    \
-                       RB_RIGHT(parent, field) = elm;                  \
-               RB_AUGMENT(parent);                                     \
-       } else                                                          \
-               RB_ROOT(head) = elm;                                    \
-       name##_RB_INSERT_COLOR(head, elm);                              \
-       return (NULL);                                                  \
-}
-
-#define RB_GENERATE_FIND(name, type, field, cmp, attr)                 \
-/* Finds the node with the same key as elm */                          \
-attr struct type *                                                     \
-name##_RB_FIND(struct name *head, struct type *elm)                    \
-{                                                                      \
-       struct type *tmp = RB_ROOT(head);                               \
-       int comp;                                                       \
-       while (tmp) {                                                   \
-               comp = cmp(elm, tmp);                                   \
-               if (comp < 0)                                           \
-                       tmp = RB_LEFT(tmp, field);                      \
-               else if (comp > 0)                                      \
-                       tmp = RB_RIGHT(tmp, field);                     \
-               else                                                    \
-                       return (tmp);                                   \
-       }                                                               \
-       return (NULL);                                                  \
-}
-
-#define RB_GENERATE_NFIND(name, type, field, cmp, attr)                        \
-/* Finds the first node greater than or equal to the search key */     \
-attr struct type *                                                     \
-name##_RB_NFIND(struct name *head, struct type *elm)                   \
-{                                                                      \
-       struct type *tmp = RB_ROOT(head);                               \
-       struct type *res = NULL;                                        \
-       int comp;                                                       \
-       while (tmp) {                                                   \
-               comp = cmp(elm, tmp);                                   \
-               if (comp < 0) {                                         \
-                       res = tmp;                                      \
-                       tmp = RB_LEFT(tmp, field);                      \
-               }                                                       \
-               else if (comp > 0)                                      \
-                       tmp = RB_RIGHT(tmp, field);                     \
-               else                                                    \
-                       return (tmp);                                   \
-       }                                                               \
-       return (res);                                                   \
-}
-
-#define RB_GENERATE_NEXT(name, type, field, attr)                      \
-/* ARGSUSED */                                                         \
-attr struct type *                                                     \
-name##_RB_NEXT(struct type *elm)                                       \
-{                                                                      \
-       if (RB_RIGHT(elm, field)) {                                     \
-               elm = RB_RIGHT(elm, field);                             \
-               while (RB_LEFT(elm, field))                             \
-                       elm = RB_LEFT(elm, field);                      \
-       } else {                                                        \
-               if (RB_PARENT(elm, field) &&                            \
-                   (elm == RB_LEFT(RB_PARENT(elm, field), field)))     \
-                       elm = RB_PARENT(elm, field);                    \
-               else {                                                  \
-                       while (RB_PARENT(elm, field) &&                 \
-                           (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
-                               elm = RB_PARENT(elm, field);            \
-                       elm = RB_PARENT(elm, field);                    \
-               }                                                       \
-       }                                                               \
-       return (elm);                                                   \
-}
-
-#define RB_GENERATE_PREV(name, type, field, attr)                      \
-/* ARGSUSED */                                                         \
-attr struct type *                                                     \
-name##_RB_PREV(struct type *elm)                                       \
-{                                                                      \
-       if (RB_LEFT(elm, field)) {                                      \
-               elm = RB_LEFT(elm, field);                              \
-               while (RB_RIGHT(elm, field))                            \
-                       elm = RB_RIGHT(elm, field);                     \
-       } else {                                                        \
-               if (RB_PARENT(elm, field) &&                            \
-                   (elm == RB_RIGHT(RB_PARENT(elm, field), field)))    \
-                       elm = RB_PARENT(elm, field);                    \
-               else {                                                  \
-                       while (RB_PARENT(elm, field) &&                 \
-                           (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
-                               elm = RB_PARENT(elm, field);            \
-                       elm = RB_PARENT(elm, field);                    \
-               }                                                       \
-       }                                                               \
-       return (elm);                                                   \
-}
-
-#define RB_GENERATE_MINMAX(name, type, field, attr)                    \
-attr struct type *                                                     \
-name##_RB_MINMAX(struct name *head, int val)                           \
-{                                                                      \
-       struct type *tmp = RB_ROOT(head);                               \
-       struct type *parent = NULL;                                     \
-       while (tmp) {                                                   \
-               parent = tmp;                                           \
-               if (val < 0)                                            \
-                       tmp = RB_LEFT(tmp, field);                      \
-               else                                                    \
-                       tmp = RB_RIGHT(tmp, field);                     \
-       }                                                               \
-       return (parent);                                                \
-}
-
-#define RB_NEGINF      -1
-#define RB_INF 1
-
-#define RB_INSERT(name, x, y)  name##_RB_INSERT(x, y)
-#define RB_REMOVE(name, x, y)  name##_RB_REMOVE(x, y)
-#define RB_FIND(name, x, y)    name##_RB_FIND(x, y)
-#define RB_NFIND(name, x, y)   name##_RB_NFIND(x, y)
-#define RB_NEXT(name, x, y)    name##_RB_NEXT(y)
-#define RB_PREV(name, x, y)    name##_RB_PREV(y)
-#define RB_MIN(name, x)                name##_RB_MINMAX(x, RB_NEGINF)
-#define RB_MAX(name, x)                name##_RB_MINMAX(x, RB_INF)
-
-#define RB_FOREACH(x, name, head)                                      \
-       for ((x) = RB_MIN(name, head);                                  \
-            (x) != NULL;                                               \
-            (x) = name##_RB_NEXT(x))
-
-#define RB_FOREACH_FROM(x, name, y)                                    \
-       for ((x) = (y);                                                 \
-           ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \
-            (x) = (y))
-
-#define RB_FOREACH_SAFE(x, name, head, y)                              \
-       for ((x) = RB_MIN(name, head);                                  \
-           ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \
-            (x) = (y))
-
-#define RB_FOREACH_REVERSE(x, name, head)                              \
-       for ((x) = RB_MAX(name, head);                                  \
-            (x) != NULL;                                               \
-            (x) = name##_RB_PREV(x))
-
-#define RB_FOREACH_REVERSE_FROM(x, name, y)                            \
-       for ((x) = (y);                                                 \
-           ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \
-            (x) = (y))
-
-#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                      \
-       for ((x) = RB_MAX(name, head);                                  \
-           ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \
-            (x) = (y))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_TREE_H_ */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..78b233d
--- /dev/null
@@ -0,0 +1,10 @@
+
+#LIBRARY
+include_directories(.)
+aux_source_directory(. LWEXT4_SRC)
+add_library(lwext4  ${LWEXT4_SRC})
+if  (DEFINED SIZE)
+    add_custom_target(lib_size ALL DEPENDS lwext4 COMMAND ${SIZE} liblwext4.a)
+else()
+
+endif()
diff --git a/src/ext4.c b/src/ext4.c
new file mode 100644 (file)
index 0000000..38ec6d4
--- /dev/null
@@ -0,0 +1,2935 @@
+/*
+ * 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.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_xattr.h"
+#include "ext4_journal.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;
+
+       struct jbd_fs jbd_fs;
+       struct jbd_journal jbd_journal;
+};
+
+/**@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_sblock *sb = &enode->fs->sb;
+
+       /* Check if node is directory */
+       if (!ext4_inode_is_type(sb, enode->inode, EXT4_INODE_MODE_DIRECTORY)) {
+               *has_children = false;
+               return EOK;
+       }
+
+       struct ext4_dir_iter 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.curr != NULL) {
+               if (ext4_dir_en_get_inode(it.curr) != 0) {
+                       uint16_t nsize;
+                       nsize = ext4_dir_en_get_name_len(sb, it.curr);
+                       if (!ext4_is_dots(it.curr->name, nsize)) {
+                               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 *ch, const char *n,
+                    uint32_t len, bool rename)
+{
+       /* Check maximum name length */
+       if (len > EXT4_DIRECTORY_FILENAME_LEN)
+               return EINVAL;
+
+       /* Add entry to parent directory */
+       int r = ext4_dir_add_entry(parent, n, len, ch);
+       if (r != EOK)
+               return r;
+
+       /* Fill new dir -> add '.' and '..' entries.
+        * Also newly allocated inode should have 0 link count.
+        */
+
+       bool is_dir = ext4_inode_is_type(&mp->fs.sb, ch->inode,
+                              EXT4_INODE_MODE_DIRECTORY);
+       if (is_dir && !rename) {
+
+#if CONFIG_DIR_INDEX_ENABLE
+               /* Initialize directory index if supported */
+               if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
+                       r = ext4_dir_dx_init(ch, parent);
+                       if (r != EOK)
+                               return r;
+
+                       ext4_inode_set_flag(ch->inode, EXT4_INODE_FLAG_INDEX);
+                       ch->dirty = true;
+               } else
+#endif
+               {
+                       r = ext4_dir_add_entry(ch, ".", strlen("."), ch);
+                       if (r != EOK) {
+                               ext4_dir_remove_entry(parent, n, strlen(n));
+                               return r;
+                       }
+
+                       r = ext4_dir_add_entry(ch, "..", strlen(".."), parent);
+                       if (r != EOK) {
+                               ext4_dir_remove_entry(parent, n, strlen(n));
+                               ext4_dir_remove_entry(ch, ".", strlen("."));
+                               return r;
+                       }
+               }
+
+               /*New empty directory. Two links (. and ..) */
+               ext4_inode_set_links_cnt(ch->inode, 2);
+               ext4_fs_inode_links_count_inc(parent);
+               ch->dirty = true;
+               parent->dirty = true;
+               return r;
+       }
+       /*
+        * In case we want to rename a directory,
+        * we reset the original '..' pointer.
+        */
+       if (is_dir) {
+               bool idx;
+               idx = ext4_inode_has_flag(ch->inode, EXT4_INODE_FLAG_INDEX);
+               struct ext4_dir_search_result res;
+               if (!idx) {
+                       r = ext4_dir_find_entry(&res, ch, "..", strlen(".."));
+                       if (r != EOK)
+                               return EIO;
+
+                       ext4_dir_en_set_inode(res.dentry, parent->index);
+                       ext4_trans_set_block_dirty(res.block.buf);
+                       r = ext4_dir_destroy_result(ch, &res);
+                       if (r != EOK)
+                               return r;
+
+               } else {
+#if CONFIG_DIR_INDEX_ENABLE
+                       r = ext4_dir_dx_reset_parent_inode(ch, parent->index);
+                       if (r != EOK)
+                               return r;
+
+#endif
+               }
+
+               ext4_fs_inode_links_count_inc(parent);
+               parent->dirty = true;
+       }
+       if (!rename) {
+               ext4_fs_inode_links_count_inc(ch);
+               ch->dirty = true;
+       }
+
+       return r;
+}
+
+static int ext4_unlink(struct ext4_mountpoint *mp,
+                      struct ext4_inode_ref *parent,
+                      struct ext4_inode_ref *child, const char *name,
+                      uint32_t name_len)
+{
+       bool has_children;
+       int rc = ext4_has_children(&has_children, child);
+       if (rc != EOK)
+               return rc;
+
+       /* Cannot unlink non-empty node */
+       if (has_children)
+               return ENOTEMPTY;
+
+       /* 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,
+                                        EXT4_INODE_MODE_DIRECTORY);
+
+       /* If directory - handle links from parent */
+       if (is_dir) {
+               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,
+        *     (uint32_t) now);
+        */
+       if (ext4_inode_get_links_cnt(child->inode)) {
+               ext4_fs_inode_links_count_dec(child);
+               child->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_bcache_cleanup(bc);
+               ext4_block_fini(bd);
+               if (mp->cache_dynamic) {
+                       ext4_bcache_fini_dynamic(bc);
+                       free(bc);
+               }
+               return r;
+       }
+       bd->fs = &mp->fs;
+
+       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)
+               goto Finish;
+
+       mp->mounted = 0;
+
+       ext4_bcache_cleanup(mp->fs.bdev->bc);
+       if (mp->cache_dynamic) {
+               ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
+               free(mp->fs.bdev->bc);
+       }
+       r = ext4_block_fini(mp->fs.bdev);
+Finish:
+       mp->fs.bdev->fs = NULL;
+       return r;
+}
+
+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 NULL;
+}
+
+__unused
+static int __ext4_journal_start(const char *mount_point)
+{
+       int r = EOK;
+       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+       if (!mp)
+               return ENOENT;
+
+       if (ext4_sb_feature_com(&mp->fs.sb,
+                               EXT4_FCOM_HAS_JOURNAL)) {
+               r = jbd_get_fs(&mp->fs, &mp->jbd_fs);
+               if (r != EOK)
+                       goto Finish;
+
+               r = jbd_journal_start(&mp->jbd_fs, &mp->jbd_journal);
+               if (r != EOK) {
+                       mp->jbd_fs.dirty = false;
+                       jbd_put_fs(&mp->jbd_fs);
+                       goto Finish;
+               }
+               mp->fs.jbd_fs = &mp->jbd_fs;
+               mp->fs.jbd_journal = &mp->jbd_journal;
+       }
+Finish:
+       return r;
+}
+
+__unused
+static int __ext4_journal_stop(const char *mount_point)
+{
+       int r = EOK;
+       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+       if (!mp)
+               return ENOENT;
+
+       if (ext4_sb_feature_com(&mp->fs.sb,
+                               EXT4_FCOM_HAS_JOURNAL)) {
+               r = jbd_journal_stop(&mp->jbd_journal);
+               if (r != EOK) {
+                       mp->jbd_fs.dirty = false;
+                       jbd_put_fs(&mp->jbd_fs);
+                       mp->fs.jbd_journal = NULL;
+                       mp->fs.jbd_fs = NULL;
+                       goto Finish;
+               }
+
+               r = jbd_put_fs(&mp->jbd_fs);
+               if (r != EOK) {
+                       mp->fs.jbd_journal = NULL;
+                       mp->fs.jbd_fs = NULL;
+                       goto Finish;
+               }
+
+               mp->fs.jbd_journal = NULL;
+               mp->fs.jbd_fs = NULL;
+       }
+Finish:
+       return r;
+}
+
+__unused
+static int __ext4_recover(const char *mount_point)
+{
+       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+       if (!mp)
+               return ENOENT;
+
+       int r = ENOTSUP;
+       EXT4_MP_LOCK(mp);
+       if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL)) {
+               struct jbd_fs *jbd_fs = calloc(1, sizeof(struct jbd_fs));
+               if (!jbd_fs) {
+                        r = ENOMEM;
+                        goto Finish;
+               }
+
+
+               r = jbd_get_fs(&mp->fs, jbd_fs);
+               if (r != EOK) {
+                       free(jbd_fs);
+                       goto Finish;
+               }
+
+               r = jbd_recover(jbd_fs);
+               jbd_put_fs(jbd_fs);
+               free(jbd_fs);
+       }
+
+
+Finish:
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+__unused
+static int __ext4_trans_start(struct ext4_mountpoint *mp)
+{
+       int r = EOK;
+       if (mp->fs.jbd_journal && !mp->fs.curr_trans) {
+               struct jbd_journal *journal = mp->fs.jbd_journal;
+               struct jbd_trans *trans;
+               trans = jbd_journal_new_trans(journal);
+               if (!trans) {
+                       r = ENOMEM;
+                       goto Finish;
+               }
+               mp->fs.curr_trans = trans;
+       }
+Finish:
+       return r;
+}
+
+__unused
+static int __ext4_trans_stop(struct ext4_mountpoint *mp)
+{
+       int r = EOK;
+       if (mp->fs.jbd_journal && mp->fs.curr_trans) {
+               struct jbd_journal *journal = mp->fs.jbd_journal;
+               struct jbd_trans *trans = mp->fs.curr_trans;
+               r = jbd_journal_commit_trans(journal, trans);
+               mp->fs.curr_trans = NULL;
+       }
+       return r;
+}
+
+__unused
+static void __ext4_trans_abort(struct ext4_mountpoint *mp)
+{
+       if (mp->fs.jbd_journal && mp->fs.curr_trans) {
+               struct jbd_journal *journal = mp->fs.jbd_journal;
+               struct jbd_trans *trans = mp->fs.curr_trans;
+               jbd_journal_free_trans(journal, trans, true);
+               mp->fs.curr_trans = NULL;
+       }
+}
+
+int ext4_journal_start(const char *mount_point __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       r = __ext4_journal_start(mount_point);
+#endif
+       return r;
+}
+
+int ext4_journal_stop(const char *mount_point __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       r = __ext4_journal_stop(mount_point);
+#endif
+       return r;
+}
+
+int ext4_recover(const char *mount_point __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       r = __ext4_recover(mount_point);
+#endif
+       return r;
+}
+
+static int ext4_trans_start(struct ext4_mountpoint *mp __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       r = __ext4_trans_start(mp);
+#endif
+       return r;
+}
+
+static int ext4_trans_stop(struct ext4_mountpoint *mp __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       r = __ext4_trans_stop(mp);
+#endif
+       return r;
+}
+
+static void ext4_trans_abort(struct ext4_mountpoint *mp __unused)
+{
+#if CONFIG_JOURNALING_ENABLE
+       __ext4_trans_abort(mp);
+#endif
+}
+
+
+int ext4_mount_point_stats(const char *mount_point,
+                          struct ext4_mount_stats *stats)
+{
+       struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+
+       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 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;
+}
+
+static int ext4_trunc_inode(struct ext4_mountpoint *mp,
+                           uint32_t index, uint64_t new_size)
+{
+       int r = EOK;
+       struct ext4_fs *const fs = &mp->fs;
+       struct ext4_inode_ref inode_ref;
+       uint64_t inode_size;
+       bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans;
+       r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
+       if (r != EOK)
+               return r;
+
+       inode_size = ext4_inode_get_size(&fs->sb, inode_ref.inode);
+       ext4_fs_put_inode_ref(&inode_ref);
+       if (has_trans)
+               ext4_trans_stop(mp);
+
+       while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) {
+
+               inode_size -= CONFIG_MAX_TRUNCATE_SIZE;
+
+               ext4_trans_start(mp);
+               r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
+               if (r != EOK) {
+                       ext4_trans_abort(mp);
+                       break;
+               }
+               r = ext4_fs_truncate_inode(&inode_ref, inode_size);
+               if (r != EOK)
+                       ext4_fs_put_inode_ref(&inode_ref);
+               else
+                       r = ext4_fs_put_inode_ref(&inode_ref);
+
+               if (r != EOK) {
+                       ext4_trans_abort(mp);
+                       goto Finish;
+               } else
+                       ext4_trans_stop(mp);
+       }
+
+       if (inode_size > new_size) {
+
+               inode_size = new_size;
+
+               ext4_trans_start(mp);
+               r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
+               if (r != EOK) {
+                       ext4_trans_abort(mp);
+                       goto Finish;
+               }
+               r = ext4_fs_truncate_inode(&inode_ref, inode_size);
+               if (r != EOK)
+                       ext4_fs_put_inode_ref(&inode_ref);
+               else
+                       r = ext4_fs_put_inode_ref(&inode_ref);
+
+               if (r != EOK)
+                       ext4_trans_abort(mp);
+               else
+                       ext4_trans_stop(mp);
+
+       }
+
+Finish:
+
+       if (has_trans)
+               ext4_trans_start(mp);
+
+       return r;
+}
+
+static int ext4_trunc_dir(struct ext4_mountpoint *mp,
+                         struct ext4_inode_ref *parent,
+                         struct ext4_inode_ref *dir)
+{
+       int r = EOK;
+       bool is_dir = ext4_inode_is_type(&mp->fs.sb, dir->inode,
+                       EXT4_INODE_MODE_DIRECTORY);
+       uint32_t block_size = ext4_sb_get_block_size(&mp->fs.sb);
+       if (!is_dir)
+               return EINVAL;
+
+#if CONFIG_DIR_INDEX_ENABLE
+       /* Initialize directory index if supported */
+       if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
+               r = ext4_dir_dx_init(dir, parent);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_trunc_inode(mp, dir->index,
+                                    EXT4_DIR_DX_INIT_BCNT * block_size);
+               if (r != EOK)
+                       return r;
+       } else
+#endif
+       {
+               r = ext4_trunc_inode(mp, dir->index, block_size);
+               if (r != EOK)
+                       return r;
+       }
+
+       return ext4_fs_truncate_inode(dir, 0);
+}
+
+/*
+ * NOTICE: if filetype is equal to EXT4_DIRENTRY_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 ftype, uint32_t *parent_inode,
+                             uint32_t *name_off)
+{
+       bool is_goal = false;
+       uint32_t imode = EXT4_INODE_MODE_DIRECTORY;
+       uint32_t next_inode;
+
+       int r;
+       int len;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       struct ext4_dir_search_result result;
+       struct ext4_inode_ref ref;
+
+       f->mp = 0;
+
+       if (!mp)
+               return ENOENT;
+
+       struct ext4_fs *const fs = &mp->fs;
+       struct ext4_sblock *const sb = &mp->fs.sb;
+
+       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(fs, EXT4_INODE_ROOT_INDEX, &ref);
+       if (r != EOK)
+               return r;
+
+       if (parent_inode)
+               *parent_inode = ref.index;
+
+       if (flags & O_CREAT)
+               ext4_trans_start(mp);
+
+       len = ext4_path_check(path, &is_goal);
+       while (1) {
+
+               len = ext4_path_check(path, &is_goal);
+               if (!len) {
+                       /*If root open was request.*/
+                       if (ftype == EXT4_DE_DIR || ftype == EXT4_DE_UNKNOWN)
+                               if (is_goal)
+                                       break;
+
+                       r = ENOENT;
+                       break;
+               }
+
+               r = ext4_dir_find_entry(&result, &ref, path, len);
+               if (r != EOK) {
+
+                       /*Destroy last result*/
+                       ext4_dir_destroy_result(&ref, &result);
+                       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(fs, &child_ref,
+                                       is_goal ? ftype : EXT4_DE_DIR);
+                       if (r != EOK)
+                               break;
+
+
+                       /*Link with root dir.*/
+                       r = ext4_link(mp, &ref, &child_ref, path, len, false);
+                       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_en_get_inode(result.dentry);
+               if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) {
+                       uint8_t t;
+                       t = ext4_dir_en_get_inode_type(sb, result.dentry);
+                       imode = ext4_fs_correspond_inode_mode(t);
+               } else {
+                       struct ext4_inode_ref child_ref;
+                       r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref);
+                       if (r != EOK)
+                               break;
+
+                       imode = ext4_inode_type(sb, child_ref.inode);
+                       ext4_fs_put_inode_ref(&child_ref);
+               }
+
+               r = ext4_dir_destroy_result(&ref, &result);
+               if (r != EOK)
+                       break;
+
+               /*If expected file error*/
+               if (imode != EXT4_INODE_MODE_DIRECTORY && !is_goal) {
+                       r = ENOENT;
+                       break;
+               }
+               if (ftype != EXT4_DE_UNKNOWN) {
+                       bool df = imode != ext4_fs_correspond_inode_mode(ftype);
+                       if (df && is_goal) {
+                               r = ENOENT;
+                               break;
+                       }
+               }
+
+               r = ext4_fs_put_inode_ref(&ref);
+               if (r != EOK)
+                       break;
+
+               r = ext4_fs_get_inode_ref(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) && (imode == EXT4_INODE_MODE_FILE)) {
+                       r = ext4_trunc_inode(mp, ref.index, 0);
+                       if (r != EOK) {
+                               ext4_fs_put_inode_ref(&ref);
+                               return r;
+                       }
+               }
+
+               f->mp = mp;
+               f->fsize = ext4_inode_get_size(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);
+       if (flags & O_CREAT) {
+               if (r == EOK)
+                       ext4_trans_stop(mp);
+               else
+                       ext4_trans_abort(mp);
+
+       }
+
+       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_DE_REG_FILE;
+       else
+               filetype = EXT4_DE_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 rename)
+{
+       bool is_goal = false;
+       uint32_t inode_mode = EXT4_INODE_MODE_DIRECTORY;
+       uint32_t next_inode;
+
+       int r;
+       int len;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       struct ext4_dir_search_result result;
+       struct ext4_inode_ref ref;
+
+       if (!mp)
+               return ENOENT;
+
+       struct ext4_fs *const fs = &mp->fs;
+       struct ext4_sblock *const sb = &mp->fs.sb;
+
+       /*Skip mount point*/
+       path += strlen(mp->name);
+
+       /*Load root*/
+       r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref);
+       if (r != EOK)
+               return r;
+
+       len = ext4_path_check(path, &is_goal);
+       while (1) {
+
+               len = ext4_path_check(path, &is_goal);
+               if (!len) {
+                       /*If root open was request.*/
+                       r = is_goal ? EINVAL : ENOENT;
+                       break;
+               }
+
+               r = ext4_dir_find_entry(&result, &ref, path, len);
+               if (r != EOK) {
+
+                       /*Destroy last result*/
+                       ext4_dir_destroy_result(&ref, &result);
+
+                       if (r != ENOENT || !is_goal)
+                               break;
+
+                       /*Link with root dir.*/
+                       r = ext4_link(mp, &ref, child_ref, path, len, rename);
+                       break;
+               } else if (r == EOK && is_goal) {
+                       /*Destroy last result*/
+                       ext4_dir_destroy_result(&ref, &result);
+                       r = EEXIST;
+                       break;
+               }
+
+               next_inode = result.dentry->inode;
+               if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) {
+                       uint8_t t;
+                       t = ext4_dir_en_get_inode_type(sb, result.dentry);
+                       inode_mode = ext4_fs_correspond_inode_mode(t);
+               } else {
+                       struct ext4_inode_ref child_ref;
+                       r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref);
+                       if (r != EOK)
+                               break;
+
+                       inode_mode = ext4_inode_type(sb, child_ref.inode);
+                       ext4_fs_put_inode_ref(&child_ref);
+               }
+
+               r = ext4_dir_destroy_result(&ref, &result);
+               if (r != EOK)
+                       break;
+
+               if (inode_mode != EXT4_INODE_MODE_DIRECTORY) {
+                       r = is_goal ? EEXIST : ENOENT;
+                       break;
+               }
+
+               r = ext4_fs_put_inode_ref(&ref);
+               if (r != EOK)
+                       break;
+
+               r = ext4_fs_get_inode_ref(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_remove_orig_reference(const char *path, uint32_t name_off,
+                                     struct ext4_inode_ref *parent_ref,
+                                     struct ext4_inode_ref *child_ref)
+{
+       bool is_goal;
+       int r;
+       int len;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+       if (!mp)
+               return ENOENT;
+
+       /*Set path*/
+       path += name_off;
+
+       len = ext4_path_check(path, &is_goal);
+
+       /* Remove entry from parent directory */
+       r = ext4_dir_remove_entry(parent_ref, path, len);
+       if (r != EOK)
+               goto Finish;
+
+       if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode,
+                              EXT4_INODE_MODE_DIRECTORY)) {
+               ext4_fs_inode_links_count_dec(parent_ref);
+               parent_ref->dirty = true;
+       }
+Finish:
+       return r;
+}
+
+int ext4_flink(const char *path, const char *hardlink_path)
+{
+       int r;
+       ext4_file f;
+       uint32_t name_off;
+       bool child_loaded = false;
+       uint32_t parent_inode, child_inode;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path);
+       struct ext4_inode_ref child_ref;
+
+       if (!mp)
+               return ENOENT;
+
+       /* Will that happen? Anyway return EINVAL for such case. */
+       if (mp != target_mp)
+               return EINVAL;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
+                              &parent_inode, &name_off);
+       if (r != EOK)
+               goto Finish;
+
+       child_inode = f.inode;
+       ext4_fclose(&f);
+
+       /*We have file to unlink. Load it.*/
+       r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
+       if (r != EOK)
+               goto Finish;
+
+       child_loaded = true;
+
+       /* Creating hardlink for directory is not allowed. */
+       if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode,
+                              EXT4_INODE_MODE_DIRECTORY)) {
+               r = EINVAL;
+               goto Finish;
+       }
+
+       r = ext4_create_hardlink(hardlink_path, &child_ref, false);
+
+Finish:
+       if (child_loaded)
+               ext4_fs_put_inode_ref(&child_ref);
+
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+
+}
+
+int ext4_frename(const char *path, const char *new_path)
+{
+       int r;
+       ext4_file f;
+       uint32_t name_off;
+       bool parent_loaded = false, child_loaded = false;
+       uint32_t parent_inode, child_inode;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       struct ext4_inode_ref child_ref, parent_ref;
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
+                               &parent_inode, &name_off);
+       if (r != EOK)
+               goto Finish;
+
+       child_inode = f.inode;
+       ext4_fclose(&f);
+
+       /*Load parent*/
+       r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref);
+       if (r != EOK)
+               goto Finish;
+
+       parent_loaded = true;
+
+       /*We have file to unlink. Load it.*/
+       r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
+       if (r != EOK)
+               goto Finish;
+
+       child_loaded = true;
+
+       r = ext4_create_hardlink(new_path, &child_ref, true);
+       if (r != EOK)
+               goto Finish;
+
+       r = ext4_remove_orig_reference(path, name_off, &parent_ref, &child_ref);
+       if (r != EOK)
+               goto Finish;
+
+Finish:
+       if (parent_loaded)
+               ext4_fs_put_inode_ref(&parent_ref);
+
+       if (child_loaded)
+               ext4_fs_put_inode_ref(&child_ref);
+
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       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);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN,
+                              &parent_inode, &name_off);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       /*Load parent*/
+       r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               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_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+       /* We do not allow opening files here. */
+       if (ext4_inode_type(&mp->fs.sb, child.inode) ==
+           EXT4_INODE_MODE_DIRECTORY) {
+               ext4_fs_put_inode_ref(&parent);
+               ext4_fs_put_inode_ref(&child);
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       /*Link count will be zero, the inode should be freed. */
+       if (ext4_inode_get_links_cnt(child.inode) == 1) {
+               ext4_block_cache_write_back(mp->fs.bdev, 1);
+               r = ext4_trunc_inode(mp, child.index, 0);
+               if (r != EOK) {
+                       ext4_fs_put_inode_ref(&parent);
+                       ext4_fs_put_inode_ref(&child);
+                       ext4_trans_abort(mp);
+                       EXT4_MP_UNLOCK(mp);
+                       return r;
+               }
+               ext4_block_cache_write_back(mp->fs.bdev, 0);
+       }
+
+       /*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_cnt(child.inode)) {
+               ext4_inode_set_del_time(child.inode, -1L);
+
+               r = ext4_fs_free_inode(&child);
+               if (r != EOK)
+                       goto Finish;
+       }
+
+Finish:
+       ext4_fs_put_inode_ref(&child);
+       ext4_fs_put_inode_ref(&parent);
+
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_fill_raw_inode(const char *path, uint32_t *ret_ino,
+                       struct ext4_inode *inode)
+{
+       int r;
+       ext4_file f;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       uint32_t ino;
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK) {
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       ino = f.inode;
+       ext4_fclose(&f);
+
+       /*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);
+
+       if (ret_ino)
+               *ret_ino = ino;
+
+       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)
+{
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       int r;
+       int filetype;
+
+       if (!mp)
+               return ENOENT;
+
+        filetype = EXT4_DE_REG_FILE;
+
+       EXT4_MP_LOCK(mp);
+
+       ext4_block_cache_write_back(mp->fs.bdev, 1);
+       r = ext4_generic_open2(f, path, flags, filetype, NULL, NULL);
+       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;
+}
+
+static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
+{
+       struct ext4_inode_ref ref;
+       int r;
+
+
+       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_trunc_inode(f->mp, ref.index, 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);
+       return r;
+
+}
+
+int ext4_ftruncate(ext4_file *f, uint64_t size)
+{
+       int r;
+       ext4_assert(f && f->mp);
+
+       if (f->flags & O_RDONLY)
+               return EPERM;
+
+       EXT4_MP_LOCK(f->mp);
+
+       ext4_trans_start(f->mp);
+       r = ext4_ftruncate_no_lock(f, size);
+       if (r != EOK)
+               ext4_trans_abort(f->mp);
+       else
+               ext4_trans_stop(f->mp);
+
+       EXT4_MP_UNLOCK(f->mp);
+       return r;
+}
+
+int ext4_fread(ext4_file *f, void *buf, size_t size, size_t *rcnt)
+{
+       uint32_t unalg;
+       uint32_t iblock_idx;
+       uint32_t iblock_last;
+       uint32_t block_size;
+
+       ext4_fsblk_t fblock;
+       ext4_fsblk_t fblock_start;
+       uint32_t fblock_count;
+
+       uint8_t *u8_buf = buf;
+       int r;
+       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);
+
+       struct ext4_fs *const fs = &f->mp->fs;
+       struct ext4_sblock *const sb = &f->mp->fs.sb;
+
+       if (rcnt)
+               *rcnt = 0;
+
+       r = ext4_fs_get_inode_ref(fs, f->inode, &ref);
+       if (r != EOK) {
+               EXT4_MP_UNLOCK(f->mp);
+               return r;
+       }
+
+       /*Sync file size*/
+       f->fsize = ext4_inode_get_size(sb, ref.inode);
+
+       block_size = ext4_sb_get_block_size(sb);
+       size = ((uint64_t)size > (f->fsize - f->fpos))
+               ? ((size_t)(f->fsize - f->fpos)) : size;
+
+       iblock_idx = (uint32_t)((f->fpos) / block_size);
+       iblock_last = (uint32_t)((f->fpos + size) / block_size);
+       unalg = (f->fpos) % block_size;
+
+       /*If the size of symlink is smaller than 60 bytes*/
+       bool softlink;
+       softlink = ext4_inode_is_type(sb, ref.inode, EXT4_INODE_MODE_SOFTLINK);
+       if (softlink && f->fsize < sizeof(ref.inode->blocks)
+                    && !ext4_inode_get_blocks_count(sb, ref.inode)) {
+
+               char *content = (char *)ref.inode->blocks;
+               if (f->fpos < f->fsize) {
+                       size_t len = size;
+                       if (unalg + size > (uint32_t)f->fsize)
+                               len = (uint32_t)f->fsize - unalg;
+                       memcpy(buf, content + unalg, len);
+                       if (rcnt)
+                               *rcnt = len;
+
+               }
+
+               r = EOK;
+               goto Finish;
+       }
+
+       if (unalg) {
+               size_t len =  size;
+               if (size > (block_size - unalg))
+                       len = block_size - unalg;
+
+               r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true);
+               if (r != EOK)
+                       goto Finish;
+
+               /* Do we get an unwritten range? */
+               if (fblock != 0) {
+                       uint64_t off = fblock * block_size + unalg;
+                       r = ext4_block_readbytes(f->mp->fs.bdev, off, u8_buf, len);
+                       if (r != EOK)
+                               goto Finish;
+
+               } else {
+                       /* Yes, we do. */
+                       memset(u8_buf, 0, len);
+               }
+
+               u8_buf += len;
+               size -= len;
+               f->fpos += len;
+
+               if (rcnt)
+                       *rcnt += len;
+
+               iblock_idx++;
+       }
+
+       fblock_start = 0;
+       fblock_count = 0;
+       while (size >= block_size) {
+               while (iblock_idx < iblock_last) {
+                       r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx,
+                                                      &fblock, true);
+                       if (r != EOK)
+                               goto Finish;
+
+                       iblock_idx++;
+
+                       if (!fblock_start)
+                               fblock_start = fblock;
+
+                       if ((fblock_start + fblock_count) != fblock)
+                               break;
+
+                       fblock_count++;
+               }
+
+               r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
+                                          fblock_count);
+               if (r != EOK)
+                       goto Finish;
+
+               size -= block_size * fblock_count;
+               u8_buf += block_size * fblock_count;
+               f->fpos += block_size * fblock_count;
+
+               if (rcnt)
+                       *rcnt += block_size * fblock_count;
+
+               fblock_start = fblock;
+               fblock_count = 1;
+       }
+
+       if (size) {
+               uint64_t off;
+               r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true);
+               if (r != EOK)
+                       goto Finish;
+
+               off = fblock * block_size;
+               r = ext4_block_readbytes(f->mp->fs.bdev, off, u8_buf, size);
+               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, size_t size, size_t *wcnt)
+{
+       uint32_t unalg;
+       uint32_t iblk_idx;
+       uint32_t iblock_last;
+       uint32_t ifile_blocks;
+       uint32_t block_size;
+
+       uint32_t fblock_count;
+       ext4_fsblk_t fblk;
+       ext4_fsblk_t fblock_start;
+
+       struct ext4_inode_ref ref;
+       const uint8_t *u8_buf = buf;
+       int r, rr = EOK;
+
+       ext4_assert(f && f->mp);
+
+       if (f->flags & O_RDONLY)
+               return EPERM;
+
+       if (!size)
+               return EOK;
+
+       EXT4_MP_LOCK(f->mp);
+       ext4_trans_start(f->mp);
+
+       struct ext4_fs *const fs = &f->mp->fs;
+       struct ext4_sblock *const sb = &f->mp->fs.sb;
+
+       if (wcnt)
+               *wcnt = 0;
+
+       r = ext4_fs_get_inode_ref(fs, f->inode, &ref);
+       if (r != EOK) {
+               ext4_trans_abort(f->mp);
+               EXT4_MP_UNLOCK(f->mp);
+               return r;
+       }
+
+       /*Sync file size*/
+       f->fsize = ext4_inode_get_size(sb, ref.inode);
+       block_size = ext4_sb_get_block_size(sb);
+
+       iblock_last = (uint32_t)((f->fpos + size) / block_size);
+       iblk_idx = (uint32_t)(f->fpos / block_size);
+       ifile_blocks = (uint32_t)((f->fsize + block_size - 1) / block_size);
+
+       unalg = (f->fpos) % block_size;
+
+       if (unalg) {
+               size_t len =  size;
+               uint64_t off;
+               if (size > (block_size - unalg))
+                       len = block_size - unalg;
+
+               r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk);
+               if (r != EOK)
+                       goto Finish;
+
+               off = fblk * block_size + unalg;
+               r = ext4_block_writebytes(f->mp->fs.bdev, off, u8_buf, len);
+               if (r != EOK)
+                       goto Finish;
+
+               u8_buf += len;
+               size -= len;
+               f->fpos += len;
+
+               if (wcnt)
+                       *wcnt += len;
+
+               iblk_idx++;
+       }
+
+       /*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_count = 0;
+       while (size >= block_size) {
+
+               while (iblk_idx < iblock_last) {
+                       if (iblk_idx < ifile_blocks) {
+                               r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx,
+                                                               &fblk);
+                               if (r != EOK)
+                                       goto Finish;
+                       } else {
+                               rr = ext4_fs_append_inode_dblk(&ref, &fblk,
+                                                              &iblk_idx);
+                               if (rr != EOK) {
+                                       /* Unable to append more blocks. But
+                                        * some block might be allocated already
+                                        * */
+                                       break;
+                               }
+                       }
+
+                       iblk_idx++;
+
+                       if (!fblock_start) {
+                               fblock_start = fblk;
+                       }
+
+                       if ((fblock_start + fblock_count) != fblk)
+                               break;
+
+                       fblock_count++;
+               }
+
+               r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
+                                          fblock_count);
+               if (r != EOK)
+                       break;
+
+               size -= block_size * fblock_count;
+               u8_buf += block_size * fblock_count;
+               f->fpos += block_size * fblock_count;
+
+               if (wcnt)
+                       *wcnt += block_size * fblock_count;
+
+               fblock_start = fblk;
+               fblock_count = 1;
+
+               if (rr != EOK) {
+                       /*ext4_fs_append_inode_block has failed and no
+                        * more blocks might be written. But node size
+                        * should be updated.*/
+                       r = rr;
+                       goto out_fsize;
+               }
+       }
+
+       /*Stop write back cache mode*/
+       ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+       if (r != EOK)
+               goto Finish;
+
+       if (size) {
+               uint64_t off;
+               if (iblk_idx < ifile_blocks) {
+                       r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk);
+                       if (r != EOK)
+                               goto Finish;
+               } else {
+                       r = ext4_fs_append_inode_dblk(&ref, &fblk, &iblk_idx);
+                       if (r != EOK)
+                               /*Node size sholud be updated.*/
+                               goto out_fsize;
+               }
+
+               off = fblk * block_size;
+               r = ext4_block_writebytes(f->mp->fs.bdev, off, u8_buf, size);
+               if (r != EOK)
+                       goto Finish;
+
+               f->fpos += size;
+
+               if (wcnt)
+                       *wcnt += size;
+       }
+
+out_fsize:
+       if (f->fpos > f->fsize) {
+               f->fsize = f->fpos;
+               ext4_inode_set_size(ref.inode, f->fsize);
+               ref.dirty = true;
+       }
+
+Finish:
+       r = ext4_fs_put_inode_ref(&ref);
+
+       if (r != EOK)
+               ext4_trans_abort(f->mp);
+       else
+               ext4_trans_stop(f->mp);
+
+       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_chmod(const char *path, uint32_t mode)
+{
+       int r;
+       uint32_t ino, orig_mode;
+       ext4_file f;
+       struct ext4_sblock *sb;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+       ino = f.inode;
+       sb = &mp->fs.sb;
+       ext4_fclose(&f);
+       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       orig_mode = ext4_inode_get_mode(sb, inode_ref.inode);
+       orig_mode &= ~0xFFF;
+       orig_mode |= mode & 0xFFF;
+       ext4_inode_set_mode(sb, inode_ref.inode, orig_mode);
+       inode_ref.dirty = true;
+
+       r = ext4_fs_put_inode_ref(&inode_ref);
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_chown(const char *path, uint32_t uid, uint32_t gid)
+{
+       int r;
+       ext4_file f;
+       uint32_t ino;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+       ino = f.inode;
+       ext4_fclose(&f);
+       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               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;
+
+       r = ext4_fs_put_inode_ref(&inode_ref);
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_file_set_atime(const char *path, uint32_t atime)
+{
+       int r;
+       ext4_file f;
+       uint32_t ino;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+       ino = f.inode;
+       ext4_fclose(&f);
+       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       ext4_inode_set_access_time(inode_ref.inode, atime);
+       inode_ref.dirty = true;
+
+       r = ext4_fs_put_inode_ref(&inode_ref);
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_file_set_mtime(const char *path, uint32_t mtime)
+{
+       int r;
+       ext4_file f;
+       uint32_t ino;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+       ino = f.inode;
+       ext4_fclose(&f);
+       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       ext4_inode_set_modif_time(inode_ref.inode, mtime);
+       inode_ref.dirty = true;
+
+       r = ext4_fs_put_inode_ref(&inode_ref);
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_file_set_ctime(const char *path, uint32_t ctime)
+{
+       int r;
+       ext4_file f;
+       uint32_t ino;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+       if (!mp)
+               return ENOENT;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+       ino = f.inode;
+       ext4_fclose(&f);
+       r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
+       inode_ref.dirty = true;
+
+       r = ext4_fs_put_inode_ref(&inode_ref);
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
+{
+       struct ext4_inode_ref ref;
+       uint32_t sblock;
+       ext4_fsblk_t fblock;
+       uint32_t block_size;
+       int r;
+
+       ext4_assert(f && f->mp);
+
+       if (!size)
+               return EOK;
+
+       r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+       if (r != EOK)
+               return r;
+
+       /*Sync file size*/
+       block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
+       if (size > block_size) {
+               r = EINVAL;
+               goto Finish;
+       }
+       r = ext4_ftruncate_no_lock(f, 0);
+       if (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;
+
+       /*If the size of symlink is smaller than 60 bytes*/
+       if (size < sizeof(ref.inode->blocks)) {
+               memset(ref.inode->blocks, 0, sizeof(ref.inode->blocks));
+               memcpy(ref.inode->blocks, buf, size);
+               ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
+       } else {
+               ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
+               r = ext4_fs_append_inode_dblk(&ref, &fblock, &sblock);
+               if (r != EOK)
+                       goto Finish;
+
+               r = ext4_block_writebytes(f->mp->fs.bdev, 0, buf, size);
+               if (r != EOK)
+                       goto Finish;
+
+       }
+
+       /*Stop write back cache mode*/
+       ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+       if (r != EOK)
+               goto Finish;
+
+       ext4_inode_set_size(ref.inode, size);
+       ref.dirty = true;
+
+       f->fsize = size;
+       if (f->fpos > size)
+               f->fpos = size;
+
+Finish:
+       ext4_fs_put_inode_ref(&ref);
+       return r;
+}
+
+int ext4_fsymlink(const char *target, const char *path)
+{
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       int r;
+       ext4_file f;
+       int filetype;
+
+       if (!mp)
+               return ENOENT;
+
+       filetype = EXT4_DE_SYMLINK;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       ext4_block_cache_write_back(mp->fs.bdev, 1);
+       r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, NULL, NULL);
+       if (r == EOK)
+               r = ext4_fsymlink_set(&f, target, strlen(target));
+       else
+               goto Finish;
+
+       ext4_fclose(&f);
+
+Finish:
+       ext4_block_cache_write_back(mp->fs.bdev, 0);
+
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
+{
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       int r;
+       ext4_file f;
+       int filetype;
+
+       if (!mp)
+               return ENOENT;
+
+       if (!buf)
+               return EINVAL;
+
+       filetype = EXT4_DE_SYMLINK;
+
+       EXT4_MP_LOCK(mp);
+       ext4_block_cache_write_back(mp->fs.bdev, 1);
+       r = ext4_generic_open2(&f, path, O_RDONLY, filetype, NULL, NULL);
+       if (r == EOK)
+               r = ext4_fread(&f, buf, bufsize, rcnt);
+       else
+               goto Finish;
+
+       ext4_fclose(&f);
+
+Finish:
+       ext4_block_cache_write_back(mp->fs.bdev, 0);
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_setxattr(const char *path, const char *name, size_t name_len,
+                 const void *data, size_t data_size, bool replace)
+{
+       int r = EOK;
+       ext4_file f;
+       uint32_t inode;
+       uint8_t name_index;
+       const char *dissected_name = NULL;
+       size_t dissected_len = 0;
+       struct ext4_xattr_ref xattr_ref;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       if (!mp)
+               return ENOENT;
+
+       dissected_name = ext4_extract_xattr_name(name, name_len,
+                               &name_index, &dissected_len);
+       if (!dissected_len)
+               return EINVAL;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK)
+               goto Finish;
+       inode = f.inode;
+       ext4_fclose(&f);
+
+       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
+       if (r != EOK)
+               goto Finish;
+
+       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
+       if (r != EOK) {
+               ext4_fs_put_inode_ref(&inode_ref);
+               goto Finish;
+       }
+
+       r = ext4_fs_set_xattr(&xattr_ref, name_index, dissected_name,
+                       dissected_len, data, data_size, replace);
+
+       ext4_fs_put_xattr_ref(&xattr_ref);
+       ext4_fs_put_inode_ref(&inode_ref);
+Finish:
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+int ext4_getxattr(const char *path, const char *name, size_t name_len,
+                 void *buf, size_t buf_size, size_t *data_size)
+{
+       int r = EOK;
+       ext4_file f;
+       uint32_t inode;
+       uint8_t name_index;
+       const char *dissected_name = NULL;
+       size_t dissected_len = 0;
+       struct ext4_xattr_ref xattr_ref;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       if (!mp)
+               return ENOENT;
+
+       dissected_name = ext4_extract_xattr_name(name, name_len,
+                               &name_index, &dissected_len);
+       if (!dissected_len)
+               return EINVAL;
+
+       EXT4_MP_LOCK(mp);
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK)
+               goto Finish;
+       inode = f.inode;
+       ext4_fclose(&f);
+
+       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
+       if (r != EOK)
+               goto Finish;
+
+       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
+       if (r != EOK) {
+               ext4_fs_put_inode_ref(&inode_ref);
+               goto Finish;
+       }
+
+       r = ext4_fs_get_xattr(&xattr_ref, name_index, dissected_name,
+                               dissected_len, buf, buf_size, data_size);
+
+       ext4_fs_put_xattr_ref(&xattr_ref);
+       ext4_fs_put_inode_ref(&inode_ref);
+Finish:
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
+struct ext4_listxattr_iterator {
+       char *list;
+       char *list_ptr;
+       size_t size;
+       size_t ret_size;
+       bool list_too_small;
+       bool get_required_size;
+};
+
+static int ext4_iterate_ea_list(struct ext4_xattr_ref *ref,
+                               struct ext4_xattr_item *item)
+{
+       struct ext4_listxattr_iterator *lxi;
+       lxi = ref->iter_arg;
+       if (!lxi->get_required_size) {
+               size_t plen;
+               const char *prefix;
+               prefix = ext4_get_xattr_name_prefix(item->name_index, &plen);
+               if (lxi->ret_size + plen + item->name_len + 1 > lxi->size) {
+                       lxi->list_too_small = 1;
+                       return EXT4_XATTR_ITERATE_STOP;
+               }
+               if (prefix) {
+                       memcpy(lxi->list_ptr, prefix, plen);
+                       lxi->list_ptr += plen;
+                       lxi->ret_size += plen;
+               }
+               memcpy(lxi->list_ptr, item->name, item->name_len);
+               lxi->list_ptr[item->name_len] = 0;
+               lxi->list_ptr += item->name_len + 1;
+       }
+       lxi->ret_size += item->name_len + 1;
+       return EXT4_XATTR_ITERATE_CONT;
+}
+
+int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size)
+{
+       int r = EOK;
+       ext4_file f;
+       uint32_t inode;
+       struct ext4_xattr_ref xattr_ref;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_listxattr_iterator lxi;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       if (!mp)
+               return ENOENT;
+
+       lxi.list = list;
+       lxi.list_ptr = list;
+       lxi.size = size;
+       lxi.ret_size = 0;
+       lxi.list_too_small = false;
+       lxi.get_required_size = (!size) ? true : false;
+
+       EXT4_MP_LOCK(mp);
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK)
+               goto Finish;
+       inode = f.inode;
+       ext4_fclose(&f);
+
+       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
+       if (r != EOK)
+               goto Finish;
+
+       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
+       if (r != EOK) {
+               ext4_fs_put_inode_ref(&inode_ref);
+               goto Finish;
+       }
+
+       xattr_ref.iter_arg = &lxi;
+       ext4_fs_xattr_iterate(&xattr_ref, ext4_iterate_ea_list);
+       if (lxi.list_too_small)
+               r = ERANGE;
+
+       if (r == EOK) {
+               if (ret_size)
+                       *ret_size = lxi.ret_size;
+
+       }
+       ext4_fs_put_xattr_ref(&xattr_ref);
+       ext4_fs_put_inode_ref(&inode_ref);
+Finish:
+       EXT4_MP_UNLOCK(mp);
+       return r;
+
+}
+
+int ext4_removexattr(const char *path, const char *name, size_t name_len)
+{
+       int r = EOK;
+       ext4_file f;
+       uint32_t inode;
+       uint8_t name_index;
+       const char *dissected_name = NULL;
+       size_t dissected_len = 0;
+       struct ext4_xattr_ref xattr_ref;
+       struct ext4_inode_ref inode_ref;
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       if (!mp)
+               return ENOENT;
+
+       dissected_name = ext4_extract_xattr_name(name, name_len,
+                                               &name_index, &dissected_len);
+       if (!dissected_len)
+               return EINVAL;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
+       if (r != EOK)
+               goto Finish;
+       inode = f.inode;
+       ext4_fclose(&f);
+
+       r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
+       if (r != EOK)
+               goto Finish;
+
+       r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref, &xattr_ref);
+       if (r != EOK) {
+               ext4_fs_put_inode_ref(&inode_ref);
+               goto Finish;
+       }
+
+       r = ext4_fs_remove_xattr(&xattr_ref, name_index, dissected_name,
+                               dissected_len);
+
+       ext4_fs_put_xattr_ref(&xattr_ref);
+       ext4_fs_put_inode_ref(&inode_ref);
+Finish:
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       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 act;
+       struct ext4_inode_ref child;
+       struct ext4_dir_iter 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);
+
+       struct ext4_fs *const fs = &mp->fs;
+
+       /*Check if exist.*/
+       r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       path += name_off;
+       len = ext4_path_check(path, &is_goal);
+
+       inode_current = f.inode;
+
+       ext4_block_cache_write_back(mp->fs.bdev, 1);
+
+       do {
+
+               uint64_t act_curr_pos = 0;
+               has_children = false;
+               dir_end = false;
+
+               while (r == EOK && !has_children && !dir_end) {
+
+                       /*Load directory node.*/
+                       r = ext4_fs_get_inode_ref(fs, inode_current, &act);
+                       if (r != EOK) {
+                               break;
+                       }
+
+                       /*Initialize iterator.*/
+                       r = ext4_dir_iterator_init(&it, &act, act_curr_pos);
+                       if (r != EOK) {
+                               ext4_fs_put_inode_ref(&act);
+                               break;
+                       }
+
+                       if (!it.curr) {
+                               dir_end = true;
+                               goto End;
+                       }
+
+                       ext4_trans_start(mp);
+
+                       /*Get up directory inode when ".." entry*/
+                       if ((it.curr->name_len == 2) &&
+                           ext4_is_dots(it.curr->name, it.curr->name_len)) {
+                               inode_up = ext4_dir_en_get_inode(it.curr);
+                       }
+
+                       /*If directory or file entry,  but not "." ".." entry*/
+                       if (!ext4_is_dots(it.curr->name, it.curr->name_len)) {
+
+                               /*Get child inode reference do unlink
+                                * directory/file.*/
+                               uint32_t cinode;
+                               uint32_t inode_type;
+                               cinode = ext4_dir_en_get_inode(it.curr);
+                               r = ext4_fs_get_inode_ref(fs, cinode, &child);
+                               if (r != EOK)
+                                       goto End;
+
+                               /*If directory with no leaf children*/
+                               r = ext4_has_children(&has_children, &child);
+                               if (r != EOK) {
+                                       ext4_fs_put_inode_ref(&child);
+                                       goto End;
+                               }
+
+                               if (has_children) {
+                                       /*Has directory children. Go into this
+                                        * directory.*/
+                                       inode_up = inode_current;
+                                       inode_current = cinode;
+                                       depth++;
+                                       ext4_fs_put_inode_ref(&child);
+                                       goto End;
+                               }
+                               inode_type = ext4_inode_type(&mp->fs.sb,
+                                               child.inode);
+
+                               /* Truncate */
+                               if (inode_type != EXT4_INODE_MODE_DIRECTORY)
+                                       r = ext4_trunc_inode(mp, child.index, 0);
+                               else
+                                       r = ext4_trunc_dir(mp, &act, &child);
+
+                               if (r != EOK) {
+                                       ext4_fs_put_inode_ref(&child);
+                                       goto End;
+                               }
+
+                               /*No children in child directory or file. Just
+                                * unlink.*/
+                               r = ext4_unlink(f.mp, &act, &child,
+                                               (char *)it.curr->name,
+                                               it.curr->name_len);
+                               if (r != EOK) {
+                                       ext4_fs_put_inode_ref(&child);
+                                       goto End;
+                               }
+
+                               ext4_inode_set_del_time(child.inode, -1L);
+                               ext4_inode_set_links_cnt(child.inode, 0);
+                               child.dirty = true;
+
+                               r = ext4_fs_free_inode(&child);
+                               if (r != EOK) {
+                                       ext4_fs_put_inode_ref(&child);
+                                       goto End;
+                               }
+
+                               r = ext4_fs_put_inode_ref(&child);
+                               if (r != EOK)
+                                       goto End;
+
+                       }
+
+                       r = ext4_dir_iterator_next(&it);
+                       if (r != EOK)
+                               goto End;
+
+                       act_curr_pos = it.curr_off;
+End:
+                       ext4_dir_iterator_fini(&it);
+                       if (r == EOK)
+                               r = ext4_fs_put_inode_ref(&act);
+                       else
+                               ext4_fs_put_inode_ref(&act);
+
+                       if (r != EOK)
+                               ext4_trans_abort(mp);
+                       else
+                               ext4_trans_stop(mp);
+               }
+
+               if (dir_end) {
+                       /*Directory iterator reached last entry*/
+                       depth--;
+                       if (depth)
+                               inode_current = inode_up;
+
+               }
+
+               if (r != EOK)
+                       break;
+
+       } while (depth);
+
+       /*Last unlink*/
+       if (r == EOK && !depth) {
+               /*Load parent.*/
+               struct ext4_inode_ref parent;
+               r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
+                               &parent);
+               if (r != EOK)
+                       goto Finish;
+               r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
+                               &act);
+               if (r != EOK) {
+                       ext4_fs_put_inode_ref(&act);
+                       goto Finish;
+               }
+
+               ext4_trans_start(mp);
+
+               /* In this place all directories should be
+                * unlinked.
+                * Last unlink from root of current directory*/
+               r = ext4_unlink(f.mp, &parent, &act,
+                               (char *)path, len);
+               if (r != EOK) {
+                       ext4_fs_put_inode_ref(&parent);
+                       ext4_fs_put_inode_ref(&act);
+                       goto Finish;
+               }
+
+               if (ext4_inode_get_links_cnt(act.inode) == 2) {
+                       ext4_inode_set_del_time(act.inode, -1L);
+                       ext4_inode_set_links_cnt(act.inode, 0);
+                       act.dirty = true;
+                       /*Truncate*/
+                       r = ext4_trunc_dir(mp, &parent, &act);
+                       if (r != EOK) {
+                               ext4_fs_put_inode_ref(&parent);
+                               ext4_fs_put_inode_ref(&act);
+                               goto Finish;
+                       }
+
+                       r = ext4_fs_free_inode(&act);
+                       if (r != EOK) {
+                               ext4_fs_put_inode_ref(&parent);
+                               ext4_fs_put_inode_ref(&act);
+                               goto Finish;
+                       }
+               }
+
+               r = ext4_fs_put_inode_ref(&parent);
+               if (r != EOK)
+                       goto Finish;
+
+               r = ext4_fs_put_inode_ref(&act);
+       Finish:
+               if (r != EOK)
+                       ext4_trans_abort(mp);
+               else
+                       ext4_trans_stop(mp);
+       }
+
+       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);
+       ext4_trans_start(mp);
+
+       /*Check if exist.*/
+       r = ext4_generic_open(&f, path, "r", false, 0, 0);
+       if (r == EOK) {
+               /*Directory already created*/
+               ext4_trans_stop(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       /*Create new dir*/
+       r = ext4_generic_open(&f, path, "w", false, 0, 0);
+       if (r != EOK) {
+               ext4_trans_abort(mp);
+               EXT4_MP_UNLOCK(mp);
+               return r;
+       }
+
+       ext4_trans_stop(mp);
+       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_dir_iter it;
+
+       EXT4_MP_LOCK(d->f.mp);
+
+       if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
+               EXT4_MP_UNLOCK(d->f.mp);
+               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.curr, sizeof(ext4_direntry));
+       de = &d->de;
+
+       ext4_dir_iterator_next(&it);
+
+       d->next_off = it.curr ? it.curr_off : 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;
+}
+
+void ext4_dir_entry_rewind(ext4_dir *d)
+{
+       d->next_off = 0;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_balloc.c b/src/ext4_balloc.c
new file mode 100644 (file)
index 0000000..2980e26
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * 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_balloc.c
+ * @brief Physical block allocator.
+ */
+
+#include "ext4_config.h"
+#include "ext4_balloc.h"
+#include "ext4_super.h"
+#include "ext4_crc32.h"
+#include "ext4_block_group.h"
+#include "ext4_fs.h"
+#include "ext4_bitmap.h"
+#include "ext4_inode.h"
+
+/**@brief Compute number of block group from block address.
+ * @param sb superblock pointer.
+ * @param baddr Absolute address of block.
+ * @return Block group index
+ */
+uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s,
+                                      uint64_t baddr)
+{
+       if (ext4_get32(s, first_data_block) && baddr)
+               baddr--;
+
+       return (uint32_t)(baddr / ext4_get32(s, blocks_per_group));
+}
+
+/**@brief Compute the starting block address of a block group
+ * @param sb   superblock pointer.
+ * @param bgid block group index
+ * @return Block address
+ */
+uint64_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s,
+                                      uint32_t bgid)
+{
+       uint64_t baddr = 0;
+       if (ext4_get32(s, first_data_block))
+               baddr++;
+
+       baddr += bgid * ext4_get32(s, blocks_per_group);
+       return baddr;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_balloc_bitmap_csum(struct ext4_sblock *sb,
+                                       void *bitmap)
+{
+       uint32_t checksum = 0;
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t blocks_per_group = ext4_get32(sb, blocks_per_group);
+
+               /* First calculate crc32 checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
+                               sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against block_group_desc */
+               checksum = ext4_crc32c(checksum, bitmap, blocks_per_group / 8);
+       }
+       return checksum;
+}
+#else
+#define ext4_balloc_bitmap_csum(...) 0
+#endif
+
+void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb,
+                                struct ext4_bgroup *bg,
+                                void *bitmap __unused)
+{
+       int desc_size = ext4_sb_get_desc_size(sb);
+       uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap);
+       uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
+                hi_checksum = to_le16(checksum >> 16);
+
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return;
+
+       /* See if we need to assign a 32bit checksum */
+       bg->block_bitmap_csum_lo = lo_checksum;
+       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->block_bitmap_csum_hi = hi_checksum;
+
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+ext4_balloc_verify_bitmap_csum(struct ext4_sblock *sb,
+                              struct ext4_bgroup *bg,
+                              void *bitmap __unused)
+{
+       int desc_size = ext4_sb_get_desc_size(sb);
+       uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap);
+       uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
+                hi_checksum = to_le16(checksum >> 16);
+
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return true;
+
+       if (bg->block_bitmap_csum_lo != lo_checksum)
+               return false;
+
+       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               if (bg->block_bitmap_csum_hi != hi_checksum)
+                       return false;
+
+       return true;
+}
+#else
+#define ext4_balloc_verify_bitmap_csum(...) true
+#endif
+
+int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref, ext4_fsblk_t baddr)
+{
+       struct ext4_fs *fs = inode_ref->fs;
+       struct ext4_sblock *sb = &fs->sb;
+
+       uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, baddr);
+       uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr);
+
+       /* Load block group reference */
+       struct ext4_block_group_ref bg_ref;
+       int rc = ext4_fs_get_block_group_ref(fs, bg_id, &bg_ref);
+       if (rc != EOK)
+               return rc;
+
+       struct ext4_bgroup *bg = bg_ref.block_group;
+
+       /* Load block with bitmap */
+       ext4_fsblk_t bitmap_block_addr =
+           ext4_bg_get_block_bitmap(bg, sb);
+
+       struct ext4_block bitmap_block;
+
+       rc = ext4_trans_block_get(fs->bdev, &bitmap_block, bitmap_block_addr);
+       if (rc != EOK) {
+               ext4_fs_put_block_group_ref(&bg_ref);
+               return rc;
+       }
+
+       if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap_block.data)) {
+               ext4_dbg(DEBUG_BALLOC,
+                       DBG_WARN "Bitmap checksum failed."
+                       "Group: %" PRIu32"\n",
+                       bg_ref.index);
+       }
+
+       /* Modify bitmap */
+       ext4_bmap_bit_clr(bitmap_block.data, index_in_group);
+       ext4_balloc_set_bitmap_csum(sb, bg, bitmap_block.data);
+       ext4_trans_set_block_dirty(bitmap_block.buf);
+
+       /* Release 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;
+       }
+
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+
+       /* Update superblock free blocks count */
+       uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
+       sb_free_blocks++;
+       ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
+
+       /* Update inode blocks count */
+       uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
+       ino_blocks -= block_size / EXT4_INODE_BLOCK_SIZE;
+       ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
+       inode_ref->dirty = true;
+
+       /* Update block group free blocks count */
+       uint32_t free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
+       free_blocks++;
+       ext4_bg_set_free_blocks_count(bg, sb, free_blocks);
+
+       bg_ref.dirty = true;
+
+       rc = ext4_trans_try_revoke_block(fs->bdev, baddr);
+       if (rc != EOK) {
+               bg_ref.dirty = false;
+               ext4_fs_put_block_group_ref(&bg_ref);
+               return rc;
+       }
+       ext4_bcache_invalidate_lba(fs->bdev->bc, baddr, 1);
+       /* Release block group reference */
+       return ext4_fs_put_block_group_ref(&bg_ref);
+}
+
+int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref,
+                           ext4_fsblk_t first, uint32_t count)
+{
+       int rc = EOK;
+       struct ext4_fs *fs = inode_ref->fs;
+       struct ext4_sblock *sb = &fs->sb;
+
+       /* Compute indexes */
+       uint32_t bg_first = ext4_balloc_get_bgid_of_block(sb, first);
+
+       /* Compute indexes */
+       uint32_t bg_last = ext4_balloc_get_bgid_of_block(sb, first + count - 1);
+
+       if (!ext4_sb_feature_incom(sb, EXT4_FINCOM_FLEX_BG)) {
+               /*It is not possible without flex_bg that blocks are continuous
+                * and and last block belongs to other bg.*/
+               if (bg_last != bg_first) {
+                       ext4_dbg(DEBUG_BALLOC, DBG_WARN "FLEX_BG: disabled & "
+                               "bg_last: %"PRIu32" bg_first: %"PRIu32"\n",
+                               bg_last, bg_first);
+               }
+       }
+
+       /* Load block group reference */
+       struct ext4_block_group_ref bg_ref;
+       while (bg_first <= bg_last) {
+
+               rc = ext4_fs_get_block_group_ref(fs, bg_first, &bg_ref);
+               if (rc != EOK)
+                       return rc;
+
+               struct ext4_bgroup *bg = bg_ref.block_group;
+
+               uint32_t idx_in_bg_first;
+               idx_in_bg_first = ext4_fs_addr_to_idx_bg(sb, first);
+
+               /* Load block with bitmap */
+               ext4_fsblk_t bitmap_blk = ext4_bg_get_block_bitmap(bg, sb);
+
+               struct ext4_block blk;
+               rc = ext4_trans_block_get(fs->bdev, &blk, bitmap_blk);
+               if (rc != EOK) {
+                       ext4_fs_put_block_group_ref(&bg_ref);
+                       return rc;
+               }
+
+               if (!ext4_balloc_verify_bitmap_csum(sb, bg, blk.data)) {
+                       ext4_dbg(DEBUG_BALLOC,
+                               DBG_WARN "Bitmap checksum failed."
+                               "Group: %" PRIu32"\n",
+                               bg_ref.index);
+               }
+               uint32_t free_cnt;
+               free_cnt = ext4_sb_get_block_size(sb) * 8 - idx_in_bg_first;
+
+               /*If last block, free only count blocks*/
+               free_cnt = count > free_cnt ? free_cnt : count;
+
+               /* Modify bitmap */
+               ext4_bmap_bits_free(blk.data, idx_in_bg_first, free_cnt);
+               ext4_balloc_set_bitmap_csum(sb, bg, blk.data);
+               ext4_trans_set_block_dirty(blk.buf);
+
+               count -= free_cnt;
+               first += free_cnt;
+
+               /* Release block with bitmap */
+               rc = ext4_block_set(fs->bdev, &blk);
+               if (rc != EOK) {
+                       ext4_fs_put_block_group_ref(&bg_ref);
+                       return rc;
+               }
+
+               uint32_t block_size = ext4_sb_get_block_size(sb);
+
+               /* Update superblock free blocks count */
+               uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
+               sb_free_blocks += free_cnt;
+               ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
+
+               /* Update inode blocks count */
+               uint64_t ino_blocks;
+               ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
+               ino_blocks -= free_cnt * (block_size / EXT4_INODE_BLOCK_SIZE);
+               ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
+               inode_ref->dirty = true;
+
+               /* Update block group free blocks count */
+               uint32_t free_blocks;
+               free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
+               free_blocks += free_cnt;
+               ext4_bg_set_free_blocks_count(bg, sb, free_blocks);
+               bg_ref.dirty = true;
+
+               /* Release block group reference */
+               rc = ext4_fs_put_block_group_ref(&bg_ref);
+               if (rc != EOK)
+                       break;
+
+               bg_first++;
+       }
+
+       uint32_t i;
+       for (i = 0;i < count;i++) {
+               rc = ext4_trans_try_revoke_block(fs->bdev, first + i);
+               if (rc != EOK)
+                       return rc;
+
+       }
+
+       ext4_bcache_invalidate_lba(fs->bdev->bc, first, count);
+       /*All blocks should be released*/
+       ext4_assert(count == 0);
+       return rc;
+}
+
+int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref,
+                           ext4_fsblk_t goal,
+                           ext4_fsblk_t *fblock)
+{
+       ext4_fsblk_t alloc = 0;
+       ext4_fsblk_t bmp_blk_adr;
+       uint32_t rel_blk_idx = 0;
+       uint64_t free_blocks;
+       int r;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       /* Load block group number for goal and relative index */
+       uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, goal);
+       uint32_t idx_in_bg = ext4_fs_addr_to_idx_bg(sb, goal);
+
+       struct ext4_block b;
+       struct ext4_block_group_ref bg_ref;
+
+       /* Load block group reference */
+       r = ext4_fs_get_block_group_ref(inode_ref->fs, bg_id, &bg_ref);
+       if (r != EOK)
+               return r;
+
+       struct ext4_bgroup *bg = bg_ref.block_group;
+
+       free_blocks = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb);
+       if (free_blocks == 0) {
+               /* This group has no free blocks */
+               goto goal_failed;
+       }
+
+       /* Compute indexes */
+       ext4_fsblk_t first_in_bg;
+       first_in_bg = ext4_balloc_get_block_of_bgid(sb, bg_ref.index);
+
+       uint32_t first_in_bg_index;
+       first_in_bg_index = ext4_fs_addr_to_idx_bg(sb, first_in_bg);
+
+       if (idx_in_bg < first_in_bg_index)
+               idx_in_bg = first_in_bg_index;
+
+       /* Load block with bitmap */
+       bmp_blk_adr = ext4_bg_get_block_bitmap(bg_ref.block_group, sb);
+
+       r = ext4_trans_block_get(inode_ref->fs->bdev, &b, bmp_blk_adr);
+       if (r != EOK) {
+               ext4_fs_put_block_group_ref(&bg_ref);
+               return r;
+       }
+
+       if (!ext4_balloc_verify_bitmap_csum(sb, bg, b.data)) {
+               ext4_dbg(DEBUG_BALLOC,
+                       DBG_WARN "Bitmap checksum failed."
+                       "Group: %" PRIu32"\n",
+                       bg_ref.index);
+       }
+
+       /* Check if goal is free */
+       if (ext4_bmap_is_bit_clr(b.data, idx_in_bg)) {
+               ext4_bmap_bit_set(b.data, idx_in_bg);
+               ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group,
+                                           b.data);
+               ext4_trans_set_block_dirty(b.buf);
+               r = ext4_block_set(inode_ref->fs->bdev, &b);
+               if (r != EOK) {
+                       ext4_fs_put_block_group_ref(&bg_ref);
+                       return r;
+               }
+
+               alloc = ext4_fs_bg_idx_to_addr(sb, idx_in_bg, bg_id);
+               goto success;
+       }
+
+       uint32_t blk_in_bg = ext4_blocks_in_group_cnt(sb, bg_id);
+
+       uint32_t end_idx = (idx_in_bg + 63) & ~63;
+       if (end_idx > blk_in_bg)
+               end_idx = blk_in_bg;
+
+       /* Try to find free block near to goal */
+       uint32_t tmp_idx;
+       for (tmp_idx = idx_in_bg + 1; tmp_idx < end_idx; ++tmp_idx) {
+               if (ext4_bmap_is_bit_clr(b.data, tmp_idx)) {
+                       ext4_bmap_bit_set(b.data, tmp_idx);
+
+                       ext4_balloc_set_bitmap_csum(sb, bg, b.data);
+                       ext4_trans_set_block_dirty(b.buf);
+                       r = ext4_block_set(inode_ref->fs->bdev, &b);
+                       if (r != EOK)
+                               return r;
+
+                       alloc = ext4_fs_bg_idx_to_addr(sb, tmp_idx, bg_id);
+                       goto success;
+               }
+       }
+
+       /* Find free bit in bitmap */
+       r = ext4_bmap_bit_find_clr(b.data, idx_in_bg, blk_in_bg, &rel_blk_idx);
+       if (r == EOK) {
+               ext4_bmap_bit_set(b.data, rel_blk_idx);
+               ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, b.data);
+               ext4_trans_set_block_dirty(b.buf);
+               r = ext4_block_set(inode_ref->fs->bdev, &b);
+               if (r != EOK)
+                       return r;
+
+               alloc = ext4_fs_bg_idx_to_addr(sb, rel_blk_idx, bg_id);
+               goto success;
+       }
+
+       /* No free block found yet */
+       r = ext4_block_set(inode_ref->fs->bdev, &b);
+       if (r != EOK) {
+               ext4_fs_put_block_group_ref(&bg_ref);
+               return r;
+       }
+
+goal_failed:
+
+       r = ext4_fs_put_block_group_ref(&bg_ref);
+       if (r != EOK)
+               return r;
+
+       /* Try other block groups */
+       uint32_t block_group_count = ext4_block_group_cnt(sb);
+       uint32_t bgid = (bg_id + 1) % block_group_count;
+       uint32_t count = block_group_count;
+
+       while (count > 0) {
+               r = ext4_fs_get_block_group_ref(inode_ref->fs, bgid, &bg_ref);
+               if (r != EOK)
+                       return r;
+
+               struct ext4_bgroup *bg = bg_ref.block_group;
+               free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
+               if (free_blocks == 0) {
+                       /* This group has no free blocks */
+                       goto next_group;
+               }
+
+               /* Load block with bitmap */
+               bmp_blk_adr = ext4_bg_get_block_bitmap(bg, sb);
+               r = ext4_trans_block_get(inode_ref->fs->bdev, &b, bmp_blk_adr);
+               if (r != EOK) {
+                       ext4_fs_put_block_group_ref(&bg_ref);
+                       return r;
+               }
+
+               if (!ext4_balloc_verify_bitmap_csum(sb, bg, b.data)) {
+                       ext4_dbg(DEBUG_BALLOC,
+                               DBG_WARN "Bitmap checksum failed."
+                               "Group: %" PRIu32"\n",
+                               bg_ref.index);
+               }
+
+               /* Compute indexes */
+               first_in_bg = ext4_balloc_get_block_of_bgid(sb, bgid);
+               idx_in_bg = ext4_fs_addr_to_idx_bg(sb, first_in_bg);
+               blk_in_bg = ext4_blocks_in_group_cnt(sb, bgid);
+               first_in_bg_index = ext4_fs_addr_to_idx_bg(sb, first_in_bg);
+
+               if (idx_in_bg < first_in_bg_index)
+                       idx_in_bg = first_in_bg_index;
+
+               r = ext4_bmap_bit_find_clr(b.data, idx_in_bg, blk_in_bg,
+                               &rel_blk_idx);
+               if (r == EOK) {
+                       ext4_bmap_bit_set(b.data, rel_blk_idx);
+                       ext4_balloc_set_bitmap_csum(sb, bg, b.data);
+                       ext4_trans_set_block_dirty(b.buf);
+                       r = ext4_block_set(inode_ref->fs->bdev, &b);
+                       if (r != EOK) {
+                               ext4_fs_put_block_group_ref(&bg_ref);
+                               return r;
+                       }
+
+                       alloc = ext4_fs_bg_idx_to_addr(sb, rel_blk_idx, bgid);
+                       goto success;
+               }
+
+               r = ext4_block_set(inode_ref->fs->bdev, &b);
+               if (r != EOK) {
+                       ext4_fs_put_block_group_ref(&bg_ref);
+                       return r;
+               }
+
+       next_group:
+               r = ext4_fs_put_block_group_ref(&bg_ref);
+               if (r != EOK) {
+                       return r;
+               }
+
+               /* Goto next group */
+               bgid = (bgid + 1) % block_group_count;
+               count--;
+       }
+
+       return ENOSPC;
+
+success:
+    /* Empty command - because of syntax */
+    ;
+
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+
+       /* Update superblock free blocks count */
+       uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
+       sb_free_blocks--;
+       ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
+
+       /* Update inode blocks (different block size!) count */
+       uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
+       ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE;
+       ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
+       inode_ref->dirty = true;
+
+       /* Update block group free blocks count */
+
+       uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb);
+       fb_cnt--;
+       ext4_bg_set_free_blocks_count(bg_ref.block_group, sb, fb_cnt);
+
+       bg_ref.dirty = true;
+       r = ext4_fs_put_block_group_ref(&bg_ref);
+
+       *fblock = alloc;
+       return r;
+}
+
+int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref,
+                               ext4_fsblk_t baddr, bool *free)
+{
+       int rc;
+
+       struct ext4_fs *fs = inode_ref->fs;
+       struct ext4_sblock *sb = &fs->sb;
+
+       /* Compute indexes */
+       uint32_t block_group = ext4_balloc_get_bgid_of_block(sb, baddr);
+       uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr);
+
+       /* Load block group reference */
+       struct ext4_block_group_ref bg_ref;
+       rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
+       if (rc != EOK)
+               return rc;
+
+       /* Load block with bitmap */
+       ext4_fsblk_t bmp_blk_addr;
+       bmp_blk_addr = ext4_bg_get_block_bitmap(bg_ref.block_group, sb);
+
+       struct ext4_block b;
+       rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_addr);
+       if (rc != EOK) {
+               ext4_fs_put_block_group_ref(&bg_ref);
+               return rc;
+       }
+
+       if (!ext4_balloc_verify_bitmap_csum(sb, bg_ref.block_group, b.data)) {
+               ext4_dbg(DEBUG_BALLOC,
+                       DBG_WARN "Bitmap checksum failed."
+                       "Group: %" PRIu32"\n",
+                       bg_ref.index);
+       }
+
+       /* Check if block is free */
+       *free = ext4_bmap_is_bit_clr(b.data, index_in_group);
+
+       /* Allocate block if possible */
+       if (*free) {
+               ext4_bmap_bit_set(b.data, index_in_group);
+               ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, b.data);
+               ext4_trans_set_block_dirty(b.buf);
+       }
+
+       /* Release block with bitmap */
+       rc = ext4_block_set(fs->bdev, &b);
+       if (rc != EOK) {
+               /* Error in saving bitmap */
+               ext4_fs_put_block_group_ref(&bg_ref);
+               return rc;
+       }
+
+       /* If block is not free, return */
+       if (!(*free))
+               goto terminate;
+
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+
+       /* Update superblock free blocks count */
+       uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
+       sb_free_blocks--;
+       ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
+
+       /* Update inode blocks count */
+       uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
+       ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE;
+       ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
+       inode_ref->dirty = true;
+
+       /* Update block group free blocks count */
+       uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb);
+       fb_cnt--;
+       ext4_bg_set_free_blocks_count(bg_ref.block_group, sb, fb_cnt);
+
+       bg_ref.dirty = true;
+
+terminate:
+       return ext4_fs_put_block_group_ref(&bg_ref);
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_bcache.c b/src/ext4_bcache.c
new file mode 100644 (file)
index 0000000..1a1766a
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * 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_blockdev.h"
+#include "ext4_debug.h"
+#include "ext4_errno.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+static int ext4_bcache_lba_compare(struct ext4_buf *a, struct ext4_buf *b)
+{
+        if (a->lba > b->lba)
+                return 1;
+        else if (a->lba < b->lba)
+                return -1;
+        return 0;
+}
+
+static int ext4_bcache_lru_compare(struct ext4_buf *a, struct ext4_buf *b)
+{
+       if (a->lru_id > b->lru_id)
+               return 1;
+       else if (a->lru_id < b->lru_id)
+               return -1;
+       return 0;
+}
+
+RB_GENERATE_INTERNAL(ext4_buf_lba, ext4_buf, lba_node,
+                    ext4_bcache_lba_compare, static inline)
+RB_GENERATE_INTERNAL(ext4_buf_lru, ext4_buf, lru_node,
+                    ext4_bcache_lru_compare, static inline)
+
+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->cnt = cnt;
+       bc->itemsize = itemsize;
+       bc->ref_blocks = 0;
+       bc->max_ref_blocks = 0;
+
+       return EOK;
+}
+
+void ext4_bcache_cleanup(struct ext4_bcache *bc)
+{
+       struct ext4_buf *buf, *tmp;
+       RB_FOREACH_SAFE(buf, ext4_buf_lba, &bc->lba_root, tmp) {
+               ext4_block_flush_buf(bc->bdev, buf);
+               ext4_bcache_drop_buf(bc, buf);
+       }
+}
+
+int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
+{
+       memset(bc, 0, sizeof(struct ext4_bcache));
+       return EOK;
+}
+
+/**@brief:
+ *
+ *  This is ext4_bcache, the module handling basic buffer-cache stuff.
+ *
+ *  Buffers in a bcache are sorted by their LBA and stored in a
+ *  RB-Tree(lba_root).
+ *
+ *  Bcache also maintains another RB-Tree(lru_root) right now, where
+ *  buffers are sorted by their LRU id.
+ *
+ *  A singly-linked list is used to track those dirty buffers which are
+ *  ready to be flushed. (Those buffers which are dirty but also referenced
+ *  are not considered ready to be flushed.)
+ *
+ *  When a buffer is not referenced, it will be stored in both lba_root
+ *  and lru_root, while it will only be stored in lba_root when it is
+ *  referenced.
+ */
+
+static struct ext4_buf *
+ext4_buf_alloc(struct ext4_bcache *bc, uint64_t lba)
+{
+       void *data;
+       struct ext4_buf *buf;
+       data = malloc(bc->itemsize);
+       if (!data)
+               return NULL;
+
+       buf = calloc(1, sizeof(struct ext4_buf));
+       if (!buf) {
+               free(data);
+               return NULL;
+       }
+
+       buf->lba = lba;
+       buf->data = data;
+       buf->bc = bc;
+       return buf;
+}
+
+static void ext4_buf_free(struct ext4_buf *buf)
+{
+       free(buf->data);
+       free(buf);
+}
+
+static struct ext4_buf *
+ext4_buf_lookup(struct ext4_bcache *bc, uint64_t lba)
+{
+       struct ext4_buf tmp = {
+               .lba = lba
+       };
+
+       return RB_FIND(ext4_buf_lba, &bc->lba_root, &tmp);
+}
+
+struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc)
+{
+       return RB_MIN(ext4_buf_lru, &bc->lru_root);
+}
+
+void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf)
+{
+       /* Warn on dropping any referenced buffers.*/
+       if (buf->refctr) {
+               ext4_dbg(DEBUG_BCACHE, DBG_WARN "Buffer is still referenced. "
+                               "lba: %" PRIu64 ", refctr: %" PRIu32 "\n",
+                               buf->lba, buf->refctr);
+       } else
+               RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
+
+       RB_REMOVE(ext4_buf_lba, &bc->lba_root, buf);
+
+       /*Forcibly drop dirty buffer.*/
+       if (ext4_bcache_test_flag(buf, BC_DIRTY))
+               ext4_bcache_remove_dirty_node(bc, buf);
+
+       ext4_buf_free(buf);
+       bc->ref_blocks--;
+}
+
+void ext4_bcache_invalidate_lba(struct ext4_bcache *bc,
+                               uint64_t from,
+                               uint32_t cnt)
+{
+       uint64_t end = from + cnt - 1;
+       struct ext4_buf *tmp = ext4_buf_lookup(bc, from), *buf;
+       RB_FOREACH_FROM(buf, ext4_buf_lba, tmp) {
+               if (buf->lba > end)
+                       break;
+
+               /* Clear both dirty and up-to-date flags. */
+               if (ext4_bcache_test_flag(buf, BC_DIRTY))
+                       ext4_bcache_remove_dirty_node(bc, buf);
+
+               buf->end_write = NULL;
+               buf->end_write_arg = NULL;
+               ext4_bcache_clear_dirty(buf);
+       }
+}
+
+struct ext4_buf *
+ext4_bcache_find_get(struct ext4_bcache *bc, struct ext4_block *b,
+                    uint64_t lba)
+{
+       struct ext4_buf *buf = ext4_buf_lookup(bc, lba);
+       if (buf) {
+               /* If buffer is not referenced. */
+               if (!buf->refctr) {
+                       /* Assign new value to LRU id and increment LRU counter
+                        * by 1*/
+                       buf->lru_id = ++bc->lru_ctr;
+                       RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
+                       if (ext4_bcache_test_flag(buf, BC_DIRTY))
+                               ext4_bcache_remove_dirty_node(bc, buf);
+
+               }
+
+               ext4_bcache_inc_ref(buf);
+
+               b->lb_id = lba;
+               b->buf = buf;
+               b->data = buf->data;
+       }
+       return buf;
+}
+
+int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
+                     bool *is_new)
+{
+       /* Try to search the buffer with exaxt LBA. */
+       struct ext4_buf *buf = ext4_bcache_find_get(bc, b, b->lb_id);
+       if (buf) {
+               *is_new = false;
+               return EOK;
+       }
+
+       /* We need to allocate one buffer.*/
+       buf = ext4_buf_alloc(bc, b->lb_id);
+       if (!buf)
+               return ENOMEM;
+
+       RB_INSERT(ext4_buf_lba, &bc->lba_root, buf);
+       /* One more buffer in bcache now. :-) */
+       bc->ref_blocks++;
+
+       /*Calc ref blocks max depth*/
+       if (bc->max_ref_blocks < bc->ref_blocks)
+               bc->max_ref_blocks = bc->ref_blocks;
+
+
+       ext4_bcache_inc_ref(buf);
+       /* Assign new value to LRU id and increment LRU counter
+        * by 1*/
+       buf->lru_id = ++bc->lru_ctr;
+
+       b->buf = buf;
+       b->data = buf->data;
+
+       *is_new = true;
+       return EOK;
+}
+
+int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b)
+{
+       struct ext4_buf *buf = b->buf;
+
+       ext4_assert(bc && b);
+
+       /*Check if valid.*/
+       ext4_assert(b->lb_id);
+
+       /*Block should have a valid pointer to ext4_buf.*/
+       ext4_assert(buf);
+
+       /*Check if someone don't try free unreferenced block cache.*/
+       ext4_assert(buf->refctr);
+
+       /*Just decrease reference counter*/
+       ext4_bcache_dec_ref(buf);
+
+       /* We are the last one touching this buffer, do the cleanups. */
+       if (!buf->refctr) {
+               RB_INSERT(ext4_buf_lru, &bc->lru_root, buf);
+               /* This buffer is ready to be flushed. */
+               if (ext4_bcache_test_flag(buf, BC_DIRTY) &&
+                   ext4_bcache_test_flag(buf, BC_UPTODATE)) {
+                       if (bc->bdev->cache_write_back &&
+                           !ext4_bcache_test_flag(buf, BC_FLUSH) &&
+                           !ext4_bcache_test_flag(buf, BC_TMP))
+                               ext4_bcache_insert_dirty_node(bc, buf);
+                       else {
+                               ext4_block_flush_buf(bc->bdev, buf);
+                               ext4_bcache_clear_flag(buf, BC_FLUSH);
+                       }
+               }
+
+               /* The buffer is invalidated...drop it. */
+               if (!ext4_bcache_test_flag(buf, BC_UPTODATE) ||
+                   ext4_bcache_test_flag(buf, BC_TMP))
+                       ext4_bcache_drop_buf(bc, buf);
+
+       }
+
+       b->lb_id = 0;
+       b->data = 0;
+
+       return EOK;
+}
+
+bool ext4_bcache_is_full(struct ext4_bcache *bc)
+{
+       return (bc->cnt <= bc->ref_blocks);
+}
+
+
+/**
+ * @}
+ */
diff --git a/src/ext4_bitmap.c b/src/ext4_bitmap.c
new file mode 100644 (file)
index 0000000..1320033
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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_bitmap.c
+ * @brief Block/inode bitmap allocator.
+ */
+
+#include "ext4_config.h"
+#include "ext4_bitmap.h"
+
+#include "ext4_errno.h"
+
+void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt)
+{
+       uint32_t i = sbit;
+
+       while (i & 7) {
+
+               if (!bcnt)
+                       return;
+
+               ext4_bmap_bit_clr(bmap, i);
+
+               bcnt--;
+               i++;
+       }
+       sbit = i;
+       bmap += (sbit >> 3);
+
+       while (bcnt >= 32) {
+               *(uint32_t *)bmap = 0;
+               bmap += 4;
+               bcnt -= 32;
+               sbit += 32;
+       }
+
+       while (bcnt >= 16) {
+               *(uint16_t *)bmap = 0;
+               bmap += 2;
+               bcnt -= 16;
+               sbit += 16;
+       }
+
+       while (bcnt >= 8) {
+               *bmap = 0;
+               bmap += 1;
+               bcnt -= 8;
+               sbit += 8;
+       }
+
+       for (i = 0; i < bcnt; ++i) {
+               ext4_bmap_bit_clr(bmap, i);
+       }
+}
+
+int ext4_bmap_bit_find_clr(uint8_t *bmap, uint32_t sbit, uint32_t ebit,
+                          uint32_t *bit_id)
+{
+       uint32_t i;
+       uint32_t bcnt = ebit - sbit;
+
+       i = sbit;
+
+       while (i & 7) {
+
+               if (!bcnt)
+                       return ENOSPC;
+
+               if (ext4_bmap_is_bit_clr(bmap, i)) {
+                       *bit_id = sbit;
+                       return EOK;
+               }
+
+               i++;
+               bcnt--;
+       }
+
+       sbit = i;
+       bmap += (sbit >> 3);
+
+       while (bcnt >= 32) {
+               if (*(uint32_t *)bmap != 0xFFFFFFFF)
+                       goto finish_it;
+
+               bmap += 4;
+               bcnt -= 32;
+               sbit += 32;
+       }
+
+       while (bcnt >= 16) {
+               if (*(uint16_t *)bmap != 0xFFFF)
+                       goto finish_it;
+
+               bmap += 2;
+               bcnt -= 16;
+               sbit += 16;
+       }
+
+finish_it:
+       while (bcnt >= 8) {
+               if (*bmap != 0xFF) {
+                       for (i = 0; i < 8; ++i) {
+                               if (ext4_bmap_is_bit_clr(bmap, i)) {
+                                       *bit_id = sbit + i;
+                                       return EOK;
+                               }
+                       }
+               }
+
+               bmap += 1;
+               bcnt -= 8;
+               sbit += 8;
+       }
+
+       for (i = 0; i < bcnt; ++i) {
+               if (ext4_bmap_is_bit_clr(bmap, i)) {
+                       *bit_id = sbit + i;
+                       return EOK;
+               }
+       }
+
+       return ENOSPC;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_block_group.c b/src/ext4_block_group.c
new file mode 100644 (file)
index 0000000..7f068b9
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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_block_group.c
+ * @brief Block group function set.
+ */
+
+#include "ext4_config.h"
+#include "ext4_block_group.h"
+
+/**@brief CRC-16 look up table*/
+static uint16_t const crc16_tab[256] = {
+    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601,
+    0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0,
+    0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81,
+    0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941,
+    0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01,
+    0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0,
+    0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081,
+    0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+    0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00,
+    0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0,
+    0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981,
+    0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41,
+    0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700,
+    0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0,
+    0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281,
+    0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+    0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01,
+    0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1,
+    0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80,
+    0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541,
+    0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101,
+    0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0,
+    0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481,
+    0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+    0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801,
+    0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1,
+    0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581,
+    0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341,
+    0x4100, 0x81C1, 0x8081, 0x4040};
+
+uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len)
+{
+       while (len--)
+
+               crc = (((crc >> 8) & 0xffU) ^
+                      crc16_tab[(crc ^ *buffer++) & 0xffU]) &
+                     0x0000ffffU;
+       return crc;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_blockdev.c b/src/ext4_blockdev.c
new file mode 100644 (file)
index 0000000..23ad9ee
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * 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_blockdev.c
+ * @brief Block device module.
+ */
+
+#include "ext4_config.h"
+#include "ext4_blockdev.h"
+#include "ext4_errno.h"
+#include "ext4_debug.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+static void ext4_bdif_lock(struct ext4_blockdev *bdev)
+{
+       if (!bdev->bdif->lock)
+               return;
+
+       int r = bdev->bdif->lock(bdev);
+       ext4_assert(r == EOK);
+}
+
+static void ext4_bdif_unlock(struct ext4_blockdev *bdev)
+{
+       if (!bdev->bdif->unlock)
+               return;
+
+       int r = bdev->bdif->unlock(bdev);
+       ext4_assert(r == EOK);
+}
+
+static int ext4_bdif_bread(struct ext4_blockdev *bdev, void *buf,
+                          uint64_t blk_id, uint32_t blk_cnt)
+{
+       ext4_bdif_lock(bdev);
+       int r = bdev->bdif->bread(bdev, buf, blk_id, blk_cnt);
+       bdev->bdif->bread_ctr++;
+       ext4_bdif_unlock(bdev);
+       return r;
+}
+
+static int ext4_bdif_bwrite(struct ext4_blockdev *bdev, const void *buf,
+                           uint64_t blk_id, uint32_t blk_cnt)
+{
+       ext4_bdif_lock(bdev);
+       int r = bdev->bdif->bwrite(bdev, buf, blk_id, blk_cnt);
+       bdev->bdif->bwrite_ctr++;
+       ext4_bdif_unlock(bdev);
+       return r;
+}
+
+int ext4_block_init(struct ext4_blockdev *bdev)
+{
+       int rc;
+       ext4_assert(bdev);
+       ext4_assert(bdev->bdif);
+       ext4_assert(bdev->bdif->open &&
+                  bdev->bdif->close &&
+                  bdev->bdif->bread &&
+                  bdev->bdif->bwrite);
+
+       if (bdev->bdif->ph_refctr) {
+               bdev->bdif->ph_refctr++;
+               return EOK;
+       }
+
+       /*Low level block init*/
+       rc = bdev->bdif->open(bdev);
+       if (rc != EOK)
+               return rc;
+
+       bdev->bdif->ph_refctr = 1;
+       return EOK;
+}
+
+int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc)
+{
+       ext4_assert(bdev && bc);
+       bdev->bc = bc;
+       bc->bdev = bdev;
+       return EOK;
+}
+
+void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint64_t lb_bsize)
+{
+       /*Logical block size has to be multiply of physical */
+       ext4_assert(!(lb_bsize % bdev->bdif->ph_bsize));
+
+       bdev->lg_bsize = lb_bsize;
+       bdev->lg_bcnt = bdev->part_size / lb_bsize;
+}
+
+int ext4_block_fini(struct ext4_blockdev *bdev)
+{
+       ext4_assert(bdev);
+
+       if (!bdev->bdif->ph_refctr)
+               return EOK;
+
+       bdev->bdif->ph_refctr--;
+       if (bdev->bdif->ph_refctr)
+               return EOK;
+
+       /*Low level block fini*/
+       return bdev->bdif->close(bdev);
+}
+
+int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf)
+{
+       int r;
+       struct ext4_bcache *bc = bdev->bc;
+
+       if (ext4_bcache_test_flag(buf, BC_DIRTY) &&
+           ext4_bcache_test_flag(buf, BC_UPTODATE)) {
+               r = ext4_blocks_set_direct(bdev, buf->data, buf->lba, 1);
+               if (r) {
+                       if (buf->end_write) {
+                               bc->dont_shake = true;
+                               buf->end_write(bc, buf, r, buf->end_write_arg);
+                               bc->dont_shake = false;
+                       }
+
+                       return r;
+               }
+
+               ext4_bcache_remove_dirty_node(bc, buf);
+               ext4_bcache_clear_flag(buf, BC_DIRTY);
+               if (buf->end_write) {
+                       bc->dont_shake = true;
+                       buf->end_write(bc, buf, r, buf->end_write_arg);
+                       bc->dont_shake = false;
+               }
+       }
+       return EOK;
+}
+
+int ext4_block_flush_lba(struct ext4_blockdev *bdev, uint64_t lba)
+{
+       int r = EOK;
+       struct ext4_buf *buf;
+       struct ext4_block b;
+       buf = ext4_bcache_find_get(bdev->bc, &b, lba);
+       if (buf) {
+               r = ext4_block_flush_buf(bdev, buf);
+               ext4_bcache_free(bdev->bc, &b);
+       }
+       return r;
+}
+
+int ext4_block_cache_shake(struct ext4_blockdev *bdev)
+{
+       int r = EOK;
+       struct ext4_buf *buf;
+       if (bdev->bc->dont_shake)
+               return EOK;
+
+       while (!RB_EMPTY(&bdev->bc->lru_root) &&
+               ext4_bcache_is_full(bdev->bc)) {
+
+               buf = ext4_buf_lowest_lru(bdev->bc);
+               ext4_assert(buf);
+               if (ext4_bcache_test_flag(buf, BC_DIRTY)) {
+                       r = ext4_block_flush_buf(bdev, buf);
+                       if (r != EOK)
+                               break;
+
+               }
+
+               ext4_bcache_drop_buf(bdev->bc, buf);
+       }
+       return r;
+}
+
+int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b,
+                         uint64_t lba)
+{
+       bool is_new;
+       int r;
+
+       ext4_assert(bdev && b);
+
+       if (!bdev->bdif->ph_refctr)
+               return EIO;
+
+       if (!(lba < bdev->lg_bcnt))
+               return ERANGE;
+
+       b->lb_id = lba;
+
+       /*If cache is full we have to (flush and) drop it anyway :(*/
+       r = ext4_block_cache_shake(bdev);
+       if (r != EOK)
+               return r;
+
+       r = ext4_bcache_alloc(bdev->bc, b, &is_new);
+       if (r != EOK)
+               return r;
+
+       if (!b->data)
+               return ENOMEM;
+
+       return EOK;
+}
+
+int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b,
+                  uint64_t lba)
+{
+       int r = ext4_block_get_noread(bdev, b, lba);
+       if (r != EOK)
+               return r;
+
+       if (ext4_bcache_test_flag(b->buf, BC_UPTODATE)) {
+               /* Data in the cache is up-to-date.
+                * Reading from physical device is not required */
+               return EOK;
+       }
+
+       r = ext4_blocks_get_direct(bdev, b->data, lba, 1);
+       if (r != EOK) {
+               ext4_bcache_free(bdev->bc, b);
+               b->lb_id = 0;
+               return r;
+       }
+
+       /* Mark buffer up-to-date, since
+        * fresh data is read from physical device just now. */
+       ext4_bcache_set_flag(b->buf, BC_UPTODATE);
+       return EOK;
+}
+
+int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b)
+{
+       ext4_assert(bdev && b);
+       ext4_assert(b->buf);
+
+       if (!bdev->bdif->ph_refctr)
+               return EIO;
+
+       return ext4_bcache_free(bdev->bc, b);
+}
+
+int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba,
+                          uint32_t cnt)
+{
+       uint64_t pba;
+       uint32_t pb_cnt;
+
+       ext4_assert(bdev && buf);
+
+       pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize;
+       pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize;
+
+       return ext4_bdif_bread(bdev, buf, pba, pb_cnt * cnt);
+}
+
+int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf,
+                          uint64_t lba, uint32_t cnt)
+{
+       uint64_t pba;
+       uint32_t pb_cnt;
+
+       ext4_assert(bdev && buf);
+
+       pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize;
+       pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize;
+
+       return ext4_bdif_bwrite(bdev, buf, pba, pb_cnt * cnt);
+}
+
+int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off,
+                         const void *buf, uint32_t len)
+{
+       uint64_t block_idx;
+       uint32_t blen;
+       uint32_t unalg;
+       int r = EOK;
+
+       const uint8_t *p = (void *)buf;
+
+       ext4_assert(bdev && buf);
+
+       if (!bdev->bdif->ph_refctr)
+               return EIO;
+
+       if (off + len > bdev->part_size)
+               return EINVAL; /*Ups. Out of range operation*/
+
+       block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize);
+
+       /*OK lets deal with the first possible unaligned block*/
+       unalg = (off & (bdev->bdif->ph_bsize - 1));
+       if (unalg) {
+
+               uint32_t wlen = (bdev->bdif->ph_bsize - unalg) > len
+                                   ? len
+                                   : (bdev->bdif->ph_bsize - unalg);
+
+               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
+               if (r != EOK)
+                       return r;
+
+               memcpy(bdev->bdif->ph_bbuf + unalg, p, wlen);
+               r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
+               if (r != EOK)
+                       return r;
+
+               p += wlen;
+               len -= wlen;
+               block_idx++;
+       }
+
+       /*Aligned data*/
+       blen = len / bdev->bdif->ph_bsize;
+       r = ext4_bdif_bwrite(bdev, p, block_idx, blen);
+       if (r != EOK)
+               return r;
+
+       p += bdev->bdif->ph_bsize * blen;
+       len -= bdev->bdif->ph_bsize * blen;
+
+       block_idx += blen;
+
+       /*Rest of the data*/
+       if (len) {
+               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
+               if (r != EOK)
+                       return r;
+
+               memcpy(bdev->bdif->ph_bbuf, p, len);
+               r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
+               if (r != EOK)
+                       return r;
+       }
+
+       return r;
+}
+
+int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf,
+                        uint32_t len)
+{
+       uint64_t block_idx;
+       uint32_t blen;
+       uint32_t unalg;
+       int r = EOK;
+
+       uint8_t *p = (void *)buf;
+
+       ext4_assert(bdev && buf);
+
+       if (!bdev->bdif->ph_refctr)
+               return EIO;
+
+       if (off + len > bdev->part_size)
+               return EINVAL; /*Ups. Out of range operation*/
+
+       block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize);
+
+       /*OK lets deal with the first possible unaligned block*/
+       unalg = (off & (bdev->bdif->ph_bsize - 1));
+       if (unalg) {
+
+               uint32_t rlen = (bdev->bdif->ph_bsize - unalg) > len
+                                   ? len
+                                   : (bdev->bdif->ph_bsize - unalg);
+
+               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
+               if (r != EOK)
+                       return r;
+
+               memcpy(p, bdev->bdif->ph_bbuf + unalg, rlen);
+
+               p += rlen;
+               len -= rlen;
+               block_idx++;
+       }
+
+       /*Aligned data*/
+       blen = len / bdev->bdif->ph_bsize;
+
+       r = ext4_bdif_bread(bdev, p, block_idx, blen);
+       if (r != EOK)
+               return r;
+
+       p += bdev->bdif->ph_bsize * blen;
+       len -= bdev->bdif->ph_bsize * blen;
+
+       block_idx += blen;
+
+       /*Rest of the data*/
+       if (len) {
+               r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1);
+               if (r != EOK)
+                       return r;
+
+               memcpy(p, bdev->bdif->ph_bbuf, len);
+       }
+
+       return r;
+}
+
+int ext4_block_cache_flush(struct ext4_blockdev *bdev)
+{
+       while (!SLIST_EMPTY(&bdev->bc->dirty_list)) {
+               int r;
+               struct ext4_buf *buf = SLIST_FIRST(&bdev->bc->dirty_list);
+               ext4_assert(buf);
+               r = ext4_block_flush_buf(bdev, buf);
+               if (r != EOK)
+                       return r;
+
+       }
+       return EOK;
+}
+
+int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off)
+{
+       if (on_off)
+               bdev->cache_write_back++;
+
+       if (!on_off && bdev->cache_write_back)
+               bdev->cache_write_back--;
+
+       if (bdev->cache_write_back)
+               return EOK;
+
+       /*Flush data in all delayed cache blocks*/
+       return ext4_block_cache_flush(bdev);
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_crc32.c b/src/ext4_crc32.c
new file mode 100644 (file)
index 0000000..a6ee6b0
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * All rights reserved.
+ *
+ * Based on FreeBSD.
+ *
+ * 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_crc32c.c
+ * @brief Crc32c routine. Taken from FreeBSD kernel.
+ */
+
+#include "ext4_config.h"
+#include "ext4_crc32.h"
+
+static const uint32_t crc32_tab[] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+       0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+       0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+       0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+       0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+       0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+       0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+       0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+       0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+       0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+       0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+       0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+       0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+       0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+       0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+       0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+       0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+       0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+       0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+       0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+       0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+       0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+/* */
+/* CRC LOOKUP TABLE */
+/* ================ */
+/* The following CRC lookup table was generated automagically */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
+/* Program V1.0 using the following model parameters: */
+/* */
+/* Width : 4 bytes. */
+/* Poly : 0x1EDC6F41L */
+/* Reverse : TRUE. */
+/* */
+/* For more information on the Rocksoft^tm Model CRC Algorithm, */
+/* see the document titled "A Painless Guide to CRC Error */
+/* Detection Algorithms" by Ross Williams */
+/* (ross@guest.adelaide.edu.au.). This document is likely to be */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
+/* */
+static const uint32_t crc32c_tab[256] = {
+    0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL,
+    0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL,
+    0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L,
+    0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+    0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L,
+    0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL,
+    0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L,
+    0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+    0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL,
+    0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L,
+    0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L,
+    0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+    0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL,
+    0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L,
+    0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L,
+    0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+    0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL,
+    0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL,
+    0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L,
+    0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+    0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL,
+    0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L,
+    0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL,
+    0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+    0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL,
+    0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL,
+    0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L,
+    0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+    0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L,
+    0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL,
+    0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL,
+    0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+    0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L,
+    0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL,
+    0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L,
+    0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+    0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L,
+    0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL,
+    0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L,
+    0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+    0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L,
+    0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L,
+    0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L,
+    0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+    0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL,
+    0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L,
+    0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L,
+    0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+    0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL,
+    0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L,
+    0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L,
+    0xAD7D5351L};
+
+static inline uint32_t crc32(uint32_t crc, const void *buf, uint32_t size,
+                            const uint32_t *tab)
+{
+       const uint8_t *p = (const uint8_t *)buf;
+
+       while (size--)
+               crc = tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+       return (crc);
+}
+
+uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size)
+{
+       return crc32(crc, buf, size, crc32_tab);
+}
+
+uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size)
+{
+       return crc32(crc, buf, size, crc32c_tab);
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_debug.c b/src/ext4_debug.c
new file mode 100644 (file)
index 0000000..b38593b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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_debug.c
+ * @brief Debug printf and assert macros.
+ */
+
+#include "ext4_config.h"
+#include "ext4_debug.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+static uint32_t debug_mask;
+
+void ext4_dmask_set(uint32_t m)
+{
+       debug_mask |= m;
+}
+
+void ext4_dmask_clr(uint32_t m)
+{
+       debug_mask &= ~m;
+}
+
+uint32_t ext4_dmask_get(void)
+{
+       return debug_mask;
+}
+
+
+
+/**
+ * @}
+ */
diff --git a/src/ext4_dir.c b/src/ext4_dir.c
new file mode 100644 (file)
index 0000000..f26dc6d
--- /dev/null
@@ -0,0 +1,690 @@
+/*
+ * 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_crc32.h"
+#include "ext4_inode.h"
+#include "ext4_fs.h"
+
+#include <string.h>
+
+/****************************************************************************/
+
+/* Walk through a dirent block to find a checksum "dirent" at the tail */
+static struct ext4_dir_entry_tail *
+ext4_dir_get_tail(struct ext4_inode_ref *inode_ref,
+               struct ext4_dir_en *de)
+{
+       struct ext4_dir_entry_tail *t;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       t = EXT4_DIRENT_TAIL(de, ext4_sb_get_block_size(sb));
+
+       if (t->reserved_zero1 || t->reserved_zero2)
+               return NULL;
+       if (to_le16(t->rec_len) != sizeof(struct ext4_dir_entry_tail))
+               return NULL;
+       if (t->reserved_ft != EXT4_DIRENTRY_DIR_CSUM)
+               return NULL;
+
+       return t;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_dir_csum(struct ext4_inode_ref *inode_ref,
+                             struct ext4_dir_en *dirent, int size)
+{
+       uint32_t csum;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint32_t ino_index = to_le32(inode_ref->index);
+       uint32_t ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode));
+
+       /* First calculate crc32 checksum against fs uuid */
+       csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
+       /* Then calculate crc32 checksum against inode number
+        * and inode generation */
+       csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index));
+       csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen));
+       /* Finally calculate crc32 checksum against directory entries */
+       csum = ext4_crc32c(csum, dirent, size);
+       return csum;
+}
+#else
+#define ext4_dir_csum(...) 0
+#endif
+
+bool ext4_dir_csum_verify(struct ext4_inode_ref *inode_ref,
+                             struct ext4_dir_en *dirent)
+{
+#ifdef CONFIG_META_CSUM_ENABLE
+       struct ext4_dir_entry_tail *t;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       /* Compute the checksum only if the filesystem supports it */
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               t = ext4_dir_get_tail(inode_ref, dirent);
+               if (!t) {
+                       /* There is no space to hold the checksum */
+                       return false;
+               }
+
+               ptrdiff_t __unused diff = (char *)t - (char *)dirent;
+               uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff);
+               if (t->checksum != to_le32(csum))
+                       return false;
+
+       }
+#endif
+       return true;
+}
+
+void ext4_dir_init_entry_tail(struct ext4_dir_entry_tail *t)
+{
+       memset(t, 0, sizeof(struct ext4_dir_entry_tail));
+       t->rec_len = to_le16(sizeof(struct ext4_dir_entry_tail));
+       t->reserved_ft = EXT4_DIRENTRY_DIR_CSUM;
+}
+
+void ext4_dir_set_csum(struct ext4_inode_ref *inode_ref,
+                          struct ext4_dir_en *dirent)
+{
+       struct ext4_dir_entry_tail *t;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       /* Compute the checksum only if the filesystem supports it */
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               t = ext4_dir_get_tail(inode_ref, dirent);
+               if (!t) {
+                       /* There is no space to hold the checksum */
+                       return;
+               }
+
+               ptrdiff_t __unused diff = (char *)t - (char *)dirent;
+               uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff);
+               t->checksum = to_le32(csum);
+       }
+}
+
+/**@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_dir_iter *it,
+                                uint32_t block_size)
+{
+       uint32_t off_in_block = it->curr_off % block_size;
+       struct ext4_sblock *sb = &it->inode_ref->fs->sb;
+
+       it->curr = NULL;
+
+       /* Ensure proper alignment */
+       if ((off_in_block % 4) != 0)
+               return EIO;
+
+       /* Ensure that the core of the entry does not overflow the block */
+       if (off_in_block > block_size - 8)
+               return EIO;
+
+       struct ext4_dir_en *en;
+       en = (void *)(it->curr_blk.data + off_in_block);
+
+       /* Ensure that the whole entry does not overflow the block */
+       uint16_t length = ext4_dir_en_get_entry_len(en);
+       if (off_in_block + length > block_size)
+               return EIO;
+
+       /* Ensure the name length is not too large */
+       if (ext4_dir_en_get_name_len(sb, en) > length - 8)
+               return EIO;
+
+       /* Everything OK - "publish" the entry */
+       it->curr = en;
+       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_dir_iter *it, uint64_t pos)
+{
+       struct ext4_sblock *sb = &it->inode_ref->fs->sb;
+       struct ext4_inode *inode = it->inode_ref->inode;
+       struct ext4_blockdev *bdev = it->inode_ref->fs->bdev;
+       uint64_t size = ext4_inode_get_size(sb, inode);
+       int r;
+
+       /* The iterator is not valid until we seek to the desired position */
+       it->curr = NULL;
+
+       /* Are we at the end? */
+       if (pos >= size) {
+               if (it->curr_blk.lb_id) {
+
+                       r = ext4_block_set(bdev, &it->curr_blk);
+                       it->curr_blk.lb_id = 0;
+                       if (r != EOK)
+                               return r;
+               }
+
+               it->curr_off = pos;
+               return EOK;
+       }
+
+       /* Compute next block address */
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint64_t current_blk_idx = it->curr_off / block_size;
+       uint32_t next_blk_idx = (uint32_t)(pos / block_size);
+
+       /*
+        * If we don't have a block or are moving across block boundary,
+        * we need to get another block
+        */
+       if ((it->curr_blk.lb_id == 0) ||
+           (current_blk_idx != next_blk_idx)) {
+               if (it->curr_blk.lb_id) {
+                       r = ext4_block_set(bdev, &it->curr_blk);
+                       it->curr_blk.lb_id = 0;
+
+                       if (r != EOK)
+                               return r;
+               }
+
+               ext4_fsblk_t next_blk;
+               r = ext4_fs_get_inode_dblk_idx(it->inode_ref, next_blk_idx,
+                                              &next_blk, false);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_trans_block_get(bdev, &it->curr_blk, next_blk);
+               if (r != EOK) {
+                       it->curr_blk.lb_id = 0;
+                       return r;
+               }
+       }
+
+       it->curr_off = pos;
+       return ext4_dir_iterator_set(it, block_size);
+}
+
+int ext4_dir_iterator_init(struct ext4_dir_iter *it,
+                          struct ext4_inode_ref *inode_ref, uint64_t pos)
+{
+       it->inode_ref = inode_ref;
+       it->curr = 0;
+       it->curr_off = 0;
+       it->curr_blk.lb_id = 0;
+
+       return ext4_dir_iterator_seek(it, pos);
+}
+
+int ext4_dir_iterator_next(struct ext4_dir_iter *it)
+{
+       int r = EOK;
+       uint16_t skip;
+
+       while (r == EOK) {
+               skip = ext4_dir_en_get_entry_len(it->curr);
+               r = ext4_dir_iterator_seek(it, it->curr_off + skip);
+
+               if (!it->curr)
+                       break;
+               /*Skip NULL referenced entry*/
+               if (ext4_dir_en_get_inode(it->curr) != 0)
+                       break;
+       }
+
+       return r;
+}
+
+int ext4_dir_iterator_fini(struct ext4_dir_iter *it)
+{
+       it->curr = 0;
+
+       if (it->curr_blk.lb_id)
+               return ext4_block_set(it->inode_ref->fs->bdev, &it->curr_blk);
+
+       return EOK;
+}
+
+void ext4_dir_write_entry(struct ext4_sblock *sb, struct ext4_dir_en *en,
+                         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 type of entry */
+       switch (ext4_inode_type(sb, child->inode)) {
+       case EXT4_INODE_MODE_DIRECTORY:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_DIR);
+               break;
+       case EXT4_INODE_MODE_FILE:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_REG_FILE);
+               break;
+       case EXT4_INODE_MODE_SOFTLINK:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SYMLINK);
+               break;
+       default:
+               /* FIXME: right now we only support 3 inode type. */
+               ext4_assert(0);
+       }
+
+       /* Set basic attributes */
+       ext4_dir_en_set_inode(en, child->index);
+       ext4_dir_en_set_entry_len(en, entry_len);
+       ext4_dir_en_set_name_len(sb, en, (uint16_t)name_len);
+
+       /* Write name */
+       memcpy(en->name, name, name_len);
+}
+
+int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
+                      uint32_t name_len, struct ext4_inode_ref *child)
+{
+       int r;
+       struct ext4_fs *fs = parent->fs;
+       struct ext4_sblock *sb = &parent->fs->sb;
+
+#if CONFIG_DIR_INDEX_ENABLE
+       /* Index adding (if allowed) */
+       if ((ext4_sb_feature_com(sb, EXT4_FCOM_DIR_INDEX)) &&
+           (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
+               r = ext4_dir_dx_add_entry(parent, child, name);
+
+               /* Check if index is not corrupted */
+               if (r != EXT4_ERR_BAD_DX_DIR) {
+                       if (r != EOK)
+                               return r;
+
+                       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;
+       ext4_fsblk_t fblock = 0;
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint64_t inode_size = ext4_inode_get_size(sb, parent->inode);
+       uint32_t total_blocks = (uint32_t)(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) {
+               r = ext4_fs_get_inode_dblk_idx(parent, iblock, &fblock, false);
+               if (r != EOK)
+                       return r;
+
+               struct ext4_block block;
+               r = ext4_trans_block_get(fs->bdev, &block, fblock);
+               if (r != EOK)
+                       return r;
+
+               if (!ext4_dir_csum_verify(parent, (void *)block.data)) {
+                       ext4_dbg(DEBUG_DIR,
+                                DBG_WARN "Leaf block checksum failed."
+                                "Inode: %" PRIu32", "
+                                "Block: %" PRIu32"\n",
+                                parent->index,
+                                iblock);
+               }
+
+               /* If adding is successful, function can finish */
+               r = ext4_dir_try_insert_entry(sb, parent, &block, child,
+                                               name, name_len);
+               if (r == EOK)
+                       success = true;
+
+               r = ext4_block_set(fs->bdev, &block);
+               if (r != EOK)
+                       return r;
+
+               if (success)
+                       return EOK;
+       }
+
+       /* No free block found - needed to allocate next data block */
+
+       iblock = 0;
+       fblock = 0;
+       r = ext4_fs_append_inode_dblk(parent, &fblock, &iblock);
+       if (r != EOK)
+               return r;
+
+       /* Load new block */
+       struct ext4_block b;
+
+       r = ext4_trans_block_get_noread(fs->bdev, &b, fblock);
+       if (r != EOK)
+               return r;
+
+       /* Fill block with zeroes */
+       memset(b.data, 0, block_size);
+       struct ext4_dir_en *blk_en = (void *)b.data;
+
+       /* Save new block */
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint16_t el = block_size - sizeof(struct ext4_dir_entry_tail);
+               ext4_dir_write_entry(sb, blk_en, el, child, name, name_len);
+               ext4_dir_init_entry_tail(EXT4_DIRENT_TAIL(b.data, block_size));
+       } else {
+               ext4_dir_write_entry(sb, blk_en, block_size, child, name,
+                               name_len);
+       }
+
+       ext4_dir_set_csum(parent, (void *)b.data);
+       ext4_trans_set_block_dirty(b.buf);
+       r = ext4_block_set(fs->bdev, &b);
+
+       return r;
+}
+
+int ext4_dir_find_entry(struct ext4_dir_search_result *result,
+                       struct ext4_inode_ref *parent, const char *name,
+                       uint32_t name_len)
+{
+       int r;
+       struct ext4_sblock *sb = &parent->fs->sb;
+
+       /* Entry clear */
+       result->block.lb_id = 0;
+       result->dentry = NULL;
+
+#if CONFIG_DIR_INDEX_ENABLE
+       /* Index search */
+       if ((ext4_sb_feature_com(sb, EXT4_FCOM_DIR_INDEX)) &&
+           (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
+               r = ext4_dir_dx_find_entry(result, parent, name_len, name);
+               /* Check if index is not corrupted */
+               if (r != EXT4_ERR_BAD_DX_DIR) {
+                       if (r != EOK)
+                               return r;
+
+                       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;
+       ext4_fsblk_t fblock;
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint64_t inode_size = ext4_inode_get_size(sb, parent->inode);
+       uint32_t total_blocks = (uint32_t)(inode_size / block_size);
+
+       /* Walk through all data blocks */
+       for (iblock = 0; iblock < total_blocks; ++iblock) {
+               /* Load block address */
+               r = ext4_fs_get_inode_dblk_idx(parent, iblock, &fblock, false);
+               if (r != EOK)
+                       return r;
+
+               /* Load data block */
+               struct ext4_block b;
+               r = ext4_trans_block_get(parent->fs->bdev, &b, fblock);
+               if (r != EOK)
+                       return r;
+
+               if (!ext4_dir_csum_verify(parent, (void *)b.data)) {
+                       ext4_dbg(DEBUG_DIR,
+                                DBG_WARN "Leaf block checksum failed."
+                                "Inode: %" PRIu32", "
+                                "Block: %" PRIu32"\n",
+                                parent->index,
+                                iblock);
+               }
+
+               /* Try to find entry in block */
+               struct ext4_dir_en *res_entry;
+               r = ext4_dir_find_in_block(&b, sb, name_len, name, &res_entry);
+               if (r == EOK) {
+                       result->block = b;
+                       result->dentry = res_entry;
+                       return EOK;
+               }
+
+               /* Entry not found - put block and continue to the next block */
+
+               r = ext4_block_set(parent->fs->bdev, &b);
+               if (r != EOK)
+                       return r;
+       }
+
+       return ENOENT;
+}
+
+int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name,
+                         uint32_t name_len)
+{
+       struct ext4_sblock *sb = &parent->fs->sb;
+       /* Check if removing from directory */
+       if (!ext4_inode_is_type(sb, parent->inode, EXT4_INODE_MODE_DIRECTORY))
+               return ENOTDIR;
+
+       /* Try to find entry */
+       struct ext4_dir_search_result result;
+       int rc = ext4_dir_find_entry(&result, parent, name, name_len);
+       if (rc != EOK)
+               return rc;
+
+       /* Invalidate entry */
+       ext4_dir_en_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_dir_en *tmp_de =(void *)result.block.data;
+               uint16_t de_len = ext4_dir_en_get_entry_len(tmp_de);
+
+               /* Find direct predecessor of removed entry */
+               while ((offset + de_len) < pos) {
+                       offset += ext4_dir_en_get_entry_len(tmp_de);
+                       tmp_de = (void *)(result.block.data + offset);
+                       de_len = ext4_dir_en_get_entry_len(tmp_de);
+               }
+
+               ext4_assert(de_len + offset == pos);
+
+               /* Add to removed entry length to predecessor's length */
+               uint16_t del_len;
+               del_len = ext4_dir_en_get_entry_len(result.dentry);
+               ext4_dir_en_set_entry_len(tmp_de, de_len + del_len);
+       }
+
+       ext4_dir_set_csum(parent,
+                       (struct ext4_dir_en *)result.block.data);
+       ext4_trans_set_block_dirty(result.block.buf);
+
+       return ext4_dir_destroy_result(parent, &result);
+}
+
+int ext4_dir_try_insert_entry(struct ext4_sblock *sb,
+                             struct ext4_inode_ref *inode_ref,
+                             struct ext4_block *dst_blk,
+                             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_dir_entry) + name_len;
+
+       if ((required_len % 4) != 0)
+               required_len += 4 - (required_len % 4);
+
+       /* Initialize pointers, stop means to upper bound */
+       struct ext4_dir_en *start = (void *)dst_blk->data;
+       struct ext4_dir_en *stop = (void *)(dst_blk->data + block_size);
+
+       /*
+        * Walk through the block and check for invalid entries
+        * or entries with free space for new entry
+        */
+       while (start < stop) {
+               uint32_t inode = ext4_dir_en_get_inode(start);
+               uint16_t rec_len = ext4_dir_en_get_entry_len(start);
+               uint8_t itype = ext4_dir_en_get_inode_type(sb, start);
+
+               /* If invalid and large enough entry, use it */
+               if ((inode == 0) && (itype != EXT4_DIRENTRY_DIR_CSUM) &&
+                   (rec_len >= required_len)) {
+                       ext4_dir_write_entry(sb, start, rec_len, child, name,
+                                            name_len);
+                       ext4_dir_set_csum(inode_ref, (void *)dst_blk->data);
+                       ext4_trans_set_block_dirty(dst_blk->buf);
+
+                       return EOK;
+               }
+
+               /* Valid entry, try to split it */
+               if (inode != 0) {
+                       uint16_t used_len;
+                       used_len = ext4_dir_en_get_name_len(sb, start);
+
+                       uint16_t sz;
+                       sz = sizeof(struct ext4_fake_dir_entry) + used_len;
+
+                       if ((used_len % 4) != 0)
+                               sz += 4 - (used_len % 4);
+
+                       uint16_t free_space = rec_len - sz;
+
+                       /* There is free space for new entry */
+                       if (free_space >= required_len) {
+                               /* Cut tail of current entry */
+                               struct ext4_dir_en * new_entry;
+                               new_entry = (void *)((uint8_t *)start + sz);
+                               ext4_dir_en_set_entry_len(start, sz);
+                               ext4_dir_write_entry(sb, new_entry, free_space,
+                                                    child, name, name_len);
+
+                               ext4_dir_set_csum(inode_ref,
+                                                 (void *)dst_blk->data);
+                               ext4_trans_set_block_dirty(dst_blk->buf);
+                               return EOK;
+                       }
+               }
+
+               /* Jump to the next entry */
+               start = (void *)((uint8_t *)start + 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_dir_en **res_entry)
+{
+       /* Start from the first entry in block */
+       struct ext4_dir_en *de = (struct ext4_dir_en *)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 *)de < addr_limit) {
+               /* Termination condition */
+               if ((uint8_t *)de + name_len > addr_limit)
+                       break;
+
+               /* Valid entry - check it */
+               if (ext4_dir_en_get_inode(de) != 0) {
+                       /* For more efficient compare only lengths firstly*/
+                       uint16_t el = ext4_dir_en_get_name_len(sb, de);
+                       if (el == name_len) {
+                               /* Compare names */
+                               if (memcmp(name, de->name, name_len) == 0) {
+                                       *res_entry = de;
+                                       return EOK;
+                               }
+                       }
+               }
+
+               uint16_t de_len = ext4_dir_en_get_entry_len(de);
+
+               /* Corrupted entry */
+               if (de_len == 0)
+                       return EINVAL;
+
+               /* Jump to next entry */
+               de = (struct ext4_dir_en *)((uint8_t *)de + de_len);
+       }
+
+       /* Entry not found */
+       return ENOENT;
+}
+
+int ext4_dir_destroy_result(struct ext4_inode_ref *parent,
+                           struct ext4_dir_search_result *result)
+{
+       if (result->block.lb_id)
+               return ext4_block_set(parent->fs->bdev, &result->block);
+
+       return EOK;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_dir_idx.c b/src/ext4_dir_idx.c
new file mode 100644 (file)
index 0000000..81da0de
--- /dev/null
@@ -0,0 +1,1439 @@
+/*
+ * 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_inode.h"
+#include "ext4_crc32.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_rinfo_get_hash_version(struct ext4_dir_idx_rinfo *ri)
+{
+       return ri->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_rinfo_set_hash_version(struct ext4_dir_idx_rinfo *ri, uint8_t v)
+{
+       ri->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_rinfo_get_info_length(struct ext4_dir_idx_rinfo *ri)
+{
+       return ri->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_dir_idx_rinfo *ri,
+                                     uint8_t len)
+{
+       ri->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_rinfo_get_indirect_levels(struct ext4_dir_idx_rinfo *ri)
+{
+       return ri->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_rinfo_set_indirect_levels(struct ext4_dir_idx_rinfo *ri, uint8_t l)
+{
+       ri->indirect_levels = l;
+}
+
+/**@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_climit_get_limit(struct ext4_dir_idx_climit *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_climit_set_limit(struct ext4_dir_idx_climit *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_climit_get_count(struct ext4_dir_idx_climit *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_climit_set_count(struct ext4_dir_idx_climit *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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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);
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_dir_dx_checksum(struct ext4_inode_ref *inode_ref, void *de,
+                                    int count_offset, int count,
+                                    struct ext4_dir_idx_tail *t)
+{
+       uint32_t orig_cum, csum = 0;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       int sz;
+
+       /* Compute the checksum only if the filesystem supports it */
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t ino_index = to_le32(inode_ref->index);
+               uint32_t ino_gen;
+               ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode));
+
+               sz = count_offset + (count * sizeof(struct ext4_dir_idx_tail));
+               orig_cum = t->checksum;
+               t->checksum = 0;
+               /* First calculate crc32 checksum against fs uuid */
+               csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against inode number
+                * and inode generation */
+               csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index));
+               csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen));
+               /* After that calculate crc32 checksum against all the dx_entry */
+               csum = ext4_crc32c(csum, de, sz);
+               /* Finally calculate crc32 checksum for dx_tail */
+               csum = ext4_crc32c(csum, t, sizeof(struct ext4_dir_idx_tail));
+               t->checksum = orig_cum;
+       }
+       return csum;
+}
+
+static struct ext4_dir_idx_climit *
+ext4_dir_dx_get_climit(struct ext4_inode_ref *inode_ref,
+                          struct ext4_dir_en *dirent, int *offset)
+{
+       struct ext4_dir_en *dp;
+       struct ext4_dir_idx_root *root;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint16_t entry_len = ext4_dir_en_get_entry_len(dirent);
+       int count_offset;
+
+
+       if (entry_len == 12) {
+               root = (struct ext4_dir_idx_root *)dirent;
+               dp = (struct ext4_dir_en *)&root->dots[1];
+               if (ext4_dir_en_get_entry_len(dp) != (block_size - 12))
+                       return NULL;
+               if (root->info.reserved_zero)
+                       return NULL;
+               if (root->info.info_length != sizeof(struct ext4_dir_idx_rinfo))
+                       return NULL;
+               count_offset = 32;
+       } else if (entry_len == block_size) {
+               count_offset = 8;
+       } else {
+               return NULL;
+       }
+
+       if (offset)
+               *offset = count_offset;
+       return (struct ext4_dir_idx_climit *)(((char *)dirent) + count_offset);
+}
+
+/*
+ * BIG FAT NOTES:
+ *       Currently we do not verify the checksum of HTree node.
+ */
+static bool ext4_dir_dx_csum_verify(struct ext4_inode_ref *inode_ref,
+                                   struct ext4_dir_en *de)
+{
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       int coff, limit, cnt;
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               struct ext4_dir_idx_climit *climit;
+               climit = ext4_dir_dx_get_climit(inode_ref, de, &coff);
+               if (!climit) {
+                       /* Directory seems corrupted. */
+                       return true;
+               }
+               struct ext4_dir_idx_tail *t;
+               limit = ext4_dir_dx_climit_get_limit(climit);
+               cnt = ext4_dir_dx_climit_get_count(climit);
+               if (coff + (limit * sizeof(struct ext4_dir_idx_entry)) >
+                   (block_size - sizeof(struct ext4_dir_idx_tail))) {
+                       /* There is no space to hold the checksum */
+                       return true;
+               }
+               t = (void *)(((struct ext4_dir_idx_entry *)climit) + limit);
+
+               uint32_t c;
+               c = to_le32(ext4_dir_dx_checksum(inode_ref, de, coff, cnt, t));
+               if (t->checksum != c)
+                       return false;
+       }
+       return true;
+}
+
+
+static void ext4_dir_set_dx_csum(struct ext4_inode_ref *inode_ref,
+                                struct ext4_dir_en *dirent)
+{
+       int coff, limit, count;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               struct ext4_dir_idx_climit *climit;
+               climit = ext4_dir_dx_get_climit(inode_ref, dirent, &coff);
+               if (!climit) {
+                       /* Directory seems corrupted. */
+                       return;
+               }
+               struct ext4_dir_idx_tail *t;
+               limit = ext4_dir_dx_climit_get_limit(climit);
+               count = ext4_dir_dx_climit_get_count(climit);
+               if (coff + (limit * sizeof(struct ext4_dir_idx_entry)) >
+                  (block_size - sizeof(struct ext4_dir_idx_tail))) {
+                       /* There is no space to hold the checksum */
+                       return;
+               }
+
+               t = (void *)(((struct ext4_dir_idx_entry *)climit) + limit);
+               t->checksum = to_le32(ext4_dir_dx_checksum(inode_ref, dirent,
+                                       coff, count, t));
+       }
+}
+#else
+#define ext4_dir_dx_csum_verify(...) true
+#define ext4_dir_set_dx_csum(...)
+#endif
+
+/****************************************************************************/
+
+int ext4_dir_dx_init(struct ext4_inode_ref *dir, struct ext4_inode_ref *parent)
+{
+       /* Load block 0, where will be index root located */
+       ext4_fsblk_t fblock;
+       uint32_t iblock = 0;
+       bool need_append =
+               (ext4_inode_get_size(&dir->fs->sb, dir->inode)
+                       < EXT4_DIR_DX_INIT_BCNT)
+               ? true : false;
+       struct ext4_sblock *sb = &dir->fs->sb;
+       uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb);
+       struct ext4_block block;
+
+       int rc;
+
+       if (!need_append)
+               rc = ext4_fs_init_inode_dblk_idx(dir, iblock, &fblock);
+       else
+               rc = ext4_fs_append_inode_dblk(dir, &fblock, &iblock);
+
+       if (rc != EOK)
+               return rc;
+
+       rc = ext4_trans_block_get_noread(dir->fs->bdev, &block, fblock);
+       if (rc != EOK)
+               return rc;
+
+       /* Initialize pointers to data structures */
+       struct ext4_dir_idx_root *root = (void *)block.data;
+       struct ext4_dir_idx_rinfo *info = &(root->info);
+
+       memset(root, 0, sizeof(struct ext4_dir_idx_root));
+       struct ext4_dir_en *de;
+
+       /* Initialize dot entries */
+       de = (struct ext4_dir_en *)root->dots;
+       ext4_dir_write_entry(sb, de, 12, dir, ".", strlen("."));
+
+       de = (struct ext4_dir_en *)(root->dots + 1);
+       uint16_t elen = block_size - 12;
+       ext4_dir_write_entry(sb, de, elen, parent, "..", strlen(".."));
+
+       /* Initialize root info structure */
+       uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version);
+
+       ext4_dir_dx_rinfo_set_hash_version(info, hash_version);
+       ext4_dir_dx_rinfo_set_indirect_levels(info, 0);
+       ext4_dir_dx_root_info_set_info_length(info, 8);
+
+       /* Set limit and current number of entries */
+       struct ext4_dir_idx_climit *climit;
+       climit = (struct ext4_dir_idx_climit *)&root->en;
+
+       ext4_dir_dx_climit_set_count(climit, 1);
+
+       uint32_t entry_space;
+       entry_space = block_size - 2 * sizeof(struct ext4_dir_idx_dot_en) -
+                       sizeof(struct ext4_dir_idx_rinfo);
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               entry_space -= sizeof(struct ext4_dir_idx_tail);
+
+       uint16_t root_limit = entry_space / sizeof(struct ext4_dir_idx_entry);
+       ext4_dir_dx_climit_set_limit(climit, root_limit);
+
+       /* Append new block, where will be new entries inserted in the future */
+       iblock++;
+       if (!need_append)
+               rc = ext4_fs_init_inode_dblk_idx(dir, iblock, &fblock);
+       else
+               rc = ext4_fs_append_inode_dblk(dir, &fblock, &iblock);
+
+       if (rc != EOK) {
+               ext4_block_set(dir->fs->bdev, &block);
+               return rc;
+       }
+
+       struct ext4_block new_block;
+       rc = ext4_trans_block_get_noread(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_dir_en *be = (void *)new_block.data;
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint16_t len = block_size - sizeof(struct ext4_dir_entry_tail);
+               ext4_dir_en_set_entry_len(be, len);
+               ext4_dir_en_set_name_len(sb, be, 0);
+               ext4_dir_en_set_inode_type(sb, be, EXT4_DE_UNKNOWN);
+               ext4_dir_init_entry_tail(EXT4_DIRENT_TAIL(be, block_size));
+               ext4_dir_set_csum(dir, be);
+       } else {
+               ext4_dir_en_set_entry_len(be, block_size);
+       }
+
+       ext4_dir_en_set_inode(be, 0);
+
+       ext4_trans_set_block_dirty(new_block.buf);
+       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_dir_idx_entry *entry = root->en;
+       ext4_dir_dx_entry_set_block(entry, iblock);
+
+       ext4_dir_set_dx_csum(dir, (struct ext4_dir_en *)block.data);
+       ext4_trans_set_block_dirty(block.buf);
+
+       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_dir_idx_root *root;
+
+       root = (struct ext4_dir_idx_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_dir_idx_dot_en);
+       entry_space -= sizeof(struct ext4_dir_idx_rinfo);
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               entry_space -= sizeof(struct ext4_dir_idx_tail);
+       entry_space = entry_space / sizeof(struct ext4_dir_idx_entry);
+
+       struct ext4_dir_idx_climit *climit = (void *)&root->en;
+       uint16_t limit = ext4_dir_dx_climit_get_limit(climit);
+       if (limit != entry_space)
+               return EXT4_ERR_BAD_DX_DIR;
+
+       /* Check hash version and modify if necessary */
+       hinfo->hash_version = ext4_dir_dx_rinfo_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_dir_idx_block **dx_block,
+                               struct ext4_dir_idx_block *dx_blocks)
+{
+       struct ext4_dir_idx_root *root;
+       struct ext4_dir_idx_entry *entries;
+       struct ext4_dir_idx_entry *p;
+       struct ext4_dir_idx_entry *q;
+       struct ext4_dir_idx_entry *m;
+       struct ext4_dir_idx_entry *at;
+       ext4_fsblk_t fblk;
+       uint32_t block_size;
+       uint16_t limit;
+       uint16_t entry_space;
+       uint8_t ind_level;
+       int r;
+
+       struct ext4_dir_idx_block *tmp_dx_blk = dx_blocks;
+       struct ext4_block *tmp_blk = root_block;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       block_size = ext4_sb_get_block_size(sb);
+       root = (struct ext4_dir_idx_root *)root_block->data;
+       entries = (struct ext4_dir_idx_entry *)&root->en;
+       limit = ext4_dir_dx_climit_get_limit((void *)entries);
+       ind_level = ext4_dir_dx_rinfo_get_indirect_levels(&root->info);
+
+       /* Walk through the index tree */
+       while (true) {
+               uint16_t cnt = ext4_dir_dx_climit_get_count((void *)entries);
+               if ((cnt == 0) || (cnt > limit))
+                       return EXT4_ERR_BAD_DX_DIR;
+
+               /* Do binary search in every node */
+               p = entries + 1;
+               q = entries + cnt - 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_blk->b, tmp_blk, sizeof(struct ext4_block));
+               tmp_dx_blk->entries = entries;
+               tmp_dx_blk->position = at;
+
+               /* Is algorithm in the leaf? */
+               if (ind_level == 0) {
+                       *dx_block = tmp_dx_blk;
+                       return EOK;
+               }
+
+               /* Goto child node */
+               uint32_t n_blk = ext4_dir_dx_entry_get_block(at);
+
+               ind_level--;
+
+               r = ext4_fs_get_inode_dblk_idx(inode_ref, n_blk, &fblk, false);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_trans_block_get(inode_ref->fs->bdev, tmp_blk, fblk);
+               if (r != EOK)
+                       return r;
+
+               entries = ((struct ext4_dir_idx_node *)tmp_blk->data)->entries;
+               limit = ext4_dir_dx_climit_get_limit((void *)entries);
+
+               entry_space = block_size - sizeof(struct ext4_fake_dir_entry);
+               if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+                       entry_space -= sizeof(struct ext4_dir_idx_tail);
+
+               entry_space = entry_space / sizeof(struct ext4_dir_idx_entry);
+
+               if (limit != entry_space) {
+                       ext4_block_set(inode_ref->fs->bdev, tmp_blk);
+                       return EXT4_ERR_BAD_DX_DIR;
+               }
+
+               if (!ext4_dir_dx_csum_verify(inode_ref, (void *)tmp_blk->data)) {
+                       ext4_dbg(DEBUG_DIR_IDX,
+                                       DBG_WARN "HTree checksum failed."
+                                       "Inode: %" PRIu32", "
+                                       "Block: %" PRIu32"\n",
+                                       inode_ref->index,
+                                       n_blk);
+               }
+
+               ++tmp_dx_blk;
+       }
+
+       /* 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_dir_idx_block *dx_block,
+                                 struct ext4_dir_idx_block *dx_blocks)
+{
+       int r;
+       uint32_t num_handles = 0;
+       ext4_fsblk_t blk_adr;
+       struct ext4_dir_idx_block *p = dx_block;
+
+       /* Try to find data block with next bunch of entries */
+       while (true) {
+               uint16_t cnt = ext4_dir_dx_climit_get_count((void *)p->entries);
+
+               p->position++;
+               if (p->position < p->entries + cnt)
+                       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 blk = ext4_dir_dx_entry_get_block(p->position);
+               r = ext4_fs_get_inode_dblk_idx(inode_ref, blk, &blk_adr, false);
+               if (r != EOK)
+                       return r;
+
+               struct ext4_block b;
+               r = ext4_trans_block_get(inode_ref->fs->bdev, &b, blk_adr);
+               if (r != EOK)
+                       return r;
+
+               if (!ext4_dir_dx_csum_verify(inode_ref, (void *)b.data)) {
+                       ext4_dbg(DEBUG_DIR_IDX,
+                                       DBG_WARN "HTree checksum failed."
+                                       "Inode: %" PRIu32", "
+                                       "Block: %" PRIu32"\n",
+                                       inode_ref->index,
+                                       blk);
+               }
+
+               p++;
+
+               /* Don't forget to put old block (prevent memory leak) */
+               r = ext4_block_set(inode_ref->fs->bdev, &p->b);
+               if (r != EOK)
+                       return r;
+
+               memcpy(&p->b, &b, sizeof(b));
+               p->entries = ((struct ext4_dir_idx_node *)b.data)->entries;
+               p->position = p->entries;
+       }
+
+       return ENOENT;
+}
+
+int ext4_dir_dx_find_entry(struct ext4_dir_search_result *result,
+                          struct ext4_inode_ref *inode_ref, size_t name_len,
+                          const char *name)
+{
+       /* Load direct block 0 (index root) */
+       ext4_fsblk_t root_block_addr;
+       int rc2;
+       int rc;
+       rc = ext4_fs_get_inode_dblk_idx(inode_ref,  0, &root_block_addr, false);
+       if (rc != EOK)
+               return rc;
+
+       struct ext4_fs *fs = inode_ref->fs;
+
+       struct ext4_block root_block;
+       rc = ext4_trans_block_get(fs->bdev, &root_block, root_block_addr);
+       if (rc != EOK)
+               return rc;
+
+       if (!ext4_dir_dx_csum_verify(inode_ref, (void *)root_block.data)) {
+               ext4_dbg(DEBUG_DIR_IDX,
+                        DBG_WARN "HTree root checksum failed."
+                        "Inode: %" PRIu32", "
+                        "Block: %" PRIu32"\n",
+                        inode_ref->index,
+                        (uint32_t)0);
+       }
+
+       /* 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_dir_idx_block dx_blocks[2];
+       struct ext4_dir_idx_block *dx_block;
+       struct ext4_dir_idx_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_blk_idx;
+               ext4_fsblk_t leaf_block_addr;
+               struct ext4_block b;
+
+               leaf_blk_idx = ext4_dir_dx_entry_get_block(dx_block->position);
+               rc = ext4_fs_get_inode_dblk_idx(inode_ref, leaf_blk_idx,
+                                               &leaf_block_addr, false);
+               if (rc != EOK)
+                       goto cleanup;
+
+               rc = ext4_trans_block_get(fs->bdev, &b, leaf_block_addr);
+               if (rc != EOK)
+                       goto cleanup;
+
+               if (!ext4_dir_csum_verify(inode_ref, (void *)b.data)) {
+                       ext4_dbg(DEBUG_DIR_IDX,
+                                DBG_WARN "HTree leaf block checksum failed."
+                                "Inode: %" PRIu32", "
+                                "Block: %" PRIu32"\n",
+                                inode_ref->index,
+                                leaf_blk_idx);
+               }
+
+               /* Linear search inside block */
+               struct ext4_dir_en *de;
+               rc = ext4_dir_find_in_block(&b, &fs->sb, name_len, name, &de);
+
+               /* Found => return it */
+               if (rc == EOK) {
+                       result->block = b;
+                       result->dentry = de;
+                       goto cleanup;
+               }
+
+               /* Not found, leave untouched */
+               rc2 = ext4_block_set(fs->bdev, &b);
+               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->b);
+               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 inode_ref   Directory i-node
+ * @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_inode_ref *inode_ref __unused,
+                        struct ext4_dir_idx_block *index_block,
+                        uint32_t hash, uint32_t iblock)
+{
+       struct ext4_dir_idx_entry *old_index_entry = index_block->position;
+       struct ext4_dir_idx_entry *new_index_entry = old_index_entry + 1;
+       struct ext4_dir_idx_climit *climit = (void *)index_block->entries;
+       struct ext4_dir_idx_entry *start_index = index_block->entries;
+       uint32_t count = ext4_dir_dx_climit_get_count(climit);
+
+       size_t bytes;
+       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_climit_set_count(climit, count + 1);
+       ext4_dir_set_dx_csum(inode_ref, (void *)index_block->b.data);
+       ext4_trans_set_block_dirty(index_block->b.buf);
+}
+
+/**@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_dir_idx_block *index_block,
+                                 struct ext4_block *new_data_block)
+{
+       int rc = EOK;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
+
+       /* Allocate buffer for directory entries */
+       uint8_t *entry_buffer = malloc(block_size);
+       if (entry_buffer == NULL)
+               return ENOMEM;
+
+       /* dot entry has the smallest size available */
+       uint32_t max_ecnt = block_size / sizeof(struct ext4_dir_idx_dot_en);
+
+       /* Allocate sort entry */
+       struct ext4_dx_sort_entry *sort;
+
+       sort = malloc(max_ecnt * sizeof(struct ext4_dx_sort_entry));
+       if (sort == NULL) {
+               free(entry_buffer);
+               return ENOMEM;
+       }
+
+       uint32_t idx = 0;
+       uint32_t real_size = 0;
+
+       /* Initialize hinfo */
+       struct ext4_hash_info hinfo_tmp;
+       memcpy(&hinfo_tmp, hinfo, sizeof(struct ext4_hash_info));
+
+       /* Load all valid entries to the buffer */
+       struct ext4_dir_en *de = (void *)old_data_block->data;
+       uint8_t *entry_buffer_ptr = entry_buffer;
+       while ((void *)de < (void *)(old_data_block->data + block_size)) {
+               /* Read only valid entries */
+               if (ext4_dir_en_get_inode(de) && de->name_len) {
+                       uint16_t len = ext4_dir_en_get_name_len(sb, de);
+                       rc = ext4_dir_dx_hash_string(&hinfo_tmp, len,
+                                                    (char *)de->name);
+                       if (rc != EOK) {
+                               free(sort);
+                               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, de, rec_len);
+
+                       sort[idx].dentry = entry_buffer_ptr;
+                       sort[idx].rec_len = rec_len;
+                       sort[idx].hash = hinfo_tmp.hash;
+
+                       entry_buffer_ptr += rec_len;
+                       real_size += rec_len;
+                       idx++;
+               }
+
+               size_t elen = ext4_dir_en_get_entry_len(de);
+               de = (void *)((uint8_t *)de + elen);
+       }
+
+/* Sort all entries */
+#if CONFIG_DIR_INDEX_COMB_SORT
+       comb_sort(sort, idx);
+#else
+       qsort(sort, idx, sizeof(struct ext4_dx_sort_entry),
+             ext4_dir_dx_entry_comparator);
+#endif
+       /* Allocate new block for store the second part of entries */
+       ext4_fsblk_t new_fblock;
+       uint32_t new_iblock;
+       rc = ext4_fs_append_inode_dblk(inode_ref, &new_fblock, &new_iblock);
+       if (rc != EOK) {
+               free(sort);
+               free(entry_buffer);
+               return rc;
+       }
+
+       /* Load new block */
+       struct ext4_block new_data_block_tmp;
+       rc = ext4_trans_block_get_noread(inode_ref->fs->bdev, &new_data_block_tmp,
+                                  new_fblock);
+       if (rc != EOK) {
+               free(sort);
+               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[i].rec_len) > (block_size / 2)) {
+                       new_hash = sort[i].hash;
+                       mid = i;
+                       break;
+               }
+
+               current_size += sort[i].rec_len;
+       }
+
+       /* Check hash collision */
+       uint32_t continued = 0;
+       if (new_hash == sort[mid - 1].hash)
+               continued = 1;
+
+       uint32_t off = 0;
+       void *ptr;
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               block_size -= sizeof(struct ext4_dir_entry_tail);
+
+       /* First part - to the old block */
+       for (i = 0; i < mid; ++i) {
+               ptr = old_data_block->data + off;
+               memcpy(ptr, sort[i].dentry, sort[i].rec_len);
+
+               struct ext4_dir_en *t = ptr;
+               if (i < (mid - 1))
+                       ext4_dir_en_set_entry_len(t, sort[i].rec_len);
+               else
+                       ext4_dir_en_set_entry_len(t, block_size - off);
+
+               off += sort[i].rec_len;
+       }
+
+       /* Second part - to the new block */
+       off = 0;
+       for (i = mid; i < idx; ++i) {
+               ptr = new_data_block_tmp.data + off;
+               memcpy(ptr, sort[i].dentry, sort[i].rec_len);
+
+               struct ext4_dir_en *t = ptr;
+               if (i < (idx - 1))
+                       ext4_dir_en_set_entry_len(t, sort[i].rec_len);
+               else
+                       ext4_dir_en_set_entry_len(t, block_size - off);
+
+               off += sort[i].rec_len;
+       }
+
+       block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
+
+       /* Do some steps to finish operation */
+       sb = &inode_ref->fs->sb;
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               struct ext4_dir_entry_tail *t;
+
+               t = EXT4_DIRENT_TAIL(old_data_block->data, block_size);
+               ext4_dir_init_entry_tail(t);
+               t = EXT4_DIRENT_TAIL(new_data_block_tmp.data, block_size);
+               ext4_dir_init_entry_tail(t);
+       }
+       ext4_dir_set_csum(inode_ref, (void *)old_data_block->data);
+       ext4_dir_set_csum(inode_ref, (void *)new_data_block_tmp.data);
+       ext4_trans_set_block_dirty(old_data_block->buf);
+       ext4_trans_set_block_dirty(new_data_block_tmp.buf);
+
+       free(sort);
+       free(entry_buffer);
+
+       ext4_dir_dx_insert_entry(inode_ref, 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 *ino_ref,
+                       struct ext4_dir_idx_block *dx_blks,
+                       struct ext4_dir_idx_block *dxb,
+                       struct ext4_dir_idx_block **new_dx_block)
+{
+       struct ext4_sblock *sb = &ino_ref->fs->sb;
+       struct ext4_dir_idx_entry *e;
+       int r;
+
+       uint32_t block_size = ext4_sb_get_block_size(&ino_ref->fs->sb);
+       uint32_t entry_space = block_size - sizeof(struct ext4_fake_dir_entry);
+       uint32_t node_limit =  entry_space / sizeof(struct ext4_dir_idx_entry);
+
+       bool meta_csum = ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM);
+
+       if (dxb == dx_blks)
+               e = ((struct ext4_dir_idx_root *)dxb->b.data)->en;
+       else
+               e = ((struct ext4_dir_idx_node *)dxb->b.data)->entries;
+
+       struct ext4_dir_idx_climit *climit = (struct ext4_dir_idx_climit *)e;
+
+       uint16_t leaf_limit = ext4_dir_dx_climit_get_limit(climit);
+       uint16_t leaf_count = ext4_dir_dx_climit_get_count(climit);
+
+       /* Check if is necessary to split index block */
+       if (leaf_limit == leaf_count) {
+               struct ext4_dir_idx_entry *ren;
+               ptrdiff_t levels = dxb - dx_blks;
+
+               ren = ((struct ext4_dir_idx_root *)dx_blks[0].b.data)->en;
+               struct ext4_dir_idx_climit *rclimit = (void *)ren;
+               uint16_t root_limit = ext4_dir_dx_climit_get_limit(rclimit);
+               uint16_t root_count = ext4_dir_dx_climit_get_count(rclimit);
+
+
+               /* Linux limitation */
+               if ((levels > 0) && (root_limit == root_count))
+                       return ENOSPC;
+
+               /* Add new block to directory */
+               ext4_fsblk_t new_fblk;
+               uint32_t new_iblk;
+               r = ext4_fs_append_inode_dblk(ino_ref, &new_fblk, &new_iblk);
+               if (r != EOK)
+                       return r;
+
+               /* load new block */
+               struct ext4_block b;
+               r = ext4_trans_block_get_noread(ino_ref->fs->bdev, &b, new_fblk);
+               if (r != EOK)
+                       return r;
+
+               struct ext4_dir_idx_node *new_node = (void *)b.data;
+               struct ext4_dir_idx_entry *new_en = new_node->entries;
+
+               memset(&new_node->fake, 0, sizeof(struct ext4_fake_dir_entry));
+               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;
+                       size_t sz;
+
+                       struct ext4_dir_idx_climit *left_climit;
+                       struct ext4_dir_idx_climit *right_climit;
+
+                       hash_right = ext4_dir_dx_entry_get_hash(e + count_left);
+                       /* Copy data to new node */
+                       sz = count_right * sizeof(struct ext4_dir_idx_entry);
+                       memcpy(new_en, e + count_left, sz);
+
+                       /* Initialize new node */
+                       left_climit = (struct ext4_dir_idx_climit *)e;
+                       right_climit = (struct ext4_dir_idx_climit *)new_en;
+
+                       ext4_dir_dx_climit_set_count(left_climit, count_left);
+                       ext4_dir_dx_climit_set_count(right_climit, count_right);
+
+                       if (meta_csum)
+                               entry_space -= sizeof(struct ext4_dir_idx_tail);
+
+                       ext4_dir_dx_climit_set_limit(right_climit, node_limit);
+
+                       /* Which index block is target for new entry */
+                       uint32_t position_index =
+                           (dxb->position - dxb->entries);
+                       if (position_index >= count_left) {
+                               ext4_dir_set_dx_csum(
+                                               ino_ref,
+                                               (struct ext4_dir_en *)
+                                               dxb->b.data);
+                               ext4_trans_set_block_dirty(dxb->b.buf);
+
+                               struct ext4_block block_tmp = dxb->b;
+
+                               dxb->b = b;
+
+                               dxb->position =
+                                   new_en + position_index - count_left;
+                               dxb->entries = new_en;
+
+                               b = block_tmp;
+                       }
+
+                       /* Finally insert new entry */
+                       ext4_dir_dx_insert_entry(ino_ref, dx_blks, hash_right,
+                                                new_iblk);
+                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[0].b.data);
+                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[1].b.data);
+                       ext4_trans_set_block_dirty(dx_blks[0].b.buf);
+                       ext4_trans_set_block_dirty(dx_blks[1].b.buf);
+
+                       ext4_dir_set_dx_csum(ino_ref, (void *)b.data);
+                       ext4_trans_set_block_dirty(b.buf);
+                       return ext4_block_set(ino_ref->fs->bdev, &b);
+               } else {
+                       size_t sz;
+                       /* Copy data from root to child block */
+                       sz = leaf_count * sizeof(struct ext4_dir_idx_entry);
+                       memcpy(new_en, e, sz);
+
+                       struct ext4_dir_idx_climit *new_climit = (void*)new_en;
+                       if (meta_csum)
+                               entry_space -= sizeof(struct ext4_dir_idx_tail);
+
+                       ext4_dir_dx_climit_set_limit(new_climit, node_limit);
+
+                       /* Set values in root node */
+                       struct ext4_dir_idx_climit *new_root_climit = (void *)e;
+
+                       ext4_dir_dx_climit_set_count(new_root_climit, 1);
+                       ext4_dir_dx_entry_set_block(e, new_iblk);
+
+                       struct ext4_dir_idx_root *r = (void *)dx_blks[0].b.data;
+                       r->info.indirect_levels = 1;
+
+                       /* Add new entry to the path */
+                       dxb = dx_blks + 1;
+                       dxb->position = dx_blks->position - e + new_en;
+                       dxb->entries = new_en;
+                       dxb->b = b;
+                       *new_dx_block = dxb;
+
+                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[0].b.data);
+                       ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[1].b.data);
+                       ext4_trans_set_block_dirty(dx_blks[0].b.buf);
+                       ext4_trans_set_block_dirty(dx_blks[1].b.buf);
+               }
+       }
+
+       return EOK;
+}
+
+int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
+                         struct ext4_inode_ref *child, const char *name)
+{
+       int rc2 = EOK;
+       int r;
+       /* Get direct block 0 (index root) */
+       ext4_fsblk_t rblock_addr;
+       r =  ext4_fs_get_inode_dblk_idx(parent, 0, &rblock_addr, false);
+       if (r != EOK)
+               return r;
+
+       struct ext4_fs *fs = parent->fs;
+       struct ext4_block root_blk;
+
+       r = ext4_trans_block_get(fs->bdev, &root_blk, rblock_addr);
+       if (r != EOK)
+               return r;
+
+       if (!ext4_dir_dx_csum_verify(parent, (void*)root_blk.data)) {
+               ext4_dbg(DEBUG_DIR_IDX,
+                        DBG_WARN "HTree root checksum failed."
+                        "Inode: %" PRIu32", "
+                        "Block: %" PRIu32"\n",
+                        parent->index,
+                        (uint32_t)0);
+       }
+
+       /* Initialize hinfo structure (mainly compute hash) */
+       uint32_t name_len = strlen(name);
+       struct ext4_hash_info hinfo;
+       r = ext4_dir_hinfo_init(&hinfo, &root_blk, &fs->sb, name_len, name);
+       if (r != EOK) {
+               ext4_block_set(fs->bdev, &root_blk);
+               return EXT4_ERR_BAD_DX_DIR;
+       }
+
+       /*
+        * Hardcoded number 2 means maximum height of index
+        * tree defined in Linux.
+        */
+       struct ext4_dir_idx_block dx_blks[2];
+       struct ext4_dir_idx_block *dx_blk;
+       struct ext4_dir_idx_block *dx_it;
+
+       r = ext4_dir_dx_get_leaf(&hinfo, parent, &root_blk, &dx_blk, dx_blks);
+       if (r != EOK) {
+               r = 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_blk->position);
+       ext4_fsblk_t leaf_block_addr;
+       r = ext4_fs_get_inode_dblk_idx(parent, leaf_block_idx,
+                                               &leaf_block_addr, false);
+       if (r != EOK)
+               goto release_index;
+
+       /*
+        * Check if there is needed to split index node
+        * (and recursively also parent nodes)
+        */
+       r = ext4_dir_dx_split_index(parent, dx_blks, dx_blk, &dx_blk);
+       if (r != EOK)
+               goto release_target_index;
+
+       struct ext4_block target_block;
+       r = ext4_trans_block_get(fs->bdev, &target_block, leaf_block_addr);
+       if (r != EOK)
+               goto release_index;
+
+       if (!ext4_dir_csum_verify(parent,(void *)target_block.data)) {
+               ext4_dbg(DEBUG_DIR_IDX,
+                               DBG_WARN "HTree leaf block checksum failed."
+                               "Inode: %" PRIu32", "
+                               "Block: %" PRIu32"\n",
+                               parent->index,
+                               leaf_block_idx);
+       }
+
+       /* Check if insert operation passed */
+       r = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child,
+                                       name, name_len);
+       if (r == EOK)
+               goto release_target_index;
+
+       /* Split entries to two blocks (includes sorting by hash value) */
+       struct ext4_block new_block;
+       r = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_blk,
+                                   &new_block);
+       if (r != EOK) {
+               rc2 = r;
+               goto release_target_index;
+       }
+
+       /* Where to save new entry */
+       uint32_t blk_hash = ext4_dir_dx_entry_get_hash(dx_blk->position + 1);
+       if (hinfo.hash >= blk_hash)
+               r = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block,
+                                               child, name, name_len);
+       else
+               r = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block,
+                                               child, name, name_len);
+
+       /* Cleanup */
+       r = ext4_block_set(fs->bdev, &new_block);
+       if (r != EOK)
+               return r;
+
+/* Cleanup operations */
+
+release_target_index:
+       rc2 = r;
+
+       r = ext4_block_set(fs->bdev, &target_block);
+       if (r != EOK)
+               return r;
+
+release_index:
+       if (r != EOK)
+               rc2 = r;
+
+       dx_it = dx_blks;
+
+       while (dx_it <= dx_blk) {
+               r = ext4_block_set(fs->bdev, &dx_it->b);
+               if (r != EOK)
+                       return r;
+
+               dx_it++;
+       }
+
+       return rc2;
+}
+
+int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir,
+                                   uint32_t parent_inode)
+{
+       /* Load block 0, where will be index root located */
+       ext4_fsblk_t fblock;
+       int rc = ext4_fs_get_inode_dblk_idx(dir, 0, &fblock, false);
+       if (rc != EOK)
+               return rc;
+
+       struct ext4_block block;
+       rc = ext4_trans_block_get(dir->fs->bdev, &block, fblock);
+       if (rc != EOK)
+               return rc;
+
+       if (!ext4_dir_dx_csum_verify(dir, (void *)block.data)) {
+               ext4_dbg(DEBUG_DIR_IDX,
+                        DBG_WARN "HTree root checksum failed."
+                        "Inode: %" PRIu32", "
+                        "Block: %" PRIu32"\n",
+                        dir->index,
+                        (uint32_t)0);
+       }
+
+       /* Initialize pointers to data structures */
+       struct ext4_dir_idx_root *root = (void *)block.data;
+
+       /* Fill the inode field with a new parent ino. */
+       ext4_dx_dot_en_set_inode(&root->dots[1], parent_inode);
+
+       ext4_dir_set_dx_csum(dir, (void *)block.data);
+       ext4_trans_set_block_dirty(block.buf);
+
+       return ext4_block_set(dir->fs->bdev, &block);
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_extent.c b/src/ext4_extent.c
new file mode 100644 (file)
index 0000000..f53733d
--- /dev/null
@@ -0,0 +1,1859 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
+ *
+ * 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.
+ */
+
+#include "ext4_config.h"
+#include "ext4_blockdev.h"
+#include "ext4_fs.h"
+#include "ext4_super.h"
+#include "ext4_crc32.h"
+#include "ext4_balloc.h"
+#include "ext4_debug.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "ext4_extent.h"
+
+/*
+ * used by extent splitting.
+ */
+#define EXT4_EXT_MARK_UNWRIT1 0x02 /* mark first half unwritten */
+#define EXT4_EXT_MARK_UNWRIT2 0x04 /* mark second half unwritten */
+#define EXT4_EXT_DATA_VALID1 0x08  /* first half contains valid data */
+#define EXT4_EXT_DATA_VALID2 0x10  /* second half contains valid data */
+#define EXT4_EXT_NO_COMBINE 0x20   /* do not combine two extents */
+
+static struct ext4_extent_tail *
+find_ext4_extent_tail(struct ext4_extent_header *eh)
+{
+       return (struct ext4_extent_tail *)(((char *)eh) +
+                                          EXT4_EXTENT_TAIL_OFFSET(eh));
+}
+
+static struct ext4_extent_header *ext_inode_hdr(struct ext4_inode *inode)
+{
+       return (struct ext4_extent_header *)inode->blocks;
+}
+
+static struct ext4_extent_header *ext_block_hdr(struct ext4_block *block)
+{
+       return (struct ext4_extent_header *)block->data;
+}
+
+static uint16_t ext_depth(struct ext4_inode *inode)
+{
+       return to_le16(ext_inode_hdr(inode)->depth);
+}
+
+static uint16_t ext4_ext_get_actual_len(struct ext4_extent *ext)
+{
+       return (to_le16(ext->block_count) <= EXT_INIT_MAX_LEN
+                   ? to_le16(ext->block_count)
+                   : (to_le16(ext->block_count) - EXT_INIT_MAX_LEN));
+}
+
+static void ext4_ext_mark_initialized(struct ext4_extent *ext)
+{
+       ext->block_count = to_le16(ext4_ext_get_actual_len(ext));
+}
+
+static void ext4_ext_mark_unwritten(struct ext4_extent *ext)
+{
+       ext->block_count |= to_le16(EXT_INIT_MAX_LEN);
+}
+
+static int ext4_ext_is_unwritten(struct ext4_extent *ext)
+{
+       /* Extent with ee_len of 0x8000 is treated as an initialized extent */
+       return (to_le16(ext->block_count) > EXT_INIT_MAX_LEN);
+}
+
+/*
+ * ext4_ext_pblock:
+ * combine low and high parts of physical block number into ext4_fsblk_t
+ */
+static ext4_fsblk_t ext4_ext_pblock(struct ext4_extent *ex)
+{
+       ext4_fsblk_t block;
+
+       block = to_le32(ex->start_lo);
+       block |= ((ext4_fsblk_t)to_le16(ex->start_hi) << 31) << 1;
+       return block;
+}
+
+/*
+ * ext4_idx_pblock:
+ * combine low and high parts of a leaf physical block number into ext4_fsblk_t
+ */
+static ext4_fsblk_t ext4_idx_pblock(struct ext4_extent_index *ix)
+{
+       ext4_fsblk_t block;
+
+       block = to_le32(ix->leaf_lo);
+       block |= ((ext4_fsblk_t)to_le16(ix->leaf_hi) << 31) << 1;
+       return block;
+}
+
+/*
+ * ext4_ext_store_pblock:
+ * stores a large physical block number into an extent struct,
+ * breaking it into parts
+ */
+static void ext4_ext_store_pblock(struct ext4_extent *ex, ext4_fsblk_t pb)
+{
+       ex->start_lo = to_le32((uint32_t)(pb & 0xffffffff));
+       ex->start_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff);
+}
+
+/*
+ * ext4_idx_store_pblock:
+ * stores a large physical block number into an index struct,
+ * breaking it into parts
+ */
+static void ext4_idx_store_pblock(struct ext4_extent_index *ix, ext4_fsblk_t pb)
+{
+       ix->leaf_lo = to_le32((uint32_t)(pb & 0xffffffff));
+       ix->leaf_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff);
+}
+
+static int ext4_allocate_single_block(struct ext4_inode_ref *inode_ref,
+                                     ext4_fsblk_t goal,
+                                     ext4_fsblk_t *blockp)
+{
+       return ext4_balloc_alloc_block(inode_ref, goal, blockp);
+}
+
+static ext4_fsblk_t ext4_new_meta_blocks(struct ext4_inode_ref *inode_ref,
+                                        ext4_fsblk_t goal,
+                                        uint32_t flags __unused,
+                                        uint32_t *count, int *errp)
+{
+       ext4_fsblk_t block = 0;
+
+       *errp = ext4_allocate_single_block(inode_ref, goal, &block);
+       if (count)
+               *count = 1;
+       return block;
+}
+
+static void ext4_ext_free_blocks(struct ext4_inode_ref *inode_ref,
+                                ext4_fsblk_t block, uint32_t count,
+                                uint32_t flags __unused)
+{
+       ext4_balloc_free_blocks(inode_ref, block, count);
+}
+
+static uint16_t ext4_ext_space_block(struct ext4_inode_ref *inode_ref)
+{
+       uint16_t size;
+       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
+
+       size = (block_size - sizeof(struct ext4_extent_header)) /
+              sizeof(struct ext4_extent);
+#ifdef AGGRESSIVE_TEST
+       if (size > 6)
+               size = 6;
+#endif
+       return size;
+}
+
+static uint16_t ext4_ext_space_block_idx(struct ext4_inode_ref *inode_ref)
+{
+       uint16_t size;
+       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
+
+       size = (block_size - sizeof(struct ext4_extent_header)) /
+              sizeof(struct ext4_extent_index);
+#ifdef AGGRESSIVE_TEST
+       if (size > 5)
+               size = 5;
+#endif
+       return size;
+}
+
+static uint16_t ext4_ext_space_root(struct ext4_inode_ref *inode_ref)
+{
+       uint16_t size;
+
+       size = sizeof(inode_ref->inode->blocks);
+       size -= sizeof(struct ext4_extent_header);
+       size /= sizeof(struct ext4_extent);
+#ifdef AGGRESSIVE_TEST
+       if (size > 3)
+               size = 3;
+#endif
+       return size;
+}
+
+static uint16_t ext4_ext_space_root_idx(struct ext4_inode_ref *inode_ref)
+{
+       uint16_t size;
+
+       size = sizeof(inode_ref->inode->blocks);
+       size -= sizeof(struct ext4_extent_header);
+       size /= sizeof(struct ext4_extent_index);
+#ifdef AGGRESSIVE_TEST
+       if (size > 4)
+               size = 4;
+#endif
+       return size;
+}
+
+static uint16_t ext4_ext_max_entries(struct ext4_inode_ref *inode_ref,
+                                  uint32_t depth)
+{
+       uint16_t max;
+
+       if (depth == ext_depth(inode_ref->inode)) {
+               if (depth == 0)
+                       max = ext4_ext_space_root(inode_ref);
+               else
+                       max = ext4_ext_space_root_idx(inode_ref);
+       } else {
+               if (depth == 0)
+                       max = ext4_ext_space_block(inode_ref);
+               else
+                       max = ext4_ext_space_block_idx(inode_ref);
+       }
+
+       return max;
+}
+
+static ext4_fsblk_t ext4_ext_find_goal(struct ext4_inode_ref *inode_ref,
+                                      struct ext4_extent_path *path,
+                                      ext4_lblk_t block)
+{
+       if (path) {
+               uint32_t depth = path->depth;
+               struct ext4_extent *ex;
+
+               /*
+                * Try to predict block placement assuming that we are
+                * filling in a file which will eventually be
+                * non-sparse --- i.e., in the case of libbfd writing
+                * an ELF object sections out-of-order but in a way
+                * the eventually results in a contiguous object or
+                * executable file, or some database extending a table
+                * space file.  However, this is actually somewhat
+                * non-ideal if we are writing a sparse file such as
+                * qemu or KVM writing a raw image file that is going
+                * to stay fairly sparse, since it will end up
+                * fragmenting the file system's free space.  Maybe we
+                * should have some hueristics or some way to allow
+                * userspace to pass a hint to file system,
+                * especially if the latter case turns out to be
+                * common.
+                */
+               ex = path[depth].extent;
+               if (ex) {
+                       ext4_fsblk_t ext_pblk = ext4_ext_pblock(ex);
+                       ext4_lblk_t ext_block = to_le32(ex->first_block);
+
+                       if (block > ext_block)
+                               return ext_pblk + (block - ext_block);
+                       else
+                               return ext_pblk - (ext_block - block);
+               }
+
+               /* it looks like index is empty;
+                * try to find starting block from index itself */
+               if (path[depth].block.lb_id)
+                       return path[depth].block.lb_id;
+       }
+
+       /* OK. use inode's group */
+       return ext4_fs_inode_to_goal_block(inode_ref);
+}
+
+/*
+ * Allocation for a meta data block
+ */
+static ext4_fsblk_t ext4_ext_new_meta_block(struct ext4_inode_ref *inode_ref,
+                                           struct ext4_extent_path *path,
+                                           struct ext4_extent *ex, int *err,
+                                           uint32_t flags)
+{
+       ext4_fsblk_t goal, newblock;
+
+       goal = ext4_ext_find_goal(inode_ref, path, to_le32(ex->first_block));
+       newblock = ext4_new_meta_blocks(inode_ref, goal, flags, NULL, err);
+       return newblock;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_ext_block_csum(struct ext4_inode_ref *inode_ref,
+                                   struct ext4_extent_header *eh)
+{
+       uint32_t checksum = 0;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t ino_index = to_le32(inode_ref->index);
+               uint32_t ino_gen =
+                       to_le32(ext4_inode_get_generation(inode_ref->inode));
+               /* First calculate crc32 checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
+                               sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against inode number
+                * and inode generation */
+               checksum = ext4_crc32c(checksum, &ino_index,
+                                    sizeof(ino_index));
+               checksum = ext4_crc32c(checksum, &ino_gen,
+                                    sizeof(ino_gen));
+               /* Finally calculate crc32 checksum against 
+                * the entire extent block up to the checksum field */
+               checksum = ext4_crc32c(checksum, eh,
+                               EXT4_EXTENT_TAIL_OFFSET(eh));
+       }
+       return checksum;
+}
+#else
+#define ext4_ext_block_csum(...) 0
+#endif
+
+static void ext4_extent_block_csum_set(struct ext4_inode_ref *inode_ref __unused,
+                                      struct ext4_extent_header *eh)
+{
+       struct ext4_extent_tail *tail;
+
+       tail = find_ext4_extent_tail(eh);
+       tail->et_checksum = to_le32(ext4_ext_block_csum(inode_ref, eh));
+}
+
+static int ext4_ext_dirty(struct ext4_inode_ref *inode_ref,
+                         struct ext4_extent_path *path)
+{
+       if (path->block.lb_id)
+               ext4_trans_set_block_dirty(path->block.buf);
+       else
+               inode_ref->dirty = true;
+
+       return EOK;
+}
+
+static void ext4_ext_drop_refs(struct ext4_inode_ref *inode_ref,
+                              struct ext4_extent_path *path, bool keep_other)
+{
+       int32_t depth, i;
+
+       if (!path)
+               return;
+       if (keep_other)
+               depth = 0;
+       else
+               depth = path->depth;
+
+       for (i = 0; i <= depth; i++, path++) {
+               if (path->block.lb_id) {
+                       if (ext4_bcache_test_flag(path->block.buf, BC_DIRTY))
+                               ext4_extent_block_csum_set(inode_ref,
+                                               path->header);
+
+                       ext4_block_set(inode_ref->fs->bdev, &path->block);
+               }
+       }
+}
+
+/*
+ * Check that whether the basic information inside the extent header
+ * is correct or not.
+ */
+static int ext4_ext_check(struct ext4_inode_ref *inode_ref,
+                         struct ext4_extent_header *eh, uint16_t depth,
+                         ext4_fsblk_t pblk __unused)
+{
+       struct ext4_extent_tail *tail;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       const char *error_msg;
+       (void)error_msg;
+
+       if (to_le16(eh->magic) != EXT4_EXTENT_MAGIC) {
+               error_msg = "invalid magic";
+               goto corrupted;
+       }
+       if (to_le16(eh->depth) != depth) {
+               error_msg = "unexpected eh_depth";
+               goto corrupted;
+       }
+       if (eh->max_entries_count == 0) {
+               error_msg = "invalid eh_max";
+               goto corrupted;
+       }
+       if (to_le16(eh->entries_count) > to_le16(eh->max_entries_count)) {
+               error_msg = "invalid eh_entries";
+               goto corrupted;
+       }
+
+       tail = find_ext4_extent_tail(eh);
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               if (tail->et_checksum != to_le32(ext4_ext_block_csum(inode_ref, eh))) {
+                       ext4_dbg(DEBUG_EXTENT,
+                                DBG_WARN "Extent block checksum failed."
+                                "Blocknr: %" PRIu64"\n",
+                                pblk);
+
+               }
+       }
+
+       return EOK;
+
+corrupted:
+       ext4_dbg(DEBUG_EXTENT, "Bad extents B+ tree block: %s. "
+                              "Blocknr: %" PRId64 "\n",
+                error_msg, pblk);
+       return EIO;
+}
+
+static int read_extent_tree_block(struct ext4_inode_ref *inode_ref,
+                                 ext4_fsblk_t pblk, int32_t depth,
+                                 struct ext4_block *bh,
+                                 uint32_t flags __unused)
+{
+       int err;
+
+       err = ext4_trans_block_get(inode_ref->fs->bdev, bh, pblk);
+       if (err != EOK)
+               goto errout;
+
+       err = ext4_ext_check(inode_ref, ext_block_hdr(bh), depth, pblk);
+       if (err != EOK)
+               goto errout;
+
+       return EOK;
+errout:
+       if (bh->lb_id)
+               ext4_block_set(inode_ref->fs->bdev, bh);
+
+       return err;
+}
+
+/*
+ * ext4_ext_binsearch_idx:
+ * binary search for the closest index of the given block
+ * the header must be checked before calling this
+ */
+static void ext4_ext_binsearch_idx(struct ext4_extent_path *path,
+                                  ext4_lblk_t block)
+{
+       struct ext4_extent_header *eh = path->header;
+       struct ext4_extent_index *r, *l, *m;
+
+       l = EXT_FIRST_INDEX(eh) + 1;
+       r = EXT_LAST_INDEX(eh);
+       while (l <= r) {
+               m = l + (r - l) / 2;
+               if (block < to_le32(m->first_block))
+                       r = m - 1;
+               else
+                       l = m + 1;
+       }
+
+       path->index = l - 1;
+}
+
+/*
+ * ext4_ext_binsearch:
+ * binary search for closest extent of the given block
+ * the header must be checked before calling this
+ */
+static void ext4_ext_binsearch(struct ext4_extent_path *path, ext4_lblk_t block)
+{
+       struct ext4_extent_header *eh = path->header;
+       struct ext4_extent *r, *l, *m;
+
+       if (eh->entries_count == 0) {
+               /*
+                * this leaf is empty:
+                * we get such a leaf in split/add case
+                */
+               return;
+       }
+
+       l = EXT_FIRST_EXTENT(eh) + 1;
+       r = EXT_LAST_EXTENT(eh);
+
+       while (l <= r) {
+               m = l + (r - l) / 2;
+               if (block < to_le32(m->first_block))
+                       r = m - 1;
+               else
+                       l = m + 1;
+       }
+
+       path->extent = l - 1;
+}
+
+static int ext4_find_extent(struct ext4_inode_ref *inode_ref, ext4_lblk_t block,
+                           struct ext4_extent_path **orig_path, uint32_t flags)
+{
+       struct ext4_extent_header *eh;
+       struct ext4_block bh = EXT4_BLOCK_ZERO();
+       ext4_fsblk_t buf_block = 0;
+       struct ext4_extent_path *path = *orig_path;
+       int32_t depth, ppos = 0;
+       int32_t i;
+       int ret;
+
+       eh = ext_inode_hdr(inode_ref->inode);
+       depth = ext_depth(inode_ref->inode);
+
+       if (path) {
+               ext4_ext_drop_refs(inode_ref, path, 0);
+               if (depth > path[0].maxdepth) {
+                       free(path);
+                       *orig_path = path = NULL;
+               }
+       }
+       if (!path) {
+               int32_t path_depth = depth + 1;
+               /* account possible depth increase */
+               path = calloc(1, sizeof(struct ext4_extent_path) *
+                                    (path_depth + 1));
+               if (!path)
+                       return ENOMEM;
+               path[0].maxdepth = path_depth;
+       }
+       path[0].header = eh;
+       path[0].block = bh;
+
+       i = depth;
+       /* walk through the tree */
+       while (i) {
+               ext4_ext_binsearch_idx(path + ppos, block);
+               path[ppos].p_block = ext4_idx_pblock(path[ppos].index);
+               path[ppos].depth = i;
+               path[ppos].extent = NULL;
+               buf_block = path[ppos].p_block;
+
+               i--;
+               ppos++;
+               if (!path[ppos].block.lb_id ||
+                   path[ppos].block.lb_id != buf_block) {
+                       ret = read_extent_tree_block(inode_ref, buf_block, i,
+                                                    &bh, flags);
+                       if (ret != EOK) {
+                               goto err;
+                       }
+                       if (ppos > depth) {
+                               ext4_block_set(inode_ref->fs->bdev, &bh);
+                               ret = EIO;
+                               goto err;
+                       }
+
+                       eh = ext_block_hdr(&bh);
+                       path[ppos].block = bh;
+                       path[ppos].header = eh;
+               }
+       }
+
+       path[ppos].depth = i;
+       path[ppos].extent = NULL;
+       path[ppos].index = NULL;
+
+       /* find extent */
+       ext4_ext_binsearch(path + ppos, block);
+       /* if not an empty leaf */
+       if (path[ppos].extent)
+               path[ppos].p_block = ext4_ext_pblock(path[ppos].extent);
+
+       *orig_path = path;
+
+       ret = EOK;
+       return ret;
+
+err:
+       ext4_ext_drop_refs(inode_ref, path, 0);
+       free(path);
+       if (orig_path)
+               *orig_path = NULL;
+       return ret;
+}
+
+static void ext4_ext_init_header(struct ext4_inode_ref *inode_ref,
+                                struct ext4_extent_header *eh, int32_t depth)
+{
+       eh->entries_count = 0;
+       eh->max_entries_count = to_le16(ext4_ext_max_entries(inode_ref, depth));
+       eh->magic = to_le16(EXT4_EXTENT_MAGIC);
+       eh->depth = depth;
+}
+
+/*
+ * Be cautious, the buffer_head returned is not yet mark dirtied. */
+static int ext4_ext_split_node(struct ext4_inode_ref *inode_ref,
+                              struct ext4_extent_path *path, int32_t at,
+                              struct ext4_extent *newext,
+                              ext4_fsblk_t *sibling, struct ext4_block *new_bh)
+{
+       int ret;
+       ext4_fsblk_t newblock;
+       struct ext4_block bh = EXT4_BLOCK_ZERO();
+       int32_t depth = ext_depth(inode_ref->inode);
+
+       ext4_assert(sibling);
+
+       /* FIXME: currently we split at the point after the current extent. */
+       newblock = ext4_ext_new_meta_block(inode_ref, path, newext, &ret, 0);
+       if (ret)
+               goto cleanup;
+
+       /*  For write access.# */
+       ret = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, newblock);
+       if (ret != EOK)
+               goto cleanup;
+
+       if (at == depth) {
+               /* start copy from next extent */
+               ptrdiff_t m = EXT_MAX_EXTENT(path[at].header) - path[at].extent;
+               struct ext4_extent_header *neh;
+               neh = ext_block_hdr(&bh);
+               ext4_ext_init_header(inode_ref, neh, 0);
+               if (m) {
+                       struct ext4_extent *ex;
+                       ex = EXT_FIRST_EXTENT(neh);
+                       memmove(ex, path[at].extent + 1,
+                               sizeof(struct ext4_extent) * m);
+                       neh->entries_count =
+                           to_le16(to_le16(neh->entries_count) + m);
+                       path[at].header->entries_count = to_le16(
+                           to_le16(path[at].header->entries_count) - m);
+                       ret = ext4_ext_dirty(inode_ref, path + at);
+                       if (ret)
+                               goto cleanup;
+               }
+       } else {
+               ptrdiff_t m = EXT_MAX_INDEX(path[at].header) - path[at].index;
+               struct ext4_extent_header *neh;
+               neh = ext_block_hdr(&bh);
+               ext4_ext_init_header(inode_ref, neh, depth - at);
+               if (m) {
+                       struct ext4_extent_index *ix;
+                       ix = EXT_FIRST_INDEX(neh);
+                       memmove(ix, path[at].index + 1,
+                               sizeof(struct ext4_extent) * m);
+                       neh->entries_count =
+                           to_le16(to_le16(neh->entries_count) + m);
+                       path[at].header->entries_count = to_le16(
+                           to_le16(path[at].header->entries_count) - m);
+                       ret = ext4_ext_dirty(inode_ref, path + at);
+                       if (ret)
+                               goto cleanup;
+               }
+       }
+cleanup:
+       if (ret) {
+               if (bh.lb_id) {
+                       ext4_block_set(inode_ref->fs->bdev, &bh);
+               }
+               if (newblock)
+                       ext4_ext_free_blocks(inode_ref, newblock, 1, 0);
+
+               newblock = 0;
+       }
+       *sibling = newblock;
+       *new_bh = bh;
+       return ret;
+}
+
+static ext4_lblk_t ext4_ext_block_index(struct ext4_extent_header *eh)
+{
+       if (eh->depth)
+               return to_le32(EXT_FIRST_INDEX(eh)->first_block);
+
+       return to_le32(EXT_FIRST_EXTENT(eh)->first_block);
+}
+
+struct ext_split_trans {
+       ext4_fsblk_t ptr;
+       struct ext4_extent_path path;
+       int switch_to;
+};
+
+static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref,
+                                struct ext4_extent_path *path,
+                                int32_t at,
+                                struct ext4_extent *newext,
+                                ext4_lblk_t insert_index,
+                                ext4_fsblk_t insert_block,
+                                struct ext_split_trans *spt,
+                                bool *need_grow)
+{
+       struct ext4_extent_index *ix;
+       struct ext4_extent_path *curp = path + at;
+       struct ext4_block bh = EXT4_BLOCK_ZERO();
+       int32_t len;
+       int err;
+       struct ext4_extent_header *eh;
+
+       *need_grow = false;
+
+       if (curp->index && insert_index == to_le32(curp->index->first_block))
+               return EIO;
+
+       if (to_le16(curp->header->entries_count) ==
+           to_le16(curp->header->max_entries_count)) {
+               if (at) {
+                       struct ext4_extent_header *neh;
+                       err = ext4_ext_split_node(inode_ref, path, at, newext,
+                                                 &spt->ptr, &bh);
+                       if (err != EOK)
+                               goto out;
+
+                       neh = ext_block_hdr(&bh);
+                       if (insert_index > to_le32(curp->index->first_block)) {
+                               /* Make decision which node should be used to
+                                * insert the index.*/
+                               if (to_le16(neh->entries_count) >
+                                   to_le16(curp->header->entries_count)) {
+                                       eh = curp->header;
+                                       /* insert after */
+                                       ix = EXT_LAST_INDEX(eh) + 1;
+                               } else {
+                                       eh = neh;
+                                       ix = EXT_FIRST_INDEX(eh);
+                               }
+                       } else {
+                               eh = curp->header;
+                               /* insert before */
+                               ix = EXT_LAST_INDEX(eh);
+                       }
+               } else {
+                       err = EOK;
+                       *need_grow = true;
+                       goto out;
+               }
+       } else {
+               eh = curp->header;
+               if (curp->index == NULL) {
+                       ix = EXT_FIRST_INDEX(eh);
+                       curp->index = ix;
+               } else if (insert_index > to_le32(curp->index->first_block)) {
+                       /* insert after */
+                       ix = curp->index + 1;
+               } else {
+                       /* insert before */
+                       ix = curp->index;
+               }
+       }
+
+       len = EXT_LAST_INDEX(eh) - ix + 1;
+       ext4_assert(len >= 0);
+       if (len > 0)
+               memmove(ix + 1, ix, len * sizeof(struct ext4_extent_index));
+
+       if (ix > EXT_MAX_INDEX(eh)) {
+               err = EIO;
+               goto out;
+       }
+
+       ix->first_block = to_le32(insert_index);
+       ext4_idx_store_pblock(ix, insert_block);
+       eh->entries_count = to_le16(to_le16(eh->entries_count) + 1);
+
+       if (ix > EXT_LAST_INDEX(eh)) {
+               err = EIO;
+               goto out;
+       }
+
+       if (eh == curp->header)
+               err = ext4_ext_dirty(inode_ref, curp);
+       else
+               err = EOK;
+
+out:
+       if (err != EOK || *need_grow) {
+               if (bh.lb_id)
+                       ext4_block_set(inode_ref->fs->bdev, &bh);
+
+               spt->ptr = 0;
+       } else if (bh.lb_id) {
+               /* If we got a sibling leaf. */
+               ext4_extent_block_csum_set(inode_ref, ext_block_hdr(&bh));
+               ext4_trans_set_block_dirty(bh.buf);
+
+               spt->path.p_block = ext4_idx_pblock(ix);
+               spt->path.depth = to_le16(eh->depth);
+               spt->path.maxdepth = 0;
+               spt->path.extent = NULL;
+               spt->path.index = ix;
+               spt->path.header = eh;
+               spt->path.block = bh;
+
+               /*
+                * If newext->ee_block can be included into the
+                * right sub-tree.
+                */
+               if (to_le32(newext->first_block) >=
+                   ext4_ext_block_index(ext_block_hdr(&bh)))
+                       spt->switch_to = 1;
+               else {
+                       curp->index = ix;
+                       curp->p_block = ext4_idx_pblock(ix);
+               }
+
+       } else {
+               spt->ptr = 0;
+               curp->index = ix;
+               curp->p_block = ext4_idx_pblock(ix);
+       }
+       return err;
+}
+
+/*
+ * ext4_ext_correct_indexes:
+ * if leaf gets modified and modified extent is first in the leaf,
+ * then we have to correct all indexes above.
+ */
+static int ext4_ext_correct_indexes(struct ext4_inode_ref *inode_ref,
+                                   struct ext4_extent_path *path)
+{
+       struct ext4_extent_header *eh;
+       int32_t depth = ext_depth(inode_ref->inode);
+       struct ext4_extent *ex;
+       uint32_t border;
+       int32_t k;
+       int err = EOK;
+
+       eh = path[depth].header;
+       ex = path[depth].extent;
+
+       if (ex == NULL || eh == NULL)
+               return EIO;
+
+       if (depth == 0) {
+               /* there is no tree at all */
+               return EOK;
+       }
+
+       if (ex != EXT_FIRST_EXTENT(eh)) {
+               /* we correct tree if first leaf got modified only */
+               return EOK;
+       }
+
+       k = depth - 1;
+       border = path[depth].extent->first_block;
+       path[k].index->first_block = border;
+       err = ext4_ext_dirty(inode_ref, path + k);
+       if (err != EOK)
+               return err;
+
+       while (k--) {
+               /* change all left-side indexes */
+               if (path[k + 1].index != EXT_FIRST_INDEX(path[k + 1].header))
+                       break;
+               path[k].index->first_block = border;
+               err = ext4_ext_dirty(inode_ref, path + k);
+               if (err != EOK)
+                       break;
+       }
+
+       return err;
+}
+
+static bool ext4_ext_can_prepend(struct ext4_extent *ex1,
+                                struct ext4_extent *ex2)
+{
+       if (ext4_ext_pblock(ex2) + ext4_ext_get_actual_len(ex2) !=
+           ext4_ext_pblock(ex1))
+               return false;
+
+#ifdef AGGRESSIVE_TEST
+       if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4)
+               return 0;
+#else
+       if (ext4_ext_is_unwritten(ex1)) {
+               if (ext4_ext_get_actual_len(ex1) +
+                       ext4_ext_get_actual_len(ex2) >
+                   EXT_UNWRITTEN_MAX_LEN)
+                       return false;
+       } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) >
+                  EXT_INIT_MAX_LEN)
+               return false;
+#endif
+
+       if (to_le32(ex2->first_block) + ext4_ext_get_actual_len(ex2) !=
+           to_le32(ex1->first_block))
+               return false;
+
+       return true;
+}
+
+static bool ext4_ext_can_append(struct ext4_extent *ex1,
+                               struct ext4_extent *ex2)
+{
+       if (ext4_ext_pblock(ex1) + ext4_ext_get_actual_len(ex1) !=
+           ext4_ext_pblock(ex2))
+               return false;
+
+#ifdef AGGRESSIVE_TEST
+       if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4)
+               return 0;
+#else
+       if (ext4_ext_is_unwritten(ex1)) {
+               if (ext4_ext_get_actual_len(ex1) +
+                       ext4_ext_get_actual_len(ex2) >
+                   EXT_UNWRITTEN_MAX_LEN)
+                       return false;
+       } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) >
+                  EXT_INIT_MAX_LEN)
+               return false;
+#endif
+
+       if (to_le32(ex1->first_block) + ext4_ext_get_actual_len(ex1) !=
+           to_le32(ex2->first_block))
+               return false;
+
+       return true;
+}
+
+static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref,
+                               struct ext4_extent_path *path,
+                               int32_t at,
+                               struct ext4_extent *newext,
+                               struct ext_split_trans *spt,
+                               uint32_t flags,
+                               bool *need_grow)
+{
+       struct ext4_extent_path *curp = path + at;
+       struct ext4_extent *ex = curp->extent;
+       struct ext4_block bh = EXT4_BLOCK_ZERO();
+       int32_t len;
+       int err = EOK;
+       int unwritten;
+       struct ext4_extent_header *eh = NULL;
+
+       *need_grow = false;
+
+       if (curp->extent &&
+           to_le32(newext->first_block) == to_le32(curp->extent->first_block))
+               return EIO;
+
+       if (!(flags & EXT4_EXT_NO_COMBINE)) {
+               if (curp->extent && ext4_ext_can_append(curp->extent, newext)) {
+                       unwritten = ext4_ext_is_unwritten(curp->extent);
+                       curp->extent->block_count =
+                           to_le16(ext4_ext_get_actual_len(curp->extent) +
+                                   ext4_ext_get_actual_len(newext));
+                       if (unwritten)
+                               ext4_ext_mark_unwritten(curp->extent);
+                       err = ext4_ext_dirty(inode_ref, curp);
+                       goto out;
+               }
+
+               if (curp->extent &&
+                   ext4_ext_can_prepend(curp->extent, newext)) {
+                       unwritten = ext4_ext_is_unwritten(curp->extent);
+                       curp->extent->first_block = newext->first_block;
+                       curp->extent->block_count =
+                           to_le16(ext4_ext_get_actual_len(curp->extent) +
+                                   ext4_ext_get_actual_len(newext));
+                       if (unwritten)
+                               ext4_ext_mark_unwritten(curp->extent);
+                       err = ext4_ext_dirty(inode_ref, curp);
+                       goto out;
+               }
+       }
+
+       if (to_le16(curp->header->entries_count) ==
+           to_le16(curp->header->max_entries_count)) {
+               if (at) {
+                       struct ext4_extent_header *neh;
+                       err = ext4_ext_split_node(inode_ref, path, at, newext,
+                                                 &spt->ptr, &bh);
+                       if (err != EOK)
+                               goto out;
+
+                       neh = ext_block_hdr(&bh);
+                       if (to_le32(newext->first_block) >
+                           to_le32(curp->extent->first_block)) {
+                               if (to_le16(neh->entries_count) >
+                                   to_le16(curp->header->entries_count)) {
+                                       eh = curp->header;
+                                       /* insert after */
+                                       ex = EXT_LAST_EXTENT(eh) + 1;
+                               } else {
+                                       eh = neh;
+                                       ex = EXT_FIRST_EXTENT(eh);
+                               }
+                       } else {
+                               eh = curp->header;
+                               /* insert before */
+                               ex = EXT_LAST_EXTENT(eh);
+                       }
+               } else {
+                       err = EOK;
+                       *need_grow = true;
+                       goto out;
+               }
+       } else {
+               eh = curp->header;
+               if (curp->extent == NULL) {
+                       ex = EXT_FIRST_EXTENT(eh);
+                       curp->extent = ex;
+               } else if (to_le32(newext->first_block) >
+                          to_le32(curp->extent->first_block)) {
+                       /* insert after */
+                       ex = curp->extent + 1;
+               } else {
+                       /* insert before */
+                       ex = curp->extent;
+               }
+       }
+
+       len = EXT_LAST_EXTENT(eh) - ex + 1;
+       ext4_assert(len >= 0);
+       if (len > 0)
+               memmove(ex + 1, ex, len * sizeof(struct ext4_extent));
+
+       if (ex > EXT_MAX_EXTENT(eh)) {
+               err = EIO;
+               goto out;
+       }
+
+       ex->first_block = newext->first_block;
+       ex->block_count = newext->block_count;
+       ext4_ext_store_pblock(ex, ext4_ext_pblock(newext));
+       eh->entries_count = to_le16(to_le16(eh->entries_count) + 1);
+
+       if (ex > EXT_LAST_EXTENT(eh)) {
+               err = EIO;
+               goto out;
+       }
+
+       if (eh == curp->header) {
+               err = ext4_ext_correct_indexes(inode_ref, path);
+               if (err != EOK)
+                       goto out;
+               err = ext4_ext_dirty(inode_ref, curp);
+       } else
+               err = EOK;
+
+out:
+       if (err != EOK || *need_grow) {
+               if (bh.lb_id)
+                       ext4_block_set(inode_ref->fs->bdev, &bh);
+
+               spt->ptr = 0;
+       } else if (bh.lb_id) {
+               /* If we got a sibling leaf. */
+               ext4_extent_block_csum_set(inode_ref, ext_block_hdr(&bh));
+               ext4_trans_set_block_dirty(bh.buf);
+
+               spt->path.p_block = ext4_ext_pblock(ex);
+               spt->path.depth = to_le16(eh->depth);
+               spt->path.maxdepth = 0;
+               spt->path.extent = ex;
+               spt->path.index = NULL;
+               spt->path.header = eh;
+               spt->path.block = bh;
+
+               /*
+                * If newext->ee_block can be included into the
+                * right sub-tree.
+                */
+               if (to_le32(newext->first_block) >=
+                   ext4_ext_block_index(ext_block_hdr(&bh)))
+                       spt->switch_to = 1;
+               else {
+                       curp->extent = ex;
+                       curp->p_block = ext4_ext_pblock(ex);
+               }
+
+       } else {
+               spt->ptr = 0;
+               curp->extent = ex;
+               curp->p_block = ext4_ext_pblock(ex);
+       }
+
+       return err;
+}
+
+/*
+ * ext4_ext_grow_indepth:
+ * implements tree growing procedure:
+ * - allocates new block
+ * - moves top-level data (index block or leaf) into the new block
+ * - initializes new top-level, creating index that points to the
+ *   just created block
+ */
+static int ext4_ext_grow_indepth(struct ext4_inode_ref *inode_ref,
+                                uint32_t flags)
+{
+       struct ext4_extent_header *neh;
+       struct ext4_block bh = EXT4_BLOCK_ZERO();
+       ext4_fsblk_t newblock, goal = 0;
+       int err = EOK;
+
+       /* Try to prepend new index to old one */
+       if (ext_depth(inode_ref->inode))
+               goal = ext4_idx_pblock(
+                   EXT_FIRST_INDEX(ext_inode_hdr(inode_ref->inode)));
+       else
+               goal = ext4_fs_inode_to_goal_block(inode_ref);
+
+       newblock = ext4_new_meta_blocks(inode_ref, goal, flags, NULL, &err);
+       if (newblock == 0)
+               return err;
+
+       /* # */
+       err = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, newblock);
+       if (err != EOK) {
+               ext4_ext_free_blocks(inode_ref, newblock, 1, 0);
+               return err;
+       }
+
+       /* move top-level index/leaf into new block */
+       memmove(bh.data, inode_ref->inode->blocks,
+               sizeof(inode_ref->inode->blocks));
+
+       /* set size of new block */
+       neh = ext_block_hdr(&bh);
+       /* old root could have indexes or leaves
+        * so calculate e_max right way */
+       if (ext_depth(inode_ref->inode))
+               neh->max_entries_count =
+                   to_le16(ext4_ext_space_block_idx(inode_ref));
+       else
+               neh->max_entries_count =
+                   to_le16(ext4_ext_space_block(inode_ref));
+
+       neh->magic = to_le16(EXT4_EXTENT_MAGIC);
+       ext4_extent_block_csum_set(inode_ref, neh);
+
+       /* Update top-level index: num,max,pointer */
+       neh = ext_inode_hdr(inode_ref->inode);
+       neh->entries_count = to_le16(1);
+       ext4_idx_store_pblock(EXT_FIRST_INDEX(neh), newblock);
+       if (neh->depth == 0) {
+               /* Root extent block becomes index block */
+               neh->max_entries_count =
+                   to_le16(ext4_ext_space_root_idx(inode_ref));
+               EXT_FIRST_INDEX(neh)
+                   ->first_block = EXT_FIRST_EXTENT(neh)->first_block;
+       }
+       neh->depth = to_le16(to_le16(neh->depth) + 1);
+
+       ext4_trans_set_block_dirty(bh.buf);
+       inode_ref->dirty = true;
+       ext4_block_set(inode_ref->fs->bdev, &bh);
+
+       return err;
+}
+
+__unused static void print_path(struct ext4_extent_path *path)
+{
+       int32_t i = path->depth;
+       while (i >= 0) {
+
+               ptrdiff_t a =
+                   (path->extent)
+                       ? (path->extent - EXT_FIRST_EXTENT(path->header))
+                       : 0;
+               ptrdiff_t b =
+                   (path->index)
+                       ? (path->index - EXT_FIRST_INDEX(path->header))
+                       : 0;
+
+               (void)a;
+               (void)b;
+               ext4_dbg(DEBUG_EXTENT,
+                        "depth %" PRId32 ", p_block: %" PRIu64 ","
+                        "p_ext offset: %td, p_idx offset: %td\n",
+                        i, path->p_block, a, b);
+               i--;
+               path++;
+       }
+}
+
+static void ext4_ext_replace_path(struct ext4_inode_ref *inode_ref,
+                                 struct ext4_extent_path *path,
+                                 struct ext_split_trans *spt,
+                                 int32_t level)
+{
+       int32_t depth = ext_depth(inode_ref->inode);
+       int32_t i = depth - level;
+       ext4_ext_drop_refs(inode_ref, path + i, 1);
+       path[i] = spt[level].path;
+}
+
+static int ext4_ext_insert_extent(struct ext4_inode_ref *inode_ref,
+                                 struct ext4_extent_path **ppath,
+                                 struct ext4_extent *newext, uint32_t flags)
+{
+       int32_t i, depth, level;
+       int ret = EOK;
+       ext4_fsblk_t ptr = 0;
+       bool need_grow = false;
+       struct ext4_extent_path *path = *ppath;
+       struct ext_split_trans *spt = NULL;
+       struct ext_split_trans newblock;
+
+       memset(&newblock, 0, sizeof(newblock));
+
+       depth = ext_depth(inode_ref->inode);
+       for (i = depth, level = 0; i >= 0; i--, level++)
+               if (EXT_HAS_FREE_INDEX(path + i))
+                       break;
+
+       if (level) {
+               spt = calloc(1, sizeof(struct ext_split_trans) * (level));
+               if (!spt) {
+                       ret = ENOMEM;
+                       goto out;
+               }
+       }
+       i = 0;
+again:
+       depth = ext_depth(inode_ref->inode);
+
+       do {
+               if (!i) {
+                       ret = ext4_ext_insert_leaf(inode_ref, path, depth - i,
+                                                  newext, &newblock, flags,
+                                                  &need_grow);
+               } else {
+                       ret = ext4_ext_insert_index(
+                           inode_ref, path, depth - i, newext,
+                           ext4_ext_block_index(
+                               ext_block_hdr(&spt[i - 1].path.block)),
+                           spt[i - 1].ptr, &newblock,
+                           &need_grow);
+               }
+               ptr = newblock.ptr;
+
+               if (ret != EOK)
+                       goto out;
+
+               else if (spt && ptr && !ret) {
+                       /* Prepare for the next iteration after splitting. */
+                       spt[i] = newblock;
+               }
+
+               i++;
+       } while (ptr != 0 && i <= depth);
+
+       if (need_grow) {
+               ret = ext4_ext_grow_indepth(inode_ref, 0);
+               if (ret)
+                       goto out;
+               ret = ext4_find_extent(inode_ref, to_le32(newext->first_block),
+                                      ppath, 0);
+               if (ret)
+                       goto out;
+               i = depth;
+               path = *ppath;
+               goto again;
+       }
+out:
+       if (ret) {
+               if (path)
+                       ext4_ext_drop_refs(inode_ref, path, 0);
+
+               while (--level >= 0 && spt) {
+                       if (spt[level].ptr) {
+                               ext4_ext_free_blocks(inode_ref, spt[level].ptr,
+                                                    1, 0);
+                               ext4_ext_drop_refs(inode_ref, &spt[level].path,
+                                                  1);
+                       }
+               }
+       } else {
+               while (--level >= 0 && spt) {
+                       if (spt[level].switch_to)
+                               ext4_ext_replace_path(inode_ref, path, spt,
+                                                     level);
+                       else if (spt[level].ptr)
+                               ext4_ext_drop_refs(inode_ref, &spt[level].path,
+                                                  1);
+               }
+       }
+       if (spt)
+               free(spt);
+
+       return ret;
+}
+
+static void ext4_ext_remove_blocks(struct ext4_inode_ref *inode_ref,
+                                  struct ext4_extent *ex, ext4_lblk_t from,
+                                  ext4_lblk_t to)
+{
+       ext4_lblk_t len = to - from + 1;
+       ext4_lblk_t num;
+       ext4_fsblk_t start;
+       num = from - to_le32(ex->first_block);
+       start = ext4_ext_pblock(ex) + num;
+       ext4_dbg(DEBUG_EXTENT,
+                "Freeing %" PRIu32 " at %" PRIu64 ", %" PRIu32 "\n", from,
+                start, len);
+
+       ext4_ext_free_blocks(inode_ref, start, len, 0);
+}
+
+static int ext4_ext_remove_idx(struct ext4_inode_ref *inode_ref,
+                              struct ext4_extent_path *path, int32_t depth)
+{
+       int err = EOK;
+       int32_t i = depth;
+       ext4_fsblk_t leaf;
+
+       /* free index block */
+       leaf = ext4_idx_pblock(path[i].index);
+
+       if (path[i].index != EXT_LAST_INDEX(path[i].header)) {
+               ptrdiff_t len = EXT_LAST_INDEX(path[i].header) - path[i].index;
+               memmove(path[i].index, path[i].index + 1,
+                       len * sizeof(struct ext4_extent_index));
+       }
+
+       path[i].header->entries_count =
+           to_le16(to_le16(path[i].header->entries_count) - 1);
+       err = ext4_ext_dirty(inode_ref, path + i);
+       if (err != EOK)
+               return err;
+
+       ext4_dbg(DEBUG_EXTENT, "IDX: Freeing %" PRIu32 " at %" PRIu64 ", %d\n",
+                to_le32(path[i].index->first_block), leaf, 1);
+       ext4_ext_free_blocks(inode_ref, leaf, 1, 0);
+
+       while (i > 0) {
+               if (path[i].index != EXT_FIRST_INDEX(path[i].header))
+                       break;
+
+               path[i - 1].index->first_block = path[i].index->first_block;
+               err = ext4_ext_dirty(inode_ref, path + i - 1);
+               if (err != EOK)
+                       break;
+
+               i--;
+       }
+       return err;
+}
+
+static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref,
+                               struct ext4_extent_path *path, ext4_lblk_t from,
+                               ext4_lblk_t to)
+{
+
+       int32_t depth = ext_depth(inode_ref->inode);
+       struct ext4_extent *ex = path[depth].extent;
+       struct ext4_extent *start_ex, *ex2 = NULL;
+       struct ext4_extent_header *eh = path[depth].header;
+       int32_t len;
+       int err = EOK;
+       uint16_t new_entries;
+
+       start_ex = ex;
+       new_entries = to_le16(eh->entries_count);
+       while (ex <= EXT_LAST_EXTENT(path[depth].header) &&
+              to_le32(ex->first_block) <= to) {
+               int32_t new_len = 0;
+               int unwritten;
+               ext4_lblk_t start, new_start;
+               ext4_fsblk_t newblock;
+               new_start = start = to_le32(ex->first_block);
+               len = ext4_ext_get_actual_len(ex);
+               newblock = ext4_ext_pblock(ex);
+               if (start < from) {
+                       len -= from - start;
+                       new_len = from - start;
+                       start = from;
+                       start_ex++;
+               } else {
+                       if (start + len - 1 > to) {
+                               len -= start + len - 1 - to;
+                               new_len = start + len - 1 - to;
+                               new_start = to + 1;
+                               newblock += to + 1 - start;
+                               ex2 = ex;
+                       }
+               }
+
+               ext4_ext_remove_blocks(inode_ref, ex, start, start + len - 1);
+               ex->first_block = to_le32(new_start);
+               if (!new_len)
+                       new_entries--;
+               else {
+                       unwritten = ext4_ext_is_unwritten(ex);
+                       ex->block_count = to_le16(new_len);
+                       ext4_ext_store_pblock(ex, newblock);
+                       if (unwritten)
+                               ext4_ext_mark_unwritten(ex);
+               }
+
+               ex += 1;
+       }
+
+       if (ex2 == NULL)
+               ex2 = ex;
+
+       if (ex2 <= EXT_LAST_EXTENT(eh))
+               memmove(start_ex, ex2, EXT_LAST_EXTENT(eh) - ex2 + 1);
+
+       eh->entries_count = to_le16(new_entries);
+       ext4_ext_dirty(inode_ref, path + depth);
+       if (path[depth].extent == EXT_FIRST_EXTENT(eh) && eh->entries_count)
+               err = ext4_ext_correct_indexes(inode_ref, path);
+
+       /* if this leaf is free, then we should
+        * remove it from index block above */
+       if (err == EOK && eh->entries_count == 0 && path[depth].block.lb_id)
+               err = ext4_ext_remove_idx(inode_ref, path, depth - 1);
+       else if (depth > 0)
+               path[depth - 1].index++;
+
+       return err;
+}
+
+static bool ext4_ext_more_to_rm(struct ext4_extent_path *path, ext4_lblk_t to)
+{
+       if (!to_le16(path->header->entries_count))
+               return false;
+
+       if (path->index > EXT_LAST_INDEX(path->header))
+               return false;
+
+       if (to_le32(path->index->first_block) > to)
+               return false;
+
+       return true;
+}
+
+int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from,
+                         ext4_lblk_t to)
+{
+       struct ext4_extent_path *path = NULL;
+       int ret = EOK;
+       int32_t depth = ext_depth(inode_ref->inode);
+       int32_t i;
+
+       ret = ext4_find_extent(inode_ref, from, &path, 0);
+       if (ret)
+               goto out;
+
+       if (!path[depth].extent) {
+               ret = EOK;
+               goto out;
+       }
+
+       bool in_range = IN_RANGE(from, to_le32(path[depth].extent->first_block),
+                       ext4_ext_get_actual_len(path[depth].extent));
+
+       if (!in_range) {
+               ret = EOK;
+               goto out;
+       }
+
+       /* If we do remove_space inside the range of an extent */
+       if ((to_le32(path[depth].extent->first_block) < from) &&
+           (to < to_le32(path[depth].extent->first_block) +
+                       ext4_ext_get_actual_len(path[depth].extent) - 1)) {
+
+               struct ext4_extent *ex = path[depth].extent, newex;
+               int unwritten = ext4_ext_is_unwritten(ex);
+               ext4_lblk_t ee_block = to_le32(ex->first_block);
+               int32_t len = ext4_ext_get_actual_len(ex);
+               ext4_fsblk_t newblock =
+                       to + 1 - ee_block + ext4_ext_pblock(ex);
+
+               ex->block_count = to_le16(from - ee_block);
+               if (unwritten)
+                       ext4_ext_mark_unwritten(ex);
+
+               ext4_ext_dirty(inode_ref, path + depth);
+
+               newex.first_block = to_le32(to + 1);
+               newex.block_count = to_le16(ee_block + len - 1 - to);
+               ext4_ext_store_pblock(&newex, newblock);
+               if (unwritten)
+                       ext4_ext_mark_unwritten(&newex);
+
+               ret = ext4_ext_insert_extent(inode_ref, &path, &newex, 0);
+               goto out;
+       }
+
+       i = depth;
+       while (i >= 0) {
+               if (i == depth) {
+                       struct ext4_extent_header *eh;
+                       struct ext4_extent *first_ex, *last_ex;
+                       ext4_lblk_t leaf_from, leaf_to;
+                       eh = path[i].header;
+                       ext4_assert(to_le16(eh->entries_count) > 0);
+                       first_ex = EXT_FIRST_EXTENT(eh);
+                       last_ex = EXT_LAST_EXTENT(eh);
+                       leaf_from = to_le32(first_ex->first_block);
+                       leaf_to = to_le32(last_ex->first_block) +
+                                 ext4_ext_get_actual_len(last_ex) - 1;
+                       if (leaf_from < from)
+                               leaf_from = from;
+
+                       if (leaf_to > to)
+                               leaf_to = to;
+
+                       ext4_ext_remove_leaf(inode_ref, path, leaf_from,
+                                       leaf_to);
+                       ext4_ext_drop_refs(inode_ref, path + i, 0);
+                       i--;
+                       continue;
+               }
+
+               struct ext4_extent_header *eh;
+               eh = path[i].header;
+               if (ext4_ext_more_to_rm(path + i, to)) {
+                       struct ext4_block bh = EXT4_BLOCK_ZERO();
+                       if (path[i + 1].block.lb_id)
+                               ext4_ext_drop_refs(inode_ref, path + i + 1, 0);
+
+                       ret = read_extent_tree_block(inode_ref,
+                                       ext4_idx_pblock(path[i].index),
+                                       depth - i - 1, &bh, 0);
+                       if (ret)
+                               goto out;
+
+                       path[i].p_block =
+                                       ext4_idx_pblock(path[i].index);
+                       path[i + 1].block = bh;
+                       path[i + 1].header = ext_block_hdr(&bh);
+                       path[i + 1].depth = depth - i - 1;
+                       if (i + 1 == depth)
+                               path[i + 1].extent = EXT_FIRST_EXTENT(
+                                       path[i + 1].header);
+                       else
+                               path[i + 1].index =
+                                       EXT_FIRST_INDEX(path[i + 1].header);
+
+                       i++;
+               } else {
+                       if (i > 0) {
+                               if (!eh->entries_count)
+                                       ret = ext4_ext_remove_idx(inode_ref, path,
+                                                       i - 1);
+                               else
+                                       path[i - 1].index++;
+
+                       }
+
+                       if (i)
+                               ext4_block_set(inode_ref->fs->bdev,
+                                               &path[i].block);
+
+
+                       i--;
+               }
+
+       }
+
+       /* TODO: flexible tree reduction should be here */
+       if (path->header->entries_count == 0) {
+               /*
+                * truncate to zero freed all the tree,
+                * so we need to correct eh_depth
+                */
+               ext_inode_hdr(inode_ref->inode)->depth = 0;
+               ext_inode_hdr(inode_ref->inode)->max_entries_count =
+                   to_le16(ext4_ext_space_root(inode_ref));
+               ret = ext4_ext_dirty(inode_ref, path);
+       }
+
+out:
+       ext4_ext_drop_refs(inode_ref, path, 0);
+       free(path);
+       path = NULL;
+       return ret;
+}
+
+static int ext4_ext_split_extent_at(struct ext4_inode_ref *inode_ref,
+                                   struct ext4_extent_path **ppath,
+                                   ext4_lblk_t split, uint32_t split_flag)
+{
+       struct ext4_extent *ex, newex;
+       ext4_fsblk_t newblock;
+       ext4_lblk_t ee_block;
+       int32_t ee_len;
+       int32_t depth = ext_depth(inode_ref->inode);
+       int err = EOK;
+
+       ex = (*ppath)[depth].extent;
+       ee_block = to_le32(ex->first_block);
+       ee_len = ext4_ext_get_actual_len(ex);
+       newblock = split - ee_block + ext4_ext_pblock(ex);
+
+       if (split == ee_block) {
+               /*
+                * case b: block @split is the block that the extent begins with
+                * then we just change the state of the extent, and splitting
+                * is not needed.
+                */
+               if (split_flag & EXT4_EXT_MARK_UNWRIT2)
+                       ext4_ext_mark_unwritten(ex);
+               else
+                       ext4_ext_mark_initialized(ex);
+
+               err = ext4_ext_dirty(inode_ref, *ppath + depth);
+               goto out;
+       }
+
+       ex->block_count = to_le16(split - ee_block);
+       if (split_flag & EXT4_EXT_MARK_UNWRIT1)
+               ext4_ext_mark_unwritten(ex);
+
+       err = ext4_ext_dirty(inode_ref, *ppath + depth);
+       if (err != EOK)
+               goto out;
+
+       newex.first_block = to_le32(split);
+       newex.block_count = to_le16(ee_len - (split - ee_block));
+       ext4_ext_store_pblock(&newex, newblock);
+       if (split_flag & EXT4_EXT_MARK_UNWRIT2)
+               ext4_ext_mark_unwritten(&newex);
+       err = ext4_ext_insert_extent(inode_ref, ppath, &newex,
+                                    EXT4_EXT_NO_COMBINE);
+       if (err != EOK)
+               goto restore_extent_len;
+
+out:
+       return err;
+restore_extent_len:
+       ex->block_count = to_le16(ee_len);
+       err = ext4_ext_dirty(inode_ref, *ppath + depth);
+       return err;
+}
+
+static int ext4_ext_convert_to_initialized(struct ext4_inode_ref *inode_ref,
+                                          struct ext4_extent_path **ppath,
+                                          ext4_lblk_t split, uint32_t blocks)
+{
+       int32_t depth = ext_depth(inode_ref->inode), err = EOK;
+       struct ext4_extent *ex = (*ppath)[depth].extent;
+
+       ext4_assert(to_le32(ex->first_block) <= split);
+
+       if (split + blocks ==
+           to_le32(ex->first_block) + ext4_ext_get_actual_len(ex)) {
+               /* split and initialize right part */
+               err = ext4_ext_split_extent_at(inode_ref, ppath, split,
+                                              EXT4_EXT_MARK_UNWRIT1);
+       } else if (to_le32(ex->first_block) == split) {
+               /* split and initialize left part */
+               err = ext4_ext_split_extent_at(inode_ref, ppath, split + blocks,
+                                              EXT4_EXT_MARK_UNWRIT2);
+       } else {
+               /* split 1 extent to 3 and initialize the 2nd */
+               err = ext4_ext_split_extent_at(inode_ref, ppath, split + blocks,
+                                              EXT4_EXT_MARK_UNWRIT1 |
+                                                  EXT4_EXT_MARK_UNWRIT2);
+               if (!err) {
+                       err = ext4_ext_split_extent_at(inode_ref, ppath, split,
+                                                      EXT4_EXT_MARK_UNWRIT1);
+               }
+       }
+
+       return err;
+}
+
+static ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_extent_path *path)
+{
+       int32_t depth;
+
+       depth = path->depth;
+
+       if (depth == 0 && path->extent == NULL)
+               return EXT_MAX_BLOCKS;
+
+       while (depth >= 0) {
+               if (depth == path->depth) {
+                       /* leaf */
+                       if (path[depth].extent &&
+                           path[depth].extent !=
+                               EXT_LAST_EXTENT(path[depth].header))
+                               return to_le32(
+                                   path[depth].extent[1].first_block);
+               } else {
+                       /* index */
+                       if (path[depth].index !=
+                           EXT_LAST_INDEX(path[depth].header))
+                               return to_le32(
+                                   path[depth].index[1].first_block);
+               }
+               depth--;
+       }
+
+       return EXT_MAX_BLOCKS;
+}
+
+static int ext4_ext_zero_unwritten_range(struct ext4_inode_ref *inode_ref,
+                                        ext4_fsblk_t block,
+                                        uint32_t blocks_count)
+{
+       int err = EOK;
+       uint32_t i;
+       uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
+       for (i = 0; i < blocks_count; i++) {
+               struct ext4_block bh = EXT4_BLOCK_ZERO();
+               err = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, block + i);
+               if (err != EOK)
+                       break;
+
+               memset(bh.data, 0, block_size);
+               ext4_trans_set_block_dirty(bh.buf);
+               err = ext4_block_set(inode_ref->fs->bdev, &bh);
+               if (err != EOK)
+                       break;
+       }
+       return err;
+}
+
+int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_lblk_t iblock,
+                       uint32_t max_blocks, ext4_fsblk_t *result, bool create,
+                       uint32_t *blocks_count)
+{
+       struct ext4_extent_path *path = NULL;
+       struct ext4_extent newex, *ex;
+       ext4_fsblk_t goal;
+       int err = EOK;
+       int32_t depth;
+       uint32_t allocated = 0;
+       ext4_lblk_t next;
+       ext4_fsblk_t newblock;
+
+       if (result)
+               *result = 0;
+
+       if (blocks_count)
+               *blocks_count = 0;
+
+       /* find extent for this block */
+       err = ext4_find_extent(inode_ref, iblock, &path, 0);
+       if (err != EOK) {
+               path = NULL;
+               goto out2;
+       }
+
+       depth = ext_depth(inode_ref->inode);
+
+       /*
+        * consistent leaf must not be empty
+        * this situations is possible, though, _during_ tree modification
+        * this is why assert can't be put in ext4_ext_find_extent()
+        */
+       ex = path[depth].extent;
+       if (ex) {
+               ext4_lblk_t ee_block = to_le32(ex->first_block);
+               ext4_fsblk_t ee_start = ext4_ext_pblock(ex);
+               uint16_t ee_len = ext4_ext_get_actual_len(ex);
+               /* if found exent covers block, simple return it */
+               if (IN_RANGE(iblock, ee_block, ee_len)) {
+                       /* number of remain blocks in the extent */
+                       allocated = ee_len - (iblock - ee_block);
+
+                       if (!ext4_ext_is_unwritten(ex)) {
+                               newblock = iblock - ee_block + ee_start;
+                               goto out;
+                       }
+
+                       if (!create) {
+                               newblock = 0;
+                               goto out;
+                       }
+
+                       uint32_t zero_range;
+                       zero_range = allocated;
+                       if (zero_range > max_blocks)
+                               zero_range = max_blocks;
+
+                       newblock = iblock - ee_block + ee_start;
+                       err = ext4_ext_zero_unwritten_range(inode_ref, newblock,
+                                       zero_range);
+                       if (err != EOK)
+                               goto out2;
+
+                       err = ext4_ext_convert_to_initialized(inode_ref, &path,
+                                       iblock, zero_range);
+                       if (err != EOK)
+                               goto out2;
+
+                       goto out;
+               }
+       }
+
+       /*
+        * requested block isn't allocated yet
+        * we couldn't try to create block if create flag is zero
+        */
+       if (!create) {
+               goto out2;
+       }
+
+       /* find next allocated block so that we know how many
+        * blocks we can allocate without ovelapping next extent */
+       next = ext4_ext_next_allocated_block(path);
+       allocated = next - iblock;
+       if (allocated > max_blocks)
+               allocated = max_blocks;
+
+       /* allocate new block */
+       goal = ext4_ext_find_goal(inode_ref, path, iblock);
+       newblock = ext4_new_meta_blocks(inode_ref, goal, 0, &allocated, &err);
+       if (!newblock)
+               goto out2;
+
+       /* try to insert new extent into found leaf and return */
+       newex.first_block = to_le32(iblock);
+       ext4_ext_store_pblock(&newex, newblock);
+       newex.block_count = to_le16(allocated);
+       err = ext4_ext_insert_extent(inode_ref, &path, &newex, 0);
+       if (err != EOK) {
+               /* free data blocks we just allocated */
+               ext4_ext_free_blocks(inode_ref, ext4_ext_pblock(&newex),
+                                    to_le16(newex.block_count), 0);
+               goto out2;
+       }
+
+       /* previous routine could use block we allocated */
+       newblock = ext4_ext_pblock(&newex);
+
+out:
+       if (allocated > max_blocks)
+               allocated = max_blocks;
+
+       if (result)
+               *result = newblock;
+
+       if (blocks_count)
+               *blocks_count = allocated;
+
+out2:
+       if (path) {
+               ext4_ext_drop_refs(inode_ref, path, 0);
+               free(path);
+       }
+
+       return err;
+}
diff --git a/src/ext4_fs.c b/src/ext4_fs.c
new file mode 100644 (file)
index 0000000..392728d
--- /dev/null
@@ -0,0 +1,1713 @@
+/*
+ * 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_fs.c
+ * @brief More complex filesystem functions.
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_fs.h"
+#include "ext4_errno.h"
+#include "ext4_blockdev.h"
+#include "ext4_super.h"
+#include "ext4_crc32.h"
+#include "ext4_debug.h"
+#include "ext4_block_group.h"
+#include "ext4_balloc.h"
+#include "ext4_bitmap.h"
+#include "ext4_inode.h"
+#include "ext4_ialloc.h"
+#include "ext4_extent.h"
+
+#include <string.h>
+
+int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev)
+{
+       int r, i;
+       uint16_t tmp;
+       uint32_t bsize;
+       bool read_only = false;
+
+       ext4_assert(fs && bdev);
+
+       fs->bdev = bdev;
+
+       r = ext4_sb_read(fs->bdev, &fs->sb);
+       if (r != EOK)
+               return r;
+
+       if (!ext4_sb_check(&fs->sb))
+               return ENOTSUP;
+
+       bsize = ext4_sb_get_block_size(&fs->sb);
+       if (bsize > EXT4_MAX_BLOCK_SIZE)
+               return ENXIO;
+
+       r = ext4_fs_check_features(fs, &read_only);
+       if (r != EOK)
+               return r;
+
+       if (read_only)
+               return ENOTSUP;
+
+       /* Compute limits for indirect block levels */
+       uint32_t blocks_id = bsize / sizeof(uint32_t);
+
+       fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
+       fs->inode_blocks_per_level[0] = 1;
+
+       for (i = 1; i < 4; i++) {
+               fs->inode_blocks_per_level[i] =
+                   fs->inode_blocks_per_level[i - 1] * blocks_id;
+               fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] +
+                                           fs->inode_blocks_per_level[i];
+       }
+
+       /*Validate FS*/
+       tmp = ext4_get16(&fs->sb, state);
+       if (tmp & EXT4_SUPERBLOCK_STATE_ERROR_FS)
+               ext4_dbg(DEBUG_FS, DBG_WARN
+                               "last umount error: superblock fs_error flag\n");
+
+
+       /* Mark system as mounted */
+       ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_ERROR_FS);
+       r = ext4_sb_write(fs->bdev, &fs->sb);
+       if (r != EOK)
+               return r;
+
+       /*Update mount count*/
+       ext4_set16(&fs->sb, mount_count, ext4_get16(&fs->sb, mount_count) + 1);
+
+       return r;
+}
+
+int ext4_fs_fini(struct ext4_fs *fs)
+{
+       ext4_assert(fs);
+
+       /*Set superblock state*/
+       ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_VALID_FS);
+
+       return ext4_sb_write(fs->bdev, &fs->sb);
+}
+
+static void ext4_fs_debug_features_inc(uint32_t features_incompatible)
+{
+       if (features_incompatible & EXT4_FINCOM_COMPRESSION)
+               ext4_dbg(DEBUG_FS, DBG_NONE "compression\n");
+       if (features_incompatible & EXT4_FINCOM_FILETYPE)
+               ext4_dbg(DEBUG_FS, DBG_NONE "filetype\n");
+       if (features_incompatible & EXT4_FINCOM_RECOVER)
+               ext4_dbg(DEBUG_FS, DBG_NONE "recover\n");
+       if (features_incompatible & EXT4_FINCOM_JOURNAL_DEV)
+               ext4_dbg(DEBUG_FS, DBG_NONE "journal_dev\n");
+       if (features_incompatible & EXT4_FINCOM_META_BG)
+               ext4_dbg(DEBUG_FS, DBG_NONE "meta_bg\n");
+       if (features_incompatible & EXT4_FINCOM_EXTENTS)
+               ext4_dbg(DEBUG_FS, DBG_NONE "extents\n");
+       if (features_incompatible & EXT4_FINCOM_64BIT)
+               ext4_dbg(DEBUG_FS, DBG_NONE "64bit\n");
+       if (features_incompatible & EXT4_FINCOM_MMP)
+               ext4_dbg(DEBUG_FS, DBG_NONE "mnp\n");
+       if (features_incompatible & EXT4_FINCOM_FLEX_BG)
+               ext4_dbg(DEBUG_FS, DBG_NONE "flex_bg\n");
+       if (features_incompatible & EXT4_FINCOM_EA_INODE)
+               ext4_dbg(DEBUG_FS, DBG_NONE "ea_inode\n");
+       if (features_incompatible & EXT4_FINCOM_DIRDATA)
+               ext4_dbg(DEBUG_FS, DBG_NONE "dirdata\n");
+       if (features_incompatible & EXT4_FINCOM_BG_USE_META_CSUM)
+               ext4_dbg(DEBUG_FS, DBG_NONE "meta_csum\n");
+       if (features_incompatible & EXT4_FINCOM_LARGEDIR)
+               ext4_dbg(DEBUG_FS, DBG_NONE "largedir\n");
+       if (features_incompatible & EXT4_FINCOM_INLINE_DATA)
+               ext4_dbg(DEBUG_FS, DBG_NONE "inline_data\n");
+}
+static void ext4_fs_debug_features_comp(uint32_t features_compatible)
+{
+       if (features_compatible & EXT4_FCOM_DIR_PREALLOC)
+               ext4_dbg(DEBUG_FS, DBG_NONE "dir_prealloc\n");
+       if (features_compatible & EXT4_FCOM_IMAGIC_INODES)
+               ext4_dbg(DEBUG_FS, DBG_NONE "imagic_inodes\n");
+       if (features_compatible & EXT4_FCOM_HAS_JOURNAL)
+               ext4_dbg(DEBUG_FS, DBG_NONE "has_journal\n");
+       if (features_compatible & EXT4_FCOM_EXT_ATTR)
+               ext4_dbg(DEBUG_FS, DBG_NONE "ext_attr\n");
+       if (features_compatible & EXT4_FCOM_RESIZE_INODE)
+               ext4_dbg(DEBUG_FS, DBG_NONE "resize_inode\n");
+       if (features_compatible & EXT4_FCOM_DIR_INDEX)
+               ext4_dbg(DEBUG_FS, DBG_NONE "dir_index\n");
+}
+
+static void ext4_fs_debug_features_ro(uint32_t features_ro)
+{
+       if (features_ro & EXT4_FRO_COM_SPARSE_SUPER)
+               ext4_dbg(DEBUG_FS, DBG_NONE "sparse_super\n");
+       if (features_ro & EXT4_FRO_COM_LARGE_FILE)
+               ext4_dbg(DEBUG_FS, DBG_NONE "large_file\n");
+       if (features_ro & EXT4_FRO_COM_BTREE_DIR)
+               ext4_dbg(DEBUG_FS, DBG_NONE "btree_dir\n");
+       if (features_ro & EXT4_FRO_COM_HUGE_FILE)
+               ext4_dbg(DEBUG_FS, DBG_NONE "huge_file\n");
+       if (features_ro & EXT4_FRO_COM_GDT_CSUM)
+               ext4_dbg(DEBUG_FS, DBG_NONE "gtd_csum\n");
+       if (features_ro & EXT4_FRO_COM_DIR_NLINK)
+               ext4_dbg(DEBUG_FS, DBG_NONE "dir_nlink\n");
+       if (features_ro & EXT4_FRO_COM_EXTRA_ISIZE)
+               ext4_dbg(DEBUG_FS, DBG_NONE "extra_isize\n");
+       if (features_ro & EXT4_FRO_COM_QUOTA)
+               ext4_dbg(DEBUG_FS, DBG_NONE "quota\n");
+       if (features_ro & EXT4_FRO_COM_BIGALLOC)
+               ext4_dbg(DEBUG_FS, DBG_NONE "bigalloc\n");
+       if (features_ro & EXT4_FRO_COM_METADATA_CSUM)
+               ext4_dbg(DEBUG_FS, DBG_NONE "metadata_csum\n");
+}
+
+int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only)
+{
+       ext4_assert(fs && read_only);
+       uint32_t v;
+       if (ext4_get32(&fs->sb, rev_level) == 0) {
+               *read_only = false;
+               return EOK;
+       }
+
+       ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_incompatible:\n");
+       ext4_fs_debug_features_inc(ext4_get32(&fs->sb, features_incompatible));
+
+       ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_compatible:\n");
+       ext4_fs_debug_features_comp(ext4_get32(&fs->sb, features_compatible));
+
+       ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_read_only:\n");
+       ext4_fs_debug_features_ro(ext4_get32(&fs->sb, features_read_only));
+
+       /*Check features_incompatible*/
+       v = (ext4_get32(&fs->sb, features_incompatible) &
+            (~CONFIG_SUPPORTED_FINCOM));
+       if (v) {
+               ext4_dbg(DEBUG_FS, DBG_ERROR
+                               "sblock has unsupported features incompatible:\n");
+               ext4_fs_debug_features_inc(v);
+               return ENOTSUP;
+       }
+
+       /*Check features_read_only*/
+       v = ext4_get32(&fs->sb, features_read_only);
+       v &= ~CONFIG_SUPPORTED_FRO_COM;
+       if (v) {
+               ext4_dbg(DEBUG_FS, DBG_WARN
+                       "sblock has unsupported features read only:\n");
+               ext4_fs_debug_features_ro(v);
+               *read_only = true;
+               return EOK;
+       }
+       *read_only = false;
+
+       return EOK;
+}
+
+/**@brief Determine whether the block is inside the group.
+ * @param baddr   block address
+ * @param bgid    block group id
+ * @return Error code
+ */
+static bool ext4_block_in_group(struct ext4_sblock *s, ext4_fsblk_t baddr,
+                               uint32_t bgid)
+{
+       uint32_t actual_bgid;
+       actual_bgid = ext4_balloc_get_bgid_of_block(s, baddr);
+       if (actual_bgid == bgid)
+               return true;
+       return false;
+}
+
+/**@brief   To avoid calling the atomic setbit hundreds or thousands of times, we only
+ *          need to use it within a single byte (to ensure we get endianness right).
+ *          We can use memset for the rest of the bitmap as there are no other users.
+ */
+static void ext4_fs_mark_bitmap_end(int start_bit, int end_bit, void *bitmap)
+{
+       int i;
+
+       if (start_bit >= end_bit)
+               return;
+
+       for (i = start_bit; (unsigned)i < ((start_bit + 7) & ~7UL); i++)
+               ext4_bmap_bit_set(bitmap, i);
+
+       if (i < end_bit)
+               memset((char *)bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
+}
+
+/**@brief Initialize block bitmap in block group.
+ * @param bg_ref Reference to block group
+ * @return Error code
+ */
+static int ext4_fs_init_block_bitmap(struct ext4_block_group_ref *bg_ref)
+{
+       struct ext4_sblock *sb = &bg_ref->fs->sb;
+       struct ext4_bgroup *bg = bg_ref->block_group;
+       int rc;
+
+       uint32_t i, bit, bit_max;
+       uint32_t group_blocks;
+       uint16_t inode_size = ext4_get16(sb, inode_size);
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
+
+       ext4_fsblk_t bmp_blk = ext4_bg_get_block_bitmap(bg, sb);
+       ext4_fsblk_t bmp_inode = ext4_bg_get_inode_bitmap(bg, sb);
+       ext4_fsblk_t inode_table = ext4_bg_get_inode_table_first_block(bg, sb);
+       ext4_fsblk_t first_bg = ext4_balloc_get_block_of_bgid(sb, bg_ref->index);
+
+       uint32_t dsc_per_block =  block_size / ext4_sb_get_desc_size(sb);
+
+       bool flex_bg = ext4_sb_feature_incom(sb, EXT4_FINCOM_FLEX_BG);
+       bool meta_bg = ext4_sb_feature_incom(sb, EXT4_FINCOM_META_BG);
+
+       uint32_t inode_table_bcnt = inodes_per_group * inode_size / block_size;
+
+       struct ext4_block block_bitmap;
+       rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &block_bitmap, bmp_blk);
+       if (rc != EOK)
+               return rc;
+
+       memset(block_bitmap.data, 0, block_size);
+       bit_max = ext4_sb_is_super_in_bg(sb, bg_ref->index);
+
+       uint32_t count = ext4_sb_first_meta_bg(sb) * dsc_per_block;
+       if (!meta_bg || bg_ref->index < count) {
+               if (bit_max) {
+                       bit_max += ext4_bg_num_gdb(sb, bg_ref->index);
+                       bit_max += ext4_get16(sb, s_reserved_gdt_blocks);
+               }
+       } else { /* For META_BG_BLOCK_GROUPS */
+               bit_max += ext4_bg_num_gdb(sb, bg_ref->index);
+       }
+       for (bit = 0; bit < bit_max; bit++)
+               ext4_bmap_bit_set(block_bitmap.data, bit);
+
+       if (bg_ref->index == ext4_block_group_cnt(sb) - 1) {
+               /*
+                * Even though mke2fs always initialize first and last group
+                * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need
+                * to make sure we calculate the right free blocks
+                */
+
+               group_blocks = (uint32_t)(ext4_sb_get_blocks_cnt(sb) -
+                                         ext4_get32(sb, first_data_block) -
+                                         ext4_get32(sb, blocks_per_group) *
+                                         (ext4_block_group_cnt(sb) - 1));
+       } else {
+               group_blocks = ext4_get32(sb, blocks_per_group);
+       }
+
+       bool in_bg;
+       in_bg = ext4_block_in_group(sb, bmp_blk, bg_ref->index);
+       if (!flex_bg || in_bg)
+               ext4_bmap_bit_set(block_bitmap.data,
+                                 (uint32_t)(bmp_blk - first_bg));
+
+       in_bg = ext4_block_in_group(sb, bmp_inode, bg_ref->index);
+       if (!flex_bg || in_bg)
+               ext4_bmap_bit_set(block_bitmap.data,
+                                 (uint32_t)(bmp_inode - first_bg));
+
+        for (i = inode_table; i < inode_table + inode_table_bcnt; i++) {
+               in_bg = ext4_block_in_group(sb, i, bg_ref->index);
+               if (!flex_bg || in_bg)
+                       ext4_bmap_bit_set(block_bitmap.data,
+                                         (uint32_t)(i - first_bg));
+       }
+        /*
+         * Also if the number of blocks within the group is
+         * less than the blocksize * 8 ( which is the size
+         * of bitmap ), set rest of the block bitmap to 1
+         */
+        ext4_fs_mark_bitmap_end(group_blocks, block_size * 8, block_bitmap.data);
+       ext4_trans_set_block_dirty(block_bitmap.buf);
+
+       ext4_balloc_set_bitmap_csum(sb, bg_ref->block_group, block_bitmap.data);
+       bg_ref->dirty = true;
+
+       /* Save bitmap */
+       return ext4_block_set(bg_ref->fs->bdev, &block_bitmap);
+}
+
+/**@brief Initialize i-node bitmap in block group.
+ * @param bg_ref Reference to block group
+ * @return Error code
+ */
+static int ext4_fs_init_inode_bitmap(struct ext4_block_group_ref *bg_ref)
+{
+       int rc;
+       struct ext4_sblock *sb = &bg_ref->fs->sb;
+       struct ext4_bgroup *bg = bg_ref->block_group;
+
+       /* Load bitmap */
+       ext4_fsblk_t bitmap_block_addr = ext4_bg_get_inode_bitmap(bg, sb);
+
+       struct ext4_block b;
+       rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &b, bitmap_block_addr);
+       if (rc != EOK)
+               return rc;
+
+       /* Initialize all bitmap bits to zero */
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
+
+       memset(b.data, 0, (inodes_per_group + 7) / 8);
+
+       uint32_t start_bit = inodes_per_group;
+       uint32_t end_bit = block_size * 8;
+
+       uint32_t i;
+       for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++)
+               ext4_bmap_bit_set(b.data, i);
+
+       if (i < end_bit)
+               memset(b.data + (i >> 3), 0xff, (end_bit - i) >> 3);
+
+       ext4_trans_set_block_dirty(b.buf);
+
+       ext4_ialloc_set_bitmap_csum(sb, bg, b.data);
+       bg_ref->dirty = true;
+
+       /* Save bitmap */
+       return ext4_block_set(bg_ref->fs->bdev, &b);
+}
+
+/**@brief Initialize i-node table in block group.
+ * @param bg_ref Reference to block group
+ * @return Error code
+ */
+static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref)
+{
+       struct ext4_sblock *sb = &bg_ref->fs->sb;
+       struct ext4_bgroup *bg = bg_ref->block_group;
+
+       uint32_t inode_size = ext4_get32(sb, inode_size);
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint32_t inodes_per_block = block_size / inode_size;
+       uint32_t inodes_in_group = ext4_inodes_in_group_cnt(sb, bg_ref->index);
+       uint32_t table_blocks = inodes_in_group / inodes_per_block;
+       ext4_fsblk_t fblock;
+
+       if (inodes_in_group % inodes_per_block)
+               table_blocks++;
+
+       /* Compute initialization bounds */
+       ext4_fsblk_t first_block = ext4_bg_get_inode_table_first_block(bg, sb);
+
+       ext4_fsblk_t last_block = first_block + table_blocks - 1;
+
+       /* Initialization of all itable blocks */
+       for (fblock = first_block; fblock <= last_block; ++fblock) {
+               struct ext4_block b;
+               int rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &b, fblock);
+               if (rc != EOK)
+                       return rc;
+
+               memset(b.data, 0, block_size);
+               ext4_trans_set_block_dirty(b.buf);
+
+               ext4_block_set(bg_ref->fs->bdev, &b);
+               if (rc != EOK)
+                       return rc;
+       }
+
+       return EOK;
+}
+
+static ext4_fsblk_t ext4_fs_get_descriptor_block(struct ext4_sblock *s,
+                                            uint32_t bgid,
+                                            uint32_t dsc_per_block)
+{
+       uint32_t first_meta_bg, dsc_id;
+       int has_super = 0;
+       dsc_id = bgid / dsc_per_block;
+       first_meta_bg = ext4_sb_first_meta_bg(s);
+
+       bool meta_bg = ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG);
+
+       if (!meta_bg || dsc_id < first_meta_bg)
+               return ext4_get32(s, first_data_block) + dsc_id + 1;
+
+       if (ext4_sb_is_super_in_bg(s, bgid))
+               has_super = 1;
+
+       return (has_super + ext4_fs_first_bg_block_no(s, bgid));
+}
+
+/**@brief  Compute checksum of block group descriptor.
+ * @param sb   Superblock
+ * @param bgid Index of block group in the filesystem
+ * @param bg   Block group to compute checksum for
+ * @return Checksum value
+ */
+static uint16_t ext4_fs_bg_checksum(struct ext4_sblock *sb, uint32_t bgid,
+                                   struct ext4_bgroup *bg)
+{
+       /* If checksum not supported, 0 will be returned */
+       uint16_t crc = 0;
+#if CONFIG_META_CSUM_ENABLE
+       /* Compute the checksum only if the filesystem supports it */
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               /* Use metadata_csum algorithm instead */
+               uint32_t le32_bgid = to_le32(bgid);
+               uint32_t orig_checksum, checksum;
+
+               /* Preparation: temporarily set bg checksum to 0 */
+               orig_checksum = bg->checksum;
+               bg->checksum = 0;
+
+               /* First calculate crc32 checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
+                               sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against bgid */
+               checksum = ext4_crc32c(checksum, &le32_bgid, sizeof(bgid));
+               /* Finally calculate crc32 checksum against block_group_desc */
+               checksum = ext4_crc32c(checksum, bg, ext4_sb_get_desc_size(sb));
+               bg->checksum = orig_checksum;
+
+               crc = checksum & 0xFFFF;
+               return crc;
+       }
+#endif
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_GDT_CSUM)) {
+               uint8_t *base = (uint8_t *)bg;
+               uint8_t *checksum = (uint8_t *)&bg->checksum;
+
+               uint32_t offset = (uint32_t)(checksum - base);
+
+               /* Convert block group index to little endian */
+               uint32_t group = to_le32(bgid);
+
+               /* Initialization */
+               crc = ext4_bg_crc16(~0, sb->uuid, sizeof(sb->uuid));
+
+               /* Include index of block group */
+               crc = ext4_bg_crc16(crc, (uint8_t *)&group, sizeof(group));
+
+               /* Compute crc from the first part (stop before checksum field)
+                */
+               crc = ext4_bg_crc16(crc, (uint8_t *)bg, offset);
+
+               /* Skip checksum */
+               offset += sizeof(bg->checksum);
+
+               /* Checksum of the rest of block group descriptor */
+               if ((ext4_sb_feature_incom(sb, EXT4_FINCOM_64BIT)) &&
+                   (offset < ext4_sb_get_desc_size(sb))) {
+
+                       const uint8_t *start = ((uint8_t *)bg) + offset;
+                       size_t len = ext4_sb_get_desc_size(sb) - offset;
+                       crc = ext4_bg_crc16(crc, start, len);
+               }
+       }
+       return crc;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool ext4_fs_verify_bg_csum(struct ext4_sblock *sb,
+                                  uint32_t bgid,
+                                  struct ext4_bgroup *bg)
+{
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return true;
+
+       return ext4_fs_bg_checksum(sb, bgid, bg) == to_le16(bg->checksum);
+}
+#else
+#define ext4_fs_verify_bg_csum(...) true
+#endif
+
+int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid,
+                               struct ext4_block_group_ref *ref)
+{
+       /* Compute number of descriptors, that fits in one data block */
+       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
+       uint32_t dsc_cnt = block_size / ext4_sb_get_desc_size(&fs->sb);
+
+       /* Block group descriptor table starts at the next block after
+        * superblock */
+       uint64_t block_id = ext4_fs_get_descriptor_block(&fs->sb, bgid, dsc_cnt);
+
+       uint32_t offset = (bgid % dsc_cnt) * ext4_sb_get_desc_size(&fs->sb);
+
+       int rc = ext4_trans_block_get(fs->bdev, &ref->block, block_id);
+       if (rc != EOK)
+               return rc;
+
+       ref->block_group = (void *)(ref->block.data + offset);
+       ref->fs = fs;
+       ref->index = bgid;
+       ref->dirty = false;
+       struct ext4_bgroup *bg = ref->block_group;
+
+       if (!ext4_fs_verify_bg_csum(&fs->sb, bgid, bg)) {
+               ext4_dbg(DEBUG_FS,
+                        DBG_WARN "Block group descriptor checksum failed."
+                        "Block group index: %" PRIu32"\n",
+                        bgid);
+       }
+
+       if (ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_BLOCK_UNINIT)) {
+               rc = ext4_fs_init_block_bitmap(ref);
+               if (rc != EOK) {
+                       ext4_block_set(fs->bdev, &ref->block);
+                       return rc;
+               }
+               ext4_bg_clear_flag(bg, EXT4_BLOCK_GROUP_BLOCK_UNINIT);
+               ref->dirty = true;
+       }
+
+       if (ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_INODE_UNINIT)) {
+               rc = ext4_fs_init_inode_bitmap(ref);
+               if (rc != EOK) {
+                       ext4_block_set(ref->fs->bdev, &ref->block);
+                       return rc;
+               }
+
+               ext4_bg_clear_flag(bg, EXT4_BLOCK_GROUP_INODE_UNINIT);
+
+               if (!ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
+                       rc = ext4_fs_init_inode_table(ref);
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &ref->block);
+                               return rc;
+                       }
+
+                       ext4_bg_set_flag(bg, EXT4_BLOCK_GROUP_ITABLE_ZEROED);
+               }
+
+               ref->dirty = true;
+       }
+
+       return EOK;
+}
+
+int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref)
+{
+       /* Check if reference modified */
+       if (ref->dirty) {
+               /* Compute new checksum of block group */
+               uint16_t cs;
+               cs = ext4_fs_bg_checksum(&ref->fs->sb, ref->index,
+                                        ref->block_group);
+               ref->block_group->checksum = to_le16(cs);
+
+               /* Mark block dirty for writing changes to physical device */
+               ext4_trans_set_block_dirty(ref->block.buf);
+       }
+
+       /* Put back block, that contains block group descriptor */
+       return ext4_block_set(ref->fs->bdev, &ref->block);
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_fs_inode_checksum(struct ext4_inode_ref *inode_ref)
+{
+       uint32_t checksum = 0;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint16_t inode_size = ext4_get16(sb, inode_size);
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t orig_checksum;
+
+               uint32_t ino_index = to_le32(inode_ref->index);
+               uint32_t ino_gen =
+                       to_le32(ext4_inode_get_generation(inode_ref->inode));
+
+               /* Preparation: temporarily set bg checksum to 0 */
+               orig_checksum = ext4_inode_get_csum(sb, inode_ref->inode);
+               ext4_inode_set_csum(sb, inode_ref->inode, 0);
+
+               /* First calculate crc32 checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
+                                      sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against inode number
+                * and inode generation */
+               checksum = ext4_crc32c(checksum, &ino_index, sizeof(ino_index));
+               checksum = ext4_crc32c(checksum, &ino_gen, sizeof(ino_gen));
+               /* Finally calculate crc32 checksum against
+                * the entire inode */
+               checksum = ext4_crc32c(checksum, inode_ref->inode, inode_size);
+               ext4_inode_set_csum(sb, inode_ref->inode, orig_checksum);
+
+               /* If inode size is not large enough to hold the
+                * upper 16bit of the checksum */
+               if (inode_size == EXT4_GOOD_OLD_INODE_SIZE)
+                       checksum &= 0xFFFF;
+
+       }
+       return checksum;
+}
+#else
+#define ext4_fs_inode_checksum(...) 0
+#endif
+
+static void ext4_fs_set_inode_checksum(struct ext4_inode_ref *inode_ref)
+{
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return;
+
+       uint32_t csum = ext4_fs_inode_checksum(inode_ref);
+       ext4_inode_set_csum(sb, inode_ref->inode, csum);
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool ext4_fs_verify_inode_csum(struct ext4_inode_ref *inode_ref)
+{
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return true;
+
+       return ext4_inode_get_csum(sb, inode_ref->inode) ==
+               ext4_fs_inode_checksum(inode_ref);
+}
+#else
+#define ext4_fs_verify_inode_csum(...) true
+#endif
+
+static int
+__ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
+                       struct ext4_inode_ref *ref,
+                       bool initialized)
+{
+       /* Compute number of i-nodes, that fits in one data block */
+       uint32_t inodes_per_group = ext4_get32(&fs->sb, inodes_per_group);
+
+       /*
+        * Inode numbers are 1-based, but it is simpler to work with 0-based
+        * when computing indices
+        */
+       index -= 1;
+       uint32_t block_group = index / inodes_per_group;
+       uint32_t offset_in_group = index % inodes_per_group;
+
+       /* Load block group, where i-node is located */
+       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 block address, where i-node table is located */
+       ext4_fsblk_t inode_table_start =
+           ext4_bg_get_inode_table_first_block(bg_ref.block_group, &fs->sb);
+
+       /* Put back block group reference (not needed more) */
+       rc = ext4_fs_put_block_group_ref(&bg_ref);
+       if (rc != EOK) {
+               return rc;
+       }
+
+       /* Compute position of i-node in the block group */
+       uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
+       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
+       uint32_t byte_offset_in_group = offset_in_group * inode_size;
+
+       /* Compute block address */
+       ext4_fsblk_t block_id =
+           inode_table_start + (byte_offset_in_group / block_size);
+
+       rc = ext4_trans_block_get(fs->bdev, &ref->block, block_id);
+       if (rc != EOK) {
+               return rc;
+       }
+
+       /* Compute position of i-node in the data block */
+       uint32_t offset_in_block = byte_offset_in_group % block_size;
+       ref->inode = (struct ext4_inode *)(ref->block.data + offset_in_block);
+
+       /* We need to store the original value of index in the reference */
+       ref->index = index + 1;
+       ref->fs = fs;
+       ref->dirty = false;
+
+       if (initialized && !ext4_fs_verify_inode_csum(ref)) {
+               ext4_dbg(DEBUG_FS,
+                       DBG_WARN "Inode checksum failed."
+                       "Inode: %" PRIu32"\n",
+                       ref->index);
+       }
+
+       return EOK;
+}
+
+int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
+                         struct ext4_inode_ref *ref)
+{
+       return __ext4_fs_get_inode_ref(fs, index, ref, true);
+}
+
+int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref)
+{
+       /* Check if reference modified */
+       if (ref->dirty) {
+               /* Mark block dirty for writing changes to physical device */
+               ext4_fs_set_inode_checksum(ref);
+               ext4_trans_set_block_dirty(ref->block.buf);
+       }
+
+       /* Put back block, that contains i-node */
+       return ext4_block_set(ref->fs->bdev, &ref->block);
+}
+
+void ext4_fs_inode_blocks_init(struct ext4_fs *fs,
+                              struct ext4_inode_ref *inode_ref)
+{
+       int i;
+       struct ext4_inode *inode = inode_ref->inode;
+
+       for (i = 0; i < EXT4_INODE_BLOCKS; i++)
+               inode->blocks[i] = 0;
+
+       (void)fs;
+#if CONFIG_EXTENT_ENABLE
+       /* Initialize extents if needed */
+       if (ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) {
+               ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
+
+               /* Initialize extent root header */
+               ext4_extent_tree_init(inode_ref);
+       }
+#endif
+}
+
+uint32_t ext4_fs_correspond_inode_mode(int filetype)
+{
+       switch (filetype) {
+       case EXT4_DE_DIR:
+               return EXT4_INODE_MODE_DIRECTORY;
+       case EXT4_DE_REG_FILE:
+               return EXT4_INODE_MODE_FILE;
+       case EXT4_DE_SYMLINK:
+               return EXT4_INODE_MODE_SOFTLINK;
+       default:
+               /* FIXME: right now we only support 3 file type. */
+               ext4_assert(0);
+       }
+       return 0;
+}
+
+int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
+                       int filetype)
+{
+       /* Check if newly allocated i-node will be a directory */
+       bool is_dir;
+       uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
+
+       is_dir = (filetype == EXT4_DE_DIR);
+
+       /* Allocate inode by allocation algorithm */
+       uint32_t index;
+       int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
+       if (rc != EOK)
+               return rc;
+
+       /* Load i-node from on-disk i-node table */
+       rc = __ext4_fs_get_inode_ref(fs, index, inode_ref, false);
+       if (rc != EOK) {
+               ext4_ialloc_free_inode(fs, index, is_dir);
+               return rc;
+       }
+
+       /* Initialize i-node */
+       struct ext4_inode *inode = inode_ref->inode;
+
+       uint32_t mode;
+       if (is_dir) {
+               /*
+                * Default directory permissions to be compatible with other
+                * systems
+                * 0777 (octal) == rwxrwxrwx
+                */
+
+               mode = 0777;
+               mode |= EXT4_INODE_MODE_DIRECTORY;
+       } else {
+               /*
+                * Default file permissions to be compatible with other systems
+                * 0666 (octal) == rw-rw-rw-
+                */
+
+               mode = 0666;
+               mode |= ext4_fs_correspond_inode_mode(filetype);
+       }
+       ext4_inode_set_mode(&fs->sb, inode, mode);
+
+       ext4_inode_set_links_cnt(inode, 0);
+       ext4_inode_set_uid(inode, 0);
+       ext4_inode_set_gid(inode, 0);
+       ext4_inode_set_size(inode, 0);
+       ext4_inode_set_access_time(inode, 0);
+       ext4_inode_set_change_inode_time(inode, 0);
+       ext4_inode_set_modif_time(inode, 0);
+       ext4_inode_set_del_time(inode, 0);
+       ext4_inode_set_blocks_count(&fs->sb, inode, 0);
+       ext4_inode_set_flags(inode, 0);
+       ext4_inode_set_generation(inode, 0);
+       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
+               uint16_t off = offsetof(struct ext4_inode, extra_isize);
+               uint16_t size = sizeof(struct ext4_inode) - off;
+               ext4_inode_set_extra_isize(inode, size);
+       }
+
+       /* Reset blocks array. For symbolic link inode, just
+        * fill in blocks with 0 */
+       if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
+               for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
+                       inode->blocks[i] = 0;
+
+       } else {
+               ext4_fs_inode_blocks_init(fs, inode_ref);
+       }
+
+       inode_ref->dirty = true;
+
+       return EOK;
+}
+
+int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref)
+{
+       struct ext4_fs *fs = inode_ref->fs;
+       uint32_t offset;
+       uint32_t suboff;
+       int rc;
+#if CONFIG_EXTENT_ENABLE
+       /* For extents must be data block destroyed by other way */
+       if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) &&
+           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
+               /* Data structures are released during truncate operation... */
+               goto finish;
+       }
+#endif
+       /* Release all indirect (no data) blocks */
+
+       /* 1) Single indirect */
+       ext4_fsblk_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
+       if (fblock != 0) {
+               int rc = ext4_balloc_free_block(inode_ref, fblock);
+               if (rc != EOK)
+                       return rc;
+
+               ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
+       }
+
+       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
+       uint32_t count = block_size / sizeof(uint32_t);
+
+       struct ext4_block block;
+
+       /* 2) Double indirect */
+       fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
+       if (fblock != 0) {
+               int rc = ext4_trans_block_get(fs->bdev, &block, fblock);
+               if (rc != EOK)
+                       return rc;
+
+               ext4_fsblk_t ind_block;
+               for (offset = 0; offset < count; ++offset) {
+                       ind_block = to_le32(((uint32_t *)block.data)[offset]);
+
+                       if (ind_block == 0)
+                               continue;
+                       rc = ext4_balloc_free_block(inode_ref, ind_block);
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &block);
+                               return rc;
+                       }
+
+               }
+
+               ext4_block_set(fs->bdev, &block);
+               rc = ext4_balloc_free_block(inode_ref, fblock);
+               if (rc != EOK)
+                       return rc;
+
+               ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
+       }
+
+       /* 3) Tripple indirect */
+       struct ext4_block subblock;
+       fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
+       if (fblock == 0)
+               goto finish;
+       rc = ext4_trans_block_get(fs->bdev, &block, fblock);
+       if (rc != EOK)
+               return rc;
+
+       ext4_fsblk_t ind_block;
+       for (offset = 0; offset < count; ++offset) {
+               ind_block = to_le32(((uint32_t *)block.data)[offset]);
+
+               if (ind_block == 0)
+                       continue;
+               rc = ext4_trans_block_get(fs->bdev, &subblock,
+                               ind_block);
+               if (rc != EOK) {
+                       ext4_block_set(fs->bdev, &block);
+                       return rc;
+               }
+
+               ext4_fsblk_t ind_subblk;
+               for (suboff = 0; suboff < count; ++suboff) {
+                       ind_subblk = to_le32(((uint32_t *)subblock.data)[suboff]);
+
+                       if (ind_subblk == 0)
+                               continue;
+                       rc = ext4_balloc_free_block(inode_ref, ind_subblk);
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &subblock);
+                               ext4_block_set(fs->bdev, &block);
+                               return rc;
+                       }
+
+               }
+
+               ext4_block_set(fs->bdev, &subblock);
+
+               rc = ext4_balloc_free_block(inode_ref,
+                               ind_block);
+               if (rc != EOK) {
+                       ext4_block_set(fs->bdev, &block);
+                       return rc;
+               }
+
+       }
+
+       ext4_block_set(fs->bdev, &block);
+       rc = ext4_balloc_free_block(inode_ref, fblock);
+       if (rc != EOK)
+               return rc;
+
+       ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
+finish:
+       /* Mark inode dirty for writing to the physical device */
+       inode_ref->dirty = true;
+
+       /* Free block with extended attributes if present */
+       ext4_fsblk_t xattr_block =
+           ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
+       if (xattr_block) {
+               int rc = ext4_balloc_free_block(inode_ref, xattr_block);
+               if (rc != EOK)
+                       return rc;
+
+               ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0);
+       }
+
+       /* Free inode by allocator */
+       if (ext4_inode_is_type(&fs->sb, inode_ref->inode,
+                              EXT4_INODE_MODE_DIRECTORY))
+               rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
+       else
+               rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
+
+       return rc;
+}
+
+
+/**@brief Release data block from i-node
+ * @param inode_ref I-node to release block from
+ * @param iblock    Logical block to be released
+ * @return Error code
+ */
+static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
+                               uint32_t iblock)
+{
+       ext4_fsblk_t fblock;
+
+       struct ext4_fs *fs = inode_ref->fs;
+
+       /* Extents are handled otherwise = there is not support in this function
+        */
+       ext4_assert(!(
+           ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS) &&
+           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
+
+       struct ext4_inode *inode = inode_ref->inode;
+
+       /* Handle simple case when we are dealing with direct reference */
+       if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
+               fblock = ext4_inode_get_direct_block(inode, iblock);
+
+               /* Sparse file */
+               if (fblock == 0)
+                       return EOK;
+
+               ext4_inode_set_direct_block(inode, iblock, 0);
+               return ext4_balloc_free_block(inode_ref, fblock);
+       }
+
+       /* Determine the indirection level needed to get the desired block */
+       unsigned int level = 0;
+       unsigned int i;
+       for (i = 1; i < 4; i++) {
+               if (iblock < fs->inode_block_limits[i]) {
+                       level = i;
+                       break;
+               }
+       }
+
+       if (level == 0)
+               return EIO;
+
+       /* Compute offsets for the topmost level */
+       uint32_t block_offset_in_level =
+           iblock - fs->inode_block_limits[level - 1];
+       ext4_fsblk_t current_block =
+           ext4_inode_get_indirect_block(inode, level - 1);
+       uint32_t offset_in_block =
+           block_offset_in_level / fs->inode_blocks_per_level[level - 1];
+
+       /*
+        * Navigate through other levels, until we find the block number
+        * or find null reference meaning we are dealing with sparse file
+        */
+       struct ext4_block block;
+
+       while (level > 0) {
+
+               /* Sparse check */
+               if (current_block == 0)
+                       return EOK;
+
+               int rc = ext4_trans_block_get(fs->bdev, &block, current_block);
+               if (rc != EOK)
+                       return rc;
+
+               current_block =
+                   to_le32(((uint32_t *)block.data)[offset_in_block]);
+
+               /* Set zero if physical data block address found */
+               if (level == 1) {
+                       ((uint32_t *)block.data)[offset_in_block] = to_le32(0);
+                       ext4_trans_set_block_dirty(block.buf);
+               }
+
+               rc = ext4_block_set(fs->bdev, &block);
+               if (rc != EOK)
+                       return rc;
+
+               level--;
+
+               /*
+                * If we are on the last level, break here as
+                * there is no next level to visit
+                */
+               if (level == 0)
+                       break;
+
+               /* Visit the next level */
+               block_offset_in_level %= fs->inode_blocks_per_level[level];
+               offset_in_block = block_offset_in_level /
+                                 fs->inode_blocks_per_level[level - 1];
+       }
+
+       fblock = current_block;
+       if (fblock == 0)
+               return EOK;
+
+       /* Physical block is not referenced, it can be released */
+       return ext4_balloc_free_block(inode_ref, fblock);
+}
+
+int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
+{
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       uint32_t i;
+       int r;
+
+       /* Check flags, if i-node can be truncated */
+       if (!ext4_inode_can_truncate(sb, inode_ref->inode))
+               return EINVAL;
+
+       /* If sizes are equal, nothing has to be done. */
+       uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
+       if (old_size == new_size)
+               return EOK;
+
+       /* It's not supported to make the larger file by truncate operation */
+       if (old_size < new_size)
+               return EINVAL;
+
+       bool v;
+       v = ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK);
+       if (v && old_size < sizeof(inode_ref->inode->blocks) &&
+           !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
+               char *content = (char *)inode_ref->inode->blocks + new_size;
+               memset(content, 0,
+                      sizeof(inode_ref->inode->blocks) - (uint32_t)new_size);
+               ext4_inode_set_size(inode_ref->inode, new_size);
+               inode_ref->dirty = true;
+
+               return EOK;
+       }
+
+       /* Compute how many blocks will be released */
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint32_t new_blocks_cnt = (uint32_t)((new_size + block_size - 1) / block_size);
+       uint32_t old_blocks_cnt = (uint32_t)((old_size + block_size - 1) / block_size);
+       uint32_t diff_blocks_cnt = old_blocks_cnt - new_blocks_cnt;
+#if CONFIG_EXTENT_ENABLE
+       if ((ext4_sb_feature_incom(sb, EXT4_FINCOM_EXTENTS)) &&
+           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
+
+               /* Extents require special operation */
+               if (diff_blocks_cnt) {
+                       r = ext4_extent_remove_space(inode_ref, new_blocks_cnt,
+                                                    EXT_MAX_BLOCKS);
+                       if (r != EOK)
+                               return r;
+
+               }
+       } else
+#endif
+       {
+               /* Release data blocks from the end of file */
+
+               /* Starting from 1 because of logical blocks are numbered from 0
+                */
+               for (i = 0; i < diff_blocks_cnt; ++i) {
+                       r = ext4_fs_release_inode_block(inode_ref,
+                                                       new_blocks_cnt + i);
+                       if (r != EOK)
+                               return r;
+               }
+       }
+
+       /* Update i-node */
+       ext4_inode_set_size(inode_ref->inode, new_size);
+       inode_ref->dirty = true;
+
+       return EOK;
+}
+
+/**@brief Compute 'goal' for inode index
+ * @param inode_ref Reference to inode, to allocate block for
+ * @return goal
+ */
+ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref)
+{
+       uint32_t grp_inodes = ext4_get32(&inode_ref->fs->sb, inodes_per_group);
+       return (inode_ref->index - 1) / grp_inodes;
+}
+
+/**@brief Compute 'goal' for allocation algorithm (For blockmap).
+ * @param inode_ref Reference to inode, to allocate block for
+ * @param goal
+ * @return error code
+ */
+int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
+                              ext4_fsblk_t *goal)
+{
+       int r;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       *goal = 0;
+
+       uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+       uint32_t iblock_cnt = (uint32_t)(inode_size / block_size);
+
+       if (inode_size % block_size != 0)
+               iblock_cnt++;
+
+       /* If inode has some blocks, get last block address + 1 */
+       if (iblock_cnt > 0) {
+               r = ext4_fs_get_inode_dblk_idx(inode_ref, iblock_cnt - 1,
+                                              goal, false);
+               if (r != EOK)
+                       return r;
+
+               if (*goal != 0) {
+                       (*goal)++;
+                       return r;
+               }
+
+               /* If goal == 0, sparse file -> continue */
+       }
+
+       /* Identify block group of inode */
+
+       uint32_t inodes_per_bg = ext4_get32(sb, inodes_per_group);
+       uint32_t block_group = (inode_ref->index - 1) / inodes_per_bg;
+       block_size = ext4_sb_get_block_size(sb);
+
+       /* Load block group reference */
+       struct ext4_block_group_ref bg_ref;
+       r = ext4_fs_get_block_group_ref(inode_ref->fs, block_group, &bg_ref);
+       if (r != EOK)
+               return r;
+
+       struct ext4_bgroup *bg = bg_ref.block_group;
+
+       /* Compute indexes */
+       uint32_t bg_count = ext4_block_group_cnt(sb);
+       ext4_fsblk_t itab_first_block = ext4_bg_get_inode_table_first_block(bg, sb);
+       uint16_t itab_item_size = ext4_get16(sb, inode_size);
+       uint32_t itab_bytes;
+
+       /* Check for last block group */
+       if (block_group < bg_count - 1) {
+               itab_bytes = inodes_per_bg * itab_item_size;
+       } else {
+               /* Last block group could be smaller */
+               uint32_t inodes_cnt = ext4_get32(sb, inodes_count);
+
+               itab_bytes = (inodes_cnt - ((bg_count - 1) * inodes_per_bg));
+               itab_bytes *= itab_item_size;
+       }
+
+       ext4_fsblk_t inode_table_blocks = itab_bytes / block_size;
+
+       if (itab_bytes % block_size)
+               inode_table_blocks++;
+
+       *goal = itab_first_block + inode_table_blocks;
+
+       return ext4_fs_put_block_group_ref(&bg_ref);
+}
+
+static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref,
+                                      uint64_t iblock, ext4_fsblk_t *fblock,
+                                      bool extent_create,
+                                      bool support_unwritten __unused)
+{
+       struct ext4_fs *fs = inode_ref->fs;
+
+       /* For empty file is situation simple */
+       if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) {
+               *fblock = 0;
+               return EOK;
+       }
+
+       ext4_fsblk_t current_block;
+
+       (void)extent_create;
+#if CONFIG_EXTENT_ENABLE
+       /* Handle i-node using extents */
+       if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) &&
+           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
+
+               ext4_fsblk_t current_fsblk;
+               int rc = ext4_extent_get_blocks(inode_ref, iblock, 1,
+                               &current_fsblk, extent_create, NULL);
+               if (rc != EOK)
+                       return rc;
+
+               current_block = current_fsblk;
+               *fblock = current_block;
+
+               ext4_assert(*fblock || support_unwritten);
+               return EOK;
+       }
+#endif
+
+       struct ext4_inode *inode = inode_ref->inode;
+
+       /* Direct block are read directly from array in i-node structure */
+       if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
+               current_block =
+                   ext4_inode_get_direct_block(inode, (uint32_t)iblock);
+               *fblock = current_block;
+               return EOK;
+       }
+
+       /* Determine indirection level of the target block */
+       unsigned int l = 0;
+       unsigned int i;
+       for (i = 1; i < 4; i++) {
+               if (iblock < fs->inode_block_limits[i]) {
+                       l = i;
+                       break;
+               }
+       }
+
+       if (l == 0)
+               return EIO;
+
+       /* Compute offsets for the topmost level */
+       uint32_t blk_off_in_lvl =  iblock - fs->inode_block_limits[l - 1];
+       current_block = ext4_inode_get_indirect_block(inode, l - 1);
+       uint32_t off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+
+       /* Sparse file */
+       if (current_block == 0) {
+               *fblock = 0;
+               return EOK;
+       }
+
+       struct ext4_block block;
+
+       /*
+        * Navigate through other levels, until we find the block number
+        * or find null reference meaning we are dealing with sparse file
+        */
+       while (l > 0) {
+               /* Load indirect block */
+               int rc = ext4_trans_block_get(fs->bdev, &block, current_block);
+               if (rc != EOK)
+                       return rc;
+
+               /* Read block address from indirect block */
+               current_block =
+                   to_le32(((uint32_t *)block.data)[off_in_blk]);
+
+               /* Put back indirect block untouched */
+               rc = ext4_block_set(fs->bdev, &block);
+               if (rc != EOK)
+                       return rc;
+
+               /* Check for sparse file */
+               if (current_block == 0) {
+                       *fblock = 0;
+                       return EOK;
+               }
+
+               /* Jump to the next level */
+               l--;
+
+               /* Termination condition - we have address of data block loaded
+                */
+               if (l == 0)
+                       break;
+
+               /* Visit the next level */
+               blk_off_in_lvl %= fs->inode_blocks_per_level[l];
+               off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+       }
+
+       *fblock = current_block;
+
+       return EOK;
+}
+
+
+int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
+                              uint64_t iblock, ext4_fsblk_t *fblock,
+                              bool support_unwritten)
+{
+       return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock,
+                                                  false, support_unwritten);
+}
+
+int ext4_fs_init_inode_dblk_idx(struct ext4_inode_ref *inode_ref,
+                               uint64_t iblock, ext4_fsblk_t *fblock)
+{
+       return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock,
+                                                  true, true);
+}
+
+static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
+                                      uint32_t iblock, ext4_fsblk_t fblock)
+{
+       struct ext4_fs *fs = inode_ref->fs;
+
+#if CONFIG_EXTENT_ENABLE
+       /* Handle inode using extents */
+       if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) &&
+           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
+               /* Not reachable */
+               return ENOTSUP;
+       }
+#endif
+
+       /* Handle simple case when we are dealing with direct reference */
+       if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
+               ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock,
+                                           (uint32_t)fblock);
+               inode_ref->dirty = true;
+
+               return EOK;
+       }
+
+       /* Determine the indirection level needed to get the desired block */
+       unsigned int l = 0;
+       unsigned int i;
+       for (i = 1; i < 4; i++) {
+               if (iblock < fs->inode_block_limits[i]) {
+                       l = i;
+                       break;
+               }
+       }
+
+       if (l == 0)
+               return EIO;
+
+       uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
+
+       /* Compute offsets for the topmost level */
+       uint32_t blk_off_in_lvl =  iblock - fs->inode_block_limits[l - 1];
+       ext4_fsblk_t current_block =
+                       ext4_inode_get_indirect_block(inode_ref->inode, l - 1);
+       uint32_t off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+
+       ext4_fsblk_t new_blk;
+
+       struct ext4_block block;
+       struct ext4_block new_block;
+
+       /* Is needed to allocate indirect block on the i-node level */
+       if (current_block == 0) {
+               /* Allocate new indirect block */
+               ext4_fsblk_t goal;
+               int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+               if (rc != EOK)
+                       return rc;
+
+               rc = ext4_balloc_alloc_block(inode_ref, goal, &new_blk);
+               if (rc != EOK)
+                       return rc;
+
+               /* Update i-node */
+               ext4_inode_set_indirect_block(inode_ref->inode, l - 1,
+                               (uint32_t)new_blk);
+               inode_ref->dirty = true;
+
+               /* Load newly allocated block */
+               rc = ext4_trans_block_get_noread(fs->bdev, &new_block, new_blk);
+               if (rc != EOK) {
+                       ext4_balloc_free_block(inode_ref, new_blk);
+                       return rc;
+               }
+
+               /* Initialize new block */
+               memset(new_block.data, 0, block_size);
+               ext4_trans_set_block_dirty(new_block.buf);
+
+               /* Put back the allocated block */
+               rc = ext4_block_set(fs->bdev, &new_block);
+               if (rc != EOK)
+                       return rc;
+
+               current_block = new_blk;
+       }
+
+       /*
+        * Navigate through other levels, until we find the block number
+        * or find null reference meaning we are dealing with sparse file
+        */
+       while (l > 0) {
+               int rc = ext4_trans_block_get(fs->bdev, &block, current_block);
+               if (rc != EOK)
+                       return rc;
+
+               current_block = to_le32(((uint32_t *)block.data)[off_in_blk]);
+               if ((l > 1) && (current_block == 0)) {
+                       ext4_fsblk_t goal;
+                       rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &block);
+                               return rc;
+                       }
+
+                       /* Allocate new block */
+                       rc =
+                           ext4_balloc_alloc_block(inode_ref, goal, &new_blk);
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &block);
+                               return rc;
+                       }
+
+                       /* Load newly allocated block */
+                       rc = ext4_trans_block_get_noread(fs->bdev, &new_block,
+                                           new_blk);
+
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &block);
+                               return rc;
+                       }
+
+                       /* Initialize allocated block */
+                       memset(new_block.data, 0, block_size);
+                       ext4_trans_set_block_dirty(new_block.buf);
+
+                       rc = ext4_block_set(fs->bdev, &new_block);
+                       if (rc != EOK) {
+                               ext4_block_set(fs->bdev, &block);
+                               return rc;
+                       }
+
+                       /* Write block address to the parent */
+                       uint32_t * p = (uint32_t * )block.data;
+                       p[off_in_blk] = to_le32((uint32_t)new_blk);
+                       ext4_trans_set_block_dirty(block.buf);
+                       current_block = new_blk;
+               }
+
+               /* Will be finished, write the fblock address */
+               if (l == 1) {
+                       uint32_t * p = (uint32_t * )block.data;
+                       p[off_in_blk] = to_le32((uint32_t)fblock);
+                       ext4_trans_set_block_dirty(block.buf);
+               }
+
+               rc = ext4_block_set(fs->bdev, &block);
+               if (rc != EOK)
+                       return rc;
+
+               l--;
+
+               /*
+                * If we are on the last level, break here as
+                * there is no next level to visit
+                */
+               if (l == 0)
+                       break;
+
+               /* Visit the next level */
+               blk_off_in_lvl %= fs->inode_blocks_per_level[l];
+               off_in_blk = blk_off_in_lvl / fs->inode_blocks_per_level[l - 1];
+       }
+
+       return EOK;
+}
+
+
+int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref,
+                             ext4_fsblk_t *fblock, uint32_t *iblock)
+{
+#if CONFIG_EXTENT_ENABLE
+       /* Handle extents separately */
+       if ((ext4_sb_feature_incom(&inode_ref->fs->sb, EXT4_FINCOM_EXTENTS)) &&
+           (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
+               int rc;
+               ext4_fsblk_t current_fsblk;
+               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);
+               *iblock = (uint32_t)((inode_size + block_size - 1) / block_size);
+
+               rc = ext4_extent_get_blocks(inode_ref, *iblock, 1,
+                                               &current_fsblk, true, NULL);
+
+               *fblock = current_fsblk;
+               ext4_assert(*fblock);
+
+               ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
+               inode_ref->dirty = true;
+
+
+               return rc;
+       }
+#endif
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       /* Compute next block index and allocate data block */
+       uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
+       uint32_t block_size = ext4_sb_get_block_size(sb);
+
+       /* Align size i-node size */
+       if ((inode_size % block_size) != 0)
+               inode_size += block_size - (inode_size % block_size);
+
+       /* Logical blocks are numbered from 0 */
+       uint32_t new_block_idx = (uint32_t)(inode_size / block_size);
+
+       /* Allocate new physical block */
+       ext4_fsblk_t goal, phys_block;
+       int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
+       if (rc != EOK)
+               return rc;
+
+       rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block);
+       if (rc != EOK)
+               return rc;
+
+       /* Add physical block address to the i-node */
+       rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx,
+                                               phys_block);
+       if (rc != EOK) {
+               ext4_balloc_free_block(inode_ref, phys_block);
+               return rc;
+       }
+
+       /* Update i-node */
+       ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
+       inode_ref->dirty = true;
+
+       *fblock = phys_block;
+       *iblock = new_block_idx;
+
+       return EOK;
+}
+
+void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
+{
+       uint16_t link;
+       bool is_dx;
+       link = ext4_inode_get_links_cnt(inode_ref->inode);
+       link++;
+       ext4_inode_set_links_cnt(inode_ref->inode, link);
+
+       is_dx = ext4_sb_feature_com(&inode_ref->fs->sb, EXT4_FCOM_DIR_INDEX) &&
+               ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX);
+
+       if (is_dx && link > 1) {
+               if (link >= EXT4_LINK_MAX || link == 2) {
+                       ext4_inode_set_links_cnt(inode_ref->inode, 1);
+
+                       uint32_t v;
+                       v = ext4_get32(&inode_ref->fs->sb, features_read_only);
+                       v |= EXT4_FRO_COM_DIR_NLINK;
+                       ext4_set32(&inode_ref->fs->sb, features_read_only, v);
+               }
+       }
+}
+
+void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
+{
+       uint16_t links = ext4_inode_get_links_cnt(inode_ref->inode);
+       if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
+                               EXT4_INODE_MODE_DIRECTORY)) {
+               if (links > 0)
+                       ext4_inode_set_links_cnt(inode_ref->inode, links - 1);
+               return;
+       }
+
+       if (links > 2)
+               ext4_inode_set_links_cnt(inode_ref->inode, links - 1);
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_hash.c b/src/ext4_hash.c
new file mode 100644 (file)
index 0000000..4f97eac
--- /dev/null
@@ -0,0 +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;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_ialloc.c b/src/ext4_ialloc.c
new file mode 100644 (file)
index 0000000..bbdcd4d
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * 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_crc32.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_inode_to_bgidx(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_bgidx_to_inode(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;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_ialloc_bitmap_csum(struct ext4_sblock *sb,        void *bitmap)
+{
+       uint32_t csum = 0;
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t inodes_per_group =
+                       ext4_get32(sb, inodes_per_group);
+
+               /* First calculate crc32 checksum against fs uuid */
+               csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
+               /* Then calculate crc32 checksum against inode bitmap */
+               csum = ext4_crc32c(csum, bitmap, (inodes_per_group + 7) / 8);
+       }
+       return csum;
+}
+#else
+#define ext4_ialloc_bitmap_csum(...) 0
+#endif
+
+void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
+                                void *bitmap __unused)
+{
+       int desc_size = ext4_sb_get_desc_size(sb);
+       uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap);
+       uint16_t lo_csum = to_le16(csum & 0xFFFF),
+                hi_csum = to_le16(csum >> 16);
+
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return;
+
+       /* See if we need to assign a 32bit checksum */
+       bg->inode_bitmap_csum_lo = lo_csum;
+       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               bg->inode_bitmap_csum_hi = hi_csum;
+
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+ext4_ialloc_verify_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
+                              void *bitmap __unused)
+{
+
+       int desc_size = ext4_sb_get_desc_size(sb);
+       uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap);
+       uint16_t lo_csum = to_le16(csum & 0xFFFF),
+                hi_csum = to_le16(csum >> 16);
+
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return true;
+
+       if (bg->inode_bitmap_csum_lo != lo_csum)
+               return false;
+
+       if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               if (bg->inode_bitmap_csum_hi != hi_csum)
+                       return false;
+
+       return true;
+}
+#else
+#define ext4_ialloc_verify_bitmap_csum(...) true
+#endif
+
+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;
+
+       struct ext4_bgroup *bg = bg_ref.block_group;
+
+       /* Load i-node bitmap */
+       ext4_fsblk_t bitmap_block_addr =
+           ext4_bg_get_inode_bitmap(bg, sb);
+
+       struct ext4_block b;
+       rc = ext4_trans_block_get(fs->bdev, &b, bitmap_block_addr);
+       if (rc != EOK)
+               return rc;
+
+       if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) {
+               ext4_dbg(DEBUG_IALLOC,
+                       DBG_WARN "Bitmap checksum failed."
+                       "Group: %" PRIu32"\n",
+                       bg_ref.index);
+       }
+
+       /* Free i-node in the bitmap */
+       uint32_t index_in_group = ext4_ialloc_inode_to_bgidx(sb, index);
+       ext4_bmap_bit_clr(b.data, index_in_group);
+       ext4_ialloc_set_bitmap_csum(sb, bg, b.data);
+       ext4_trans_set_block_dirty(b.buf);
+
+       /* Put back the block with bitmap */
+       rc = ext4_block_set(fs->bdev, &b);
+       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, sb);
+               bg_used_dirs--;
+               ext4_bg_set_used_dirs_count(bg, sb, bg_used_dirs);
+       }
+
+       /* Update block group free inodes count */
+       uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
+       free_inodes++;
+       ext4_bg_set_free_inodes_count(bg, 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 *idx, 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 */
+                       ext4_fsblk_t bmp_blk_add = ext4_bg_get_inode_bitmap(bg, sb);
+
+                       struct ext4_block b;
+                       rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_add);
+                       if (rc != EOK) {
+                               ext4_fs_put_block_group_ref(&bg_ref);
+                               return rc;
+                       }
+
+                       if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) {
+                               ext4_dbg(DEBUG_IALLOC,
+                                       DBG_WARN "Bitmap checksum failed."
+                                       "Group: %" PRIu32"\n",
+                                       bg_ref.index);
+                       }
+
+                       /* Try to allocate i-node in the bitmap */
+                       uint32_t inodes_in_bg;
+                       uint32_t idx_in_bg;
+
+                       inodes_in_bg = ext4_inodes_in_group_cnt(sb, bgid);
+                       rc = ext4_bmap_bit_find_clr(b.data, 0, inodes_in_bg,
+                                                   &idx_in_bg);
+                       /* Block group has not any free i-node */
+                       if (rc == ENOSPC) {
+                               rc = ext4_block_set(fs->bdev, &b);
+                               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(b.data, idx_in_bg);
+
+                       /* Free i-node found, save the bitmap */
+                       ext4_ialloc_set_bitmap_csum(sb,bg,
+                                                   b.data);
+                       ext4_trans_set_block_dirty(b.buf);
+
+                       ext4_block_set(fs->bdev, &b);
+                       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 */
+                       uint32_t unused =
+                           ext4_bg_get_itable_unused(bg, sb);
+
+                       uint32_t free = inodes_in_bg - unused;
+
+                       if (idx_in_bg >= free) {
+                               unused = inodes_in_bg - (idx_in_bg + 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 */
+                       *idx = ext4_ialloc_bgidx_to_inode(sb, idx_in_bg, 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;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_inode.c b/src/ext4_inode.c
new file mode 100644 (file)
index 0000000..9b6eb87
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * 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_csum(struct ext4_sblock *sb, struct ext4_inode *inode)
+{
+       uint16_t inode_size = ext4_get16(sb, inode_size);
+       uint32_t v = to_le16(inode->osd2.linux2.checksum_lo);
+
+       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE)
+               v |= ((uint32_t)to_le16(inode->checksum_hi)) << 16;
+
+       return v;
+}
+
+void ext4_inode_set_csum(struct ext4_sblock *sb, struct ext4_inode *inode,
+                       uint32_t checksum)
+{
+       uint16_t inode_size = ext4_get16(sb, inode_size);
+       inode->osd2.linux2.checksum_lo =
+               to_le16((checksum << 16) >> 16);
+
+       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE)
+               inode->checksum_hi = to_le16(checksum >> 16);
+
+}
+
+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_modif_time(struct ext4_inode *inode)
+{
+       return to_le32(inode->modification_time);
+}
+
+void ext4_inode_set_modif_time(struct ext4_inode *inode, uint32_t time)
+{
+       inode->modification_time = to_le32(time);
+}
+
+uint32_t ext4_inode_get_del_time(struct ext4_inode *inode)
+{
+       return to_le32(inode->deletion_time);
+}
+
+void ext4_inode_set_del_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_cnt(struct ext4_inode *inode)
+{
+       return to_le16(inode->links_count);
+}
+void ext4_inode_set_links_cnt(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 cnt = to_le32(inode->blocks_count_lo);
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE)) {
+
+               /* 48-bit field */
+               cnt |= (uint64_t)to_le16(inode->osd2.linux2.blocks_high) << 32;
+
+               if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) {
+
+                       uint32_t block_count = ext4_sb_get_block_size(sb);
+                       uint32_t b = ext4_inode_block_bits_count(block_count);
+                       return cnt << (b - 9);
+               }
+       }
+
+       return cnt;
+}
+
+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((uint32_t)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_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE))
+               return EINVAL;
+
+       /* 48-bit maximum */
+       max = 0;
+       max = ~max >> 16;
+
+       if (count <= max) {
+               inode->blocks_count_lo = to_le32((uint32_t)count);
+               inode->osd2.linux2.blocks_high = to_le16((uint16_t)(count >> 32));
+               ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
+       } else {
+               uint32_t block_count = ext4_sb_get_block_size(sb);
+               uint32_t block_bits =ext4_inode_block_bits_count(block_count);
+
+               ext4_inode_set_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
+               count = count >> (block_bits - 9);
+               inode->blocks_count_lo = to_le32((uint32_t)count);
+               inode->osd2.linux2.blocks_high = to_le16((uint16_t)(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);
+}
+
+uint16_t ext4_inode_get_extra_isize(struct ext4_inode *inode)
+{
+       return to_le16(inode->extra_isize);
+}
+
+void ext4_inode_set_extra_isize(struct ext4_inode *inode, uint16_t size)
+{
+       inode->extra_isize = to_le16(size);
+}
+
+uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode,
+                                struct ext4_sblock *sb)
+{
+       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)
+{
+       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((uint16_t)(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);
+}
+
+uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode)
+{
+       return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK);
+}
+
+bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
+                       uint32_t type)
+{
+       return ext4_inode_type(sb, inode) == 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)) ||
+           (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_SOFTLINK)))
+               return true;
+
+       return false;
+}
+
+struct ext4_extent_header *
+ext4_inode_get_extent_header(struct ext4_inode *inode)
+{
+       return (struct ext4_extent_header *)inode->blocks;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_journal.c b/src/ext4_journal.c
new file mode 100644 (file)
index 0000000..73a84bf
--- /dev/null
@@ -0,0 +1,2158 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@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_journal.c
+ * @brief Journal handle functions
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_fs.h"
+#include "ext4_super.h"
+#include "ext4_journal.h"
+#include "ext4_errno.h"
+#include "ext4_blockdev.h"
+#include "ext4_crc32.h"
+#include "ext4_debug.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/**@brief  Revoke entry during journal replay.*/
+struct revoke_entry {
+       /**@brief  Block number not to be replayed.*/
+       ext4_fsblk_t block;
+
+       /**@brief  For any transaction id smaller
+        *         than trans_id, records of @block
+        *         in those transactions should not
+        *         be replayed.*/
+       uint32_t trans_id;
+
+       /**@brief  Revoke tree node.*/
+       RB_ENTRY(revoke_entry) revoke_node;
+};
+
+/**@brief  Valid journal replay information.*/
+struct recover_info {
+       /**@brief  Starting transaction id.*/
+       uint32_t start_trans_id;
+
+       /**@brief  Ending transaction id.*/
+       uint32_t last_trans_id;
+
+       /**@brief  Used as internal argument.*/
+       uint32_t this_trans_id;
+
+       /**@brief  No of transactions went through.*/
+       uint32_t trans_cnt;
+
+       /**@brief  RB-Tree storing revoke entries.*/
+       RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
+};
+
+/**@brief  Journal replay internal arguments.*/
+struct replay_arg {
+       /**@brief  Journal replay information.*/
+       struct recover_info *info;
+
+       /**@brief  Current block we are on.*/
+       uint32_t *this_block;
+
+       /**@brief  Current trans_id we are on.*/
+       uint32_t this_trans_id;
+};
+
+static int
+jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
+{
+       if (a->block > b->block)
+               return 1;
+       else if (a->block < b->block)
+               return -1;
+       return 0;
+}
+
+static int
+jbd_block_rec_cmp(struct jbd_block_rec *a, struct jbd_block_rec *b)
+{
+       if (a->lba > b->lba)
+               return 1;
+       else if (a->lba < b->lba)
+               return -1;
+       return 0;
+}
+
+RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
+                    jbd_revoke_entry_cmp, static inline)
+RB_GENERATE_INTERNAL(jbd_block, jbd_block_rec, block_rec_node,
+                    jbd_block_rec_cmp, static inline)
+
+#define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
+#define jbd_free_revoke_entry(addr) free(addr)
+
+static int jbd_has_csum(struct jbd_sb *jbd_sb)
+{
+       if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V2))
+               return 2;
+
+       if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V3))
+               return 3;
+
+       return 0;
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_sb_csum(struct jbd_sb *jbd_sb)
+{
+       uint32_t checksum = 0;
+
+       if (jbd_has_csum(jbd_sb)) {
+               uint32_t orig_checksum = jbd_sb->checksum;
+               jbd_set32(jbd_sb, checksum, 0);
+               /* Calculate crc32c checksum against tho whole superblock */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_sb,
+                               JBD_SUPERBLOCK_SIZE);
+               jbd_sb->checksum = orig_checksum;
+       }
+       return checksum;
+}
+#else
+#define jbd_sb_csum(...) 0
+#endif
+
+static void jbd_sb_csum_set(struct jbd_sb *jbd_sb)
+{
+       if (!jbd_has_csum(jbd_sb))
+               return;
+
+       jbd_set32(jbd_sb, checksum, jbd_sb_csum(jbd_sb));
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+jbd_verify_sb_csum(struct jbd_sb *jbd_sb)
+{
+       if (!jbd_has_csum(jbd_sb))
+               return true;
+
+       return jbd_sb_csum(jbd_sb) == jbd_get32(jbd_sb, checksum);
+}
+#else
+#define jbd_verify_sb_csum(...) true
+#endif
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_meta_csum(struct jbd_fs *jbd_fs,
+                             struct jbd_bhdr *bhdr)
+{
+       uint32_t checksum = 0;
+
+       if (jbd_has_csum(&jbd_fs->sb)) {
+               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+               struct jbd_block_tail *tail =
+                       (struct jbd_block_tail *)((char *)bhdr + block_size -
+                               sizeof(struct jbd_block_tail));
+               uint32_t orig_checksum = tail->checksum;
+               tail->checksum = 0;
+
+               /* First calculate crc32c checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
+                                      sizeof(jbd_fs->sb.uuid));
+               /* Calculate crc32c checksum against tho whole block */
+               checksum = ext4_crc32c(checksum, bhdr,
+                               block_size);
+               tail->checksum = orig_checksum;
+       }
+       return checksum;
+}
+#else
+#define jbd_meta_csum(...) 0
+#endif
+
+static void jbd_meta_csum_set(struct jbd_fs *jbd_fs,
+                             struct jbd_bhdr *bhdr)
+{
+       uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+       struct jbd_block_tail *tail = (struct jbd_block_tail *)
+                               ((char *)bhdr + block_size -
+                               sizeof(struct jbd_block_tail));
+       if (!jbd_has_csum(&jbd_fs->sb))
+               return;
+
+       tail->checksum = to_be32(jbd_meta_csum(jbd_fs, bhdr));
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool
+jbd_verify_meta_csum(struct jbd_fs *jbd_fs,
+                    struct jbd_bhdr *bhdr)
+{
+       uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+       struct jbd_block_tail *tail = (struct jbd_block_tail *)
+                               ((char *)bhdr + block_size -
+                               sizeof(struct jbd_block_tail));
+       if (!jbd_has_csum(&jbd_fs->sb))
+               return true;
+
+       return jbd_meta_csum(jbd_fs, bhdr) == to_be32(tail->checksum);
+}
+#else
+#define jbd_verify_meta_csum(...) true
+#endif
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t jbd_commit_csum(struct jbd_fs *jbd_fs,
+                             struct jbd_commit_header *header)
+{
+       uint32_t checksum = 0;
+
+       if (jbd_has_csum(&jbd_fs->sb)) {
+               uint32_t orig_checksum_type = header->chksum_type,
+                        orig_checksum_size = header->chksum_size,
+                        orig_checksum = header->chksum[0];
+               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+               header->chksum_type = 0;
+               header->chksum_size = 0;
+               header->chksum[0] = 0;
+
+               /* First calculate crc32c checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
+                                      sizeof(jbd_fs->sb.uuid));
+               /* Calculate crc32c checksum against tho whole block */
+               checksum = ext4_crc32c(checksum, header,
+                               block_size);
+
+               header->chksum_type = orig_checksum_type;
+               header->chksum_size = orig_checksum_size;
+               header->chksum[0] = orig_checksum;
+       }
+       return checksum;
+}
+#else
+#define jbd_commit_csum(...) 0
+#endif
+
+static void jbd_commit_csum_set(struct jbd_fs *jbd_fs,
+                             struct jbd_commit_header *header)
+{
+       if (!jbd_has_csum(&jbd_fs->sb))
+               return;
+
+       header->chksum_type = 0;
+       header->chksum_size = 0;
+       header->chksum[0] = jbd_commit_csum(jbd_fs, header);
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static bool jbd_verify_commit_csum(struct jbd_fs *jbd_fs,
+                                  struct jbd_commit_header *header)
+{
+       if (!jbd_has_csum(&jbd_fs->sb))
+               return true;
+
+       return header->chksum[0] == to_be32(jbd_commit_csum(jbd_fs,
+                                           header));
+}
+#else
+#define jbd_verify_commit_csum(...) true
+#endif
+
+#if CONFIG_META_CSUM_ENABLE
+/*
+ * NOTE: We only make use of @csum parameter when
+ *       JBD_FEATURE_COMPAT_CHECKSUM is enabled.
+ */
+static uint32_t jbd_block_csum(struct jbd_fs *jbd_fs, const void *buf,
+                              uint32_t csum,
+                              uint32_t sequence)
+{
+       uint32_t checksum = 0;
+
+       if (jbd_has_csum(&jbd_fs->sb)) {
+               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+               /* First calculate crc32c checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid,
+                                      sizeof(jbd_fs->sb.uuid));
+               /* Then calculate crc32c checksum against sequence no. */
+               checksum = ext4_crc32c(checksum, &sequence,
+                               sizeof(uint32_t));
+               /* Calculate crc32c checksum against tho whole block */
+               checksum = ext4_crc32c(checksum, buf,
+                               block_size);
+       } else if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_COMPAT_CHECKSUM)) {
+               uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize);
+               /* Calculate crc32c checksum against tho whole block */
+               checksum = ext4_crc32(csum, buf,
+                               block_size);
+       }
+       return checksum;
+}
+#else
+#define jbd_block_csum(...) 0
+#endif
+
+static void jbd_block_tag_csum_set(struct jbd_fs *jbd_fs, void *__tag,
+                                  uint32_t checksum)
+{
+       int ver = jbd_has_csum(&jbd_fs->sb);
+       if (!ver)
+               return;
+
+       if (ver == 2) {
+               struct jbd_block_tag *tag = __tag;
+               tag->checksum = (uint16_t)to_be32(checksum);
+       } else {
+               struct jbd_block_tag3 *tag = __tag;
+               tag->checksum = to_be32(checksum);
+       }
+}
+
+/**@brief  Write jbd superblock to disk.
+ * @param  jbd_fs jbd filesystem
+ * @param  s jbd superblock
+ * @return standard error code*/
+static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
+{
+       int rc;
+       struct ext4_fs *fs = jbd_fs->inode_ref.fs;
+       uint64_t offset;
+       ext4_fsblk_t fblock;
+       rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
+       if (rc != EOK)
+               return rc;
+
+       jbd_sb_csum_set(s);
+       offset = fblock * ext4_sb_get_block_size(&fs->sb);
+       return ext4_block_writebytes(fs->bdev, offset, s,
+                                    EXT4_SUPERBLOCK_SIZE);
+}
+
+/**@brief  Read jbd superblock from disk.
+ * @param  jbd_fs jbd filesystem
+ * @param  s jbd superblock
+ * @return standard error code*/
+static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
+{
+       int rc;
+       struct ext4_fs *fs = jbd_fs->inode_ref.fs;
+       uint64_t offset;
+       ext4_fsblk_t fblock;
+       rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
+       if (rc != EOK)
+               return rc;
+
+       offset = fblock * ext4_sb_get_block_size(&fs->sb);
+       return ext4_block_readbytes(fs->bdev, offset, s,
+                                   EXT4_SUPERBLOCK_SIZE);
+}
+
+/**@brief  Verify jbd superblock.
+ * @param  sb jbd superblock
+ * @return true if jbd superblock is valid */
+static bool jbd_verify_sb(struct jbd_sb *sb)
+{
+       struct jbd_bhdr *header = &sb->header;
+       if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
+               return false;
+
+       if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
+           jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
+               return false;
+
+       return jbd_verify_sb_csum(sb);
+}
+
+/**@brief  Write back dirty jbd superblock to disk.
+ * @param  jbd_fs jbd filesystem
+ * @return standard error code*/
+static int jbd_write_sb(struct jbd_fs *jbd_fs)
+{
+       int rc = EOK;
+       if (jbd_fs->dirty) {
+               rc = jbd_sb_write(jbd_fs, &jbd_fs->sb);
+               if (rc != EOK)
+                       return rc;
+
+               jbd_fs->dirty = false;
+       }
+       return rc;
+}
+
+/**@brief  Get reference to jbd filesystem.
+ * @param  fs Filesystem to load journal of
+ * @param  jbd_fs jbd filesystem
+ * @return standard error code*/
+int jbd_get_fs(struct ext4_fs *fs,
+              struct jbd_fs *jbd_fs)
+{
+       int rc;
+       uint32_t journal_ino;
+
+       memset(jbd_fs, 0, sizeof(struct jbd_fs));
+       /* See if there is journal inode on this filesystem.*/
+       /* FIXME: detection on existance ofbkejournal bdev is
+        *        missing.*/
+       journal_ino = ext4_get32(&fs->sb, journal_inode_number);
+
+       rc = ext4_fs_get_inode_ref(fs,
+                                  journal_ino,
+                                  &jbd_fs->inode_ref);
+       if (rc != EOK) {
+               memset(jbd_fs, 0, sizeof(struct jbd_fs));
+               return rc;
+       }
+       rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
+       if (rc != EOK) {
+               memset(jbd_fs, 0, sizeof(struct jbd_fs));
+               ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
+               return rc;
+       }
+       if (!jbd_verify_sb(&jbd_fs->sb)) {
+               memset(jbd_fs, 0, sizeof(struct jbd_fs));
+               ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
+               rc = EIO;
+       }
+
+       return rc;
+}
+
+/**@brief  Put reference of jbd filesystem.
+ * @param  jbd_fs jbd filesystem
+ * @return standard error code*/
+int jbd_put_fs(struct jbd_fs *jbd_fs)
+{
+       int rc = EOK;
+       rc = jbd_write_sb(jbd_fs);
+
+       ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
+       return rc;
+}
+
+/**@brief  Data block lookup helper.
+ * @param  jbd_fs jbd filesystem
+ * @param  iblock block index
+ * @param  fblock logical block address
+ * @return standard error code*/
+int jbd_inode_bmap(struct jbd_fs *jbd_fs,
+                  ext4_lblk_t iblock,
+                  ext4_fsblk_t *fblock)
+{
+       int rc = ext4_fs_get_inode_dblk_idx(
+                       &jbd_fs->inode_ref,
+                       iblock,
+                       fblock,
+                       false);
+       return rc;
+}
+
+/**@brief   jbd block get function (through cache).
+ * @param   jbd_fs jbd filesystem
+ * @param   block block descriptor
+ * @param   fblock jbd logical block address
+ * @return  standard error code*/
+static int jbd_block_get(struct jbd_fs *jbd_fs,
+                 struct ext4_block *block,
+                 ext4_fsblk_t fblock)
+{
+       /* TODO: journal device. */
+       int rc;
+       ext4_lblk_t iblock = (ext4_lblk_t)fblock;
+
+       /* Lookup the logical block address of
+        * fblock.*/
+       rc = jbd_inode_bmap(jbd_fs, iblock,
+                           &fblock);
+       if (rc != EOK)
+               return rc;
+
+       struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
+       rc = ext4_block_get(bdev, block, fblock);
+
+       /* If succeeded, mark buffer as BC_FLUSH to indicate
+        * that data should be written to disk immediately.*/
+       if (rc == EOK) {
+               ext4_bcache_set_flag(block->buf, BC_FLUSH);
+               /* As we don't want to occupy too much space
+                * in block cache, we set this buffer BC_TMP.*/
+               ext4_bcache_set_flag(block->buf, BC_TMP);
+       }
+
+       return rc;
+}
+
+/**@brief   jbd block get function (through cache, don't read).
+ * @param   jbd_fs jbd filesystem
+ * @param   block block descriptor
+ * @param   fblock jbd logical block address
+ * @return  standard error code*/
+static int jbd_block_get_noread(struct jbd_fs *jbd_fs,
+                        struct ext4_block *block,
+                        ext4_fsblk_t fblock)
+{
+       /* TODO: journal device. */
+       int rc;
+       ext4_lblk_t iblock = (ext4_lblk_t)fblock;
+       rc = jbd_inode_bmap(jbd_fs, iblock,
+                           &fblock);
+       if (rc != EOK)
+               return rc;
+
+       struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
+       rc = ext4_block_get_noread(bdev, block, fblock);
+       if (rc == EOK)
+               ext4_bcache_set_flag(block->buf, BC_FLUSH);
+
+       return rc;
+}
+
+/**@brief   jbd block set procedure (through cache).
+ * @param   jbd_fs jbd filesystem
+ * @param   block block descriptor
+ * @return  standard error code*/
+static int jbd_block_set(struct jbd_fs *jbd_fs,
+                 struct ext4_block *block)
+{
+       return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
+                             block);
+}
+
+/**@brief  helper functions to calculate
+ *         block tag size, not including UUID part.
+ * @param  jbd_fs jbd filesystem
+ * @return tag size in bytes*/
+static int jbd_tag_bytes(struct jbd_fs *jbd_fs)
+{
+       int size;
+
+       /* It is very easy to deal with the case which
+        * JBD_FEATURE_INCOMPAT_CSUM_V3 is enabled.*/
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_CSUM_V3))
+               return sizeof(struct jbd_block_tag3);
+
+       size = sizeof(struct jbd_block_tag);
+
+       /* If JBD_FEATURE_INCOMPAT_CSUM_V2 is enabled,
+        * add 2 bytes to size.*/
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_CSUM_V2))
+               size += sizeof(uint16_t);
+
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_64BIT))
+               return size;
+
+       /* If block number is 4 bytes in size,
+        * minus 4 bytes from size */
+       return size - sizeof(uint32_t);
+}
+
+/**@brief  Tag information. */
+struct tag_info {
+       /**@brief  Tag size in bytes, including UUID part.*/
+       int tag_bytes;
+
+       /**@brief  block number stored in this tag.*/
+       ext4_fsblk_t block;
+
+       /**@brief  whether UUID part exists or not.*/
+       bool uuid_exist;
+
+       /**@brief  UUID content if UUID part exists.*/
+       uint8_t uuid[UUID_SIZE];
+
+       /**@brief  Is this the last tag? */
+       bool last_tag;
+
+       /**@brief  crc32c checksum. */
+       uint32_t checksum;
+};
+
+/**@brief  Extract information from a block tag.
+ * @param  __tag pointer to the block tag
+ * @param  tag_bytes block tag size of this jbd filesystem
+ * @param  remaining size in buffer containing the block tag
+ * @param  tag_info information of this tag.
+ * @return  EOK when succeed, otherwise return EINVAL.*/
+static int
+jbd_extract_block_tag(struct jbd_fs *jbd_fs,
+                     void *__tag,
+                     int tag_bytes,
+                     int32_t remain_buf_size,
+                     struct tag_info *tag_info)
+{
+       char *uuid_start;
+       tag_info->tag_bytes = tag_bytes;
+       tag_info->uuid_exist = false;
+       tag_info->last_tag = false;
+
+       /* See whether it is possible to hold a valid block tag.*/
+       if (remain_buf_size - tag_bytes < 0)
+               return EINVAL;
+
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_CSUM_V3)) {
+               struct jbd_block_tag3 *tag = __tag;
+               tag_info->block = jbd_get32(tag, blocknr);
+               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                            JBD_FEATURE_INCOMPAT_64BIT))
+                        tag_info->block |=
+                                (uint64_t)jbd_get32(tag, blocknr_high) << 32;
+
+               if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
+                       tag_info->block = 0;
+
+               if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
+                       /* See whether it is possible to hold UUID part.*/
+                       if (remain_buf_size - tag_bytes < UUID_SIZE)
+                               return EINVAL;
+
+                       uuid_start = (char *)tag + tag_bytes;
+                       tag_info->uuid_exist = true;
+                       tag_info->tag_bytes += UUID_SIZE;
+                       memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
+               }
+
+               if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
+                       tag_info->last_tag = true;
+
+       } else {
+               struct jbd_block_tag *tag = __tag;
+               tag_info->block = jbd_get32(tag, blocknr);
+               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                            JBD_FEATURE_INCOMPAT_64BIT))
+                        tag_info->block |=
+                                (uint64_t)jbd_get32(tag, blocknr_high) << 32;
+
+               if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
+                       tag_info->block = 0;
+
+               if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
+                       /* See whether it is possible to hold UUID part.*/
+                       if (remain_buf_size - tag_bytes < UUID_SIZE)
+                               return EINVAL;
+
+                       uuid_start = (char *)tag + tag_bytes;
+                       tag_info->uuid_exist = true;
+                       tag_info->tag_bytes += UUID_SIZE;
+                       memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
+               }
+
+               if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
+                       tag_info->last_tag = true;
+
+       }
+       return EOK;
+}
+
+/**@brief  Write information to a block tag.
+ * @param  __tag pointer to the block tag
+ * @param  remaining size in buffer containing the block tag
+ * @param  tag_info information of this tag.
+ * @return  EOK when succeed, otherwise return EINVAL.*/
+static int
+jbd_write_block_tag(struct jbd_fs *jbd_fs,
+                   void *__tag,
+                   int32_t remain_buf_size,
+                   struct tag_info *tag_info)
+{
+       char *uuid_start;
+       int tag_bytes = jbd_tag_bytes(jbd_fs);
+
+       tag_info->tag_bytes = tag_bytes;
+
+       /* See whether it is possible to hold a valid block tag.*/
+       if (remain_buf_size - tag_bytes < 0)
+               return EINVAL;
+
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_CSUM_V3)) {
+               struct jbd_block_tag3 *tag = __tag;
+               memset(tag, 0, sizeof(struct jbd_block_tag3));
+               jbd_set32(tag, blocknr, (uint32_t)tag_info->block);
+               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                            JBD_FEATURE_INCOMPAT_64BIT))
+                       jbd_set32(tag, blocknr_high, tag_info->block >> 32);
+
+               if (tag_info->uuid_exist) {
+                       /* See whether it is possible to hold UUID part.*/
+                       if (remain_buf_size - tag_bytes < UUID_SIZE)
+                               return EINVAL;
+
+                       uuid_start = (char *)tag + tag_bytes;
+                       tag_info->tag_bytes += UUID_SIZE;
+                       memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
+               } else
+                       jbd_set32(tag, flags,
+                                 jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID);
+
+               jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum);
+
+               if (tag_info->last_tag)
+                       jbd_set32(tag, flags,
+                                 jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG);
+
+       } else {
+               struct jbd_block_tag *tag = __tag;
+               memset(tag, 0, sizeof(struct jbd_block_tag));
+               jbd_set32(tag, blocknr, (uint32_t)tag_info->block);
+               if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                            JBD_FEATURE_INCOMPAT_64BIT))
+                       jbd_set32(tag, blocknr_high, tag_info->block >> 32);
+
+               if (tag_info->uuid_exist) {
+                       /* See whether it is possible to hold UUID part.*/
+                       if (remain_buf_size - tag_bytes < UUID_SIZE)
+                               return EINVAL;
+
+                       uuid_start = (char *)tag + tag_bytes;
+                       tag_info->tag_bytes += UUID_SIZE;
+                       memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
+               } else
+                       jbd_set16(tag, flags,
+                                 jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID);
+
+               jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum);
+
+               if (tag_info->last_tag)
+                       jbd_set16(tag, flags,
+                                 jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG);
+
+       }
+       return EOK;
+}
+
+/**@brief  Iterate all block tags in a block.
+ * @param  jbd_fs jbd filesystem
+ * @param  __tag_start pointer to the block
+ * @param  tag_tbl_size size of the block
+ * @param  func callback routine to indicate that
+ *         a block tag is found
+ * @param  arg additional argument to be passed to func */
+static void
+jbd_iterate_block_table(struct jbd_fs *jbd_fs,
+                       void *__tag_start,
+                       int32_t tag_tbl_size,
+                       void (*func)(struct jbd_fs * jbd_fs,
+                                       ext4_fsblk_t block,
+                                       uint8_t *uuid,
+                                       void *arg),
+                       void *arg)
+{
+       char *tag_start, *tag_ptr;
+       int tag_bytes = jbd_tag_bytes(jbd_fs);
+       tag_start = __tag_start;
+       tag_ptr = tag_start;
+
+       /* Cut off the size of block tail storing checksum. */
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_CSUM_V2) ||
+           JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_CSUM_V3))
+               tag_tbl_size -= sizeof(struct jbd_block_tail);
+
+       while (tag_tbl_size) {
+               struct tag_info tag_info;
+               int rc = jbd_extract_block_tag(jbd_fs,
+                                     tag_ptr,
+                                     tag_bytes,
+                                     tag_tbl_size,
+                                     &tag_info);
+               if (rc != EOK)
+                       break;
+
+               if (func)
+                       func(jbd_fs, tag_info.block, tag_info.uuid, arg);
+
+               /* Stop the iteration when we reach the last tag. */
+               if (tag_info.last_tag)
+                       break;
+
+               tag_ptr += tag_info.tag_bytes;
+               tag_tbl_size -= tag_info.tag_bytes;
+       }
+}
+
+static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
+                                  ext4_fsblk_t block,
+                                  uint8_t *uuid,
+                                  void *arg)
+{
+       uint32_t *iblock = arg;
+       ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
+       (*iblock)++;
+       (void)jbd_fs;
+       (void)uuid;
+       return;
+}
+
+static struct revoke_entry *
+jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
+{
+       struct revoke_entry tmp = {
+               .block = block
+       };
+
+       return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
+}
+
+/**@brief  Replay a block in a transaction.
+ * @param  jbd_fs jbd filesystem
+ * @param  block  block address to be replayed.*/
+static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
+                                 ext4_fsblk_t block,
+                                 uint8_t *uuid __unused,
+                                 void *__arg)
+{
+       int r;
+       struct replay_arg *arg = __arg;
+       struct recover_info *info = arg->info;
+       uint32_t *this_block = arg->this_block;
+       struct revoke_entry *revoke_entry;
+       struct ext4_block journal_block, ext4_block;
+       struct ext4_fs *fs = jbd_fs->inode_ref.fs;
+
+       (*this_block)++;
+
+       /* We replay this block only if the current transaction id
+        * is equal or greater than that in revoke entry.*/
+       revoke_entry = jbd_revoke_entry_lookup(info, block);
+       if (revoke_entry &&
+           arg->this_trans_id < revoke_entry->trans_id)
+               return;
+
+       ext4_dbg(DEBUG_JBD,
+                "Replaying block in block_tag: %" PRIu64 "\n",
+                block);
+
+       r = jbd_block_get(jbd_fs, &journal_block, *this_block);
+       if (r != EOK)
+               return;
+
+       /* We need special treatment for ext4 superblock. */
+       if (block) {
+               r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
+               if (r != EOK) {
+                       jbd_block_set(jbd_fs, &journal_block);
+                       return;
+               }
+
+               memcpy(ext4_block.data,
+                       journal_block.data,
+                       jbd_get32(&jbd_fs->sb, blocksize));
+
+               ext4_bcache_set_dirty(ext4_block.buf);
+               ext4_block_set(fs->bdev, &ext4_block);
+       } else {
+               uint16_t mount_count, state;
+               mount_count = ext4_get16(&fs->sb, mount_count);
+               state = ext4_get16(&fs->sb, state);
+
+               memcpy(&fs->sb,
+                       journal_block.data + EXT4_SUPERBLOCK_OFFSET,
+                       EXT4_SUPERBLOCK_SIZE);
+
+               /* Mark system as mounted */
+               ext4_set16(&fs->sb, state, state);
+               r = ext4_sb_write(fs->bdev, &fs->sb);
+               if (r != EOK)
+                       return;
+
+               /*Update mount count*/
+               ext4_set16(&fs->sb, mount_count, mount_count);
+       }
+
+       jbd_block_set(jbd_fs, &journal_block);
+       
+       return;
+}
+
+/**@brief  Add block address to revoke tree, along with
+ *         its transaction id.
+ * @param  info  journal replay info
+ * @param  block  block address to be replayed.*/
+static void jbd_add_revoke_block_tags(struct recover_info *info,
+                                     ext4_fsblk_t block)
+{
+       struct revoke_entry *revoke_entry;
+
+       ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
+       /* If the revoke entry with respect to the block address
+        * exists already, update its transaction id.*/
+       revoke_entry = jbd_revoke_entry_lookup(info, block);
+       if (revoke_entry) {
+               revoke_entry->trans_id = info->this_trans_id;
+               return;
+       }
+
+       revoke_entry = jbd_alloc_revoke_entry();
+       ext4_assert(revoke_entry);
+       revoke_entry->block = block;
+       revoke_entry->trans_id = info->this_trans_id;
+       RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
+
+       return;
+}
+
+static void jbd_destroy_revoke_tree(struct recover_info *info)
+{
+       while (!RB_EMPTY(&info->revoke_root)) {
+               struct revoke_entry *revoke_entry =
+                       RB_MIN(jbd_revoke, &info->revoke_root);
+               ext4_assert(revoke_entry);
+               RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
+               jbd_free_revoke_entry(revoke_entry);
+       }
+}
+
+/* Make sure we wrap around the log correctly! */
+#define wrap(sb, var)                                          \
+do {                                                                   \
+       if (var >= jbd_get32((sb), maxlen))                                     \
+               var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first));      \
+} while (0)
+
+#define ACTION_SCAN 0
+#define ACTION_REVOKE 1
+#define ACTION_RECOVER 2
+
+/**@brief  Add entries in a revoke block to revoke tree.
+ * @param  jbd_fs jbd filesystem
+ * @param  header revoke block header
+ * @param  recover_info  journal replay info*/
+static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
+                                 struct jbd_bhdr *header,
+                                 struct recover_info *info)
+{
+       char *blocks_entry;
+       struct jbd_revoke_header *revoke_hdr =
+               (struct jbd_revoke_header *)header;
+       uint32_t i, nr_entries, record_len = 4;
+
+       /* If we are working on a 64bit jbd filesystem, */
+       if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_64BIT))
+               record_len = 8;
+
+       nr_entries = (jbd_get32(revoke_hdr, count) -
+                       sizeof(struct jbd_revoke_header)) /
+                       record_len;
+
+       blocks_entry = (char *)(revoke_hdr + 1);
+
+       for (i = 0;i < nr_entries;i++) {
+               if (record_len == 8) {
+                       uint64_t *blocks =
+                               (uint64_t *)blocks_entry;
+                       jbd_add_revoke_block_tags(info, to_be64(*blocks));
+               } else {
+                       uint32_t *blocks =
+                               (uint32_t *)blocks_entry;
+                       jbd_add_revoke_block_tags(info, to_be32(*blocks));
+               }
+               blocks_entry += record_len;
+       }
+}
+
+static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
+                                      struct jbd_bhdr *header,
+                                      uint32_t *iblock)
+{
+       jbd_iterate_block_table(jbd_fs,
+                               header + 1,
+                               jbd_get32(&jbd_fs->sb, blocksize) -
+                                       sizeof(struct jbd_bhdr),
+                               jbd_display_block_tags,
+                               iblock);
+}
+
+static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
+                                       struct jbd_bhdr *header,
+                                       struct replay_arg *arg)
+{
+       jbd_iterate_block_table(jbd_fs,
+                               header + 1,
+                               jbd_get32(&jbd_fs->sb, blocksize) -
+                                       sizeof(struct jbd_bhdr),
+                               jbd_replay_block_tags,
+                               arg);
+}
+
+/**@brief  The core routine of journal replay.
+ * @param  jbd_fs jbd filesystem
+ * @param  recover_info  journal replay info
+ * @param  action action needed to be taken
+ * @return standard error code*/
+static int jbd_iterate_log(struct jbd_fs *jbd_fs,
+                          struct recover_info *info,
+                          int action)
+{
+       int r = EOK;
+       bool log_end = false;
+       struct jbd_sb *sb = &jbd_fs->sb;
+       uint32_t start_trans_id, this_trans_id;
+       uint32_t start_block, this_block;
+
+       /* We start iterating valid blocks in the whole journal.*/
+       start_trans_id = this_trans_id = jbd_get32(sb, sequence);
+       start_block = this_block = jbd_get32(sb, start);
+       if (action == ACTION_SCAN)
+               info->trans_cnt = 0;
+       else if (!info->trans_cnt)
+               log_end = true;
+
+       ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
+                           start_trans_id);
+
+       while (!log_end) {
+               struct ext4_block block;
+               struct jbd_bhdr *header;
+               /* If we are not scanning for the last
+                * valid transaction in the journal,
+                * we will stop when we reach the end of
+                * the journal.*/
+               if (action != ACTION_SCAN)
+                       if (this_trans_id > info->last_trans_id) {
+                               log_end = true;
+                               continue;
+                       }
+
+               r = jbd_block_get(jbd_fs, &block, this_block);
+               if (r != EOK)
+                       break;
+
+               header = (struct jbd_bhdr *)block.data;
+               /* This block does not have a valid magic number,
+                * so we have reached the end of the journal.*/
+               if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
+                       jbd_block_set(jbd_fs, &block);
+                       log_end = true;
+                       continue;
+               }
+
+               /* If the transaction id we found is not expected,
+                * we may have reached the end of the journal.
+                *
+                * If we are not scanning the journal, something
+                * bad might have taken place. :-( */
+               if (jbd_get32(header, sequence) != this_trans_id) {
+                       if (action != ACTION_SCAN)
+                               r = EIO;
+
+                       jbd_block_set(jbd_fs, &block);
+                       log_end = true;
+                       continue;
+               }
+
+               switch (jbd_get32(header, blocktype)) {
+               case JBD_DESCRIPTOR_BLOCK:
+                       if (!jbd_verify_meta_csum(jbd_fs, header)) {
+                               ext4_dbg(DEBUG_JBD,
+                                       DBG_WARN "Descriptor block checksum failed."
+                                               "Journal block: %" PRIu32"\n",
+                                               this_block);
+                               log_end = true;
+                               break;
+                       }
+                       ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
+                                           "trans_id: %" PRIu32"\n",
+                                           this_block, this_trans_id);
+                       if (action == ACTION_RECOVER) {
+                               struct replay_arg replay_arg;
+                               replay_arg.info = info;
+                               replay_arg.this_block = &this_block;
+                               replay_arg.this_trans_id = this_trans_id;
+
+                               jbd_replay_descriptor_block(jbd_fs,
+                                               header, &replay_arg);
+                       } else
+                               jbd_debug_descriptor_block(jbd_fs,
+                                               header, &this_block);
+
+                       break;
+               case JBD_COMMIT_BLOCK:
+                       if (!jbd_verify_commit_csum(jbd_fs,
+                                       (struct jbd_commit_header *)header)) {
+                               ext4_dbg(DEBUG_JBD,
+                                       DBG_WARN "Commit block checksum failed."
+                                               "Journal block: %" PRIu32"\n",
+                                               this_block);
+                               log_end = true;
+                               break;
+                       }
+                       ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
+                                           "trans_id: %" PRIu32"\n",
+                                           this_block, this_trans_id);
+                       /* This is the end of a transaction,
+                        * we may now proceed to the next transaction.
+                        */
+                       this_trans_id++;
+                       info->trans_cnt++;
+                       break;
+               case JBD_REVOKE_BLOCK:
+                       if (!jbd_verify_meta_csum(jbd_fs, header)) {
+                               ext4_dbg(DEBUG_JBD,
+                                       DBG_WARN "Revoke block checksum failed."
+                                               "Journal block: %" PRIu32"\n",
+                                               this_block);
+                               log_end = true;
+                               break;
+                       }
+                       ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
+                                           "trans_id: %" PRIu32"\n",
+                                           this_block, this_trans_id);
+                       if (action == ACTION_REVOKE) {
+                               info->this_trans_id = this_trans_id;
+                               jbd_build_revoke_tree(jbd_fs,
+                                               header, info);
+                       }
+                       break;
+               default:
+                       log_end = true;
+                       break;
+               }
+               jbd_block_set(jbd_fs, &block);
+               this_block++;
+               wrap(sb, this_block);
+               if (this_block == start_block)
+                       log_end = true;
+
+       }
+       ext4_dbg(DEBUG_JBD, "End of journal.\n");
+       if (r == EOK && action == ACTION_SCAN) {
+               /* We have finished scanning the journal. */
+               info->start_trans_id = start_trans_id;
+               if (this_trans_id > start_trans_id)
+                       info->last_trans_id = this_trans_id - 1;
+               else
+                       info->last_trans_id = this_trans_id;
+       }
+
+       return r;
+}
+
+/**@brief  Replay journal.
+ * @param  jbd_fs jbd filesystem
+ * @return standard error code*/
+int jbd_recover(struct jbd_fs *jbd_fs)
+{
+       int r;
+       struct recover_info info;
+       struct jbd_sb *sb = &jbd_fs->sb;
+       if (!sb->start)
+               return EOK;
+
+       RB_INIT(&info.revoke_root);
+
+       r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
+       if (r != EOK)
+               return r;
+
+       r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
+       if (r != EOK)
+               return r;
+
+       r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
+       if (r == EOK) {
+               /* If we successfully replay the journal,
+                * clear EXT4_FINCOM_RECOVER flag on the
+                * ext4 superblock, and set the start of
+                * journal to 0.*/
+               uint32_t features_incompatible =
+                       ext4_get32(&jbd_fs->inode_ref.fs->sb,
+                                  features_incompatible);
+               jbd_set32(&jbd_fs->sb, start, 0);
+               features_incompatible &= ~EXT4_FINCOM_RECOVER;
+               ext4_set32(&jbd_fs->inode_ref.fs->sb,
+                          features_incompatible,
+                          features_incompatible);
+               jbd_fs->dirty = true;
+               r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
+                                 &jbd_fs->inode_ref.fs->sb);
+       }
+       jbd_destroy_revoke_tree(&info);
+       return r;
+}
+
+static void jbd_journal_write_sb(struct jbd_journal *journal)
+{
+       struct jbd_fs *jbd_fs = journal->jbd_fs;
+       jbd_set32(&jbd_fs->sb, start, journal->start);
+       jbd_set32(&jbd_fs->sb, sequence, journal->trans_id);
+       jbd_fs->dirty = true;
+}
+
+/**@brief  Start accessing the journal.
+ * @param  jbd_fs jbd filesystem
+ * @param  journal current journal session
+ * @return standard error code*/
+int jbd_journal_start(struct jbd_fs *jbd_fs,
+                     struct jbd_journal *journal)
+{
+       int r;
+       uint32_t features_incompatible =
+                       ext4_get32(&jbd_fs->inode_ref.fs->sb,
+                                  features_incompatible);
+       struct ext4_block block = EXT4_BLOCK_ZERO();
+       features_incompatible |= EXT4_FINCOM_RECOVER;
+       ext4_set32(&jbd_fs->inode_ref.fs->sb,
+                       features_incompatible,
+                       features_incompatible);
+       r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
+                       &jbd_fs->inode_ref.fs->sb);
+       if (r != EOK)
+               return r;
+
+       journal->first = jbd_get32(&jbd_fs->sb, first);
+       journal->start = journal->first;
+       journal->last = journal->first;
+       journal->trans_id = 1;
+       journal->alloc_trans_id = 1;
+
+       journal->block_size = jbd_get32(&jbd_fs->sb, blocksize);
+
+       r = jbd_block_get_noread(jbd_fs,
+                        &block,
+                        journal->start);
+       if (r != EOK) {
+               memset(journal, 0, sizeof(struct jbd_journal));
+               return r;
+       }
+       memset(block.data, 0, journal->block_size);
+       ext4_bcache_set_dirty(block.buf);
+       r = jbd_block_set(jbd_fs, &block);
+       if (r != EOK) {
+               memset(journal, 0, sizeof(struct jbd_journal));
+               return r;
+       }
+
+       TAILQ_INIT(&journal->trans_queue);
+       TAILQ_INIT(&journal->cp_queue);
+       RB_INIT(&journal->block_rec_root);
+       journal->jbd_fs = jbd_fs;
+       jbd_journal_write_sb(journal);
+       return jbd_write_sb(jbd_fs);
+}
+
+static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
+                         struct ext4_buf *buf __unused,
+                         int res,
+                         void *arg);
+
+static void jbd_journal_flush_trans(struct jbd_trans *trans)
+{
+       struct jbd_buf *jbd_buf, *tmp;
+       struct jbd_journal *journal = trans->journal;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       void *tmp_data = malloc(journal->block_size);
+       ext4_assert(tmp_data);
+
+       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
+                       tmp) {
+               struct ext4_buf *buf = jbd_buf->block_rec->buf;
+               /* The buffer in memory is still dirty. */
+               if (buf) {
+                       if (jbd_buf->block_rec->trans != trans) {
+                               int r;
+                               struct ext4_block jbd_block = EXT4_BLOCK_ZERO();
+                               ext4_assert(ext4_block_get(fs->bdev,
+                                                       &jbd_block,
+                                                       jbd_buf->jbd_lba) == EOK);
+                               memcpy(tmp_data, jbd_block.data,
+                                               journal->block_size);
+                               ext4_block_set(fs->bdev, &jbd_block);
+                               r = ext4_blocks_set_direct(fs->bdev, tmp_data,
+                                               buf->lba, 1);
+                               jbd_trans_end_write(fs->bdev->bc, buf, r, jbd_buf);
+                       } else
+                               ext4_block_flush_buf(fs->bdev, buf);
+
+               }
+       }
+
+       free(tmp_data);
+}
+
+static void
+jbd_journal_skip_pure_revoke(struct jbd_journal *journal,
+                            struct jbd_trans *trans)
+{
+       journal->start = trans->start_iblock +
+               trans->alloc_blocks;
+       wrap(&journal->jbd_fs->sb, journal->start);
+       journal->trans_id = trans->trans_id + 1;
+       jbd_journal_free_trans(journal,
+                       trans, false);
+       jbd_journal_write_sb(journal);
+}
+
+static void
+jbd_journal_purge_cp_trans(struct jbd_journal *journal,
+                          bool flush)
+{
+       struct jbd_trans *trans;
+       while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
+               if (!trans->data_cnt) {
+                       TAILQ_REMOVE(&journal->cp_queue,
+                                       trans,
+                                       trans_node);
+                       jbd_journal_skip_pure_revoke(journal, trans);
+               } else {
+                       if (trans->data_cnt ==
+                                       trans->written_cnt) {
+                               journal->start =
+                                       trans->start_iblock +
+                                       trans->alloc_blocks;
+                               wrap(&journal->jbd_fs->sb,
+                                               journal->start);
+                               journal->trans_id =
+                                       trans->trans_id + 1;
+                               TAILQ_REMOVE(&journal->cp_queue,
+                                               trans,
+                                               trans_node);
+                               jbd_journal_free_trans(journal,
+                                               trans,
+                                               false);
+                               jbd_journal_write_sb(journal);
+                       } else if (!flush) {
+                               journal->start =
+                                       trans->start_iblock;
+                               wrap(&journal->jbd_fs->sb,
+                                               journal->start);
+                               journal->trans_id =
+                                       trans->trans_id;
+                               jbd_journal_write_sb(journal);
+                               break;
+                       } else
+                               jbd_journal_flush_trans(trans);
+               }
+       }
+}
+
+/**@brief  Stop accessing the journal.
+ * @param  journal current journal session
+ * @return standard error code*/
+int jbd_journal_stop(struct jbd_journal *journal)
+{
+       int r;
+       struct jbd_fs *jbd_fs = journal->jbd_fs;
+       uint32_t features_incompatible;
+
+       /* Make sure that journalled content have reached
+        * the disk.*/
+       jbd_journal_purge_cp_trans(journal, true);
+
+       /* There should be no block record in this journal
+        * session. */
+       if (!RB_EMPTY(&journal->block_rec_root))
+               ext4_dbg(DEBUG_JBD,
+                        DBG_WARN "There are still block records "
+                                 "in this journal session!\n");
+
+       features_incompatible =
+               ext4_get32(&jbd_fs->inode_ref.fs->sb,
+                          features_incompatible);
+       features_incompatible &= ~EXT4_FINCOM_RECOVER;
+       ext4_set32(&jbd_fs->inode_ref.fs->sb,
+                       features_incompatible,
+                       features_incompatible);
+       r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
+                       &jbd_fs->inode_ref.fs->sb);
+       if (r != EOK)
+               return r;
+
+       journal->start = 0;
+       journal->trans_id = 0;
+       jbd_journal_write_sb(journal);
+       return jbd_write_sb(journal->jbd_fs);
+}
+
+/**@brief  Allocate a block in the journal.
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @return allocated block address*/
+static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal,
+                                       struct jbd_trans *trans)
+{
+       uint32_t start_block;
+
+       start_block = journal->last++;
+       trans->alloc_blocks++;
+       wrap(&journal->jbd_fs->sb, journal->last);
+       
+       /* If there is no space left, flush all journalled
+        * blocks to disk first.*/
+       if (journal->last == journal->start)
+               jbd_journal_purge_cp_trans(journal, true);
+
+       return start_block;
+}
+
+/**@brief  Allocate a new transaction
+ * @param  journal current journal session
+ * @return transaction allocated*/
+struct jbd_trans *
+jbd_journal_new_trans(struct jbd_journal *journal)
+{
+       struct jbd_trans *trans = calloc(1, sizeof(struct jbd_trans));
+       if (!trans)
+               return NULL;
+
+       /* We will assign a trans_id to this transaction,
+        * once it has been committed.*/
+       trans->journal = journal;
+       trans->data_csum = EXT4_CRC32_INIT;
+       trans->error = EOK;
+       TAILQ_INIT(&trans->buf_queue);
+       return trans;
+}
+
+/**@brief  gain access to it before making any modications.
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @param  block descriptor
+ * @return standard error code.*/
+int jbd_trans_get_access(struct jbd_journal *journal,
+                        struct jbd_trans *trans,
+                        struct ext4_block *block)
+{
+       int r = EOK;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       struct jbd_buf *jbd_buf = block->buf->end_write_arg;
+
+       /* If the buffer has already been modified, we should
+        * flush dirty data in this buffer to disk.*/
+       if (ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
+           block->buf->end_write == jbd_trans_end_write) {
+               ext4_assert(jbd_buf);
+               if (jbd_buf->trans != trans)
+                       r = ext4_block_flush_buf(fs->bdev, block->buf);
+
+       }
+       return r;
+}
+
+static struct jbd_block_rec *
+jbd_trans_block_rec_lookup(struct jbd_journal *journal,
+                          ext4_fsblk_t lba)
+{
+       struct jbd_block_rec tmp = {
+               .lba = lba
+       };
+
+       return RB_FIND(jbd_block,
+                      &journal->block_rec_root,
+                      &tmp);
+}
+
+static void
+jbd_trans_change_ownership(struct jbd_block_rec *block_rec,
+                          struct jbd_trans *new_trans,
+                          struct ext4_buf *new_buf)
+{
+       LIST_REMOVE(block_rec, tbrec_node);
+       /* Now this block record belongs to this transaction. */
+       LIST_INSERT_HEAD(&new_trans->tbrec_list, block_rec, tbrec_node);
+       block_rec->trans = new_trans;
+       block_rec->buf = new_buf;
+}
+
+static inline struct jbd_block_rec *
+jbd_trans_insert_block_rec(struct jbd_trans *trans,
+                          ext4_fsblk_t lba,
+                          struct ext4_buf *buf)
+{
+       struct jbd_block_rec *block_rec;
+       block_rec = jbd_trans_block_rec_lookup(trans->journal, lba);
+       if (block_rec) {
+               jbd_trans_change_ownership(block_rec, trans, buf);
+               return block_rec;
+       }
+       block_rec = calloc(1, sizeof(struct jbd_block_rec));
+       if (!block_rec)
+               return NULL;
+
+       block_rec->lba = lba;
+       block_rec->buf = buf;
+       block_rec->trans = trans;
+       TAILQ_INIT(&block_rec->dirty_buf_queue);
+       LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
+       RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec);
+       return block_rec;
+}
+
+static void
+jbd_trans_finish_callback(struct jbd_journal *journal,
+                         const struct jbd_trans *trans,
+                         struct jbd_block_rec *block_rec,
+                         bool abort)
+{
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       if (block_rec->trans != trans)
+               return;
+
+       if (!abort) {
+               struct jbd_buf *jbd_buf, *tmp;
+               TAILQ_FOREACH_SAFE(jbd_buf,
+                               &block_rec->dirty_buf_queue,
+                               dirty_buf_node,
+                               tmp) {
+                       /* All we need is a fake ext4_buf. */
+                       struct ext4_buf buf;
+
+                       jbd_trans_end_write(fs->bdev->bc,
+                                       &buf,
+                                       EOK,
+                                       jbd_buf);
+               }
+       } else {
+               struct jbd_buf *jbd_buf;
+               struct ext4_block jbd_block = EXT4_BLOCK_ZERO(),
+                                 block = EXT4_BLOCK_ZERO();
+               jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue,
+                               jbd_buf_dirty);
+               if (jbd_buf) {
+                       ext4_assert(ext4_block_get(fs->bdev,
+                                               &jbd_block,
+                                               jbd_buf->jbd_lba) == EOK);
+                       ext4_assert(ext4_block_get_noread(fs->bdev,
+                                               &block,
+                                               block_rec->lba) == EOK);
+                       memcpy(block.data, jbd_block.data,
+                                       journal->block_size);
+
+                       jbd_trans_change_ownership(block_rec,
+                                       jbd_buf->trans, block.buf);
+
+                       block.buf->end_write = jbd_trans_end_write;
+                       block.buf->end_write_arg = jbd_buf;
+
+                       ext4_bcache_set_flag(jbd_block.buf, BC_TMP);
+                       ext4_bcache_set_dirty(block.buf);
+
+                       ext4_block_set(fs->bdev, &jbd_block);
+                       ext4_block_set(fs->bdev, &block);
+                       return;
+               }
+       }
+}
+
+static inline void
+jbd_trans_remove_block_rec(struct jbd_journal *journal,
+                          struct jbd_block_rec *block_rec,
+                          struct jbd_trans *trans)
+{
+       /* If this block record doesn't belong to this transaction,
+        * give up.*/
+       if (block_rec->trans == trans) {
+               LIST_REMOVE(block_rec, tbrec_node);
+               RB_REMOVE(jbd_block,
+                               &journal->block_rec_root,
+                               block_rec);
+               free(block_rec);
+       }
+}
+
+/**@brief  Add block to a transaction and mark it dirty.
+ * @param  trans transaction
+ * @param  block block descriptor
+ * @return standard error code*/
+int jbd_trans_set_block_dirty(struct jbd_trans *trans,
+                             struct ext4_block *block)
+{
+       struct jbd_buf *buf;
+
+       struct jbd_block_rec *block_rec;
+       if (block->buf->end_write == jbd_trans_end_write) {
+               buf = block->buf->end_write_arg;
+               if (buf && buf->trans == trans)
+                       return EOK;
+       }
+       buf = calloc(1, sizeof(struct jbd_buf));
+       if (!buf)
+               return ENOMEM;
+
+       if ((block_rec = jbd_trans_insert_block_rec(trans,
+                                       block->lb_id,
+                                       block->buf)) == NULL) {
+               free(buf);
+               return ENOMEM;
+       }
+
+       TAILQ_INSERT_TAIL(&block_rec->dirty_buf_queue,
+                       buf,
+                       dirty_buf_node);
+
+       buf->block_rec = block_rec;
+       buf->trans = trans;
+       buf->block = *block;
+       ext4_bcache_inc_ref(block->buf);
+
+       /* If the content reach the disk, notify us
+        * so that we may do a checkpoint. */
+       block->buf->end_write = jbd_trans_end_write;
+       block->buf->end_write_arg = buf;
+
+       trans->data_cnt++;
+       TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
+
+       ext4_bcache_set_dirty(block->buf);
+       return EOK;
+}
+
+/**@brief  Add block to be revoked to a transaction
+ * @param  trans transaction
+ * @param  lba logical block address
+ * @return standard error code*/
+int jbd_trans_revoke_block(struct jbd_trans *trans,
+                          ext4_fsblk_t lba)
+{
+       struct jbd_revoke_rec *rec =
+               calloc(1, sizeof(struct jbd_revoke_rec));
+       if (!rec)
+               return ENOMEM;
+
+       rec->lba = lba;
+       LIST_INSERT_HEAD(&trans->revoke_list, rec, revoke_node);
+       return EOK;
+}
+
+/**@brief  Try to add block to be revoked to a transaction.
+ *         If @lba still remains in an transaction on checkpoint
+ *         queue, add @lba as a revoked block to the transaction.
+ * @param  trans transaction
+ * @param  lba logical block address
+ * @return standard error code*/
+int jbd_trans_try_revoke_block(struct jbd_trans *trans,
+                              ext4_fsblk_t lba)
+{
+       int r = EOK;
+       struct jbd_journal *journal = trans->journal;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       struct jbd_block_rec *block_rec =
+               jbd_trans_block_rec_lookup(journal, lba);
+
+       /* Make sure we don't flush any buffers belong to this transaction. */
+       if (block_rec && block_rec->trans != trans) {
+               /* If the buffer has not been flushed yet, flush it now. */
+               if (block_rec->buf) {
+                       r = ext4_block_flush_buf(fs->bdev, block_rec->buf);
+                       if (r != EOK)
+                               return r;
+
+               }
+
+               jbd_trans_revoke_block(trans, lba);
+       }
+
+       return EOK;
+}
+
+/**@brief  Free a transaction
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @param  abort discard all the modifications on the block?
+ * @return standard error code*/
+void jbd_journal_free_trans(struct jbd_journal *journal,
+                           struct jbd_trans *trans,
+                           bool abort)
+{
+       struct jbd_buf *jbd_buf, *tmp;
+       struct jbd_revoke_rec *rec, *tmp2;
+       struct jbd_block_rec *block_rec, *tmp3;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
+                         tmp) {
+               block_rec = jbd_buf->block_rec;
+               if (abort) {
+                       jbd_buf->block.buf->end_write = NULL;
+                       jbd_buf->block.buf->end_write_arg = NULL;
+                       ext4_bcache_clear_dirty(jbd_buf->block.buf);
+                       ext4_block_set(fs->bdev, &jbd_buf->block);
+               }
+
+               TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+                       jbd_buf,
+                       dirty_buf_node);
+               jbd_trans_finish_callback(journal,
+                               trans,
+                               block_rec,
+                               abort);
+               TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
+               free(jbd_buf);
+       }
+       LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
+                         tmp2) {
+               LIST_REMOVE(rec, revoke_node);
+               free(rec);
+       }
+       LIST_FOREACH_SAFE(block_rec, &trans->tbrec_list, tbrec_node,
+                         tmp3) {
+               jbd_trans_remove_block_rec(journal, block_rec, trans);
+       }
+
+       free(trans);
+}
+
+/**@brief  Write commit block for a transaction
+ * @param  trans transaction
+ * @return standard error code*/
+static int jbd_trans_write_commit_block(struct jbd_trans *trans)
+{
+       int rc;
+       struct jbd_commit_header *header;
+       uint32_t commit_iblock = 0;
+       struct ext4_block commit_block;
+       struct jbd_journal *journal = trans->journal;
+
+       commit_iblock = jbd_journal_alloc_block(journal, trans);
+       rc = jbd_block_get_noread(journal->jbd_fs,
+                       &commit_block, commit_iblock);
+       if (rc != EOK)
+               return rc;
+
+       header = (struct jbd_commit_header *)commit_block.data;
+       jbd_set32(&header->header, magic, JBD_MAGIC_NUMBER);
+       jbd_set32(&header->header, blocktype, JBD_COMMIT_BLOCK);
+       jbd_set32(&header->header, sequence, trans->trans_id);
+
+       if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
+                               JBD_FEATURE_COMPAT_CHECKSUM)) {
+               jbd_set32(header, chksum_type, JBD_CRC32_CHKSUM);
+               jbd_set32(header, chksum_size, JBD_CRC32_CHKSUM_SIZE);
+               jbd_set32(header, chksum[0], trans->data_csum);
+       }
+       jbd_commit_csum_set(journal->jbd_fs, header);
+       ext4_bcache_set_dirty(commit_block.buf);
+       rc = jbd_block_set(journal->jbd_fs, &commit_block);
+       if (rc != EOK)
+               return rc;
+
+       return EOK;
+}
+
+/**@brief  Write descriptor block for a transaction
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @return standard error code*/
+static int jbd_journal_prepare(struct jbd_journal *journal,
+                              struct jbd_trans *trans)
+{
+       int rc = EOK, i = 0;
+       int32_t tag_tbl_size;
+       uint32_t desc_iblock = 0;
+       uint32_t data_iblock = 0;
+       char *tag_start = NULL, *tag_ptr = NULL;
+       struct jbd_buf *jbd_buf, *tmp;
+       struct ext4_block desc_block, data_block;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       uint32_t checksum = EXT4_CRC32_INIT;
+
+       /* Try to remove any non-dirty buffers from the tail of
+        * buf_queue. */
+       TAILQ_FOREACH_REVERSE_SAFE(jbd_buf, &trans->buf_queue,
+                       jbd_trans_buf, buf_node, tmp) {
+               /* We stop the iteration when we find a dirty buffer. */
+               if (ext4_bcache_test_flag(jbd_buf->block.buf,
+                                       BC_DIRTY))
+                       break;
+       
+               TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+                       jbd_buf,
+                       dirty_buf_node);
+
+               jbd_buf->block.buf->end_write = NULL;
+               jbd_buf->block.buf->end_write_arg = NULL;
+               jbd_trans_finish_callback(journal,
+                               trans,
+                               jbd_buf->block_rec,
+                               true);
+
+               /* The buffer has not been modified, just release
+                * that jbd_buf. */
+               jbd_trans_remove_block_rec(journal,
+                               jbd_buf->block_rec, trans);
+               trans->data_cnt--;
+
+               ext4_block_set(fs->bdev, &jbd_buf->block);
+               TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
+               free(jbd_buf);
+       }
+
+       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, tmp) {
+               struct tag_info tag_info;
+               bool uuid_exist = false;
+               if (!ext4_bcache_test_flag(jbd_buf->block.buf,
+                                          BC_DIRTY)) {
+                       TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+                                       jbd_buf,
+                                       dirty_buf_node);
+
+                       jbd_buf->block.buf->end_write = NULL;
+                       jbd_buf->block.buf->end_write_arg = NULL;
+                       jbd_trans_finish_callback(journal,
+                                       trans,
+                                       jbd_buf->block_rec,
+                                       true);
+
+                       /* The buffer has not been modified, just release
+                        * that jbd_buf. */
+                       jbd_trans_remove_block_rec(journal,
+                                       jbd_buf->block_rec, trans);
+                       trans->data_cnt--;
+
+                       ext4_block_set(fs->bdev, &jbd_buf->block);
+                       TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
+                       free(jbd_buf);
+                       continue;
+               }
+               checksum = jbd_block_csum(journal->jbd_fs,
+                                         jbd_buf->block.data,
+                                         checksum,
+                                         trans->trans_id);
+again:
+               if (!desc_iblock) {
+                       struct jbd_bhdr *bhdr;
+                       desc_iblock = jbd_journal_alloc_block(journal, trans);
+                       rc = jbd_block_get_noread(journal->jbd_fs,
+                                          &desc_block, desc_iblock);
+                       if (rc != EOK)
+                               break;
+
+                       ext4_bcache_set_dirty(desc_block.buf);
+
+                       bhdr = (struct jbd_bhdr *)desc_block.data;
+                       jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
+                       jbd_set32(bhdr, blocktype, JBD_DESCRIPTOR_BLOCK);
+                       jbd_set32(bhdr, sequence, trans->trans_id);
+
+                       tag_start = (char *)(bhdr + 1);
+                       tag_ptr = tag_start;
+                       uuid_exist = true;
+                       tag_tbl_size = journal->block_size -
+                               sizeof(struct jbd_bhdr);
+
+                       if (jbd_has_csum(&journal->jbd_fs->sb))
+                               tag_tbl_size -= sizeof(struct jbd_block_tail);
+
+                       if (!trans->start_iblock)
+                               trans->start_iblock = desc_iblock;
+
+               }
+               tag_info.block = jbd_buf->block.lb_id;
+               tag_info.uuid_exist = uuid_exist;
+               if (i == trans->data_cnt - 1)
+                       tag_info.last_tag = true;
+               else
+                       tag_info.last_tag = false;
+
+               tag_info.checksum = checksum;
+
+               if (uuid_exist)
+                       memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid,
+                                       UUID_SIZE);
+
+               rc = jbd_write_block_tag(journal->jbd_fs,
+                               tag_ptr,
+                               tag_tbl_size,
+                               &tag_info);
+               if (rc != EOK) {
+                       jbd_meta_csum_set(journal->jbd_fs,
+                                       (struct jbd_bhdr *)desc_block.data);
+                       jbd_block_set(journal->jbd_fs, &desc_block);
+                       desc_iblock = 0;
+                       goto again;
+               }
+
+               data_iblock = jbd_journal_alloc_block(journal, trans);
+               rc = jbd_block_get_noread(journal->jbd_fs,
+                               &data_block, data_iblock);
+               if (rc != EOK)
+                       break;
+
+               ext4_bcache_set_dirty(data_block.buf);
+
+               memcpy(data_block.data, jbd_buf->block.data,
+                       journal->block_size);
+               jbd_buf->jbd_lba = data_block.lb_id;
+
+               rc = jbd_block_set(journal->jbd_fs, &data_block);
+               if (rc != EOK)
+                       break;
+
+               tag_ptr += tag_info.tag_bytes;
+               tag_tbl_size -= tag_info.tag_bytes;
+
+               i++;
+       }
+       if (rc == EOK && desc_iblock) {
+               jbd_meta_csum_set(journal->jbd_fs,
+                               (struct jbd_bhdr *)desc_block.data);
+               trans->data_csum = checksum;
+               jbd_block_set(journal->jbd_fs, &desc_block);
+       }
+
+       return rc;
+}
+
+/**@brief  Write revoke block for a transaction
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @return standard error code*/
+static int
+jbd_journal_prepare_revoke(struct jbd_journal *journal,
+                          struct jbd_trans *trans)
+{
+       int rc = EOK, i = 0;
+       int32_t tag_tbl_size;
+       uint32_t desc_iblock = 0;
+       char *blocks_entry = NULL;
+       struct jbd_revoke_rec *rec, *tmp;
+       struct ext4_block desc_block;
+       struct jbd_revoke_header *header = NULL;
+       int32_t record_len = 4;
+
+       if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
+                                    JBD_FEATURE_INCOMPAT_64BIT))
+               record_len = 8;
+
+       LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
+                         tmp) {
+again:
+               if (!desc_iblock) {
+                       struct jbd_bhdr *bhdr;
+                       desc_iblock = jbd_journal_alloc_block(journal, trans);
+                       rc = jbd_block_get_noread(journal->jbd_fs,
+                                          &desc_block, desc_iblock);
+                       if (rc != EOK) {
+                               break;
+                       }
+
+                       ext4_bcache_set_dirty(desc_block.buf);
+
+                       bhdr = (struct jbd_bhdr *)desc_block.data;
+                       jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
+                       jbd_set32(bhdr, blocktype, JBD_REVOKE_BLOCK);
+                       jbd_set32(bhdr, sequence, trans->trans_id);
+                       
+                       header = (struct jbd_revoke_header *)bhdr;
+                       blocks_entry = (char *)(header + 1);
+                       tag_tbl_size = journal->block_size -
+                               sizeof(struct jbd_revoke_header);
+
+                       if (jbd_has_csum(&journal->jbd_fs->sb))
+                               tag_tbl_size -= sizeof(struct jbd_block_tail);
+
+                       if (!trans->start_iblock)
+                               trans->start_iblock = desc_iblock;
+
+               }
+
+               if (tag_tbl_size < record_len) {
+                       jbd_set32(header, count,
+                                 journal->block_size - tag_tbl_size);
+                       jbd_meta_csum_set(journal->jbd_fs,
+                                       (struct jbd_bhdr *)desc_block.data);
+                       jbd_block_set(journal->jbd_fs, &desc_block);
+                       desc_iblock = 0;
+                       header = NULL;
+                       goto again;
+               }
+               if (record_len == 8) {
+                       uint64_t *blocks =
+                               (uint64_t *)blocks_entry;
+                       *blocks = to_be64(rec->lba);
+               } else {
+                       uint32_t *blocks =
+                               (uint32_t *)blocks_entry;
+                       *blocks = to_be32((uint32_t)rec->lba);
+               }
+               blocks_entry += record_len;
+               tag_tbl_size -= record_len;
+
+               i++;
+       }
+       if (rc == EOK && desc_iblock) {
+               if (header != NULL)
+                       jbd_set32(header, count,
+                                 journal->block_size - tag_tbl_size);
+
+               jbd_meta_csum_set(journal->jbd_fs,
+                               (struct jbd_bhdr *)desc_block.data);
+               jbd_block_set(journal->jbd_fs, &desc_block);
+       }
+
+       return rc;
+}
+
+/**@brief  Put references of block descriptors in a transaction.
+ * @param  journal current journal session
+ * @param  trans transaction*/
+void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans)
+{
+       struct jbd_buf *jbd_buf, *tmp;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
+                       tmp) {
+               struct ext4_block block = jbd_buf->block;
+               ext4_block_set(fs->bdev, &block);
+       }
+}
+
+/**@brief  Update the start block of the journal when
+ *         all the contents in a transaction reach the disk.*/
+static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
+                         struct ext4_buf *buf,
+                         int res,
+                         void *arg)
+{
+       struct jbd_buf *jbd_buf = arg;
+       struct jbd_trans *trans = jbd_buf->trans;
+       struct jbd_block_rec *block_rec = jbd_buf->block_rec;
+       struct jbd_journal *journal = trans->journal;
+       bool first_in_queue =
+               trans == TAILQ_FIRST(&journal->cp_queue);
+       if (res != EOK)
+               trans->error = res;
+
+       TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
+       TAILQ_REMOVE(&block_rec->dirty_buf_queue,
+                       jbd_buf,
+                       dirty_buf_node);
+
+       jbd_trans_finish_callback(journal,
+                       trans,
+                       jbd_buf->block_rec,
+                       false);
+       if (block_rec->trans == trans) {
+               block_rec->buf = NULL;
+               /* Clear the end_write and end_write_arg fields. */
+               buf->end_write = NULL;
+               buf->end_write_arg = NULL;
+       }
+
+       free(jbd_buf);
+
+       trans->written_cnt++;
+       if (trans->written_cnt == trans->data_cnt) {
+               /* If it is the first transaction on checkpoint queue,
+                * we will shift the start of the journal to the next
+                * transaction, and remove subsequent written
+                * transactions from checkpoint queue until we find
+                * an unwritten one. */
+               if (first_in_queue) {
+                       journal->start = trans->start_iblock +
+                               trans->alloc_blocks;
+                       wrap(&journal->jbd_fs->sb, journal->start);
+                       journal->trans_id = trans->trans_id + 1;
+                       TAILQ_REMOVE(&journal->cp_queue, trans, trans_node);
+                       jbd_journal_free_trans(journal, trans, false);
+
+                       jbd_journal_purge_cp_trans(journal, false);
+                       jbd_journal_write_sb(journal);
+                       jbd_write_sb(journal->jbd_fs);
+               }
+       }
+}
+
+/**@brief  Commit a transaction to the journal immediately.
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @return standard error code*/
+int jbd_journal_commit_trans(struct jbd_journal *journal,
+                            struct jbd_trans *trans)
+{
+       int rc = EOK;
+       uint32_t last = journal->last;
+
+       trans->trans_id = journal->alloc_trans_id;
+       rc = jbd_journal_prepare(journal, trans);
+       if (rc != EOK)
+               goto Finish;
+
+       rc = jbd_journal_prepare_revoke(journal, trans);
+       if (rc != EOK)
+               goto Finish;
+
+       if (TAILQ_EMPTY(&trans->buf_queue) &&
+           LIST_EMPTY(&trans->revoke_list)) {
+               /* Since there are no entries in both buffer list
+                * and revoke entry list, we do not consider trans as
+                * complete transaction and just return EOK.*/
+               jbd_journal_free_trans(journal, trans, false);
+               goto Finish;
+       }
+
+       rc = jbd_trans_write_commit_block(trans);
+       if (rc != EOK)
+               goto Finish;
+
+       journal->alloc_trans_id++;
+       if (TAILQ_EMPTY(&journal->cp_queue)) {
+               if (trans->data_cnt) {
+                       journal->start = trans->start_iblock;
+                       wrap(&journal->jbd_fs->sb, journal->start);
+                       journal->trans_id = trans->trans_id;
+                       jbd_journal_write_sb(journal);
+                       jbd_write_sb(journal->jbd_fs);
+                       TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
+                                       trans_node);
+                       jbd_journal_cp_trans(journal, trans);
+               } else {
+                       journal->start = trans->start_iblock +
+                               trans->alloc_blocks;
+                       wrap(&journal->jbd_fs->sb, journal->start);
+                       journal->trans_id = trans->trans_id + 1;
+                       jbd_journal_write_sb(journal);
+                       jbd_journal_free_trans(journal, trans, false);
+               }
+       } else {
+               TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
+                               trans_node);
+               if (trans->data_cnt)
+                       jbd_journal_cp_trans(journal, trans);
+
+       }
+Finish:
+       if (rc != EOK) {
+               journal->last = last;
+               jbd_journal_free_trans(journal, trans, true);
+       }
+       return rc;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_mbr.c b/src/ext4_mbr.c
new file mode 100644 (file)
index 0000000..52df852
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015 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_mbr.c
+ * @brief Master boot record parser
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_debug.h"
+#include "ext4_mbr.h"
+
+#include <inttypes.h>
+#include <string.h>
+
+#define MBR_SIGNATURE 0xAA55
+
+#pragma pack(push, 1)
+
+struct ext4_part_entry {
+       uint8_t status;
+       uint8_t chs1[3];
+       uint8_t type;
+       uint8_t chs2[3];
+       uint32_t first_lba;
+       uint32_t sectors;
+};
+
+struct ext4_mbr {
+       uint8_t bootstrap[446];
+       struct ext4_part_entry part_entry[4];
+       uint16_t signature;
+};
+
+#pragma pack(pop)
+
+int ext4_mbr_scan(struct ext4_blockdev *parent, struct ext4_mbr_bdevs *bdevs)
+{
+       int r;
+       size_t i;
+
+       ext4_dbg(DEBUG_MBR, DBG_INFO "ext4_mbr_scan\n");
+       memset(bdevs, 0, sizeof(struct ext4_mbr_bdevs));
+       r = ext4_block_init(parent);
+       if (r != EOK)
+               return r;
+
+       r = ext4_block_readbytes(parent, 0, parent->bdif->ph_bbuf, 512);
+       if (r != EOK) {
+               goto blockdev_fini;
+       }
+
+       const struct ext4_mbr *mbr = (void *)parent->bdif->ph_bbuf;
+
+       if (to_le16(mbr->signature) != MBR_SIGNATURE) {
+               ext4_dbg(DEBUG_MBR, DBG_ERROR "ext4_mbr_scan: unknown "
+                        "signature: 0x%x\n", to_le16(mbr->signature));
+               r = ENOENT;
+               goto blockdev_fini;
+       }
+
+       /*Show bootstrap code*/
+       ext4_dbg(DEBUG_MBR, "mbr_part: bootstrap:");
+       for (i = 0; i < sizeof(mbr->bootstrap); ++i) {
+               if (!(i & 0xF))
+                               ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "\n");
+               ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "%02x, ", mbr->bootstrap[i]);
+       }
+
+       ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "\n\n");
+       for (i = 0; i < 4; ++i) {
+               const struct ext4_part_entry *pe = &mbr->part_entry[i];
+               ext4_dbg(DEBUG_MBR, "mbr_part: %d\n", (int)i);
+               ext4_dbg(DEBUG_MBR, "\tstatus: 0x%x\n", pe->status);
+               ext4_dbg(DEBUG_MBR, "\ttype 0x%x:\n", pe->type);
+               ext4_dbg(DEBUG_MBR, "\tfirst_lba: 0x%"PRIx32"\n", pe->first_lba);
+               ext4_dbg(DEBUG_MBR, "\tsectors: 0x%"PRIx32"\n", pe->sectors);
+
+               if (!pe->sectors)
+                       continue; /*Empty entry*/
+
+               if (pe->type != 0x83)
+                       continue; /*Unsupported entry. 0x83 - linux native*/
+
+               bdevs->partitions[i].bdif = parent->bdif;
+               bdevs->partitions[i].part_offset =
+                       (uint64_t)pe->first_lba * parent->bdif->ph_bsize;
+               bdevs->partitions[i].part_size =
+                       (uint64_t)pe->sectors * parent->bdif->ph_bsize;
+       }
+
+       blockdev_fini:
+       ext4_block_fini(parent);
+       return r;
+}
+
+/**
+ * @}
+ */
+
diff --git a/src/ext4_mkfs.c b/src/ext4_mkfs.c
new file mode 100644 (file)
index 0000000..5712ef3
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ * Copyright (c) 2015 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_mkfs.c
+ * @brief
+ */
+
+#include "ext4_config.h"
+#include "ext4_super.h"
+#include "ext4_block_group.h"
+#include "ext4_dir.h"
+#include "ext4_dir_idx.h"
+#include "ext4_fs.h"
+#include "ext4_inode.h"
+#include "ext4_debug.h"
+#include "ext4_ialloc.h"
+#include "ext4_mkfs.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define EXT4_ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+
+struct fs_aux_info {
+       struct ext4_sblock *sb;
+       struct ext4_bgroup *bg_desc;
+       struct xattr_list_element *xattrs;
+       uint32_t first_data_block;
+       uint64_t len_blocks;
+       uint32_t inode_table_blocks;
+       uint32_t groups;
+       uint32_t bg_desc_blocks;
+       uint32_t default_i_flags;
+       uint32_t blocks_per_ind;
+       uint32_t blocks_per_dind;
+       uint32_t blocks_per_tind;
+};
+
+static inline int log_2(int j)
+{
+       int i;
+
+       for (i = 0; j > 0; i++)
+               j >>= 1;
+
+       return i - 1;
+}
+
+static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info)
+{
+        if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC)
+                return EINVAL;
+
+       info->block_size = 1024 << to_le32(sb->log_block_size);
+       info->blocks_per_group = to_le32(sb->blocks_per_group);
+       info->inodes_per_group = to_le32(sb->inodes_per_group);
+       info->inode_size = to_le16(sb->inode_size);
+       info->inodes = to_le32(sb->inodes_count);
+       info->feat_ro_compat = to_le32(sb->features_read_only);
+       info->feat_compat = to_le32(sb->features_compatible);
+       info->feat_incompat = to_le32(sb->features_incompatible);
+       info->bg_desc_reserve_blocks = to_le16(sb->s_reserved_gdt_blocks);
+       info->label = sb->volume_name;
+       info->len = (uint64_t)info->block_size * ext4_sb_get_blocks_cnt(sb);
+       info->dsc_size = to_le16(sb->desc_size);
+
+       return EOK;
+}
+
+static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info)
+{
+       return info->block_size * 8;
+}
+
+static uint32_t compute_inodes(struct ext4_mkfs_info *info)
+{
+       return (uint32_t)DIV_ROUND_UP(info->len, info->block_size) / 4;
+}
+
+static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info)
+{
+       uint32_t blocks = (uint32_t)DIV_ROUND_UP(info->len, info->block_size);
+       uint32_t block_groups = DIV_ROUND_UP(blocks, info->blocks_per_group);
+       uint32_t inodes = DIV_ROUND_UP(info->inodes, block_groups);
+       inodes = EXT4_ALIGN(inodes, (info->block_size / info->inode_size));
+
+       /* After properly rounding up the number of inodes/group,
+        * make sure to update the total inodes field in the info struct.
+        */
+       info->inodes = inodes * block_groups;
+
+       return inodes;
+}
+
+
+static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info)
+{
+       uint32_t journal_blocks = (uint32_t)DIV_ROUND_UP(info->len,
+                                                info->block_size) / 64;
+       if (journal_blocks < 1024)
+               journal_blocks = 1024;
+       if (journal_blocks > 32768)
+               journal_blocks = 32768;
+       return journal_blocks;
+}
+
+static bool has_superblock(struct ext4_mkfs_info *info, uint32_t bgid)
+{
+       if (!(info->feat_ro_compat & EXT4_FRO_COM_SPARSE_SUPER))
+               return true;
+
+       return ext4_sb_sparse(bgid);
+}
+
+static int create_fs_aux_info(struct fs_aux_info *aux_info,
+                             struct ext4_mkfs_info *info)
+{
+       aux_info->first_data_block = (info->block_size > 1024) ? 0 : 1;
+       aux_info->len_blocks = info->len / info->block_size;
+       aux_info->inode_table_blocks = DIV_ROUND_UP(info->inodes_per_group *
+                       info->inode_size, info->block_size);
+       aux_info->groups = (uint32_t)DIV_ROUND_UP(aux_info->len_blocks -
+                       aux_info->first_data_block, info->blocks_per_group);
+       aux_info->blocks_per_ind = info->block_size / sizeof(uint32_t);
+       aux_info->blocks_per_dind =
+                       aux_info->blocks_per_ind * aux_info->blocks_per_ind;
+       aux_info->blocks_per_tind =
+                       aux_info->blocks_per_dind * aux_info->blocks_per_dind;
+
+       aux_info->bg_desc_blocks =
+               DIV_ROUND_UP(aux_info->groups * info->dsc_size,
+                       info->block_size);
+
+       aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME;
+
+       uint32_t last_group_size = aux_info->len_blocks % info->blocks_per_group;
+       uint32_t last_header_size = 2 + aux_info->inode_table_blocks;
+       if (has_superblock(info, aux_info->groups - 1))
+               last_header_size += 1 + aux_info->bg_desc_blocks +
+                       info->bg_desc_reserve_blocks;
+
+       if (last_group_size > 0 && last_group_size < last_header_size) {
+               aux_info->groups--;
+               aux_info->len_blocks -= last_group_size;
+       }
+
+       aux_info->sb = calloc(1, EXT4_SUPERBLOCK_SIZE);
+       if (!aux_info->sb)
+               return ENOMEM;
+
+       aux_info->bg_desc = calloc(aux_info->groups, sizeof(struct ext4_bgroup));
+       if (!aux_info->bg_desc)
+               return ENOMEM;
+
+       aux_info->xattrs = NULL;
+
+
+       ext4_dbg(DEBUG_MKFS, DBG_INFO "create_fs_aux_info\n");
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "first_data_block: %"PRIu32"\n",
+                       aux_info->first_data_block);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "len_blocks: %"PRIu64"\n",
+                       aux_info->len_blocks);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "inode_table_blocks: %"PRIu32"\n",
+                       aux_info->inode_table_blocks);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "groups: %"PRIu32"\n",
+                       aux_info->groups);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "bg_desc_blocks: %"PRIu32"\n",
+                       aux_info->bg_desc_blocks);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "default_i_flags: %"PRIu32"\n",
+                       aux_info->default_i_flags);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_ind: %"PRIu32"\n",
+                       aux_info->blocks_per_ind);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_dind: %"PRIu32"\n",
+                       aux_info->blocks_per_dind);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_tind: %"PRIu32"\n",
+                       aux_info->blocks_per_tind);
+
+       return EOK;
+}
+
+static void release_fs_aux_info(struct fs_aux_info *aux_info)
+{
+       if (aux_info->sb)
+               free(aux_info->sb);
+       if (aux_info->bg_desc)
+               free(aux_info->bg_desc);
+}
+
+
+/* Fill in the superblock memory buffer based on the filesystem parameters */
+static void fill_in_sb(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info)
+{
+       struct ext4_sblock *sb = aux_info->sb;
+
+       sb->inodes_count = to_le32(info->inodes_per_group * aux_info->groups);
+
+       ext4_sb_set_blocks_cnt(sb, aux_info->len_blocks);
+       ext4_sb_set_free_blocks_cnt(sb, aux_info->len_blocks);
+       sb->free_inodes_count = to_le32(info->inodes_per_group * aux_info->groups);
+
+       sb->reserved_blocks_count_lo = to_le32(0);
+       sb->first_data_block = to_le32(aux_info->first_data_block);
+       sb->log_block_size = to_le32(log_2(info->block_size / 1024));
+       sb->log_cluster_size = to_le32(log_2(info->block_size / 1024));
+       sb->blocks_per_group = to_le32(info->blocks_per_group);
+       sb->frags_per_group = to_le32(info->blocks_per_group);
+       sb->inodes_per_group = to_le32(info->inodes_per_group);
+       sb->mount_time = to_le32(0);
+       sb->write_time = to_le32(0);
+       sb->mount_count = to_le16(0);
+       sb->max_mount_count = to_le16(0xFFFF);
+       sb->magic = to_le16(EXT4_SUPERBLOCK_MAGIC);
+       sb->state = to_le16(EXT4_SUPERBLOCK_STATE_VALID_FS);
+       sb->errors = to_le16(EXT4_SUPERBLOCK_ERRORS_RO);
+       sb->minor_rev_level = to_le16(0);
+       sb->last_check_time = to_le32(0);
+       sb->check_interval = to_le32(0);
+       sb->creator_os = to_le32(EXT4_SUPERBLOCK_OS_LINUX);
+       sb->rev_level = to_le32(1);
+       sb->def_resuid = to_le16(0);
+       sb->def_resgid = to_le16(0);
+
+       sb->first_inode = to_le32(EXT4_GOOD_OLD_FIRST_INO);
+       sb->inode_size = to_le16(info->inode_size);
+       sb->block_group_index = to_le16(0);
+
+       sb->features_compatible = to_le32(info->feat_compat);
+       sb->features_incompatible = to_le32(info->feat_incompat);
+       sb->features_read_only = to_le32(info->feat_ro_compat);
+
+       memset(sb->uuid, 0, sizeof(sb->uuid));
+
+       memset(sb->volume_name, 0, sizeof(sb->volume_name));
+       strncpy(sb->volume_name, info->label, sizeof(sb->volume_name));
+       memset(sb->last_mounted, 0, sizeof(sb->last_mounted));
+
+       sb->algorithm_usage_bitmap = to_le32(0);
+       sb->s_prealloc_blocks = 0;
+       sb->s_prealloc_dir_blocks = 0;
+       sb->s_reserved_gdt_blocks = to_le16(info->bg_desc_reserve_blocks);
+
+       if (info->feat_compat & EXT4_FCOM_HAS_JOURNAL)
+               sb->journal_inode_number = to_le32(EXT4_JOURNAL_INO);
+       sb->journal_dev = to_le32(0);
+       sb->last_orphan = to_le32(0);
+       sb->hash_seed[0] = to_le32(0x11111111);
+       sb->hash_seed[1] = to_le32(0x22222222);
+       sb->hash_seed[2] = to_le32(0x33333333);
+       sb->hash_seed[3] = to_le32(0x44444444);
+       sb->default_hash_version = EXT2_HTREE_HALF_MD4;
+       sb->checksum_type = 1;
+       sb->desc_size = to_le16(info->dsc_size);
+       sb->default_mount_opts = to_le32(0);
+       sb->first_meta_bg = to_le32(0);
+       sb->mkfs_time = to_le32(0);
+
+       sb->reserved_blocks_count_hi = to_le32(0);
+       sb->min_extra_isize = to_le32(sizeof(struct ext4_inode) -
+               EXT4_GOOD_OLD_INODE_SIZE);
+       sb->want_extra_isize = to_le32(sizeof(struct ext4_inode) -
+               EXT4_GOOD_OLD_INODE_SIZE);
+       sb->flags = to_le32(EXT4_SUPERBLOCK_FLAGS_SIGNED_HASH);
+}
+
+static void fill_bgroups(struct fs_aux_info *aux_info,
+                        struct ext4_mkfs_info *info)
+{
+       uint32_t i;
+
+       uint32_t bg_free_blk = 0;
+       uint64_t sb_free_blk = 0;
+
+       for (i = 0; i < aux_info->groups; i++) {
+
+               uint64_t bg_start_block = aux_info->first_data_block +
+                       aux_info->first_data_block + i * info->blocks_per_group;
+               uint32_t blk_off = 0;
+
+               bg_free_blk = info->blocks_per_group -
+                               aux_info->inode_table_blocks;
+
+               bg_free_blk -= 2;
+               blk_off += aux_info->bg_desc_blocks;
+
+               if (i == (aux_info->groups - 1))
+                       bg_free_blk -= aux_info->first_data_block;
+
+               if (has_superblock(info, i)) {
+                       bg_start_block++;
+                       blk_off += info->bg_desc_reserve_blocks;
+                       bg_free_blk -= info->bg_desc_reserve_blocks + 1;
+                       bg_free_blk -= aux_info->bg_desc_blocks;
+               }
+
+               ext4_bg_set_block_bitmap(&aux_info->bg_desc[i], aux_info->sb,
+                               bg_start_block + blk_off + 1);
+
+               ext4_bg_set_inode_bitmap(&aux_info->bg_desc[i], aux_info->sb,
+                               bg_start_block + blk_off + 2);
+
+               ext4_bg_set_inode_table_first_block(&aux_info->bg_desc[i],
+                               aux_info->sb,
+                               bg_start_block + blk_off + 3);
+
+               ext4_bg_set_free_blocks_count(&aux_info->bg_desc[i],
+                               aux_info->sb, bg_free_blk);
+
+               ext4_bg_set_free_inodes_count(&aux_info->bg_desc[i],
+                               aux_info->sb, aux_info->sb->inodes_per_group);
+
+               ext4_bg_set_used_dirs_count(&aux_info->bg_desc[i], aux_info->sb,
+                                           0);
+
+               ext4_bg_set_flag(&aux_info->bg_desc[i],
+                               EXT4_BLOCK_GROUP_BLOCK_UNINIT |
+                               EXT4_BLOCK_GROUP_INODE_UNINIT);
+
+               sb_free_blk += bg_free_blk;
+       }
+
+       ext4_sb_set_free_blocks_cnt(aux_info->sb, sb_free_blk);
+}
+
+
+static int write_bgroups(struct ext4_blockdev *bd, struct fs_aux_info *aux_info,
+                        struct ext4_mkfs_info *info)
+{
+       int r;
+       uint32_t i;
+       struct ext4_block b;
+       for (i = 0; i < aux_info->groups; i++) {
+               uint64_t bg_start_block = aux_info->first_data_block +
+                       + i * info->blocks_per_group;
+               uint32_t blk_off = 0;
+
+               blk_off += aux_info->bg_desc_blocks;
+               if (has_superblock(info, i)) {
+                       bg_start_block++;
+                       blk_off += info->bg_desc_reserve_blocks;
+               }
+
+               uint32_t block_size = ext4_sb_get_block_size(aux_info->sb);
+               uint32_t dsc_pos = 0;
+               uint32_t dsc_id = 0;
+               uint32_t dsc_size = ext4_sb_get_desc_size(aux_info->sb);
+               uint32_t dsc_blk_cnt = aux_info->bg_desc_blocks;
+               uint64_t dsc_blk = bg_start_block;
+
+               while (dsc_blk_cnt--) {
+                       r = ext4_block_get(bd, &b, dsc_blk++);
+                       if (r != EOK)
+                               return r;
+
+                       dsc_pos = 0;
+                       while (dsc_pos + dsc_size <= block_size) {
+                               memcpy(b.data + dsc_pos,
+                                      &aux_info->bg_desc[dsc_id],
+                                      dsc_size);
+
+                               dsc_pos += dsc_size;
+                               dsc_id++;
+
+                               if (dsc_id == aux_info->groups)
+                                       break;
+                       }
+
+                       ext4_bcache_set_dirty(b.buf);
+                       r = ext4_block_set(bd, &b);
+                       if (r != EOK)
+                               return r;
+
+                       if (dsc_id == aux_info->groups)
+                               break;
+               }
+
+               r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 1);
+               if (r != EOK)
+                       return r;
+               memset(b.data, 0, block_size);
+               ext4_bcache_set_dirty(b.buf);
+               r = ext4_block_set(bd, &b);
+               if (r != EOK)
+                       return r;
+               r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 2);
+               if (r != EOK)
+                       return r;
+               memset(b.data, 0, block_size);
+               ext4_bcache_set_dirty(b.buf);
+               r = ext4_block_set(bd, &b);
+               if (r != EOK)
+                       return r;
+       }
+
+
+       return r;
+}
+
+static int write_sblocks(struct ext4_blockdev *bd, struct fs_aux_info *aux_info,
+                         struct ext4_mkfs_info *info)
+{
+       uint64_t offset;
+       uint32_t i;
+       int r;
+
+       /* write out the backup superblocks */
+       for (i = 1; i < aux_info->groups; i++) {
+               if (has_superblock(info, i)) {
+                       offset = info->block_size * (aux_info->first_data_block
+                               + i * info->blocks_per_group);
+
+                       aux_info->sb->block_group_index = to_le16(i);
+                       r = ext4_block_writebytes(bd, offset, aux_info->sb,
+                                                 EXT4_SUPERBLOCK_SIZE);
+                       if (r != EOK)
+                               return r;
+               }
+       }
+
+       /* write out the primary superblock */
+       aux_info->sb->block_group_index = to_le16(0);
+       return ext4_block_writebytes(bd, 1024, aux_info->sb,
+                       EXT4_SUPERBLOCK_SIZE);
+}
+
+
+int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
+{
+       int r;
+       struct ext4_sblock *sb = NULL;
+       r = ext4_block_init(bd);
+       if (r != EOK)
+               return r;
+
+       sb = malloc(EXT4_SUPERBLOCK_SIZE);
+       if (!sb)
+               goto Finish;
+
+
+       r = ext4_sb_read(bd, sb);
+       if (r != EOK)
+               goto Finish;
+
+       r = sb2info(sb, info);
+
+Finish:
+       if (sb)
+               free(sb);
+       ext4_block_fini(bd);
+       return r;
+}
+
+static int mkfs_initial(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
+{
+       int r;
+       struct fs_aux_info aux_info;
+       memset(&aux_info, 0, sizeof(struct fs_aux_info));
+
+       r = create_fs_aux_info(&aux_info, info);
+       if (r != EOK)
+               goto Finish;
+
+       fill_in_sb(&aux_info, info);
+       fill_bgroups(&aux_info, info);
+
+
+       r = write_bgroups(bd, &aux_info, info);
+       if (r != EOK)
+               goto Finish;
+
+       r = write_sblocks(bd, &aux_info, info);
+       if (r != EOK)
+               goto Finish;
+
+       Finish:
+       release_fs_aux_info(&aux_info);
+       return r;
+}
+
+static int init_bgs(struct ext4_fs *fs)
+{
+       int r = EOK;
+       struct ext4_block_group_ref ref;
+       uint32_t i;
+       uint32_t bg_count = ext4_block_group_cnt(&fs->sb);
+       for (i = 0; i < bg_count; ++i) {
+               r = ext4_fs_get_block_group_ref(fs, i, &ref);
+               if (r != EOK)
+                       break;
+
+               r = ext4_fs_put_block_group_ref(&ref);
+               if (r != EOK)
+                       break;
+       }
+       return r;
+}
+
+static int alloc_inodes(struct ext4_fs *fs)
+{
+       int r = EOK;
+       int i;
+       struct ext4_inode_ref inode_ref;
+       for (i = 1; i < 12; ++i) {
+               int filetype = EXT4_DE_REG_FILE;
+
+               switch (i) {
+               case EXT4_ROOT_INO:
+               case EXT4_GOOD_OLD_FIRST_INO:
+                       filetype = EXT4_DE_DIR;
+                       break;
+               }
+
+               r = ext4_fs_alloc_inode(fs, &inode_ref, filetype);
+               if (r != EOK)
+                       return r;
+
+               ext4_inode_set_mode(&fs->sb, inode_ref.inode, 0);
+               ext4_fs_put_inode_ref(&inode_ref);
+       }
+
+       return r;
+}
+
+static int create_dirs(struct ext4_fs *fs)
+{
+       int r = EOK;
+       struct ext4_inode_ref root;
+       struct ext4_inode_ref child;
+
+       r = ext4_fs_get_inode_ref(fs, EXT4_ROOT_INO, &root);
+       if (r != EOK)
+               return r;
+
+       r = ext4_fs_get_inode_ref(fs, EXT4_GOOD_OLD_FIRST_INO, &child);
+       if (r != EOK)
+               return r;
+
+       ext4_inode_set_mode(&fs->sb, child.inode,
+                       EXT4_INODE_MODE_DIRECTORY | 0777);
+
+       ext4_inode_set_mode(&fs->sb, root.inode,
+                       EXT4_INODE_MODE_DIRECTORY | 0777);
+
+#if CONFIG_DIR_INDEX_ENABLE
+       /* Initialize directory index if supported */
+       if (ext4_sb_feature_com(&fs->sb, EXT4_FCOM_DIR_INDEX)) {
+               r = ext4_dir_dx_init(&root, &root);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_dir_dx_init(&child, &root);
+               if (r != EOK)
+                       return r;
+
+               ext4_inode_set_flag(root.inode, EXT4_INODE_FLAG_INDEX);
+               ext4_inode_set_flag(child.inode, EXT4_INODE_FLAG_INDEX);
+       } else
+#endif
+       {
+               r = ext4_dir_add_entry(&root, ".", strlen("."), &root);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_dir_add_entry(&root, "..", strlen(".."), &root);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_dir_add_entry(&child, ".", strlen("."), &child);
+               if (r != EOK)
+                       return r;
+
+               r = ext4_dir_add_entry(&child, "..", strlen(".."), &root);
+               if (r != EOK)
+                       return r;
+       }
+
+       r = ext4_dir_add_entry(&root, "lost+found", strlen("lost+found"), &child);
+       if (r != EOK)
+               return r;
+
+       ext4_inode_set_links_cnt(root.inode, 3);
+       ext4_inode_set_links_cnt(child.inode, 2);
+
+       child.dirty = true;
+       root.dirty = true;
+       ext4_fs_put_inode_ref(&child);
+       ext4_fs_put_inode_ref(&root);
+       return r;
+}
+
+int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd,
+             struct ext4_mkfs_info *info, int fs_type)
+{
+       int r;
+
+       r = ext4_block_init(bd);
+       if (r != EOK)
+               return r;
+
+       bd->fs = fs;
+
+       if (info->len == 0)
+               info->len = bd->bdif->ph_bcnt * bd->bdif->ph_bsize;
+
+       if (info->block_size == 0)
+               info->block_size = 4096; /*Set block size to default value*/
+
+       /* Round down the filesystem length to be a multiple of the block size */
+       info->len &= ~((uint64_t)info->block_size - 1);
+
+       if (info->journal_blocks == 0)
+               info->journal_blocks = compute_journal_blocks(info);
+
+       if (info->blocks_per_group == 0)
+               info->blocks_per_group = compute_blocks_per_group(info);
+
+       if (info->inodes == 0)
+               info->inodes = compute_inodes(info);
+
+       if (info->inode_size == 0)
+               info->inode_size = 256;
+
+       if (info->label == NULL)
+               info->label = "";
+
+       info->inodes_per_group = compute_inodes_per_group(info);
+
+       switch (fs_type) {
+       case F_SET_EXT2:
+               info->feat_compat = EXT2_SUPPORTED_FCOM;
+               info->feat_ro_compat = EXT2_SUPPORTED_FRO_COM;
+               info->feat_incompat = EXT2_SUPPORTED_FINCOM;
+               break;
+       case F_SET_EXT3:
+               info->feat_compat = EXT3_SUPPORTED_FCOM;
+               info->feat_ro_compat = EXT3_SUPPORTED_FRO_COM;
+               info->feat_incompat = EXT3_SUPPORTED_FINCOM;
+               break;
+       case F_SET_EXT4:
+               info->feat_compat = EXT4_SUPPORTED_FCOM;
+               info->feat_ro_compat = EXT4_SUPPORTED_FRO_COM;
+               info->feat_incompat = EXT4_SUPPORTED_FINCOM;
+               break;
+       }
+
+       /*TODO: handle this features*/
+       info->feat_incompat &= ~EXT4_FINCOM_META_BG;
+       info->feat_incompat &= ~EXT4_FINCOM_FLEX_BG;
+       info->feat_ro_compat &= ~EXT4_FRO_COM_METADATA_CSUM;
+
+       /*TODO: handle journal feature & inode*/
+       if (info->journal == 0)
+               info->feat_compat |= 0;
+
+       if (info->dsc_size == 0) {
+
+               if (info->feat_incompat & EXT4_FINCOM_64BIT)
+                       info->dsc_size = EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE;
+               else
+                       info->dsc_size = EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE;
+       }
+
+       info->bg_desc_reserve_blocks = 0;
+
+       ext4_dbg(DEBUG_MKFS, DBG_INFO "Creating filesystem with parameters:\n");
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Size: %"PRIu64"\n", info->len);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Block size: %"PRIu32"\n",
+                       info->block_size);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Blocks per group: %"PRIu32"\n",
+                       info->blocks_per_group);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes per group: %"PRIu32"\n",
+                       info->inodes_per_group);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Inode size: %"PRIu32"\n",
+                       info->inode_size);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes: %"PRIu32"\n", info->inodes);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Journal blocks: %"PRIu32"\n",
+                       info->journal_blocks);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Features ro_compat: 0x%x\n",
+                       info->feat_ro_compat);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Features compat: 0x%x\n",
+                       info->feat_compat);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Features incompat: 0x%x\n",
+                       info->feat_incompat);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "BG desc reserve: %"PRIu32"\n",
+                       info->bg_desc_reserve_blocks);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Descriptor size: %"PRIu16"\n",
+                       info->dsc_size);
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "journal: %s\n",
+                       info->journal ? "yes" : "no");
+       ext4_dbg(DEBUG_MKFS, DBG_NONE "Label: %s\n", info->label);
+
+       struct ext4_bcache bc;
+       memset(&bc, 0, sizeof(struct ext4_bcache));
+       ext4_block_set_lb_size(bd, info->block_size);
+       r = ext4_bcache_init_dynamic(&bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
+                                     info->block_size);
+       if (r != EOK)
+               goto block_fini;
+
+       /*Bind block cache to block device*/
+       r = ext4_block_bind_bcache(bd, &bc);
+       if (r != EOK)
+               goto cache_fini;
+
+       r = ext4_block_cache_write_back(bd, 1);
+       if (r != EOK)
+               goto cache_fini;
+
+       r = mkfs_initial(bd, info);
+       if (r != EOK)
+               goto cache_fini;
+
+       r = ext4_fs_init(fs, bd);
+       if (r != EOK)
+               goto cache_fini;
+
+       r = init_bgs(fs);
+       if (r != EOK)
+               goto fs_fini;
+
+       r = alloc_inodes(fs);
+       if (r != EOK)
+               goto fs_fini;
+
+       r = create_dirs(fs);
+       if (r != EOK)
+               goto fs_fini;
+
+       fs_fini:
+       ext4_fs_fini(fs);
+
+       cache_fini:
+       ext4_block_cache_write_back(bd, 0);
+       ext4_bcache_fini_dynamic(&bc);
+
+       block_fini:
+       ext4_block_fini(bd);
+
+       return r;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_super.c b/src/ext4_super.c
new file mode 100644 (file)
index 0000000..582d1fb
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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_super.h
+ * @brief Superblock operations.
+ */
+
+#include "ext4_config.h"
+#include "ext4_super.h"
+#include "ext4_crc32.h"
+
+uint32_t ext4_block_group_cnt(struct ext4_sblock *s)
+{
+       uint64_t blocks_count = ext4_sb_get_blocks_cnt(s);
+       uint32_t blocks_per_group = ext4_get32(s, blocks_per_group);
+
+       uint32_t block_groups_count = (uint32_t)(blocks_count / blocks_per_group);
+
+       if (blocks_count % blocks_per_group)
+               block_groups_count++;
+
+       return block_groups_count;
+}
+
+uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid)
+{
+       uint32_t block_group_count = ext4_block_group_cnt(s);
+       uint32_t blocks_per_group = ext4_get32(s, blocks_per_group);
+       uint64_t total_blocks = ext4_sb_get_blocks_cnt(s);
+
+       if (bgid < block_group_count - 1)
+               return blocks_per_group;
+
+       return (uint32_t)(total_blocks - ((block_group_count - 1) * blocks_per_group));
+}
+
+uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid)
+{
+       uint32_t block_group_count = ext4_block_group_cnt(s);
+       uint32_t inodes_per_group = ext4_get32(s, inodes_per_group);
+       uint32_t total_inodes = ext4_get32(s, inodes_count);
+
+       if (bgid < block_group_count - 1)
+               return inodes_per_group;
+
+       return (total_inodes - ((block_group_count - 1) * inodes_per_group));
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t ext4_sb_csum(struct ext4_sblock *s)
+{
+
+       return ext4_crc32c(EXT4_CRC32_INIT, s,
+                       offsetof(struct ext4_sblock, checksum));
+}
+#else
+#define ext4_sb_csum(...) 0
+#endif
+
+static bool ext4_sb_verify_csum(struct ext4_sblock *s)
+{
+       if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM))
+               return true;
+
+       if (s->checksum_type != to_le32(EXT4_CHECKSUM_CRC32C))
+               return false;
+
+       return s->checksum == to_le32(ext4_sb_csum(s));
+}
+
+static void ext4_sb_set_csum(struct ext4_sblock *s)
+{
+       if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM))
+               return;
+
+       s->checksum = to_le32(ext4_sb_csum(s));
+}
+
+int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s)
+{
+       ext4_sb_set_csum(s);
+       return ext4_block_writebytes(bdev, EXT4_SUPERBLOCK_OFFSET, s,
+                                    EXT4_SUPERBLOCK_SIZE);
+}
+
+int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s)
+{
+       return ext4_block_readbytes(bdev, EXT4_SUPERBLOCK_OFFSET, s,
+                                   EXT4_SUPERBLOCK_SIZE);
+}
+
+bool ext4_sb_check(struct ext4_sblock *s)
+{
+       if (ext4_get16(s, magic) != EXT4_SUPERBLOCK_MAGIC)
+               return false;
+
+       if (ext4_get32(s, inodes_count) == 0)
+               return false;
+
+       if (ext4_sb_get_blocks_cnt(s) == 0)
+               return false;
+
+       if (ext4_get32(s, blocks_per_group) == 0)
+               return false;
+
+       if (ext4_get32(s, inodes_per_group) == 0)
+               return false;
+
+       if (ext4_get16(s, inode_size) < 128)
+               return false;
+
+       if (ext4_get32(s, first_inode) < 11)
+               return false;
+
+       if (ext4_sb_get_desc_size(s) < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               return false;
+
+       if (ext4_sb_get_desc_size(s) > EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
+               return false;
+
+       if (!ext4_sb_verify_csum(s))
+               return false;
+
+       return true;
+}
+
+static inline int is_power_of(uint32_t a, uint32_t b)
+{
+       while (1) {
+               if (a < b)
+                       return 0;
+               if (a == b)
+                       return 1;
+               if ((a % b) != 0)
+                       return 0;
+               a = a / b;
+       }
+}
+
+bool ext4_sb_sparse(uint32_t group)
+{
+       if (group <= 1)
+               return 1;
+
+       if (!(group & 1))
+               return 0;
+
+       return (is_power_of(group, 7) || is_power_of(group, 5) ||
+               is_power_of(group, 3));
+}
+
+bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t group)
+{
+       if (ext4_sb_feature_ro_com(s, EXT4_FRO_COM_SPARSE_SUPER) &&
+           !ext4_sb_sparse(group))
+               return false;
+       return true;
+}
+
+static uint32_t ext4_bg_num_gdb_meta(struct ext4_sblock *s, uint32_t group)
+{
+       uint32_t dsc_per_block =
+           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+
+       uint32_t metagroup = group / dsc_per_block;
+       uint32_t first = metagroup * dsc_per_block;
+       uint32_t last = first + dsc_per_block - 1;
+
+       if (group == first || group == first + 1 || group == last)
+               return 1;
+       return 0;
+}
+
+static uint32_t ext4_bg_num_gdb_nometa(struct ext4_sblock *s, uint32_t group)
+{
+       if (!ext4_sb_is_super_in_bg(s, group))
+               return 0;
+       uint32_t dsc_per_block =
+           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+
+       uint32_t db_count =
+           (ext4_block_group_cnt(s) + dsc_per_block - 1) / dsc_per_block;
+
+       if (ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG))
+               return ext4_sb_first_meta_bg(s);
+
+       return db_count;
+}
+
+uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group)
+{
+       uint32_t dsc_per_block =
+           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+       uint32_t first_meta_bg = ext4_sb_first_meta_bg(s);
+       uint32_t metagroup = group / dsc_per_block;
+
+       if (!ext4_sb_feature_incom(s,EXT4_FINCOM_META_BG) ||
+           metagroup < first_meta_bg)
+               return ext4_bg_num_gdb_nometa(s, group);
+
+       return ext4_bg_num_gdb_meta(s, group);
+}
+
+uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
+                                    uint32_t block_group)
+{
+       uint32_t num;
+       uint32_t dsc_per_block =
+           ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+
+       num = ext4_sb_is_super_in_bg(s, block_group);
+
+       if (!ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG) ||
+           block_group < ext4_sb_first_meta_bg(s) * dsc_per_block) {
+               if (num) {
+                       num += ext4_bg_num_gdb(s, block_group);
+                       num += ext4_get16(s, s_reserved_gdt_blocks);
+               }
+       } else {
+               num += ext4_bg_num_gdb(s, block_group);
+       }
+
+       uint32_t clustersize = 1024 << ext4_get32(s, log_cluster_size);
+       uint32_t cluster_ratio = clustersize / ext4_sb_get_block_size(s);
+       uint32_t v =
+           (num + cluster_ratio - 1) >> ext4_get32(s, log_cluster_size);
+
+       return v;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_trans.c b/src/ext4_trans.c
new file mode 100644 (file)
index 0000000..9f32fb8
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@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_trans.c
+ * @brief Ext4 transaction buffer operations.
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_journal.h"
+
+static int ext4_trans_get_write_access(struct ext4_fs *fs __unused,
+                               struct ext4_block *block __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       if (fs->jbd_journal && fs->curr_trans) {
+               struct jbd_journal *journal = fs->jbd_journal;
+               struct jbd_trans *trans = fs->curr_trans;
+               r = jbd_trans_get_access(journal, trans, block);
+       }
+#endif
+       return r;
+}
+
+int ext4_trans_set_block_dirty(struct ext4_buf *buf)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       struct ext4_fs *fs = buf->bc->bdev->fs;
+       struct ext4_block block = {
+               .lb_id = buf->lba,
+               .data = buf->data,
+               .buf = buf
+       };
+
+       if (fs->jbd_journal && fs->curr_trans) {
+               struct jbd_trans *trans = fs->curr_trans;
+               return jbd_trans_set_block_dirty(trans, &block);
+       }
+#endif
+       ext4_bcache_set_dirty(buf);
+       return r;
+}
+
+int ext4_trans_block_get_noread(struct ext4_blockdev *bdev,
+                         struct ext4_block *b,
+                         uint64_t lba)
+{
+       int r = ext4_block_get_noread(bdev, b, lba);
+       if (r != EOK)
+               return r;
+
+       r = ext4_trans_get_write_access(bdev->fs, b);
+       if (r != EOK)
+               ext4_block_set(bdev, b);
+
+       return r;
+}
+
+int ext4_trans_block_get(struct ext4_blockdev *bdev,
+                  struct ext4_block *b,
+                  uint64_t lba)
+{
+       int r = ext4_block_get(bdev, b, lba);
+       if (r != EOK)
+               return r;
+
+       r = ext4_trans_get_write_access(bdev->fs, b);
+       if (r != EOK)
+               ext4_block_set(bdev, b);
+
+       return r;
+}
+
+int ext4_trans_try_revoke_block(struct ext4_blockdev *bdev __unused,
+                               uint64_t lba __unused)
+{
+       int r = EOK;
+#if CONFIG_JOURNALING_ENABLE
+       struct ext4_fs *fs = bdev->fs;
+       if (fs->jbd_journal && fs->curr_trans) {
+               struct jbd_trans *trans = fs->curr_trans;
+               r = jbd_trans_try_revoke_block(trans, lba);
+       } else if (fs->jbd_journal) {
+               r = ext4_block_flush_lba(fs->bdev, lba);
+       }
+#endif
+       return r;
+}
+
+/**
+ * @}
+ */
diff --git a/src/ext4_xattr.c b/src/ext4_xattr.c
new file mode 100644 (file)
index 0000000..54d457d
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
+ * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
+ *
+ * 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_xattr.c
+ * @brief Extended Attribute manipulation.
+ */
+
+#include "ext4_config.h"
+#include "ext4_types.h"
+#include "ext4_fs.h"
+#include "ext4_errno.h"
+#include "ext4_blockdev.h"
+#include "ext4_super.h"
+#include "ext4_crc32.h"
+#include "ext4_debug.h"
+#include "ext4_block_group.h"
+#include "ext4_balloc.h"
+#include "ext4_inode.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/**
+ * @file  ext4_xattr.c
+ * @brief Extended Attribute Manipulation
+ */
+
+#define NAME_HASH_SHIFT 5
+#define VALUE_HASH_SHIFT 16
+
+static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header,
+                                          struct ext4_xattr_entry *entry)
+{
+       uint32_t hash = 0;
+       char *name = EXT4_XATTR_NAME(entry);
+       int n;
+
+       for (n = 0; n < entry->e_name_len; n++) {
+               hash = (hash << NAME_HASH_SHIFT) ^
+                      (hash >> (8 * sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++;
+       }
+
+       if (entry->e_value_block == 0 && entry->e_value_size != 0) {
+               uint32_t *value =
+                   (uint32_t *)((char *)header + to_le16(entry->e_value_offs));
+               for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >>
+                        EXT4_XATTR_PAD_BITS;
+                    n; n--) {
+                       hash = (hash << VALUE_HASH_SHIFT) ^
+                              (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^
+                              to_le32(*value++);
+               }
+       }
+       entry->e_hash = to_le32(hash);
+}
+
+#define BLOCK_HASH_SHIFT 16
+
+/*
+ * ext4_xattr_rehash()
+ *
+ * Re-compute the extended attribute hash value after an entry has changed.
+ */
+static void ext4_xattr_rehash(struct ext4_xattr_header *header,
+                             struct ext4_xattr_entry *entry)
+{
+       struct ext4_xattr_entry *here;
+       uint32_t hash = 0;
+
+       ext4_xattr_compute_hash(header, entry);
+       here = EXT4_XATTR_ENTRY(header + 1);
+       while (!EXT4_XATTR_IS_LAST_ENTRY(here)) {
+               if (!here->e_hash) {
+                       /* Block is not shared if an entry's hash value == 0 */
+                       hash = 0;
+                       break;
+               }
+               hash = (hash << BLOCK_HASH_SHIFT) ^
+                      (hash >> (8 * sizeof(hash) - BLOCK_HASH_SHIFT)) ^
+                      to_le32(here->e_hash);
+               here = EXT4_XATTR_NEXT(here);
+       }
+       header->h_hash = to_le32(hash);
+}
+
+#if CONFIG_META_CSUM_ENABLE
+static uint32_t
+ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref,
+                         ext4_fsblk_t blocknr,
+                         struct ext4_xattr_header *header)
+{
+       uint32_t checksum = 0;
+       uint64_t le64_blocknr = blocknr;
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+
+       if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
+               uint32_t orig_checksum;
+
+               /* Preparation: temporarily set bg checksum to 0 */
+               orig_checksum = header->h_checksum;
+               header->h_checksum = 0;
+               /* First calculate crc32 checksum against fs uuid */
+               checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
+                               sizeof(sb->uuid));
+               /* Then calculate crc32 checksum block number */
+               checksum = ext4_crc32c(checksum, &le64_blocknr,
+                                    sizeof(le64_blocknr));
+               /* Finally calculate crc32 checksum against 
+                * the entire xattr block */
+               checksum = ext4_crc32c(checksum, header,
+                                  ext4_sb_get_block_size(sb));
+               header->h_checksum = orig_checksum;
+       }
+       return checksum;
+}
+#else
+#define ext4_xattr_block_checksum(...) 0
+#endif
+
+static void
+ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref,
+                             ext4_fsblk_t blocknr __unused,
+                             struct ext4_xattr_header *header)
+{
+       struct ext4_sblock *sb = &inode_ref->fs->sb;
+       if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
+               return;
+
+       header->h_checksum =
+               ext4_xattr_block_checksum(inode_ref, blocknr, header);
+}
+
+static int ext4_xattr_item_cmp(struct ext4_xattr_item *a,
+                              struct ext4_xattr_item *b)
+{
+       int result;
+       if (a->in_inode && !b->in_inode)
+               return -1;
+       
+       if (!a->in_inode && b->in_inode)
+               return 1;
+
+       result = a->name_index - b->name_index;
+       if (result)
+               return result;
+
+       result = a->name_len - b->name_len;
+       if (result)
+               return result;
+
+       return memcmp(a->name, b->name, a->name_len);
+}
+
+RB_GENERATE_INTERNAL(ext4_xattr_tree, ext4_xattr_item, node,
+                    ext4_xattr_item_cmp, static inline)
+
+static struct ext4_xattr_item *
+ext4_xattr_item_alloc(uint8_t name_index, const char *name, size_t name_len)
+{
+       struct ext4_xattr_item *item;
+       item = malloc(sizeof(struct ext4_xattr_item) + name_len);
+       if (!item)
+               return NULL;
+
+       item->name_index = name_index;
+       item->name = (char *)(item + 1);
+       item->name_len = name_len;
+       item->data = NULL;
+       item->data_size = 0;
+
+       memset(&item->node, 0, sizeof(item->node));
+       memcpy(item->name, name, name_len);
+
+       if (name_index == EXT4_XATTR_INDEX_SYSTEM &&
+           name_len == 4 &&
+           !memcmp(name, "data", 4))
+               item->in_inode = true;
+       else
+               item->in_inode = false;
+
+       return item;
+}
+
+static int ext4_xattr_item_alloc_data(struct ext4_xattr_item *item,
+                                     const void *orig_data, size_t data_size)
+{
+       void *data = NULL;
+       ext4_assert(!item->data);
+       data = malloc(data_size);
+       if (!data)
+               return ENOMEM;
+
+       if (orig_data)
+               memcpy(data, orig_data, data_size);
+
+       item->data = data;
+       item->data_size = data_size;
+       return EOK;
+}
+
+static void ext4_xattr_item_free_data(struct ext4_xattr_item *item)
+{
+       ext4_assert(item->data);
+       free(item->data);
+       item->data = NULL;
+       item->data_size = 0;
+}
+
+static int ext4_xattr_item_resize_data(struct ext4_xattr_item *item,
+                                      size_t new_data_size)
+{
+       if (new_data_size != item->data_size) {
+               void *new_data;
+               new_data = realloc(item->data, new_data_size);
+               if (!new_data)
+                       return ENOMEM;
+
+               item->data = new_data;
+               item->data_size = new_data_size;
+       }
+       return EOK;
+}
+
+static void ext4_xattr_item_free(struct ext4_xattr_item *item)
+{
+       if (item->data)
+               ext4_xattr_item_free_data(item);
+
+       free(item);
+}
+
+static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref,
+                                  struct ext4_xattr_entry *entry,
+                                  bool in_inode)
+{
+       char *ret;
+       if (in_inode) {
+               struct ext4_xattr_ibody_header *header;
+               struct ext4_xattr_entry *first_entry;
+               int16_t inode_size =
+                   ext4_get16(&xattr_ref->fs->sb, inode_size);
+               header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
+               first_entry = EXT4_XATTR_IFIRST(header);
+
+               ret = ((char *)first_entry + to_le16(entry->e_value_offs));
+               if (ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size)) -
+                       (char *)xattr_ref->inode_ref->inode > inode_size)
+                       ret = NULL;
+
+               return ret;
+
+       }
+       int32_t block_size = ext4_sb_get_block_size(&xattr_ref->fs->sb);
+       ret = ((char *)xattr_ref->block.data + to_le16(entry->e_value_offs));
+       if (ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size)) -
+                       (char *)xattr_ref->block.data > block_size)
+               ret = NULL;
+       return ret;
+}
+
+static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref)
+{
+       int ret = EOK;
+       size_t size_rem;
+       void *data;
+       struct ext4_xattr_entry *entry = NULL;
+
+       ext4_assert(xattr_ref->block.data);
+       entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
+
+       size_rem = ext4_sb_get_block_size(&xattr_ref->fs->sb);
+       for (; size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
+            entry = EXT4_XATTR_NEXT(entry),
+            size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
+               struct ext4_xattr_item *item;
+               char *e_name = EXT4_XATTR_NAME(entry);
+
+               data = ext4_xattr_entry_data(xattr_ref, entry, false);
+               if (!data) {
+                       ret = EIO;
+                       goto Finish;
+               }
+
+               item = ext4_xattr_item_alloc(entry->e_name_index, e_name,
+                                            (size_t)entry->e_name_len);
+               if (!item) {
+                       ret = ENOMEM;
+                       goto Finish;
+               }
+               if (ext4_xattr_item_alloc_data(
+                       item, data, to_le32(entry->e_value_size)) != EOK) {
+                       ext4_xattr_item_free(item);
+                       ret = ENOMEM;
+                       goto Finish;
+               }
+               RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
+               xattr_ref->ea_size += EXT4_XATTR_SIZE(item->data_size) +
+                                     EXT4_XATTR_LEN(item->name_len);
+       }
+
+Finish:
+       return ret;
+}
+
+static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref)
+{
+       void *data;
+       size_t size_rem;
+       int ret = EOK;
+       struct ext4_xattr_ibody_header *header = NULL;
+       struct ext4_xattr_entry *entry = NULL;
+       uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
+
+       header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
+       entry = EXT4_XATTR_IFIRST(header);
+
+       size_rem = inode_size - EXT4_GOOD_OLD_INODE_SIZE -
+                  xattr_ref->inode_ref->inode->extra_isize;
+       for (; size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
+            entry = EXT4_XATTR_NEXT(entry),
+            size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
+               struct ext4_xattr_item *item;
+               char *e_name = EXT4_XATTR_NAME(entry);
+
+               data = ext4_xattr_entry_data(xattr_ref, entry, true);
+               if (!data) {
+                       ret = EIO;
+                       goto Finish;
+               }
+
+               item = ext4_xattr_item_alloc(entry->e_name_index, e_name,
+                                            (size_t)entry->e_name_len);
+               if (!item) {
+                       ret = ENOMEM;
+                       goto Finish;
+               }
+               if (ext4_xattr_item_alloc_data(
+                       item, data, to_le32(entry->e_value_size)) != EOK) {
+                       ext4_xattr_item_free(item);
+                       ret = ENOMEM;
+                       goto Finish;
+               }
+               RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
+               xattr_ref->ea_size += EXT4_XATTR_SIZE(item->data_size) +
+                                     EXT4_XATTR_LEN(item->name_len);
+       }
+
+Finish:
+       return ret;
+}
+
+static size_t ext4_xattr_inode_space(struct ext4_xattr_ref *xattr_ref)
+{
+       uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
+       uint16_t size_rem = inode_size - EXT4_GOOD_OLD_INODE_SIZE -
+                           xattr_ref->inode_ref->inode->extra_isize;
+       return size_rem;
+}
+
+static size_t ext4_xattr_block_space(struct ext4_xattr_ref *xattr_ref)
+{
+       return ext4_sb_get_block_size(&xattr_ref->fs->sb);
+}
+
+static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref)
+{
+       int ret = EOK;
+       uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
+       if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
+               ret = ext4_xattr_inode_fetch(xattr_ref);
+               if (ret != EOK)
+                       return ret;
+       }
+
+       if (xattr_ref->block_loaded)
+               ret = ext4_xattr_block_fetch(xattr_ref);
+
+       xattr_ref->dirty = false;
+       return ret;
+}
+
+static struct ext4_xattr_item *
+ext4_xattr_lookup_item(struct ext4_xattr_ref *xattr_ref, uint8_t name_index,
+                      const char *name, size_t name_len)
+{
+       struct ext4_xattr_item tmp = {
+               .name_index = name_index,
+               .name = (char *)name, /*RB_FIND - won't touch this string*/
+               .name_len = name_len,
+       };
+       if (name_index == EXT4_XATTR_INDEX_SYSTEM &&
+           name_len == 4 &&
+           !memcmp(name, "data", 4))
+               tmp.in_inode = true;
+
+       return RB_FIND(ext4_xattr_tree, &xattr_ref->root, &tmp);
+}
+
+static struct ext4_xattr_item *
+ext4_xattr_insert_item(struct ext4_xattr_ref *xattr_ref, uint8_t name_index,
+                      const char *name, size_t name_len, const void *data,
+                      size_t data_size)
+{
+       struct ext4_xattr_item *item;
+       item = ext4_xattr_item_alloc(name_index, name, name_len);
+       if (!item)
+               return NULL;
+
+       if ((xattr_ref->ea_size + EXT4_XATTR_SIZE(data_size) +
+               EXT4_XATTR_LEN(item->name_len)
+                       >
+           ext4_xattr_inode_space(xattr_ref) -
+               sizeof(struct ext4_xattr_ibody_header))
+               &&
+           (xattr_ref->ea_size + EXT4_XATTR_SIZE(data_size) +
+               EXT4_XATTR_LEN(item->name_len) >
+           ext4_xattr_block_space(xattr_ref) -
+               sizeof(struct ext4_xattr_header))) {
+               ext4_xattr_item_free(item);
+
+               return NULL;
+       }
+       if (ext4_xattr_item_alloc_data(item, data, data_size) != EOK) {
+               ext4_xattr_item_free(item);
+               return NULL;
+       }
+       RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
+       xattr_ref->ea_size +=
+           EXT4_XATTR_SIZE(item->data_size) + EXT4_XATTR_LEN(item->name_len);
+       xattr_ref->dirty = true;
+       return item;
+}
+
+static int ext4_xattr_remove_item(struct ext4_xattr_ref *xattr_ref,
+                                 uint8_t name_index, const char *name,
+                                 size_t name_len)
+{
+       int ret = ENOENT;
+       struct ext4_xattr_item *item =
+           ext4_xattr_lookup_item(xattr_ref, name_index, name, name_len);
+       if (item) {
+               if (item == xattr_ref->iter_from)
+                       xattr_ref->iter_from =
+                           RB_NEXT(ext4_xattr_tree, &xattr_ref->root, item);
+
+               xattr_ref->ea_size -= EXT4_XATTR_SIZE(item->data_size) +
+                                     EXT4_XATTR_LEN(item->name_len);
+
+               RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
+               ext4_xattr_item_free(item);
+               xattr_ref->dirty = true;
+               ret = EOK;
+       }
+       return ret;
+}
+
+static int ext4_xattr_resize_item(struct ext4_xattr_ref *xattr_ref,
+                                 struct ext4_xattr_item *item,
+                                 size_t new_data_size)
+{
+       int ret = EOK;
+       size_t old_data_size = item->data_size;
+       if ((xattr_ref->ea_size - EXT4_XATTR_SIZE(old_data_size) +
+               EXT4_XATTR_SIZE(new_data_size)
+                       >
+           ext4_xattr_inode_space(xattr_ref) -
+               sizeof(struct ext4_xattr_ibody_header))
+               &&
+           (xattr_ref->ea_size - EXT4_XATTR_SIZE(old_data_size) +
+               EXT4_XATTR_SIZE(new_data_size)
+                       >
+           ext4_xattr_block_space(xattr_ref) -
+               sizeof(struct ext4_xattr_header))) {
+
+               return ENOSPC;
+       }
+       ret = ext4_xattr_item_resize_data(item, new_data_size);
+       if (ret != EOK) {
+               return ret;
+       }
+       xattr_ref->ea_size =
+           xattr_ref->ea_size -
+           EXT4_XATTR_SIZE(old_data_size) +
+           EXT4_XATTR_SIZE(new_data_size);
+       xattr_ref->dirty = true;
+       return ret;
+}
+
+static void ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
+{
+       struct ext4_xattr_item *item, *save_item;
+       RB_FOREACH_SAFE(item, ext4_xattr_tree, &xattr_ref->root, save_item)
+       {
+               RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
+               ext4_xattr_item_free(item);
+       }
+       xattr_ref->ea_size = 0;
+}
+
+static int ext4_xattr_try_alloc_block(struct ext4_xattr_ref *xattr_ref)
+{
+       int ret = EOK;
+
+       ext4_fsblk_t xattr_block = 0;
+       xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
+                                             &xattr_ref->fs->sb);
+       if (!xattr_block) {
+               ext4_fsblk_t goal =
+                       ext4_fs_inode_to_goal_block(xattr_ref->inode_ref);
+
+               ret = ext4_balloc_alloc_block(xattr_ref->inode_ref,
+                                             goal,
+                                             &xattr_block);
+               if (ret != EOK)
+                       goto Finish;
+
+               ret = ext4_trans_block_get(xattr_ref->fs->bdev, &xattr_ref->block,
+                                    xattr_block);
+               if (ret != EOK) {
+                       ext4_balloc_free_block(xattr_ref->inode_ref,
+                                              xattr_block);
+                       goto Finish;
+               }
+
+               ext4_inode_set_file_acl(xattr_ref->inode_ref->inode,
+                                       &xattr_ref->fs->sb, xattr_block);
+               xattr_ref->inode_ref->dirty = true;
+               xattr_ref->block_loaded = true;
+       }
+
+Finish:
+       return ret;
+}
+
+static void ext4_xattr_try_free_block(struct ext4_xattr_ref *xattr_ref)
+{
+       ext4_fsblk_t xattr_block;
+       xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
+                                             &xattr_ref->fs->sb);
+       ext4_inode_set_file_acl(xattr_ref->inode_ref->inode, &xattr_ref->fs->sb,
+                               0);
+       ext4_block_set(xattr_ref->fs->bdev, &xattr_ref->block);
+       ext4_balloc_free_block(xattr_ref->inode_ref, xattr_block);
+       xattr_ref->inode_ref->dirty = true;
+       xattr_ref->block_loaded = false;
+}
+
+static void ext4_xattr_set_block_header(struct ext4_xattr_ref *xattr_ref)
+{
+       struct ext4_xattr_header *block_header = NULL;
+       block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
+
+       memset(block_header, 0, sizeof(struct ext4_xattr_header));
+       block_header->h_magic = EXT4_XATTR_MAGIC;
+       block_header->h_refcount = to_le32(1);
+       block_header->h_blocks = to_le32(1);
+}
+
+static void
+ext4_xattr_set_inode_entry(struct ext4_xattr_item *item,
+                          struct ext4_xattr_ibody_header *ibody_header,
+                          struct ext4_xattr_entry *entry, void *ibody_data_ptr)
+{
+       entry->e_name_len = (uint8_t)item->name_len;
+       entry->e_name_index = item->name_index;
+       entry->e_value_offs =
+           (char *)ibody_data_ptr - (char *)EXT4_XATTR_IFIRST(ibody_header);
+       entry->e_value_block = 0;
+       entry->e_value_size = item->data_size;
+}
+
+static void ext4_xattr_set_block_entry(struct ext4_xattr_item *item,
+                                      struct ext4_xattr_header *block_header,
+                                      struct ext4_xattr_entry *block_entry,
+                                      void *block_data_ptr)
+{
+       block_entry->e_name_len = (uint8_t)item->name_len;
+       block_entry->e_name_index = item->name_index;
+       block_entry->e_value_offs =
+           (char *)block_data_ptr - (char *)block_header;
+       block_entry->e_value_block = 0;
+       block_entry->e_value_size = item->data_size;
+}
+
+static int ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
+{
+       int ret = EOK;
+       bool block_modified = false;
+       void *ibody_data = NULL;
+       void *block_data = NULL;
+       struct ext4_xattr_item *item, *save_item;
+       size_t inode_size_rem, block_size_rem;
+       struct ext4_xattr_ibody_header *ibody_header = NULL;
+       struct ext4_xattr_header *block_header = NULL;
+       struct ext4_xattr_entry *entry = NULL;
+       struct ext4_xattr_entry *block_entry = NULL;
+
+       inode_size_rem = ext4_xattr_inode_space(xattr_ref);
+       block_size_rem = ext4_xattr_block_space(xattr_ref);
+       if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
+               ibody_header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
+               entry = EXT4_XATTR_IFIRST(ibody_header);
+       }
+
+       if (!xattr_ref->dirty)
+               goto Finish;
+       /* If there are enough spaces in the ibody EA table.*/
+       if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
+               memset(ibody_header, 0, inode_size_rem);
+               ibody_header->h_magic = EXT4_XATTR_MAGIC;
+               ibody_data = (char *)ibody_header + inode_size_rem;
+               inode_size_rem -= sizeof(struct ext4_xattr_ibody_header);
+
+               xattr_ref->inode_ref->dirty = true;
+       }
+       /* If we need an extra block to hold the EA entries*/
+       if (xattr_ref->ea_size > inode_size_rem) {
+               if (!xattr_ref->block_loaded) {
+                       ret = ext4_xattr_try_alloc_block(xattr_ref);
+                       if (ret != EOK)
+                               goto Finish;
+               }
+               block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
+               block_entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
+               ext4_xattr_set_block_header(xattr_ref);
+               block_data = (char *)block_header + block_size_rem;
+               block_size_rem -= sizeof(struct ext4_xattr_header);
+
+               ext4_trans_set_block_dirty(xattr_ref->block.buf);
+       } else {
+               /* We don't need an extra block.*/
+               if (xattr_ref->block_loaded) {
+                       block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
+                       block_header->h_refcount =
+                           to_le32(to_le32(block_header->h_refcount) - 1);
+                       if (!block_header->h_refcount) {
+                               ext4_xattr_try_free_block(xattr_ref);
+                               block_header = NULL;
+                       } else {
+                               block_entry =
+                                   EXT4_XATTR_BFIRST(&xattr_ref->block);
+                               block_data =
+                                   (char *)block_header + block_size_rem;
+                               block_size_rem -=
+                                   sizeof(struct ext4_xattr_header);
+                               ext4_inode_set_file_acl(
+                                   xattr_ref->inode_ref->inode,
+                                   &xattr_ref->fs->sb, 0);
+
+                               xattr_ref->inode_ref->dirty = true;
+                               ext4_trans_set_block_dirty(xattr_ref->block.buf);
+                       }
+               }
+       }
+       RB_FOREACH_SAFE(item, ext4_xattr_tree, &xattr_ref->root, save_item)
+       {
+               if (EXT4_XATTR_SIZE(item->data_size) +
+                       EXT4_XATTR_LEN(item->name_len) <=
+                   inode_size_rem) {
+                       ibody_data = (char *)ibody_data -
+                                    EXT4_XATTR_SIZE(item->data_size);
+                       ext4_xattr_set_inode_entry(item, ibody_header, entry,
+                                                  ibody_data);
+                       memcpy(EXT4_XATTR_NAME(entry), item->name,
+                              item->name_len);
+                       memcpy(ibody_data, item->data, item->data_size);
+                       entry = EXT4_XATTR_NEXT(entry);
+                       inode_size_rem -= EXT4_XATTR_SIZE(item->data_size) +
+                                         EXT4_XATTR_LEN(item->name_len);
+
+                       xattr_ref->inode_ref->dirty = true;
+                       continue;
+               }
+               if (EXT4_XATTR_SIZE(item->data_size) +
+                       EXT4_XATTR_LEN(item->name_len) >
+                   block_size_rem) {
+                       ret = ENOSPC;
+                       goto Finish;
+               }
+               block_data =
+                   (char *)block_data - EXT4_XATTR_SIZE(item->data_size);
+               ext4_xattr_set_block_entry(item, block_header, block_entry,
+                                          block_data);
+               memcpy(EXT4_XATTR_NAME(block_entry), item->name,
+                      item->name_len);
+               memcpy(block_data, item->data, item->data_size);
+               block_entry = EXT4_XATTR_NEXT(block_entry);
+               block_size_rem -= EXT4_XATTR_SIZE(item->data_size) +
+                                 EXT4_XATTR_LEN(item->name_len);
+
+               block_modified = true;
+       }
+       xattr_ref->dirty = false;
+       if (block_modified) {
+               ext4_xattr_rehash(block_header,
+                                 EXT4_XATTR_BFIRST(&xattr_ref->block));
+               ext4_xattr_set_block_checksum(xattr_ref->inode_ref,
+                                             xattr_ref->block.lb_id,
+                                             block_header);
+               ext4_trans_set_block_dirty(xattr_ref->block.buf);
+       }
+
+Finish:
+       return ret;
+}
+
+void ext4_fs_xattr_iterate(struct ext4_xattr_ref *ref,
+                          int (*iter)(struct ext4_xattr_ref *ref,
+                                    struct ext4_xattr_item *item))
+{
+       struct ext4_xattr_item *item;
+       if (!ref->iter_from)
+               ref->iter_from = RB_MIN(ext4_xattr_tree, &ref->root);
+
+       RB_FOREACH_FROM(item, ext4_xattr_tree, ref->iter_from)
+       {
+               int ret = EXT4_XATTR_ITERATE_CONT;
+               if (iter)
+                       iter(ref, item);
+
+               if (ret != EXT4_XATTR_ITERATE_CONT) {
+                       if (ret == EXT4_XATTR_ITERATE_STOP)
+                               ref->iter_from = NULL;
+
+                       break;
+               }
+       }
+}
+
+void ext4_fs_xattr_iterate_reset(struct ext4_xattr_ref *ref)
+{
+       ref->iter_from = NULL;
+}
+
+int ext4_fs_set_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
+                     const char *name, size_t name_len, const void *data,
+                     size_t data_size, bool replace)
+{
+       int ret = EOK;
+       struct ext4_xattr_item *item =
+           ext4_xattr_lookup_item(ref, name_index, name, name_len);
+       if (replace) {
+               if (!item) {
+                       ret = ENODATA;
+                       goto Finish;
+               }
+               if (item->data_size != data_size)
+                       ret = ext4_xattr_resize_item(ref, item, data_size);
+
+               if (ret != EOK) {
+                       goto Finish;
+               }
+               memcpy(item->data, data, data_size);
+       } else {
+               if (item) {
+                       ret = EEXIST;
+                       goto Finish;
+               }
+               item = ext4_xattr_insert_item(ref, name_index, name, name_len,
+                                             data, data_size);
+               if (!item)
+                       ret = ENOMEM;
+       }
+Finish:
+       return ret;
+}
+
+int ext4_fs_remove_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
+                        const char *name, size_t name_len)
+{
+       return ext4_xattr_remove_item(ref, name_index, name, name_len);
+}
+
+int ext4_fs_get_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
+                     const char *name, size_t name_len, void *buf,
+                     size_t buf_size, size_t *data_size)
+{
+       int ret = EOK;
+       size_t item_size = 0;
+       struct ext4_xattr_item *item =
+           ext4_xattr_lookup_item(ref, name_index, name, name_len);
+
+       if (!item) {
+               ret = ENODATA;
+               goto Finish;
+       }
+       item_size = item->data_size;
+       if (buf_size > item_size)
+               buf_size = item_size;
+
+       if (buf)
+               memcpy(buf, item->data, buf_size);
+
+Finish:
+       if (data_size)
+               *data_size = item_size;
+
+       return ret;
+}
+
+int ext4_fs_get_xattr_ref(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
+                         struct ext4_xattr_ref *ref)
+{
+       int rc;
+       ext4_fsblk_t xattr_block;
+       xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
+       RB_INIT(&ref->root);
+       ref->ea_size = 0;
+       ref->iter_from = NULL;
+       if (xattr_block) {
+               rc = ext4_trans_block_get(fs->bdev, &ref->block, xattr_block);
+               if (rc != EOK)
+                       return EIO;
+
+               ref->block_loaded = true;
+       } else
+               ref->block_loaded = false;
+
+       ref->inode_ref = inode_ref;
+       ref->fs = fs;
+
+       rc = ext4_xattr_fetch(ref);
+       if (rc != EOK) {
+               ext4_xattr_purge_items(ref);
+               if (xattr_block)
+                       ext4_block_set(fs->bdev, &inode_ref->block);
+
+               ref->block_loaded = false;
+               return rc;
+       }
+       return EOK;
+}
+
+void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref)
+{
+       ext4_xattr_write_to_disk(ref);
+       if (ref->block_loaded) {
+               ext4_block_set(ref->fs->bdev, &ref->block);
+               ref->block_loaded = false;
+       }
+       ext4_xattr_purge_items(ref);
+       ref->inode_ref = NULL;
+       ref->fs = NULL;
+}
+
+struct xattr_prefix {
+       const char *prefix;
+       uint8_t name_index;
+};
+
+static const struct xattr_prefix prefix_tbl[] = {
+    {"user.", EXT4_XATTR_INDEX_USER},
+    {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS},
+    {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT},
+    {"trusted.", EXT4_XATTR_INDEX_TRUSTED},
+    {"security.", EXT4_XATTR_INDEX_SECURITY},
+    {"system.", EXT4_XATTR_INDEX_SYSTEM},
+    {"system.richacl", EXT4_XATTR_INDEX_RICHACL},
+    {NULL, 0},
+};
+
+const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
+                             uint8_t *name_index, size_t *name_len)
+{
+       int i;
+       ext4_assert(name_index);
+       if (!full_name_len) {
+               if (name_len)
+                       *name_len = 0;
+
+               return NULL;
+       }
+
+       for (i = 0; prefix_tbl[i].prefix; i++) {
+               size_t prefix_len = strlen(prefix_tbl[i].prefix);
+               if (full_name_len >= prefix_len &&
+                   !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) {
+                       *name_index = prefix_tbl[i].name_index;
+                       if (name_len)
+                               *name_len = full_name_len - prefix_len;
+
+                       return full_name + prefix_len;
+               }
+       }
+       if (name_len)
+               *name_len = 0;
+
+       return NULL;
+}
+
+const char *ext4_get_xattr_name_prefix(uint8_t name_index,
+                                      size_t *ret_prefix_len)
+{
+       int i;
+
+       for (i = 0; prefix_tbl[i].prefix; i++) {
+               size_t prefix_len = strlen(prefix_tbl[i].prefix);
+               if (prefix_tbl[i].name_index == name_index) {
+                       if (ret_prefix_len)
+                               *ret_prefix_len = prefix_len;
+
+                       return prefix_tbl[i].prefix;
+               }
+       }
+       if (ret_prefix_len)
+               *ret_prefix_len = 0;
+
+       return NULL;
+}
+
+/**
+ * @}
+ */