diff options
| author | ngkaho1234 <ngkaho1234@gmail.com> | 2016-01-28 22:46:36 +0800 |
|---|---|---|
| committer | ngkaho1234 <ngkaho1234@gmail.com> | 2016-01-28 22:46:36 +0800 |
| commit | a45154a49b743eba4669442e6993c50583329d99 (patch) | |
| tree | 343ba15a0cdbbfa32b87f1ed1603ae7c8bfdfd65 /include | |
| parent | d3bb06fff7af3b2162633b4ab79f7f09b3fe3fdb (diff) | |
Reconstruct source directory tree.
Diffstat (limited to 'include')
| -rw-r--r-- | include/ext4.h | 459 | ||||
| -rw-r--r-- | include/ext4_balloc.h | 119 | ||||
| -rw-r--r-- | include/ext4_bcache.h | 291 | ||||
| -rw-r--r-- | include/ext4_bitmap.h | 103 | ||||
| -rw-r--r-- | include/ext4_block_group.h | 326 | ||||
| -rw-r--r-- | include/ext4_blockdev.h | 266 | ||||
| -rw-r--r-- | include/ext4_config.h | 155 | ||||
| -rw-r--r-- | include/ext4_crc32.h | 72 | ||||
| -rw-r--r-- | include/ext4_debug.h | 189 | ||||
| -rw-r--r-- | include/ext4_dir.h | 287 | ||||
| -rw-r--r-- | include/ext4_dir_idx.h | 102 | ||||
| -rw-r--r-- | include/ext4_errno.h | 95 | ||||
| -rw-r--r-- | include/ext4_extent.h | 288 | ||||
| -rw-r--r-- | include/ext4_fs.h | 244 | ||||
| -rw-r--r-- | include/ext4_hash.h | 68 | ||||
| -rw-r--r-- | include/ext4_ialloc.h | 85 | ||||
| -rw-r--r-- | include/ext4_inode.h | 336 | ||||
| -rw-r--r-- | include/ext4_journal.h | 81 | ||||
| -rw-r--r-- | include/ext4_mbr.h | 69 | ||||
| -rw-r--r-- | include/ext4_mkfs.h | 82 | ||||
| -rw-r--r-- | include/ext4_oflags.h | 103 | ||||
| -rw-r--r-- | include/ext4_super.h | 237 | ||||
| -rw-r--r-- | include/ext4_trans.h | 90 | ||||
| -rw-r--r-- | include/ext4_types.h | 1304 | ||||
| -rw-r--r-- | include/ext4_xattr.h | 82 | ||||
| -rw-r--r-- | include/misc/queue.h | 702 | ||||
| -rw-r--r-- | include/misc/tree.h | 809 |
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_ */ |
