summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorngkaho1234 <ngkaho1234@gmail.com>2016-01-28 22:46:36 +0800
committerngkaho1234 <ngkaho1234@gmail.com>2016-01-28 22:46:36 +0800
commita45154a49b743eba4669442e6993c50583329d99 (patch)
tree343ba15a0cdbbfa32b87f1ed1603ae7c8bfdfd65 /include
parentd3bb06fff7af3b2162633b4ab79f7f09b3fe3fdb (diff)
Reconstruct source directory tree.
Diffstat (limited to 'include')
-rw-r--r--include/ext4.h459
-rw-r--r--include/ext4_balloc.h119
-rw-r--r--include/ext4_bcache.h291
-rw-r--r--include/ext4_bitmap.h103
-rw-r--r--include/ext4_block_group.h326
-rw-r--r--include/ext4_blockdev.h266
-rw-r--r--include/ext4_config.h155
-rw-r--r--include/ext4_crc32.h72
-rw-r--r--include/ext4_debug.h189
-rw-r--r--include/ext4_dir.h287
-rw-r--r--include/ext4_dir_idx.h102
-rw-r--r--include/ext4_errno.h95
-rw-r--r--include/ext4_extent.h288
-rw-r--r--include/ext4_fs.h244
-rw-r--r--include/ext4_hash.h68
-rw-r--r--include/ext4_ialloc.h85
-rw-r--r--include/ext4_inode.h336
-rw-r--r--include/ext4_journal.h81
-rw-r--r--include/ext4_mbr.h69
-rw-r--r--include/ext4_mkfs.h82
-rw-r--r--include/ext4_oflags.h103
-rw-r--r--include/ext4_super.h237
-rw-r--r--include/ext4_trans.h90
-rw-r--r--include/ext4_types.h1304
-rw-r--r--include/ext4_xattr.h82
-rw-r--r--include/misc/queue.h702
-rw-r--r--include/misc/tree.h809
27 files changed, 7044 insertions, 0 deletions
diff --git a/include/ext4.h b/include/ext4.h
new file mode 100644
index 0000000..4bf31ac
--- /dev/null
+++ b/include/ext4.h
@@ -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
index 0000000..f2c3dc9
--- /dev/null
+++ b/include/ext4_balloc.h
@@ -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
index 0000000..bceb75a
--- /dev/null
+++ b/include/ext4_bcache.h
@@ -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
index 0000000..cb73b76
--- /dev/null
+++ b/include/ext4_bitmap.h
@@ -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
index 0000000..715967e
--- /dev/null
+++ b/include/ext4_block_group.h
@@ -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
index 0000000..f5329ec
--- /dev/null
+++ b/include/ext4_blockdev.h
@@ -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
index 0000000..3d856eb
--- /dev/null
+++ b/include/ext4_config.h
@@ -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
index 0000000..3dad1d1
--- /dev/null
+++ b/include/ext4_crc32.h
@@ -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
index 0000000..2afc39c
--- /dev/null
+++ b/include/ext4_debug.h
@@ -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
index 0000000..37547ea
--- /dev/null
+++ b/include/ext4_dir.h
@@ -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
index 0000000..dd0067d
--- /dev/null
+++ b/include/ext4_dir_idx.h
@@ -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
index 0000000..edf89a9
--- /dev/null
+++ b/include/ext4_errno.h
@@ -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
index 0000000..1d4ddf1
--- /dev/null
+++ b/include/ext4_extent.h
@@ -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
index 0000000..97e1d1d
--- /dev/null
+++ b/include/ext4_fs.h
@@ -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
index 0000000..b7a9ac5
--- /dev/null
+++ b/include/ext4_hash.h
@@ -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
index 0000000..cea3fe6
--- /dev/null
+++ b/include/ext4_ialloc.h
@@ -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
index 0000000..90c5754
--- /dev/null
+++ b/include/ext4_inode.h
@@ -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
index 0000000..8e193cc
--- /dev/null
+++ b/include/ext4_journal.h
@@ -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
index 0000000..2fc91f2
--- /dev/null
+++ b/include/ext4_mbr.h
@@ -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
index 0000000..497bf3b
--- /dev/null
+++ b/include/ext4_mkfs.h
@@ -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
index 0000000..7f7be7e
--- /dev/null
+++ b/include/ext4_oflags.h
@@ -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
index 0000000..2172698
--- /dev/null
+++ b/include/ext4_super.h
@@ -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
index 0000000..e3cb28a
--- /dev/null
+++ b/include/ext4_trans.h
@@ -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
index 0000000..41cb33f
--- /dev/null
+++ b/include/ext4_types.h
@@ -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
index 0000000..f6076d2
--- /dev/null
+++ b/include/ext4_xattr.h
@@ -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
index 0000000..6efd6f3
--- /dev/null
+++ b/include/misc/queue.h
@@ -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
index 0000000..8ed5d41
--- /dev/null
+++ b/include/misc/tree.h
@@ -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_ */