From 0584a7baa0614df7a55aadd700de3616d09f2f73 Mon Sep 17 00:00:00 2001 From: luka177 Date: Fri, 27 Aug 2021 17:49:43 +0300 Subject: [PATCH] Add lwext4 ext2/3/4 driver --- Abm.dsc | 2 + Include/ext4.h | 628 ++++ Include/ext4_balloc.h | 121 + Include/ext4_bcache.h | 297 ++ Include/ext4_bitmap.h | 103 + Include/ext4_block_group.h | 330 ++ Include/ext4_blockdev.h | 269 ++ Include/ext4_config.h | 180 + Include/ext4_crc32.h | 72 + Include/ext4_debug.h | 192 + Include/ext4_dir.h | 301 ++ Include/ext4_dir_idx.h | 112 + Include/ext4_errno.h | 95 + Include/ext4_extent.h | 74 + Include/ext4_fs.h | 280 ++ Include/ext4_hash.h | 76 + Include/ext4_ialloc.h | 85 + Include/ext4_inode.h | 354 ++ Include/ext4_journal.h | 148 + Include/ext4_mbr.h | 73 + Include/ext4_misc.h | 156 + Include/ext4_mkfs.h | 85 + Include/ext4_oflags.h | 103 + Include/ext4_super.h | 238 ++ Include/ext4_trans.h | 90 + Include/ext4_types.h | 844 +++++ Include/ext4_xattr.h | 108 + Include/misc/queue.h | 702 ++++ Include/misc/tree.h | 809 ++++ Library/AbmLib/AbmLib.inf | 1 + Library/lwext4/blockdev/blockdev.c | 99 + Library/lwext4/blockdev/blockdev.h | 41 + Library/lwext4/blockdev/linux/file_dev.c | 142 + Library/lwext4/blockdev/linux/file_dev.h | 43 + .../lwext4/blockdev/windows/file_windows.c | 170 + .../lwext4/blockdev/windows/file_windows.h | 44 + Library/lwext4/include/ext4.h | 628 ++++ Library/lwext4/include/ext4_balloc.h | 121 + Library/lwext4/include/ext4_bcache.h | 297 ++ Library/lwext4/include/ext4_bitmap.h | 103 + Library/lwext4/include/ext4_block_group.h | 330 ++ Library/lwext4/include/ext4_blockdev.h | 269 ++ Library/lwext4/include/ext4_config.h | 180 + Library/lwext4/include/ext4_crc32.h | 72 + Library/lwext4/include/ext4_debug.h | 192 + Library/lwext4/include/ext4_dir.h | 301 ++ Library/lwext4/include/ext4_dir_idx.h | 112 + Library/lwext4/include/ext4_errno.h | 95 + Library/lwext4/include/ext4_extent.h | 74 + Library/lwext4/include/ext4_fs.h | 280 ++ Library/lwext4/include/ext4_hash.h | 76 + Library/lwext4/include/ext4_ialloc.h | 85 + Library/lwext4/include/ext4_inode.h | 354 ++ Library/lwext4/include/ext4_journal.h | 148 + Library/lwext4/include/ext4_mbr.h | 73 + Library/lwext4/include/ext4_misc.h | 156 + Library/lwext4/include/ext4_mkfs.h | 85 + Library/lwext4/include/ext4_oflags.h | 103 + Library/lwext4/include/ext4_super.h | 238 ++ Library/lwext4/include/ext4_trans.h | 90 + Library/lwext4/include/ext4_types.h | 844 +++++ Library/lwext4/include/ext4_xattr.h | 108 + Library/lwext4/include/misc/queue.h | 702 ++++ Library/lwext4/include/misc/tree.h | 809 ++++ Library/lwext4/lwext4Lib.inf | 46 + Library/lwext4/src/ext4.c | 3240 +++++++++++++++++ Library/lwext4/src/ext4_balloc.c | 665 ++++ Library/lwext4/src/ext4_bcache.c | 325 ++ Library/lwext4/src/ext4_bitmap.c | 162 + Library/lwext4/src/ext4_block_group.c | 94 + Library/lwext4/src/ext4_blockdev.c | 475 +++ Library/lwext4/src/ext4_crc32.c | 187 + Library/lwext4/src/ext4_debug.c | 66 + Library/lwext4/src/ext4_dir.c | 708 ++++ Library/lwext4/src/ext4_dir_idx.c | 1403 +++++++ Library/lwext4/src/ext4_extent.c | 2140 +++++++++++ Library/lwext4/src/ext4_fs.c | 1751 +++++++++ Library/lwext4/src/ext4_hash.c | 327 ++ Library/lwext4/src/ext4_ialloc.c | 370 ++ Library/lwext4/src/ext4_inode.c | 405 +++ Library/lwext4/src/ext4_journal.c | 2292 ++++++++++++ Library/lwext4/src/ext4_mbr.c | 208 ++ Library/lwext4/src/ext4_mkfs.c | 865 +++++ Library/lwext4/src/ext4_super.c | 272 ++ Library/lwext4/src/ext4_trans.c | 107 + Library/lwext4/src/ext4_xattr.c | 1564 ++++++++ abm.inf | 1 + 87 files changed, 32065 insertions(+) create mode 100644 Include/ext4.h create mode 100644 Include/ext4_balloc.h create mode 100644 Include/ext4_bcache.h create mode 100644 Include/ext4_bitmap.h create mode 100644 Include/ext4_block_group.h create mode 100644 Include/ext4_blockdev.h create mode 100644 Include/ext4_config.h create mode 100644 Include/ext4_crc32.h create mode 100644 Include/ext4_debug.h create mode 100644 Include/ext4_dir.h create mode 100644 Include/ext4_dir_idx.h create mode 100644 Include/ext4_errno.h create mode 100644 Include/ext4_extent.h create mode 100644 Include/ext4_fs.h create mode 100644 Include/ext4_hash.h create mode 100644 Include/ext4_ialloc.h create mode 100644 Include/ext4_inode.h create mode 100644 Include/ext4_journal.h create mode 100644 Include/ext4_mbr.h create mode 100644 Include/ext4_misc.h create mode 100644 Include/ext4_mkfs.h create mode 100644 Include/ext4_oflags.h create mode 100644 Include/ext4_super.h create mode 100644 Include/ext4_trans.h create mode 100644 Include/ext4_types.h create mode 100644 Include/ext4_xattr.h create mode 100644 Include/misc/queue.h create mode 100644 Include/misc/tree.h create mode 100644 Library/lwext4/blockdev/blockdev.c create mode 100644 Library/lwext4/blockdev/blockdev.h create mode 100644 Library/lwext4/blockdev/linux/file_dev.c create mode 100644 Library/lwext4/blockdev/linux/file_dev.h create mode 100644 Library/lwext4/blockdev/windows/file_windows.c create mode 100644 Library/lwext4/blockdev/windows/file_windows.h create mode 100644 Library/lwext4/include/ext4.h create mode 100644 Library/lwext4/include/ext4_balloc.h create mode 100644 Library/lwext4/include/ext4_bcache.h create mode 100644 Library/lwext4/include/ext4_bitmap.h create mode 100644 Library/lwext4/include/ext4_block_group.h create mode 100644 Library/lwext4/include/ext4_blockdev.h create mode 100644 Library/lwext4/include/ext4_config.h create mode 100644 Library/lwext4/include/ext4_crc32.h create mode 100644 Library/lwext4/include/ext4_debug.h create mode 100644 Library/lwext4/include/ext4_dir.h create mode 100644 Library/lwext4/include/ext4_dir_idx.h create mode 100644 Library/lwext4/include/ext4_errno.h create mode 100644 Library/lwext4/include/ext4_extent.h create mode 100644 Library/lwext4/include/ext4_fs.h create mode 100644 Library/lwext4/include/ext4_hash.h create mode 100644 Library/lwext4/include/ext4_ialloc.h create mode 100644 Library/lwext4/include/ext4_inode.h create mode 100644 Library/lwext4/include/ext4_journal.h create mode 100644 Library/lwext4/include/ext4_mbr.h create mode 100644 Library/lwext4/include/ext4_misc.h create mode 100644 Library/lwext4/include/ext4_mkfs.h create mode 100644 Library/lwext4/include/ext4_oflags.h create mode 100644 Library/lwext4/include/ext4_super.h create mode 100644 Library/lwext4/include/ext4_trans.h create mode 100644 Library/lwext4/include/ext4_types.h create mode 100644 Library/lwext4/include/ext4_xattr.h create mode 100644 Library/lwext4/include/misc/queue.h create mode 100644 Library/lwext4/include/misc/tree.h create mode 100644 Library/lwext4/lwext4Lib.inf create mode 100644 Library/lwext4/src/ext4.c create mode 100644 Library/lwext4/src/ext4_balloc.c create mode 100644 Library/lwext4/src/ext4_bcache.c create mode 100644 Library/lwext4/src/ext4_bitmap.c create mode 100644 Library/lwext4/src/ext4_block_group.c create mode 100644 Library/lwext4/src/ext4_blockdev.c create mode 100644 Library/lwext4/src/ext4_crc32.c create mode 100644 Library/lwext4/src/ext4_debug.c create mode 100644 Library/lwext4/src/ext4_dir.c create mode 100644 Library/lwext4/src/ext4_dir_idx.c create mode 100644 Library/lwext4/src/ext4_extent.c create mode 100644 Library/lwext4/src/ext4_fs.c create mode 100644 Library/lwext4/src/ext4_hash.c create mode 100644 Library/lwext4/src/ext4_ialloc.c create mode 100644 Library/lwext4/src/ext4_inode.c create mode 100644 Library/lwext4/src/ext4_journal.c create mode 100644 Library/lwext4/src/ext4_mbr.c create mode 100644 Library/lwext4/src/ext4_mkfs.c create mode 100644 Library/lwext4/src/ext4_super.c create mode 100644 Library/lwext4/src/ext4_trans.c create mode 100644 Library/lwext4/src/ext4_xattr.c diff --git a/Abm.dsc b/Abm.dsc index 022c7b5..f13d835 100644 --- a/Abm.dsc +++ b/Abm.dsc @@ -81,6 +81,8 @@ MicroLibC|AbmPkg/Library/MicroLibC/MicroLibC.inf AbmLib|AbmPkg/Library/AbmLib/AbmLib.inf + + lwext4Lib|AbmPkg/Library/lwext4Lib/lwext4Lib.inf [LibraryClasses.AARCH64] CompilerIntrinsicsLib|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf diff --git a/Include/ext4.h b/Include/ext4.h new file mode 100644 index 0000000..516bbdd --- /dev/null +++ b/Include/ext4.h @@ -0,0 +1,628 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +#include + +/********************************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 Actual file position.*/ + uint64_t fpos; +} ext4_file; + +/*****************************DIRECTORY DESCRIPTOR***************************/ + +/**@brief Directory entry descriptor. */ +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; + +/**@brief Directory descriptor. */ +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 block device. + * + * @param bd Block device. + * @param dev_name Block device name. + * + * @return Standard error code.*/ +int ext4_device_register(struct ext4_blockdev *bd, + const char *dev_name); + +/**@brief Un-register block device. + * + * @param dev_name Block device name. + * + * @return Standard error code.*/ +int ext4_device_unregister(const char *dev_name); + +/**@brief Un-register all block devices. + * + * @return Standard error code.*/ +int ext4_device_unregister_all(void); + +/**@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/ + * @param read_only mount as read-only mode. + * + * @return Standard error code */ +int ext4_mount(const char *dev_name, + const char *mount_point, + bool read_only); + +/**@brief Umount operation. + * + * @param mount_pount Mount point. + * + * @return Standard error code */ +int ext4_umount(const char *mount_point); + +/**@brief Starts 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_pount Mount point. + * + * @return Standard error code. */ +int ext4_journal_start(const char *mount_point); + +/**@brief Stops journaling. Journaling start/stop functions are transparent + * and might be used on filesystems without journaling support. + * + * @param mount_pount Mount point name. + * + * @return Standard error code. */ +int ext4_journal_stop(const char *mount_point); + +/**@brief Journal recovery. + * @warning Must be called after @ref ext4_mount. + * + * @param mount_pount Mount point. + * + * @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 mount point stats. + * + * @param mount_pount Mount point. + * @param stats Filesystem 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_pount Mount point. + * @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_pount Mount point. + * @param sb Superblock handle + * + * @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_fwrite(...); + * < --- data is flushed to physical drive + * + * When you do: + * ext4_cache_write_back(..., 1); + * ext4_fopen(...); + * ext4_fwrite(...); + * < --- 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 mount_pount Mount point. + * @param on Enable/disable cache writeback mode. + * + * @return Standard error code. */ +int ext4_cache_write_back(const char *path, bool on); + + +/**@brief Force cache flush. + * + * @param mount_pount Mount point. + * + * @return Standard error code. */ +int ext4_cache_flush(const char *path); + +/********************************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 file File handle. + * @param path File path, has to start from mount point:/my_partition/file. + * @param flags File open 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 *file, const char *path, const char *flags); + +/**@brief Alternate file open function. + * + * @param file File handle. + * @param path File path, has to start from mount point:/my_partition/file. + * @param flags File open flags. + * + * @return Standard error code.*/ +int ext4_fopen2(ext4_file *file, const char *path, int flags); + +/**@brief File close function. + * + * @param file File handle. + * + * @return Standard error code.*/ +int ext4_fclose(ext4_file *file); + + +/**@brief File truncate function. + * + * @param file File handle. + * @param size New file size. + * + * @return Standard error code.*/ +int ext4_ftruncate(ext4_file *file, uint64_t size); + +/**@brief Read data from file. + * + * @param file File handle. + * @param buf Output buffer. + * @param size Bytes to read. + * @param rcnt Bytes read (NULL allowed). + * + * @return Standard error code.*/ +int ext4_fread(ext4_file *file, void *buf, size_t size, size_t *rcnt); + +/**@brief Write data to file. + * + * @param file File handle. + * @param buf Data to write + * @param size Write length.. + * @param wcnt Bytes written (NULL allowed). + * + * @return Standard error code.*/ +int ext4_fwrite(ext4_file *file, const void *buf, size_t size, size_t *wcnt); + +/**@brief File seek operation. + * + * @param file 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 *file, int64_t offset, uint32_t origin); + +/**@brief Get file position. + * + * @param file File handle. + * + * @return Actual file position */ +uint64_t ext4_ftell(ext4_file *file); + +/**@brief Get file size. + * + * @param file File handle. + * + * @return File size. */ +uint64_t ext4_fsize(ext4_file *file); + + +/**@brief Get inode of file/directory/link. + * + * @param path Parh to file/dir/link. + * @param ret_ino Inode number. + * @param inode Inode internals. + * + * @return Standard error code.*/ +int ext4_raw_inode_fill(const char *path, uint32_t *ret_ino, + struct ext4_inode *inode); + +/**@brief Check if inode exists. + * + * @param path Parh to file/dir/link. + * @param type Inode type. + * @ref EXT4_DIRENTRY_UNKNOWN + * @ref EXT4_DE_REG_FILE + * @ref EXT4_DE_DIR + * @ref EXT4_DE_CHRDEV + * @ref EXT4_DE_BLKDEV + * @ref EXT4_DE_FIFO + * @ref EXT4_DE_SOCK + * @ref EXT4_DE_SYMLINK + * + * @return Standard error code.*/ +int ext4_inode_exist(const char *path, int type); + +/**@brief Change file/directory/link mode bits. + * + * @param path Path to file/dir/link. + * @param mode New mode bits (for example 0777). + * + * @return Standard error code.*/ +int ext4_mode_set(const char *path, uint32_t mode); + + +/**@brief Get file/directory/link mode bits. + * + * @param path Path to file/dir/link. + * @param mode New mode bits (for example 0777). + * + * @return Standard error code.*/ +int ext4_mode_get(const char *path, uint32_t *mode); + +/**@brief Change file owner and group. + * + * @param path Path to file/dir/link. + * @param uid User id. + * @param gid Group id. + * + * @return Standard error code.*/ +int ext4_owner_set(const char *path, uint32_t uid, uint32_t gid); + +/**@brief Get file/directory/link owner and group. + * + * @param path Path to file/dir/link. + * @param uid User id. + * @param gid Group id. + * + * @return Standard error code.*/ +int ext4_owner_get(const char *path, uint32_t *uid, uint32_t *gid); + +/**@brief Set file/directory/link access time. + * + * @param path Path to file/dir/link. + * @param atime Access timestamp. + * + * @return Standard error code.*/ +int ext4_atime_set(const char *path, uint32_t atime); + +/**@brief Set file/directory/link modify time. + * + * @param path Path to file/dir/link. + * @param mtime Modify timestamp. + * + * @return Standard error code.*/ +int ext4_mtime_set(const char *path, uint32_t mtime); + +/**@brief Set file/directory/link change time. + * + * @param path Path to file/dir/link. + * @param ctime Change timestamp. + * + * @return Standard error code.*/ +int ext4_ctime_set(const char *path, uint32_t ctime); + +/**@brief Get file/directory/link access time. + * + * @param path Path to file/dir/link. + * @param atime Access timestamp. + * + * @return Standard error code.*/ +int ext4_atime_get(const char *path, uint32_t *atime); + +/**@brief Get file/directory/link modify time. + * + * @param path Path to file/dir/link. + * @param mtime Modify timestamp. + * + * @return Standard error code.*/ +int ext4_mtime_get(const char *path, uint32_t *mtime); + +/**@brief Get file/directory/link change time. + * + * @param path Pathto file/dir/link. + * @param ctime Change timestamp. + * + * @return standard error code*/ +int ext4_ctime_get(const char *path, uint32_t *ctime); + +/**@brief Create symbolic link. + * + * @param target Destination entry path. + * @param path Source entry path. + * + * @return Standard error code.*/ +int ext4_fsymlink(const char *target, const char *path); + +/**@brief Create special file. + * @param path Path to new special file. + * @param filetype Filetype of the new special file. + * (that must not be regular file, directory, or unknown type) + * @param dev If filetype is char device or block device, + * the device number will become the payload in the inode. + * @return Standard error code.*/ +int ext4_mknod(const char *path, int filetype, uint32_t dev); + +/**@brief Read symbolic link payload. + * + * @param path 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 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. + * + * @return Standard error code.*/ +int ext4_setxattr(const char *path, const char *name, size_t name_len, + const void *data, size_t data_size); + +/**@brief Get extended attribute. + * + * @param path 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 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 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 Rename/move directory. + * + * @param path Source path. + * @param new_path Destination path. + * + * @return Standard error code. */ +int ext4_dir_mv(const char *path, const char *new_path); + +/**@brief Create new directory. + * + * @param path Directory name. + * + * @return Standard error code.*/ +int ext4_dir_mk(const char *path); + +/**@brief Directory open. + * + * @param dir Directory handle. + * @param path Directory path. + * + * @return Standard error code.*/ +int ext4_dir_open(ext4_dir *dir, const char *path); + +/**@brief Directory close. + * + * @param dir directory handle. + * + * @return Standard error code.*/ +int ext4_dir_close(ext4_dir *dir); + +/**@brief Return next directory entry. + * + * @param dir Directory handle. + * + * @return Directory entry id (NULL if no entry)*/ +const ext4_direntry *ext4_dir_entry_next(ext4_dir *dir); + +/**@brief Rewine directory entry offset. + * + * @param dir Directory handle.*/ +void ext4_dir_entry_rewind(ext4_dir *dir); + + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_H_ */ + +/** + * @} + */ diff --git a/Include/ext4_balloc.h b/Include/ext4_balloc.h new file mode 100644 index 0000000..ad481dc --- /dev/null +++ b/Include/ext4_balloc.h @@ -0,0 +1,121 @@ +/* + * 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 +#include + +#include + +#include +#include + +/**@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..f8bec7d --- /dev/null +++ b/Include/ext4_bcache.h @@ -0,0 +1,297 @@ +/* + * 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 + +#include +#include +#include +#include + +#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 buffer. + * @param bc block cache descriptor + * @param buf buffer*/ +void ext4_bcache_invalidate_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..6bcb100 --- /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 + +#include +#include + +/**@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..61fbc98 --- /dev/null +++ b/Include/ext4_block_group.h @@ -0,0 +1,330 @@ +/* + * 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 +#include +#include + +#include +#include + +/**@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); + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_BLOCK_GROUP_H_ */ + +/** + * @} + */ diff --git a/Include/ext4_blockdev.h b/Include/ext4_blockdev.h new file mode 100644 index 0000000..4efbb43 --- /dev/null +++ b/Include/ext4_blockdev.h @@ -0,0 +1,269 @@ +/* + * 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 +#include + +#include +#include + +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 User data pointer*/ + void* p_user; +}; + +/**@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; + + void *journal; +}; + +/**@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, uint32_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..810df4c --- /dev/null +++ b/Include/ext4_config.h @@ -0,0 +1,180 @@ +/* + * 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 + +/*****************************************************************************/ + +#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/disable xattr*/ +#ifndef CONFIG_XATTR_ENABLE +#define CONFIG_XATTR_ENABLE 1 +#endif + +/**@brief Enable/disable extents*/ +#ifndef CONFIG_EXTENTS_ENABLE +#define CONFIG_EXTENTS_ENABLE 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 name*/ +#ifndef CONFIG_EXT4_MAX_BLOCKDEV_NAME +#define CONFIG_EXT4_MAX_BLOCKDEV_NAME 32 +#endif + + +/**@brief Maximum block device count*/ +#ifndef CONFIG_EXT4_BLOCKDEVS_COUNT +#define CONFIG_EXT4_BLOCKDEVS_COUNT 2 +#endif + +/**@brief Maximum mountpoint name*/ +#ifndef CONFIG_EXT4_MAX_MP_NAME +#define CONFIG_EXT4_MAX_MP_NAME 32 +#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 + + +/**@brief Unaligned access switch on/off*/ +#ifndef CONFIG_UNALIGNED_ACCESS +#define CONFIG_UNALIGNED_ACCESS 0 +#endif + +/**@brief Switches use of malloc/free functions family + * from standard library to user provided*/ +#ifndef CONFIG_USE_USER_MALLOC +#define CONFIG_USE_USER_MALLOC 0 +#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..a9580c2 --- /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 + +#include + +/**@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..10c581e --- /dev/null +++ b/Include/ext4_debug.h @@ -0,0 +1,192 @@ +/* + * 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 +#include + +#if !CONFIG_HAVE_OWN_ASSERT +#include +#endif + +#include +#include + +#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 +#include + +/**@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 +#include + +#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..0fa631f --- /dev/null +++ b/Include/ext4_dir.h @@ -0,0 +1,301 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +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; +}; + + +/**@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..f1c5b26 --- /dev/null +++ b/Include/ext4_dir_idx.h @@ -0,0 +1,112 @@ +/* + * 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 +#include + +#include +#include + +#include +#include + +struct ext4_dir_idx_block { + struct ext4_block b; + struct ext4_dir_idx_entry *entries; + struct ext4_dir_idx_entry *position; +}; + +#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, uint32_t name_len); + +/**@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..2d92280 --- /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 + +#if !CONFIG_HAVE_OWN_ERRNO +#include +#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..fee0926 --- /dev/null +++ b/Include/ext4_extent.h @@ -0,0 +1,74 @@ +/* + * 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 +#include +#include + +void ext4_extent_tree_init(struct ext4_inode_ref *inode_ref); + + +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..c520326 --- /dev/null +++ b/Include/ext4_fs.h @@ -0,0 +1,280 @@ +/* + * 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 +#include +#include + +#include +#include + +struct ext4_fs { + bool read_only; + + 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; +}; + +struct ext4_block_group_ref { + struct ext4_block block; + struct ext4_bgroup *block_group; + struct ext4_fs *fs; + uint32_t index; + bool dirty; +}; + +struct ext4_inode_ref { + struct ext4_block block; + struct ext4_inode *inode; + struct ext4_fs *fs; + uint32_t index; + bool dirty; +}; + + +/**@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) + 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 + * @param read_only Mark the filesystem as read-only. + * @return Error code + */ +int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev, + bool read_only); + +/**@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, + ext4_lblk_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, + ext4_lblk_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, ext4_lblk_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..43ecbab --- /dev/null +++ b/Include/ext4_hash.h @@ -0,0 +1,76 @@ +/* + * 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 + +#include + +struct ext4_hash_info { + uint32_t hash; + uint32_t minor_hash; + uint32_t hash_version; + const uint32_t *seed; +}; + + +/**@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..e845c79 --- /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 +#include + +/**@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..bee5b8a --- /dev/null +++ b/Include/ext4_inode.h @@ -0,0 +1,354 @@ +/* + * 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 +#include + +#include + +/**@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 sb Superblock + * @param inode I-node + * @return extra I-node size + */ +uint16_t ext4_inode_get_extra_isize(struct ext4_sblock *sb, + struct ext4_inode *inode); + +/**@brief Set extra I-node size field. + * @param sb Superblock + * @param inode I-node + * @param size extra I-node size + */ +void ext4_inode_set_extra_isize(struct ext4_sblock *sb, + 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 Get device number + * @param inode I-node to get device number from + * @return Device number + */ +uint32_t ext4_inode_get_dev(struct ext4_inode *inode); + +/**@brief Set device number + * @param inode I-node to set device number to + * @param dev Device number + */ +void ext4_inode_set_dev(struct ext4_inode *inode, uint32_t dev); + +/**@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..415618b --- /dev/null +++ b/Include/ext4_journal.h @@ -0,0 +1,148 @@ +/* + * 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 +#include +#include +#include + +struct jbd_fs { + struct ext4_blockdev *bdev; + struct ext4_inode_ref inode_ref; + struct jbd_sb sb; + + bool dirty; +}; + +struct jbd_buf { + uint32_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; + RB_ENTRY(jbd_revoke_rec) revoke_node; +}; + +struct jbd_block_rec { + ext4_fsblk_t lba; + 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; + RB_HEAD(jbd_revoke_tree, jbd_revoke_rec) revoke_root; + 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_cp_queue, jbd_trans) cp_queue; + RB_HEAD(jbd_block, jbd_block_rec) block_rec_root; + + struct jbd_fs *jbd_fs; +}; + +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_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); +void +jbd_journal_purge_cp_trans(struct jbd_journal *journal, + bool flush, + bool once); + +#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..97a4459 --- /dev/null +++ b/Include/ext4_mbr.h @@ -0,0 +1,73 @@ +/* + * 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 +#include + +/**@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 { + + /**@brief Percentage division tab: + * - {50, 20, 10, 20} + * Sum of all 4 elements must be <= 100*/ + uint8_t division[4]; +}; + +int ext4_mbr_write(struct ext4_blockdev *parent, struct ext4_mbr_parts *parts, uint32_t disk_id); + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_MBR_H_ */ + +/** + * @} + */ diff --git a/Include/ext4_misc.h b/Include/ext4_misc.h new file mode 100644 index 0000000..3067d4d --- /dev/null +++ b/Include/ext4_misc.h @@ -0,0 +1,156 @@ +/* + * 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_misc.h + * @brief Miscellaneous helpers. + */ + +#ifndef EXT4_MISC_H_ +#define EXT4_MISC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/**************************************************************/ + +#define EXT4_DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y)) +#define EXT4_ALIGN(x, y) ((y) * EXT4_DIV_ROUND_UP((x), (y))) + +/****************************Endian conversion*****************/ + +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_MISC_H_ */ + +/** + * @} + */ diff --git a/Include/ext4_mkfs.h b/Include/ext4_mkfs.h new file mode 100644 index 0000000..aadedb0 --- /dev/null +++ b/Include/ext4_mkfs.h @@ -0,0 +1,85 @@ +/* + * 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 +#include + +#include +#include + +#include +#include + +struct ext4_mkfs_info { + uint64_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; + uint32_t feat_ro_compat; + uint32_t feat_compat; + uint32_t feat_incompat; + uint32_t bg_desc_reserve_blocks; + uint16_t dsc_size; + uint8_t uuid[UUID_SIZE]; + bool 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 + #include +#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..5afd18d --- /dev/null +++ b/Include/ext4_super.h @@ -0,0 +1,238 @@ +/* + * 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 +#include +#include + +/**@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..b17373c --- /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 +#include + + +/**@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..c9cdd34 --- /dev/null +++ b/Include/ext4_types.h @@ -0,0 +1,844 @@ +/* + * 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 +#include +#include + +#include +#include + +/* + * Types of blocks. + */ +typedef uint32_t ext4_lblk_t; +typedef uint64_t ext4_fsblk_t; + + +#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 + + +/* 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 */ +}; + + +#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 + + +#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[]; /* Entry name */ +}; + +/* 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[]; +}; + +/* + * 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 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 + +#define EXT4_GOOD_OLD_INODE_SIZE 128 + +/*****************************************************************************/ + +/* + * 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) + +/*****************************************************************************/ + +#define EXT4_CRC32_INIT (0xFFFFFFFFUL) + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + + +#if CONFIG_USE_USER_MALLOC + +#define ext4_malloc ext4_user_malloc +#define ext4_calloc ext4_user_calloc +#define ext4_realloc ext4_user_realloc +#define ext4_free ext4_user_free + +#else + +#define ext4_malloc malloc +#define ext4_calloc calloc +#define ext4_realloc realloc +#define ext4_free free + +#endif + + +#endif /* EXT4_TYPES_H_ */ + +/** + * @} + */ diff --git a/Include/ext4_xattr.h b/Include/ext4_xattr.h new file mode 100644 index 0000000..d79febc --- /dev/null +++ b/Include/ext4_xattr.h @@ -0,0 +1,108 @@ +/* + * 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 +#include +#include + +struct ext4_xattr_info { + uint8_t name_index; + const char *name; + size_t name_len; + const void *value; + size_t value_len; +}; + +struct ext4_xattr_list_entry { + uint8_t name_index; + char *name; + size_t name_len; + struct ext4_xattr_list_entry *next; +}; + +struct ext4_xattr_search { + /* The first entry in the buffer */ + struct ext4_xattr_entry *first; + + /* The address of the buffer */ + void *base; + + /* The first inaccessible address */ + void *end; + + /* The current entry pointer */ + struct ext4_xattr_entry *here; + + /* Entry not found */ + bool not_found; +}; + +const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len, + uint8_t *name_index, size_t *name_len, + bool *found); + +const char *ext4_get_xattr_name_prefix(uint8_t name_index, + size_t *ret_prefix_len); + +int ext4_xattr_list(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_list_entry *list, size_t *list_len); + +int ext4_xattr_get(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len, void *buf, size_t buf_len, + size_t *data_len); + +int ext4_xattr_remove(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len); + +int ext4_xattr_set(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len, const void *value, + size_t value_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 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../ext4_config.h" + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ + RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \ + RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \ + RB_PROTOTYPE_INSERT(name, type, attr); \ + RB_PROTOTYPE_REMOVE(name, type, attr); \ + RB_PROTOTYPE_FIND(name, type, attr); \ + RB_PROTOTYPE_NFIND(name, type, attr); \ + RB_PROTOTYPE_NEXT(name, type, attr); \ + RB_PROTOTYPE_PREV(name, type, attr); \ + RB_PROTOTYPE_MINMAX(name, type, attr); +#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \ + attr void name##_RB_INSERT_COLOR(struct name *, struct type *) +#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \ + attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *) +#define RB_PROTOTYPE_REMOVE(name, type, attr) \ + attr struct type *name##_RB_REMOVE(struct name *, struct type *) +#define RB_PROTOTYPE_INSERT(name, type, attr) \ + attr struct type *name##_RB_INSERT(struct name *, struct type *) +#define RB_PROTOTYPE_FIND(name, type, attr) \ + attr struct type *name##_RB_FIND(struct name *, struct type *) +#define RB_PROTOTYPE_NFIND(name, type, attr) \ + attr struct type *name##_RB_NFIND(struct name *, struct type *) +#define RB_PROTOTYPE_NEXT(name, type, attr) \ + attr struct type *name##_RB_NEXT(struct type *) +#define RB_PROTOTYPE_PREV(name, type, attr) \ + attr struct type *name##_RB_PREV(struct type *) +#define RB_PROTOTYPE_MINMAX(name, type, attr) \ + attr struct type *name##_RB_MINMAX(struct name *, int) + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ + RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ + RB_GENERATE_INSERT(name, type, field, cmp, attr) \ + RB_GENERATE_REMOVE(name, type, field, attr) \ + RB_GENERATE_FIND(name, type, field, cmp, attr) \ + RB_GENERATE_NFIND(name, type, field, cmp, attr) \ + RB_GENERATE_NEXT(name, type, field, attr) \ + RB_GENERATE_PREV(name, type, field, attr) \ + RB_GENERATE_MINMAX(name, type, field, attr) + +#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} + +#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} + +#define RB_GENERATE_REMOVE(name, type, field, attr) \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + +#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} + +#define RB_GENERATE_FIND(name, type, field, cmp, attr) \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} + +#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} + +#define RB_GENERATE_NEXT(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} + +#define RB_GENERATE_PREV(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} + +#define RB_GENERATE_MINMAX(name, type, field, attr) \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_TREE_H_ */ diff --git a/Library/AbmLib/AbmLib.inf b/Library/AbmLib/AbmLib.inf index 39b14f2..ccefd73 100644 --- a/Library/AbmLib/AbmLib.inf +++ b/Library/AbmLib/AbmLib.inf @@ -31,6 +31,7 @@ TimerLib BootLib EfiFileLib + lwext4Lib [Sources.common] abm_base.h diff --git a/Library/lwext4/blockdev/blockdev.c b/Library/lwext4/blockdev/blockdev.c new file mode 100644 index 0000000..a44a2e1 --- /dev/null +++ b/Library/lwext4/blockdev/blockdev.c @@ -0,0 +1,99 @@ +/* + * 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. + */ + + +#include +#include +#include + + +/**********************BLOCKDEV INTERFACE**************************************/ +static int blockdev_open(struct ext4_blockdev *bdev); +static int blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, + uint32_t blk_cnt); +static int blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt); +static int blockdev_close(struct ext4_blockdev *bdev); +static int blockdev_lock(struct ext4_blockdev *bdev); +static int blockdev_unlock(struct ext4_blockdev *bdev); + +/******************************************************************************/ +EXT4_BLOCKDEV_STATIC_INSTANCE(blockdev, 512, 0, blockdev_open, + blockdev_bread, blockdev_bwrite, blockdev_close, + blockdev_lock, blockdev_unlock); + +/******************************************************************************/ +static int blockdev_open(struct ext4_blockdev *bdev) +{ + /*blockdev_open: skeleton*/ + return EIO; +} + +/******************************************************************************/ + +static int blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, + uint32_t blk_cnt) +{ + /*blockdev_bread: skeleton*/ + return EIO; +} + + +/******************************************************************************/ +static int blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + /*blockdev_bwrite: skeleton*/ + return EIO; +} +/******************************************************************************/ +static int blockdev_close(struct ext4_blockdev *bdev) +{ + /*blockdev_close: skeleton*/ + return EIO; +} + +static int blockdev_lock(struct ext4_blockdev *bdev) +{ + /*blockdev_lock: skeleton*/ + return EIO; +} + +static int blockdev_unlock(struct ext4_blockdev *bdev) +{ + /*blockdev_unlock: skeleton*/ + return EIO; +} + +/******************************************************************************/ +struct ext4_blockdev *ext4_blockdev_get(void) +{ + return &blockdev; +} +/******************************************************************************/ + diff --git a/Library/lwext4/blockdev/blockdev.h b/Library/lwext4/blockdev/blockdev.h new file mode 100644 index 0000000..17f947a --- /dev/null +++ b/Library/lwext4/blockdev/blockdev.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef BLOCKDEV_H_ +#define BLOCKDEV_H_ + +#include +#include + +#include +#include + +/**@brief File blockdev get.*/ +struct ext4_blockdev *ext4_blockdev_get(void); + +#endif /* BLOCKDEV_H_ */ diff --git a/Library/lwext4/blockdev/linux/file_dev.c b/Library/lwext4/blockdev/linux/file_dev.c new file mode 100644 index 0000000..dd0743b --- /dev/null +++ b/Library/lwext4/blockdev/linux/file_dev.c @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include + +/**@brief Default filename.*/ +static const char *fname = "ext2"; + +/**@brief Image block size.*/ +#define EXT4_FILEDEV_BSIZE 512 + +/**@brief Image file descriptor.*/ +static FILE *dev_file; + +#define DROP_LINUXCACHE_BUFFERS 0 + +/**********************BLOCKDEV INTERFACE**************************************/ +static int file_dev_open(struct ext4_blockdev *bdev); +static int file_dev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, + uint32_t blk_cnt); +static int file_dev_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt); +static int file_dev_close(struct ext4_blockdev *bdev); + +/******************************************************************************/ +EXT4_BLOCKDEV_STATIC_INSTANCE(file_dev, EXT4_FILEDEV_BSIZE, 0, file_dev_open, + file_dev_bread, file_dev_bwrite, file_dev_close, 0, 0); + +/******************************************************************************/ +static int file_dev_open(struct ext4_blockdev *bdev) +{ + dev_file = fopen(fname, "r+b"); + + if (!dev_file) + return EIO; + + /*No buffering at file.*/ + setbuf(dev_file, 0); + + if (fseeko(dev_file, 0, SEEK_END)) + return EFAULT; + + file_dev.part_offset = 0; + file_dev.part_size = ftello(dev_file); + file_dev.bdif->ph_bcnt = file_dev.part_size / file_dev.bdif->ph_bsize; + + return EOK; +} + +/******************************************************************************/ + +static int file_dev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, + uint32_t blk_cnt) +{ + if (fseeko(dev_file, blk_id * bdev->bdif->ph_bsize, SEEK_SET)) + return EIO; + if (!blk_cnt) + return EOK; + if (!fread(buf, bdev->bdif->ph_bsize * blk_cnt, 1, dev_file)) + return EIO; + + return EOK; +} + +static void drop_cache(void) +{ +#if defined(__linux__) && DROP_LINUXCACHE_BUFFERS + int fd; + char *data = "3"; + + sync(); + fd = open("/proc/sys/vm/drop_caches", O_WRONLY); + write(fd, data, sizeof(char)); + close(fd); +#endif +} + +/******************************************************************************/ +static int file_dev_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + if (fseeko(dev_file, blk_id * bdev->bdif->ph_bsize, SEEK_SET)) + return EIO; + if (!blk_cnt) + return EOK; + if (!fwrite(buf, bdev->bdif->ph_bsize * blk_cnt, 1, dev_file)) + return EIO; + + drop_cache(); + return EOK; +} +/******************************************************************************/ +static int file_dev_close(struct ext4_blockdev *bdev) +{ + fclose(dev_file); + return EOK; +} + +/******************************************************************************/ +struct ext4_blockdev *file_dev_get(void) +{ + return &file_dev; +} +/******************************************************************************/ +void file_dev_name_set(const char *n) +{ + fname = n; +} +/******************************************************************************/ diff --git a/Library/lwext4/blockdev/linux/file_dev.h b/Library/lwext4/blockdev/linux/file_dev.h new file mode 100644 index 0000000..ce4690d --- /dev/null +++ b/Library/lwext4/blockdev/linux/file_dev.h @@ -0,0 +1,43 @@ +/* + * 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. + */ +#ifndef FILE_DEV_H_ +#define FILE_DEV_H_ + +#include +#include + +#include +#include + +/**@brief File blockdev get.*/ +struct ext4_blockdev *file_dev_get(void); + +/**@brief Set filename to open.*/ +void file_dev_name_set(const char *n); + +#endif /* FILE_DEV_H_ */ diff --git a/Library/lwext4/blockdev/windows/file_windows.c b/Library/lwext4/blockdev/windows/file_windows.c new file mode 100644 index 0000000..9142132 --- /dev/null +++ b/Library/lwext4/blockdev/windows/file_windows.c @@ -0,0 +1,170 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include + +/**@brief Default filename.*/ +static const char *fname = "ext2"; + +/**@brief IO block size.*/ +#define EXT4_IORAW_BSIZE 512 + +/**@brief Image file descriptor.*/ +static HANDLE dev_file; + +/**********************BLOCKDEV INTERFACE**************************************/ +static int file_open(struct ext4_blockdev *bdev); +static int file_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, + uint32_t blk_cnt); +static int file_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt); +static int file_close(struct ext4_blockdev *bdev); + +/******************************************************************************/ +EXT4_BLOCKDEV_STATIC_INSTANCE(_filedev, EXT4_IORAW_BSIZE, 0, file_open, + file_bread, file_bwrite, file_close, 0, 0); + +/******************************************************************************/ +static int file_open(struct ext4_blockdev *bdev) +{ + char path[64]; + DISK_GEOMETRY pdg; + uint64_t disk_size; + BOOL bResult = FALSE; + DWORD junk; + + sprintf(path, "\\\\.\\%s", fname); + + dev_file = + CreateFile(path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); + + if (dev_file == INVALID_HANDLE_VALUE) { + return EIO; + } + + bResult = + DeviceIoControl(dev_file, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &pdg, sizeof(pdg), &junk, (LPOVERLAPPED)NULL); + + if (bResult == FALSE) { + CloseHandle(dev_file); + return EIO; + } + + disk_size = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder * + (ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector; + + _filedev.bdif->ph_bsize = pdg.BytesPerSector; + _filedev.bdif->ph_bcnt = disk_size / pdg.BytesPerSector; + + _filedev.part_offset = 0; + _filedev.part_size = disk_size; + + return EOK; +} + +/******************************************************************************/ + +static int file_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, + uint32_t blk_cnt) +{ + long hipart = blk_id >> (32 - 9); + long lopart = blk_id << 9; + long err; + + SetLastError(0); + lopart = SetFilePointer(dev_file, lopart, &hipart, FILE_BEGIN); + + if (lopart == -1 && NO_ERROR != (err = GetLastError())) { + return EIO; + } + + DWORD n; + + if (!ReadFile(dev_file, buf, blk_cnt * 512, &n, NULL)) { + err = GetLastError(); + return EIO; + } + return EOK; +} + +/******************************************************************************/ +static int file_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + long hipart = blk_id >> (32 - 9); + long lopart = blk_id << 9; + long err; + + SetLastError(0); + lopart = SetFilePointer(dev_file, lopart, &hipart, FILE_BEGIN); + + if (lopart == -1 && NO_ERROR != (err = GetLastError())) { + return EIO; + } + + DWORD n; + + if (!WriteFile(dev_file, buf, blk_cnt * 512, &n, NULL)) { + err = GetLastError(); + return EIO; + } + return EOK; +} + +/******************************************************************************/ +static int file_close(struct ext4_blockdev *bdev) +{ + CloseHandle(dev_file); + return EOK; +} + +/******************************************************************************/ +struct ext4_blockdev *file_windows_dev_get(void) +{ + return &_filedev; +} +/******************************************************************************/ +void file_windows_name_set(const char *n) +{ + fname = n; +} + +/******************************************************************************/ +#endif diff --git a/Library/lwext4/blockdev/windows/file_windows.h b/Library/lwext4/blockdev/windows/file_windows.h new file mode 100644 index 0000000..013d1f3 --- /dev/null +++ b/Library/lwext4/blockdev/windows/file_windows.h @@ -0,0 +1,44 @@ +/* + * 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. + */ +#ifndef FILE_WINDOWS_H_ +#define FILE_WINDOWS_H_ + +#include +#include + +#include +#include + +/**@brief IO raw blockdev get.*/ +struct ext4_blockdev *file_windows_dev_get(void); + +/**@brief Set filrname to open.*/ +void file_windows_name_set(const char *n); + + +#endif /* FILE_WINDOWS_H_ */ diff --git a/Library/lwext4/include/ext4.h b/Library/lwext4/include/ext4.h new file mode 100644 index 0000000..516bbdd --- /dev/null +++ b/Library/lwext4/include/ext4.h @@ -0,0 +1,628 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +#include + +/********************************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 Actual file position.*/ + uint64_t fpos; +} ext4_file; + +/*****************************DIRECTORY DESCRIPTOR***************************/ + +/**@brief Directory entry descriptor. */ +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; + +/**@brief Directory descriptor. */ +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 block device. + * + * @param bd Block device. + * @param dev_name Block device name. + * + * @return Standard error code.*/ +int ext4_device_register(struct ext4_blockdev *bd, + const char *dev_name); + +/**@brief Un-register block device. + * + * @param dev_name Block device name. + * + * @return Standard error code.*/ +int ext4_device_unregister(const char *dev_name); + +/**@brief Un-register all block devices. + * + * @return Standard error code.*/ +int ext4_device_unregister_all(void); + +/**@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/ + * @param read_only mount as read-only mode. + * + * @return Standard error code */ +int ext4_mount(const char *dev_name, + const char *mount_point, + bool read_only); + +/**@brief Umount operation. + * + * @param mount_pount Mount point. + * + * @return Standard error code */ +int ext4_umount(const char *mount_point); + +/**@brief Starts 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_pount Mount point. + * + * @return Standard error code. */ +int ext4_journal_start(const char *mount_point); + +/**@brief Stops journaling. Journaling start/stop functions are transparent + * and might be used on filesystems without journaling support. + * + * @param mount_pount Mount point name. + * + * @return Standard error code. */ +int ext4_journal_stop(const char *mount_point); + +/**@brief Journal recovery. + * @warning Must be called after @ref ext4_mount. + * + * @param mount_pount Mount point. + * + * @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 mount point stats. + * + * @param mount_pount Mount point. + * @param stats Filesystem 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_pount Mount point. + * @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_pount Mount point. + * @param sb Superblock handle + * + * @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_fwrite(...); + * < --- data is flushed to physical drive + * + * When you do: + * ext4_cache_write_back(..., 1); + * ext4_fopen(...); + * ext4_fwrite(...); + * < --- 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 mount_pount Mount point. + * @param on Enable/disable cache writeback mode. + * + * @return Standard error code. */ +int ext4_cache_write_back(const char *path, bool on); + + +/**@brief Force cache flush. + * + * @param mount_pount Mount point. + * + * @return Standard error code. */ +int ext4_cache_flush(const char *path); + +/********************************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 file File handle. + * @param path File path, has to start from mount point:/my_partition/file. + * @param flags File open 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 *file, const char *path, const char *flags); + +/**@brief Alternate file open function. + * + * @param file File handle. + * @param path File path, has to start from mount point:/my_partition/file. + * @param flags File open flags. + * + * @return Standard error code.*/ +int ext4_fopen2(ext4_file *file, const char *path, int flags); + +/**@brief File close function. + * + * @param file File handle. + * + * @return Standard error code.*/ +int ext4_fclose(ext4_file *file); + + +/**@brief File truncate function. + * + * @param file File handle. + * @param size New file size. + * + * @return Standard error code.*/ +int ext4_ftruncate(ext4_file *file, uint64_t size); + +/**@brief Read data from file. + * + * @param file File handle. + * @param buf Output buffer. + * @param size Bytes to read. + * @param rcnt Bytes read (NULL allowed). + * + * @return Standard error code.*/ +int ext4_fread(ext4_file *file, void *buf, size_t size, size_t *rcnt); + +/**@brief Write data to file. + * + * @param file File handle. + * @param buf Data to write + * @param size Write length.. + * @param wcnt Bytes written (NULL allowed). + * + * @return Standard error code.*/ +int ext4_fwrite(ext4_file *file, const void *buf, size_t size, size_t *wcnt); + +/**@brief File seek operation. + * + * @param file 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 *file, int64_t offset, uint32_t origin); + +/**@brief Get file position. + * + * @param file File handle. + * + * @return Actual file position */ +uint64_t ext4_ftell(ext4_file *file); + +/**@brief Get file size. + * + * @param file File handle. + * + * @return File size. */ +uint64_t ext4_fsize(ext4_file *file); + + +/**@brief Get inode of file/directory/link. + * + * @param path Parh to file/dir/link. + * @param ret_ino Inode number. + * @param inode Inode internals. + * + * @return Standard error code.*/ +int ext4_raw_inode_fill(const char *path, uint32_t *ret_ino, + struct ext4_inode *inode); + +/**@brief Check if inode exists. + * + * @param path Parh to file/dir/link. + * @param type Inode type. + * @ref EXT4_DIRENTRY_UNKNOWN + * @ref EXT4_DE_REG_FILE + * @ref EXT4_DE_DIR + * @ref EXT4_DE_CHRDEV + * @ref EXT4_DE_BLKDEV + * @ref EXT4_DE_FIFO + * @ref EXT4_DE_SOCK + * @ref EXT4_DE_SYMLINK + * + * @return Standard error code.*/ +int ext4_inode_exist(const char *path, int type); + +/**@brief Change file/directory/link mode bits. + * + * @param path Path to file/dir/link. + * @param mode New mode bits (for example 0777). + * + * @return Standard error code.*/ +int ext4_mode_set(const char *path, uint32_t mode); + + +/**@brief Get file/directory/link mode bits. + * + * @param path Path to file/dir/link. + * @param mode New mode bits (for example 0777). + * + * @return Standard error code.*/ +int ext4_mode_get(const char *path, uint32_t *mode); + +/**@brief Change file owner and group. + * + * @param path Path to file/dir/link. + * @param uid User id. + * @param gid Group id. + * + * @return Standard error code.*/ +int ext4_owner_set(const char *path, uint32_t uid, uint32_t gid); + +/**@brief Get file/directory/link owner and group. + * + * @param path Path to file/dir/link. + * @param uid User id. + * @param gid Group id. + * + * @return Standard error code.*/ +int ext4_owner_get(const char *path, uint32_t *uid, uint32_t *gid); + +/**@brief Set file/directory/link access time. + * + * @param path Path to file/dir/link. + * @param atime Access timestamp. + * + * @return Standard error code.*/ +int ext4_atime_set(const char *path, uint32_t atime); + +/**@brief Set file/directory/link modify time. + * + * @param path Path to file/dir/link. + * @param mtime Modify timestamp. + * + * @return Standard error code.*/ +int ext4_mtime_set(const char *path, uint32_t mtime); + +/**@brief Set file/directory/link change time. + * + * @param path Path to file/dir/link. + * @param ctime Change timestamp. + * + * @return Standard error code.*/ +int ext4_ctime_set(const char *path, uint32_t ctime); + +/**@brief Get file/directory/link access time. + * + * @param path Path to file/dir/link. + * @param atime Access timestamp. + * + * @return Standard error code.*/ +int ext4_atime_get(const char *path, uint32_t *atime); + +/**@brief Get file/directory/link modify time. + * + * @param path Path to file/dir/link. + * @param mtime Modify timestamp. + * + * @return Standard error code.*/ +int ext4_mtime_get(const char *path, uint32_t *mtime); + +/**@brief Get file/directory/link change time. + * + * @param path Pathto file/dir/link. + * @param ctime Change timestamp. + * + * @return standard error code*/ +int ext4_ctime_get(const char *path, uint32_t *ctime); + +/**@brief Create symbolic link. + * + * @param target Destination entry path. + * @param path Source entry path. + * + * @return Standard error code.*/ +int ext4_fsymlink(const char *target, const char *path); + +/**@brief Create special file. + * @param path Path to new special file. + * @param filetype Filetype of the new special file. + * (that must not be regular file, directory, or unknown type) + * @param dev If filetype is char device or block device, + * the device number will become the payload in the inode. + * @return Standard error code.*/ +int ext4_mknod(const char *path, int filetype, uint32_t dev); + +/**@brief Read symbolic link payload. + * + * @param path 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 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. + * + * @return Standard error code.*/ +int ext4_setxattr(const char *path, const char *name, size_t name_len, + const void *data, size_t data_size); + +/**@brief Get extended attribute. + * + * @param path 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 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 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 Rename/move directory. + * + * @param path Source path. + * @param new_path Destination path. + * + * @return Standard error code. */ +int ext4_dir_mv(const char *path, const char *new_path); + +/**@brief Create new directory. + * + * @param path Directory name. + * + * @return Standard error code.*/ +int ext4_dir_mk(const char *path); + +/**@brief Directory open. + * + * @param dir Directory handle. + * @param path Directory path. + * + * @return Standard error code.*/ +int ext4_dir_open(ext4_dir *dir, const char *path); + +/**@brief Directory close. + * + * @param dir directory handle. + * + * @return Standard error code.*/ +int ext4_dir_close(ext4_dir *dir); + +/**@brief Return next directory entry. + * + * @param dir Directory handle. + * + * @return Directory entry id (NULL if no entry)*/ +const ext4_direntry *ext4_dir_entry_next(ext4_dir *dir); + +/**@brief Rewine directory entry offset. + * + * @param dir Directory handle.*/ +void ext4_dir_entry_rewind(ext4_dir *dir); + + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_balloc.h b/Library/lwext4/include/ext4_balloc.h new file mode 100644 index 0000000..ad481dc --- /dev/null +++ b/Library/lwext4/include/ext4_balloc.h @@ -0,0 +1,121 @@ +/* + * 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 +#include + +#include + +#include +#include + +/**@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/Library/lwext4/include/ext4_bcache.h b/Library/lwext4/include/ext4_bcache.h new file mode 100644 index 0000000..f8bec7d --- /dev/null +++ b/Library/lwext4/include/ext4_bcache.h @@ -0,0 +1,297 @@ +/* + * 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 + +#include +#include +#include +#include + +#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 buffer. + * @param bc block cache descriptor + * @param buf buffer*/ +void ext4_bcache_invalidate_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/Library/lwext4/include/ext4_bitmap.h b/Library/lwext4/include/ext4_bitmap.h new file mode 100644 index 0000000..6bcb100 --- /dev/null +++ b/Library/lwext4/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 + +#include +#include + +/**@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/Library/lwext4/include/ext4_block_group.h b/Library/lwext4/include/ext4_block_group.h new file mode 100644 index 0000000..61fbc98 --- /dev/null +++ b/Library/lwext4/include/ext4_block_group.h @@ -0,0 +1,330 @@ +/* + * 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 +#include +#include + +#include +#include + +/**@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); + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_BLOCK_GROUP_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_blockdev.h b/Library/lwext4/include/ext4_blockdev.h new file mode 100644 index 0000000..4efbb43 --- /dev/null +++ b/Library/lwext4/include/ext4_blockdev.h @@ -0,0 +1,269 @@ +/* + * 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 +#include + +#include +#include + +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 User data pointer*/ + void* p_user; +}; + +/**@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; + + void *journal; +}; + +/**@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, uint32_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/Library/lwext4/include/ext4_config.h b/Library/lwext4/include/ext4_config.h new file mode 100644 index 0000000..810df4c --- /dev/null +++ b/Library/lwext4/include/ext4_config.h @@ -0,0 +1,180 @@ +/* + * 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 + +/*****************************************************************************/ + +#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/disable xattr*/ +#ifndef CONFIG_XATTR_ENABLE +#define CONFIG_XATTR_ENABLE 1 +#endif + +/**@brief Enable/disable extents*/ +#ifndef CONFIG_EXTENTS_ENABLE +#define CONFIG_EXTENTS_ENABLE 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 name*/ +#ifndef CONFIG_EXT4_MAX_BLOCKDEV_NAME +#define CONFIG_EXT4_MAX_BLOCKDEV_NAME 32 +#endif + + +/**@brief Maximum block device count*/ +#ifndef CONFIG_EXT4_BLOCKDEVS_COUNT +#define CONFIG_EXT4_BLOCKDEVS_COUNT 2 +#endif + +/**@brief Maximum mountpoint name*/ +#ifndef CONFIG_EXT4_MAX_MP_NAME +#define CONFIG_EXT4_MAX_MP_NAME 32 +#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 + + +/**@brief Unaligned access switch on/off*/ +#ifndef CONFIG_UNALIGNED_ACCESS +#define CONFIG_UNALIGNED_ACCESS 0 +#endif + +/**@brief Switches use of malloc/free functions family + * from standard library to user provided*/ +#ifndef CONFIG_USE_USER_MALLOC +#define CONFIG_USE_USER_MALLOC 0 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_CONFIG_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_crc32.h b/Library/lwext4/include/ext4_crc32.h new file mode 100644 index 0000000..a9580c2 --- /dev/null +++ b/Library/lwext4/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 + +#include + +/**@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/Library/lwext4/include/ext4_debug.h b/Library/lwext4/include/ext4_debug.h new file mode 100644 index 0000000..10c581e --- /dev/null +++ b/Library/lwext4/include/ext4_debug.h @@ -0,0 +1,192 @@ +/* + * 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 +#include + +#if !CONFIG_HAVE_OWN_ASSERT +#include +#endif + +#include +#include + +#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 +#include + +/**@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 +#include + +#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/Library/lwext4/include/ext4_dir.h b/Library/lwext4/include/ext4_dir.h new file mode 100644 index 0000000..0fa631f --- /dev/null +++ b/Library/lwext4/include/ext4_dir.h @@ -0,0 +1,301 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +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; +}; + + +/**@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/Library/lwext4/include/ext4_dir_idx.h b/Library/lwext4/include/ext4_dir_idx.h new file mode 100644 index 0000000..f1c5b26 --- /dev/null +++ b/Library/lwext4/include/ext4_dir_idx.h @@ -0,0 +1,112 @@ +/* + * 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 +#include + +#include +#include + +#include +#include + +struct ext4_dir_idx_block { + struct ext4_block b; + struct ext4_dir_idx_entry *entries; + struct ext4_dir_idx_entry *position; +}; + +#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, uint32_t name_len); + +/**@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/Library/lwext4/include/ext4_errno.h b/Library/lwext4/include/ext4_errno.h new file mode 100644 index 0000000..2d92280 --- /dev/null +++ b/Library/lwext4/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 + +#if !CONFIG_HAVE_OWN_ERRNO +#include +#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/Library/lwext4/include/ext4_extent.h b/Library/lwext4/include/ext4_extent.h new file mode 100644 index 0000000..fee0926 --- /dev/null +++ b/Library/lwext4/include/ext4_extent.h @@ -0,0 +1,74 @@ +/* + * 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 +#include +#include + +void ext4_extent_tree_init(struct ext4_inode_ref *inode_ref); + + +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/Library/lwext4/include/ext4_fs.h b/Library/lwext4/include/ext4_fs.h new file mode 100644 index 0000000..c520326 --- /dev/null +++ b/Library/lwext4/include/ext4_fs.h @@ -0,0 +1,280 @@ +/* + * 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 +#include +#include + +#include +#include + +struct ext4_fs { + bool read_only; + + 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; +}; + +struct ext4_block_group_ref { + struct ext4_block block; + struct ext4_bgroup *block_group; + struct ext4_fs *fs; + uint32_t index; + bool dirty; +}; + +struct ext4_inode_ref { + struct ext4_block block; + struct ext4_inode *inode; + struct ext4_fs *fs; + uint32_t index; + bool dirty; +}; + + +/**@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) + 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 + * @param read_only Mark the filesystem as read-only. + * @return Error code + */ +int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev, + bool read_only); + +/**@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, + ext4_lblk_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, + ext4_lblk_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, ext4_lblk_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/Library/lwext4/include/ext4_hash.h b/Library/lwext4/include/ext4_hash.h new file mode 100644 index 0000000..43ecbab --- /dev/null +++ b/Library/lwext4/include/ext4_hash.h @@ -0,0 +1,76 @@ +/* + * 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 + +#include + +struct ext4_hash_info { + uint32_t hash; + uint32_t minor_hash; + uint32_t hash_version; + const uint32_t *seed; +}; + + +/**@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/Library/lwext4/include/ext4_ialloc.h b/Library/lwext4/include/ext4_ialloc.h new file mode 100644 index 0000000..e845c79 --- /dev/null +++ b/Library/lwext4/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 +#include + +/**@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/Library/lwext4/include/ext4_inode.h b/Library/lwext4/include/ext4_inode.h new file mode 100644 index 0000000..bee5b8a --- /dev/null +++ b/Library/lwext4/include/ext4_inode.h @@ -0,0 +1,354 @@ +/* + * 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 +#include + +#include + +/**@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 sb Superblock + * @param inode I-node + * @return extra I-node size + */ +uint16_t ext4_inode_get_extra_isize(struct ext4_sblock *sb, + struct ext4_inode *inode); + +/**@brief Set extra I-node size field. + * @param sb Superblock + * @param inode I-node + * @param size extra I-node size + */ +void ext4_inode_set_extra_isize(struct ext4_sblock *sb, + 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 Get device number + * @param inode I-node to get device number from + * @return Device number + */ +uint32_t ext4_inode_get_dev(struct ext4_inode *inode); + +/**@brief Set device number + * @param inode I-node to set device number to + * @param dev Device number + */ +void ext4_inode_set_dev(struct ext4_inode *inode, uint32_t dev); + +/**@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/Library/lwext4/include/ext4_journal.h b/Library/lwext4/include/ext4_journal.h new file mode 100644 index 0000000..415618b --- /dev/null +++ b/Library/lwext4/include/ext4_journal.h @@ -0,0 +1,148 @@ +/* + * 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 +#include +#include +#include + +struct jbd_fs { + struct ext4_blockdev *bdev; + struct ext4_inode_ref inode_ref; + struct jbd_sb sb; + + bool dirty; +}; + +struct jbd_buf { + uint32_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; + RB_ENTRY(jbd_revoke_rec) revoke_node; +}; + +struct jbd_block_rec { + ext4_fsblk_t lba; + 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; + RB_HEAD(jbd_revoke_tree, jbd_revoke_rec) revoke_root; + 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_cp_queue, jbd_trans) cp_queue; + RB_HEAD(jbd_block, jbd_block_rec) block_rec_root; + + struct jbd_fs *jbd_fs; +}; + +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_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); +void +jbd_journal_purge_cp_trans(struct jbd_journal *journal, + bool flush, + bool once); + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_JOURNAL_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_mbr.h b/Library/lwext4/include/ext4_mbr.h new file mode 100644 index 0000000..97a4459 --- /dev/null +++ b/Library/lwext4/include/ext4_mbr.h @@ -0,0 +1,73 @@ +/* + * 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 +#include + +/**@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 { + + /**@brief Percentage division tab: + * - {50, 20, 10, 20} + * Sum of all 4 elements must be <= 100*/ + uint8_t division[4]; +}; + +int ext4_mbr_write(struct ext4_blockdev *parent, struct ext4_mbr_parts *parts, uint32_t disk_id); + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_MBR_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_misc.h b/Library/lwext4/include/ext4_misc.h new file mode 100644 index 0000000..3067d4d --- /dev/null +++ b/Library/lwext4/include/ext4_misc.h @@ -0,0 +1,156 @@ +/* + * 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_misc.h + * @brief Miscellaneous helpers. + */ + +#ifndef EXT4_MISC_H_ +#define EXT4_MISC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/**************************************************************/ + +#define EXT4_DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y)) +#define EXT4_ALIGN(x, y) ((y) * EXT4_DIV_ROUND_UP((x), (y))) + +/****************************Endian conversion*****************/ + +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_MISC_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_mkfs.h b/Library/lwext4/include/ext4_mkfs.h new file mode 100644 index 0000000..aadedb0 --- /dev/null +++ b/Library/lwext4/include/ext4_mkfs.h @@ -0,0 +1,85 @@ +/* + * 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 +#include + +#include +#include + +#include +#include + +struct ext4_mkfs_info { + uint64_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; + uint32_t feat_ro_compat; + uint32_t feat_compat; + uint32_t feat_incompat; + uint32_t bg_desc_reserve_blocks; + uint16_t dsc_size; + uint8_t uuid[UUID_SIZE]; + bool 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/Library/lwext4/include/ext4_oflags.h b/Library/lwext4/include/ext4_oflags.h new file mode 100644 index 0000000..7f7be7e --- /dev/null +++ b/Library/lwext4/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 + #include +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EXT4_OFLAGS_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_super.h b/Library/lwext4/include/ext4_super.h new file mode 100644 index 0000000..5afd18d --- /dev/null +++ b/Library/lwext4/include/ext4_super.h @@ -0,0 +1,238 @@ +/* + * 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 +#include +#include + +/**@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/Library/lwext4/include/ext4_trans.h b/Library/lwext4/include/ext4_trans.h new file mode 100644 index 0000000..b17373c --- /dev/null +++ b/Library/lwext4/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 +#include + + +/**@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/Library/lwext4/include/ext4_types.h b/Library/lwext4/include/ext4_types.h new file mode 100644 index 0000000..c9cdd34 --- /dev/null +++ b/Library/lwext4/include/ext4_types.h @@ -0,0 +1,844 @@ +/* + * 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 +#include +#include + +#include +#include + +/* + * Types of blocks. + */ +typedef uint32_t ext4_lblk_t; +typedef uint64_t ext4_fsblk_t; + + +#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 + + +/* 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 */ +}; + + +#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 + + +#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[]; /* Entry name */ +}; + +/* 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[]; +}; + +/* + * 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 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 + +#define EXT4_GOOD_OLD_INODE_SIZE 128 + +/*****************************************************************************/ + +/* + * 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) + +/*****************************************************************************/ + +#define EXT4_CRC32_INIT (0xFFFFFFFFUL) + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + + +#if CONFIG_USE_USER_MALLOC + +#define ext4_malloc ext4_user_malloc +#define ext4_calloc ext4_user_calloc +#define ext4_realloc ext4_user_realloc +#define ext4_free ext4_user_free + +#else + +#define ext4_malloc malloc +#define ext4_calloc calloc +#define ext4_realloc realloc +#define ext4_free free + +#endif + + +#endif /* EXT4_TYPES_H_ */ + +/** + * @} + */ diff --git a/Library/lwext4/include/ext4_xattr.h b/Library/lwext4/include/ext4_xattr.h new file mode 100644 index 0000000..d79febc --- /dev/null +++ b/Library/lwext4/include/ext4_xattr.h @@ -0,0 +1,108 @@ +/* + * 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 +#include +#include + +struct ext4_xattr_info { + uint8_t name_index; + const char *name; + size_t name_len; + const void *value; + size_t value_len; +}; + +struct ext4_xattr_list_entry { + uint8_t name_index; + char *name; + size_t name_len; + struct ext4_xattr_list_entry *next; +}; + +struct ext4_xattr_search { + /* The first entry in the buffer */ + struct ext4_xattr_entry *first; + + /* The address of the buffer */ + void *base; + + /* The first inaccessible address */ + void *end; + + /* The current entry pointer */ + struct ext4_xattr_entry *here; + + /* Entry not found */ + bool not_found; +}; + +const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len, + uint8_t *name_index, size_t *name_len, + bool *found); + +const char *ext4_get_xattr_name_prefix(uint8_t name_index, + size_t *ret_prefix_len); + +int ext4_xattr_list(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_list_entry *list, size_t *list_len); + +int ext4_xattr_get(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len, void *buf, size_t buf_len, + size_t *data_len); + +int ext4_xattr_remove(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len); + +int ext4_xattr_set(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len, const void *value, + size_t value_len); + +#ifdef __cplusplus +} +#endif + +#endif +/** + * @} + */ diff --git a/Library/lwext4/include/misc/queue.h b/Library/lwext4/include/misc/queue.h new file mode 100644 index 0000000..6efd6f3 --- /dev/null +++ b/Library/lwext4/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/Library/lwext4/include/misc/tree.h b/Library/lwext4/include/misc/tree.h new file mode 100644 index 0000000..8ed5d41 --- /dev/null +++ b/Library/lwext4/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 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../ext4_config.h" + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ + RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \ + RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \ + RB_PROTOTYPE_INSERT(name, type, attr); \ + RB_PROTOTYPE_REMOVE(name, type, attr); \ + RB_PROTOTYPE_FIND(name, type, attr); \ + RB_PROTOTYPE_NFIND(name, type, attr); \ + RB_PROTOTYPE_NEXT(name, type, attr); \ + RB_PROTOTYPE_PREV(name, type, attr); \ + RB_PROTOTYPE_MINMAX(name, type, attr); +#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \ + attr void name##_RB_INSERT_COLOR(struct name *, struct type *) +#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \ + attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *) +#define RB_PROTOTYPE_REMOVE(name, type, attr) \ + attr struct type *name##_RB_REMOVE(struct name *, struct type *) +#define RB_PROTOTYPE_INSERT(name, type, attr) \ + attr struct type *name##_RB_INSERT(struct name *, struct type *) +#define RB_PROTOTYPE_FIND(name, type, attr) \ + attr struct type *name##_RB_FIND(struct name *, struct type *) +#define RB_PROTOTYPE_NFIND(name, type, attr) \ + attr struct type *name##_RB_NFIND(struct name *, struct type *) +#define RB_PROTOTYPE_NEXT(name, type, attr) \ + attr struct type *name##_RB_NEXT(struct type *) +#define RB_PROTOTYPE_PREV(name, type, attr) \ + attr struct type *name##_RB_PREV(struct type *) +#define RB_PROTOTYPE_MINMAX(name, type, attr) \ + attr struct type *name##_RB_MINMAX(struct name *, int) + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ + RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ + RB_GENERATE_INSERT(name, type, field, cmp, attr) \ + RB_GENERATE_REMOVE(name, type, field, attr) \ + RB_GENERATE_FIND(name, type, field, cmp, attr) \ + RB_GENERATE_NFIND(name, type, field, cmp, attr) \ + RB_GENERATE_NEXT(name, type, field, attr) \ + RB_GENERATE_PREV(name, type, field, attr) \ + RB_GENERATE_MINMAX(name, type, field, attr) + +#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} + +#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} + +#define RB_GENERATE_REMOVE(name, type, field, attr) \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + +#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} + +#define RB_GENERATE_FIND(name, type, field, cmp, attr) \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} + +#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} + +#define RB_GENERATE_NEXT(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} + +#define RB_GENERATE_PREV(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} + +#define RB_GENERATE_MINMAX(name, type, field, attr) \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_TREE_H_ */ diff --git a/Library/lwext4/lwext4Lib.inf b/Library/lwext4/lwext4Lib.inf new file mode 100644 index 0000000..5670e60 --- /dev/null +++ b/Library/lwext4/lwext4Lib.inf @@ -0,0 +1,46 @@ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = lwext4Lib + FILE_GUID = fe526182-36e0-4a0e-8d6a-79ca9a0247c0 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = lwext4 + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + AbmPkg/Abm.dec + +[LibraryClasses] + BaseLib + IoLib + PrintLib + DebugLib + TimerLib + CacheMaintenanceLib + MicroLibC + +[Sources.common] + src/ext4_journal.c + src/ext4_trans.c + src/ext4.c + src/ext4_balloc.c + src/ext4_super.c + src/ext4_fs.c + src/ext4_extent.c + src/ext4_dir.c + src/ext4_xattr.c + src/ext4_blockdev.c + src/ext4_mkfs.c + src/ext4_debug.c + src/ext4_bcache.c + src/ext4_dir_idx.c + src/ext4_inode.c + src/ext4_crc32.c + src/ext4_mbr.c + src/ext4_ialloc.c + src/ext4_hash.c + src/ext4_bitmap.c + src/ext4_block_group.c + + diff --git a/Library/lwext4/src/ext4.c b/Library/lwext4/src/ext4.c new file mode 100644 index 0000000..b270361 --- /dev/null +++ b/Library/lwext4/src/ext4.c @@ -0,0 +1,3240 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4.h + * @brief Ext4 high level operations (file, directory, mountpoints...) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +/**@brief Mount point OS dependent lock*/ +#define EXT4_MP_LOCK(_m) \ + do { \ + if ((_m)->os_locks) \ + (_m)->os_locks->lock(); \ + } while (0) + +/**@brief Mount point OS dependent unlock*/ +#define EXT4_MP_UNLOCK(_m) \ + do { \ + if ((_m)->os_locks) \ + (_m)->os_locks->unlock(); \ + } while (0) + +/**@brief Mount point descriptor.*/ +struct ext4_mountpoint { + + /**@brief Mount done flag.*/ + bool mounted; + + /**@brief Mount point name (@ref ext4_mount)*/ + char name[CONFIG_EXT4_MAX_MP_NAME + 1]; + + /**@brief OS dependent lock/unlock functions.*/ + const struct ext4_lock *os_locks; + + /**@brief Ext4 filesystem internals.*/ + struct ext4_fs fs; + + /**@brief JBD fs.*/ + struct jbd_fs jbd_fs; + + /**@brief Journal.*/ + struct jbd_journal jbd_journal; + + /**@brief Block cache.*/ + struct ext4_bcache bc; +}; + +/**@brief Block devices descriptor.*/ +struct ext4_block_devices { + + /**@brief Block device name.*/ + char name[CONFIG_EXT4_MAX_BLOCKDEV_NAME + 1]; + + /**@brief Block device handle.*/ + struct ext4_blockdev *bd; +}; + +/**@brief Block devices.*/ +static struct ext4_block_devices s_bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT]; + +/**@brief Mountpoints.*/ +static struct ext4_mountpoint s_mp[CONFIG_EXT4_MOUNTPOINTS_COUNT]; + +int ext4_device_register(struct ext4_blockdev *bd, + const char *dev_name) +{ + ext4_assert(bd && dev_name); + + if (strlen(dev_name) > CONFIG_EXT4_MAX_BLOCKDEV_NAME) + return EINVAL; + + for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) { + if (!strcmp(s_bdevices[i].name, dev_name)) + return EEXIST; + } + + for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) { + if (!s_bdevices[i].bd) { + strcpy(s_bdevices[i].name, dev_name); + s_bdevices[i].bd = bd; + return EOK; + } + } + + return ENOSPC; +} + +int ext4_device_unregister(const char *dev_name) +{ + ext4_assert(dev_name); + + for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) { + if (strcmp(s_bdevices[i].name, dev_name)) + continue; + + memset(&s_bdevices[i], 0, sizeof(s_bdevices[i])); + } + + return ENOENT; +} + +int ext4_device_unregister_all(void) +{ + memset(s_bdevices, 0, sizeof(s_bdevices)); + + return EOK; +} + +/****************************************************************************/ + +static bool ext4_is_dots(const uint8_t *name, size_t name_size) +{ + if ((name_size == 1) && (name[0] == '.')) + return true; + + if ((name_size == 2) && (name[0] == '.') && (name[1] == '.')) + return true; + + return false; +} + +static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode) +{ + struct ext4_sblock *sb = &enode->fs->sb; + + /* Check if node is directory */ + if (!ext4_inode_is_type(sb, enode->inode, EXT4_INODE_MODE_DIRECTORY)) { + *has_children = false; + return EOK; + } + + struct ext4_dir_iter it; + int rc = ext4_dir_iterator_init(&it, enode, 0); + if (rc != EOK) + return rc; + + /* Find a non-empty directory entry */ + bool found = false; + while (it.curr != NULL) { + if (ext4_dir_en_get_inode(it.curr) != 0) { + uint16_t nsize; + nsize = ext4_dir_en_get_name_len(sb, it.curr); + if (!ext4_is_dots(it.curr->name, nsize)) { + found = true; + break; + } + } + + rc = ext4_dir_iterator_next(&it); + if (rc != EOK) { + ext4_dir_iterator_fini(&it); + return rc; + } + } + + rc = ext4_dir_iterator_fini(&it); + if (rc != EOK) + return rc; + + *has_children = found; + + return EOK; +} + +static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent, + struct ext4_inode_ref *ch, const char *n, + uint32_t len, bool rename) +{ + /* Check maximum name length */ + if (len > EXT4_DIRECTORY_FILENAME_LEN) + return EINVAL; + + /* Add entry to parent directory */ + int r = ext4_dir_add_entry(parent, n, len, ch); + if (r != EOK) + return r; + + /* Fill new dir -> add '.' and '..' entries. + * Also newly allocated inode should have 0 link count. + */ + + bool is_dir = ext4_inode_is_type(&mp->fs.sb, ch->inode, + EXT4_INODE_MODE_DIRECTORY); + if (is_dir && !rename) { + +#if CONFIG_DIR_INDEX_ENABLE + /* Initialize directory index if supported */ + if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) { + r = ext4_dir_dx_init(ch, parent); + if (r != EOK) + return r; + + ext4_inode_set_flag(ch->inode, EXT4_INODE_FLAG_INDEX); + ch->dirty = true; + } else +#endif + { + r = ext4_dir_add_entry(ch, ".", strlen("."), ch); + if (r != EOK) { + ext4_dir_remove_entry(parent, n, strlen(n)); + return r; + } + + r = ext4_dir_add_entry(ch, "..", strlen(".."), parent); + if (r != EOK) { + ext4_dir_remove_entry(parent, n, strlen(n)); + ext4_dir_remove_entry(ch, ".", strlen(".")); + return r; + } + } + + /*New empty directory. Two links (. and ..) */ + ext4_inode_set_links_cnt(ch->inode, 2); + ext4_fs_inode_links_count_inc(parent); + ch->dirty = true; + parent->dirty = true; + return r; + } + /* + * In case we want to rename a directory, + * we reset the original '..' pointer. + */ + if (is_dir) { + bool idx; + idx = ext4_inode_has_flag(ch->inode, EXT4_INODE_FLAG_INDEX); + struct ext4_dir_search_result res; + if (!idx) { + r = ext4_dir_find_entry(&res, ch, "..", strlen("..")); + if (r != EOK) + return EIO; + + ext4_dir_en_set_inode(res.dentry, parent->index); + ext4_trans_set_block_dirty(res.block.buf); + r = ext4_dir_destroy_result(ch, &res); + if (r != EOK) + return r; + + } else { +#if CONFIG_DIR_INDEX_ENABLE + r = ext4_dir_dx_reset_parent_inode(ch, parent->index); + if (r != EOK) + return r; + +#endif + } + + ext4_fs_inode_links_count_inc(parent); + parent->dirty = true; + } + if (!rename) { + ext4_fs_inode_links_count_inc(ch); + ch->dirty = true; + } + + return r; +} + +static int ext4_unlink(struct ext4_mountpoint *mp, + struct ext4_inode_ref *parent, + struct ext4_inode_ref *child, const char *name, + uint32_t name_len) +{ + bool has_children; + int rc = ext4_has_children(&has_children, child); + if (rc != EOK) + return rc; + + /* Cannot unlink non-empty node */ + if (has_children) + return ENOTEMPTY; + + /* Remove entry from parent directory */ + rc = ext4_dir_remove_entry(parent, name, name_len); + if (rc != EOK) + return rc; + + bool is_dir = ext4_inode_is_type(&mp->fs.sb, child->inode, + EXT4_INODE_MODE_DIRECTORY); + + /* If directory - handle links from parent */ + if (is_dir) { + ext4_fs_inode_links_count_dec(parent); + parent->dirty = true; + } + + /* + * TODO: Update timestamps of the parent + * (when we have wall-clock time). + * + * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now); + * ext4_inode_set_modification_time(parent->inode, (uint32_t) now); + * parent->dirty = true; + */ + + /* + * TODO: Update timestamp for inode. + * + * ext4_inode_set_change_inode_time(child->inode, + * (uint32_t) now); + */ + if (ext4_inode_get_links_cnt(child->inode)) { + ext4_fs_inode_links_count_dec(child); + child->dirty = true; + } + + return EOK; +} + +/****************************************************************************/ + +int ext4_mount(const char *dev_name, const char *mount_point, + bool read_only) +{ + int r; + uint32_t bsize; + struct ext4_bcache *bc; + struct ext4_blockdev *bd = 0; + struct ext4_mountpoint *mp = 0; + + ext4_assert(mount_point && dev_name); + + size_t mp_len = strlen(mount_point); + + if (mp_len > CONFIG_EXT4_MAX_MP_NAME) + return EINVAL; + + if (mount_point[mp_len - 1] != '/') + return ENOTSUP; + + for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) { + if (!strcmp(dev_name, s_bdevices[i].name)) { + bd = s_bdevices[i].bd; + break; + } + } + + if (!bd) + return ENODEV; + + for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) { + if (!s_mp[i].mounted) { + strcpy(s_mp[i].name, mount_point); + s_mp[i].mounted = 1; + mp = &s_mp[i]; + break; + } + + if (!strcmp(s_mp[i].name, mount_point)) + return EOK; + } + + if (!mp) + return ENOMEM; + + r = ext4_block_init(bd); + if (r != EOK) + return r; + + r = ext4_fs_init(&mp->fs, bd, read_only); + if (r != EOK) { + ext4_block_fini(bd); + return r; + } + + bsize = ext4_sb_get_block_size(&mp->fs.sb); + ext4_block_set_lb_size(bd, bsize); + bc = &mp->bc; + + r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize); + if (r != EOK) { + ext4_block_fini(bd); + return r; + } + + if (bsize != bc->itemsize) + return ENOTSUP; + + /*Bind block cache to block device*/ + r = ext4_block_bind_bcache(bd, bc); + if (r != EOK) { + ext4_bcache_cleanup(bc); + ext4_block_fini(bd); + ext4_bcache_fini_dynamic(bc); + return r; + } + + bd->fs = &mp->fs; + return r; +} + + +int ext4_umount(const char *mount_point) +{ + int i; + int r; + struct ext4_mountpoint *mp = 0; + + for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) { + if (!strcmp(s_mp[i].name, mount_point)) { + mp = &s_mp[i]; + break; + } + } + + if (!mp) + return ENODEV; + + r = ext4_fs_fini(&mp->fs); + if (r != EOK) + goto Finish; + + mp->mounted = 0; + + ext4_bcache_cleanup(mp->fs.bdev->bc); + ext4_bcache_fini_dynamic(mp->fs.bdev->bc); + + r = ext4_block_fini(mp->fs.bdev); +Finish: + mp->fs.bdev->fs = NULL; + return r; +} + +static struct ext4_mountpoint *ext4_get_mount(const char *path) +{ + for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) { + + if (!s_mp[i].mounted) + continue; + + if (!strncmp(s_mp[i].name, path, strlen(s_mp[i].name))) + return &s_mp[i]; + } + + return NULL; +} + +__unused +static int __ext4_journal_start(const char *mount_point) +{ + int r = EOK; + struct ext4_mountpoint *mp = ext4_get_mount(mount_point); + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EOK; + + if (ext4_sb_feature_com(&mp->fs.sb, + EXT4_FCOM_HAS_JOURNAL)) { + r = jbd_get_fs(&mp->fs, &mp->jbd_fs); + if (r != EOK) + goto Finish; + + r = jbd_journal_start(&mp->jbd_fs, &mp->jbd_journal); + if (r != EOK) { + mp->jbd_fs.dirty = false; + jbd_put_fs(&mp->jbd_fs); + goto Finish; + } + mp->fs.jbd_fs = &mp->jbd_fs; + mp->fs.jbd_journal = &mp->jbd_journal; + } +Finish: + return r; +} + +__unused +static int __ext4_journal_stop(const char *mount_point) +{ + int r = EOK; + struct ext4_mountpoint *mp = ext4_get_mount(mount_point); + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EOK; + + if (ext4_sb_feature_com(&mp->fs.sb, + EXT4_FCOM_HAS_JOURNAL)) { + r = jbd_journal_stop(&mp->jbd_journal); + if (r != EOK) { + mp->jbd_fs.dirty = false; + jbd_put_fs(&mp->jbd_fs); + mp->fs.jbd_journal = NULL; + mp->fs.jbd_fs = NULL; + goto Finish; + } + + r = jbd_put_fs(&mp->jbd_fs); + if (r != EOK) { + mp->fs.jbd_journal = NULL; + mp->fs.jbd_fs = NULL; + goto Finish; + } + + mp->fs.jbd_journal = NULL; + mp->fs.jbd_fs = NULL; + } +Finish: + return r; +} + +__unused +static int __ext4_recover(const char *mount_point) +{ + struct ext4_mountpoint *mp = ext4_get_mount(mount_point); + int r = ENOTSUP; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL)) { + struct jbd_fs *jbd_fs = ext4_calloc(1, sizeof(struct jbd_fs)); + if (!jbd_fs) { + r = ENOMEM; + goto Finish; + } + + r = jbd_get_fs(&mp->fs, jbd_fs); + if (r != EOK) { + ext4_free(jbd_fs); + goto Finish; + } + + r = jbd_recover(jbd_fs); + jbd_put_fs(jbd_fs); + ext4_free(jbd_fs); + } + if (r == EOK && !mp->fs.read_only) { + uint32_t bgid; + uint64_t free_blocks_count = 0; + uint32_t free_inodes_count = 0; + struct ext4_block_group_ref bg_ref; + + /* Update superblock's stats */ + for (bgid = 0;bgid < ext4_block_group_cnt(&mp->fs.sb);bgid++) { + r = ext4_fs_get_block_group_ref(&mp->fs, bgid, &bg_ref); + if (r != EOK) + goto Finish; + + free_blocks_count += + ext4_bg_get_free_blocks_count(bg_ref.block_group, + &mp->fs.sb); + free_inodes_count += + ext4_bg_get_free_inodes_count(bg_ref.block_group, + &mp->fs.sb); + + ext4_fs_put_block_group_ref(&bg_ref); + } + ext4_sb_set_free_blocks_cnt(&mp->fs.sb, free_blocks_count); + ext4_set32(&mp->fs.sb, free_inodes_count, free_inodes_count); + /* We don't need to save the superblock stats immediately. */ + } + +Finish: + EXT4_MP_UNLOCK(mp); + return r; +} + +__unused +static int __ext4_trans_start(struct ext4_mountpoint *mp) +{ + int r = EOK; + + if (mp->fs.jbd_journal && !mp->fs.curr_trans) { + struct jbd_journal *journal = mp->fs.jbd_journal; + struct jbd_trans *trans; + trans = jbd_journal_new_trans(journal); + if (!trans) { + r = ENOMEM; + goto Finish; + } + mp->fs.curr_trans = trans; + } +Finish: + return r; +} + +__unused +static int __ext4_trans_stop(struct ext4_mountpoint *mp) +{ + int r = EOK; + + if (mp->fs.jbd_journal && mp->fs.curr_trans) { + struct jbd_journal *journal = mp->fs.jbd_journal; + struct jbd_trans *trans = mp->fs.curr_trans; + r = jbd_journal_commit_trans(journal, trans); + mp->fs.curr_trans = NULL; + } + return r; +} + +__unused +static void __ext4_trans_abort(struct ext4_mountpoint *mp) +{ + if (mp->fs.jbd_journal && mp->fs.curr_trans) { + struct jbd_journal *journal = mp->fs.jbd_journal; + struct jbd_trans *trans = mp->fs.curr_trans; + jbd_journal_free_trans(journal, trans, true); + mp->fs.curr_trans = NULL; + } +} + +int ext4_journal_start(const char *mount_point __unused) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + r = __ext4_journal_start(mount_point); +#endif + return r; +} + +int ext4_journal_stop(const char *mount_point __unused) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + r = __ext4_journal_stop(mount_point); +#endif + return r; +} + +int ext4_recover(const char *mount_point __unused) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + r = __ext4_recover(mount_point); +#endif + return r; +} + +static int ext4_trans_start(struct ext4_mountpoint *mp __unused) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + r = __ext4_trans_start(mp); +#endif + return r; +} + +static int ext4_trans_stop(struct ext4_mountpoint *mp __unused) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + r = __ext4_trans_stop(mp); +#endif + return r; +} + +static void ext4_trans_abort(struct ext4_mountpoint *mp __unused) +{ +#if CONFIG_JOURNALING_ENABLE + __ext4_trans_abort(mp); +#endif +} + + +int ext4_mount_point_stats(const char *mount_point, + struct ext4_mount_stats *stats) +{ + struct ext4_mountpoint *mp = ext4_get_mount(mount_point); + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count); + stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count); + stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb); + stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb); + stats->block_size = ext4_sb_get_block_size(&mp->fs.sb); + + stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb); + stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group); + stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group); + + memcpy(stats->volume_name, mp->fs.sb.volume_name, 16); + EXT4_MP_UNLOCK(mp); + + return EOK; +} + +int ext4_mount_setup_locks(const char *mount_point, + const struct ext4_lock *locks) +{ + uint32_t i; + struct ext4_mountpoint *mp = 0; + + for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) { + if (!strcmp(s_mp[i].name, mount_point)) { + mp = &s_mp[i]; + break; + } + } + if (!mp) + return ENOENT; + + mp->os_locks = locks; + return EOK; +} + +/********************************FILE OPERATIONS*****************************/ + +static int ext4_path_check(const char *path, bool *is_goal) +{ + int i; + + for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) { + + if (path[i] == '/') { + *is_goal = false; + return i; + } + + if (path[i] == 0) { + *is_goal = true; + return i; + } + } + + return 0; +} + +static bool ext4_parse_flags(const char *flags, uint32_t *file_flags) +{ + if (!flags) + return false; + + if (!strcmp(flags, "r") || !strcmp(flags, "rb")) { + *file_flags = O_RDONLY; + return true; + } + + if (!strcmp(flags, "w") || !strcmp(flags, "wb")) { + *file_flags = O_WRONLY | O_CREAT | O_TRUNC; + return true; + } + + if (!strcmp(flags, "a") || !strcmp(flags, "ab")) { + *file_flags = O_WRONLY | O_CREAT | O_APPEND; + return true; + } + + if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") || + !strcmp(flags, "r+b")) { + *file_flags = O_RDWR; + return true; + } + + if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") || + !strcmp(flags, "w+b")) { + *file_flags = O_RDWR | O_CREAT | O_TRUNC; + return true; + } + + if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") || + !strcmp(flags, "a+b")) { + *file_flags = O_RDWR | O_CREAT | O_APPEND; + return true; + } + + return false; +} + +static int ext4_trunc_inode(struct ext4_mountpoint *mp, + uint32_t index, uint64_t new_size) +{ + int r = EOK; + struct ext4_fs *const fs = &mp->fs; + struct ext4_inode_ref inode_ref; + uint64_t inode_size; + bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans; + r = ext4_fs_get_inode_ref(fs, index, &inode_ref); + if (r != EOK) + return r; + + inode_size = ext4_inode_get_size(&fs->sb, inode_ref.inode); + ext4_fs_put_inode_ref(&inode_ref); + if (has_trans) + ext4_trans_stop(mp); + + while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) { + + inode_size -= CONFIG_MAX_TRUNCATE_SIZE; + + ext4_trans_start(mp); + r = ext4_fs_get_inode_ref(fs, index, &inode_ref); + if (r != EOK) { + ext4_trans_abort(mp); + break; + } + r = ext4_fs_truncate_inode(&inode_ref, inode_size); + if (r != EOK) + ext4_fs_put_inode_ref(&inode_ref); + else + r = ext4_fs_put_inode_ref(&inode_ref); + + if (r != EOK) { + ext4_trans_abort(mp); + goto Finish; + } else + ext4_trans_stop(mp); + } + + if (inode_size > new_size) { + + inode_size = new_size; + + ext4_trans_start(mp); + r = ext4_fs_get_inode_ref(fs, index, &inode_ref); + if (r != EOK) { + ext4_trans_abort(mp); + goto Finish; + } + r = ext4_fs_truncate_inode(&inode_ref, inode_size); + if (r != EOK) + ext4_fs_put_inode_ref(&inode_ref); + else + r = ext4_fs_put_inode_ref(&inode_ref); + + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + } + +Finish: + + if (has_trans) + ext4_trans_start(mp); + + return r; +} + +static int ext4_trunc_dir(struct ext4_mountpoint *mp, + struct ext4_inode_ref *parent, + struct ext4_inode_ref *dir) +{ + int r = EOK; + bool is_dir = ext4_inode_is_type(&mp->fs.sb, dir->inode, + EXT4_INODE_MODE_DIRECTORY); + uint32_t block_size = ext4_sb_get_block_size(&mp->fs.sb); + if (!is_dir) + return EINVAL; + +#if CONFIG_DIR_INDEX_ENABLE + /* Initialize directory index if supported */ + if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) { + r = ext4_dir_dx_init(dir, parent); + if (r != EOK) + return r; + + r = ext4_trunc_inode(mp, dir->index, + EXT4_DIR_DX_INIT_BCNT * block_size); + if (r != EOK) + return r; + } else +#endif + { + r = ext4_trunc_inode(mp, dir->index, block_size); + if (r != EOK) + return r; + } + + return ext4_fs_truncate_inode(dir, 0); +} + +/* + * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN, + * any filetype of the target dir entry will be accepted. + */ +static int ext4_generic_open2(ext4_file *f, const char *path, int flags, + int ftype, uint32_t *parent_inode, + uint32_t *name_off) +{ + bool is_goal = false; + uint32_t imode = EXT4_INODE_MODE_DIRECTORY; + uint32_t next_inode; + + int r; + int len; + struct ext4_mountpoint *mp = ext4_get_mount(path); + struct ext4_dir_search_result result; + struct ext4_inode_ref ref; + + f->mp = 0; + + if (!mp) + return ENOENT; + + struct ext4_fs *const fs = &mp->fs; + struct ext4_sblock *const sb = &mp->fs.sb; + + if (fs->read_only && flags & O_CREAT) + return EROFS; + + f->flags = flags; + + /*Skip mount point*/ + path += strlen(mp->name); + + if (name_off) + *name_off = strlen(mp->name); + + /*Load root*/ + r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref); + if (r != EOK) + return r; + + if (parent_inode) + *parent_inode = ref.index; + + len = ext4_path_check(path, &is_goal); + while (1) { + + len = ext4_path_check(path, &is_goal); + if (!len) { + /*If root open was request.*/ + if (ftype == EXT4_DE_DIR || ftype == EXT4_DE_UNKNOWN) + if (is_goal) + break; + + r = ENOENT; + break; + } + + r = ext4_dir_find_entry(&result, &ref, path, len); + if (r != EOK) { + + /*Destroy last result*/ + ext4_dir_destroy_result(&ref, &result); + if (r != ENOENT) + break; + + if (!(f->flags & O_CREAT)) + break; + + /*O_CREAT allows create new entry*/ + struct ext4_inode_ref child_ref; + r = ext4_fs_alloc_inode(fs, &child_ref, + is_goal ? ftype : EXT4_DE_DIR); + + if (r != EOK) + break; + + ext4_fs_inode_blocks_init(fs, &child_ref); + + /*Link with root dir.*/ + r = ext4_link(mp, &ref, &child_ref, path, len, false); + if (r != EOK) { + /*Fail. Free new inode.*/ + ext4_fs_free_inode(&child_ref); + /*We do not want to write new inode. + But block has to be released.*/ + child_ref.dirty = false; + ext4_fs_put_inode_ref(&child_ref); + break; + } + + ext4_fs_put_inode_ref(&child_ref); + continue; + } + + if (parent_inode) + *parent_inode = ref.index; + + next_inode = ext4_dir_en_get_inode(result.dentry); + if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) { + uint8_t t; + t = ext4_dir_en_get_inode_type(sb, result.dentry); + imode = ext4_fs_correspond_inode_mode(t); + } else { + struct ext4_inode_ref child_ref; + r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref); + if (r != EOK) + break; + + imode = ext4_inode_type(sb, child_ref.inode); + ext4_fs_put_inode_ref(&child_ref); + } + + r = ext4_dir_destroy_result(&ref, &result); + if (r != EOK) + break; + + /*If expected file error*/ + if (imode != EXT4_INODE_MODE_DIRECTORY && !is_goal) { + r = ENOENT; + break; + } + if (ftype != EXT4_DE_UNKNOWN) { + bool df = imode != ext4_fs_correspond_inode_mode(ftype); + if (df && is_goal) { + r = ENOENT; + break; + } + } + + r = ext4_fs_put_inode_ref(&ref); + if (r != EOK) + break; + + r = ext4_fs_get_inode_ref(fs, next_inode, &ref); + if (r != EOK) + break; + + if (is_goal) + break; + + path += len + 1; + + if (name_off) + *name_off += len + 1; + } + + if (r != EOK) { + ext4_fs_put_inode_ref(&ref); + return r; + } + + if (is_goal) { + + if ((f->flags & O_TRUNC) && (imode == EXT4_INODE_MODE_FILE)) { + r = ext4_trunc_inode(mp, ref.index, 0); + if (r != EOK) { + ext4_fs_put_inode_ref(&ref); + return r; + } + } + + f->mp = mp; + f->fsize = ext4_inode_get_size(sb, ref.inode); + f->inode = ref.index; + f->fpos = 0; + + if (f->flags & O_APPEND) + f->fpos = f->fsize; + } + + return ext4_fs_put_inode_ref(&ref); +} + +/****************************************************************************/ + +static int ext4_generic_open(ext4_file *f, const char *path, const char *flags, + bool file_expect, uint32_t *parent_inode, + uint32_t *name_off) +{ + uint32_t iflags; + int filetype; + int r; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (ext4_parse_flags(flags, &iflags) == false) + return EINVAL; + + if (file_expect == true) + filetype = EXT4_DE_REG_FILE; + else + filetype = EXT4_DE_DIR; + + if (iflags & O_CREAT) + ext4_trans_start(mp); + + r = ext4_generic_open2(f, path, iflags, filetype, parent_inode, + name_off); + + if (iflags & O_CREAT) { + if (r == EOK) + ext4_trans_stop(mp); + else + ext4_trans_abort(mp); + } + + return r; +} + +static int ext4_create_hardlink(const char *path, + struct ext4_inode_ref *child_ref, bool rename) +{ + bool is_goal = false; + uint32_t inode_mode = EXT4_INODE_MODE_DIRECTORY; + uint32_t next_inode; + + int r; + int len; + struct ext4_mountpoint *mp = ext4_get_mount(path); + struct ext4_dir_search_result result; + struct ext4_inode_ref ref; + + if (!mp) + return ENOENT; + + struct ext4_fs *const fs = &mp->fs; + struct ext4_sblock *const sb = &mp->fs.sb; + + /*Skip mount point*/ + path += strlen(mp->name); + + /*Load root*/ + r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref); + if (r != EOK) + return r; + + len = ext4_path_check(path, &is_goal); + while (1) { + + len = ext4_path_check(path, &is_goal); + if (!len) { + /*If root open was request.*/ + r = is_goal ? EINVAL : ENOENT; + break; + } + + r = ext4_dir_find_entry(&result, &ref, path, len); + if (r != EOK) { + + /*Destroy last result*/ + ext4_dir_destroy_result(&ref, &result); + + if (r != ENOENT || !is_goal) + break; + + /*Link with root dir.*/ + r = ext4_link(mp, &ref, child_ref, path, len, rename); + break; + } else if (r == EOK && is_goal) { + /*Destroy last result*/ + ext4_dir_destroy_result(&ref, &result); + r = EEXIST; + break; + } + + next_inode = result.dentry->inode; + if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) { + uint8_t t; + t = ext4_dir_en_get_inode_type(sb, result.dentry); + inode_mode = ext4_fs_correspond_inode_mode(t); + } else { + struct ext4_inode_ref child_ref; + r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref); + if (r != EOK) + break; + + inode_mode = ext4_inode_type(sb, child_ref.inode); + ext4_fs_put_inode_ref(&child_ref); + } + + r = ext4_dir_destroy_result(&ref, &result); + if (r != EOK) + break; + + if (inode_mode != EXT4_INODE_MODE_DIRECTORY) { + r = is_goal ? EEXIST : ENOENT; + break; + } + + r = ext4_fs_put_inode_ref(&ref); + if (r != EOK) + break; + + r = ext4_fs_get_inode_ref(fs, next_inode, &ref); + if (r != EOK) + break; + + if (is_goal) + break; + + path += len + 1; + }; + + if (r != EOK) { + ext4_fs_put_inode_ref(&ref); + return r; + } + + r = ext4_fs_put_inode_ref(&ref); + return r; +} + +static int ext4_remove_orig_reference(const char *path, uint32_t name_off, + struct ext4_inode_ref *parent_ref, + struct ext4_inode_ref *child_ref) +{ + bool is_goal; + int r; + int len; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + /*Set path*/ + path += name_off; + + len = ext4_path_check(path, &is_goal); + + /* Remove entry from parent directory */ + r = ext4_dir_remove_entry(parent_ref, path, len); + if (r != EOK) + goto Finish; + + if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode, + EXT4_INODE_MODE_DIRECTORY)) { + ext4_fs_inode_links_count_dec(parent_ref); + parent_ref->dirty = true; + } +Finish: + return r; +} + +int ext4_flink(const char *path, const char *hardlink_path) +{ + int r; + ext4_file f; + uint32_t name_off; + bool child_loaded = false; + uint32_t parent_inode, child_inode; + struct ext4_mountpoint *mp = ext4_get_mount(path); + struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path); + struct ext4_inode_ref child_ref; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + /* Will that happen? Anyway return EINVAL for such case. */ + if (mp != target_mp) + return EINVAL; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, + &parent_inode, &name_off); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + child_inode = f.inode; + ext4_fclose(&f); + ext4_trans_start(mp); + + /*We have file to unlink. Load it.*/ + r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref); + if (r != EOK) + goto Finish; + + child_loaded = true; + + /* Creating hardlink for directory is not allowed. */ + if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode, + EXT4_INODE_MODE_DIRECTORY)) { + r = EINVAL; + goto Finish; + } + + r = ext4_create_hardlink(hardlink_path, &child_ref, false); + +Finish: + if (child_loaded) + ext4_fs_put_inode_ref(&child_ref); + + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + EXT4_MP_UNLOCK(mp); + return r; + +} + +int ext4_frename(const char *path, const char *new_path) +{ + int r; + ext4_file f; + uint32_t name_off; + bool parent_loaded = false, child_loaded = false; + uint32_t parent_inode, child_inode; + struct ext4_mountpoint *mp = ext4_get_mount(path); + struct ext4_inode_ref child_ref, parent_ref; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, + &parent_inode, &name_off); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + child_inode = f.inode; + ext4_fclose(&f); + ext4_trans_start(mp); + + /*Load parent*/ + r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref); + if (r != EOK) + goto Finish; + + parent_loaded = true; + + /*We have file to unlink. Load it.*/ + r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref); + if (r != EOK) + goto Finish; + + child_loaded = true; + + r = ext4_create_hardlink(new_path, &child_ref, true); + if (r != EOK) + goto Finish; + + r = ext4_remove_orig_reference(path, name_off, &parent_ref, &child_ref); + if (r != EOK) + goto Finish; + +Finish: + if (parent_loaded) + ext4_fs_put_inode_ref(&parent_ref); + + if (child_loaded) + ext4_fs_put_inode_ref(&child_ref); + + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + EXT4_MP_UNLOCK(mp); + return r; + +} + +/****************************************************************************/ + +int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb) +{ + struct ext4_mountpoint *mp = ext4_get_mount(mount_point); + + if (!mp) + return ENOENT; + + *sb = &mp->fs.sb; + return EOK; +} + +int ext4_cache_write_back(const char *path, bool on) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int ret; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + ret = ext4_block_cache_write_back(mp->fs.bdev, on); + EXT4_MP_UNLOCK(mp); + return ret; +} + +int ext4_cache_flush(const char *path) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int ret; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + ret = ext4_block_cache_flush(mp->fs.bdev); + EXT4_MP_UNLOCK(mp); + return ret; +} + +int ext4_fremove(const char *path) +{ + ext4_file f; + uint32_t parent_inode; + uint32_t child_inode; + uint32_t name_off; + bool is_goal; + int r; + int len; + struct ext4_inode_ref child; + struct ext4_inode_ref parent; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, + &parent_inode, &name_off); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + child_inode = f.inode; + ext4_fclose(&f); + ext4_trans_start(mp); + + /*Load parent*/ + r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent); + if (r != EOK) { + ext4_trans_abort(mp); + EXT4_MP_UNLOCK(mp); + return r; + } + + /*We have file to delete. Load it.*/ + r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child); + if (r != EOK) { + ext4_fs_put_inode_ref(&parent); + ext4_trans_abort(mp); + EXT4_MP_UNLOCK(mp); + return r; + } + /* We do not allow opening files here. */ + if (ext4_inode_type(&mp->fs.sb, child.inode) == + EXT4_INODE_MODE_DIRECTORY) { + ext4_fs_put_inode_ref(&parent); + ext4_fs_put_inode_ref(&child); + ext4_trans_abort(mp); + EXT4_MP_UNLOCK(mp); + return r; + } + + /*Link count will be zero, the inode should be freed. */ + if (ext4_inode_get_links_cnt(child.inode) == 1) { + ext4_block_cache_write_back(mp->fs.bdev, 1); + r = ext4_trunc_inode(mp, child.index, 0); + if (r != EOK) { + ext4_fs_put_inode_ref(&parent); + ext4_fs_put_inode_ref(&child); + ext4_trans_abort(mp); + EXT4_MP_UNLOCK(mp); + return r; + } + ext4_block_cache_write_back(mp->fs.bdev, 0); + } + + /*Set path*/ + path += name_off; + + len = ext4_path_check(path, &is_goal); + + /*Unlink from parent*/ + r = ext4_unlink(mp, &parent, &child, path, len); + if (r != EOK) + goto Finish; + + /*Link count is zero, the inode should be freed. */ + if (!ext4_inode_get_links_cnt(child.inode)) { + ext4_inode_set_del_time(child.inode, -1L); + + r = ext4_fs_free_inode(&child); + if (r != EOK) + goto Finish; + } + +Finish: + ext4_fs_put_inode_ref(&child); + ext4_fs_put_inode_ref(&parent); + + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_fopen(ext4_file *file, const char *path, const char *flags) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + ext4_block_cache_write_back(mp->fs.bdev, 1); + r = ext4_generic_open(file, path, flags, true, 0, 0); + ext4_block_cache_write_back(mp->fs.bdev, 0); + + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_fopen2(ext4_file *file, const char *path, int flags) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + int filetype; + + if (!mp) + return ENOENT; + + filetype = EXT4_DE_REG_FILE; + + EXT4_MP_LOCK(mp); + ext4_block_cache_write_back(mp->fs.bdev, 1); + + if (flags & O_CREAT) + ext4_trans_start(mp); + + r = ext4_generic_open2(file, path, flags, filetype, NULL, NULL); + + if (flags & O_CREAT) { + if (r == EOK) + ext4_trans_stop(mp); + else + ext4_trans_abort(mp); + } + + ext4_block_cache_write_back(mp->fs.bdev, 0); + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_fclose(ext4_file *file) +{ + ext4_assert(file && file->mp); + + file->mp = 0; + file->flags = 0; + file->inode = 0; + file->fpos = file->fsize = 0; + + return EOK; +} + +static int ext4_ftruncate_no_lock(ext4_file *file, uint64_t size) +{ + struct ext4_inode_ref ref; + int r; + + + r = ext4_fs_get_inode_ref(&file->mp->fs, file->inode, &ref); + if (r != EOK) { + EXT4_MP_UNLOCK(file->mp); + return r; + } + + /*Sync file size*/ + file->fsize = ext4_inode_get_size(&file->mp->fs.sb, ref.inode); + if (file->fsize <= size) { + r = EOK; + goto Finish; + } + + /*Start write back cache mode.*/ + r = ext4_block_cache_write_back(file->mp->fs.bdev, 1); + if (r != EOK) + goto Finish; + + r = ext4_trunc_inode(file->mp, ref.index, size); + if (r != EOK) + goto Finish; + + file->fsize = size; + if (file->fpos > size) + file->fpos = size; + + /*Stop write back cache mode*/ + ext4_block_cache_write_back(file->mp->fs.bdev, 0); + + if (r != EOK) + goto Finish; + +Finish: + ext4_fs_put_inode_ref(&ref); + return r; + +} + +int ext4_ftruncate(ext4_file *f, uint64_t size) +{ + int r; + ext4_assert(f && f->mp); + + if (f->mp->fs.read_only) + return EROFS; + + if (f->flags & O_RDONLY) + return EPERM; + + EXT4_MP_LOCK(f->mp); + + ext4_trans_start(f->mp); + r = ext4_ftruncate_no_lock(f, size); + if (r != EOK) + ext4_trans_abort(f->mp); + else + ext4_trans_stop(f->mp); + + EXT4_MP_UNLOCK(f->mp); + return r; +} + +int ext4_fread(ext4_file *file, void *buf, size_t size, size_t *rcnt) +{ + uint32_t unalg; + uint32_t iblock_idx; + uint32_t iblock_last; + uint32_t block_size; + + ext4_fsblk_t fblock; + ext4_fsblk_t fblock_start; + uint32_t fblock_count; + + uint8_t *u8_buf = buf; + int r; + struct ext4_inode_ref ref; + + ext4_assert(file && file->mp); + + if (file->flags & O_WRONLY) + return EPERM; + + if (!size) + return EOK; + + EXT4_MP_LOCK(file->mp); + + struct ext4_fs *const fs = &file->mp->fs; + struct ext4_sblock *const sb = &file->mp->fs.sb; + + if (rcnt) + *rcnt = 0; + + r = ext4_fs_get_inode_ref(fs, file->inode, &ref); + if (r != EOK) { + EXT4_MP_UNLOCK(file->mp); + return r; + } + + /*Sync file size*/ + file->fsize = ext4_inode_get_size(sb, ref.inode); + + block_size = ext4_sb_get_block_size(sb); + size = ((uint64_t)size > (file->fsize - file->fpos)) + ? ((size_t)(file->fsize - file->fpos)) : size; + + iblock_idx = (uint32_t)((file->fpos) / block_size); + iblock_last = (uint32_t)((file->fpos + size) / block_size); + unalg = (file->fpos) % block_size; + + /*If the size of symlink is smaller than 60 bytes*/ + bool softlink; + softlink = ext4_inode_is_type(sb, ref.inode, EXT4_INODE_MODE_SOFTLINK); + if (softlink && file->fsize < sizeof(ref.inode->blocks) + && !ext4_inode_get_blocks_count(sb, ref.inode)) { + + char *content = (char *)ref.inode->blocks; + if (file->fpos < file->fsize) { + size_t len = size; + if (unalg + size > (uint32_t)file->fsize) + len = (uint32_t)file->fsize - unalg; + memcpy(buf, content + unalg, len); + if (rcnt) + *rcnt = len; + + } + + r = EOK; + goto Finish; + } + + if (unalg) { + size_t len = size; + if (size > (block_size - unalg)) + len = block_size - unalg; + + r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true); + if (r != EOK) + goto Finish; + + /* Do we get an unwritten range? */ + if (fblock != 0) { + uint64_t off = fblock * block_size + unalg; + r = ext4_block_readbytes(file->mp->fs.bdev, off, u8_buf, len); + if (r != EOK) + goto Finish; + + } else { + /* Yes, we do. */ + memset(u8_buf, 0, len); + } + + u8_buf += len; + size -= len; + file->fpos += len; + + if (rcnt) + *rcnt += len; + + iblock_idx++; + } + + fblock_start = 0; + fblock_count = 0; + while (size >= block_size) { + while (iblock_idx < iblock_last) { + r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, + &fblock, true); + if (r != EOK) + goto Finish; + + iblock_idx++; + + if (!fblock_start) + fblock_start = fblock; + + if ((fblock_start + fblock_count) != fblock) + break; + + fblock_count++; + } + + r = ext4_blocks_get_direct(file->mp->fs.bdev, u8_buf, fblock_start, + fblock_count); + if (r != EOK) + goto Finish; + + size -= block_size * fblock_count; + u8_buf += block_size * fblock_count; + file->fpos += block_size * fblock_count; + + if (rcnt) + *rcnt += block_size * fblock_count; + + fblock_start = fblock; + fblock_count = 1; + } + + if (size) { + uint64_t off; + r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true); + if (r != EOK) + goto Finish; + + off = fblock * block_size; + r = ext4_block_readbytes(file->mp->fs.bdev, off, u8_buf, size); + if (r != EOK) + goto Finish; + + file->fpos += size; + + if (rcnt) + *rcnt += size; + } + +Finish: + ext4_fs_put_inode_ref(&ref); + EXT4_MP_UNLOCK(file->mp); + return r; +} + +int ext4_fwrite(ext4_file *file, const void *buf, size_t size, size_t *wcnt) +{ + uint32_t unalg; + uint32_t iblk_idx; + uint32_t iblock_last; + uint32_t ifile_blocks; + uint32_t block_size; + + uint32_t fblock_count; + ext4_fsblk_t fblk; + ext4_fsblk_t fblock_start; + + struct ext4_inode_ref ref; + const uint8_t *u8_buf = buf; + int r, rr = EOK; + + ext4_assert(file && file->mp); + + if (file->mp->fs.read_only) + return EROFS; + + if (file->flags & O_RDONLY) + return EPERM; + + if (!size) + return EOK; + + EXT4_MP_LOCK(file->mp); + ext4_trans_start(file->mp); + + struct ext4_fs *const fs = &file->mp->fs; + struct ext4_sblock *const sb = &file->mp->fs.sb; + + if (wcnt) + *wcnt = 0; + + r = ext4_fs_get_inode_ref(fs, file->inode, &ref); + if (r != EOK) { + ext4_trans_abort(file->mp); + EXT4_MP_UNLOCK(file->mp); + return r; + } + + /*Sync file size*/ + file->fsize = ext4_inode_get_size(sb, ref.inode); + block_size = ext4_sb_get_block_size(sb); + + iblock_last = (uint32_t)((file->fpos + size) / block_size); + iblk_idx = (uint32_t)(file->fpos / block_size); + ifile_blocks = (uint32_t)((file->fsize + block_size - 1) / block_size); + + unalg = (file->fpos) % block_size; + + if (unalg) { + size_t len = size; + uint64_t off; + if (size > (block_size - unalg)) + len = block_size - unalg; + + r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk); + if (r != EOK) + goto Finish; + + off = fblk * block_size + unalg; + r = ext4_block_writebytes(file->mp->fs.bdev, off, u8_buf, len); + if (r != EOK) + goto Finish; + + u8_buf += len; + size -= len; + file->fpos += len; + + if (wcnt) + *wcnt += len; + + iblk_idx++; + } + + /*Start write back cache mode.*/ + r = ext4_block_cache_write_back(file->mp->fs.bdev, 1); + if (r != EOK) + goto Finish; + + fblock_start = 0; + fblock_count = 0; + while (size >= block_size) { + + while (iblk_idx < iblock_last) { + if (iblk_idx < ifile_blocks) { + r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, + &fblk); + if (r != EOK) + goto Finish; + } else { + rr = ext4_fs_append_inode_dblk(&ref, &fblk, + &iblk_idx); + if (rr != EOK) { + /* Unable to append more blocks. But + * some block might be allocated already + * */ + break; + } + } + + iblk_idx++; + + if (!fblock_start) { + fblock_start = fblk; + } + + if ((fblock_start + fblock_count) != fblk) + break; + + fblock_count++; + } + + r = ext4_blocks_set_direct(file->mp->fs.bdev, u8_buf, fblock_start, + fblock_count); + if (r != EOK) + break; + + size -= block_size * fblock_count; + u8_buf += block_size * fblock_count; + file->fpos += block_size * fblock_count; + + if (wcnt) + *wcnt += block_size * fblock_count; + + fblock_start = fblk; + fblock_count = 1; + + if (rr != EOK) { + /*ext4_fs_append_inode_block has failed and no + * more blocks might be written. But node size + * should be updated.*/ + r = rr; + goto out_fsize; + } + } + + /*Stop write back cache mode*/ + ext4_block_cache_write_back(file->mp->fs.bdev, 0); + + if (r != EOK) + goto Finish; + + if (size) { + uint64_t off; + if (iblk_idx < ifile_blocks) { + r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk); + if (r != EOK) + goto Finish; + } else { + r = ext4_fs_append_inode_dblk(&ref, &fblk, &iblk_idx); + if (r != EOK) + /*Node size sholud be updated.*/ + goto out_fsize; + } + + off = fblk * block_size; + r = ext4_block_writebytes(file->mp->fs.bdev, off, u8_buf, size); + if (r != EOK) + goto Finish; + + file->fpos += size; + + if (wcnt) + *wcnt += size; + } + +out_fsize: + if (file->fpos > file->fsize) { + file->fsize = file->fpos; + ext4_inode_set_size(ref.inode, file->fsize); + ref.dirty = true; + } + +Finish: + r = ext4_fs_put_inode_ref(&ref); + + if (r != EOK) + ext4_trans_abort(file->mp); + else + ext4_trans_stop(file->mp); + + EXT4_MP_UNLOCK(file->mp); + return r; +} + +int ext4_fseek(ext4_file *file, int64_t offset, uint32_t origin) +{ + switch (origin) { + case SEEK_SET: + if (offset < 0 || (uint64_t)offset > file->fsize) + return EINVAL; + + file->fpos = offset; + return EOK; + case SEEK_CUR: + if ((offset < 0 && (uint64_t)(-offset) > file->fpos) || + (offset > 0 && + (uint64_t)offset > (file->fsize - file->fpos))) + return EINVAL; + + file->fpos += offset; + return EOK; + case SEEK_END: + if (offset < 0 || (uint64_t)offset > file->fsize) + return EINVAL; + + file->fpos = file->fsize - offset; + return EOK; + } + return EINVAL; +} + +uint64_t ext4_ftell(ext4_file *file) +{ + return file->fpos; +} + +uint64_t ext4_fsize(ext4_file *file) +{ + return file->fsize; +} + + +static int ext4_trans_get_inode_ref(const char *path, + struct ext4_mountpoint *mp, + struct ext4_inode_ref *inode_ref) +{ + int r; + ext4_file f; + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + return r; + + ext4_trans_start(mp); + + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, inode_ref); + if (r != EOK) { + ext4_trans_abort(mp); + return r; + } + + return r; +} + +static int ext4_trans_put_inode_ref(struct ext4_mountpoint *mp, + struct ext4_inode_ref *inode_ref) +{ + int r; + + r = ext4_fs_put_inode_ref(inode_ref); + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + return r; +} + + +int ext4_raw_inode_fill(const char *path, uint32_t *ret_ino, + struct ext4_inode *inode) +{ + int r; + ext4_file f; + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + /*Load parent*/ + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + if (ret_ino) + *ret_ino = f.inode; + + memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode)); + ext4_fs_put_inode_ref(&inode_ref); + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_inode_exist(const char *path, int type) +{ + int r; + ext4_file f; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, type, NULL, NULL); + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_mode_set(const char *path, uint32_t mode) +{ + int r; + uint32_t orig_mode; + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + r = ext4_trans_get_inode_ref(path, mp, &inode_ref); + if (r != EOK) + goto Finish; + + orig_mode = ext4_inode_get_mode(&mp->fs.sb, inode_ref.inode); + orig_mode &= ~0xFFF; + orig_mode |= mode & 0xFFF; + ext4_inode_set_mode(&mp->fs.sb, inode_ref.inode, orig_mode); + + inode_ref.dirty = true; + r = ext4_trans_put_inode_ref(mp, &inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_owner_set(const char *path, uint32_t uid, uint32_t gid) +{ + int r; + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + r = ext4_trans_get_inode_ref(path, mp, &inode_ref); + if (r != EOK) + goto Finish; + + ext4_inode_set_uid(inode_ref.inode, uid); + ext4_inode_set_gid(inode_ref.inode, gid); + + inode_ref.dirty = true; + r = ext4_trans_put_inode_ref(mp, &inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_mode_get(const char *path, uint32_t *mode) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + ext4_file f; + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref); + if (r != EOK) + goto Finish; + + *mode = ext4_inode_get_mode(&mp->fs.sb, inode_ref.inode); + r = ext4_fs_put_inode_ref(&inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_owner_get(const char *path, uint32_t *uid, uint32_t *gid) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + ext4_file f; + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref); + if (r != EOK) + goto Finish; + + *uid = ext4_inode_get_uid(inode_ref.inode); + *gid = ext4_inode_get_gid(inode_ref.inode); + r = ext4_fs_put_inode_ref(&inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_atime_set(const char *path, uint32_t atime) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + r = ext4_trans_get_inode_ref(path, mp, &inode_ref); + if (r != EOK) + goto Finish; + + ext4_inode_set_access_time(inode_ref.inode, atime); + inode_ref.dirty = true; + r = ext4_trans_put_inode_ref(mp, &inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_mtime_set(const char *path, uint32_t mtime) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + r = ext4_trans_get_inode_ref(path, mp, &inode_ref); + if (r != EOK) + goto Finish; + + ext4_inode_set_modif_time(inode_ref.inode, mtime); + inode_ref.dirty = true; + r = ext4_trans_put_inode_ref(mp, &inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_ctime_set(const char *path, uint32_t ctime) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + r = ext4_trans_get_inode_ref(path, mp, &inode_ref); + if (r != EOK) + goto Finish; + + ext4_inode_set_change_inode_time(inode_ref.inode, ctime); + inode_ref.dirty = true; + r = ext4_trans_put_inode_ref(mp, &inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_atime_get(const char *path, uint32_t *atime) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + ext4_file f; + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref); + if (r != EOK) + goto Finish; + + *atime = ext4_inode_get_access_time(inode_ref.inode); + r = ext4_fs_put_inode_ref(&inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_mtime_get(const char *path, uint32_t *mtime) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + ext4_file f; + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref); + if (r != EOK) + goto Finish; + + *mtime = ext4_inode_get_modif_time(inode_ref.inode); + r = ext4_fs_put_inode_ref(&inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_ctime_get(const char *path, uint32_t *ctime) +{ + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + ext4_file f; + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + + r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref); + if (r != EOK) + goto Finish; + + *ctime = ext4_inode_get_change_inode_time(inode_ref.inode); + r = ext4_fs_put_inode_ref(&inode_ref); + + Finish: + EXT4_MP_UNLOCK(mp); + + return r; +} + +static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size) +{ + struct ext4_inode_ref ref; + uint32_t sblock; + ext4_fsblk_t fblock; + uint32_t block_size; + int r; + + ext4_assert(f && f->mp); + + if (!size) + return EOK; + + r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref); + if (r != EOK) + return r; + + /*Sync file size*/ + block_size = ext4_sb_get_block_size(&f->mp->fs.sb); + if (size > block_size) { + r = EINVAL; + goto Finish; + } + r = ext4_ftruncate_no_lock(f, 0); + if (r != EOK) + goto Finish; + + /*Start write back cache mode.*/ + r = ext4_block_cache_write_back(f->mp->fs.bdev, 1); + if (r != EOK) + goto Finish; + + /*If the size of symlink is smaller than 60 bytes*/ + if (size < sizeof(ref.inode->blocks)) { + memset(ref.inode->blocks, 0, sizeof(ref.inode->blocks)); + memcpy(ref.inode->blocks, buf, size); + ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS); + } else { + uint64_t off; + ext4_fs_inode_blocks_init(&f->mp->fs, &ref); + r = ext4_fs_append_inode_dblk(&ref, &fblock, &sblock); + if (r != EOK) + goto Finish; + + off = fblock * block_size; + r = ext4_block_writebytes(f->mp->fs.bdev, off, buf, size); + if (r != EOK) + goto Finish; + } + + /*Stop write back cache mode*/ + ext4_block_cache_write_back(f->mp->fs.bdev, 0); + + if (r != EOK) + goto Finish; + + ext4_inode_set_size(ref.inode, size); + ref.dirty = true; + + f->fsize = size; + if (f->fpos > size) + f->fpos = size; + +Finish: + ext4_fs_put_inode_ref(&ref); + return r; +} + +int ext4_fsymlink(const char *target, const char *path) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + ext4_file f; + int filetype; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + filetype = EXT4_DE_SYMLINK; + + EXT4_MP_LOCK(mp); + ext4_block_cache_write_back(mp->fs.bdev, 1); + ext4_trans_start(mp); + + r = ext4_generic_open2(&f, path, O_RDWR | O_CREAT, filetype, NULL, NULL); + if (r == EOK) + r = ext4_fsymlink_set(&f, target, strlen(target)); + else + goto Finish; + + ext4_fclose(&f); + +Finish: + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + ext4_block_cache_write_back(mp->fs.bdev, 0); + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + ext4_file f; + int filetype; + + if (!mp) + return ENOENT; + + if (!buf) + return EINVAL; + + filetype = EXT4_DE_SYMLINK; + + EXT4_MP_LOCK(mp); + ext4_block_cache_write_back(mp->fs.bdev, 1); + r = ext4_generic_open2(&f, path, O_RDONLY, filetype, NULL, NULL); + if (r == EOK) + r = ext4_fread(&f, buf, bufsize, rcnt); + else + goto Finish; + + ext4_fclose(&f); + +Finish: + ext4_block_cache_write_back(mp->fs.bdev, 0); + EXT4_MP_UNLOCK(mp); + return r; +} + +static int ext4_mknod_set(ext4_file *f, uint32_t dev) +{ + struct ext4_inode_ref ref; + int r; + + ext4_assert(f && f->mp); + + r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref); + if (r != EOK) + return r; + + ext4_inode_set_dev(ref.inode, dev); + + ext4_inode_set_size(ref.inode, 0); + ref.dirty = true; + + f->fsize = 0; + f->fpos = 0; + + r = ext4_fs_put_inode_ref(&ref); + return r; +} + +int ext4_mknod(const char *path, int filetype, uint32_t dev) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + ext4_file f; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + /* + * The filetype shouldn't be normal file, directory or + * unknown. + */ + if (filetype == EXT4_DE_UNKNOWN || + filetype == EXT4_DE_REG_FILE || + filetype == EXT4_DE_DIR || + filetype == EXT4_DE_SYMLINK) + return EINVAL; + + /* + * Nor should it be any bogus value. + */ + if (filetype != EXT4_DE_CHRDEV && + filetype != EXT4_DE_BLKDEV && + filetype != EXT4_DE_FIFO && + filetype != EXT4_DE_SOCK) + return EINVAL; + + EXT4_MP_LOCK(mp); + ext4_block_cache_write_back(mp->fs.bdev, 1); + ext4_trans_start(mp); + + r = ext4_generic_open2(&f, path, O_RDWR | O_CREAT, filetype, NULL, NULL); + if (r == EOK) { + if (filetype == EXT4_DE_CHRDEV || + filetype == EXT4_DE_BLKDEV) + r = ext4_mknod_set(&f, dev); + } else { + goto Finish; + } + + ext4_fclose(&f); + +Finish: + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + ext4_block_cache_write_back(mp->fs.bdev, 0); + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_setxattr(const char *path, const char *name, size_t name_len, + const void *data, size_t data_size) +{ + bool found; + int r = EOK; + ext4_file f; + uint32_t inode; + uint8_t name_index; + const char *dissected_name = NULL; + size_t dissected_len = 0; + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + dissected_name = ext4_extract_xattr_name(name, name_len, + &name_index, &dissected_len, + &found); + if (!found) + return EINVAL; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + inode = f.inode; + ext4_fclose(&f); + ext4_trans_start(mp); + + r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref); + if (r != EOK) + goto Finish; + + r = ext4_xattr_set(&inode_ref, name_index, dissected_name, + dissected_len, data, data_size); + + ext4_fs_put_inode_ref(&inode_ref); +Finish: + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_getxattr(const char *path, const char *name, size_t name_len, + void *buf, size_t buf_size, size_t *data_size) +{ + bool found; + int r = EOK; + ext4_file f; + uint32_t inode; + uint8_t name_index; + const char *dissected_name = NULL; + size_t dissected_len = 0; + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + if (!mp) + return ENOENT; + + dissected_name = ext4_extract_xattr_name(name, name_len, + &name_index, &dissected_len, + &found); + if (!found) + return EINVAL; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + + inode = f.inode; + ext4_fclose(&f); + + r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref); + if (r != EOK) + goto Finish; + + r = ext4_xattr_get(&inode_ref, name_index, dissected_name, + dissected_len, buf, buf_size, data_size); + + ext4_fs_put_inode_ref(&inode_ref); +Finish: + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size) +{ + int r = EOK; + ext4_file f; + uint32_t inode; + size_t list_len, list_size = 0; + struct ext4_inode_ref inode_ref; + struct ext4_xattr_list_entry *xattr_list = NULL, + *entry = NULL; + struct ext4_mountpoint *mp = ext4_get_mount(path); + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) + goto Finish; + inode = f.inode; + ext4_fclose(&f); + + r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref); + if (r != EOK) + goto Finish; + + r = ext4_xattr_list(&inode_ref, NULL, &list_len); + if (r == EOK && list_len) { + xattr_list = ext4_malloc(list_len); + if (!xattr_list) { + ext4_fs_put_inode_ref(&inode_ref); + r = ENOMEM; + goto Finish; + } + entry = xattr_list; + r = ext4_xattr_list(&inode_ref, entry, &list_len); + if (r != EOK) { + ext4_fs_put_inode_ref(&inode_ref); + goto Finish; + } + + for (;entry;entry = entry->next) { + size_t prefix_len; + const char *prefix = + ext4_get_xattr_name_prefix(entry->name_index, + &prefix_len); + if (size) { + if (prefix_len + entry->name_len + 1 > size) { + ext4_fs_put_inode_ref(&inode_ref); + r = ERANGE; + goto Finish; + } + } + + if (list && size) { + memcpy(list, prefix, prefix_len); + list += prefix_len; + memcpy(list, entry->name, + entry->name_len); + list[entry->name_len] = 0; + list += entry->name_len + 1; + + size -= prefix_len + entry->name_len + 1; + } + + list_size += prefix_len + entry->name_len + 1; + } + if (ret_size) + *ret_size = list_size; + + } + ext4_fs_put_inode_ref(&inode_ref); +Finish: + EXT4_MP_UNLOCK(mp); + if (xattr_list) + ext4_free(xattr_list); + + return r; + +} + +int ext4_removexattr(const char *path, const char *name, size_t name_len) +{ + bool found; + int r = EOK; + ext4_file f; + uint32_t inode; + uint8_t name_index; + const char *dissected_name = NULL; + size_t dissected_len = 0; + struct ext4_inode_ref inode_ref; + struct ext4_mountpoint *mp = ext4_get_mount(path); + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + dissected_name = ext4_extract_xattr_name(name, name_len, + &name_index, &dissected_len, + &found); + if (!found) + return EINVAL; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL); + if (r != EOK) { + EXT4_MP_LOCK(mp); + return r; + } + + inode = f.inode; + ext4_fclose(&f); + ext4_trans_start(mp); + + r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref); + if (r != EOK) + goto Finish; + + r = ext4_xattr_remove(&inode_ref, name_index, dissected_name, + dissected_len); + + ext4_fs_put_inode_ref(&inode_ref); +Finish: + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + + EXT4_MP_UNLOCK(mp); + return r; + +} + +/*********************************DIRECTORY OPERATION************************/ + +int ext4_dir_rm(const char *path) +{ + int r; + int len; + ext4_file f; + + struct ext4_mountpoint *mp = ext4_get_mount(path); + struct ext4_inode_ref act; + struct ext4_inode_ref child; + struct ext4_dir_iter it; + + uint32_t name_off; + uint32_t inode_up; + uint32_t inode_current; + uint32_t depth = 1; + + bool has_children; + bool is_goal; + bool dir_end; + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + struct ext4_fs *const fs = &mp->fs; + + /*Check if exist.*/ + r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off); + if (r != EOK) { + EXT4_MP_UNLOCK(mp); + return r; + } + + path += name_off; + len = ext4_path_check(path, &is_goal); + inode_current = f.inode; + + ext4_block_cache_write_back(mp->fs.bdev, 1); + + do { + + uint64_t act_curr_pos = 0; + has_children = false; + dir_end = false; + + while (r == EOK && !has_children && !dir_end) { + + /*Load directory node.*/ + r = ext4_fs_get_inode_ref(fs, inode_current, &act); + if (r != EOK) { + break; + } + + /*Initialize iterator.*/ + r = ext4_dir_iterator_init(&it, &act, act_curr_pos); + if (r != EOK) { + ext4_fs_put_inode_ref(&act); + break; + } + + if (!it.curr) { + dir_end = true; + goto End; + } + + ext4_trans_start(mp); + + /*Get up directory inode when ".." entry*/ + if ((it.curr->name_len == 2) && + ext4_is_dots(it.curr->name, it.curr->name_len)) { + inode_up = ext4_dir_en_get_inode(it.curr); + } + + /*If directory or file entry, but not "." ".." entry*/ + if (!ext4_is_dots(it.curr->name, it.curr->name_len)) { + + /*Get child inode reference do unlink + * directory/file.*/ + uint32_t cinode; + uint32_t inode_type; + cinode = ext4_dir_en_get_inode(it.curr); + r = ext4_fs_get_inode_ref(fs, cinode, &child); + if (r != EOK) + goto End; + + /*If directory with no leaf children*/ + r = ext4_has_children(&has_children, &child); + if (r != EOK) { + ext4_fs_put_inode_ref(&child); + goto End; + } + + if (has_children) { + /*Has directory children. Go into this + * directory.*/ + inode_up = inode_current; + inode_current = cinode; + depth++; + ext4_fs_put_inode_ref(&child); + goto End; + } + inode_type = ext4_inode_type(&mp->fs.sb, + child.inode); + + /* Truncate */ + if (inode_type != EXT4_INODE_MODE_DIRECTORY) + r = ext4_trunc_inode(mp, child.index, 0); + else + r = ext4_trunc_dir(mp, &act, &child); + + if (r != EOK) { + ext4_fs_put_inode_ref(&child); + goto End; + } + + /*No children in child directory or file. Just + * unlink.*/ + r = ext4_unlink(f.mp, &act, &child, + (char *)it.curr->name, + it.curr->name_len); + if (r != EOK) { + ext4_fs_put_inode_ref(&child); + goto End; + } + + ext4_inode_set_del_time(child.inode, -1L); + ext4_inode_set_links_cnt(child.inode, 0); + child.dirty = true; + + r = ext4_fs_free_inode(&child); + if (r != EOK) { + ext4_fs_put_inode_ref(&child); + goto End; + } + + r = ext4_fs_put_inode_ref(&child); + if (r != EOK) + goto End; + + } + + r = ext4_dir_iterator_next(&it); + if (r != EOK) + goto End; + + act_curr_pos = it.curr_off; +End: + ext4_dir_iterator_fini(&it); + if (r == EOK) + r = ext4_fs_put_inode_ref(&act); + else + ext4_fs_put_inode_ref(&act); + + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + } + + if (dir_end) { + /*Directory iterator reached last entry*/ + depth--; + if (depth) + inode_current = inode_up; + + } + + if (r != EOK) + break; + + } while (depth); + + /*Last unlink*/ + if (r == EOK && !depth) { + /*Load parent.*/ + struct ext4_inode_ref parent; + r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up, + &parent); + if (r != EOK) + goto Finish; + r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, + &act); + if (r != EOK) { + ext4_fs_put_inode_ref(&act); + goto Finish; + } + + ext4_trans_start(mp); + + /* In this place all directories should be + * unlinked. + * Last unlink from root of current directory*/ + r = ext4_unlink(f.mp, &parent, &act, + (char *)path, len); + if (r != EOK) { + ext4_fs_put_inode_ref(&parent); + ext4_fs_put_inode_ref(&act); + goto Finish; + } + + if (ext4_inode_get_links_cnt(act.inode) == 2) { + ext4_inode_set_del_time(act.inode, -1L); + ext4_inode_set_links_cnt(act.inode, 0); + act.dirty = true; + /*Truncate*/ + r = ext4_trunc_dir(mp, &parent, &act); + if (r != EOK) { + ext4_fs_put_inode_ref(&parent); + ext4_fs_put_inode_ref(&act); + goto Finish; + } + + r = ext4_fs_free_inode(&act); + if (r != EOK) { + ext4_fs_put_inode_ref(&parent); + ext4_fs_put_inode_ref(&act); + goto Finish; + } + } + + r = ext4_fs_put_inode_ref(&parent); + if (r != EOK) + goto Finish; + + r = ext4_fs_put_inode_ref(&act); + Finish: + if (r != EOK) + ext4_trans_abort(mp); + else + ext4_trans_stop(mp); + } + + ext4_block_cache_write_back(mp->fs.bdev, 0); + EXT4_MP_UNLOCK(mp); + + return r; +} + +int ext4_dir_mv(const char *path, const char *new_path) +{ + return ext4_frename(path, new_path); +} + +int ext4_dir_mk(const char *path) +{ + int r; + ext4_file f; + struct ext4_mountpoint *mp = ext4_get_mount(path); + + if (!mp) + return ENOENT; + + if (mp->fs.read_only) + return EROFS; + + EXT4_MP_LOCK(mp); + + /*Check if exist.*/ + r = ext4_generic_open(&f, path, "r", false, 0, 0); + if (r == EOK) + goto Finish; + + /*Create new directory.*/ + r = ext4_generic_open(&f, path, "w", false, 0, 0); + +Finish: + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_dir_open(ext4_dir *dir, const char *path) +{ + struct ext4_mountpoint *mp = ext4_get_mount(path); + int r; + + if (!mp) + return ENOENT; + + EXT4_MP_LOCK(mp); + r = ext4_generic_open(&dir->f, path, "r", false, 0, 0); + dir->next_off = 0; + EXT4_MP_UNLOCK(mp); + return r; +} + +int ext4_dir_close(ext4_dir *dir) +{ + return ext4_fclose(&dir->f); +} + +const ext4_direntry *ext4_dir_entry_next(ext4_dir *dir) +{ +#define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1) + + int r; + uint16_t name_length; + ext4_direntry *de = 0; + struct ext4_inode_ref dir_inode; + struct ext4_dir_iter it; + + EXT4_MP_LOCK(dir->f.mp); + + if (dir->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) { + EXT4_MP_UNLOCK(dir->f.mp); + return 0; + } + + r = ext4_fs_get_inode_ref(&dir->f.mp->fs, dir->f.inode, &dir_inode); + if (r != EOK) { + goto Finish; + } + + r = ext4_dir_iterator_init(&it, &dir_inode, dir->next_off); + if (r != EOK) { + ext4_fs_put_inode_ref(&dir_inode); + goto Finish; + } + + memset(&dir->de.name, 0, sizeof(dir->de.name)); + name_length = ext4_dir_en_get_name_len(&dir->f.mp->fs.sb, + it.curr); + memcpy(&dir->de.name, it.curr->name, name_length); + + /* Directly copying the content isn't safe for Big-endian targets*/ + dir->de.inode = ext4_dir_en_get_inode(it.curr); + dir->de.entry_length = ext4_dir_en_get_entry_len(it.curr); + dir->de.name_length = name_length; + dir->de.inode_type = ext4_dir_en_get_inode_type(&dir->f.mp->fs.sb, + it.curr); + + de = &dir->de; + + ext4_dir_iterator_next(&it); + + dir->next_off = it.curr ? it.curr_off : EXT4_DIR_ENTRY_OFFSET_TERM; + + ext4_dir_iterator_fini(&it); + ext4_fs_put_inode_ref(&dir_inode); + +Finish: + EXT4_MP_UNLOCK(dir->f.mp); + return de; +} + +void ext4_dir_entry_rewind(ext4_dir *dir) +{ + dir->next_off = 0; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_balloc.c b/Library/lwext4/src/ext4_balloc.c new file mode 100644 index 0000000..8945554 --- /dev/null +++ b/Library/lwext4/src/ext4_balloc.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_balloc.c + * @brief Physical block allocator. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/**@brief Compute number of block group from block address. + * @param sb superblock pointer. + * @param baddr Absolute address of block. + * @return Block group index + */ +uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s, + uint64_t baddr) +{ + if (ext4_get32(s, first_data_block) && baddr) + baddr--; + + return (uint32_t)(baddr / ext4_get32(s, blocks_per_group)); +} + +/**@brief Compute the starting block address of a block group + * @param sb superblock pointer. + * @param bgid block group index + * @return Block address + */ +uint64_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s, + uint32_t bgid) +{ + uint64_t baddr = 0; + if (ext4_get32(s, first_data_block)) + baddr++; + + baddr += bgid * ext4_get32(s, blocks_per_group); + return baddr; +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_balloc_bitmap_csum(struct ext4_sblock *sb, + void *bitmap) +{ + uint32_t checksum = 0; + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t blocks_per_group = ext4_get32(sb, blocks_per_group); + + /* First calculate crc32 checksum against fs uuid */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, + sizeof(sb->uuid)); + /* Then calculate crc32 checksum against block_group_desc */ + checksum = ext4_crc32c(checksum, bitmap, blocks_per_group / 8); + } + return checksum; +} +#else +#define ext4_balloc_bitmap_csum(...) 0 +#endif + +void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb, + struct ext4_bgroup *bg, + void *bitmap __unused) +{ + int desc_size = ext4_sb_get_desc_size(sb); + uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap); + uint16_t lo_checksum = to_le16(checksum & 0xFFFF), + hi_checksum = to_le16(checksum >> 16); + + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return; + + /* See if we need to assign a 32bit checksum */ + bg->block_bitmap_csum_lo = lo_checksum; + if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) + bg->block_bitmap_csum_hi = hi_checksum; + +} + +#if CONFIG_META_CSUM_ENABLE +static bool +ext4_balloc_verify_bitmap_csum(struct ext4_sblock *sb, + struct ext4_bgroup *bg, + void *bitmap __unused) +{ + int desc_size = ext4_sb_get_desc_size(sb); + uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap); + uint16_t lo_checksum = to_le16(checksum & 0xFFFF), + hi_checksum = to_le16(checksum >> 16); + + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return true; + + if (bg->block_bitmap_csum_lo != lo_checksum) + return false; + + if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) + if (bg->block_bitmap_csum_hi != hi_checksum) + return false; + + return true; +} +#else +#define ext4_balloc_verify_bitmap_csum(...) true +#endif + +int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref, ext4_fsblk_t baddr) +{ + struct ext4_fs *fs = inode_ref->fs; + struct ext4_sblock *sb = &fs->sb; + + uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, baddr); + uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr); + + /* Load block group reference */ + struct ext4_block_group_ref bg_ref; + int rc = ext4_fs_get_block_group_ref(fs, bg_id, &bg_ref); + if (rc != EOK) + return rc; + + struct ext4_bgroup *bg = bg_ref.block_group; + + /* Load block with bitmap */ + ext4_fsblk_t bitmap_block_addr = + ext4_bg_get_block_bitmap(bg, sb); + + struct ext4_block bitmap_block; + + rc = ext4_trans_block_get(fs->bdev, &bitmap_block, bitmap_block_addr); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap_block.data)) { + ext4_dbg(DEBUG_BALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + + /* Modify bitmap */ + ext4_bmap_bit_clr(bitmap_block.data, index_in_group); + ext4_balloc_set_bitmap_csum(sb, bg, bitmap_block.data); + ext4_trans_set_block_dirty(bitmap_block.buf); + + /* Release block with bitmap */ + rc = ext4_block_set(fs->bdev, &bitmap_block); + if (rc != EOK) { + /* Error in saving bitmap */ + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + uint32_t block_size = ext4_sb_get_block_size(sb); + + /* Update superblock free blocks count */ + uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb); + sb_free_blocks++; + ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks); + + /* Update inode blocks count */ + uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode); + ino_blocks -= block_size / EXT4_INODE_BLOCK_SIZE; + ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); + inode_ref->dirty = true; + + /* Update block group free blocks count */ + uint32_t free_blocks = ext4_bg_get_free_blocks_count(bg, sb); + free_blocks++; + ext4_bg_set_free_blocks_count(bg, sb, free_blocks); + + bg_ref.dirty = true; + + rc = ext4_trans_try_revoke_block(fs->bdev, baddr); + if (rc != EOK) { + bg_ref.dirty = false; + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + ext4_bcache_invalidate_lba(fs->bdev->bc, baddr, 1); + /* Release block group reference */ + rc = ext4_fs_put_block_group_ref(&bg_ref); + + return rc; +} + +int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t first, uint32_t count) +{ + int rc = EOK; + uint32_t blk_cnt = count; + ext4_fsblk_t start_block = first; + struct ext4_fs *fs = inode_ref->fs; + struct ext4_sblock *sb = &fs->sb; + + /* Compute indexes */ + uint32_t bg_first = ext4_balloc_get_bgid_of_block(sb, first); + + /* Compute indexes */ + uint32_t bg_last = ext4_balloc_get_bgid_of_block(sb, first + count - 1); + + if (!ext4_sb_feature_incom(sb, EXT4_FINCOM_FLEX_BG)) { + /*It is not possible without flex_bg that blocks are continuous + * and and last block belongs to other bg.*/ + if (bg_last != bg_first) { + ext4_dbg(DEBUG_BALLOC, DBG_WARN "FLEX_BG: disabled & " + "bg_last: %"PRIu32" bg_first: %"PRIu32"\n", + bg_last, bg_first); + } + } + + /* Load block group reference */ + struct ext4_block_group_ref bg_ref; + while (bg_first <= bg_last) { + + rc = ext4_fs_get_block_group_ref(fs, bg_first, &bg_ref); + if (rc != EOK) + return rc; + + struct ext4_bgroup *bg = bg_ref.block_group; + + uint32_t idx_in_bg_first; + idx_in_bg_first = ext4_fs_addr_to_idx_bg(sb, first); + + /* Load block with bitmap */ + ext4_fsblk_t bitmap_blk = ext4_bg_get_block_bitmap(bg, sb); + + struct ext4_block blk; + rc = ext4_trans_block_get(fs->bdev, &blk, bitmap_blk); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + if (!ext4_balloc_verify_bitmap_csum(sb, bg, blk.data)) { + ext4_dbg(DEBUG_BALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + uint32_t free_cnt; + free_cnt = ext4_sb_get_block_size(sb) * 8 - idx_in_bg_first; + + /*If last block, free only count blocks*/ + free_cnt = count > free_cnt ? free_cnt : count; + + /* Modify bitmap */ + ext4_bmap_bits_free(blk.data, idx_in_bg_first, free_cnt); + ext4_balloc_set_bitmap_csum(sb, bg, blk.data); + ext4_trans_set_block_dirty(blk.buf); + + count -= free_cnt; + first += free_cnt; + + /* Release block with bitmap */ + rc = ext4_block_set(fs->bdev, &blk); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + uint32_t block_size = ext4_sb_get_block_size(sb); + + /* Update superblock free blocks count */ + uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb); + sb_free_blocks += free_cnt; + ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks); + + /* Update inode blocks count */ + uint64_t ino_blocks; + ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode); + ino_blocks -= free_cnt * (block_size / EXT4_INODE_BLOCK_SIZE); + ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); + inode_ref->dirty = true; + + /* Update block group free blocks count */ + uint32_t free_blocks; + free_blocks = ext4_bg_get_free_blocks_count(bg, sb); + free_blocks += free_cnt; + ext4_bg_set_free_blocks_count(bg, sb, free_blocks); + bg_ref.dirty = true; + + /* Release block group reference */ + rc = ext4_fs_put_block_group_ref(&bg_ref); + if (rc != EOK) + break; + + bg_first++; + } + + uint32_t i; + for (i = 0;i < blk_cnt;i++) { + rc = ext4_trans_try_revoke_block(fs->bdev, start_block + i); + if (rc != EOK) + return rc; + + } + + ext4_bcache_invalidate_lba(fs->bdev->bc, start_block, blk_cnt); + /*All blocks should be released*/ + ext4_assert(count == 0); + + return rc; +} + +int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t goal, + ext4_fsblk_t *fblock) +{ + ext4_fsblk_t alloc = 0; + ext4_fsblk_t bmp_blk_adr; + uint32_t rel_blk_idx = 0; + uint64_t free_blocks; + int r; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + /* Load block group number for goal and relative index */ + uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, goal); + uint32_t idx_in_bg = ext4_fs_addr_to_idx_bg(sb, goal); + + struct ext4_block b; + struct ext4_block_group_ref bg_ref; + + /* Load block group reference */ + r = ext4_fs_get_block_group_ref(inode_ref->fs, bg_id, &bg_ref); + if (r != EOK) + return r; + + struct ext4_bgroup *bg = bg_ref.block_group; + + free_blocks = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb); + if (free_blocks == 0) { + /* This group has no free blocks */ + goto goal_failed; + } + + /* Compute indexes */ + ext4_fsblk_t first_in_bg; + first_in_bg = ext4_balloc_get_block_of_bgid(sb, bg_ref.index); + + uint32_t first_in_bg_index; + first_in_bg_index = ext4_fs_addr_to_idx_bg(sb, first_in_bg); + + if (idx_in_bg < first_in_bg_index) + idx_in_bg = first_in_bg_index; + + /* Load block with bitmap */ + bmp_blk_adr = ext4_bg_get_block_bitmap(bg_ref.block_group, sb); + + r = ext4_trans_block_get(inode_ref->fs->bdev, &b, bmp_blk_adr); + if (r != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return r; + } + + if (!ext4_balloc_verify_bitmap_csum(sb, bg, b.data)) { + ext4_dbg(DEBUG_BALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + + /* Check if goal is free */ + if (ext4_bmap_is_bit_clr(b.data, idx_in_bg)) { + ext4_bmap_bit_set(b.data, idx_in_bg); + ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, + b.data); + ext4_trans_set_block_dirty(b.buf); + r = ext4_block_set(inode_ref->fs->bdev, &b); + if (r != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return r; + } + + alloc = ext4_fs_bg_idx_to_addr(sb, idx_in_bg, bg_id); + goto success; + } + + uint32_t blk_in_bg = ext4_blocks_in_group_cnt(sb, bg_id); + + uint32_t end_idx = (idx_in_bg + 63) & ~63; + if (end_idx > blk_in_bg) + end_idx = blk_in_bg; + + /* Try to find free block near to goal */ + uint32_t tmp_idx; + for (tmp_idx = idx_in_bg + 1; tmp_idx < end_idx; ++tmp_idx) { + if (ext4_bmap_is_bit_clr(b.data, tmp_idx)) { + ext4_bmap_bit_set(b.data, tmp_idx); + + ext4_balloc_set_bitmap_csum(sb, bg, b.data); + ext4_trans_set_block_dirty(b.buf); + r = ext4_block_set(inode_ref->fs->bdev, &b); + if (r != EOK) + return r; + + alloc = ext4_fs_bg_idx_to_addr(sb, tmp_idx, bg_id); + goto success; + } + } + + /* Find free bit in bitmap */ + r = ext4_bmap_bit_find_clr(b.data, idx_in_bg, blk_in_bg, &rel_blk_idx); + if (r == EOK) { + ext4_bmap_bit_set(b.data, rel_blk_idx); + ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, b.data); + ext4_trans_set_block_dirty(b.buf); + r = ext4_block_set(inode_ref->fs->bdev, &b); + if (r != EOK) + return r; + + alloc = ext4_fs_bg_idx_to_addr(sb, rel_blk_idx, bg_id); + goto success; + } + + /* No free block found yet */ + r = ext4_block_set(inode_ref->fs->bdev, &b); + if (r != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return r; + } + +goal_failed: + + r = ext4_fs_put_block_group_ref(&bg_ref); + if (r != EOK) + return r; + + /* Try other block groups */ + uint32_t block_group_count = ext4_block_group_cnt(sb); + uint32_t bgid = (bg_id + 1) % block_group_count; + uint32_t count = block_group_count; + + while (count > 0) { + r = ext4_fs_get_block_group_ref(inode_ref->fs, bgid, &bg_ref); + if (r != EOK) + return r; + + struct ext4_bgroup *bg = bg_ref.block_group; + free_blocks = ext4_bg_get_free_blocks_count(bg, sb); + if (free_blocks == 0) { + /* This group has no free blocks */ + goto next_group; + } + + /* Load block with bitmap */ + bmp_blk_adr = ext4_bg_get_block_bitmap(bg, sb); + r = ext4_trans_block_get(inode_ref->fs->bdev, &b, bmp_blk_adr); + if (r != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return r; + } + + if (!ext4_balloc_verify_bitmap_csum(sb, bg, b.data)) { + ext4_dbg(DEBUG_BALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + + /* Compute indexes */ + first_in_bg = ext4_balloc_get_block_of_bgid(sb, bgid); + idx_in_bg = ext4_fs_addr_to_idx_bg(sb, first_in_bg); + blk_in_bg = ext4_blocks_in_group_cnt(sb, bgid); + first_in_bg_index = ext4_fs_addr_to_idx_bg(sb, first_in_bg); + + if (idx_in_bg < first_in_bg_index) + idx_in_bg = first_in_bg_index; + + r = ext4_bmap_bit_find_clr(b.data, idx_in_bg, blk_in_bg, + &rel_blk_idx); + if (r == EOK) { + ext4_bmap_bit_set(b.data, rel_blk_idx); + ext4_balloc_set_bitmap_csum(sb, bg, b.data); + ext4_trans_set_block_dirty(b.buf); + r = ext4_block_set(inode_ref->fs->bdev, &b); + if (r != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return r; + } + + alloc = ext4_fs_bg_idx_to_addr(sb, rel_blk_idx, bgid); + goto success; + } + + r = ext4_block_set(inode_ref->fs->bdev, &b); + if (r != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return r; + } + + next_group: + r = ext4_fs_put_block_group_ref(&bg_ref); + if (r != EOK) { + return r; + } + + /* Goto next group */ + bgid = (bgid + 1) % block_group_count; + count--; + } + + return ENOSPC; + +success: + /* Empty command - because of syntax */ + ; + + uint32_t block_size = ext4_sb_get_block_size(sb); + + /* Update superblock free blocks count */ + uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb); + sb_free_blocks--; + ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks); + + /* Update inode blocks (different block size!) count */ + uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode); + ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE; + ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); + inode_ref->dirty = true; + + /* Update block group free blocks count */ + + uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb); + fb_cnt--; + ext4_bg_set_free_blocks_count(bg_ref.block_group, sb, fb_cnt); + + bg_ref.dirty = true; + r = ext4_fs_put_block_group_ref(&bg_ref); + + *fblock = alloc; + return r; +} + +int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t baddr, bool *free) +{ + int rc; + + struct ext4_fs *fs = inode_ref->fs; + struct ext4_sblock *sb = &fs->sb; + + /* Compute indexes */ + uint32_t block_group = ext4_balloc_get_bgid_of_block(sb, baddr); + uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr); + + /* Load block group reference */ + struct ext4_block_group_ref bg_ref; + rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref); + if (rc != EOK) + return rc; + + /* Load block with bitmap */ + ext4_fsblk_t bmp_blk_addr; + bmp_blk_addr = ext4_bg_get_block_bitmap(bg_ref.block_group, sb); + + struct ext4_block b; + rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_addr); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + if (!ext4_balloc_verify_bitmap_csum(sb, bg_ref.block_group, b.data)) { + ext4_dbg(DEBUG_BALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + + /* Check if block is free */ + *free = ext4_bmap_is_bit_clr(b.data, index_in_group); + + /* Allocate block if possible */ + if (*free) { + ext4_bmap_bit_set(b.data, index_in_group); + ext4_balloc_set_bitmap_csum(sb, bg_ref.block_group, b.data); + ext4_trans_set_block_dirty(b.buf); + } + + /* Release block with bitmap */ + rc = ext4_block_set(fs->bdev, &b); + if (rc != EOK) { + /* Error in saving bitmap */ + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + /* If block is not free, return */ + if (!(*free)) + goto terminate; + + uint32_t block_size = ext4_sb_get_block_size(sb); + + /* Update superblock free blocks count */ + uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb); + sb_free_blocks--; + ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks); + + /* Update inode blocks count */ + uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode); + ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE; + ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); + inode_ref->dirty = true; + + /* Update block group free blocks count */ + uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg_ref.block_group, sb); + fb_cnt--; + ext4_bg_set_free_blocks_count(bg_ref.block_group, sb, fb_cnt); + + bg_ref.dirty = true; + +terminate: + return ext4_fs_put_block_group_ref(&bg_ref); +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_bcache.c b/Library/lwext4/src/ext4_bcache.c new file mode 100644 index 0000000..9d3c7fb --- /dev/null +++ b/Library/lwext4/src/ext4_bcache.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_bcache.c + * @brief Block cache allocator. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +static int ext4_bcache_lba_compare(struct ext4_buf *a, struct ext4_buf *b) +{ + if (a->lba > b->lba) + return 1; + else if (a->lba < b->lba) + return -1; + return 0; +} + +static int ext4_bcache_lru_compare(struct ext4_buf *a, struct ext4_buf *b) +{ + if (a->lru_id > b->lru_id) + return 1; + else if (a->lru_id < b->lru_id) + return -1; + return 0; +} + +RB_GENERATE_INTERNAL(ext4_buf_lba, ext4_buf, lba_node, + ext4_bcache_lba_compare, static inline) +RB_GENERATE_INTERNAL(ext4_buf_lru, ext4_buf, lru_node, + ext4_bcache_lru_compare, static inline) + +int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt, + uint32_t itemsize) +{ + ext4_assert(bc && cnt && itemsize); + + memset(bc, 0, sizeof(struct ext4_bcache)); + + bc->cnt = cnt; + bc->itemsize = itemsize; + bc->ref_blocks = 0; + bc->max_ref_blocks = 0; + + return EOK; +} + +void ext4_bcache_cleanup(struct ext4_bcache *bc) +{ + struct ext4_buf *buf, *tmp; + RB_FOREACH_SAFE(buf, ext4_buf_lba, &bc->lba_root, tmp) { + ext4_block_flush_buf(bc->bdev, buf); + ext4_bcache_drop_buf(bc, buf); + } +} + +int ext4_bcache_fini_dynamic(struct ext4_bcache *bc) +{ + memset(bc, 0, sizeof(struct ext4_bcache)); + return EOK; +} + +/**@brief: + * + * This is ext4_bcache, the module handling basic buffer-cache stuff. + * + * Buffers in a bcache are sorted by their LBA and stored in a + * RB-Tree(lba_root). + * + * Bcache also maintains another RB-Tree(lru_root) right now, where + * buffers are sorted by their LRU id. + * + * A singly-linked list is used to track those dirty buffers which are + * ready to be flushed. (Those buffers which are dirty but also referenced + * are not considered ready to be flushed.) + * + * When a buffer is not referenced, it will be stored in both lba_root + * and lru_root, while it will only be stored in lba_root when it is + * referenced. + */ + +static struct ext4_buf * +ext4_buf_alloc(struct ext4_bcache *bc, uint64_t lba) +{ + void *data; + struct ext4_buf *buf; + data = ext4_malloc(bc->itemsize); + if (!data) + return NULL; + + buf = ext4_calloc(1, sizeof(struct ext4_buf)); + if (!buf) { + ext4_free(data); + return NULL; + } + + buf->lba = lba; + buf->data = data; + buf->bc = bc; + return buf; +} + +static void ext4_buf_free(struct ext4_buf *buf) +{ + ext4_free(buf->data); + ext4_free(buf); +} + +static struct ext4_buf * +ext4_buf_lookup(struct ext4_bcache *bc, uint64_t lba) +{ + struct ext4_buf tmp = { + .lba = lba + }; + + return RB_FIND(ext4_buf_lba, &bc->lba_root, &tmp); +} + +struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc) +{ + return RB_MIN(ext4_buf_lru, &bc->lru_root); +} + +void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf) +{ + /* Warn on dropping any referenced buffers.*/ + if (buf->refctr) { + ext4_dbg(DEBUG_BCACHE, DBG_WARN "Buffer is still referenced. " + "lba: %" PRIu64 ", refctr: %" PRIu32 "\n", + buf->lba, buf->refctr); + } else + RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf); + + RB_REMOVE(ext4_buf_lba, &bc->lba_root, buf); + + /*Forcibly drop dirty buffer.*/ + if (ext4_bcache_test_flag(buf, BC_DIRTY)) + ext4_bcache_remove_dirty_node(bc, buf); + + ext4_buf_free(buf); + bc->ref_blocks--; +} + +void ext4_bcache_invalidate_buf(struct ext4_bcache *bc, + struct ext4_buf *buf) +{ + buf->end_write = NULL; + buf->end_write_arg = NULL; + + /* Clear both dirty and up-to-date flags. */ + if (ext4_bcache_test_flag(buf, BC_DIRTY)) + ext4_bcache_remove_dirty_node(bc, buf); + + ext4_bcache_clear_dirty(buf); +} + +void ext4_bcache_invalidate_lba(struct ext4_bcache *bc, + uint64_t from, + uint32_t cnt) +{ + uint64_t end = from + cnt - 1; + struct ext4_buf *tmp = ext4_buf_lookup(bc, from), *buf; + RB_FOREACH_FROM(buf, ext4_buf_lba, tmp) { + if (buf->lba > end) + break; + + ext4_bcache_invalidate_buf(bc, buf); + } +} + +struct ext4_buf * +ext4_bcache_find_get(struct ext4_bcache *bc, struct ext4_block *b, + uint64_t lba) +{ + struct ext4_buf *buf = ext4_buf_lookup(bc, lba); + if (buf) { + /* If buffer is not referenced. */ + if (!buf->refctr) { + /* Assign new value to LRU id and increment LRU counter + * by 1*/ + buf->lru_id = ++bc->lru_ctr; + RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf); + if (ext4_bcache_test_flag(buf, BC_DIRTY)) + ext4_bcache_remove_dirty_node(bc, buf); + + } + + ext4_bcache_inc_ref(buf); + + b->lb_id = lba; + b->buf = buf; + b->data = buf->data; + } + return buf; +} + +int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b, + bool *is_new) +{ + /* Try to search the buffer with exaxt LBA. */ + struct ext4_buf *buf = ext4_bcache_find_get(bc, b, b->lb_id); + if (buf) { + *is_new = false; + return EOK; + } + + /* We need to allocate one buffer.*/ + buf = ext4_buf_alloc(bc, b->lb_id); + if (!buf) + return ENOMEM; + + RB_INSERT(ext4_buf_lba, &bc->lba_root, buf); + /* One more buffer in bcache now. :-) */ + bc->ref_blocks++; + + /*Calc ref blocks max depth*/ + if (bc->max_ref_blocks < bc->ref_blocks) + bc->max_ref_blocks = bc->ref_blocks; + + + ext4_bcache_inc_ref(buf); + /* Assign new value to LRU id and increment LRU counter + * by 1*/ + buf->lru_id = ++bc->lru_ctr; + + b->buf = buf; + b->data = buf->data; + + *is_new = true; + return EOK; +} + +int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b) +{ + struct ext4_buf *buf = b->buf; + + ext4_assert(bc && b); + + /*Check if valid.*/ + ext4_assert(b->lb_id); + + /*Block should have a valid pointer to ext4_buf.*/ + ext4_assert(buf); + + /*Check if someone don't try free unreferenced block cache.*/ + ext4_assert(buf->refctr); + + /*Just decrease reference counter*/ + ext4_bcache_dec_ref(buf); + + /* We are the last one touching this buffer, do the cleanups. */ + if (!buf->refctr) { + RB_INSERT(ext4_buf_lru, &bc->lru_root, buf); + /* This buffer is ready to be flushed. */ + if (ext4_bcache_test_flag(buf, BC_DIRTY) && + ext4_bcache_test_flag(buf, BC_UPTODATE)) { + if (bc->bdev->cache_write_back && + !ext4_bcache_test_flag(buf, BC_FLUSH) && + !ext4_bcache_test_flag(buf, BC_TMP)) + ext4_bcache_insert_dirty_node(bc, buf); + else { + ext4_block_flush_buf(bc->bdev, buf); + ext4_bcache_clear_flag(buf, BC_FLUSH); + } + } + + /* The buffer is invalidated...drop it. */ + if (!ext4_bcache_test_flag(buf, BC_UPTODATE) || + ext4_bcache_test_flag(buf, BC_TMP)) + ext4_bcache_drop_buf(bc, buf); + + } + + b->lb_id = 0; + b->data = 0; + + return EOK; +} + +bool ext4_bcache_is_full(struct ext4_bcache *bc) +{ + return (bc->cnt <= bc->ref_blocks); +} + + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_bitmap.c b/Library/lwext4/src/ext4_bitmap.c new file mode 100644 index 0000000..43b6431 --- /dev/null +++ b/Library/lwext4/src/ext4_bitmap.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_bitmap.c + * @brief Block/inode bitmap allocator. + */ + +#include +#include +#include +#include +#include + +#include + +void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt) +{ + uint32_t i = sbit; + + while (i & 7) { + + if (!bcnt) + return; + + ext4_bmap_bit_clr(bmap, i); + + bcnt--; + i++; + } + sbit = i; + bmap += (sbit >> 3); + +#if CONFIG_UNALIGNED_ACCESS + while (bcnt >= 32) { + *(uint32_t *)bmap = 0; + bmap += 4; + bcnt -= 32; + sbit += 32; + } + + while (bcnt >= 16) { + *(uint16_t *)bmap = 0; + bmap += 2; + bcnt -= 16; + sbit += 16; + } +#endif + + while (bcnt >= 8) { + *bmap = 0; + bmap += 1; + bcnt -= 8; + sbit += 8; + } + + for (i = 0; i < bcnt; ++i) { + ext4_bmap_bit_clr(bmap, i); + } +} + +int ext4_bmap_bit_find_clr(uint8_t *bmap, uint32_t sbit, uint32_t ebit, + uint32_t *bit_id) +{ + uint32_t i; + uint32_t bcnt = ebit - sbit; + + i = sbit; + + while (i & 7) { + + if (!bcnt) + return ENOSPC; + + if (ext4_bmap_is_bit_clr(bmap, i)) { + *bit_id = sbit; + return EOK; + } + + i++; + bcnt--; + } + + sbit = i; + bmap += (sbit >> 3); + +#if CONFIG_UNALIGNED_ACCESS + while (bcnt >= 32) { + if (*(uint32_t *)bmap != 0xFFFFFFFF) + goto finish_it; + + bmap += 4; + bcnt -= 32; + sbit += 32; + } + + while (bcnt >= 16) { + if (*(uint16_t *)bmap != 0xFFFF) + goto finish_it; + + bmap += 2; + bcnt -= 16; + sbit += 16; + } +finish_it: +#endif + while (bcnt >= 8) { + if (*bmap != 0xFF) { + for (i = 0; i < 8; ++i) { + if (ext4_bmap_is_bit_clr(bmap, i)) { + *bit_id = sbit + i; + return EOK; + } + } + } + + bmap += 1; + bcnt -= 8; + sbit += 8; + } + + for (i = 0; i < bcnt; ++i) { + if (ext4_bmap_is_bit_clr(bmap, i)) { + *bit_id = sbit + i; + return EOK; + } + } + + return ENOSPC; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_block_group.c b/Library/lwext4/src/ext4_block_group.c new file mode 100644 index 0000000..d2bb1b1 --- /dev/null +++ b/Library/lwext4/src/ext4_block_group.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_block_group.c + * @brief Block group function set. + */ + +#include +#include +#include +#include +#include + +#include + +/**@brief CRC-16 look up table*/ +static uint16_t const crc16_tab[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, + 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, + 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, + 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, + 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, + 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, + 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, + 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, + 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, + 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, + 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, + 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, + 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, + 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, + 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, + 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, + 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, + 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, + 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, + 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, + 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, + 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, + 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, + 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, + 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, + 0x4100, 0x81C1, 0x8081, 0x4040}; + +uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len) +{ + while (len--) + + crc = (((crc >> 8) & 0xffU) ^ + crc16_tab[(crc ^ *buffer++) & 0xffU]) & + 0x0000ffffU; + return crc; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_blockdev.c b/Library/lwext4/src/ext4_blockdev.c new file mode 100644 index 0000000..c01093a --- /dev/null +++ b/Library/lwext4/src/ext4_blockdev.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_blockdev.c + * @brief Block device module. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static void ext4_bdif_lock(struct ext4_blockdev *bdev) +{ + if (!bdev->bdif->lock) + return; + + int r = bdev->bdif->lock(bdev); + ext4_assert(r == EOK); +} + +static void ext4_bdif_unlock(struct ext4_blockdev *bdev) +{ + if (!bdev->bdif->unlock) + return; + + int r = bdev->bdif->unlock(bdev); + ext4_assert(r == EOK); +} + +static int ext4_bdif_bread(struct ext4_blockdev *bdev, void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + ext4_bdif_lock(bdev); + int r = bdev->bdif->bread(bdev, buf, blk_id, blk_cnt); + bdev->bdif->bread_ctr++; + ext4_bdif_unlock(bdev); + return r; +} + +static int ext4_bdif_bwrite(struct ext4_blockdev *bdev, const void *buf, + uint64_t blk_id, uint32_t blk_cnt) +{ + ext4_bdif_lock(bdev); + int r = bdev->bdif->bwrite(bdev, buf, blk_id, blk_cnt); + bdev->bdif->bwrite_ctr++; + ext4_bdif_unlock(bdev); + return r; +} + +int ext4_block_init(struct ext4_blockdev *bdev) +{ + int rc; + ext4_assert(bdev); + ext4_assert(bdev->bdif); + ext4_assert(bdev->bdif->open && + bdev->bdif->close && + bdev->bdif->bread && + bdev->bdif->bwrite); + + if (bdev->bdif->ph_refctr) { + bdev->bdif->ph_refctr++; + return EOK; + } + + /*Low level block init*/ + rc = bdev->bdif->open(bdev); + if (rc != EOK) + return rc; + + bdev->bdif->ph_refctr = 1; + return EOK; +} + +int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc) +{ + ext4_assert(bdev && bc); + bdev->bc = bc; + bc->bdev = bdev; + return EOK; +} + +void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint32_t lb_bsize) +{ + /*Logical block size has to be multiply of physical */ + ext4_assert(!(lb_bsize % bdev->bdif->ph_bsize)); + + bdev->lg_bsize = lb_bsize; + bdev->lg_bcnt = bdev->part_size / lb_bsize; +} + +int ext4_block_fini(struct ext4_blockdev *bdev) +{ + ext4_assert(bdev); + + if (!bdev->bdif->ph_refctr) + return EOK; + + bdev->bdif->ph_refctr--; + if (bdev->bdif->ph_refctr) + return EOK; + + /*Low level block fini*/ + return bdev->bdif->close(bdev); +} + +int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf) +{ + int r; + struct ext4_bcache *bc = bdev->bc; + + if (ext4_bcache_test_flag(buf, BC_DIRTY) && + ext4_bcache_test_flag(buf, BC_UPTODATE)) { + r = ext4_blocks_set_direct(bdev, buf->data, buf->lba, 1); + if (r) { + if (buf->end_write) { + bc->dont_shake = true; + buf->end_write(bc, buf, r, buf->end_write_arg); + bc->dont_shake = false; + } + + return r; + } + + ext4_bcache_remove_dirty_node(bc, buf); + ext4_bcache_clear_flag(buf, BC_DIRTY); + if (buf->end_write) { + bc->dont_shake = true; + buf->end_write(bc, buf, r, buf->end_write_arg); + bc->dont_shake = false; + } + } + return EOK; +} + +int ext4_block_flush_lba(struct ext4_blockdev *bdev, uint64_t lba) +{ + int r = EOK; + struct ext4_buf *buf; + struct ext4_block b; + buf = ext4_bcache_find_get(bdev->bc, &b, lba); + if (buf) { + r = ext4_block_flush_buf(bdev, buf); + ext4_bcache_free(bdev->bc, &b); + } + return r; +} + +int ext4_block_cache_shake(struct ext4_blockdev *bdev) +{ + int r = EOK; + struct ext4_buf *buf; + if (bdev->bc->dont_shake) + return EOK; + + bdev->bc->dont_shake = true; + + while (!RB_EMPTY(&bdev->bc->lru_root) && + ext4_bcache_is_full(bdev->bc)) { + + buf = ext4_buf_lowest_lru(bdev->bc); + ext4_assert(buf); + if (ext4_bcache_test_flag(buf, BC_DIRTY)) { + r = ext4_block_flush_buf(bdev, buf); + if (r != EOK) + break; + + } + + ext4_bcache_drop_buf(bdev->bc, buf); + } + bdev->bc->dont_shake = false; + return r; +} + +int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b, + uint64_t lba) +{ + bool is_new; + int r; + + ext4_assert(bdev && b); + + if (!bdev->bdif->ph_refctr) + return EIO; + + if (!(lba < bdev->lg_bcnt)) + return ENXIO; + + b->lb_id = lba; + + /*If cache is full we have to (flush and) drop it anyway :(*/ + r = ext4_block_cache_shake(bdev); + if (r != EOK) + return r; + + r = ext4_bcache_alloc(bdev->bc, b, &is_new); + if (r != EOK) + return r; + + if (!b->data) + return ENOMEM; + + return EOK; +} + +int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b, + uint64_t lba) +{ + int r = ext4_block_get_noread(bdev, b, lba); + if (r != EOK) + return r; + + if (ext4_bcache_test_flag(b->buf, BC_UPTODATE)) { + /* Data in the cache is up-to-date. + * Reading from physical device is not required */ + return EOK; + } + + r = ext4_blocks_get_direct(bdev, b->data, lba, 1); + if (r != EOK) { + ext4_bcache_free(bdev->bc, b); + b->lb_id = 0; + return r; + } + + /* Mark buffer up-to-date, since + * fresh data is read from physical device just now. */ + ext4_bcache_set_flag(b->buf, BC_UPTODATE); + return EOK; +} + +int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b) +{ + ext4_assert(bdev && b); + ext4_assert(b->buf); + + if (!bdev->bdif->ph_refctr) + return EIO; + + return ext4_bcache_free(bdev->bc, b); +} + +int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba, + uint32_t cnt) +{ + uint64_t pba; + uint32_t pb_cnt; + + ext4_assert(bdev && buf); + + pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize; + pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize; + + return ext4_bdif_bread(bdev, buf, pba, pb_cnt * cnt); +} + +int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf, + uint64_t lba, uint32_t cnt) +{ + uint64_t pba; + uint32_t pb_cnt; + + ext4_assert(bdev && buf); + + pba = (lba * bdev->lg_bsize + bdev->part_offset) / bdev->bdif->ph_bsize; + pb_cnt = bdev->lg_bsize / bdev->bdif->ph_bsize; + + return ext4_bdif_bwrite(bdev, buf, pba, pb_cnt * cnt); +} + +int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off, + const void *buf, uint32_t len) +{ + uint64_t block_idx; + uint32_t blen; + uint32_t unalg; + int r = EOK; + + const uint8_t *p = (void *)buf; + + ext4_assert(bdev && buf); + + if (!bdev->bdif->ph_refctr) + return EIO; + + if (off + len > bdev->part_size) + return EINVAL; /*Ups. Out of range operation*/ + + block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize); + + /*OK lets deal with the first possible unaligned block*/ + unalg = (off & (bdev->bdif->ph_bsize - 1)); + if (unalg) { + + uint32_t wlen = (bdev->bdif->ph_bsize - unalg) > len + ? len + : (bdev->bdif->ph_bsize - unalg); + + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + + memcpy(bdev->bdif->ph_bbuf + unalg, p, wlen); + r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + + p += wlen; + len -= wlen; + block_idx++; + } + + /*Aligned data*/ + blen = len / bdev->bdif->ph_bsize; + if (blen != 0) { + r = ext4_bdif_bwrite(bdev, p, block_idx, blen); + if (r != EOK) + return r; + + p += bdev->bdif->ph_bsize * blen; + len -= bdev->bdif->ph_bsize * blen; + + block_idx += blen; + } + + /*Rest of the data*/ + if (len) { + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + + memcpy(bdev->bdif->ph_bbuf, p, len); + r = ext4_bdif_bwrite(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + } + + return r; +} + +int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf, + uint32_t len) +{ + uint64_t block_idx; + uint32_t blen; + uint32_t unalg; + int r = EOK; + + uint8_t *p = (void *)buf; + + ext4_assert(bdev && buf); + + if (!bdev->bdif->ph_refctr) + return EIO; + + if (off + len > bdev->part_size) + return EINVAL; /*Ups. Out of range operation*/ + + block_idx = ((off + bdev->part_offset) / bdev->bdif->ph_bsize); + + /*OK lets deal with the first possible unaligned block*/ + unalg = (off & (bdev->bdif->ph_bsize - 1)); + if (unalg) { + + uint32_t rlen = (bdev->bdif->ph_bsize - unalg) > len + ? len + : (bdev->bdif->ph_bsize - unalg); + + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + + memcpy(p, bdev->bdif->ph_bbuf + unalg, rlen); + + p += rlen; + len -= rlen; + block_idx++; + } + + /*Aligned data*/ + blen = len / bdev->bdif->ph_bsize; + + if (blen != 0) { + r = ext4_bdif_bread(bdev, p, block_idx, blen); + if (r != EOK) + return r; + + p += bdev->bdif->ph_bsize * blen; + len -= bdev->bdif->ph_bsize * blen; + + block_idx += blen; + } + + /*Rest of the data*/ + if (len) { + r = ext4_bdif_bread(bdev, bdev->bdif->ph_bbuf, block_idx, 1); + if (r != EOK) + return r; + + memcpy(p, bdev->bdif->ph_bbuf, len); + } + + return r; +} + +int ext4_block_cache_flush(struct ext4_blockdev *bdev) +{ + while (!SLIST_EMPTY(&bdev->bc->dirty_list)) { + int r; + struct ext4_buf *buf = SLIST_FIRST(&bdev->bc->dirty_list); + ext4_assert(buf); + r = ext4_block_flush_buf(bdev, buf); + if (r != EOK) + return r; + + } + return EOK; +} + +int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off) +{ + if (on_off) + bdev->cache_write_back++; + + if (!on_off && bdev->cache_write_back) + bdev->cache_write_back--; + + if (bdev->cache_write_back) + return EOK; + + /*Flush data in all delayed cache blocks*/ + return ext4_block_cache_flush(bdev); +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_crc32.c b/Library/lwext4/src/ext4_crc32.c new file mode 100644 index 0000000..17ae0d0 --- /dev/null +++ b/Library/lwext4/src/ext4_crc32.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Based on FreeBSD. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_crc32c.c + * @brief Crc32c routine. Taken from FreeBSD kernel. + */ + +#include +#include +#include +#include +#include + +#include "ext4_crc32.h" + +static const uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +/* */ +/* CRC LOOKUP TABLE */ +/* ================ */ +/* The following CRC lookup table was generated automagically */ +/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ +/* Program V1.0 using the following model parameters: */ +/* */ +/* Width : 4 bytes. */ +/* Poly : 0x1EDC6F41L */ +/* Reverse : TRUE. */ +/* */ +/* For more information on the Rocksoft^tm Model CRC Algorithm, */ +/* see the document titled "A Painless Guide to CRC Error */ +/* Detection Algorithms" by Ross Williams */ +/* (ross@guest.adelaide.edu.au.). This document is likely to be */ +/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ +/* */ +static const uint32_t crc32c_tab[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, + 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, + 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, + 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, + 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, + 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, + 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, + 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, + 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, + 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, + 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, + 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, + 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, + 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, + 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, + 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, + 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, + 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, + 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, + 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, + 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, + 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, + 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, + 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, + 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, + 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, + 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, + 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, + 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, + 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, + 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, + 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, + 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, + 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, + 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, + 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, + 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, + 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, + 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, + 0xAD7D5351L}; + +static inline uint32_t crc32(uint32_t crc, const void *buf, uint32_t size, + const uint32_t *tab) +{ + const uint8_t *p = (const uint8_t *)buf; + + while (size--) + crc = tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return (crc); +} + +uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size) +{ + return crc32(crc, buf, size, crc32_tab); +} + +uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size) +{ + return crc32(crc, buf, size, crc32c_tab); +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_debug.c b/Library/lwext4/src/ext4_debug.c new file mode 100644 index 0000000..356a157 --- /dev/null +++ b/Library/lwext4/src/ext4_debug.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_debug.c + * @brief Debug printf and assert macros. + */ + +#include +#include +#include +#include +#include + +#include + +static uint32_t debug_mask; + +void ext4_dmask_set(uint32_t m) +{ + debug_mask |= m; +} + +void ext4_dmask_clr(uint32_t m) +{ + debug_mask &= ~m; +} + +uint32_t ext4_dmask_get(void) +{ + return debug_mask; +} + + + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_dir.c b/Library/lwext4/src/ext4_dir.c new file mode 100644 index 0000000..29a51c5 --- /dev/null +++ b/Library/lwext4/src/ext4_dir.c @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_dir.h + * @brief Directory handle procedures. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/****************************************************************************/ + +/* Walk through a dirent block to find a checksum "dirent" at the tail */ +static struct ext4_dir_entry_tail * +ext4_dir_get_tail(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *de) +{ + struct ext4_dir_entry_tail *t; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + t = EXT4_DIRENT_TAIL(de, ext4_sb_get_block_size(sb)); + + if (t->reserved_zero1 || t->reserved_zero2) + return NULL; + if (to_le16(t->rec_len) != sizeof(struct ext4_dir_entry_tail)) + return NULL; + if (t->reserved_ft != EXT4_DIRENTRY_DIR_CSUM) + return NULL; + + return t; +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_dir_csum(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *dirent, int size) +{ + uint32_t csum; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint32_t ino_index = to_le32(inode_ref->index); + uint32_t ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode)); + + /* First calculate crc32 checksum against fs uuid */ + csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); + /* Then calculate crc32 checksum against inode number + * and inode generation */ + csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index)); + csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen)); + /* Finally calculate crc32 checksum against directory entries */ + csum = ext4_crc32c(csum, dirent, size); + return csum; +} +#else +#define ext4_dir_csum(...) 0 +#endif + +bool ext4_dir_csum_verify(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *dirent) +{ +#ifdef CONFIG_META_CSUM_ENABLE + struct ext4_dir_entry_tail *t; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + /* Compute the checksum only if the filesystem supports it */ + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + t = ext4_dir_get_tail(inode_ref, dirent); + if (!t) { + /* There is no space to hold the checksum */ + return false; + } + + ptrdiff_t __unused diff = (char *)t - (char *)dirent; + uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff); + if (t->checksum != to_le32(csum)) + return false; + + } +#endif + return true; +} + +void ext4_dir_init_entry_tail(struct ext4_dir_entry_tail *t) +{ + memset(t, 0, sizeof(struct ext4_dir_entry_tail)); + t->rec_len = to_le16(sizeof(struct ext4_dir_entry_tail)); + t->reserved_ft = EXT4_DIRENTRY_DIR_CSUM; +} + +void ext4_dir_set_csum(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *dirent) +{ + struct ext4_dir_entry_tail *t; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + /* Compute the checksum only if the filesystem supports it */ + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + t = ext4_dir_get_tail(inode_ref, dirent); + if (!t) { + /* There is no space to hold the checksum */ + return; + } + + ptrdiff_t __unused diff = (char *)t - (char *)dirent; + uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff); + t->checksum = to_le32(csum); + } +} + +/**@brief Do some checks before returning iterator. + * @param it Iterator to be checked + * @param block_size Size of data block + * @return Error code + */ +static int ext4_dir_iterator_set(struct ext4_dir_iter *it, + uint32_t block_size) +{ + uint32_t off_in_block = it->curr_off % block_size; + struct ext4_sblock *sb = &it->inode_ref->fs->sb; + + it->curr = NULL; + + /* Ensure proper alignment */ + if ((off_in_block % 4) != 0) + return EIO; + + /* Ensure that the core of the entry does not overflow the block */ + if (off_in_block > block_size - 8) + return EIO; + + struct ext4_dir_en *en; + en = (void *)(it->curr_blk.data + off_in_block); + + /* Ensure that the whole entry does not overflow the block */ + uint16_t length = ext4_dir_en_get_entry_len(en); + if (off_in_block + length > block_size) + return EIO; + + /* Ensure the name length is not too large */ + if (ext4_dir_en_get_name_len(sb, en) > length - 8) + return EIO; + + /* Everything OK - "publish" the entry */ + it->curr = en; + return EOK; +} + +/**@brief Seek to next valid directory entry. + * Here can be jumped to the next data block. + * @param it Initialized iterator + * @param pos Position of the next entry + * @return Error code + */ +static int ext4_dir_iterator_seek(struct ext4_dir_iter *it, uint64_t pos) +{ + struct ext4_sblock *sb = &it->inode_ref->fs->sb; + struct ext4_inode *inode = it->inode_ref->inode; + struct ext4_blockdev *bdev = it->inode_ref->fs->bdev; + uint64_t size = ext4_inode_get_size(sb, inode); + int r; + + /* The iterator is not valid until we seek to the desired position */ + it->curr = NULL; + + /* Are we at the end? */ + if (pos >= size) { + if (it->curr_blk.lb_id) { + + r = ext4_block_set(bdev, &it->curr_blk); + it->curr_blk.lb_id = 0; + if (r != EOK) + return r; + } + + it->curr_off = pos; + return EOK; + } + + /* Compute next block address */ + uint32_t block_size = ext4_sb_get_block_size(sb); + uint64_t current_blk_idx = it->curr_off / block_size; + uint32_t next_blk_idx = (uint32_t)(pos / block_size); + + /* + * If we don't have a block or are moving across block boundary, + * we need to get another block + */ + if ((it->curr_blk.lb_id == 0) || + (current_blk_idx != next_blk_idx)) { + if (it->curr_blk.lb_id) { + r = ext4_block_set(bdev, &it->curr_blk); + it->curr_blk.lb_id = 0; + + if (r != EOK) + return r; + } + + ext4_fsblk_t next_blk; + r = ext4_fs_get_inode_dblk_idx(it->inode_ref, next_blk_idx, + &next_blk, false); + if (r != EOK) + return r; + + r = ext4_trans_block_get(bdev, &it->curr_blk, next_blk); + if (r != EOK) { + it->curr_blk.lb_id = 0; + return r; + } + } + + it->curr_off = pos; + return ext4_dir_iterator_set(it, block_size); +} + +int ext4_dir_iterator_init(struct ext4_dir_iter *it, + struct ext4_inode_ref *inode_ref, uint64_t pos) +{ + it->inode_ref = inode_ref; + it->curr = 0; + it->curr_off = 0; + it->curr_blk.lb_id = 0; + + return ext4_dir_iterator_seek(it, pos); +} + +int ext4_dir_iterator_next(struct ext4_dir_iter *it) +{ + int r = EOK; + uint16_t skip; + + while (r == EOK) { + skip = ext4_dir_en_get_entry_len(it->curr); + r = ext4_dir_iterator_seek(it, it->curr_off + skip); + + if (!it->curr) + break; + /*Skip NULL referenced entry*/ + if (ext4_dir_en_get_inode(it->curr) != 0) + break; + } + + return r; +} + +int ext4_dir_iterator_fini(struct ext4_dir_iter *it) +{ + it->curr = 0; + + if (it->curr_blk.lb_id) + return ext4_block_set(it->inode_ref->fs->bdev, &it->curr_blk); + + return EOK; +} + +void ext4_dir_write_entry(struct ext4_sblock *sb, struct ext4_dir_en *en, + uint16_t entry_len, struct ext4_inode_ref *child, + const char *name, size_t name_len) +{ + /* Check maximum entry length */ + ext4_assert(entry_len <= ext4_sb_get_block_size(sb)); + + /* Set type of entry */ + switch (ext4_inode_type(sb, child->inode)) { + case EXT4_INODE_MODE_DIRECTORY: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_DIR); + break; + case EXT4_INODE_MODE_FILE: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_REG_FILE); + break; + case EXT4_INODE_MODE_SOFTLINK: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SYMLINK); + break; + case EXT4_INODE_MODE_CHARDEV: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_CHRDEV); + break; + case EXT4_INODE_MODE_BLOCKDEV: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_BLKDEV); + break; + case EXT4_INODE_MODE_FIFO: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_FIFO); + break; + case EXT4_INODE_MODE_SOCKET: + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SOCK); + break; + default: + /* FIXME: unsupported filetype */ + ext4_dir_en_set_inode_type(sb, en, EXT4_DE_UNKNOWN); + } + + /* Set basic attributes */ + ext4_dir_en_set_inode(en, child->index); + ext4_dir_en_set_entry_len(en, entry_len); + ext4_dir_en_set_name_len(sb, en, (uint16_t)name_len); + + /* Write name */ + memcpy(en->name, name, name_len); +} + +int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name, + uint32_t name_len, struct ext4_inode_ref *child) +{ + int r; + struct ext4_fs *fs = parent->fs; + struct ext4_sblock *sb = &parent->fs->sb; + +#if CONFIG_DIR_INDEX_ENABLE + /* Index adding (if allowed) */ + if ((ext4_sb_feature_com(sb, EXT4_FCOM_DIR_INDEX)) && + (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) { + r = ext4_dir_dx_add_entry(parent, child, name, name_len); + + /* Check if index is not corrupted */ + if (r != EXT4_ERR_BAD_DX_DIR) { + if (r != EOK) + return r; + + return EOK; + } + + /* Needed to clear dir index flag if corrupted */ + ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX); + parent->dirty = true; + } +#endif + + /* Linear algorithm */ + uint32_t iblock = 0; + ext4_fsblk_t fblock = 0; + uint32_t block_size = ext4_sb_get_block_size(sb); + uint64_t inode_size = ext4_inode_get_size(sb, parent->inode); + uint32_t total_blocks = (uint32_t)(inode_size / block_size); + + /* Find block, where is space for new entry and try to add */ + bool success = false; + for (iblock = 0; iblock < total_blocks; ++iblock) { + r = ext4_fs_get_inode_dblk_idx(parent, iblock, &fblock, false); + if (r != EOK) + return r; + + struct ext4_block block; + r = ext4_trans_block_get(fs->bdev, &block, fblock); + if (r != EOK) + return r; + + if (!ext4_dir_csum_verify(parent, (void *)block.data)) { + ext4_dbg(DEBUG_DIR, + DBG_WARN "Leaf block checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + parent->index, + iblock); + } + + /* If adding is successful, function can finish */ + r = ext4_dir_try_insert_entry(sb, parent, &block, child, + name, name_len); + if (r == EOK) + success = true; + + r = ext4_block_set(fs->bdev, &block); + if (r != EOK) + return r; + + if (success) + return EOK; + } + + /* No free block found - needed to allocate next data block */ + + iblock = 0; + fblock = 0; + r = ext4_fs_append_inode_dblk(parent, &fblock, &iblock); + if (r != EOK) + return r; + + /* Load new block */ + struct ext4_block b; + + r = ext4_trans_block_get_noread(fs->bdev, &b, fblock); + if (r != EOK) + return r; + + /* Fill block with zeroes */ + memset(b.data, 0, block_size); + struct ext4_dir_en *blk_en = (void *)b.data; + + /* Save new block */ + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint16_t el = block_size - sizeof(struct ext4_dir_entry_tail); + ext4_dir_write_entry(sb, blk_en, el, child, name, name_len); + ext4_dir_init_entry_tail(EXT4_DIRENT_TAIL(b.data, block_size)); + } else { + ext4_dir_write_entry(sb, blk_en, block_size, child, name, + name_len); + } + + ext4_dir_set_csum(parent, (void *)b.data); + ext4_trans_set_block_dirty(b.buf); + r = ext4_block_set(fs->bdev, &b); + + return r; +} + +int ext4_dir_find_entry(struct ext4_dir_search_result *result, + struct ext4_inode_ref *parent, const char *name, + uint32_t name_len) +{ + int r; + struct ext4_sblock *sb = &parent->fs->sb; + + /* Entry clear */ + result->block.lb_id = 0; + result->dentry = NULL; + +#if CONFIG_DIR_INDEX_ENABLE + /* Index search */ + if ((ext4_sb_feature_com(sb, EXT4_FCOM_DIR_INDEX)) && + (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) { + r = ext4_dir_dx_find_entry(result, parent, name_len, name); + /* Check if index is not corrupted */ + if (r != EXT4_ERR_BAD_DX_DIR) { + if (r != EOK) + return r; + + return EOK; + } + + /* Needed to clear dir index flag if corrupted */ + ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX); + parent->dirty = true; + } +#endif + + /* Linear algorithm */ + + uint32_t iblock; + ext4_fsblk_t fblock; + uint32_t block_size = ext4_sb_get_block_size(sb); + uint64_t inode_size = ext4_inode_get_size(sb, parent->inode); + uint32_t total_blocks = (uint32_t)(inode_size / block_size); + + /* Walk through all data blocks */ + for (iblock = 0; iblock < total_blocks; ++iblock) { + /* Load block address */ + r = ext4_fs_get_inode_dblk_idx(parent, iblock, &fblock, false); + if (r != EOK) + return r; + + /* Load data block */ + struct ext4_block b; + r = ext4_trans_block_get(parent->fs->bdev, &b, fblock); + if (r != EOK) + return r; + + if (!ext4_dir_csum_verify(parent, (void *)b.data)) { + ext4_dbg(DEBUG_DIR, + DBG_WARN "Leaf block checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + parent->index, + iblock); + } + + /* Try to find entry in block */ + struct ext4_dir_en *res_entry; + r = ext4_dir_find_in_block(&b, sb, name_len, name, &res_entry); + if (r == EOK) { + result->block = b; + result->dentry = res_entry; + return EOK; + } + + /* Entry not found - put block and continue to the next block */ + + r = ext4_block_set(parent->fs->bdev, &b); + if (r != EOK) + return r; + } + + return ENOENT; +} + +int ext4_dir_remove_entry(struct ext4_inode_ref *parent, const char *name, + uint32_t name_len) +{ + struct ext4_sblock *sb = &parent->fs->sb; + /* Check if removing from directory */ + if (!ext4_inode_is_type(sb, parent->inode, EXT4_INODE_MODE_DIRECTORY)) + return ENOTDIR; + + /* Try to find entry */ + struct ext4_dir_search_result result; + int rc = ext4_dir_find_entry(&result, parent, name, name_len); + if (rc != EOK) + return rc; + + /* Invalidate entry */ + ext4_dir_en_set_inode(result.dentry, 0); + + /* Store entry position in block */ + uint32_t pos = (uint8_t *)result.dentry - result.block.data; + + /* + * If entry is not the first in block, it must be merged + * with previous entry + */ + if (pos != 0) { + uint32_t offset = 0; + + /* Start from the first entry in block */ + struct ext4_dir_en *tmp_de =(void *)result.block.data; + uint16_t de_len = ext4_dir_en_get_entry_len(tmp_de); + + /* Find direct predecessor of removed entry */ + while ((offset + de_len) < pos) { + offset += ext4_dir_en_get_entry_len(tmp_de); + tmp_de = (void *)(result.block.data + offset); + de_len = ext4_dir_en_get_entry_len(tmp_de); + } + + ext4_assert(de_len + offset == pos); + + /* Add to removed entry length to predecessor's length */ + uint16_t del_len; + del_len = ext4_dir_en_get_entry_len(result.dentry); + ext4_dir_en_set_entry_len(tmp_de, de_len + del_len); + } + + ext4_dir_set_csum(parent, + (struct ext4_dir_en *)result.block.data); + ext4_trans_set_block_dirty(result.block.buf); + + return ext4_dir_destroy_result(parent, &result); +} + +int ext4_dir_try_insert_entry(struct ext4_sblock *sb, + struct ext4_inode_ref *inode_ref, + struct ext4_block *dst_blk, + struct ext4_inode_ref *child, const char *name, + uint32_t name_len) +{ + /* Compute required length entry and align it to 4 bytes */ + uint32_t block_size = ext4_sb_get_block_size(sb); + uint16_t required_len = sizeof(struct ext4_fake_dir_entry) + name_len; + + if ((required_len % 4) != 0) + required_len += 4 - (required_len % 4); + + /* Initialize pointers, stop means to upper bound */ + struct ext4_dir_en *start = (void *)dst_blk->data; + struct ext4_dir_en *stop = (void *)(dst_blk->data + block_size); + + /* + * Walk through the block and check for invalid entries + * or entries with free space for new entry + */ + while (start < stop) { + uint32_t inode = ext4_dir_en_get_inode(start); + uint16_t rec_len = ext4_dir_en_get_entry_len(start); + uint8_t itype = ext4_dir_en_get_inode_type(sb, start); + + /* If invalid and large enough entry, use it */ + if ((inode == 0) && (itype != EXT4_DIRENTRY_DIR_CSUM) && + (rec_len >= required_len)) { + ext4_dir_write_entry(sb, start, rec_len, child, name, + name_len); + ext4_dir_set_csum(inode_ref, (void *)dst_blk->data); + ext4_trans_set_block_dirty(dst_blk->buf); + + return EOK; + } + + /* Valid entry, try to split it */ + if (inode != 0) { + uint16_t used_len; + used_len = ext4_dir_en_get_name_len(sb, start); + + uint16_t sz; + sz = sizeof(struct ext4_fake_dir_entry) + used_len; + + if ((used_len % 4) != 0) + sz += 4 - (used_len % 4); + + uint16_t free_space = rec_len - sz; + + /* There is free space for new entry */ + if (free_space >= required_len) { + /* Cut tail of current entry */ + struct ext4_dir_en * new_entry; + new_entry = (void *)((uint8_t *)start + sz); + ext4_dir_en_set_entry_len(start, sz); + ext4_dir_write_entry(sb, new_entry, free_space, + child, name, name_len); + + ext4_dir_set_csum(inode_ref, + (void *)dst_blk->data); + ext4_trans_set_block_dirty(dst_blk->buf); + return EOK; + } + } + + /* Jump to the next entry */ + start = (void *)((uint8_t *)start + rec_len); + } + + /* No free space found for new entry */ + return ENOSPC; +} + +int ext4_dir_find_in_block(struct ext4_block *block, struct ext4_sblock *sb, + size_t name_len, const char *name, + struct ext4_dir_en **res_entry) +{ + /* Start from the first entry in block */ + struct ext4_dir_en *de = (struct ext4_dir_en *)block->data; + + /* Set upper bound for cycling */ + uint8_t *addr_limit = block->data + ext4_sb_get_block_size(sb); + + /* Walk through the block and check entries */ + while ((uint8_t *)de < addr_limit) { + /* Termination condition */ + if ((uint8_t *)de + name_len > addr_limit) + break; + + /* Valid entry - check it */ + if (ext4_dir_en_get_inode(de) != 0) { + /* For more efficient compare only lengths firstly*/ + uint16_t el = ext4_dir_en_get_name_len(sb, de); + if (el == name_len) { + /* Compare names */ + if (memcmp(name, de->name, name_len) == 0) { + *res_entry = de; + return EOK; + } + } + } + + uint16_t de_len = ext4_dir_en_get_entry_len(de); + + /* Corrupted entry */ + if (de_len == 0) + return EINVAL; + + /* Jump to next entry */ + de = (struct ext4_dir_en *)((uint8_t *)de + de_len); + } + + /* Entry not found */ + return ENOENT; +} + +int ext4_dir_destroy_result(struct ext4_inode_ref *parent, + struct ext4_dir_search_result *result) +{ + if (result->block.lb_id) + return ext4_block_set(parent->fs->bdev, &result->block); + + return EOK; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_dir_idx.c b/Library/lwext4/src/ext4_dir_idx.c new file mode 100644 index 0000000..f7a64dd --- /dev/null +++ b/Library/lwext4/src/ext4_dir_idx.c @@ -0,0 +1,1403 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_dir_idx.c + * @brief Directory indexing procedures. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/**@brief Get hash version used in directory index. + * @param root_info Pointer to root info structure of index + * @return Hash algorithm version + */ +static inline uint8_t +ext4_dir_dx_rinfo_get_hash_version(struct ext4_dir_idx_rinfo *ri) +{ + return ri->hash_version; +} + +/**@brief Set hash version, that will be used in directory index. + * @param root_info Pointer to root info structure of index + * @param v Hash algorithm version + */ +static inline void +ext4_dir_dx_rinfo_set_hash_version(struct ext4_dir_idx_rinfo *ri, uint8_t v) +{ + ri->hash_version = v; +} + +/**@brief Get length of root_info structure in bytes. + * @param root_info Pointer to root info structure of index + * @return Length of the structure + */ +static inline uint8_t +ext4_dir_dx_rinfo_get_info_length(struct ext4_dir_idx_rinfo *ri) +{ + return ri->info_length; +} + +/**@brief Set length of root_info structure in bytes. + * @param root_info Pointer to root info structure of index + * @param info_length Length of the structure + */ +static inline void +ext4_dir_dx_root_info_set_info_length(struct ext4_dir_idx_rinfo *ri, + uint8_t len) +{ + ri->info_length = len; +} + +/**@brief Get number of indirect levels of HTree. + * @param root_info Pointer to root info structure of index + * @return Height of HTree (actually only 0 or 1) + */ +static inline uint8_t +ext4_dir_dx_rinfo_get_indirect_levels(struct ext4_dir_idx_rinfo *ri) +{ + return ri->indirect_levels; +} + +/**@brief Set number of indirect levels of HTree. + * @param root_info Pointer to root info structure of index + * @param lvl Height of HTree (actually only 0 or 1) + */ +static inline void +ext4_dir_dx_rinfo_set_indirect_levels(struct ext4_dir_idx_rinfo *ri, uint8_t l) +{ + ri->indirect_levels = l; +} + +/**@brief Get maximum number of index node entries. + * @param climit Pointer to counlimit structure + * @return Maximum of entries in node + */ +static inline uint16_t +ext4_dir_dx_climit_get_limit(struct ext4_dir_idx_climit *climit) +{ + return to_le16(climit->limit); +} + +/**@brief Set maximum number of index node entries. + * @param climit Pointer to counlimit structure + * @param limit Maximum of entries in node + */ +static inline void +ext4_dir_dx_climit_set_limit(struct ext4_dir_idx_climit *climit, uint16_t limit) +{ + climit->limit = to_le16(limit); +} + +/**@brief Get current number of index node entries. + * @param climit Pointer to counlimit structure + * @return Number of entries in node + */ +static inline uint16_t +ext4_dir_dx_climit_get_count(struct ext4_dir_idx_climit *climit) +{ + return to_le16(climit->count); +} + +/**@brief Set current number of index node entries. + * @param climit Pointer to counlimit structure + * @param count Number of entries in node + */ +static inline void +ext4_dir_dx_climit_set_count(struct ext4_dir_idx_climit *climit, uint16_t count) +{ + climit->count = to_le16(count); +} + +/**@brief Get hash value of index entry. + * @param entry Pointer to index entry + * @return Hash value + */ +static inline uint32_t +ext4_dir_dx_entry_get_hash(struct ext4_dir_idx_entry *entry) +{ + return to_le32(entry->hash); +} + +/**@brief Set hash value of index entry. + * @param entry Pointer to index entry + * @param hash Hash value + */ +static inline void +ext4_dir_dx_entry_set_hash(struct ext4_dir_idx_entry *entry, uint32_t hash) +{ + entry->hash = to_le32(hash); +} + +/**@brief Get block address where child node is located. + * @param entry Pointer to index entry + * @return Block address of child node + */ +static inline uint32_t +ext4_dir_dx_entry_get_block(struct ext4_dir_idx_entry *entry) +{ + return to_le32(entry->block); +} + +/**@brief Set block address where child node is located. + * @param entry Pointer to index entry + * @param block Block address of child node + */ +static inline void +ext4_dir_dx_entry_set_block(struct ext4_dir_idx_entry *entry, uint32_t block) +{ + entry->block = to_le32(block); +} + +/**@brief Sort entry item.*/ +struct ext4_dx_sort_entry { + uint32_t hash; + uint32_t rec_len; + void *dentry; +}; + +static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len, + const char *name) +{ + return ext2_htree_hash(name, len, hinfo->seed, hinfo->hash_version, + &hinfo->hash, &hinfo->minor_hash); +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_dir_dx_checksum(struct ext4_inode_ref *inode_ref, void *de, + int count_offset, int count, + struct ext4_dir_idx_tail *t) +{ + uint32_t orig_cum, csum = 0; + struct ext4_sblock *sb = &inode_ref->fs->sb; + int sz; + + /* Compute the checksum only if the filesystem supports it */ + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t ino_index = to_le32(inode_ref->index); + uint32_t ino_gen; + ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode)); + + sz = count_offset + (count * sizeof(struct ext4_dir_idx_tail)); + orig_cum = t->checksum; + t->checksum = 0; + /* First calculate crc32 checksum against fs uuid */ + csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); + /* Then calculate crc32 checksum against inode number + * and inode generation */ + csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index)); + csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen)); + /* After that calculate crc32 checksum against all the dx_entry */ + csum = ext4_crc32c(csum, de, sz); + /* Finally calculate crc32 checksum for dx_tail */ + csum = ext4_crc32c(csum, t, sizeof(struct ext4_dir_idx_tail)); + t->checksum = orig_cum; + } + return csum; +} + +static struct ext4_dir_idx_climit * +ext4_dir_dx_get_climit(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *dirent, int *offset) +{ + struct ext4_dir_en *dp; + struct ext4_dir_idx_root *root; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint32_t block_size = ext4_sb_get_block_size(sb); + uint16_t entry_len = ext4_dir_en_get_entry_len(dirent); + int count_offset; + + + if (entry_len == 12) { + root = (struct ext4_dir_idx_root *)dirent; + dp = (struct ext4_dir_en *)&root->dots[1]; + if (ext4_dir_en_get_entry_len(dp) != (block_size - 12)) + return NULL; + if (root->info.reserved_zero) + return NULL; + if (root->info.info_length != sizeof(struct ext4_dir_idx_rinfo)) + return NULL; + count_offset = 32; + } else if (entry_len == block_size) { + count_offset = 8; + } else { + return NULL; + } + + if (offset) + *offset = count_offset; + return (struct ext4_dir_idx_climit *)(((char *)dirent) + count_offset); +} + +/* + * BIG FAT NOTES: + * Currently we do not verify the checksum of HTree node. + */ +static bool ext4_dir_dx_csum_verify(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *de) +{ + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint32_t block_size = ext4_sb_get_block_size(sb); + int coff, limit, cnt; + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + struct ext4_dir_idx_climit *climit; + climit = ext4_dir_dx_get_climit(inode_ref, de, &coff); + if (!climit) { + /* Directory seems corrupted. */ + return true; + } + struct ext4_dir_idx_tail *t; + limit = ext4_dir_dx_climit_get_limit(climit); + cnt = ext4_dir_dx_climit_get_count(climit); + if (coff + (limit * sizeof(struct ext4_dir_idx_entry)) > + (block_size - sizeof(struct ext4_dir_idx_tail))) { + /* There is no space to hold the checksum */ + return true; + } + t = (void *)(((struct ext4_dir_idx_entry *)climit) + limit); + + uint32_t c; + c = to_le32(ext4_dir_dx_checksum(inode_ref, de, coff, cnt, t)); + if (t->checksum != c) + return false; + } + return true; +} + + +static void ext4_dir_set_dx_csum(struct ext4_inode_ref *inode_ref, + struct ext4_dir_en *dirent) +{ + int coff, limit, count; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint32_t block_size = ext4_sb_get_block_size(sb); + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + struct ext4_dir_idx_climit *climit; + climit = ext4_dir_dx_get_climit(inode_ref, dirent, &coff); + if (!climit) { + /* Directory seems corrupted. */ + return; + } + struct ext4_dir_idx_tail *t; + limit = ext4_dir_dx_climit_get_limit(climit); + count = ext4_dir_dx_climit_get_count(climit); + if (coff + (limit * sizeof(struct ext4_dir_idx_entry)) > + (block_size - sizeof(struct ext4_dir_idx_tail))) { + /* There is no space to hold the checksum */ + return; + } + + t = (void *)(((struct ext4_dir_idx_entry *)climit) + limit); + t->checksum = to_le32(ext4_dir_dx_checksum(inode_ref, dirent, + coff, count, t)); + } +} +#else +#define ext4_dir_dx_csum_verify(...) true +#define ext4_dir_set_dx_csum(...) +#endif + +/****************************************************************************/ + +int ext4_dir_dx_init(struct ext4_inode_ref *dir, struct ext4_inode_ref *parent) +{ + /* Load block 0, where will be index root located */ + ext4_fsblk_t fblock; + uint32_t iblock = 0; + bool need_append = + (ext4_inode_get_size(&dir->fs->sb, dir->inode) + < EXT4_DIR_DX_INIT_BCNT) + ? true : false; + struct ext4_sblock *sb = &dir->fs->sb; + uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb); + struct ext4_block block; + + int rc; + + if (!need_append) + rc = ext4_fs_init_inode_dblk_idx(dir, iblock, &fblock); + else + rc = ext4_fs_append_inode_dblk(dir, &fblock, &iblock); + + if (rc != EOK) + return rc; + + rc = ext4_trans_block_get_noread(dir->fs->bdev, &block, fblock); + if (rc != EOK) + return rc; + + /* Initialize pointers to data structures */ + struct ext4_dir_idx_root *root = (void *)block.data; + struct ext4_dir_idx_rinfo *info = &(root->info); + + memset(root, 0, sizeof(struct ext4_dir_idx_root)); + struct ext4_dir_en *de; + + /* Initialize dot entries */ + de = (struct ext4_dir_en *)root->dots; + ext4_dir_write_entry(sb, de, 12, dir, ".", strlen(".")); + + de = (struct ext4_dir_en *)(root->dots + 1); + uint16_t elen = block_size - 12; + ext4_dir_write_entry(sb, de, elen, parent, "..", strlen("..")); + + /* Initialize root info structure */ + uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version); + + ext4_dir_dx_rinfo_set_hash_version(info, hash_version); + ext4_dir_dx_rinfo_set_indirect_levels(info, 0); + ext4_dir_dx_root_info_set_info_length(info, 8); + + /* Set limit and current number of entries */ + struct ext4_dir_idx_climit *climit; + climit = (struct ext4_dir_idx_climit *)&root->en; + + ext4_dir_dx_climit_set_count(climit, 1); + + uint32_t entry_space; + entry_space = block_size - 2 * sizeof(struct ext4_dir_idx_dot_en) - + sizeof(struct ext4_dir_idx_rinfo); + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + + uint16_t root_limit = entry_space / sizeof(struct ext4_dir_idx_entry); + ext4_dir_dx_climit_set_limit(climit, root_limit); + + /* Append new block, where will be new entries inserted in the future */ + iblock++; + if (!need_append) + rc = ext4_fs_init_inode_dblk_idx(dir, iblock, &fblock); + else + rc = ext4_fs_append_inode_dblk(dir, &fblock, &iblock); + + if (rc != EOK) { + ext4_block_set(dir->fs->bdev, &block); + return rc; + } + + struct ext4_block new_block; + rc = ext4_trans_block_get_noread(dir->fs->bdev, &new_block, fblock); + if (rc != EOK) { + ext4_block_set(dir->fs->bdev, &block); + return rc; + } + + /* Fill the whole block with empty entry */ + struct ext4_dir_en *be = (void *)new_block.data; + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint16_t len = block_size - sizeof(struct ext4_dir_entry_tail); + ext4_dir_en_set_entry_len(be, len); + ext4_dir_en_set_name_len(sb, be, 0); + ext4_dir_en_set_inode_type(sb, be, EXT4_DE_UNKNOWN); + ext4_dir_init_entry_tail(EXT4_DIRENT_TAIL(be, block_size)); + ext4_dir_set_csum(dir, be); + } else { + ext4_dir_en_set_entry_len(be, block_size); + } + + ext4_dir_en_set_inode(be, 0); + + ext4_trans_set_block_dirty(new_block.buf); + rc = ext4_block_set(dir->fs->bdev, &new_block); + if (rc != EOK) { + ext4_block_set(dir->fs->bdev, &block); + return rc; + } + + /* Connect new block to the only entry in index */ + struct ext4_dir_idx_entry *entry = root->en; + ext4_dir_dx_entry_set_block(entry, iblock); + + ext4_dir_set_dx_csum(dir, (struct ext4_dir_en *)block.data); + ext4_trans_set_block_dirty(block.buf); + + return ext4_block_set(dir->fs->bdev, &block); +} + +/**@brief Initialize hash info structure necessary for index operations. + * @param hinfo Pointer to hinfo to be initialized + * @param root_block Root block (number 0) of index + * @param sb Pointer to superblock + * @param name_len Length of name to be computed hash value from + * @param name Name to be computed hash value from + * @return Standard error code + */ +static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo, + struct ext4_block *root_block, + struct ext4_sblock *sb, size_t name_len, + const char *name) +{ + struct ext4_dir_idx_root *root; + + root = (struct ext4_dir_idx_root *)root_block->data; + if ((root->info.hash_version != EXT2_HTREE_LEGACY) && + (root->info.hash_version != EXT2_HTREE_HALF_MD4) && + (root->info.hash_version != EXT2_HTREE_TEA)) + return EXT4_ERR_BAD_DX_DIR; + + /* Check unused flags */ + if (root->info.unused_flags != 0) + return EXT4_ERR_BAD_DX_DIR; + + /* Check indirect levels */ + if (root->info.indirect_levels > 1) + return EXT4_ERR_BAD_DX_DIR; + + /* Check if node limit is correct */ + uint32_t block_size = ext4_sb_get_block_size(sb); + uint32_t entry_space = block_size; + entry_space -= 2 * sizeof(struct ext4_dir_idx_dot_en); + entry_space -= sizeof(struct ext4_dir_idx_rinfo); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + entry_space = entry_space / sizeof(struct ext4_dir_idx_entry); + + struct ext4_dir_idx_climit *climit = (void *)&root->en; + uint16_t limit = ext4_dir_dx_climit_get_limit(climit); + if (limit != entry_space) + return EXT4_ERR_BAD_DX_DIR; + + /* Check hash version and modify if necessary */ + hinfo->hash_version = ext4_dir_dx_rinfo_get_hash_version(&root->info); + if ((hinfo->hash_version <= EXT2_HTREE_TEA) && + (ext4_sb_check_flag(sb, EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH))) { + /* Use unsigned hash */ + hinfo->hash_version += 3; + } + + /* Load hash seed from superblock */ + hinfo->seed = ext4_get8(sb, hash_seed); + + /* Compute hash value of name */ + if (name) + return ext4_dir_dx_hash_string(hinfo, name_len, name); + + return EOK; +} + +/**@brief Walk through index tree and load leaf with corresponding hash value. + * @param hinfo Initialized hash info structure + * @param inode_ref Current i-node + * @param root_block Root block (iblock 0), where is root node located + * @param dx_block Pointer to leaf node in dx_blocks array + * @param dx_blocks Array with the whole path from root to leaf + * @return Standard error code + */ +static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo, + struct ext4_inode_ref *inode_ref, + struct ext4_block *root_block, + struct ext4_dir_idx_block **dx_block, + struct ext4_dir_idx_block *dx_blocks) +{ + struct ext4_dir_idx_root *root; + struct ext4_dir_idx_entry *entries; + struct ext4_dir_idx_entry *p; + struct ext4_dir_idx_entry *q; + struct ext4_dir_idx_entry *m; + struct ext4_dir_idx_entry *at; + ext4_fsblk_t fblk; + uint32_t block_size; + uint16_t limit; + uint16_t entry_space; + uint8_t ind_level; + int r; + + struct ext4_dir_idx_block *tmp_dx_blk = dx_blocks; + struct ext4_block *tmp_blk = root_block; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + block_size = ext4_sb_get_block_size(sb); + root = (struct ext4_dir_idx_root *)root_block->data; + entries = (struct ext4_dir_idx_entry *)&root->en; + limit = ext4_dir_dx_climit_get_limit((void *)entries); + ind_level = ext4_dir_dx_rinfo_get_indirect_levels(&root->info); + + /* Walk through the index tree */ + while (true) { + uint16_t cnt = ext4_dir_dx_climit_get_count((void *)entries); + if ((cnt == 0) || (cnt > limit)) + return EXT4_ERR_BAD_DX_DIR; + + /* Do binary search in every node */ + p = entries + 1; + q = entries + cnt - 1; + + while (p <= q) { + m = p + (q - p) / 2; + if (ext4_dir_dx_entry_get_hash(m) > hinfo->hash) + q = m - 1; + else + p = m + 1; + } + + at = p - 1; + + /* Write results */ + memcpy(&tmp_dx_blk->b, tmp_blk, sizeof(struct ext4_block)); + tmp_dx_blk->entries = entries; + tmp_dx_blk->position = at; + + /* Is algorithm in the leaf? */ + if (ind_level == 0) { + *dx_block = tmp_dx_blk; + return EOK; + } + + /* Goto child node */ + uint32_t n_blk = ext4_dir_dx_entry_get_block(at); + + ind_level--; + + r = ext4_fs_get_inode_dblk_idx(inode_ref, n_blk, &fblk, false); + if (r != EOK) + return r; + + r = ext4_trans_block_get(inode_ref->fs->bdev, tmp_blk, fblk); + if (r != EOK) + return r; + + entries = ((struct ext4_dir_idx_node *)tmp_blk->data)->entries; + limit = ext4_dir_dx_climit_get_limit((void *)entries); + + entry_space = block_size - sizeof(struct ext4_fake_dir_entry); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + + entry_space = entry_space / sizeof(struct ext4_dir_idx_entry); + + if (limit != entry_space) { + ext4_block_set(inode_ref->fs->bdev, tmp_blk); + return EXT4_ERR_BAD_DX_DIR; + } + + if (!ext4_dir_dx_csum_verify(inode_ref, (void *)tmp_blk->data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + n_blk); + } + + ++tmp_dx_blk; + } + + /* Unreachable */ + return EOK; +} + +/**@brief Check if the the next block would be checked during entry search. + * @param inode_ref Directory i-node + * @param hash Hash value to check + * @param dx_block Current block + * @param dx_blocks Array with path from root to leaf node + * @return Standard Error code + */ +static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref, + uint32_t hash, + struct ext4_dir_idx_block *dx_block, + struct ext4_dir_idx_block *dx_blocks) +{ + int r; + uint32_t num_handles = 0; + ext4_fsblk_t blk_adr; + struct ext4_dir_idx_block *p = dx_block; + + /* Try to find data block with next bunch of entries */ + while (true) { + uint16_t cnt = ext4_dir_dx_climit_get_count((void *)p->entries); + + p->position++; + if (p->position < p->entries + cnt) + break; + + if (p == dx_blocks) + return EOK; + + num_handles++; + p--; + } + + /* Check hash collision (if not occurred - no next block cannot be + * used)*/ + uint32_t current_hash = ext4_dir_dx_entry_get_hash(p->position); + if ((hash & 1) == 0) { + if ((current_hash & ~1) != hash) + return 0; + } + + /* Fill new path */ + while (num_handles--) { + uint32_t blk = ext4_dir_dx_entry_get_block(p->position); + r = ext4_fs_get_inode_dblk_idx(inode_ref, blk, &blk_adr, false); + if (r != EOK) + return r; + + struct ext4_block b; + r = ext4_trans_block_get(inode_ref->fs->bdev, &b, blk_adr); + if (r != EOK) + return r; + + if (!ext4_dir_dx_csum_verify(inode_ref, (void *)b.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + blk); + } + + p++; + + /* Don't forget to put old block (prevent memory leak) */ + r = ext4_block_set(inode_ref->fs->bdev, &p->b); + if (r != EOK) + return r; + + memcpy(&p->b, &b, sizeof(b)); + p->entries = ((struct ext4_dir_idx_node *)b.data)->entries; + p->position = p->entries; + } + + return ENOENT; +} + +int ext4_dir_dx_find_entry(struct ext4_dir_search_result *result, + struct ext4_inode_ref *inode_ref, size_t name_len, + const char *name) +{ + /* Load direct block 0 (index root) */ + ext4_fsblk_t root_block_addr; + int rc2; + int rc; + rc = ext4_fs_get_inode_dblk_idx(inode_ref, 0, &root_block_addr, false); + if (rc != EOK) + return rc; + + struct ext4_fs *fs = inode_ref->fs; + + struct ext4_block root_block; + rc = ext4_trans_block_get(fs->bdev, &root_block, root_block_addr); + if (rc != EOK) + return rc; + + if (!ext4_dir_dx_csum_verify(inode_ref, (void *)root_block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree root checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + (uint32_t)0); + } + + /* Initialize hash info (compute hash value) */ + struct ext4_hash_info hinfo; + rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name); + if (rc != EOK) { + ext4_block_set(fs->bdev, &root_block); + return EXT4_ERR_BAD_DX_DIR; + } + + /* + * Hardcoded number 2 means maximum height of index tree, + * specified in the Linux driver. + */ + struct ext4_dir_idx_block dx_blocks[2]; + struct ext4_dir_idx_block *dx_block; + struct ext4_dir_idx_block *tmp; + + rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block, &dx_block, + dx_blocks); + if (rc != EOK) { + ext4_block_set(fs->bdev, &root_block); + return EXT4_ERR_BAD_DX_DIR; + } + + do { + /* Load leaf block */ + uint32_t leaf_blk_idx; + ext4_fsblk_t leaf_block_addr; + struct ext4_block b; + + leaf_blk_idx = ext4_dir_dx_entry_get_block(dx_block->position); + rc = ext4_fs_get_inode_dblk_idx(inode_ref, leaf_blk_idx, + &leaf_block_addr, false); + if (rc != EOK) + goto cleanup; + + rc = ext4_trans_block_get(fs->bdev, &b, leaf_block_addr); + if (rc != EOK) + goto cleanup; + + if (!ext4_dir_csum_verify(inode_ref, (void *)b.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree leaf block checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + leaf_blk_idx); + } + + /* Linear search inside block */ + struct ext4_dir_en *de; + rc = ext4_dir_find_in_block(&b, &fs->sb, name_len, name, &de); + + /* Found => return it */ + if (rc == EOK) { + result->block = b; + result->dentry = de; + goto cleanup; + } + + /* Not found, leave untouched */ + rc2 = ext4_block_set(fs->bdev, &b); + if (rc2 != EOK) + goto cleanup; + + if (rc != ENOENT) + goto cleanup; + + /* check if the next block could be checked */ + rc = ext4_dir_dx_next_block(inode_ref, hinfo.hash, dx_block, + &dx_blocks[0]); + if (rc < 0) + goto cleanup; + } while (rc == ENOENT); + + /* Entry not found */ + rc = ENOENT; + +cleanup: + /* The whole path must be released (preventing memory leak) */ + tmp = dx_blocks; + + while (tmp <= dx_block) { + rc2 = ext4_block_set(fs->bdev, &tmp->b); + if (rc == EOK && rc2 != EOK) + rc = rc2; + ++tmp; + } + + return rc; +} + +/**@brief Compare function used to pass in quicksort implementation. + * It can compare two entries by hash value. + * @param arg1 First entry + * @param arg2 Second entry + * @param dummy Unused parameter, can be NULL + * + * @return Classic compare result + * (0: equal, -1: arg1 < arg2, 1: arg1 > arg2) + */ +static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2) +{ + struct ext4_dx_sort_entry *entry1 = (void *)arg1; + struct ext4_dx_sort_entry *entry2 = (void *)arg2; + + if (entry1->hash == entry2->hash) + return 0; + + if (entry1->hash < entry2->hash) + return -1; + else + return 1; +} + +/**@brief Insert new index entry to block. + * Note that space for new entry must be checked by caller. + * @param inode_ref Directory i-node + * @param index_block Block where to insert new entry + * @param hash Hash value covered by child node + * @param iblock Logical number of child block + * + */ +static void +ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref __unused, + struct ext4_dir_idx_block *index_block, + uint32_t hash, uint32_t iblock) +{ + struct ext4_dir_idx_entry *old_index_entry = index_block->position; + struct ext4_dir_idx_entry *new_index_entry = old_index_entry + 1; + struct ext4_dir_idx_climit *climit = (void *)index_block->entries; + struct ext4_dir_idx_entry *start_index = index_block->entries; + uint32_t count = ext4_dir_dx_climit_get_count(climit); + + size_t bytes; + bytes = (uint8_t *)(start_index + count) - (uint8_t *)(new_index_entry); + + memmove(new_index_entry + 1, new_index_entry, bytes); + + ext4_dir_dx_entry_set_block(new_index_entry, iblock); + ext4_dir_dx_entry_set_hash(new_index_entry, hash); + ext4_dir_dx_climit_set_count(climit, count + 1); + ext4_dir_set_dx_csum(inode_ref, (void *)index_block->b.data); + ext4_trans_set_block_dirty(index_block->b.buf); +} + +/**@brief Split directory entries to two parts preventing node overflow. + * @param inode_ref Directory i-node + * @param hinfo Hash info + * @param old_data_block Block with data to be split + * @param index_block Block where index entries are located + * @param new_data_block Output value for newly allocated data block + */ +static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, + struct ext4_hash_info *hinfo, + struct ext4_block *old_data_block, + struct ext4_dir_idx_block *index_block, + struct ext4_block *new_data_block) +{ + int rc = EOK; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); + + /* Allocate buffer for directory entries */ + uint8_t *entry_buffer = ext4_malloc(block_size); + if (entry_buffer == NULL) + return ENOMEM; + + /* dot entry has the smallest size available */ + uint32_t max_ecnt = block_size / sizeof(struct ext4_dir_idx_dot_en); + + /* Allocate sort entry */ + struct ext4_dx_sort_entry *sort; + + sort = ext4_malloc(max_ecnt * sizeof(struct ext4_dx_sort_entry)); + if (sort == NULL) { + ext4_free(entry_buffer); + return ENOMEM; + } + + uint32_t idx = 0; + uint32_t real_size = 0; + + /* Initialize hinfo */ + struct ext4_hash_info hinfo_tmp; + memcpy(&hinfo_tmp, hinfo, sizeof(struct ext4_hash_info)); + + /* Load all valid entries to the buffer */ + struct ext4_dir_en *de = (void *)old_data_block->data; + uint8_t *entry_buffer_ptr = entry_buffer; + while ((void *)de < (void *)(old_data_block->data + block_size)) { + /* Read only valid entries */ + if (ext4_dir_en_get_inode(de) && de->name_len) { + uint16_t len = ext4_dir_en_get_name_len(sb, de); + rc = ext4_dir_dx_hash_string(&hinfo_tmp, len, + (char *)de->name); + if (rc != EOK) { + ext4_free(sort); + ext4_free(entry_buffer); + return rc; + } + + uint32_t rec_len = 8 + len; + if ((rec_len % 4) != 0) + rec_len += 4 - (rec_len % 4); + + memcpy(entry_buffer_ptr, de, rec_len); + + sort[idx].dentry = entry_buffer_ptr; + sort[idx].rec_len = rec_len; + sort[idx].hash = hinfo_tmp.hash; + + entry_buffer_ptr += rec_len; + real_size += rec_len; + idx++; + } + + size_t elen = ext4_dir_en_get_entry_len(de); + de = (void *)((uint8_t *)de + elen); + } + + qsort(sort, idx, sizeof(struct ext4_dx_sort_entry), + ext4_dir_dx_entry_comparator); + + /* Allocate new block for store the second part of entries */ + ext4_fsblk_t new_fblock; + uint32_t new_iblock; + rc = ext4_fs_append_inode_dblk(inode_ref, &new_fblock, &new_iblock); + if (rc != EOK) { + ext4_free(sort); + ext4_free(entry_buffer); + return rc; + } + + /* Load new block */ + struct ext4_block new_data_block_tmp; + rc = ext4_trans_block_get_noread(inode_ref->fs->bdev, &new_data_block_tmp, + new_fblock); + if (rc != EOK) { + ext4_free(sort); + ext4_free(entry_buffer); + return rc; + } + + /* + * Distribute entries to two blocks (by size) + * - compute the half + */ + uint32_t new_hash = 0; + uint32_t current_size = 0; + uint32_t mid = 0; + uint32_t i; + for (i = 0; i < idx; ++i) { + if ((current_size + sort[i].rec_len) > (block_size / 2)) { + new_hash = sort[i].hash; + mid = i; + break; + } + + current_size += sort[i].rec_len; + } + + /* Check hash collision */ + uint32_t continued = 0; + if (new_hash == sort[mid - 1].hash) + continued = 1; + + uint32_t off = 0; + void *ptr; + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + block_size -= sizeof(struct ext4_dir_entry_tail); + + /* First part - to the old block */ + for (i = 0; i < mid; ++i) { + ptr = old_data_block->data + off; + memcpy(ptr, sort[i].dentry, sort[i].rec_len); + + struct ext4_dir_en *t = ptr; + if (i < (mid - 1)) + ext4_dir_en_set_entry_len(t, sort[i].rec_len); + else + ext4_dir_en_set_entry_len(t, block_size - off); + + off += sort[i].rec_len; + } + + /* Second part - to the new block */ + off = 0; + for (i = mid; i < idx; ++i) { + ptr = new_data_block_tmp.data + off; + memcpy(ptr, sort[i].dentry, sort[i].rec_len); + + struct ext4_dir_en *t = ptr; + if (i < (idx - 1)) + ext4_dir_en_set_entry_len(t, sort[i].rec_len); + else + ext4_dir_en_set_entry_len(t, block_size - off); + + off += sort[i].rec_len; + } + + block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); + + /* Do some steps to finish operation */ + sb = &inode_ref->fs->sb; + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + struct ext4_dir_entry_tail *t; + + t = EXT4_DIRENT_TAIL(old_data_block->data, block_size); + ext4_dir_init_entry_tail(t); + t = EXT4_DIRENT_TAIL(new_data_block_tmp.data, block_size); + ext4_dir_init_entry_tail(t); + } + ext4_dir_set_csum(inode_ref, (void *)old_data_block->data); + ext4_dir_set_csum(inode_ref, (void *)new_data_block_tmp.data); + ext4_trans_set_block_dirty(old_data_block->buf); + ext4_trans_set_block_dirty(new_data_block_tmp.buf); + + ext4_free(sort); + ext4_free(entry_buffer); + + ext4_dir_dx_insert_entry(inode_ref, index_block, new_hash + continued, + new_iblock); + + *new_data_block = new_data_block_tmp; + return EOK; +} + +/**@brief Split index node and maybe some parent nodes in the tree hierarchy. + * @param inode_ref Directory i-node + * @param dx_blocks Array with path from root to leaf node + * @param dx_block Leaf block to be split if needed + * @return Error code + */ +static int +ext4_dir_dx_split_index(struct ext4_inode_ref *ino_ref, + struct ext4_dir_idx_block *dx_blks, + struct ext4_dir_idx_block *dxb, + struct ext4_dir_idx_block **new_dx_block) +{ + struct ext4_sblock *sb = &ino_ref->fs->sb; + struct ext4_dir_idx_entry *e; + int r; + + uint32_t block_size = ext4_sb_get_block_size(&ino_ref->fs->sb); + uint32_t entry_space = block_size - sizeof(struct ext4_fake_dir_entry); + uint32_t node_limit = entry_space / sizeof(struct ext4_dir_idx_entry); + + bool meta_csum = ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM); + + if (dxb == dx_blks) + e = ((struct ext4_dir_idx_root *)dxb->b.data)->en; + else + e = ((struct ext4_dir_idx_node *)dxb->b.data)->entries; + + struct ext4_dir_idx_climit *climit = (struct ext4_dir_idx_climit *)e; + + uint16_t leaf_limit = ext4_dir_dx_climit_get_limit(climit); + uint16_t leaf_count = ext4_dir_dx_climit_get_count(climit); + + /* Check if is necessary to split index block */ + if (leaf_limit == leaf_count) { + struct ext4_dir_idx_entry *ren; + ptrdiff_t levels = dxb - dx_blks; + + ren = ((struct ext4_dir_idx_root *)dx_blks[0].b.data)->en; + struct ext4_dir_idx_climit *rclimit = (void *)ren; + uint16_t root_limit = ext4_dir_dx_climit_get_limit(rclimit); + uint16_t root_count = ext4_dir_dx_climit_get_count(rclimit); + + + /* Linux limitation */ + if ((levels > 0) && (root_limit == root_count)) + return ENOSPC; + + /* Add new block to directory */ + ext4_fsblk_t new_fblk; + uint32_t new_iblk; + r = ext4_fs_append_inode_dblk(ino_ref, &new_fblk, &new_iblk); + if (r != EOK) + return r; + + /* load new block */ + struct ext4_block b; + r = ext4_trans_block_get_noread(ino_ref->fs->bdev, &b, new_fblk); + if (r != EOK) + return r; + + struct ext4_dir_idx_node *new_node = (void *)b.data; + struct ext4_dir_idx_entry *new_en = new_node->entries; + + memset(&new_node->fake, 0, sizeof(struct ext4_fake_dir_entry)); + new_node->fake.entry_length = block_size; + + /* Split leaf node */ + if (levels > 0) { + uint32_t count_left = leaf_count / 2; + uint32_t count_right = leaf_count - count_left; + uint32_t hash_right; + size_t sz; + + struct ext4_dir_idx_climit *left_climit; + struct ext4_dir_idx_climit *right_climit; + + hash_right = ext4_dir_dx_entry_get_hash(e + count_left); + /* Copy data to new node */ + sz = count_right * sizeof(struct ext4_dir_idx_entry); + memcpy(new_en, e + count_left, sz); + + /* Initialize new node */ + left_climit = (struct ext4_dir_idx_climit *)e; + right_climit = (struct ext4_dir_idx_climit *)new_en; + + ext4_dir_dx_climit_set_count(left_climit, count_left); + ext4_dir_dx_climit_set_count(right_climit, count_right); + + if (meta_csum) + entry_space -= sizeof(struct ext4_dir_idx_tail); + + ext4_dir_dx_climit_set_limit(right_climit, node_limit); + + /* Which index block is target for new entry */ + uint32_t position_index = + (dxb->position - dxb->entries); + if (position_index >= count_left) { + ext4_dir_set_dx_csum( + ino_ref, + (struct ext4_dir_en *) + dxb->b.data); + ext4_trans_set_block_dirty(dxb->b.buf); + + struct ext4_block block_tmp = dxb->b; + + dxb->b = b; + + dxb->position = + new_en + position_index - count_left; + dxb->entries = new_en; + + b = block_tmp; + } + + /* Finally insert new entry */ + ext4_dir_dx_insert_entry(ino_ref, dx_blks, hash_right, + new_iblk); + ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[0].b.data); + ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[1].b.data); + ext4_trans_set_block_dirty(dx_blks[0].b.buf); + ext4_trans_set_block_dirty(dx_blks[1].b.buf); + + ext4_dir_set_dx_csum(ino_ref, (void *)b.data); + ext4_trans_set_block_dirty(b.buf); + return ext4_block_set(ino_ref->fs->bdev, &b); + } else { + size_t sz; + /* Copy data from root to child block */ + sz = leaf_count * sizeof(struct ext4_dir_idx_entry); + memcpy(new_en, e, sz); + + struct ext4_dir_idx_climit *new_climit = (void*)new_en; + if (meta_csum) + entry_space -= sizeof(struct ext4_dir_idx_tail); + + ext4_dir_dx_climit_set_limit(new_climit, node_limit); + + /* Set values in root node */ + struct ext4_dir_idx_climit *new_root_climit = (void *)e; + + ext4_dir_dx_climit_set_count(new_root_climit, 1); + ext4_dir_dx_entry_set_block(e, new_iblk); + + struct ext4_dir_idx_root *r = (void *)dx_blks[0].b.data; + r->info.indirect_levels = 1; + + /* Add new entry to the path */ + dxb = dx_blks + 1; + dxb->position = dx_blks->position - e + new_en; + dxb->entries = new_en; + dxb->b = b; + *new_dx_block = dxb; + + ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[0].b.data); + ext4_dir_set_dx_csum(ino_ref, (void*)dx_blks[1].b.data); + ext4_trans_set_block_dirty(dx_blks[0].b.buf); + ext4_trans_set_block_dirty(dx_blks[1].b.buf); + } + } + + return EOK; +} + +int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, + struct ext4_inode_ref *child, const char *name, uint32_t name_len) +{ + int rc2 = EOK; + int r; + /* Get direct block 0 (index root) */ + ext4_fsblk_t rblock_addr; + r = ext4_fs_get_inode_dblk_idx(parent, 0, &rblock_addr, false); + if (r != EOK) + return r; + + struct ext4_fs *fs = parent->fs; + struct ext4_block root_blk; + + r = ext4_trans_block_get(fs->bdev, &root_blk, rblock_addr); + if (r != EOK) + return r; + + if (!ext4_dir_dx_csum_verify(parent, (void*)root_blk.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree root checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + parent->index, + (uint32_t)0); + } + + /* Initialize hinfo structure (mainly compute hash) */ + struct ext4_hash_info hinfo; + r = ext4_dir_hinfo_init(&hinfo, &root_blk, &fs->sb, name_len, name); + if (r != EOK) { + ext4_block_set(fs->bdev, &root_blk); + return EXT4_ERR_BAD_DX_DIR; + } + + /* + * Hardcoded number 2 means maximum height of index + * tree defined in Linux. + */ + struct ext4_dir_idx_block dx_blks[2]; + struct ext4_dir_idx_block *dx_blk; + struct ext4_dir_idx_block *dx_it; + + r = ext4_dir_dx_get_leaf(&hinfo, parent, &root_blk, &dx_blk, dx_blks); + if (r != EOK) { + r = EXT4_ERR_BAD_DX_DIR; + goto release_index; + } + + /* Try to insert to existing data block */ + uint32_t leaf_block_idx = ext4_dir_dx_entry_get_block(dx_blk->position); + ext4_fsblk_t leaf_block_addr; + r = ext4_fs_get_inode_dblk_idx(parent, leaf_block_idx, + &leaf_block_addr, false); + if (r != EOK) + goto release_index; + + /* + * Check if there is needed to split index node + * (and recursively also parent nodes) + */ + r = ext4_dir_dx_split_index(parent, dx_blks, dx_blk, &dx_blk); + if (r != EOK) + goto release_target_index; + + struct ext4_block target_block; + r = ext4_trans_block_get(fs->bdev, &target_block, leaf_block_addr); + if (r != EOK) + goto release_index; + + if (!ext4_dir_csum_verify(parent,(void *)target_block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree leaf block checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + parent->index, + leaf_block_idx); + } + + /* Check if insert operation passed */ + r = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, + name, name_len); + if (r == EOK) + goto release_target_index; + + /* Split entries to two blocks (includes sorting by hash value) */ + struct ext4_block new_block; + r = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_blk, + &new_block); + if (r != EOK) { + rc2 = r; + goto release_target_index; + } + + /* Where to save new entry */ + uint32_t blk_hash = ext4_dir_dx_entry_get_hash(dx_blk->position + 1); + if (hinfo.hash >= blk_hash) + r = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block, + child, name, name_len); + else + r = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, + child, name, name_len); + + /* Cleanup */ + r = ext4_block_set(fs->bdev, &new_block); + if (r != EOK) + return r; + +/* Cleanup operations */ + +release_target_index: + rc2 = r; + + r = ext4_block_set(fs->bdev, &target_block); + if (r != EOK) + return r; + +release_index: + if (r != EOK) + rc2 = r; + + dx_it = dx_blks; + + while (dx_it <= dx_blk) { + r = ext4_block_set(fs->bdev, &dx_it->b); + if (r != EOK) + return r; + + dx_it++; + } + + return rc2; +} + +int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir, + uint32_t parent_inode) +{ + /* Load block 0, where will be index root located */ + ext4_fsblk_t fblock; + int rc = ext4_fs_get_inode_dblk_idx(dir, 0, &fblock, false); + if (rc != EOK) + return rc; + + struct ext4_block block; + rc = ext4_trans_block_get(dir->fs->bdev, &block, fblock); + if (rc != EOK) + return rc; + + if (!ext4_dir_dx_csum_verify(dir, (void *)block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree root checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + dir->index, + (uint32_t)0); + } + + /* Initialize pointers to data structures */ + struct ext4_dir_idx_root *root = (void *)block.data; + + /* Fill the inode field with a new parent ino. */ + ext4_dx_dot_en_set_inode(&root->dots[1], parent_inode); + + ext4_dir_set_dx_csum(dir, (void *)block.data); + ext4_trans_set_block_dirty(block.buf); + + return ext4_block_set(dir->fs->bdev, &block); +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_extent.c b/Library/lwext4/src/ext4_extent.c new file mode 100644 index 0000000..abac59b --- /dev/null +++ b/Library/lwext4/src/ext4_extent.c @@ -0,0 +1,2140 @@ +/* + * Copyright (c) 2017 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * Copyright (c) 2017 Kaho Ng (ngkaho1234@gmail.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if CONFIG_EXTENTS_ENABLE +/* + * used by extent splitting. + */ +#define EXT4_EXT_MARK_UNWRIT1 0x02 /* mark first half unwritten */ +#define EXT4_EXT_MARK_UNWRIT2 0x04 /* mark second half unwritten */ +#define EXT4_EXT_DATA_VALID1 0x08 /* first half contains valid data */ +#define EXT4_EXT_DATA_VALID2 0x10 /* second half contains valid data */ +#define EXT4_EXT_NO_COMBINE 0x20 /* do not combine two extents */ + +#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))) + +/* + * 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; +}; + + +#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) + + +#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__) \ + (to_le16((__path__)->header->entries_count) < \ + to_le16((__path__)->header->max_entries_count)) +#define EXT_LAST_EXTENT(__hdr__) \ + (EXT_FIRST_EXTENT((__hdr__)) + to_le16((__hdr__)->entries_count) - 1) +#define EXT_LAST_INDEX(__hdr__) \ + (EXT_FIRST_INDEX((__hdr__)) + to_le16((__hdr__)->entries_count) - 1) +#define EXT_MAX_EXTENT(__hdr__) \ + (EXT_FIRST_EXTENT((__hdr__)) + to_le16((__hdr__)->max_entries_count) - 1) +#define EXT_MAX_INDEX(__hdr__) \ + (EXT_FIRST_INDEX((__hdr__)) + to_le16((__hdr__)->max_entries_count) - 1) + +#define EXT4_EXTENT_TAIL_OFFSET(hdr) \ + (sizeof(struct ext4_extent_header) + \ + (sizeof(struct ext4_extent) * to_le16((hdr)->max_entries_count))) + + +/**@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); +} + +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; +} + + +static struct ext4_extent_tail * +find_ext4_extent_tail(struct ext4_extent_header *eh) +{ + return (struct ext4_extent_tail *)(((char *)eh) + + EXT4_EXTENT_TAIL_OFFSET(eh)); +} + +static struct ext4_extent_header *ext_inode_hdr(struct ext4_inode *inode) +{ + return (struct ext4_extent_header *)inode->blocks; +} + +static struct ext4_extent_header *ext_block_hdr(struct ext4_block *block) +{ + return (struct ext4_extent_header *)block->data; +} + +static uint16_t ext_depth(struct ext4_inode *inode) +{ + return to_le16(ext_inode_hdr(inode)->depth); +} + +static uint16_t ext4_ext_get_actual_len(struct ext4_extent *ext) +{ + return (to_le16(ext->block_count) <= EXT_INIT_MAX_LEN + ? to_le16(ext->block_count) + : (to_le16(ext->block_count) - EXT_INIT_MAX_LEN)); +} + +static void ext4_ext_mark_initialized(struct ext4_extent *ext) +{ + ext->block_count = to_le16(ext4_ext_get_actual_len(ext)); +} + +static void ext4_ext_mark_unwritten(struct ext4_extent *ext) +{ + ext->block_count |= to_le16(EXT_INIT_MAX_LEN); +} + +static int ext4_ext_is_unwritten(struct ext4_extent *ext) +{ + /* Extent with ee_len of 0x8000 is treated as an initialized extent */ + return (to_le16(ext->block_count) > EXT_INIT_MAX_LEN); +} + +/* + * ext4_ext_pblock: + * combine low and high parts of physical block number into ext4_fsblk_t + */ +static ext4_fsblk_t ext4_ext_pblock(struct ext4_extent *ex) +{ + ext4_fsblk_t block; + + block = to_le32(ex->start_lo); + block |= ((ext4_fsblk_t)to_le16(ex->start_hi) << 31) << 1; + return block; +} + +/* + * ext4_idx_pblock: + * combine low and high parts of a leaf physical block number into ext4_fsblk_t + */ +static ext4_fsblk_t ext4_idx_pblock(struct ext4_extent_index *ix) +{ + ext4_fsblk_t block; + + block = to_le32(ix->leaf_lo); + block |= ((ext4_fsblk_t)to_le16(ix->leaf_hi) << 31) << 1; + return block; +} + +/* + * ext4_ext_store_pblock: + * stores a large physical block number into an extent struct, + * breaking it into parts + */ +static void ext4_ext_store_pblock(struct ext4_extent *ex, ext4_fsblk_t pb) +{ + ex->start_lo = to_le32((uint32_t)(pb & 0xffffffff)); + ex->start_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff); +} + +/* + * ext4_idx_store_pblock: + * stores a large physical block number into an index struct, + * breaking it into parts + */ +static void ext4_idx_store_pblock(struct ext4_extent_index *ix, ext4_fsblk_t pb) +{ + ix->leaf_lo = to_le32((uint32_t)(pb & 0xffffffff)); + ix->leaf_hi = to_le16((uint16_t)((pb >> 32)) & 0xffff); +} + +static int ext4_allocate_single_block(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t goal, ext4_fsblk_t *blockp) +{ + return ext4_balloc_alloc_block(inode_ref, goal, blockp); +} + +static ext4_fsblk_t ext4_new_meta_blocks(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t goal, + uint32_t flags __unused, + uint32_t *count, int *errp) +{ + ext4_fsblk_t block = 0; + + *errp = ext4_allocate_single_block(inode_ref, goal, &block); + if (count) + *count = 1; + return block; +} + +static void ext4_ext_free_blocks(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t block, uint32_t count, + uint32_t flags __unused) +{ + ext4_balloc_free_blocks(inode_ref, block, count); +} + +static uint16_t ext4_ext_space_block(struct ext4_inode_ref *inode_ref) +{ + uint16_t size; + uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); + + size = (block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent); +#ifdef AGGRESSIVE_TEST + if (size > 6) + size = 6; +#endif + return size; +} + +static uint16_t ext4_ext_space_block_idx(struct ext4_inode_ref *inode_ref) +{ + uint16_t size; + uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); + + size = (block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent_index); +#ifdef AGGRESSIVE_TEST + if (size > 5) + size = 5; +#endif + return size; +} + +static uint16_t ext4_ext_space_root(struct ext4_inode_ref *inode_ref) +{ + uint16_t size; + + size = sizeof(inode_ref->inode->blocks); + size -= sizeof(struct ext4_extent_header); + size /= sizeof(struct ext4_extent); +#ifdef AGGRESSIVE_TEST + if (size > 3) + size = 3; +#endif + return size; +} + +static uint16_t ext4_ext_space_root_idx(struct ext4_inode_ref *inode_ref) +{ + uint16_t size; + + size = sizeof(inode_ref->inode->blocks); + size -= sizeof(struct ext4_extent_header); + size /= sizeof(struct ext4_extent_index); +#ifdef AGGRESSIVE_TEST + if (size > 4) + size = 4; +#endif + return size; +} + +static uint16_t ext4_ext_max_entries(struct ext4_inode_ref *inode_ref, + uint32_t depth) +{ + uint16_t max; + + if (depth == ext_depth(inode_ref->inode)) { + if (depth == 0) + max = ext4_ext_space_root(inode_ref); + else + max = ext4_ext_space_root_idx(inode_ref); + } else { + if (depth == 0) + max = ext4_ext_space_block(inode_ref); + else + max = ext4_ext_space_block_idx(inode_ref); + } + + return max; +} + +static ext4_fsblk_t ext4_ext_find_goal(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, + ext4_lblk_t block) +{ + if (path) { + uint32_t depth = path->depth; + struct ext4_extent *ex; + + /* + * Try to predict block placement assuming that we are + * filling in a file which will eventually be + * non-sparse --- i.e., in the case of libbfd writing + * an ELF object sections out-of-order but in a way + * the eventually results in a contiguous object or + * executable file, or some database extending a table + * space file. However, this is actually somewhat + * non-ideal if we are writing a sparse file such as + * qemu or KVM writing a raw image file that is going + * to stay fairly sparse, since it will end up + * fragmenting the file system's free space. Maybe we + * should have some hueristics or some way to allow + * userspace to pass a hint to file system, + * especially if the latter case turns out to be + * common. + */ + ex = path[depth].extent; + if (ex) { + ext4_fsblk_t ext_pblk = ext4_ext_pblock(ex); + ext4_lblk_t ext_block = to_le32(ex->first_block); + + if (block > ext_block) + return ext_pblk + (block - ext_block); + else + return ext_pblk - (ext_block - block); + } + + /* it looks like index is empty; + * try to find starting block from index itself */ + if (path[depth].block.lb_id) + return path[depth].block.lb_id; + } + + /* OK. use inode's group */ + return ext4_fs_inode_to_goal_block(inode_ref); +} + +/* + * Allocation for a meta data block + */ +static ext4_fsblk_t ext4_ext_new_meta_block(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, + struct ext4_extent *ex, int *err, + uint32_t flags) +{ + ext4_fsblk_t goal, newblock; + + goal = ext4_ext_find_goal(inode_ref, path, to_le32(ex->first_block)); + newblock = ext4_new_meta_blocks(inode_ref, goal, flags, NULL, err); + return newblock; +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_ext_block_csum(struct ext4_inode_ref *inode_ref, + struct ext4_extent_header *eh) +{ + uint32_t checksum = 0; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t ino_index = to_le32(inode_ref->index); + uint32_t ino_gen = + to_le32(ext4_inode_get_generation(inode_ref->inode)); + /* First calculate crc32 checksum against fs uuid */ + checksum = + ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); + /* Then calculate crc32 checksum against inode number + * and inode generation */ + checksum = ext4_crc32c(checksum, &ino_index, sizeof(ino_index)); + checksum = ext4_crc32c(checksum, &ino_gen, sizeof(ino_gen)); + /* Finally calculate crc32 checksum against + * the entire extent block up to the checksum field */ + checksum = + ext4_crc32c(checksum, eh, EXT4_EXTENT_TAIL_OFFSET(eh)); + } + return checksum; +} +#else +#define ext4_ext_block_csum(...) 0 +#endif + +static void +ext4_extent_block_csum_set(struct ext4_inode_ref *inode_ref __unused, + struct ext4_extent_header *eh) +{ + struct ext4_extent_tail *tail; + + tail = find_ext4_extent_tail(eh); + tail->et_checksum = to_le32(ext4_ext_block_csum(inode_ref, eh)); +} + +static int ext4_ext_dirty(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path) +{ + if (path->block.lb_id) + ext4_trans_set_block_dirty(path->block.buf); + else + inode_ref->dirty = true; + + return EOK; +} + +static void ext4_ext_drop_refs(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, bool keep_other) +{ + int32_t depth, i; + + if (!path) + return; + if (keep_other) + depth = 0; + else + depth = path->depth; + + for (i = 0; i <= depth; i++, path++) { + if (path->block.lb_id) { + if (ext4_bcache_test_flag(path->block.buf, BC_DIRTY)) + ext4_extent_block_csum_set(inode_ref, + path->header); + + ext4_block_set(inode_ref->fs->bdev, &path->block); + } + } +} + +/* + * Check that whether the basic information inside the extent header + * is correct or not. + */ +static int ext4_ext_check(struct ext4_inode_ref *inode_ref, + struct ext4_extent_header *eh, uint16_t depth, + ext4_fsblk_t pblk __unused) +{ + struct ext4_extent_tail *tail; + struct ext4_sblock *sb = &inode_ref->fs->sb; + const char *error_msg; + (void)error_msg; + + if (to_le16(eh->magic) != EXT4_EXTENT_MAGIC) { + error_msg = "invalid magic"; + goto corrupted; + } + if (to_le16(eh->depth) != depth) { + error_msg = "unexpected eh_depth"; + goto corrupted; + } + if (eh->max_entries_count == 0) { + error_msg = "invalid eh_max"; + goto corrupted; + } + if (to_le16(eh->entries_count) > to_le16(eh->max_entries_count)) { + error_msg = "invalid eh_entries"; + goto corrupted; + } + + tail = find_ext4_extent_tail(eh); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + if (tail->et_checksum != + to_le32(ext4_ext_block_csum(inode_ref, eh))) { + ext4_dbg(DEBUG_EXTENT, + DBG_WARN "Extent block checksum failed." + "Blocknr: %" PRIu64 "\n", + pblk); + } + } + + return EOK; + +corrupted: + ext4_dbg(DEBUG_EXTENT, "Bad extents B+ tree block: %s. " + "Blocknr: %" PRId64 "\n", + error_msg, pblk); + return EIO; +} + +static int read_extent_tree_block(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t pblk, int32_t depth, + struct ext4_block *bh, + uint32_t flags __unused) +{ + int err; + + err = ext4_trans_block_get(inode_ref->fs->bdev, bh, pblk); + if (err != EOK) + goto errout; + + err = ext4_ext_check(inode_ref, ext_block_hdr(bh), depth, pblk); + if (err != EOK) + goto errout; + + return EOK; +errout: + if (bh->lb_id) + ext4_block_set(inode_ref->fs->bdev, bh); + + return err; +} + +/* + * ext4_ext_binsearch_idx: + * binary search for the closest index of the given block + * the header must be checked before calling this + */ +static void ext4_ext_binsearch_idx(struct ext4_extent_path *path, + ext4_lblk_t block) +{ + struct ext4_extent_header *eh = path->header; + struct ext4_extent_index *r, *l, *m; + + l = EXT_FIRST_INDEX(eh) + 1; + r = EXT_LAST_INDEX(eh); + while (l <= r) { + m = l + (r - l) / 2; + if (block < to_le32(m->first_block)) + r = m - 1; + else + l = m + 1; + } + + path->index = l - 1; +} + +/* + * ext4_ext_binsearch: + * binary search for closest extent of the given block + * the header must be checked before calling this + */ +static void ext4_ext_binsearch(struct ext4_extent_path *path, ext4_lblk_t block) +{ + struct ext4_extent_header *eh = path->header; + struct ext4_extent *r, *l, *m; + + if (eh->entries_count == 0) { + /* + * this leaf is empty: + * we get such a leaf in split/add case + */ + return; + } + + l = EXT_FIRST_EXTENT(eh) + 1; + r = EXT_LAST_EXTENT(eh); + + while (l <= r) { + m = l + (r - l) / 2; + if (block < to_le32(m->first_block)) + r = m - 1; + else + l = m + 1; + } + + path->extent = l - 1; +} + +static int ext4_find_extent(struct ext4_inode_ref *inode_ref, ext4_lblk_t block, + struct ext4_extent_path **orig_path, uint32_t flags) +{ + struct ext4_extent_header *eh; + struct ext4_block bh = EXT4_BLOCK_ZERO(); + ext4_fsblk_t buf_block = 0; + struct ext4_extent_path *path = *orig_path; + int32_t depth, ppos = 0; + int32_t i; + int ret; + + eh = ext_inode_hdr(inode_ref->inode); + depth = ext_depth(inode_ref->inode); + + if (path) { + ext4_ext_drop_refs(inode_ref, path, 0); + if (depth > path[0].maxdepth) { + ext4_free(path); + *orig_path = path = NULL; + } + } + if (!path) { + int32_t path_depth = depth + 1; + /* account possible depth increase */ + path = ext4_calloc(1, sizeof(struct ext4_extent_path) * + (path_depth + 1)); + if (!path) + return ENOMEM; + path[0].maxdepth = path_depth; + } + path[0].header = eh; + path[0].block = bh; + + i = depth; + /* walk through the tree */ + while (i) { + ext4_ext_binsearch_idx(path + ppos, block); + path[ppos].p_block = ext4_idx_pblock(path[ppos].index); + path[ppos].depth = i; + path[ppos].extent = NULL; + buf_block = path[ppos].p_block; + + i--; + ppos++; + if (!path[ppos].block.lb_id || + path[ppos].block.lb_id != buf_block) { + ret = read_extent_tree_block(inode_ref, buf_block, i, + &bh, flags); + if (ret != EOK) { + goto err; + } + if (ppos > depth) { + ext4_block_set(inode_ref->fs->bdev, &bh); + ret = EIO; + goto err; + } + + eh = ext_block_hdr(&bh); + path[ppos].block = bh; + path[ppos].header = eh; + } + } + + path[ppos].depth = i; + path[ppos].extent = NULL; + path[ppos].index = NULL; + + /* find extent */ + ext4_ext_binsearch(path + ppos, block); + /* if not an empty leaf */ + if (path[ppos].extent) + path[ppos].p_block = ext4_ext_pblock(path[ppos].extent); + + *orig_path = path; + + ret = EOK; + return ret; + +err: + ext4_ext_drop_refs(inode_ref, path, 0); + ext4_free(path); + if (orig_path) + *orig_path = NULL; + return ret; +} + +static void ext4_ext_init_header(struct ext4_inode_ref *inode_ref, + struct ext4_extent_header *eh, int32_t depth) +{ + eh->entries_count = 0; + eh->max_entries_count = to_le16(ext4_ext_max_entries(inode_ref, depth)); + eh->magic = to_le16(EXT4_EXTENT_MAGIC); + eh->depth = depth; +} + +static int ext4_ext_insert_index(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, int at, + ext4_lblk_t insert_index, + ext4_fsblk_t insert_block, bool set_to_ix) +{ + struct ext4_extent_index *ix; + struct ext4_extent_path *curp = path + at; + int len, err; + struct ext4_extent_header *eh; + + if (curp->index && insert_index == to_le32(curp->index->first_block)) + return EIO; + + if (to_le16(curp->header->entries_count) == + to_le16(curp->header->max_entries_count)) + return EIO; + + eh = curp->header; + if (curp->index == NULL) { + ix = EXT_FIRST_INDEX(eh); + curp->index = ix; + } else if (insert_index > to_le32(curp->index->first_block)) { + /* insert after */ + ix = curp->index + 1; + } else { + /* insert before */ + ix = curp->index; + } + + if (ix > EXT_MAX_INDEX(eh)) + return EIO; + + len = EXT_LAST_INDEX(eh) - ix + 1; + ext4_assert(len >= 0); + if (len > 0) + memmove(ix + 1, ix, len * sizeof(struct ext4_extent_index)); + + ix->first_block = to_le32(insert_index); + ext4_idx_store_pblock(ix, insert_block); + eh->entries_count = to_le16(to_le16(eh->entries_count) + 1); + + if (ix > EXT_LAST_INDEX(eh)) { + err = EIO; + goto out; + } + + err = ext4_ext_dirty(inode_ref, curp); + +out: + if (err == EOK && set_to_ix) { + curp->index = ix; + curp->p_block = ext4_idx_pblock(ix); + } + return err; +} + +static int ext4_ext_split_node(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, int at, + struct ext4_extent *newext, + struct ext4_extent_path *npath, + bool *ins_right_leaf) +{ + int i, npath_at, ret; + ext4_lblk_t insert_index; + ext4_fsblk_t newblock = 0; + int depth = ext_depth(inode_ref->inode); + npath_at = depth - at; + + ext4_assert(at > 0); + + if (path[depth].extent != EXT_MAX_EXTENT(path[depth].header)) + insert_index = path[depth].extent[1].first_block; + else + insert_index = newext->first_block; + + for (i = depth; i >= at; i--, npath_at--) { + struct ext4_block bh = EXT4_BLOCK_ZERO(); + + /* FIXME: currently we split at the point after the current + * extent. */ + newblock = + ext4_ext_new_meta_block(inode_ref, path, newext, &ret, 0); + if (ret != EOK) + goto cleanup; + + /* For write access.*/ + ret = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, + newblock); + if (ret != EOK) + goto cleanup; + + if (i == depth) { + /* start copy from next extent */ + int m = EXT_MAX_EXTENT(path[i].header) - path[i].extent; + struct ext4_extent_header *neh; + struct ext4_extent *ex; + neh = ext_block_hdr(&bh); + ex = EXT_FIRST_EXTENT(neh); + ext4_ext_init_header(inode_ref, neh, 0); + if (m) { + memmove(ex, path[i].extent + 1, + sizeof(struct ext4_extent) * m); + neh->entries_count = + to_le16(to_le16(neh->entries_count) + m); + path[i].header->entries_count = to_le16( + to_le16(path[i].header->entries_count) - m); + ret = ext4_ext_dirty(inode_ref, path + i); + if (ret != EOK) + goto cleanup; + + npath[npath_at].p_block = ext4_ext_pblock(ex); + npath[npath_at].extent = ex; + } else { + npath[npath_at].p_block = 0; + npath[npath_at].extent = NULL; + } + + npath[npath_at].depth = to_le16(neh->depth); + npath[npath_at].maxdepth = 0; + npath[npath_at].index = NULL; + npath[npath_at].header = neh; + npath[npath_at].block = bh; + + ext4_trans_set_block_dirty(bh.buf); + } else { + int m = EXT_MAX_INDEX(path[i].header) - path[i].index; + struct ext4_extent_header *neh; + struct ext4_extent_index *ix; + neh = ext_block_hdr(&bh); + ix = EXT_FIRST_INDEX(neh); + ext4_ext_init_header(inode_ref, neh, depth - i); + ix->first_block = to_le32(insert_index); + ext4_idx_store_pblock(ix, + npath[npath_at + 1].block.lb_id); + neh->entries_count = to_le16(1); + if (m) { + memmove(ix + 1, path[i].index + 1, + sizeof(struct ext4_extent) * m); + neh->entries_count = + to_le16(to_le16(neh->entries_count) + m); + path[i].header->entries_count = to_le16( + to_le16(path[i].header->entries_count) - m); + ret = ext4_ext_dirty(inode_ref, path + i); + if (ret != EOK) + goto cleanup; + } + + npath[npath_at].p_block = ext4_idx_pblock(ix); + npath[npath_at].depth = to_le16(neh->depth); + npath[npath_at].maxdepth = 0; + npath[npath_at].extent = NULL; + npath[npath_at].index = ix; + npath[npath_at].header = neh; + npath[npath_at].block = bh; + + ext4_trans_set_block_dirty(bh.buf); + } + } + newblock = 0; + + /* + * If newext->first_block can be included into the + * right sub-tree. + */ + if (to_le32(newext->first_block) < insert_index) + *ins_right_leaf = false; + else + *ins_right_leaf = true; + + ret = ext4_ext_insert_index(inode_ref, path, at - 1, insert_index, + npath[0].block.lb_id, *ins_right_leaf); + +cleanup: + if (ret != EOK) { + if (newblock) + ext4_ext_free_blocks(inode_ref, newblock, 1, 0); + + npath_at = depth - at; + while (npath_at >= 0) { + if (npath[npath_at].block.lb_id) { + newblock = npath[npath_at].block.lb_id; + ext4_block_set(inode_ref->fs->bdev, + &npath[npath_at].block); + ext4_ext_free_blocks(inode_ref, newblock, 1, 0); + memset(&npath[npath_at].block, 0, + sizeof(struct ext4_block)); + } + npath_at--; + } + } + return ret; +} + +/* + * ext4_ext_correct_indexes: + * if leaf gets modified and modified extent is first in the leaf, + * then we have to correct all indexes above. + */ +static int ext4_ext_correct_indexes(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path) +{ + struct ext4_extent_header *eh; + int32_t depth = ext_depth(inode_ref->inode); + struct ext4_extent *ex; + uint32_t border; + int32_t k; + int err = EOK; + + eh = path[depth].header; + ex = path[depth].extent; + + if (ex == NULL || eh == NULL) + return EIO; + + if (depth == 0) { + /* there is no tree at all */ + return EOK; + } + + if (ex != EXT_FIRST_EXTENT(eh)) { + /* we correct tree if first leaf got modified only */ + return EOK; + } + + k = depth - 1; + border = path[depth].extent->first_block; + path[k].index->first_block = border; + err = ext4_ext_dirty(inode_ref, path + k); + if (err != EOK) + return err; + + while (k--) { + /* change all left-side indexes */ + if (path[k + 1].index != EXT_FIRST_INDEX(path[k + 1].header)) + break; + path[k].index->first_block = border; + err = ext4_ext_dirty(inode_ref, path + k); + if (err != EOK) + break; + } + + return err; +} + +static inline bool ext4_ext_can_prepend(struct ext4_extent *ex1, + struct ext4_extent *ex2) +{ + if (ext4_ext_pblock(ex2) + ext4_ext_get_actual_len(ex2) != + ext4_ext_pblock(ex1)) + return 0; + +#ifdef AGGRESSIVE_TEST + if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4) + return 0; +#else + if (ext4_ext_is_unwritten(ex1)) { + if (ext4_ext_get_actual_len(ex1) + + ext4_ext_get_actual_len(ex2) > + EXT_UNWRITTEN_MAX_LEN) + return 0; + } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > + EXT_INIT_MAX_LEN) + return 0; +#endif + + if (to_le32(ex2->first_block) + ext4_ext_get_actual_len(ex2) != + to_le32(ex1->first_block)) + return 0; + + return 1; +} + +static inline bool ext4_ext_can_append(struct ext4_extent *ex1, + struct ext4_extent *ex2) +{ + if (ext4_ext_pblock(ex1) + ext4_ext_get_actual_len(ex1) != + ext4_ext_pblock(ex2)) + return 0; + +#ifdef AGGRESSIVE_TEST + if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > 4) + return 0; +#else + if (ext4_ext_is_unwritten(ex1)) { + if (ext4_ext_get_actual_len(ex1) + + ext4_ext_get_actual_len(ex2) > + EXT_UNWRITTEN_MAX_LEN) + return 0; + } else if (ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > + EXT_INIT_MAX_LEN) + return 0; +#endif + + if (to_le32(ex1->first_block) + ext4_ext_get_actual_len(ex1) != + to_le32(ex2->first_block)) + return 0; + + return 1; +} + +static int ext4_ext_insert_leaf(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, int at, + struct ext4_extent *newext, int flags, + bool *need_split) +{ + struct ext4_extent_path *curp = path + at; + struct ext4_extent *ex = curp->extent; + int len, err, unwritten; + struct ext4_extent_header *eh; + + *need_split = false; + + if (curp->extent && + to_le32(newext->first_block) == to_le32(curp->extent->first_block)) + return EIO; + + if (!(flags & EXT4_EXT_NO_COMBINE)) { + if (curp->extent && ext4_ext_can_append(curp->extent, newext)) { + unwritten = ext4_ext_is_unwritten(curp->extent); + curp->extent->block_count = + to_le16(ext4_ext_get_actual_len(curp->extent) + + ext4_ext_get_actual_len(newext)); + if (unwritten) + ext4_ext_mark_unwritten(curp->extent); + + err = ext4_ext_dirty(inode_ref, curp); + goto out; + } + + if (curp->extent && + ext4_ext_can_prepend(curp->extent, newext)) { + unwritten = ext4_ext_is_unwritten(curp->extent); + curp->extent->first_block = newext->first_block; + curp->extent->block_count = + to_le16(ext4_ext_get_actual_len(curp->extent) + + ext4_ext_get_actual_len(newext)); + if (unwritten) + ext4_ext_mark_unwritten(curp->extent); + + err = ext4_ext_dirty(inode_ref, curp); + goto out; + } + } + + if (to_le16(curp->header->entries_count) == + to_le16(curp->header->max_entries_count)) { + err = EIO; + *need_split = true; + goto out; + } else { + eh = curp->header; + if (curp->extent == NULL) { + ex = EXT_FIRST_EXTENT(eh); + curp->extent = ex; + } else if (to_le32(newext->first_block) > + to_le32(curp->extent->first_block)) { + /* insert after */ + ex = curp->extent + 1; + } else { + /* insert before */ + ex = curp->extent; + } + } + + len = EXT_LAST_EXTENT(eh) - ex + 1; + ext4_assert(len >= 0); + if (len > 0) + memmove(ex + 1, ex, len * sizeof(struct ext4_extent)); + + if (ex > EXT_MAX_EXTENT(eh)) { + err = EIO; + goto out; + } + + ex->first_block = newext->first_block; + ex->block_count = newext->block_count; + ext4_ext_store_pblock(ex, ext4_ext_pblock(newext)); + eh->entries_count = to_le16(to_le16(eh->entries_count) + 1); + + if (ex > EXT_LAST_EXTENT(eh)) { + err = EIO; + goto out; + } + + err = ext4_ext_correct_indexes(inode_ref, path); + if (err != EOK) + goto out; + err = ext4_ext_dirty(inode_ref, curp); + +out: + if (err == EOK) { + curp->extent = ex; + curp->p_block = ext4_ext_pblock(ex); + } + + return err; +} + +/* + * ext4_ext_grow_indepth: + * implements tree growing procedure: + * - allocates new block + * - moves top-level data (index block or leaf) into the new block + * - initializes new top-level, creating index that points to the + * just created block + */ +static int ext4_ext_grow_indepth(struct ext4_inode_ref *inode_ref, + uint32_t flags) +{ + struct ext4_extent_header *neh; + struct ext4_block bh = EXT4_BLOCK_ZERO(); + ext4_fsblk_t newblock, goal = 0; + int err = EOK; + + /* Try to prepend new index to old one */ + if (ext_depth(inode_ref->inode)) + goal = ext4_idx_pblock( + EXT_FIRST_INDEX(ext_inode_hdr(inode_ref->inode))); + else + goal = ext4_fs_inode_to_goal_block(inode_ref); + + newblock = ext4_new_meta_blocks(inode_ref, goal, flags, NULL, &err); + if (newblock == 0) + return err; + + /* # */ + err = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, newblock); + if (err != EOK) { + ext4_ext_free_blocks(inode_ref, newblock, 1, 0); + return err; + } + + /* move top-level index/leaf into new block */ + memmove(bh.data, inode_ref->inode->blocks, + sizeof(inode_ref->inode->blocks)); + + /* set size of new block */ + neh = ext_block_hdr(&bh); + /* old root could have indexes or leaves + * so calculate e_max right way */ + if (ext_depth(inode_ref->inode)) + neh->max_entries_count = + to_le16(ext4_ext_space_block_idx(inode_ref)); + else + neh->max_entries_count = + to_le16(ext4_ext_space_block(inode_ref)); + + neh->magic = to_le16(EXT4_EXTENT_MAGIC); + ext4_extent_block_csum_set(inode_ref, neh); + + /* Update top-level index: num,max,pointer */ + neh = ext_inode_hdr(inode_ref->inode); + neh->entries_count = to_le16(1); + ext4_idx_store_pblock(EXT_FIRST_INDEX(neh), newblock); + if (neh->depth == 0) { + /* Root extent block becomes index block */ + neh->max_entries_count = + to_le16(ext4_ext_space_root_idx(inode_ref)); + EXT_FIRST_INDEX(neh) + ->first_block = EXT_FIRST_EXTENT(neh)->first_block; + } + neh->depth = to_le16(to_le16(neh->depth) + 1); + + ext4_trans_set_block_dirty(bh.buf); + inode_ref->dirty = true; + ext4_block_set(inode_ref->fs->bdev, &bh); + + return err; +} + +static inline void ext4_ext_replace_path(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, + struct ext4_extent_path *newpath, + int at) +{ + ext4_ext_drop_refs(inode_ref, path + at, 1); + path[at] = *newpath; + memset(newpath, 0, sizeof(struct ext4_extent_path)); +} + +int ext4_ext_insert_extent(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path **ppath, + struct ext4_extent *newext, int flags) +{ + int depth, level = 0, ret = 0; + struct ext4_extent_path *path = *ppath; + struct ext4_extent_path *npath = NULL; + bool ins_right_leaf = false; + bool need_split; + +again: + depth = ext_depth(inode_ref->inode); + ret = ext4_ext_insert_leaf(inode_ref, path, depth, newext, flags, + &need_split); + if (ret == EIO && need_split == true) { + int i; + for (i = depth, level = 0; i >= 0; i--, level++) + if (EXT_HAS_FREE_INDEX(path + i)) + break; + + /* Do we need to grow the tree? */ + if (i < 0) { + ret = ext4_ext_grow_indepth(inode_ref, 0); + if (ret != EOK) + goto out; + + ret = ext4_find_extent( + inode_ref, to_le32(newext->first_block), ppath, 0); + if (ret != EOK) + goto out; + + path = *ppath; + /* + * After growing the tree, there should be free space in + * the only child node of the root. + */ + level--; + depth++; + } + + i = depth - (level - 1); + /* We split from leaf to the i-th node */ + if (level > 0) { + npath = ext4_calloc(1, sizeof(struct ext4_extent_path) * + (level)); + if (!npath) { + ret = ENOMEM; + goto out; + } + ret = ext4_ext_split_node(inode_ref, path, i, newext, + npath, &ins_right_leaf); + if (ret != EOK) + goto out; + + while (--level >= 0) { + if (ins_right_leaf) + ext4_ext_replace_path(inode_ref, path, + &npath[level], + i + level); + else if (npath[level].block.lb_id) + ext4_ext_drop_refs(inode_ref, + npath + level, 1); + } + } + goto again; + } + +out: + if (ret != EOK) { + if (path) + ext4_ext_drop_refs(inode_ref, path, 0); + + while (--level >= 0 && npath) { + if (npath[level].block.lb_id) { + ext4_fsblk_t block = npath[level].block.lb_id; + ext4_ext_free_blocks(inode_ref, block, 1, 0); + ext4_ext_drop_refs(inode_ref, npath + level, 1); + } + } + } + if (npath) + ext4_free(npath); + + return ret; +} + +static void ext4_ext_remove_blocks(struct ext4_inode_ref *inode_ref, + struct ext4_extent *ex, ext4_lblk_t from, + ext4_lblk_t to) +{ + ext4_lblk_t len = to - from + 1; + ext4_lblk_t num; + ext4_fsblk_t start; + num = from - to_le32(ex->first_block); + start = ext4_ext_pblock(ex) + num; + ext4_dbg(DEBUG_EXTENT, + "Freeing %" PRIu32 " at %" PRIu64 ", %" PRIu32 "\n", from, + start, len); + + ext4_ext_free_blocks(inode_ref, start, len, 0); +} + +static int ext4_ext_remove_idx(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, int32_t depth) +{ + int err = EOK; + int32_t i = depth; + ext4_fsblk_t leaf; + + /* free index block */ + leaf = ext4_idx_pblock(path[i].index); + + if (path[i].index != EXT_LAST_INDEX(path[i].header)) { + ptrdiff_t len = EXT_LAST_INDEX(path[i].header) - path[i].index; + memmove(path[i].index, path[i].index + 1, + len * sizeof(struct ext4_extent_index)); + } + + path[i].header->entries_count = + to_le16(to_le16(path[i].header->entries_count) - 1); + err = ext4_ext_dirty(inode_ref, path + i); + if (err != EOK) + return err; + + ext4_dbg(DEBUG_EXTENT, "IDX: Freeing %" PRIu32 " at %" PRIu64 ", %d\n", + to_le32(path[i].index->first_block), leaf, 1); + ext4_ext_free_blocks(inode_ref, leaf, 1, 0); + + /* + * We may need to correct the paths after the first extents/indexes in + * a node being modified. + * + * We do not need to consider whether there's any extents presenting or + * not, as garbage will be cleared soon. + */ + while (i > 0) { + if (path[i].index != EXT_FIRST_INDEX(path[i].header)) + break; + + path[i - 1].index->first_block = path[i].index->first_block; + err = ext4_ext_dirty(inode_ref, path + i - 1); + if (err != EOK) + break; + + i--; + } + return err; +} + +static int ext4_ext_remove_leaf(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path *path, ext4_lblk_t from, + ext4_lblk_t to) +{ + + int32_t depth = ext_depth(inode_ref->inode); + struct ext4_extent *ex = path[depth].extent; + struct ext4_extent *start_ex, *ex2 = NULL; + struct ext4_extent_header *eh = path[depth].header; + int32_t len; + int err = EOK; + uint16_t new_entries; + + start_ex = ex; + new_entries = to_le16(eh->entries_count); + while (ex <= EXT_LAST_EXTENT(path[depth].header) && + to_le32(ex->first_block) <= to) { + int32_t new_len = 0; + int unwritten; + ext4_lblk_t start, new_start; + ext4_fsblk_t newblock; + new_start = start = to_le32(ex->first_block); + len = ext4_ext_get_actual_len(ex); + newblock = ext4_ext_pblock(ex); + /* + * The 1st case: + * The position that we start truncation is inside the range of an + * extent. Here we should calculate the new length of that extent and + * may start the removal from the next extent. + */ + if (start < from) { + len -= from - start; + new_len = from - start; + start = from; + start_ex++; + } else { + /* + * The second case: + * The last block to be truncated is inside the range of an + * extent. We need to calculate the new length and the new + * start of the extent. + */ + if (start + len - 1 > to) { + new_len = start + len - 1 - to; + len -= new_len; + new_start = to + 1; + newblock += to + 1 - start; + ex2 = ex; + } + } + + ext4_ext_remove_blocks(inode_ref, ex, start, start + len - 1); + /* + * Set the first block of the extent if it is presented. + */ + ex->first_block = to_le32(new_start); + + /* + * If the new length of the current extent we are working on is + * zero, remove it. + */ + if (!new_len) + new_entries--; + else { + unwritten = ext4_ext_is_unwritten(ex); + ex->block_count = to_le16(new_len); + ext4_ext_store_pblock(ex, newblock); + if (unwritten) + ext4_ext_mark_unwritten(ex); + } + + ex += 1; + } + + if (ex2 == NULL) + ex2 = ex; + + /* + * Move any remaining extents to the starting position of the node. + */ + if (ex2 <= EXT_LAST_EXTENT(eh)) + memmove(start_ex, ex2, (EXT_LAST_EXTENT(eh) - ex2 + 1) * + sizeof(struct ext4_extent)); + + eh->entries_count = to_le16(new_entries); + ext4_ext_dirty(inode_ref, path + depth); + + /* + * If the extent pointer is pointed to the first extent of the node, and + * there's still extents presenting, we may need to correct the indexes + * of the paths. + */ + if (path[depth].extent == EXT_FIRST_EXTENT(eh) && eh->entries_count) { + err = ext4_ext_correct_indexes(inode_ref, path); + if (err != EOK) + return err; + } + + /* if this leaf is free, then we should + * remove it from index block above */ + if (eh->entries_count == 0 && path[depth].block.lb_id) + err = ext4_ext_remove_idx(inode_ref, path, depth - 1); + else if (depth > 0) + path[depth - 1].index++; + + return err; +} + +/* + * Check if there's more to remove at a specific level. + */ +static bool ext4_ext_more_to_rm(struct ext4_extent_path *path, ext4_lblk_t to) +{ + if (!to_le16(path->header->entries_count)) + return false; + + if (path->index > EXT_LAST_INDEX(path->header)) + return false; + + if (to_le32(path->index->first_block) > to) + return false; + + return true; +} + +int ext4_extent_remove_space(struct ext4_inode_ref *inode_ref, ext4_lblk_t from, + ext4_lblk_t to) +{ + struct ext4_extent_path *path = NULL; + int ret = EOK; + int32_t depth = ext_depth(inode_ref->inode); + int32_t i; + + ret = ext4_find_extent(inode_ref, from, &path, 0); + if (ret != EOK) + goto out; + + if (!path[depth].extent) { + ret = EOK; + goto out; + } + + bool in_range = IN_RANGE(from, to_le32(path[depth].extent->first_block), + ext4_ext_get_actual_len(path[depth].extent)); + + if (!in_range) { + ret = EOK; + goto out; + } + + /* If we do remove_space inside the range of an extent */ + if ((to_le32(path[depth].extent->first_block) < from) && + (to < to_le32(path[depth].extent->first_block) + + ext4_ext_get_actual_len(path[depth].extent) - 1)) { + + struct ext4_extent *ex = path[depth].extent, newex; + int unwritten = ext4_ext_is_unwritten(ex); + ext4_lblk_t ee_block = to_le32(ex->first_block); + int32_t len = ext4_ext_get_actual_len(ex); + ext4_fsblk_t newblock = to + 1 - ee_block + ext4_ext_pblock(ex); + + ex->block_count = to_le16(from - ee_block); + if (unwritten) + ext4_ext_mark_unwritten(ex); + + ext4_ext_dirty(inode_ref, path + depth); + + newex.first_block = to_le32(to + 1); + newex.block_count = to_le16(ee_block + len - 1 - to); + ext4_ext_store_pblock(&newex, newblock); + if (unwritten) + ext4_ext_mark_unwritten(&newex); + + ret = ext4_ext_insert_extent(inode_ref, &path, &newex, 0); + goto out; + } + + i = depth; + while (i >= 0) { + if (i == depth) { + struct ext4_extent_header *eh; + struct ext4_extent *first_ex, *last_ex; + ext4_lblk_t leaf_from, leaf_to; + eh = path[i].header; + ext4_assert(to_le16(eh->entries_count) > 0); + first_ex = EXT_FIRST_EXTENT(eh); + last_ex = EXT_LAST_EXTENT(eh); + leaf_from = to_le32(first_ex->first_block); + leaf_to = to_le32(last_ex->first_block) + + ext4_ext_get_actual_len(last_ex) - 1; + if (leaf_from < from) + leaf_from = from; + + if (leaf_to > to) + leaf_to = to; + + ext4_ext_remove_leaf(inode_ref, path, leaf_from, + leaf_to); + ext4_ext_drop_refs(inode_ref, path + i, 0); + i--; + continue; + } + + struct ext4_extent_header *eh; + eh = path[i].header; + if (ext4_ext_more_to_rm(path + i, to)) { + struct ext4_block bh = EXT4_BLOCK_ZERO(); + if (path[i + 1].block.lb_id) + ext4_ext_drop_refs(inode_ref, path + i + 1, 0); + + ret = read_extent_tree_block( + inode_ref, ext4_idx_pblock(path[i].index), + depth - i - 1, &bh, 0); + if (ret != EOK) + goto out; + + path[i].p_block = ext4_idx_pblock(path[i].index); + path[i + 1].block = bh; + path[i + 1].header = ext_block_hdr(&bh); + path[i + 1].depth = depth - i - 1; + if (i + 1 == depth) + path[i + 1].extent = + EXT_FIRST_EXTENT(path[i + 1].header); + else + path[i + 1].index = + EXT_FIRST_INDEX(path[i + 1].header); + + i++; + } else { + if (i > 0) { + /* + * Garbage entries will finally be cleared here. + */ + if (!eh->entries_count) + ret = ext4_ext_remove_idx(inode_ref, + path, i - 1); + else + path[i - 1].index++; + } + + if (i) + ext4_block_set(inode_ref->fs->bdev, + &path[i].block); + + i--; + } + } + + /* TODO: flexible tree reduction should be here */ + if (path->header->entries_count == 0) { + /* + * truncate to zero freed all the tree, + * so we need to correct eh_depth + */ + ext_inode_hdr(inode_ref->inode)->depth = 0; + ext_inode_hdr(inode_ref->inode)->max_entries_count = + to_le16(ext4_ext_space_root(inode_ref)); + ret = ext4_ext_dirty(inode_ref, path); + } + +out: + ext4_ext_drop_refs(inode_ref, path, 0); + ext4_free(path); + path = NULL; + return ret; +} + +static int ext4_ext_split_extent_at(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path **ppath, + ext4_lblk_t split, uint32_t split_flag) +{ + struct ext4_extent *ex, newex; + ext4_fsblk_t newblock; + ext4_lblk_t ee_block; + int32_t ee_len; + int32_t depth = ext_depth(inode_ref->inode); + int err = EOK; + + ex = (*ppath)[depth].extent; + ee_block = to_le32(ex->first_block); + ee_len = ext4_ext_get_actual_len(ex); + newblock = split - ee_block + ext4_ext_pblock(ex); + + if (split == ee_block) { + /* + * case b: block @split is the block that the extent begins with + * then we just change the state of the extent, and splitting + * is not needed. + */ + if (split_flag & EXT4_EXT_MARK_UNWRIT2) + ext4_ext_mark_unwritten(ex); + else + ext4_ext_mark_initialized(ex); + + err = ext4_ext_dirty(inode_ref, *ppath + depth); + goto out; + } + + ex->block_count = to_le16(split - ee_block); + if (split_flag & EXT4_EXT_MARK_UNWRIT1) + ext4_ext_mark_unwritten(ex); + + err = ext4_ext_dirty(inode_ref, *ppath + depth); + if (err != EOK) + goto out; + + newex.first_block = to_le32(split); + newex.block_count = to_le16(ee_len - (split - ee_block)); + ext4_ext_store_pblock(&newex, newblock); + if (split_flag & EXT4_EXT_MARK_UNWRIT2) + ext4_ext_mark_unwritten(&newex); + err = ext4_ext_insert_extent(inode_ref, ppath, &newex, + EXT4_EXT_NO_COMBINE); + if (err != EOK) + goto restore_extent_len; + +out: + return err; +restore_extent_len: + ex->block_count = to_le16(ee_len); + err = ext4_ext_dirty(inode_ref, *ppath + depth); + return err; +} + +static int ext4_ext_convert_to_initialized(struct ext4_inode_ref *inode_ref, + struct ext4_extent_path **ppath, + ext4_lblk_t split, uint32_t blocks) +{ + int32_t depth = ext_depth(inode_ref->inode), err = EOK; + struct ext4_extent *ex = (*ppath)[depth].extent; + + ext4_assert(to_le32(ex->first_block) <= split); + + if (split + blocks == + to_le32(ex->first_block) + ext4_ext_get_actual_len(ex)) { + /* split and initialize right part */ + err = ext4_ext_split_extent_at(inode_ref, ppath, split, + EXT4_EXT_MARK_UNWRIT1); + } else if (to_le32(ex->first_block) == split) { + /* split and initialize left part */ + err = ext4_ext_split_extent_at(inode_ref, ppath, split + blocks, + EXT4_EXT_MARK_UNWRIT2); + } else { + /* split 1 extent to 3 and initialize the 2nd */ + err = ext4_ext_split_extent_at(inode_ref, ppath, split + blocks, + EXT4_EXT_MARK_UNWRIT1 | + EXT4_EXT_MARK_UNWRIT2); + if (err == EOK) { + err = ext4_ext_split_extent_at(inode_ref, ppath, split, + EXT4_EXT_MARK_UNWRIT1); + } + } + + return err; +} + +static ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_extent_path *path) +{ + int32_t depth; + + depth = path->depth; + + if (depth == 0 && path->extent == NULL) + return EXT_MAX_BLOCKS; + + while (depth >= 0) { + if (depth == path->depth) { + /* leaf */ + if (path[depth].extent && + path[depth].extent != + EXT_LAST_EXTENT(path[depth].header)) + return to_le32( + path[depth].extent[1].first_block); + } else { + /* index */ + if (path[depth].index != + EXT_LAST_INDEX(path[depth].header)) + return to_le32( + path[depth].index[1].first_block); + } + depth--; + } + + return EXT_MAX_BLOCKS; +} + +static int ext4_ext_zero_unwritten_range(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t block, + uint32_t blocks_count) +{ + int err = EOK; + uint32_t i; + uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); + for (i = 0; i < blocks_count; i++) { + struct ext4_block bh = EXT4_BLOCK_ZERO(); + err = ext4_trans_block_get_noread(inode_ref->fs->bdev, &bh, + block + i); + if (err != EOK) + break; + + memset(bh.data, 0, block_size); + ext4_trans_set_block_dirty(bh.buf); + err = ext4_block_set(inode_ref->fs->bdev, &bh); + if (err != EOK) + break; + } + return err; +} + +__unused static void print_path(struct ext4_extent_path *path) +{ + int32_t i = path->depth; + while (i >= 0) { + + ptrdiff_t a = + (path->extent) + ? (path->extent - EXT_FIRST_EXTENT(path->header)) + : 0; + ptrdiff_t b = + (path->index) + ? (path->index - EXT_FIRST_INDEX(path->header)) + : 0; + + (void)a; + (void)b; + ext4_dbg(DEBUG_EXTENT, + "depth %" PRId32 ", p_block: %" PRIu64 "," + "p_ext offset: %td, p_idx offset: %td\n", + i, path->p_block, a, b); + i--; + path++; + } +} + +int ext4_extent_get_blocks(struct ext4_inode_ref *inode_ref, ext4_lblk_t iblock, + uint32_t max_blocks, ext4_fsblk_t *result, + bool create, uint32_t *blocks_count) +{ + struct ext4_extent_path *path = NULL; + struct ext4_extent newex, *ex; + ext4_fsblk_t goal; + int err = EOK; + int32_t depth; + uint32_t allocated = 0; + ext4_lblk_t next; + ext4_fsblk_t newblock; + + if (result) + *result = 0; + + if (blocks_count) + *blocks_count = 0; + + /* find extent for this block */ + err = ext4_find_extent(inode_ref, iblock, &path, 0); + if (err != EOK) { + path = NULL; + goto out2; + } + + depth = ext_depth(inode_ref->inode); + + /* + * consistent leaf must not be empty + * this situations is possible, though, _during_ tree modification + * this is why assert can't be put in ext4_ext_find_extent() + */ + ex = path[depth].extent; + if (ex) { + ext4_lblk_t ee_block = to_le32(ex->first_block); + ext4_fsblk_t ee_start = ext4_ext_pblock(ex); + uint16_t ee_len = ext4_ext_get_actual_len(ex); + /* if found exent covers block, simple return it */ + if (IN_RANGE(iblock, ee_block, ee_len)) { + /* number of remain blocks in the extent */ + allocated = ee_len - (iblock - ee_block); + + if (!ext4_ext_is_unwritten(ex)) { + newblock = iblock - ee_block + ee_start; + goto out; + } + + if (!create) { + newblock = 0; + goto out; + } + + uint32_t zero_range; + zero_range = allocated; + if (zero_range > max_blocks) + zero_range = max_blocks; + + newblock = iblock - ee_block + ee_start; + err = ext4_ext_zero_unwritten_range(inode_ref, newblock, + zero_range); + if (err != EOK) + goto out2; + + err = ext4_ext_convert_to_initialized( + inode_ref, &path, iblock, zero_range); + if (err != EOK) + goto out2; + + goto out; + } + } + + /* + * requested block isn't allocated yet + * we couldn't try to create block if create flag is zero + */ + if (!create) { + goto out2; + } + + /* find next allocated block so that we know how many + * blocks we can allocate without ovelapping next extent */ + next = ext4_ext_next_allocated_block(path); + allocated = next - iblock; + if (allocated > max_blocks) + allocated = max_blocks; + + /* allocate new block */ + goal = ext4_ext_find_goal(inode_ref, path, iblock); + newblock = ext4_new_meta_blocks(inode_ref, goal, 0, &allocated, &err); + if (!newblock) + goto out2; + + /* try to insert new extent into found leaf and return */ + newex.first_block = to_le32(iblock); + ext4_ext_store_pblock(&newex, newblock); + newex.block_count = to_le16(allocated); + err = ext4_ext_insert_extent(inode_ref, &path, &newex, 0); + if (err != EOK) { + /* free data blocks we just allocated */ + ext4_ext_free_blocks(inode_ref, ext4_ext_pblock(&newex), + to_le16(newex.block_count), 0); + goto out2; + } + + /* previous routine could use block we allocated */ + newblock = ext4_ext_pblock(&newex); + +out: + if (allocated > max_blocks) + allocated = max_blocks; + + if (result) + *result = newblock; + + if (blocks_count) + *blocks_count = allocated; + +out2: + if (path) { + ext4_ext_drop_refs(inode_ref, path, 0); + ext4_free(path); + } + + return err; +} +#endif diff --git a/Library/lwext4/src/ext4_fs.c b/Library/lwext4/src/ext4_fs.c new file mode 100644 index 0000000..8e229ef --- /dev/null +++ b/Library/lwext4/src/ext4_fs.c @@ -0,0 +1,1751 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_fs.c + * @brief More complex filesystem functions. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev, + bool read_only) +{ + int r, i; + uint16_t tmp; + uint32_t bsize; + + ext4_assert(fs && bdev); + + fs->bdev = bdev; + + fs->read_only = read_only; + + r = ext4_sb_read(fs->bdev, &fs->sb); + if (r != EOK) + return r; + + if (!ext4_sb_check(&fs->sb)) + return ENOTSUP; + + bsize = ext4_sb_get_block_size(&fs->sb); + if (bsize > EXT4_MAX_BLOCK_SIZE) + return ENXIO; + + r = ext4_fs_check_features(fs, &read_only); + if (r != EOK) + return r; + + if (read_only) + fs->read_only = read_only; + + /* Compute limits for indirect block levels */ + uint32_t blocks_id = bsize / sizeof(uint32_t); + + fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT; + fs->inode_blocks_per_level[0] = 1; + + for (i = 1; i < 4; i++) { + fs->inode_blocks_per_level[i] = + fs->inode_blocks_per_level[i - 1] * blocks_id; + fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] + + fs->inode_blocks_per_level[i]; + } + + /*Validate FS*/ + tmp = ext4_get16(&fs->sb, state); + if (tmp & EXT4_SUPERBLOCK_STATE_ERROR_FS) + ext4_dbg(DEBUG_FS, DBG_WARN + "last umount error: superblock fs_error flag\n"); + + + if (!fs->read_only) { + /* Mark system as mounted */ + ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_ERROR_FS); + r = ext4_sb_write(fs->bdev, &fs->sb); + if (r != EOK) + return r; + + /*Update mount count*/ + ext4_set16(&fs->sb, mount_count, ext4_get16(&fs->sb, mount_count) + 1); + } + + return r; +} + +int ext4_fs_fini(struct ext4_fs *fs) +{ + ext4_assert(fs); + + /*Set superblock state*/ + ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_VALID_FS); + + if (!fs->read_only) + return ext4_sb_write(fs->bdev, &fs->sb); + + return EOK; +} + +static void ext4_fs_debug_features_inc(uint32_t features_incompatible) +{ + if (features_incompatible & EXT4_FINCOM_COMPRESSION) + ext4_dbg(DEBUG_FS, DBG_NONE "compression\n"); + if (features_incompatible & EXT4_FINCOM_FILETYPE) + ext4_dbg(DEBUG_FS, DBG_NONE "filetype\n"); + if (features_incompatible & EXT4_FINCOM_RECOVER) + ext4_dbg(DEBUG_FS, DBG_NONE "recover\n"); + if (features_incompatible & EXT4_FINCOM_JOURNAL_DEV) + ext4_dbg(DEBUG_FS, DBG_NONE "journal_dev\n"); + if (features_incompatible & EXT4_FINCOM_META_BG) + ext4_dbg(DEBUG_FS, DBG_NONE "meta_bg\n"); + if (features_incompatible & EXT4_FINCOM_EXTENTS) + ext4_dbg(DEBUG_FS, DBG_NONE "extents\n"); + if (features_incompatible & EXT4_FINCOM_64BIT) + ext4_dbg(DEBUG_FS, DBG_NONE "64bit\n"); + if (features_incompatible & EXT4_FINCOM_MMP) + ext4_dbg(DEBUG_FS, DBG_NONE "mnp\n"); + if (features_incompatible & EXT4_FINCOM_FLEX_BG) + ext4_dbg(DEBUG_FS, DBG_NONE "flex_bg\n"); + if (features_incompatible & EXT4_FINCOM_EA_INODE) + ext4_dbg(DEBUG_FS, DBG_NONE "ea_inode\n"); + if (features_incompatible & EXT4_FINCOM_DIRDATA) + ext4_dbg(DEBUG_FS, DBG_NONE "dirdata\n"); + if (features_incompatible & EXT4_FINCOM_BG_USE_META_CSUM) + ext4_dbg(DEBUG_FS, DBG_NONE "meta_csum\n"); + if (features_incompatible & EXT4_FINCOM_LARGEDIR) + ext4_dbg(DEBUG_FS, DBG_NONE "largedir\n"); + if (features_incompatible & EXT4_FINCOM_INLINE_DATA) + ext4_dbg(DEBUG_FS, DBG_NONE "inline_data\n"); +} +static void ext4_fs_debug_features_comp(uint32_t features_compatible) +{ + if (features_compatible & EXT4_FCOM_DIR_PREALLOC) + ext4_dbg(DEBUG_FS, DBG_NONE "dir_prealloc\n"); + if (features_compatible & EXT4_FCOM_IMAGIC_INODES) + ext4_dbg(DEBUG_FS, DBG_NONE "imagic_inodes\n"); + if (features_compatible & EXT4_FCOM_HAS_JOURNAL) + ext4_dbg(DEBUG_FS, DBG_NONE "has_journal\n"); + if (features_compatible & EXT4_FCOM_EXT_ATTR) + ext4_dbg(DEBUG_FS, DBG_NONE "ext_attr\n"); + if (features_compatible & EXT4_FCOM_RESIZE_INODE) + ext4_dbg(DEBUG_FS, DBG_NONE "resize_inode\n"); + if (features_compatible & EXT4_FCOM_DIR_INDEX) + ext4_dbg(DEBUG_FS, DBG_NONE "dir_index\n"); +} + +static void ext4_fs_debug_features_ro(uint32_t features_ro) +{ + if (features_ro & EXT4_FRO_COM_SPARSE_SUPER) + ext4_dbg(DEBUG_FS, DBG_NONE "sparse_super\n"); + if (features_ro & EXT4_FRO_COM_LARGE_FILE) + ext4_dbg(DEBUG_FS, DBG_NONE "large_file\n"); + if (features_ro & EXT4_FRO_COM_BTREE_DIR) + ext4_dbg(DEBUG_FS, DBG_NONE "btree_dir\n"); + if (features_ro & EXT4_FRO_COM_HUGE_FILE) + ext4_dbg(DEBUG_FS, DBG_NONE "huge_file\n"); + if (features_ro & EXT4_FRO_COM_GDT_CSUM) + ext4_dbg(DEBUG_FS, DBG_NONE "gtd_csum\n"); + if (features_ro & EXT4_FRO_COM_DIR_NLINK) + ext4_dbg(DEBUG_FS, DBG_NONE "dir_nlink\n"); + if (features_ro & EXT4_FRO_COM_EXTRA_ISIZE) + ext4_dbg(DEBUG_FS, DBG_NONE "extra_isize\n"); + if (features_ro & EXT4_FRO_COM_QUOTA) + ext4_dbg(DEBUG_FS, DBG_NONE "quota\n"); + if (features_ro & EXT4_FRO_COM_BIGALLOC) + ext4_dbg(DEBUG_FS, DBG_NONE "bigalloc\n"); + if (features_ro & EXT4_FRO_COM_METADATA_CSUM) + ext4_dbg(DEBUG_FS, DBG_NONE "metadata_csum\n"); +} + +int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only) +{ + ext4_assert(fs && read_only); + uint32_t v; + if (ext4_get32(&fs->sb, rev_level) == 0) { + *read_only = false; + return EOK; + } + + ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_incompatible:\n"); + ext4_fs_debug_features_inc(ext4_get32(&fs->sb, features_incompatible)); + + ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_compatible:\n"); + ext4_fs_debug_features_comp(ext4_get32(&fs->sb, features_compatible)); + + ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_read_only:\n"); + ext4_fs_debug_features_ro(ext4_get32(&fs->sb, features_read_only)); + + /*Check features_incompatible*/ + v = (ext4_get32(&fs->sb, features_incompatible) & + (~CONFIG_SUPPORTED_FINCOM)); + if (v) { + ext4_dbg(DEBUG_FS, DBG_ERROR + "sblock has unsupported features incompatible:\n"); + ext4_fs_debug_features_inc(v); + return ENOTSUP; + } + + /*Check features_read_only*/ + v = ext4_get32(&fs->sb, features_read_only); + v &= ~CONFIG_SUPPORTED_FRO_COM; + if (v) { + ext4_dbg(DEBUG_FS, DBG_WARN + "sblock has unsupported features read only:\n"); + ext4_fs_debug_features_ro(v); + *read_only = true; + return EOK; + } + *read_only = false; + + return EOK; +} + +/**@brief Determine whether the block is inside the group. + * @param baddr block address + * @param bgid block group id + * @return Error code + */ +static bool ext4_block_in_group(struct ext4_sblock *s, ext4_fsblk_t baddr, + uint32_t bgid) +{ + uint32_t actual_bgid; + actual_bgid = ext4_balloc_get_bgid_of_block(s, baddr); + if (actual_bgid == bgid) + return true; + return false; +} + +/**@brief To avoid calling the atomic setbit hundreds or thousands of times, we only + * need to use it within a single byte (to ensure we get endianness right). + * We can use memset for the rest of the bitmap as there are no other users. + */ +static void ext4_fs_mark_bitmap_end(int start_bit, int end_bit, void *bitmap) +{ + int i; + + if (start_bit >= end_bit) + return; + + for (i = start_bit; (unsigned)i < ((start_bit + 7) & ~7UL); i++) + ext4_bmap_bit_set(bitmap, i); + + if (i < end_bit) + memset((char *)bitmap + (i >> 3), 0xff, (end_bit - i) >> 3); +} + +/**@brief Initialize block bitmap in block group. + * @param bg_ref Reference to block group + * @return Error code + */ +static int ext4_fs_init_block_bitmap(struct ext4_block_group_ref *bg_ref) +{ + struct ext4_sblock *sb = &bg_ref->fs->sb; + struct ext4_bgroup *bg = bg_ref->block_group; + int rc; + + uint32_t bit, bit_max; + uint32_t group_blocks; + uint16_t inode_size = ext4_get16(sb, inode_size); + uint32_t block_size = ext4_sb_get_block_size(sb); + uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group); + + ext4_fsblk_t i; + ext4_fsblk_t bmp_blk = ext4_bg_get_block_bitmap(bg, sb); + ext4_fsblk_t bmp_inode = ext4_bg_get_inode_bitmap(bg, sb); + ext4_fsblk_t inode_table = ext4_bg_get_inode_table_first_block(bg, sb); + ext4_fsblk_t first_bg = ext4_balloc_get_block_of_bgid(sb, bg_ref->index); + + uint32_t dsc_per_block = block_size / ext4_sb_get_desc_size(sb); + + bool flex_bg = ext4_sb_feature_incom(sb, EXT4_FINCOM_FLEX_BG); + bool meta_bg = ext4_sb_feature_incom(sb, EXT4_FINCOM_META_BG); + + uint32_t inode_table_bcnt = inodes_per_group * inode_size / block_size; + + struct ext4_block block_bitmap; + rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &block_bitmap, bmp_blk); + if (rc != EOK) + return rc; + + memset(block_bitmap.data, 0, block_size); + bit_max = ext4_sb_is_super_in_bg(sb, bg_ref->index); + + uint32_t count = ext4_sb_first_meta_bg(sb) * dsc_per_block; + if (!meta_bg || bg_ref->index < count) { + if (bit_max) { + bit_max += ext4_bg_num_gdb(sb, bg_ref->index); + bit_max += ext4_get16(sb, s_reserved_gdt_blocks); + } + } else { /* For META_BG_BLOCK_GROUPS */ + bit_max += ext4_bg_num_gdb(sb, bg_ref->index); + } + for (bit = 0; bit < bit_max; bit++) + ext4_bmap_bit_set(block_bitmap.data, bit); + + if (bg_ref->index == ext4_block_group_cnt(sb) - 1) { + /* + * Even though mke2fs always initialize first and last group + * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need + * to make sure we calculate the right free blocks + */ + + group_blocks = (uint32_t)(ext4_sb_get_blocks_cnt(sb) - + ext4_get32(sb, first_data_block) - + ext4_get32(sb, blocks_per_group) * + (ext4_block_group_cnt(sb) - 1)); + } else { + group_blocks = ext4_get32(sb, blocks_per_group); + } + + bool in_bg; + in_bg = ext4_block_in_group(sb, bmp_blk, bg_ref->index); + if (!flex_bg || in_bg) + ext4_bmap_bit_set(block_bitmap.data, + (uint32_t)(bmp_blk - first_bg)); + + in_bg = ext4_block_in_group(sb, bmp_inode, bg_ref->index); + if (!flex_bg || in_bg) + ext4_bmap_bit_set(block_bitmap.data, + (uint32_t)(bmp_inode - first_bg)); + + for (i = inode_table; i < inode_table + inode_table_bcnt; i++) { + in_bg = ext4_block_in_group(sb, i, bg_ref->index); + if (!flex_bg || in_bg) + ext4_bmap_bit_set(block_bitmap.data, + (uint32_t)(i - first_bg)); + } + /* + * Also if the number of blocks within the group is + * less than the blocksize * 8 ( which is the size + * of bitmap ), set rest of the block bitmap to 1 + */ + ext4_fs_mark_bitmap_end(group_blocks, block_size * 8, block_bitmap.data); + ext4_trans_set_block_dirty(block_bitmap.buf); + + ext4_balloc_set_bitmap_csum(sb, bg_ref->block_group, block_bitmap.data); + bg_ref->dirty = true; + + /* Save bitmap */ + return ext4_block_set(bg_ref->fs->bdev, &block_bitmap); +} + +/**@brief Initialize i-node bitmap in block group. + * @param bg_ref Reference to block group + * @return Error code + */ +static int ext4_fs_init_inode_bitmap(struct ext4_block_group_ref *bg_ref) +{ + int rc; + struct ext4_sblock *sb = &bg_ref->fs->sb; + struct ext4_bgroup *bg = bg_ref->block_group; + + /* Load bitmap */ + ext4_fsblk_t bitmap_block_addr = ext4_bg_get_inode_bitmap(bg, sb); + + struct ext4_block b; + rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &b, bitmap_block_addr); + if (rc != EOK) + return rc; + + /* Initialize all bitmap bits to zero */ + uint32_t block_size = ext4_sb_get_block_size(sb); + uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group); + + memset(b.data, 0, (inodes_per_group + 7) / 8); + + uint32_t start_bit = inodes_per_group; + uint32_t end_bit = block_size * 8; + + uint32_t i; + for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++) + ext4_bmap_bit_set(b.data, i); + + if (i < end_bit) + memset(b.data + (i >> 3), 0xff, (end_bit - i) >> 3); + + ext4_trans_set_block_dirty(b.buf); + + ext4_ialloc_set_bitmap_csum(sb, bg, b.data); + bg_ref->dirty = true; + + /* Save bitmap */ + return ext4_block_set(bg_ref->fs->bdev, &b); +} + +/**@brief Initialize i-node table in block group. + * @param bg_ref Reference to block group + * @return Error code + */ +static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref) +{ + struct ext4_sblock *sb = &bg_ref->fs->sb; + struct ext4_bgroup *bg = bg_ref->block_group; + + uint32_t inode_size = ext4_get16(sb, inode_size); + uint32_t block_size = ext4_sb_get_block_size(sb); + uint32_t inodes_per_block = block_size / inode_size; + uint32_t inodes_in_group = ext4_inodes_in_group_cnt(sb, bg_ref->index); + uint32_t table_blocks = inodes_in_group / inodes_per_block; + ext4_fsblk_t fblock; + + if (inodes_in_group % inodes_per_block) + table_blocks++; + + /* Compute initialization bounds */ + ext4_fsblk_t first_block = ext4_bg_get_inode_table_first_block(bg, sb); + + ext4_fsblk_t last_block = first_block + table_blocks - 1; + + /* Initialization of all itable blocks */ + for (fblock = first_block; fblock <= last_block; ++fblock) { + struct ext4_block b; + int rc = ext4_trans_block_get_noread(bg_ref->fs->bdev, &b, fblock); + if (rc != EOK) + return rc; + + memset(b.data, 0, block_size); + ext4_trans_set_block_dirty(b.buf); + + rc = ext4_block_set(bg_ref->fs->bdev, &b); + if (rc != EOK) + return rc; + } + + return EOK; +} + +static ext4_fsblk_t ext4_fs_get_descriptor_block(struct ext4_sblock *s, + uint32_t bgid, + uint32_t dsc_per_block) +{ + uint32_t first_meta_bg, dsc_id; + int has_super = 0; + dsc_id = bgid / dsc_per_block; + first_meta_bg = ext4_sb_first_meta_bg(s); + + bool meta_bg = ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG); + + if (!meta_bg || dsc_id < first_meta_bg) + return ext4_get32(s, first_data_block) + dsc_id + 1; + + if (ext4_sb_is_super_in_bg(s, bgid)) + has_super = 1; + + return (has_super + ext4_fs_first_bg_block_no(s, bgid)); +} + +/**@brief Compute checksum of block group descriptor. + * @param sb Superblock + * @param bgid Index of block group in the filesystem + * @param bg Block group to compute checksum for + * @return Checksum value + */ +static uint16_t ext4_fs_bg_checksum(struct ext4_sblock *sb, uint32_t bgid, + struct ext4_bgroup *bg) +{ + /* If checksum not supported, 0 will be returned */ + uint16_t crc = 0; +#if CONFIG_META_CSUM_ENABLE + /* Compute the checksum only if the filesystem supports it */ + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + /* Use metadata_csum algorithm instead */ + uint32_t le32_bgid = to_le32(bgid); + uint32_t orig_checksum, checksum; + + /* Preparation: temporarily set bg checksum to 0 */ + orig_checksum = bg->checksum; + bg->checksum = 0; + + /* First calculate crc32 checksum against fs uuid */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, + sizeof(sb->uuid)); + /* Then calculate crc32 checksum against bgid */ + checksum = ext4_crc32c(checksum, &le32_bgid, sizeof(bgid)); + /* Finally calculate crc32 checksum against block_group_desc */ + checksum = ext4_crc32c(checksum, bg, ext4_sb_get_desc_size(sb)); + bg->checksum = orig_checksum; + + crc = checksum & 0xFFFF; + return crc; + } +#endif + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_GDT_CSUM)) { + uint8_t *base = (uint8_t *)bg; + uint8_t *checksum = (uint8_t *)&bg->checksum; + + uint32_t offset = (uint32_t)(checksum - base); + + /* Convert block group index to little endian */ + uint32_t group = to_le32(bgid); + + /* Initialization */ + crc = ext4_bg_crc16(~0, sb->uuid, sizeof(sb->uuid)); + + /* Include index of block group */ + crc = ext4_bg_crc16(crc, (uint8_t *)&group, sizeof(group)); + + /* Compute crc from the first part (stop before checksum field) + */ + crc = ext4_bg_crc16(crc, (uint8_t *)bg, offset); + + /* Skip checksum */ + offset += sizeof(bg->checksum); + + /* Checksum of the rest of block group descriptor */ + if ((ext4_sb_feature_incom(sb, EXT4_FINCOM_64BIT)) && + (offset < ext4_sb_get_desc_size(sb))) { + + const uint8_t *start = ((uint8_t *)bg) + offset; + size_t len = ext4_sb_get_desc_size(sb) - offset; + crc = ext4_bg_crc16(crc, start, len); + } + } + return crc; +} + +#if CONFIG_META_CSUM_ENABLE +static bool ext4_fs_verify_bg_csum(struct ext4_sblock *sb, + uint32_t bgid, + struct ext4_bgroup *bg) +{ + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return true; + + return ext4_fs_bg_checksum(sb, bgid, bg) == to_le16(bg->checksum); +} +#else +#define ext4_fs_verify_bg_csum(...) true +#endif + +int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid, + struct ext4_block_group_ref *ref) +{ + /* Compute number of descriptors, that fits in one data block */ + uint32_t block_size = ext4_sb_get_block_size(&fs->sb); + uint32_t dsc_cnt = block_size / ext4_sb_get_desc_size(&fs->sb); + + /* Block group descriptor table starts at the next block after + * superblock */ + uint64_t block_id = ext4_fs_get_descriptor_block(&fs->sb, bgid, dsc_cnt); + + uint32_t offset = (bgid % dsc_cnt) * ext4_sb_get_desc_size(&fs->sb); + + int rc = ext4_trans_block_get(fs->bdev, &ref->block, block_id); + if (rc != EOK) + return rc; + + ref->block_group = (void *)(ref->block.data + offset); + ref->fs = fs; + ref->index = bgid; + ref->dirty = false; + struct ext4_bgroup *bg = ref->block_group; + + if (!ext4_fs_verify_bg_csum(&fs->sb, bgid, bg)) { + ext4_dbg(DEBUG_FS, + DBG_WARN "Block group descriptor checksum failed." + "Block group index: %" PRIu32"\n", + bgid); + } + + if (ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_BLOCK_UNINIT)) { + rc = ext4_fs_init_block_bitmap(ref); + if (rc != EOK) { + ext4_block_set(fs->bdev, &ref->block); + return rc; + } + ext4_bg_clear_flag(bg, EXT4_BLOCK_GROUP_BLOCK_UNINIT); + ref->dirty = true; + } + + if (ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_INODE_UNINIT)) { + rc = ext4_fs_init_inode_bitmap(ref); + if (rc != EOK) { + ext4_block_set(ref->fs->bdev, &ref->block); + return rc; + } + + ext4_bg_clear_flag(bg, EXT4_BLOCK_GROUP_INODE_UNINIT); + + if (!ext4_bg_has_flag(bg, EXT4_BLOCK_GROUP_ITABLE_ZEROED)) { + rc = ext4_fs_init_inode_table(ref); + if (rc != EOK) { + ext4_block_set(fs->bdev, &ref->block); + return rc; + } + + ext4_bg_set_flag(bg, EXT4_BLOCK_GROUP_ITABLE_ZEROED); + } + + ref->dirty = true; + } + + return EOK; +} + +int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref) +{ + /* Check if reference modified */ + if (ref->dirty) { + /* Compute new checksum of block group */ + uint16_t cs; + cs = ext4_fs_bg_checksum(&ref->fs->sb, ref->index, + ref->block_group); + ref->block_group->checksum = to_le16(cs); + + /* Mark block dirty for writing changes to physical device */ + ext4_trans_set_block_dirty(ref->block.buf); + } + + /* Put back block, that contains block group descriptor */ + return ext4_block_set(ref->fs->bdev, &ref->block); +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_fs_inode_checksum(struct ext4_inode_ref *inode_ref) +{ + uint32_t checksum = 0; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint16_t inode_size = ext4_get16(sb, inode_size); + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t orig_checksum; + + uint32_t ino_index = to_le32(inode_ref->index); + uint32_t ino_gen = + to_le32(ext4_inode_get_generation(inode_ref->inode)); + + /* Preparation: temporarily set bg checksum to 0 */ + orig_checksum = ext4_inode_get_csum(sb, inode_ref->inode); + ext4_inode_set_csum(sb, inode_ref->inode, 0); + + /* First calculate crc32 checksum against fs uuid */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, + sizeof(sb->uuid)); + /* Then calculate crc32 checksum against inode number + * and inode generation */ + checksum = ext4_crc32c(checksum, &ino_index, sizeof(ino_index)); + checksum = ext4_crc32c(checksum, &ino_gen, sizeof(ino_gen)); + /* Finally calculate crc32 checksum against + * the entire inode */ + checksum = ext4_crc32c(checksum, inode_ref->inode, inode_size); + ext4_inode_set_csum(sb, inode_ref->inode, orig_checksum); + + /* If inode size is not large enough to hold the + * upper 16bit of the checksum */ + if (inode_size == EXT4_GOOD_OLD_INODE_SIZE) + checksum &= 0xFFFF; + + } + return checksum; +} +#else +#define ext4_fs_inode_checksum(...) 0 +#endif + +static void ext4_fs_set_inode_checksum(struct ext4_inode_ref *inode_ref) +{ + struct ext4_sblock *sb = &inode_ref->fs->sb; + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return; + + uint32_t csum = ext4_fs_inode_checksum(inode_ref); + ext4_inode_set_csum(sb, inode_ref->inode, csum); +} + +#if CONFIG_META_CSUM_ENABLE +static bool ext4_fs_verify_inode_csum(struct ext4_inode_ref *inode_ref) +{ + struct ext4_sblock *sb = &inode_ref->fs->sb; + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return true; + + return ext4_inode_get_csum(sb, inode_ref->inode) == + ext4_fs_inode_checksum(inode_ref); +} +#else +#define ext4_fs_verify_inode_csum(...) true +#endif + +static int +__ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index, + struct ext4_inode_ref *ref, + bool initialized) +{ + /* Compute number of i-nodes, that fits in one data block */ + uint32_t inodes_per_group = ext4_get32(&fs->sb, inodes_per_group); + + /* + * Inode numbers are 1-based, but it is simpler to work with 0-based + * when computing indices + */ + index -= 1; + uint32_t block_group = index / inodes_per_group; + uint32_t offset_in_group = index % inodes_per_group; + + /* Load block group, where i-node is located */ + struct ext4_block_group_ref bg_ref; + + int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref); + if (rc != EOK) { + return rc; + } + + /* Load block address, where i-node table is located */ + ext4_fsblk_t inode_table_start = + ext4_bg_get_inode_table_first_block(bg_ref.block_group, &fs->sb); + + /* Put back block group reference (not needed more) */ + rc = ext4_fs_put_block_group_ref(&bg_ref); + if (rc != EOK) { + return rc; + } + + /* Compute position of i-node in the block group */ + uint16_t inode_size = ext4_get16(&fs->sb, inode_size); + uint32_t block_size = ext4_sb_get_block_size(&fs->sb); + uint32_t byte_offset_in_group = offset_in_group * inode_size; + + /* Compute block address */ + ext4_fsblk_t block_id = + inode_table_start + (byte_offset_in_group / block_size); + + rc = ext4_trans_block_get(fs->bdev, &ref->block, block_id); + if (rc != EOK) { + return rc; + } + + /* Compute position of i-node in the data block */ + uint32_t offset_in_block = byte_offset_in_group % block_size; + ref->inode = (struct ext4_inode *)(ref->block.data + offset_in_block); + + /* We need to store the original value of index in the reference */ + ref->index = index + 1; + ref->fs = fs; + ref->dirty = false; + + if (initialized && !ext4_fs_verify_inode_csum(ref)) { + ext4_dbg(DEBUG_FS, + DBG_WARN "Inode checksum failed." + "Inode: %" PRIu32"\n", + ref->index); + } + + return EOK; +} + +int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index, + struct ext4_inode_ref *ref) +{ + return __ext4_fs_get_inode_ref(fs, index, ref, true); +} + +int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref) +{ + /* Check if reference modified */ + if (ref->dirty) { + /* Mark block dirty for writing changes to physical device */ + ext4_fs_set_inode_checksum(ref); + ext4_trans_set_block_dirty(ref->block.buf); + } + + /* Put back block, that contains i-node */ + return ext4_block_set(ref->fs->bdev, &ref->block); +} + +void ext4_fs_inode_blocks_init(struct ext4_fs *fs, + struct ext4_inode_ref *inode_ref) +{ + struct ext4_inode *inode = inode_ref->inode; + + /* Reset blocks array. For inode which is not directory or file, just + * fill in blocks with 0 */ + switch (ext4_inode_type(&fs->sb, inode_ref->inode)) { + case EXT4_INODE_MODE_FILE: + case EXT4_INODE_MODE_DIRECTORY: + break; + default: + return; + } + +#if CONFIG_EXTENT_ENABLE + /* Initialize extents if needed */ + if (ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) { + ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS); + + /* Initialize extent root header */ + ext4_extent_tree_init(inode_ref); + } + + inode_ref->dirty = true; +#endif +} + +uint32_t ext4_fs_correspond_inode_mode(int filetype) +{ + switch (filetype) { + case EXT4_DE_DIR: + return EXT4_INODE_MODE_DIRECTORY; + case EXT4_DE_REG_FILE: + return EXT4_INODE_MODE_FILE; + case EXT4_DE_SYMLINK: + return EXT4_INODE_MODE_SOFTLINK; + case EXT4_DE_CHRDEV: + return EXT4_INODE_MODE_CHARDEV; + case EXT4_DE_BLKDEV: + return EXT4_INODE_MODE_BLOCKDEV; + case EXT4_DE_FIFO: + return EXT4_INODE_MODE_FIFO; + case EXT4_DE_SOCK: + return EXT4_INODE_MODE_SOCKET; + } + /* FIXME: unsupported filetype */ + return EXT4_INODE_MODE_FILE; +} + +int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref, + int filetype) +{ + /* Check if newly allocated i-node will be a directory */ + bool is_dir; + uint16_t inode_size = ext4_get16(&fs->sb, inode_size); + + is_dir = (filetype == EXT4_DE_DIR); + + /* Allocate inode by allocation algorithm */ + uint32_t index; + int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir); + if (rc != EOK) + return rc; + + /* Load i-node from on-disk i-node table */ + rc = __ext4_fs_get_inode_ref(fs, index, inode_ref, false); + if (rc != EOK) { + ext4_ialloc_free_inode(fs, index, is_dir); + return rc; + } + + /* Initialize i-node */ + struct ext4_inode *inode = inode_ref->inode; + + memset(inode, 0, inode_size); + + uint32_t mode; + if (is_dir) { + /* + * Default directory permissions to be compatible with other + * systems + * 0777 (octal) == rwxrwxrwx + */ + + mode = 0777; + mode |= EXT4_INODE_MODE_DIRECTORY; + } else if (filetype == EXT4_DE_SYMLINK) { + /* + * Default symbolic link permissions to be compatible with other systems + * 0777 (octal) == rwxrwxrwx + */ + + mode = 0777; + mode |= EXT4_INODE_MODE_SOFTLINK; + } else { + /* + * Default file permissions to be compatible with other systems + * 0666 (octal) == rw-rw-rw- + */ + + mode = 0666; + mode |= ext4_fs_correspond_inode_mode(filetype); + } + ext4_inode_set_mode(&fs->sb, inode, mode); + + ext4_inode_set_links_cnt(inode, 0); + ext4_inode_set_uid(inode, 0); + ext4_inode_set_gid(inode, 0); + ext4_inode_set_size(inode, 0); + ext4_inode_set_access_time(inode, 0); + ext4_inode_set_change_inode_time(inode, 0); + ext4_inode_set_modif_time(inode, 0); + ext4_inode_set_del_time(inode, 0); + ext4_inode_set_blocks_count(&fs->sb, inode, 0); + ext4_inode_set_flags(inode, 0); + ext4_inode_set_generation(inode, 0); + if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) { + uint16_t size = ext4_get16(&fs->sb, want_extra_isize); + ext4_inode_set_extra_isize(&fs->sb, inode, size); + } + + memset(inode->blocks, 0, sizeof(inode->blocks)); + inode_ref->dirty = true; + + return EOK; +} + +int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref) +{ + struct ext4_fs *fs = inode_ref->fs; + uint32_t offset; + uint32_t suboff; + int rc; +#if CONFIG_EXTENT_ENABLE + /* For extents must be data block destroyed by other way */ + if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) && + (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) { + /* Data structures are released during truncate operation... */ + goto finish; + } +#endif + /* Release all indirect (no data) blocks */ + + /* 1) Single indirect */ + ext4_fsblk_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0); + if (fblock != 0) { + int rc = ext4_balloc_free_block(inode_ref, fblock); + if (rc != EOK) + return rc; + + ext4_inode_set_indirect_block(inode_ref->inode, 0, 0); + } + + uint32_t block_size = ext4_sb_get_block_size(&fs->sb); + uint32_t count = block_size / sizeof(uint32_t); + + struct ext4_block block; + + /* 2) Double indirect */ + fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1); + if (fblock != 0) { + int rc = ext4_trans_block_get(fs->bdev, &block, fblock); + if (rc != EOK) + return rc; + + ext4_fsblk_t ind_block; + for (offset = 0; offset < count; ++offset) { + ind_block = to_le32(((uint32_t *)block.data)[offset]); + + if (ind_block == 0) + continue; + rc = ext4_balloc_free_block(inode_ref, ind_block); + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + } + + ext4_block_set(fs->bdev, &block); + rc = ext4_balloc_free_block(inode_ref, fblock); + if (rc != EOK) + return rc; + + ext4_inode_set_indirect_block(inode_ref->inode, 1, 0); + } + + /* 3) Tripple indirect */ + struct ext4_block subblock; + fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2); + if (fblock == 0) + goto finish; + rc = ext4_trans_block_get(fs->bdev, &block, fblock); + if (rc != EOK) + return rc; + + ext4_fsblk_t ind_block; + for (offset = 0; offset < count; ++offset) { + ind_block = to_le32(((uint32_t *)block.data)[offset]); + + if (ind_block == 0) + continue; + rc = ext4_trans_block_get(fs->bdev, &subblock, + ind_block); + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + ext4_fsblk_t ind_subblk; + for (suboff = 0; suboff < count; ++suboff) { + ind_subblk = to_le32(((uint32_t *)subblock.data)[suboff]); + + if (ind_subblk == 0) + continue; + rc = ext4_balloc_free_block(inode_ref, ind_subblk); + if (rc != EOK) { + ext4_block_set(fs->bdev, &subblock); + ext4_block_set(fs->bdev, &block); + return rc; + } + + } + + ext4_block_set(fs->bdev, &subblock); + + rc = ext4_balloc_free_block(inode_ref, + ind_block); + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + } + + ext4_block_set(fs->bdev, &block); + rc = ext4_balloc_free_block(inode_ref, fblock); + if (rc != EOK) + return rc; + + ext4_inode_set_indirect_block(inode_ref->inode, 2, 0); +finish: + /* Mark inode dirty for writing to the physical device */ + inode_ref->dirty = true; + + /* Free block with extended attributes if present */ + ext4_fsblk_t xattr_block = + ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + if (xattr_block) { + int rc = ext4_balloc_free_block(inode_ref, xattr_block); + if (rc != EOK) + return rc; + + ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0); + } + + /* Free inode by allocator */ + if (ext4_inode_is_type(&fs->sb, inode_ref->inode, + EXT4_INODE_MODE_DIRECTORY)) + rc = ext4_ialloc_free_inode(fs, inode_ref->index, true); + else + rc = ext4_ialloc_free_inode(fs, inode_ref->index, false); + + return rc; +} + + +/**@brief Release data block from i-node + * @param inode_ref I-node to release block from + * @param iblock Logical block to be released + * @return Error code + */ +static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref, + ext4_lblk_t iblock) +{ + ext4_fsblk_t fblock; + + struct ext4_fs *fs = inode_ref->fs; + + /* Extents are handled otherwise = there is not support in this function + */ + ext4_assert(!( + ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS) && + (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)))); + + struct ext4_inode *inode = inode_ref->inode; + + /* Handle simple case when we are dealing with direct reference */ + if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) { + fblock = ext4_inode_get_direct_block(inode, iblock); + + /* Sparse file */ + if (fblock == 0) + return EOK; + + ext4_inode_set_direct_block(inode, iblock, 0); + return ext4_balloc_free_block(inode_ref, fblock); + } + + /* Determine the indirection level needed to get the desired block */ + unsigned int level = 0; + unsigned int i; + for (i = 1; i < 4; i++) { + if (iblock < fs->inode_block_limits[i]) { + level = i; + break; + } + } + + if (level == 0) + return EIO; + + /* Compute offsets for the topmost level */ + uint32_t block_offset_in_level = + (uint32_t)(iblock - fs->inode_block_limits[level - 1]); + ext4_fsblk_t current_block = + ext4_inode_get_indirect_block(inode, level - 1); + uint32_t offset_in_block = + (uint32_t)(block_offset_in_level / fs->inode_blocks_per_level[level - 1]); + + /* + * Navigate through other levels, until we find the block number + * or find null reference meaning we are dealing with sparse file + */ + struct ext4_block block; + + while (level > 0) { + + /* Sparse check */ + if (current_block == 0) + return EOK; + + int rc = ext4_trans_block_get(fs->bdev, &block, current_block); + if (rc != EOK) + return rc; + + current_block = + to_le32(((uint32_t *)block.data)[offset_in_block]); + + /* Set zero if physical data block address found */ + if (level == 1) { + ((uint32_t *)block.data)[offset_in_block] = to_le32(0); + ext4_trans_set_block_dirty(block.buf); + } + + rc = ext4_block_set(fs->bdev, &block); + if (rc != EOK) + return rc; + + level--; + + /* + * If we are on the last level, break here as + * there is no next level to visit + */ + if (level == 0) + break; + + /* Visit the next level */ + block_offset_in_level %= fs->inode_blocks_per_level[level]; + offset_in_block = (uint32_t)(block_offset_in_level / + fs->inode_blocks_per_level[level - 1]); + } + + fblock = current_block; + if (fblock == 0) + return EOK; + + /* Physical block is not referenced, it can be released */ + return ext4_balloc_free_block(inode_ref, fblock); +} + +int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size) +{ + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint32_t i; + int r; + bool v; + + /* Check flags, if i-node can be truncated */ + if (!ext4_inode_can_truncate(sb, inode_ref->inode)) + return EINVAL; + + /* If sizes are equal, nothing has to be done. */ + uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode); + if (old_size == new_size) + return EOK; + + /* It's not supported to make the larger file by truncate operation */ + if (old_size < new_size) + return EINVAL; + + /* For symbolic link which is small enough */ + v = ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK); + if (v && old_size < sizeof(inode_ref->inode->blocks) && + !ext4_inode_get_blocks_count(sb, inode_ref->inode)) { + char *content = (char *)inode_ref->inode->blocks + new_size; + memset(content, 0, + sizeof(inode_ref->inode->blocks) - (uint32_t)new_size); + ext4_inode_set_size(inode_ref->inode, new_size); + inode_ref->dirty = true; + + return EOK; + } + + i = ext4_inode_type(sb, inode_ref->inode); + if (i == EXT4_INODE_MODE_CHARDEV || + i == EXT4_INODE_MODE_BLOCKDEV || + i == EXT4_INODE_MODE_SOCKET) { + inode_ref->inode->blocks[0] = 0; + inode_ref->inode->blocks[1] = 0; + + inode_ref->dirty = true; + return EOK; + } + + /* Compute how many blocks will be released */ + uint32_t block_size = ext4_sb_get_block_size(sb); + uint32_t new_blocks_cnt = (uint32_t)((new_size + block_size - 1) / block_size); + uint32_t old_blocks_cnt = (uint32_t)((old_size + block_size - 1) / block_size); + uint32_t diff_blocks_cnt = old_blocks_cnt - new_blocks_cnt; +#if CONFIG_EXTENT_ENABLE + if ((ext4_sb_feature_incom(sb, EXT4_FINCOM_EXTENTS)) && + (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) { + + /* Extents require special operation */ + if (diff_blocks_cnt) { + r = ext4_extent_remove_space(inode_ref, new_blocks_cnt, + EXT_MAX_BLOCKS); + if (r != EOK) + return r; + + } + } else +#endif + { + /* Release data blocks from the end of file */ + + /* Starting from 1 because of logical blocks are numbered from 0 + */ + for (i = 0; i < diff_blocks_cnt; ++i) { + r = ext4_fs_release_inode_block(inode_ref, + new_blocks_cnt + i); + if (r != EOK) + return r; + } + } + + /* Update i-node */ + ext4_inode_set_size(inode_ref->inode, new_size); + inode_ref->dirty = true; + + return EOK; +} + +/**@brief Compute 'goal' for inode index + * @param inode_ref Reference to inode, to allocate block for + * @return goal + */ +ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref) +{ + uint32_t grp_inodes = ext4_get32(&inode_ref->fs->sb, inodes_per_group); + return (inode_ref->index - 1) / grp_inodes; +} + +/**@brief Compute 'goal' for allocation algorithm (For blockmap). + * @param inode_ref Reference to inode, to allocate block for + * @param goal + * @return error code + */ +int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t *goal) +{ + int r; + struct ext4_sblock *sb = &inode_ref->fs->sb; + *goal = 0; + + uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode); + uint32_t block_size = ext4_sb_get_block_size(sb); + uint32_t iblock_cnt = (uint32_t)(inode_size / block_size); + + if (inode_size % block_size != 0) + iblock_cnt++; + + /* If inode has some blocks, get last block address + 1 */ + if (iblock_cnt > 0) { + r = ext4_fs_get_inode_dblk_idx(inode_ref, iblock_cnt - 1, + goal, false); + if (r != EOK) + return r; + + if (*goal != 0) { + (*goal)++; + return r; + } + + /* If goal == 0, sparse file -> continue */ + } + + /* Identify block group of inode */ + + uint32_t inodes_per_bg = ext4_get32(sb, inodes_per_group); + uint32_t block_group = (inode_ref->index - 1) / inodes_per_bg; + block_size = ext4_sb_get_block_size(sb); + + /* Load block group reference */ + struct ext4_block_group_ref bg_ref; + r = ext4_fs_get_block_group_ref(inode_ref->fs, block_group, &bg_ref); + if (r != EOK) + return r; + + struct ext4_bgroup *bg = bg_ref.block_group; + + /* Compute indexes */ + uint32_t bg_count = ext4_block_group_cnt(sb); + ext4_fsblk_t itab_first_block = ext4_bg_get_inode_table_first_block(bg, sb); + uint16_t itab_item_size = ext4_get16(sb, inode_size); + uint32_t itab_bytes; + + /* Check for last block group */ + if (block_group < bg_count - 1) { + itab_bytes = inodes_per_bg * itab_item_size; + } else { + /* Last block group could be smaller */ + uint32_t inodes_cnt = ext4_get32(sb, inodes_count); + + itab_bytes = (inodes_cnt - ((bg_count - 1) * inodes_per_bg)); + itab_bytes *= itab_item_size; + } + + ext4_fsblk_t inode_table_blocks = itab_bytes / block_size; + + if (itab_bytes % block_size) + inode_table_blocks++; + + *goal = itab_first_block + inode_table_blocks; + + return ext4_fs_put_block_group_ref(&bg_ref); +} + +static int ext4_fs_get_inode_dblk_idx_internal(struct ext4_inode_ref *inode_ref, + ext4_lblk_t iblock, ext4_fsblk_t *fblock, + bool extent_create, + bool support_unwritten __unused) +{ + struct ext4_fs *fs = inode_ref->fs; + + /* For empty file is situation simple */ + if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) { + *fblock = 0; + return EOK; + } + + ext4_fsblk_t current_block; + + (void)extent_create; +#if CONFIG_EXTENT_ENABLE + /* Handle i-node using extents */ + if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) && + (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) { + + ext4_fsblk_t current_fsblk; + int rc = ext4_extent_get_blocks(inode_ref, iblock, 1, + ¤t_fsblk, extent_create, NULL); + if (rc != EOK) + return rc; + + current_block = current_fsblk; + *fblock = current_block; + + ext4_assert(*fblock || support_unwritten); + return EOK; + } +#endif + + struct ext4_inode *inode = inode_ref->inode; + + /* Direct block are read directly from array in i-node structure */ + if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) { + current_block = + ext4_inode_get_direct_block(inode, (uint32_t)iblock); + *fblock = current_block; + return EOK; + } + + /* Determine indirection level of the target block */ + unsigned int l = 0; + unsigned int i; + for (i = 1; i < 4; i++) { + if (iblock < fs->inode_block_limits[i]) { + l = i; + break; + } + } + + if (l == 0) + return EIO; + + /* Compute offsets for the topmost level */ + uint32_t blk_off_in_lvl = (uint32_t)(iblock - fs->inode_block_limits[l - 1]); + current_block = ext4_inode_get_indirect_block(inode, l - 1); + uint32_t off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]); + + /* Sparse file */ + if (current_block == 0) { + *fblock = 0; + return EOK; + } + + struct ext4_block block; + + /* + * Navigate through other levels, until we find the block number + * or find null reference meaning we are dealing with sparse file + */ + while (l > 0) { + /* Load indirect block */ + int rc = ext4_trans_block_get(fs->bdev, &block, current_block); + if (rc != EOK) + return rc; + + /* Read block address from indirect block */ + current_block = + to_le32(((uint32_t *)block.data)[off_in_blk]); + + /* Put back indirect block untouched */ + rc = ext4_block_set(fs->bdev, &block); + if (rc != EOK) + return rc; + + /* Check for sparse file */ + if (current_block == 0) { + *fblock = 0; + return EOK; + } + + /* Jump to the next level */ + l--; + + /* Termination condition - we have address of data block loaded + */ + if (l == 0) + break; + + /* Visit the next level */ + blk_off_in_lvl %= fs->inode_blocks_per_level[l]; + off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]); + } + + *fblock = current_block; + + return EOK; +} + + +int ext4_fs_get_inode_dblk_idx(struct ext4_inode_ref *inode_ref, + ext4_lblk_t iblock, ext4_fsblk_t *fblock, + bool support_unwritten) +{ + return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock, + false, support_unwritten); +} + +int ext4_fs_init_inode_dblk_idx(struct ext4_inode_ref *inode_ref, + ext4_lblk_t iblock, ext4_fsblk_t *fblock) +{ + return ext4_fs_get_inode_dblk_idx_internal(inode_ref, iblock, fblock, + true, true); +} + +static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref, + ext4_lblk_t iblock, ext4_fsblk_t fblock) +{ + struct ext4_fs *fs = inode_ref->fs; + +#if CONFIG_EXTENT_ENABLE + /* Handle inode using extents */ + if ((ext4_sb_feature_incom(&fs->sb, EXT4_FINCOM_EXTENTS)) && + (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) { + /* Not reachable */ + return ENOTSUP; + } +#endif + + /* Handle simple case when we are dealing with direct reference */ + if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) { + ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, + (uint32_t)fblock); + inode_ref->dirty = true; + + return EOK; + } + + /* Determine the indirection level needed to get the desired block */ + unsigned int l = 0; + unsigned int i; + for (i = 1; i < 4; i++) { + if (iblock < fs->inode_block_limits[i]) { + l = i; + break; + } + } + + if (l == 0) + return EIO; + + uint32_t block_size = ext4_sb_get_block_size(&fs->sb); + + /* Compute offsets for the topmost level */ + uint32_t blk_off_in_lvl = (uint32_t)(iblock - fs->inode_block_limits[l - 1]); + ext4_fsblk_t current_block = + ext4_inode_get_indirect_block(inode_ref->inode, l - 1); + uint32_t off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]); + + ext4_fsblk_t new_blk; + + struct ext4_block block; + struct ext4_block new_block; + + /* Is needed to allocate indirect block on the i-node level */ + if (current_block == 0) { + /* Allocate new indirect block */ + ext4_fsblk_t goal; + int rc = ext4_fs_indirect_find_goal(inode_ref, &goal); + if (rc != EOK) + return rc; + + rc = ext4_balloc_alloc_block(inode_ref, goal, &new_blk); + if (rc != EOK) + return rc; + + /* Update i-node */ + ext4_inode_set_indirect_block(inode_ref->inode, l - 1, + (uint32_t)new_blk); + inode_ref->dirty = true; + + /* Load newly allocated block */ + rc = ext4_trans_block_get_noread(fs->bdev, &new_block, new_blk); + if (rc != EOK) { + ext4_balloc_free_block(inode_ref, new_blk); + return rc; + } + + /* Initialize new block */ + memset(new_block.data, 0, block_size); + ext4_trans_set_block_dirty(new_block.buf); + + /* Put back the allocated block */ + rc = ext4_block_set(fs->bdev, &new_block); + if (rc != EOK) + return rc; + + current_block = new_blk; + } + + /* + * Navigate through other levels, until we find the block number + * or find null reference meaning we are dealing with sparse file + */ + while (l > 0) { + int rc = ext4_trans_block_get(fs->bdev, &block, current_block); + if (rc != EOK) + return rc; + + current_block = to_le32(((uint32_t *)block.data)[off_in_blk]); + if ((l > 1) && (current_block == 0)) { + ext4_fsblk_t goal; + rc = ext4_fs_indirect_find_goal(inode_ref, &goal); + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + /* Allocate new block */ + rc = + ext4_balloc_alloc_block(inode_ref, goal, &new_blk); + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + /* Load newly allocated block */ + rc = ext4_trans_block_get_noread(fs->bdev, &new_block, + new_blk); + + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + /* Initialize allocated block */ + memset(new_block.data, 0, block_size); + ext4_trans_set_block_dirty(new_block.buf); + + rc = ext4_block_set(fs->bdev, &new_block); + if (rc != EOK) { + ext4_block_set(fs->bdev, &block); + return rc; + } + + /* Write block address to the parent */ + uint32_t * p = (uint32_t * )block.data; + p[off_in_blk] = to_le32((uint32_t)new_blk); + ext4_trans_set_block_dirty(block.buf); + current_block = new_blk; + } + + /* Will be finished, write the fblock address */ + if (l == 1) { + uint32_t * p = (uint32_t * )block.data; + p[off_in_blk] = to_le32((uint32_t)fblock); + ext4_trans_set_block_dirty(block.buf); + } + + rc = ext4_block_set(fs->bdev, &block); + if (rc != EOK) + return rc; + + l--; + + /* + * If we are on the last level, break here as + * there is no next level to visit + */ + if (l == 0) + break; + + /* Visit the next level */ + blk_off_in_lvl %= fs->inode_blocks_per_level[l]; + off_in_blk = (uint32_t)(blk_off_in_lvl / fs->inode_blocks_per_level[l - 1]); + } + + return EOK; +} + + +int ext4_fs_append_inode_dblk(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t *fblock, ext4_lblk_t *iblock) +{ +#if CONFIG_EXTENT_ENABLE + /* Handle extents separately */ + if ((ext4_sb_feature_incom(&inode_ref->fs->sb, EXT4_FINCOM_EXTENTS)) && + (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) { + int rc; + ext4_fsblk_t current_fsblk; + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode); + uint32_t block_size = ext4_sb_get_block_size(sb); + *iblock = (uint32_t)((inode_size + block_size - 1) / block_size); + + rc = ext4_extent_get_blocks(inode_ref, *iblock, 1, + ¤t_fsblk, true, NULL); + if (rc != EOK) + return rc; + + *fblock = current_fsblk; + ext4_assert(*fblock); + + ext4_inode_set_size(inode_ref->inode, inode_size + block_size); + inode_ref->dirty = true; + + + return rc; + } +#endif + struct ext4_sblock *sb = &inode_ref->fs->sb; + + /* Compute next block index and allocate data block */ + uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode); + uint32_t block_size = ext4_sb_get_block_size(sb); + + /* Align size i-node size */ + if ((inode_size % block_size) != 0) + inode_size += block_size - (inode_size % block_size); + + /* Logical blocks are numbered from 0 */ + uint32_t new_block_idx = (uint32_t)(inode_size / block_size); + + /* Allocate new physical block */ + ext4_fsblk_t goal, phys_block; + int rc = ext4_fs_indirect_find_goal(inode_ref, &goal); + if (rc != EOK) + return rc; + + rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block); + if (rc != EOK) + return rc; + + /* Add physical block address to the i-node */ + rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx, + phys_block); + if (rc != EOK) { + ext4_balloc_free_block(inode_ref, phys_block); + return rc; + } + + /* Update i-node */ + ext4_inode_set_size(inode_ref->inode, inode_size + block_size); + inode_ref->dirty = true; + + *fblock = phys_block; + *iblock = new_block_idx; + + return EOK; +} + +void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref) +{ + uint16_t link; + bool is_dx; + link = ext4_inode_get_links_cnt(inode_ref->inode); + link++; + ext4_inode_set_links_cnt(inode_ref->inode, link); + + is_dx = ext4_sb_feature_com(&inode_ref->fs->sb, EXT4_FCOM_DIR_INDEX) && + ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX); + + if (is_dx && link > 1) { + if (link >= EXT4_LINK_MAX || link == 2) { + ext4_inode_set_links_cnt(inode_ref->inode, 1); + + uint32_t v; + v = ext4_get32(&inode_ref->fs->sb, features_read_only); + v |= EXT4_FRO_COM_DIR_NLINK; + ext4_set32(&inode_ref->fs->sb, features_read_only, v); + } + } +} + +void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref) +{ + uint16_t links = ext4_inode_get_links_cnt(inode_ref->inode); + if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode, + EXT4_INODE_MODE_DIRECTORY)) { + if (links > 0) + ext4_inode_set_links_cnt(inode_ref->inode, links - 1); + return; + } + + if (links > 2) + ext4_inode_set_links_cnt(inode_ref->inode, links - 1); +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_hash.c b/Library/lwext4/src/ext4_hash.c new file mode 100644 index 0000000..ff6d031 --- /dev/null +++ b/Library/lwext4/src/ext4_hash.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * FreeBSD: + * Copyright (c) 2010, 2013 Zheng Liu + * Copyright (c) 2012, Vyacheslav Matyushin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * The following notice applies to the code in ext2_half_md4(): + * + * Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD4 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD4 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_hash.c + * @brief Directory indexing hash functions. + */ + +#include +#include +#include +#include +#include + +#include + +/* F, G, and H are MD4 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +/* + * FF, GG, and HH are transformations for rounds 1, 2, and 3. + * Rotation is separated from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s) \ + { \ + (a) += F((b), (c), (d)) + (x); \ + (a) = ROTATE_LEFT((a), (s)); \ + \ +} + +#define GG(a, b, c, d, x, s) \ + { \ + (a) += G((b), (c), (d)) + (x) + (uint32_t)0x5A827999; \ + (a) = ROTATE_LEFT((a), (s)); \ + \ +} + +#define HH(a, b, c, d, x, s) \ + { \ + (a) += H((b), (c), (d)) + (x) + (uint32_t)0x6ED9EBA1; \ + (a) = ROTATE_LEFT((a), (s)); \ + \ +} + +/* + * MD4 basic transformation. It transforms state based on block. + * + * This is a half md4 algorithm since Linux uses this algorithm for dir + * index. This function is derived from the RSA Data Security, Inc. MD4 + * Message-Digest Algorithm and was modified as necessary. + * + * The return value of this function is uint32_t in Linux, but actually we don't + * need to check this value, so in our version this function doesn't return any + * value. + */ +static void ext2_half_md4(uint32_t hash[4], uint32_t data[8]) +{ + uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3]; + + /* Round 1 */ + FF(a, b, c, d, data[0], 3); + FF(d, a, b, c, data[1], 7); + FF(c, d, a, b, data[2], 11); + FF(b, c, d, a, data[3], 19); + FF(a, b, c, d, data[4], 3); + FF(d, a, b, c, data[5], 7); + FF(c, d, a, b, data[6], 11); + FF(b, c, d, a, data[7], 19); + + /* Round 2 */ + GG(a, b, c, d, data[1], 3); + GG(d, a, b, c, data[3], 5); + GG(c, d, a, b, data[5], 9); + GG(b, c, d, a, data[7], 13); + GG(a, b, c, d, data[0], 3); + GG(d, a, b, c, data[2], 5); + GG(c, d, a, b, data[4], 9); + GG(b, c, d, a, data[6], 13); + + /* Round 3 */ + HH(a, b, c, d, data[3], 3); + HH(d, a, b, c, data[7], 9); + HH(c, d, a, b, data[2], 11); + HH(b, c, d, a, data[6], 15); + HH(a, b, c, d, data[1], 3); + HH(d, a, b, c, data[5], 9); + HH(c, d, a, b, data[0], 11); + HH(b, c, d, a, data[4], 15); + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; +} + +/* + * Tiny Encryption Algorithm. + */ +static void ext2_tea(uint32_t hash[4], uint32_t data[8]) +{ + uint32_t tea_delta = 0x9E3779B9; + uint32_t sum; + uint32_t x = hash[0], y = hash[1]; + int n = 16; + int i = 1; + + while (n-- > 0) { + sum = i * tea_delta; + x += ((y << 4) + data[0]) ^ (y + sum) ^ ((y >> 5) + data[1]); + y += ((x << 4) + data[2]) ^ (x + sum) ^ ((x >> 5) + data[3]); + i++; + } + + hash[0] += x; + hash[1] += y; +} + +static uint32_t ext2_legacy_hash(const char *name, int len, int unsigned_char) +{ + uint32_t h0, h1 = 0x12A3FE2D, h2 = 0x37ABE8F9; + uint32_t multi = 0x6D22F5; + const unsigned char *uname = (const unsigned char *)name; + const signed char *sname = (const signed char *)name; + int val, i; + + for (i = 0; i < len; i++) { + if (unsigned_char) + val = (unsigned int)*uname++; + else + val = (int)*sname++; + + h0 = h2 + (h1 ^ (val * multi)); + if (h0 & 0x80000000) + h0 -= 0x7FFFFFFF; + h2 = h1; + h1 = h0; + } + + return (h1 << 1); +} + +static void ext2_prep_hashbuf(const char *src, uint32_t slen, uint32_t *dst, + int dlen, int unsigned_char) +{ + uint32_t padding = slen | (slen << 8) | (slen << 16) | (slen << 24); + uint32_t buf_val; + int len, i; + int buf_byte; + const unsigned char *ubuf = (const unsigned char *)src; + const signed char *sbuf = (const signed char *)src; + + if (slen > (uint32_t)dlen) + len = dlen; + else + len = slen; + + buf_val = padding; + + for (i = 0; i < len; i++) { + if (unsigned_char) + buf_byte = (unsigned int)ubuf[i]; + else + buf_byte = (int)sbuf[i]; + + if ((i % 4) == 0) + buf_val = padding; + + buf_val <<= 8; + buf_val += buf_byte; + + if ((i % 4) == 3) { + *dst++ = buf_val; + dlen -= sizeof(uint32_t); + buf_val = padding; + } + } + + dlen -= sizeof(uint32_t); + if (dlen >= 0) + *dst++ = buf_val; + + dlen -= sizeof(uint32_t); + while (dlen >= 0) { + *dst++ = padding; + dlen -= sizeof(uint32_t); + } +} + +int ext2_htree_hash(const char *name, int len, const uint32_t *hash_seed, + int hash_version, uint32_t *hash_major, + uint32_t *hash_minor) +{ + uint32_t hash[4]; + uint32_t data[8]; + uint32_t major = 0, minor = 0; + int unsigned_char = 0; + + if (!name || !hash_major) + return (-1); + + if (len < 1 || len > 255) + goto error; + + hash[0] = 0x67452301; + hash[1] = 0xEFCDAB89; + hash[2] = 0x98BADCFE; + hash[3] = 0x10325476; + + if (hash_seed) + memcpy(hash, hash_seed, sizeof(hash)); + + switch (hash_version) { + case EXT2_HTREE_TEA_UNSIGNED: + unsigned_char = 1; + /* FALLTHRU */ + case EXT2_HTREE_TEA: + while (len > 0) { + ext2_prep_hashbuf(name, len, data, 16, unsigned_char); + ext2_tea(hash, data); + len -= 16; + name += 16; + } + major = hash[0]; + minor = hash[1]; + break; + case EXT2_HTREE_LEGACY_UNSIGNED: + unsigned_char = 1; + /* FALLTHRU */ + case EXT2_HTREE_LEGACY: + major = ext2_legacy_hash(name, len, unsigned_char); + break; + case EXT2_HTREE_HALF_MD4_UNSIGNED: + unsigned_char = 1; + /* FALLTHRU */ + case EXT2_HTREE_HALF_MD4: + while (len > 0) { + ext2_prep_hashbuf(name, len, data, 32, unsigned_char); + ext2_half_md4(hash, data); + len -= 32; + name += 32; + } + major = hash[1]; + minor = hash[2]; + break; + default: + goto error; + } + + major &= ~1; + if (major == (EXT2_HTREE_EOF << 1)) + major = (EXT2_HTREE_EOF - 1) << 1; + *hash_major = major; + if (hash_minor) + *hash_minor = minor; + + return EOK; + +error: + *hash_major = 0; + if (hash_minor) + *hash_minor = 0; + return ENOTSUP; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_ialloc.c b/Library/lwext4/src/ext4_ialloc.c new file mode 100644 index 0000000..f2c796f --- /dev/null +++ b/Library/lwext4/src/ext4_ialloc.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_ialloc.c + * @brief Inode allocation procedures. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/**@brief Convert i-node number to relative index in block group. + * @param sb Superblock + * @param inode I-node number to be converted + * @return Index of the i-node in the block group + */ +static uint32_t ext4_ialloc_inode_to_bgidx(struct ext4_sblock *sb, + uint32_t inode) +{ + uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group); + return (inode - 1) % inodes_per_group; +} + +/**@brief Convert relative index of i-node to absolute i-node number. + * @param sb Superblock + * @param index Index to be converted + * @return Absolute number of the i-node + * + */ +static uint32_t ext4_ialloc_bgidx_to_inode(struct ext4_sblock *sb, + uint32_t index, uint32_t bgid) +{ + uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group); + return bgid * inodes_per_group + (index + 1); +} + +/**@brief Compute block group number from the i-node number. + * @param sb Superblock + * @param inode I-node number to be found the block group for + * @return Block group number computed from i-node number + */ +static uint32_t ext4_ialloc_get_bgid_of_inode(struct ext4_sblock *sb, + uint32_t inode) +{ + uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group); + return (inode - 1) / inodes_per_group; +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_ialloc_bitmap_csum(struct ext4_sblock *sb, void *bitmap) +{ + uint32_t csum = 0; + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t inodes_per_group = + ext4_get32(sb, inodes_per_group); + + /* First calculate crc32 checksum against fs uuid */ + csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); + /* Then calculate crc32 checksum against inode bitmap */ + csum = ext4_crc32c(csum, bitmap, (inodes_per_group + 7) / 8); + } + return csum; +} +#else +#define ext4_ialloc_bitmap_csum(...) 0 +#endif + +void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg, + void *bitmap __unused) +{ + int desc_size = ext4_sb_get_desc_size(sb); + uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap); + uint16_t lo_csum = to_le16(csum & 0xFFFF), + hi_csum = to_le16(csum >> 16); + + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return; + + /* See if we need to assign a 32bit checksum */ + bg->inode_bitmap_csum_lo = lo_csum; + if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) + bg->inode_bitmap_csum_hi = hi_csum; + +} + +#if CONFIG_META_CSUM_ENABLE +static bool +ext4_ialloc_verify_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg, + void *bitmap __unused) +{ + + int desc_size = ext4_sb_get_desc_size(sb); + uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap); + uint16_t lo_csum = to_le16(csum & 0xFFFF), + hi_csum = to_le16(csum >> 16); + + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return true; + + if (bg->inode_bitmap_csum_lo != lo_csum) + return false; + + if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) + if (bg->inode_bitmap_csum_hi != hi_csum) + return false; + + return true; +} +#else +#define ext4_ialloc_verify_bitmap_csum(...) true +#endif + +int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir) +{ + struct ext4_sblock *sb = &fs->sb; + + /* Compute index of block group and load it */ + uint32_t block_group = ext4_ialloc_get_bgid_of_inode(sb, index); + + struct ext4_block_group_ref bg_ref; + int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref); + if (rc != EOK) + return rc; + + struct ext4_bgroup *bg = bg_ref.block_group; + + /* Load i-node bitmap */ + ext4_fsblk_t bitmap_block_addr = + ext4_bg_get_inode_bitmap(bg, sb); + + struct ext4_block b; + rc = ext4_trans_block_get(fs->bdev, &b, bitmap_block_addr); + if (rc != EOK) + return rc; + + if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) { + ext4_dbg(DEBUG_IALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + + /* Free i-node in the bitmap */ + uint32_t index_in_group = ext4_ialloc_inode_to_bgidx(sb, index); + ext4_bmap_bit_clr(b.data, index_in_group); + ext4_ialloc_set_bitmap_csum(sb, bg, b.data); + ext4_trans_set_block_dirty(b.buf); + + /* Put back the block with bitmap */ + rc = ext4_block_set(fs->bdev, &b); + if (rc != EOK) { + /* Error in saving bitmap */ + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + /* If released i-node is a directory, decrement used directories count + */ + if (is_dir) { + uint32_t bg_used_dirs = ext4_bg_get_used_dirs_count(bg, sb); + bg_used_dirs--; + ext4_bg_set_used_dirs_count(bg, sb, bg_used_dirs); + } + + /* Update block group free inodes count */ + uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb); + free_inodes++; + ext4_bg_set_free_inodes_count(bg, sb, free_inodes); + + bg_ref.dirty = true; + + /* Put back the modified block group */ + rc = ext4_fs_put_block_group_ref(&bg_ref); + if (rc != EOK) + return rc; + + /* Update superblock free inodes count */ + ext4_set32(sb, free_inodes_count, + ext4_get32(sb, free_inodes_count) + 1); + + return EOK; +} + +int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *idx, bool is_dir) +{ + struct ext4_sblock *sb = &fs->sb; + + uint32_t bgid = fs->last_inode_bg_id; + uint32_t bg_count = ext4_block_group_cnt(sb); + uint32_t sb_free_inodes = ext4_get32(sb, free_inodes_count); + bool rewind = false; + + /* Try to find free i-node in all block groups */ + while (bgid <= bg_count) { + + if (bgid == bg_count) { + if (rewind) + break; + bg_count = fs->last_inode_bg_id; + bgid = 0; + rewind = true; + continue; + } + + /* Load block group to check */ + struct ext4_block_group_ref bg_ref; + int rc = ext4_fs_get_block_group_ref(fs, bgid, &bg_ref); + if (rc != EOK) + return rc; + + struct ext4_bgroup *bg = bg_ref.block_group; + + /* Read necessary values for algorithm */ + uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb); + uint32_t used_dirs = ext4_bg_get_used_dirs_count(bg, sb); + + /* Check if this block group is good candidate for allocation */ + if (free_inodes > 0) { + /* Load block with bitmap */ + ext4_fsblk_t bmp_blk_add = ext4_bg_get_inode_bitmap(bg, sb); + + struct ext4_block b; + rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_add); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) { + ext4_dbg(DEBUG_IALLOC, + DBG_WARN "Bitmap checksum failed." + "Group: %" PRIu32"\n", + bg_ref.index); + } + + /* Try to allocate i-node in the bitmap */ + uint32_t inodes_in_bg; + uint32_t idx_in_bg; + + inodes_in_bg = ext4_inodes_in_group_cnt(sb, bgid); + rc = ext4_bmap_bit_find_clr(b.data, 0, inodes_in_bg, + &idx_in_bg); + /* Block group has not any free i-node */ + if (rc == ENOSPC) { + rc = ext4_block_set(fs->bdev, &b); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + rc = ext4_fs_put_block_group_ref(&bg_ref); + if (rc != EOK) + return rc; + + continue; + } + + ext4_bmap_bit_set(b.data, idx_in_bg); + + /* Free i-node found, save the bitmap */ + ext4_ialloc_set_bitmap_csum(sb,bg, + b.data); + ext4_trans_set_block_dirty(b.buf); + + ext4_block_set(fs->bdev, &b); + if (rc != EOK) { + ext4_fs_put_block_group_ref(&bg_ref); + return rc; + } + + /* Modify filesystem counters */ + free_inodes--; + ext4_bg_set_free_inodes_count(bg, sb, free_inodes); + + /* Increment used directories counter */ + if (is_dir) { + used_dirs++; + ext4_bg_set_used_dirs_count(bg, sb, used_dirs); + } + + /* Decrease unused inodes count */ + uint32_t unused = + ext4_bg_get_itable_unused(bg, sb); + + uint32_t free = inodes_in_bg - unused; + + if (idx_in_bg >= free) { + unused = inodes_in_bg - (idx_in_bg + 1); + ext4_bg_set_itable_unused(bg, sb, unused); + } + + /* Save modified block group */ + bg_ref.dirty = true; + + rc = ext4_fs_put_block_group_ref(&bg_ref); + if (rc != EOK) + return rc; + + /* Update superblock */ + sb_free_inodes--; + ext4_set32(sb, free_inodes_count, sb_free_inodes); + + /* Compute the absolute i-nodex number */ + *idx = ext4_ialloc_bgidx_to_inode(sb, idx_in_bg, bgid); + + fs->last_inode_bg_id = bgid; + + return EOK; + } + + /* Block group not modified, put it and jump to the next block + * group */ + ext4_fs_put_block_group_ref(&bg_ref); + if (rc != EOK) + return rc; + + ++bgid; + } + + return ENOSPC; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_inode.c b/Library/lwext4/src/ext4_inode.c new file mode 100644 index 0000000..ff3c234 --- /dev/null +++ b/Library/lwext4/src/ext4_inode.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_inode.c + * @brief Inode handle functions + */ + +#include +#include +#include +#include +#include + +#include +#include + +/**@brief Compute number of bits for block count. + * @param block_size Filesystem block_size + * @return Number of bits + */ +static uint32_t ext4_inode_block_bits_count(uint32_t block_size) +{ + uint32_t bits = 8; + uint32_t size = block_size; + + do { + bits++; + size = size >> 1; + } while (size > 256); + + return bits; +} + +uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode) +{ + uint32_t v = to_le16(inode->mode); + + if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD) { + v |= ((uint32_t)to_le16(inode->osd2.hurd2.mode_high)) << 16; + } + + return v; +} + +void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode, + uint32_t mode) +{ + inode->mode = to_le16((mode << 16) >> 16); + + if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD) + inode->osd2.hurd2.mode_high = to_le16(mode >> 16); +} + +uint32_t ext4_inode_get_uid(struct ext4_inode *inode) +{ + return to_le32(inode->uid); +} + +void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid) +{ + inode->uid = to_le32(uid); +} + +uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode) +{ + uint64_t v = to_le32(inode->size_lo); + + if ((ext4_get32(sb, rev_level) > 0) && + (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE))) + v |= ((uint64_t)to_le32(inode->size_hi)) << 32; + + return v; +} + +void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size) +{ + inode->size_lo = to_le32((size << 32) >> 32); + inode->size_hi = to_le32(size >> 32); +} + +uint32_t ext4_inode_get_csum(struct ext4_sblock *sb, struct ext4_inode *inode) +{ + uint16_t inode_size = ext4_get16(sb, inode_size); + uint32_t v = to_le16(inode->osd2.linux2.checksum_lo); + + if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) + v |= ((uint32_t)to_le16(inode->checksum_hi)) << 16; + + return v; +} + +void ext4_inode_set_csum(struct ext4_sblock *sb, struct ext4_inode *inode, + uint32_t checksum) +{ + uint16_t inode_size = ext4_get16(sb, inode_size); + inode->osd2.linux2.checksum_lo = + to_le16((checksum << 16) >> 16); + + if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) + inode->checksum_hi = to_le16(checksum >> 16); + +} + +uint32_t ext4_inode_get_access_time(struct ext4_inode *inode) +{ + return to_le32(inode->access_time); +} +void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time) +{ + inode->access_time = to_le32(time); +} + +uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode) +{ + return to_le32(inode->change_inode_time); +} +void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time) +{ + inode->change_inode_time = to_le32(time); +} + +uint32_t ext4_inode_get_modif_time(struct ext4_inode *inode) +{ + return to_le32(inode->modification_time); +} + +void ext4_inode_set_modif_time(struct ext4_inode *inode, uint32_t time) +{ + inode->modification_time = to_le32(time); +} + +uint32_t ext4_inode_get_del_time(struct ext4_inode *inode) +{ + return to_le32(inode->deletion_time); +} + +void ext4_inode_set_del_time(struct ext4_inode *inode, uint32_t time) +{ + inode->deletion_time = to_le32(time); +} + +uint32_t ext4_inode_get_gid(struct ext4_inode *inode) +{ + return to_le32(inode->gid); +} +void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid) +{ + inode->gid = to_le32(gid); +} + +uint16_t ext4_inode_get_links_cnt(struct ext4_inode *inode) +{ + return to_le16(inode->links_count); +} +void ext4_inode_set_links_cnt(struct ext4_inode *inode, uint16_t cnt) +{ + inode->links_count = to_le16(cnt); +} + +uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb, + struct ext4_inode *inode) +{ + uint64_t cnt = to_le32(inode->blocks_count_lo); + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE)) { + + /* 48-bit field */ + cnt |= (uint64_t)to_le16(inode->osd2.linux2.blocks_high) << 32; + + if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) { + + uint32_t block_count = ext4_sb_get_block_size(sb); + uint32_t b = ext4_inode_block_bits_count(block_count); + return cnt << (b - 9); + } + } + + return cnt; +} + +int ext4_inode_set_blocks_count(struct ext4_sblock *sb, + struct ext4_inode *inode, uint64_t count) +{ + /* 32-bit maximum */ + uint64_t max = 0; + max = ~max >> 32; + + if (count <= max) { + inode->blocks_count_lo = to_le32((uint32_t)count); + inode->osd2.linux2.blocks_high = 0; + ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE); + + return EOK; + } + + /* Check if there can be used huge files (many blocks) */ + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE)) + return EINVAL; + + /* 48-bit maximum */ + max = 0; + max = ~max >> 16; + + if (count <= max) { + inode->blocks_count_lo = to_le32((uint32_t)count); + inode->osd2.linux2.blocks_high = to_le16((uint16_t)(count >> 32)); + ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE); + } else { + uint32_t block_count = ext4_sb_get_block_size(sb); + uint32_t block_bits =ext4_inode_block_bits_count(block_count); + + ext4_inode_set_flag(inode, EXT4_INODE_FLAG_HUGE_FILE); + count = count >> (block_bits - 9); + inode->blocks_count_lo = to_le32((uint32_t)count); + inode->osd2.linux2.blocks_high = to_le16((uint16_t)(count >> 32)); + } + + return EOK; +} + +uint32_t ext4_inode_get_flags(struct ext4_inode *inode) +{ + return to_le32(inode->flags); +} +void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags) +{ + inode->flags = to_le32(flags); +} + +uint32_t ext4_inode_get_generation(struct ext4_inode *inode) +{ + return to_le32(inode->generation); +} +void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen) +{ + inode->generation = to_le32(gen); +} + +uint16_t ext4_inode_get_extra_isize(struct ext4_sblock *sb, + struct ext4_inode *inode) +{ + uint16_t inode_size = ext4_get16(sb, inode_size); + if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) + return to_le16(inode->extra_isize); + else + return 0; +} + +void ext4_inode_set_extra_isize(struct ext4_sblock *sb, + struct ext4_inode *inode, + uint16_t size) +{ + uint16_t inode_size = ext4_get16(sb, inode_size); + if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) + inode->extra_isize = to_le16(size); +} + +uint64_t ext4_inode_get_file_acl(struct ext4_inode *inode, + struct ext4_sblock *sb) +{ + uint64_t v = to_le32(inode->file_acl_lo); + + if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_LINUX) + v |= (uint32_t)to_le16(inode->osd2.linux2.file_acl_high) << 16; + + return v; +} + +void ext4_inode_set_file_acl(struct ext4_inode *inode, struct ext4_sblock *sb, + uint64_t acl) +{ + inode->file_acl_lo = to_le32((acl << 32) >> 32); + + if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_LINUX) + inode->osd2.linux2.file_acl_high = to_le16((uint16_t)(acl >> 32)); +} + +uint32_t ext4_inode_get_direct_block(struct ext4_inode *inode, uint32_t idx) +{ + return to_le32(inode->blocks[idx]); +} +void ext4_inode_set_direct_block(struct ext4_inode *inode, uint32_t idx, + uint32_t block) +{ + inode->blocks[idx] = to_le32(block); +} + +uint32_t ext4_inode_get_indirect_block(struct ext4_inode *inode, uint32_t idx) +{ + return to_le32(inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK]); +} + +void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx, + uint32_t block) +{ + inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK] = to_le32(block); +} + +uint32_t ext4_inode_get_dev(struct ext4_inode *inode) +{ + uint32_t dev_0, dev_1; + dev_0 = ext4_inode_get_direct_block(inode, 0); + dev_1 = ext4_inode_get_direct_block(inode, 1); + + if (dev_0) + return dev_0; + else + return dev_1; +} + +void ext4_inode_set_dev(struct ext4_inode *inode, uint32_t dev) +{ + if (dev & ~0xFFFF) + ext4_inode_set_direct_block(inode, 1, dev); + else + ext4_inode_set_direct_block(inode, 0, dev); +} + +uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode) +{ + return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK); +} + +bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode, + uint32_t type) +{ + return ext4_inode_type(sb, inode) == type; +} + +bool ext4_inode_has_flag(struct ext4_inode *inode, uint32_t f) +{ + return ext4_inode_get_flags(inode) & f; +} + +void ext4_inode_clear_flag(struct ext4_inode *inode, uint32_t f) +{ + uint32_t flags = ext4_inode_get_flags(inode); + flags = flags & (~f); + ext4_inode_set_flags(inode, flags); +} + +void ext4_inode_set_flag(struct ext4_inode *inode, uint32_t f) +{ + uint32_t flags = ext4_inode_get_flags(inode); + flags = flags | f; + ext4_inode_set_flags(inode, flags); +} + +bool ext4_inode_can_truncate(struct ext4_sblock *sb, struct ext4_inode *inode) +{ + if ((ext4_inode_has_flag(inode, EXT4_INODE_FLAG_APPEND)) || + (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_IMMUTABLE))) + return false; + + if ((ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)) || + (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_DIRECTORY)) || + (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_SOFTLINK))) + return true; + + return false; +} + +struct ext4_extent_header * +ext4_inode_get_extent_header(struct ext4_inode *inode) +{ + return (struct ext4_extent_header *)inode->blocks; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_journal.c b/Library/lwext4/src/ext4_journal.c new file mode 100644 index 0000000..9d998a1 --- /dev/null +++ b/Library/lwext4/src/ext4_journal.c @@ -0,0 +1,2292 @@ +/* + * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_journal.c + * @brief Journal handle functions + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/**@brief Revoke entry during journal replay.*/ +struct revoke_entry { + /**@brief Block number not to be replayed.*/ + ext4_fsblk_t block; + + /**@brief For any transaction id smaller + * than trans_id, records of @block + * in those transactions should not + * be replayed.*/ + uint32_t trans_id; + + /**@brief Revoke tree node.*/ + RB_ENTRY(revoke_entry) revoke_node; +}; + +/**@brief Valid journal replay information.*/ +struct recover_info { + /**@brief Starting transaction id.*/ + uint32_t start_trans_id; + + /**@brief Ending transaction id.*/ + uint32_t last_trans_id; + + /**@brief Used as internal argument.*/ + uint32_t this_trans_id; + + /**@brief No of transactions went through.*/ + uint32_t trans_cnt; + + /**@brief RB-Tree storing revoke entries.*/ + RB_HEAD(jbd_revoke, revoke_entry) revoke_root; +}; + +/**@brief Journal replay internal arguments.*/ +struct replay_arg { + /**@brief Journal replay information.*/ + struct recover_info *info; + + /**@brief Current block we are on.*/ + uint32_t *this_block; + + /**@brief Current trans_id we are on.*/ + uint32_t this_trans_id; +}; + +/* Make sure we wrap around the log correctly! */ +#define wrap(sb, var) \ +do { \ + if (var >= jbd_get32((sb), maxlen)) \ + var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \ +} while (0) + +static inline int32_t +trans_id_diff(uint32_t x, uint32_t y) +{ + int32_t diff = x - y; + return diff; +} + +static int +jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b) +{ + if (a->block > b->block) + return 1; + else if (a->block < b->block) + return -1; + return 0; +} + +static int +jbd_block_rec_cmp(struct jbd_block_rec *a, struct jbd_block_rec *b) +{ + if (a->lba > b->lba) + return 1; + else if (a->lba < b->lba) + return -1; + return 0; +} + +static int +jbd_revoke_rec_cmp(struct jbd_revoke_rec *a, struct jbd_revoke_rec *b) +{ + if (a->lba > b->lba) + return 1; + else if (a->lba < b->lba) + return -1; + return 0; +} + +RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node, + jbd_revoke_entry_cmp, static inline) +RB_GENERATE_INTERNAL(jbd_block, jbd_block_rec, block_rec_node, + jbd_block_rec_cmp, static inline) +RB_GENERATE_INTERNAL(jbd_revoke_tree, jbd_revoke_rec, revoke_node, + jbd_revoke_rec_cmp, static inline) + +#define jbd_alloc_revoke_entry() ext4_calloc(1, sizeof(struct revoke_entry)) +#define jbd_free_revoke_entry(addr) ext4_free(addr) + +static int jbd_has_csum(struct jbd_sb *jbd_sb) +{ + if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V2)) + return 2; + + if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V3)) + return 3; + + return 0; +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t jbd_sb_csum(struct jbd_sb *jbd_sb) +{ + uint32_t checksum = 0; + + if (jbd_has_csum(jbd_sb)) { + uint32_t orig_checksum = jbd_sb->checksum; + jbd_set32(jbd_sb, checksum, 0); + /* Calculate crc32c checksum against tho whole superblock */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_sb, + JBD_SUPERBLOCK_SIZE); + jbd_sb->checksum = orig_checksum; + } + return checksum; +} +#else +#define jbd_sb_csum(...) 0 +#endif + +static void jbd_sb_csum_set(struct jbd_sb *jbd_sb) +{ + if (!jbd_has_csum(jbd_sb)) + return; + + jbd_set32(jbd_sb, checksum, jbd_sb_csum(jbd_sb)); +} + +#if CONFIG_META_CSUM_ENABLE +static bool +jbd_verify_sb_csum(struct jbd_sb *jbd_sb) +{ + if (!jbd_has_csum(jbd_sb)) + return true; + + return jbd_sb_csum(jbd_sb) == jbd_get32(jbd_sb, checksum); +} +#else +#define jbd_verify_sb_csum(...) true +#endif + +#if CONFIG_META_CSUM_ENABLE +static uint32_t jbd_meta_csum(struct jbd_fs *jbd_fs, + struct jbd_bhdr *bhdr) +{ + uint32_t checksum = 0; + + if (jbd_has_csum(&jbd_fs->sb)) { + uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); + struct jbd_block_tail *tail = + (struct jbd_block_tail *)((char *)bhdr + block_size - + sizeof(struct jbd_block_tail)); + uint32_t orig_checksum = tail->checksum; + tail->checksum = 0; + + /* First calculate crc32c checksum against fs uuid */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, + sizeof(jbd_fs->sb.uuid)); + /* Calculate crc32c checksum against tho whole block */ + checksum = ext4_crc32c(checksum, bhdr, + block_size); + tail->checksum = orig_checksum; + } + return checksum; +} +#else +#define jbd_meta_csum(...) 0 +#endif + +static void jbd_meta_csum_set(struct jbd_fs *jbd_fs, + struct jbd_bhdr *bhdr) +{ + uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); + struct jbd_block_tail *tail = (struct jbd_block_tail *) + ((char *)bhdr + block_size - + sizeof(struct jbd_block_tail)); + if (!jbd_has_csum(&jbd_fs->sb)) + return; + + tail->checksum = to_be32(jbd_meta_csum(jbd_fs, bhdr)); +} + +#if CONFIG_META_CSUM_ENABLE +static bool +jbd_verify_meta_csum(struct jbd_fs *jbd_fs, + struct jbd_bhdr *bhdr) +{ + uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); + struct jbd_block_tail *tail = (struct jbd_block_tail *) + ((char *)bhdr + block_size - + sizeof(struct jbd_block_tail)); + if (!jbd_has_csum(&jbd_fs->sb)) + return true; + + return jbd_meta_csum(jbd_fs, bhdr) == to_be32(tail->checksum); +} +#else +#define jbd_verify_meta_csum(...) true +#endif + +#if CONFIG_META_CSUM_ENABLE +static uint32_t jbd_commit_csum(struct jbd_fs *jbd_fs, + struct jbd_commit_header *header) +{ + uint32_t checksum = 0; + + if (jbd_has_csum(&jbd_fs->sb)) { + uint8_t orig_checksum_type = header->chksum_type, + orig_checksum_size = header->chksum_size; + uint32_t orig_checksum = header->chksum[0]; + uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); + header->chksum_type = 0; + header->chksum_size = 0; + header->chksum[0] = 0; + + /* First calculate crc32c checksum against fs uuid */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, + sizeof(jbd_fs->sb.uuid)); + /* Calculate crc32c checksum against tho whole block */ + checksum = ext4_crc32c(checksum, header, + block_size); + + header->chksum_type = orig_checksum_type; + header->chksum_size = orig_checksum_size; + header->chksum[0] = orig_checksum; + } + return checksum; +} +#else +#define jbd_commit_csum(...) 0 +#endif + +static void jbd_commit_csum_set(struct jbd_fs *jbd_fs, + struct jbd_commit_header *header) +{ + if (!jbd_has_csum(&jbd_fs->sb)) + return; + + header->chksum_type = 0; + header->chksum_size = 0; + header->chksum[0] = jbd_commit_csum(jbd_fs, header); +} + +#if CONFIG_META_CSUM_ENABLE +static bool jbd_verify_commit_csum(struct jbd_fs *jbd_fs, + struct jbd_commit_header *header) +{ + if (!jbd_has_csum(&jbd_fs->sb)) + return true; + + return header->chksum[0] == to_be32(jbd_commit_csum(jbd_fs, + header)); +} +#else +#define jbd_verify_commit_csum(...) true +#endif + +#if CONFIG_META_CSUM_ENABLE +/* + * NOTE: We only make use of @csum parameter when + * JBD_FEATURE_COMPAT_CHECKSUM is enabled. + */ +static uint32_t jbd_block_csum(struct jbd_fs *jbd_fs, const void *buf, + uint32_t csum, + uint32_t sequence) +{ + uint32_t checksum = 0; + + if (jbd_has_csum(&jbd_fs->sb)) { + uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); + /* First calculate crc32c checksum against fs uuid */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, + sizeof(jbd_fs->sb.uuid)); + /* Then calculate crc32c checksum against sequence no. */ + checksum = ext4_crc32c(checksum, &sequence, + sizeof(uint32_t)); + /* Calculate crc32c checksum against tho whole block */ + checksum = ext4_crc32c(checksum, buf, + block_size); + } else if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_COMPAT_CHECKSUM)) { + uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); + /* Calculate crc32c checksum against tho whole block */ + checksum = ext4_crc32(csum, buf, + block_size); + } + return checksum; +} +#else +#define jbd_block_csum(...) 0 +#endif + +static void jbd_block_tag_csum_set(struct jbd_fs *jbd_fs, void *__tag, + uint32_t checksum) +{ + int ver = jbd_has_csum(&jbd_fs->sb); + if (!ver) + return; + + if (ver == 2) { + struct jbd_block_tag *tag = __tag; + tag->checksum = (uint16_t)to_be32(checksum); + } else { + struct jbd_block_tag3 *tag = __tag; + tag->checksum = to_be32(checksum); + } +} + +/**@brief Write jbd superblock to disk. + * @param jbd_fs jbd filesystem + * @param s jbd superblock + * @return standard error code*/ +static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s) +{ + int rc; + struct ext4_fs *fs = jbd_fs->inode_ref.fs; + uint64_t offset; + ext4_fsblk_t fblock; + rc = jbd_inode_bmap(jbd_fs, 0, &fblock); + if (rc != EOK) + return rc; + + jbd_sb_csum_set(s); + offset = fblock * ext4_sb_get_block_size(&fs->sb); + return ext4_block_writebytes(fs->bdev, offset, s, + EXT4_SUPERBLOCK_SIZE); +} + +/**@brief Read jbd superblock from disk. + * @param jbd_fs jbd filesystem + * @param s jbd superblock + * @return standard error code*/ +static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s) +{ + int rc; + struct ext4_fs *fs = jbd_fs->inode_ref.fs; + uint64_t offset; + ext4_fsblk_t fblock; + rc = jbd_inode_bmap(jbd_fs, 0, &fblock); + if (rc != EOK) + return rc; + + offset = fblock * ext4_sb_get_block_size(&fs->sb); + return ext4_block_readbytes(fs->bdev, offset, s, + EXT4_SUPERBLOCK_SIZE); +} + +/**@brief Verify jbd superblock. + * @param sb jbd superblock + * @return true if jbd superblock is valid */ +static bool jbd_verify_sb(struct jbd_sb *sb) +{ + struct jbd_bhdr *header = &sb->header; + if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) + return false; + + if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK && + jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2) + return false; + + return jbd_verify_sb_csum(sb); +} + +/**@brief Write back dirty jbd superblock to disk. + * @param jbd_fs jbd filesystem + * @return standard error code*/ +static int jbd_write_sb(struct jbd_fs *jbd_fs) +{ + int rc = EOK; + if (jbd_fs->dirty) { + rc = jbd_sb_write(jbd_fs, &jbd_fs->sb); + if (rc != EOK) + return rc; + + jbd_fs->dirty = false; + } + return rc; +} + +/**@brief Get reference to jbd filesystem. + * @param fs Filesystem to load journal of + * @param jbd_fs jbd filesystem + * @return standard error code*/ +int jbd_get_fs(struct ext4_fs *fs, + struct jbd_fs *jbd_fs) +{ + int rc; + uint32_t journal_ino; + + memset(jbd_fs, 0, sizeof(struct jbd_fs)); + /* See if there is journal inode on this filesystem.*/ + /* FIXME: detection on existance ofbkejournal bdev is + * missing.*/ + journal_ino = ext4_get32(&fs->sb, journal_inode_number); + + rc = ext4_fs_get_inode_ref(fs, + journal_ino, + &jbd_fs->inode_ref); + if (rc != EOK) + return rc; + + rc = jbd_sb_read(jbd_fs, &jbd_fs->sb); + if (rc != EOK) + goto Error; + + if (!jbd_verify_sb(&jbd_fs->sb)) { + rc = EIO; + goto Error; + } + + if (rc == EOK) + jbd_fs->bdev = fs->bdev; + + return rc; +Error: + ext4_fs_put_inode_ref(&jbd_fs->inode_ref); + memset(jbd_fs, 0, sizeof(struct jbd_fs)); + + return rc; +} + +/**@brief Put reference of jbd filesystem. + * @param jbd_fs jbd filesystem + * @return standard error code*/ +int jbd_put_fs(struct jbd_fs *jbd_fs) +{ + int rc = EOK; + rc = jbd_write_sb(jbd_fs); + + ext4_fs_put_inode_ref(&jbd_fs->inode_ref); + return rc; +} + +/**@brief Data block lookup helper. + * @param jbd_fs jbd filesystem + * @param iblock block index + * @param fblock logical block address + * @return standard error code*/ +int jbd_inode_bmap(struct jbd_fs *jbd_fs, + ext4_lblk_t iblock, + ext4_fsblk_t *fblock) +{ + int rc = ext4_fs_get_inode_dblk_idx( + &jbd_fs->inode_ref, + iblock, + fblock, + false); + return rc; +} + +/**@brief jbd block get function (through cache). + * @param jbd_fs jbd filesystem + * @param block block descriptor + * @param fblock jbd logical block address + * @return standard error code*/ +static int jbd_block_get(struct jbd_fs *jbd_fs, + struct ext4_block *block, + ext4_fsblk_t fblock) +{ + /* TODO: journal device. */ + int rc; + struct ext4_blockdev *bdev = jbd_fs->bdev; + ext4_lblk_t iblock = (ext4_lblk_t)fblock; + + /* Lookup the logical block address of + * fblock.*/ + rc = jbd_inode_bmap(jbd_fs, iblock, + &fblock); + if (rc != EOK) + return rc; + + rc = ext4_block_get(bdev, block, fblock); + + /* If succeeded, mark buffer as BC_FLUSH to indicate + * that data should be written to disk immediately.*/ + if (rc == EOK) { + ext4_bcache_set_flag(block->buf, BC_FLUSH); + /* As we don't want to occupy too much space + * in block cache, we set this buffer BC_TMP.*/ + ext4_bcache_set_flag(block->buf, BC_TMP); + } + + return rc; +} + +/**@brief jbd block get function (through cache, don't read). + * @param jbd_fs jbd filesystem + * @param block block descriptor + * @param fblock jbd logical block address + * @return standard error code*/ +static int jbd_block_get_noread(struct jbd_fs *jbd_fs, + struct ext4_block *block, + ext4_fsblk_t fblock) +{ + /* TODO: journal device. */ + int rc; + struct ext4_blockdev *bdev = jbd_fs->bdev; + ext4_lblk_t iblock = (ext4_lblk_t)fblock; + rc = jbd_inode_bmap(jbd_fs, iblock, + &fblock); + if (rc != EOK) + return rc; + + rc = ext4_block_get_noread(bdev, block, fblock); + if (rc == EOK) + ext4_bcache_set_flag(block->buf, BC_FLUSH); + + return rc; +} + +/**@brief jbd block set procedure (through cache). + * @param jbd_fs jbd filesystem + * @param block block descriptor + * @return standard error code*/ +static int jbd_block_set(struct jbd_fs *jbd_fs, + struct ext4_block *block) +{ + struct ext4_blockdev *bdev = jbd_fs->bdev; + return ext4_block_set(bdev, block); +} + +/**@brief helper functions to calculate + * block tag size, not including UUID part. + * @param jbd_fs jbd filesystem + * @return tag size in bytes*/ +static int jbd_tag_bytes(struct jbd_fs *jbd_fs) +{ + int size; + + /* It is very easy to deal with the case which + * JBD_FEATURE_INCOMPAT_CSUM_V3 is enabled.*/ + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_CSUM_V3)) + return sizeof(struct jbd_block_tag3); + + size = sizeof(struct jbd_block_tag); + + /* If JBD_FEATURE_INCOMPAT_CSUM_V2 is enabled, + * add 2 bytes to size.*/ + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_CSUM_V2)) + size += sizeof(uint16_t); + + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + return size; + + /* If block number is 4 bytes in size, + * minus 4 bytes from size */ + return size - sizeof(uint32_t); +} + +/**@brief Tag information. */ +struct tag_info { + /**@brief Tag size in bytes, including UUID part.*/ + int tag_bytes; + + /**@brief block number stored in this tag.*/ + ext4_fsblk_t block; + + /**@brief Is the first 4 bytes of block equals to + * JBD_MAGIC_NUMBER? */ + bool is_escape; + + /**@brief whether UUID part exists or not.*/ + bool uuid_exist; + + /**@brief UUID content if UUID part exists.*/ + uint8_t uuid[UUID_SIZE]; + + /**@brief Is this the last tag? */ + bool last_tag; + + /**@brief crc32c checksum. */ + uint32_t checksum; +}; + +/**@brief Extract information from a block tag. + * @param __tag pointer to the block tag + * @param tag_bytes block tag size of this jbd filesystem + * @param remaining size in buffer containing the block tag + * @param tag_info information of this tag. + * @return EOK when succeed, otherwise return EINVAL.*/ +static int +jbd_extract_block_tag(struct jbd_fs *jbd_fs, + void *__tag, + int tag_bytes, + int32_t remain_buf_size, + struct tag_info *tag_info) +{ + char *uuid_start; + tag_info->tag_bytes = tag_bytes; + tag_info->uuid_exist = false; + tag_info->last_tag = false; + tag_info->is_escape = false; + + /* See whether it is possible to hold a valid block tag.*/ + if (remain_buf_size - tag_bytes < 0) + return EINVAL; + + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_CSUM_V3)) { + struct jbd_block_tag3 *tag = __tag; + tag_info->block = jbd_get32(tag, blocknr); + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + tag_info->block |= + (uint64_t)jbd_get32(tag, blocknr_high) << 32; + + if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE) + tag_info->is_escape = true; + + if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) { + /* See whether it is possible to hold UUID part.*/ + if (remain_buf_size - tag_bytes < UUID_SIZE) + return EINVAL; + + uuid_start = (char *)tag + tag_bytes; + tag_info->uuid_exist = true; + tag_info->tag_bytes += UUID_SIZE; + memcpy(tag_info->uuid, uuid_start, UUID_SIZE); + } + + if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG) + tag_info->last_tag = true; + + } else { + struct jbd_block_tag *tag = __tag; + tag_info->block = jbd_get32(tag, blocknr); + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + tag_info->block |= + (uint64_t)jbd_get32(tag, blocknr_high) << 32; + + if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE) + tag_info->is_escape = true; + + if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) { + /* See whether it is possible to hold UUID part.*/ + if (remain_buf_size - tag_bytes < UUID_SIZE) + return EINVAL; + + uuid_start = (char *)tag + tag_bytes; + tag_info->uuid_exist = true; + tag_info->tag_bytes += UUID_SIZE; + memcpy(tag_info->uuid, uuid_start, UUID_SIZE); + } + + if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG) + tag_info->last_tag = true; + + } + return EOK; +} + +/**@brief Write information to a block tag. + * @param __tag pointer to the block tag + * @param remaining size in buffer containing the block tag + * @param tag_info information of this tag. + * @return EOK when succeed, otherwise return EINVAL.*/ +static int +jbd_write_block_tag(struct jbd_fs *jbd_fs, + void *__tag, + int32_t remain_buf_size, + struct tag_info *tag_info) +{ + char *uuid_start; + int tag_bytes = jbd_tag_bytes(jbd_fs); + + tag_info->tag_bytes = tag_bytes; + + /* See whether it is possible to hold a valid block tag.*/ + if (remain_buf_size - tag_bytes < 0) + return EINVAL; + + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_CSUM_V3)) { + struct jbd_block_tag3 *tag = __tag; + memset(tag, 0, sizeof(struct jbd_block_tag3)); + jbd_set32(tag, blocknr, (uint32_t)tag_info->block); + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + jbd_set32(tag, blocknr_high, tag_info->block >> 32); + + if (tag_info->uuid_exist) { + /* See whether it is possible to hold UUID part.*/ + if (remain_buf_size - tag_bytes < UUID_SIZE) + return EINVAL; + + uuid_start = (char *)tag + tag_bytes; + tag_info->tag_bytes += UUID_SIZE; + memcpy(uuid_start, tag_info->uuid, UUID_SIZE); + } else + jbd_set32(tag, flags, + jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID); + + jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum); + + if (tag_info->last_tag) + jbd_set32(tag, flags, + jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG); + + if (tag_info->is_escape) + jbd_set32(tag, flags, + jbd_get32(tag, flags) | JBD_FLAG_ESCAPE); + + } else { + struct jbd_block_tag *tag = __tag; + memset(tag, 0, sizeof(struct jbd_block_tag)); + jbd_set32(tag, blocknr, (uint32_t)tag_info->block); + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + jbd_set32(tag, blocknr_high, tag_info->block >> 32); + + if (tag_info->uuid_exist) { + /* See whether it is possible to hold UUID part.*/ + if (remain_buf_size - tag_bytes < UUID_SIZE) + return EINVAL; + + uuid_start = (char *)tag + tag_bytes; + tag_info->tag_bytes += UUID_SIZE; + memcpy(uuid_start, tag_info->uuid, UUID_SIZE); + } else + jbd_set16(tag, flags, + jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID); + + jbd_block_tag_csum_set(jbd_fs, __tag, tag_info->checksum); + + if (tag_info->last_tag) + jbd_set16(tag, flags, + jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG); + + + if (tag_info->is_escape) + jbd_set16(tag, flags, + jbd_get16(tag, flags) | JBD_FLAG_ESCAPE); + + } + return EOK; +} + +/**@brief Iterate all block tags in a block. + * @param jbd_fs jbd filesystem + * @param __tag_start pointer to the block + * @param tag_tbl_size size of the block + * @param func callback routine to indicate that + * a block tag is found + * @param arg additional argument to be passed to func */ +static void +jbd_iterate_block_table(struct jbd_fs *jbd_fs, + void *__tag_start, + int32_t tag_tbl_size, + void (*func)(struct jbd_fs * jbd_fs, + struct tag_info *tag_info, + void *arg), + void *arg) +{ + char *tag_start, *tag_ptr; + int tag_bytes = jbd_tag_bytes(jbd_fs); + tag_start = __tag_start; + tag_ptr = tag_start; + + /* Cut off the size of block tail storing checksum. */ + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_CSUM_V2) || + JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_CSUM_V3)) + tag_tbl_size -= sizeof(struct jbd_block_tail); + + while (tag_tbl_size) { + struct tag_info tag_info; + int rc = jbd_extract_block_tag(jbd_fs, + tag_ptr, + tag_bytes, + tag_tbl_size, + &tag_info); + if (rc != EOK) + break; + + if (func) + func(jbd_fs, &tag_info, arg); + + /* Stop the iteration when we reach the last tag. */ + if (tag_info.last_tag) + break; + + tag_ptr += tag_info.tag_bytes; + tag_tbl_size -= tag_info.tag_bytes; + } +} + +static void jbd_display_block_tags(struct jbd_fs *jbd_fs, + struct tag_info *tag_info, + void *arg) +{ + uint32_t *iblock = arg; + ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", tag_info->block); + (*iblock)++; + wrap(&jbd_fs->sb, *iblock); + (void)jbd_fs; + return; +} + +static struct revoke_entry * +jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block) +{ + struct revoke_entry tmp = { + .block = block + }; + + return RB_FIND(jbd_revoke, &info->revoke_root, &tmp); +} + +/**@brief Replay a block in a transaction. + * @param jbd_fs jbd filesystem + * @param tag_info tag_info of the logged block.*/ +static void jbd_replay_block_tags(struct jbd_fs *jbd_fs, + struct tag_info *tag_info, + void *__arg) +{ + int r; + struct replay_arg *arg = __arg; + struct recover_info *info = arg->info; + uint32_t *this_block = arg->this_block; + struct revoke_entry *revoke_entry; + struct ext4_block journal_block, ext4_block; + struct ext4_fs *fs = jbd_fs->inode_ref.fs; + + (*this_block)++; + wrap(&jbd_fs->sb, *this_block); + + /* We replay this block only if the current transaction id + * is equal or greater than that in revoke entry.*/ + revoke_entry = jbd_revoke_entry_lookup(info, tag_info->block); + if (revoke_entry && + trans_id_diff(arg->this_trans_id, revoke_entry->trans_id) <= 0) + return; + + ext4_dbg(DEBUG_JBD, + "Replaying block in block_tag: %" PRIu64 "\n", + tag_info->block); + + r = jbd_block_get(jbd_fs, &journal_block, *this_block); + if (r != EOK) + return; + + /* We need special treatment for ext4 superblock. */ + if (tag_info->block) { + r = ext4_block_get_noread(fs->bdev, &ext4_block, tag_info->block); + if (r != EOK) { + jbd_block_set(jbd_fs, &journal_block); + return; + } + + memcpy(ext4_block.data, + journal_block.data, + jbd_get32(&jbd_fs->sb, blocksize)); + + if (tag_info->is_escape) + ((struct jbd_bhdr *)ext4_block.data)->magic = + to_be32(JBD_MAGIC_NUMBER); + + ext4_bcache_set_dirty(ext4_block.buf); + ext4_block_set(fs->bdev, &ext4_block); + } else { + uint16_t mount_count, state; + mount_count = ext4_get16(&fs->sb, mount_count); + state = ext4_get16(&fs->sb, state); + + memcpy(&fs->sb, + journal_block.data + EXT4_SUPERBLOCK_OFFSET, + EXT4_SUPERBLOCK_SIZE); + + /* Mark system as mounted */ + ext4_set16(&fs->sb, state, state); + r = ext4_sb_write(fs->bdev, &fs->sb); + if (r != EOK) + return; + + /*Update mount count*/ + ext4_set16(&fs->sb, mount_count, mount_count); + } + + jbd_block_set(jbd_fs, &journal_block); + + return; +} + +/**@brief Add block address to revoke tree, along with + * its transaction id. + * @param info journal replay info + * @param block block address to be replayed.*/ +static void jbd_add_revoke_block_tags(struct recover_info *info, + ext4_fsblk_t block) +{ + struct revoke_entry *revoke_entry; + + ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block); + /* If the revoke entry with respect to the block address + * exists already, update its transaction id.*/ + revoke_entry = jbd_revoke_entry_lookup(info, block); + if (revoke_entry) { + revoke_entry->trans_id = info->this_trans_id; + return; + } + + revoke_entry = jbd_alloc_revoke_entry(); + ext4_assert(revoke_entry); + revoke_entry->block = block; + revoke_entry->trans_id = info->this_trans_id; + RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry); + + return; +} + +static void jbd_destroy_revoke_tree(struct recover_info *info) +{ + while (!RB_EMPTY(&info->revoke_root)) { + struct revoke_entry *revoke_entry = + RB_MIN(jbd_revoke, &info->revoke_root); + ext4_assert(revoke_entry); + RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry); + jbd_free_revoke_entry(revoke_entry); + } +} + + +#define ACTION_SCAN 0 +#define ACTION_REVOKE 1 +#define ACTION_RECOVER 2 + +/**@brief Add entries in a revoke block to revoke tree. + * @param jbd_fs jbd filesystem + * @param header revoke block header + * @param recover_info journal replay info*/ +static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs, + struct jbd_bhdr *header, + struct recover_info *info) +{ + char *blocks_entry; + struct jbd_revoke_header *revoke_hdr = + (struct jbd_revoke_header *)header; + uint32_t i, nr_entries, record_len = 4; + + /* If we are working on a 64bit jbd filesystem, */ + if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + record_len = 8; + + nr_entries = (jbd_get32(revoke_hdr, count) - + sizeof(struct jbd_revoke_header)) / + record_len; + + blocks_entry = (char *)(revoke_hdr + 1); + + for (i = 0;i < nr_entries;i++) { + if (record_len == 8) { + uint64_t *blocks = + (uint64_t *)blocks_entry; + jbd_add_revoke_block_tags(info, to_be64(*blocks)); + } else { + uint32_t *blocks = + (uint32_t *)blocks_entry; + jbd_add_revoke_block_tags(info, to_be32(*blocks)); + } + blocks_entry += record_len; + } +} + +static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs, + struct jbd_bhdr *header, + uint32_t *iblock) +{ + jbd_iterate_block_table(jbd_fs, + header + 1, + jbd_get32(&jbd_fs->sb, blocksize) - + sizeof(struct jbd_bhdr), + jbd_display_block_tags, + iblock); +} + +static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs, + struct jbd_bhdr *header, + struct replay_arg *arg) +{ + jbd_iterate_block_table(jbd_fs, + header + 1, + jbd_get32(&jbd_fs->sb, blocksize) - + sizeof(struct jbd_bhdr), + jbd_replay_block_tags, + arg); +} + +/**@brief The core routine of journal replay. + * @param jbd_fs jbd filesystem + * @param recover_info journal replay info + * @param action action needed to be taken + * @return standard error code*/ +static int jbd_iterate_log(struct jbd_fs *jbd_fs, + struct recover_info *info, + int action) +{ + int r = EOK; + bool log_end = false; + struct jbd_sb *sb = &jbd_fs->sb; + uint32_t start_trans_id, this_trans_id; + uint32_t start_block, this_block; + + /* We start iterating valid blocks in the whole journal.*/ + start_trans_id = this_trans_id = jbd_get32(sb, sequence); + start_block = this_block = jbd_get32(sb, start); + if (action == ACTION_SCAN) + info->trans_cnt = 0; + else if (!info->trans_cnt) + log_end = true; + + ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n", + start_trans_id); + + while (!log_end) { + struct ext4_block block; + struct jbd_bhdr *header; + /* If we are not scanning for the last + * valid transaction in the journal, + * we will stop when we reach the end of + * the journal.*/ + if (action != ACTION_SCAN) + if (trans_id_diff(this_trans_id, info->last_trans_id) > 0) { + log_end = true; + continue; + } + + r = jbd_block_get(jbd_fs, &block, this_block); + if (r != EOK) + break; + + header = (struct jbd_bhdr *)block.data; + /* This block does not have a valid magic number, + * so we have reached the end of the journal.*/ + if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) { + jbd_block_set(jbd_fs, &block); + log_end = true; + continue; + } + + /* If the transaction id we found is not expected, + * we may have reached the end of the journal. + * + * If we are not scanning the journal, something + * bad might have taken place. :-( */ + if (jbd_get32(header, sequence) != this_trans_id) { + if (action != ACTION_SCAN) + r = EIO; + + jbd_block_set(jbd_fs, &block); + log_end = true; + continue; + } + + switch (jbd_get32(header, blocktype)) { + case JBD_DESCRIPTOR_BLOCK: + if (!jbd_verify_meta_csum(jbd_fs, header)) { + ext4_dbg(DEBUG_JBD, + DBG_WARN "Descriptor block checksum failed." + "Journal block: %" PRIu32"\n", + this_block); + log_end = true; + break; + } + ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", " + "trans_id: %" PRIu32"\n", + this_block, this_trans_id); + if (action == ACTION_RECOVER) { + struct replay_arg replay_arg; + replay_arg.info = info; + replay_arg.this_block = &this_block; + replay_arg.this_trans_id = this_trans_id; + + jbd_replay_descriptor_block(jbd_fs, + header, &replay_arg); + } else + jbd_debug_descriptor_block(jbd_fs, + header, &this_block); + + break; + case JBD_COMMIT_BLOCK: + if (!jbd_verify_commit_csum(jbd_fs, + (struct jbd_commit_header *)header)) { + ext4_dbg(DEBUG_JBD, + DBG_WARN "Commit block checksum failed." + "Journal block: %" PRIu32"\n", + this_block); + log_end = true; + break; + } + ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", " + "trans_id: %" PRIu32"\n", + this_block, this_trans_id); + /* + * This is the end of a transaction, + * we may now proceed to the next transaction. + */ + this_trans_id++; + if (action == ACTION_SCAN) + info->trans_cnt++; + break; + case JBD_REVOKE_BLOCK: + if (!jbd_verify_meta_csum(jbd_fs, header)) { + ext4_dbg(DEBUG_JBD, + DBG_WARN "Revoke block checksum failed." + "Journal block: %" PRIu32"\n", + this_block); + log_end = true; + break; + } + ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", " + "trans_id: %" PRIu32"\n", + this_block, this_trans_id); + if (action == ACTION_REVOKE) { + info->this_trans_id = this_trans_id; + jbd_build_revoke_tree(jbd_fs, + header, info); + } + break; + default: + log_end = true; + break; + } + jbd_block_set(jbd_fs, &block); + this_block++; + wrap(sb, this_block); + if (this_block == start_block) + log_end = true; + + } + ext4_dbg(DEBUG_JBD, "End of journal.\n"); + if (r == EOK && action == ACTION_SCAN) { + /* We have finished scanning the journal. */ + info->start_trans_id = start_trans_id; + if (trans_id_diff(this_trans_id, start_trans_id) > 0) + info->last_trans_id = this_trans_id - 1; + else + info->last_trans_id = this_trans_id; + } + + return r; +} + +/**@brief Replay journal. + * @param jbd_fs jbd filesystem + * @return standard error code*/ +int jbd_recover(struct jbd_fs *jbd_fs) +{ + int r; + struct recover_info info; + struct jbd_sb *sb = &jbd_fs->sb; + if (!sb->start) + return EOK; + + RB_INIT(&info.revoke_root); + + r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN); + if (r != EOK) + return r; + + r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE); + if (r != EOK) + return r; + + r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER); + if (r == EOK) { + /* If we successfully replay the journal, + * clear EXT4_FINCOM_RECOVER flag on the + * ext4 superblock, and set the start of + * journal to 0.*/ + uint32_t features_incompatible = + ext4_get32(&jbd_fs->inode_ref.fs->sb, + features_incompatible); + jbd_set32(&jbd_fs->sb, start, 0); + jbd_set32(&jbd_fs->sb, sequence, info.last_trans_id); + features_incompatible &= ~EXT4_FINCOM_RECOVER; + ext4_set32(&jbd_fs->inode_ref.fs->sb, + features_incompatible, + features_incompatible); + jbd_fs->dirty = true; + r = ext4_sb_write(jbd_fs->bdev, + &jbd_fs->inode_ref.fs->sb); + } + jbd_destroy_revoke_tree(&info); + return r; +} + +static void jbd_journal_write_sb(struct jbd_journal *journal) +{ + struct jbd_fs *jbd_fs = journal->jbd_fs; + jbd_set32(&jbd_fs->sb, start, journal->start); + jbd_set32(&jbd_fs->sb, sequence, journal->trans_id); + jbd_fs->dirty = true; +} + +/**@brief Start accessing the journal. + * @param jbd_fs jbd filesystem + * @param journal current journal session + * @return standard error code*/ +int jbd_journal_start(struct jbd_fs *jbd_fs, + struct jbd_journal *journal) +{ + int r; + uint32_t features_incompatible = + ext4_get32(&jbd_fs->inode_ref.fs->sb, + features_incompatible); + features_incompatible |= EXT4_FINCOM_RECOVER; + ext4_set32(&jbd_fs->inode_ref.fs->sb, + features_incompatible, + features_incompatible); + r = ext4_sb_write(jbd_fs->bdev, + &jbd_fs->inode_ref.fs->sb); + if (r != EOK) + return r; + + journal->first = jbd_get32(&jbd_fs->sb, first); + journal->start = journal->first; + journal->last = journal->first; + /* + * To invalidate any stale records we need to start from + * the checkpoint transaction ID of the previous journalling session + * plus 1. + */ + journal->trans_id = jbd_get32(&jbd_fs->sb, sequence) + 1; + journal->alloc_trans_id = journal->trans_id; + + journal->block_size = jbd_get32(&jbd_fs->sb, blocksize); + + TAILQ_INIT(&journal->cp_queue); + RB_INIT(&journal->block_rec_root); + journal->jbd_fs = jbd_fs; + jbd_journal_write_sb(journal); + r = jbd_write_sb(jbd_fs); + if (r != EOK) + return r; + + jbd_fs->bdev->journal = journal; + return EOK; +} + +static void jbd_trans_end_write(struct ext4_bcache *bc __unused, + struct ext4_buf *buf __unused, + int res, + void *arg); + +/* + * This routine is only suitable to committed transactions. */ +static void jbd_journal_flush_trans(struct jbd_trans *trans) +{ + struct jbd_buf *jbd_buf, *tmp; + struct jbd_journal *journal = trans->journal; + struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs; + void *tmp_data = ext4_malloc(journal->block_size); + ext4_assert(tmp_data); + + TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, + tmp) { + struct ext4_buf *buf; + struct ext4_block block; + /* The buffer is not yet flushed. */ + buf = ext4_bcache_find_get(fs->bdev->bc, &block, + jbd_buf->block_rec->lba); + if (!(buf && ext4_bcache_test_flag(buf, BC_UPTODATE) && + jbd_buf->block_rec->trans == trans)) { + int r; + struct ext4_block jbd_block = EXT4_BLOCK_ZERO(); + r = jbd_block_get(journal->jbd_fs, + &jbd_block, + jbd_buf->jbd_lba); + ext4_assert(r == EOK); + memcpy(tmp_data, jbd_block.data, + journal->block_size); + ext4_block_set(fs->bdev, &jbd_block); + r = ext4_blocks_set_direct(fs->bdev, tmp_data, + jbd_buf->block_rec->lba, 1); + jbd_trans_end_write(fs->bdev->bc, buf, r, jbd_buf); + } else + ext4_block_flush_buf(fs->bdev, buf); + + if (buf) + ext4_block_set(fs->bdev, &block); + } + + ext4_free(tmp_data); +} + +static void +jbd_journal_skip_pure_revoke(struct jbd_journal *journal, + struct jbd_trans *trans) +{ + journal->start = trans->start_iblock + + trans->alloc_blocks; + wrap(&journal->jbd_fs->sb, journal->start); + journal->trans_id = trans->trans_id + 1; + jbd_journal_free_trans(journal, + trans, false); + jbd_journal_write_sb(journal); +} + +void +jbd_journal_purge_cp_trans(struct jbd_journal *journal, + bool flush, + bool once) +{ + struct jbd_trans *trans; + while ((trans = TAILQ_FIRST(&journal->cp_queue))) { + if (!trans->data_cnt) { + TAILQ_REMOVE(&journal->cp_queue, + trans, + trans_node); + jbd_journal_skip_pure_revoke(journal, trans); + } else { + if (trans->data_cnt == + trans->written_cnt) { + journal->start = + trans->start_iblock + + trans->alloc_blocks; + wrap(&journal->jbd_fs->sb, + journal->start); + journal->trans_id = + trans->trans_id + 1; + TAILQ_REMOVE(&journal->cp_queue, + trans, + trans_node); + jbd_journal_free_trans(journal, + trans, + false); + jbd_journal_write_sb(journal); + } else if (!flush) { + journal->start = + trans->start_iblock; + wrap(&journal->jbd_fs->sb, + journal->start); + journal->trans_id = + trans->trans_id; + jbd_journal_write_sb(journal); + break; + } else + jbd_journal_flush_trans(trans); + } + if (once) + break; + } +} + +/**@brief Stop accessing the journal. + * @param journal current journal session + * @return standard error code*/ +int jbd_journal_stop(struct jbd_journal *journal) +{ + int r; + struct jbd_fs *jbd_fs = journal->jbd_fs; + uint32_t features_incompatible; + + /* Make sure that journalled content have reached + * the disk.*/ + jbd_journal_purge_cp_trans(journal, true, false); + + /* There should be no block record in this journal + * session. */ + if (!RB_EMPTY(&journal->block_rec_root)) + ext4_dbg(DEBUG_JBD, + DBG_WARN "There are still block records " + "in this journal session!\n"); + + features_incompatible = + ext4_get32(&jbd_fs->inode_ref.fs->sb, + features_incompatible); + features_incompatible &= ~EXT4_FINCOM_RECOVER; + ext4_set32(&jbd_fs->inode_ref.fs->sb, + features_incompatible, + features_incompatible); + r = ext4_sb_write(jbd_fs->bdev, + &jbd_fs->inode_ref.fs->sb); + if (r != EOK) + return r; + + journal->start = 0; + journal->trans_id = 0; + jbd_journal_write_sb(journal); + return jbd_write_sb(journal->jbd_fs); +} + +/**@brief Allocate a block in the journal. + * @param journal current journal session + * @param trans transaction + * @return allocated block address*/ +static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal, + struct jbd_trans *trans) +{ + uint32_t start_block; + + start_block = journal->last++; + trans->alloc_blocks++; + wrap(&journal->jbd_fs->sb, journal->last); + + /* If there is no space left, flush just one journalled + * transaction.*/ + if (journal->last == journal->start) { + jbd_journal_purge_cp_trans(journal, true, true); + ext4_assert(journal->last != journal->start); + } + + return start_block; +} + +static struct jbd_block_rec * +jbd_trans_block_rec_lookup(struct jbd_journal *journal, + ext4_fsblk_t lba) +{ + struct jbd_block_rec tmp = { + .lba = lba + }; + + return RB_FIND(jbd_block, + &journal->block_rec_root, + &tmp); +} + +static void +jbd_trans_change_ownership(struct jbd_block_rec *block_rec, + struct jbd_trans *new_trans) +{ + LIST_REMOVE(block_rec, tbrec_node); + if (new_trans) { + /* Now this block record belongs to this transaction. */ + LIST_INSERT_HEAD(&new_trans->tbrec_list, block_rec, tbrec_node); + } + block_rec->trans = new_trans; +} + +static inline struct jbd_block_rec * +jbd_trans_insert_block_rec(struct jbd_trans *trans, + ext4_fsblk_t lba) +{ + struct jbd_block_rec *block_rec; + block_rec = jbd_trans_block_rec_lookup(trans->journal, lba); + if (block_rec) { + jbd_trans_change_ownership(block_rec, trans); + return block_rec; + } + block_rec = ext4_calloc(1, sizeof(struct jbd_block_rec)); + if (!block_rec) + return NULL; + + block_rec->lba = lba; + block_rec->trans = trans; + TAILQ_INIT(&block_rec->dirty_buf_queue); + LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node); + RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec); + return block_rec; +} + +/* + * This routine will do the dirty works. + */ +static void +jbd_trans_finish_callback(struct jbd_journal *journal, + const struct jbd_trans *trans, + struct jbd_block_rec *block_rec, + bool abort, + bool revoke) +{ + struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs; + if (block_rec->trans != trans) + return; + + if (!abort) { + struct jbd_buf *jbd_buf, *tmp; + TAILQ_FOREACH_SAFE(jbd_buf, + &block_rec->dirty_buf_queue, + dirty_buf_node, + tmp) { + jbd_trans_end_write(fs->bdev->bc, + NULL, + EOK, + jbd_buf); + } + } else { + /* + * We have to roll back data if the block is going to be + * aborted. + */ + struct jbd_buf *jbd_buf; + struct ext4_block jbd_block = EXT4_BLOCK_ZERO(), + block = EXT4_BLOCK_ZERO(); + jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue, + jbd_buf_dirty); + if (jbd_buf) { + if (!revoke) { + int r; + r = ext4_block_get_noread(fs->bdev, + &block, + block_rec->lba); + ext4_assert(r == EOK); + r = jbd_block_get(journal->jbd_fs, + &jbd_block, + jbd_buf->jbd_lba); + ext4_assert(r == EOK); + memcpy(block.data, jbd_block.data, + journal->block_size); + + jbd_trans_change_ownership(block_rec, + jbd_buf->trans); + + block.buf->end_write = jbd_trans_end_write; + block.buf->end_write_arg = jbd_buf; + + ext4_bcache_set_flag(jbd_block.buf, BC_TMP); + ext4_bcache_set_dirty(block.buf); + + ext4_block_set(fs->bdev, &jbd_block); + ext4_block_set(fs->bdev, &block); + return; + } else { + /* The revoked buffer is yet written. */ + jbd_trans_change_ownership(block_rec, + jbd_buf->trans); + } + } + } +} + +static inline void +jbd_trans_remove_block_rec(struct jbd_journal *journal, + struct jbd_block_rec *block_rec, + struct jbd_trans *trans) +{ + /* If this block record doesn't belong to this transaction, + * give up.*/ + if (block_rec->trans == trans) { + LIST_REMOVE(block_rec, tbrec_node); + RB_REMOVE(jbd_block, + &journal->block_rec_root, + block_rec); + ext4_free(block_rec); + } +} + +/**@brief Add block to a transaction and mark it dirty. + * @param trans transaction + * @param block block descriptor + * @return standard error code*/ +int jbd_trans_set_block_dirty(struct jbd_trans *trans, + struct ext4_block *block) +{ + struct jbd_buf *jbd_buf; + struct jbd_revoke_rec *rec, tmp_rec = { + .lba = block->lb_id + }; + struct jbd_block_rec *block_rec; + + if (block->buf->end_write == jbd_trans_end_write) { + jbd_buf = block->buf->end_write_arg; + if (jbd_buf && jbd_buf->trans == trans) + return EOK; + } + jbd_buf = ext4_calloc(1, sizeof(struct jbd_buf)); + if (!jbd_buf) + return ENOMEM; + + if ((block_rec = jbd_trans_insert_block_rec(trans, + block->lb_id)) == NULL) { + ext4_free(jbd_buf); + return ENOMEM; + } + + TAILQ_INSERT_TAIL(&block_rec->dirty_buf_queue, + jbd_buf, + dirty_buf_node); + + jbd_buf->block_rec = block_rec; + jbd_buf->trans = trans; + jbd_buf->block = *block; + ext4_bcache_inc_ref(block->buf); + + /* If the content reach the disk, notify us + * so that we may do a checkpoint. */ + block->buf->end_write = jbd_trans_end_write; + block->buf->end_write_arg = jbd_buf; + + trans->data_cnt++; + TAILQ_INSERT_HEAD(&trans->buf_queue, jbd_buf, buf_node); + + ext4_bcache_set_dirty(block->buf); + rec = RB_FIND(jbd_revoke_tree, + &trans->revoke_root, + &tmp_rec); + if (rec) { + RB_REMOVE(jbd_revoke_tree, &trans->revoke_root, + rec); + ext4_free(rec); + } + + return EOK; +} + +/**@brief Add block to be revoked to a transaction + * @param trans transaction + * @param lba logical block address + * @return standard error code*/ +int jbd_trans_revoke_block(struct jbd_trans *trans, + ext4_fsblk_t lba) +{ + struct jbd_revoke_rec tmp_rec = { + .lba = lba + }, *rec; + rec = RB_FIND(jbd_revoke_tree, + &trans->revoke_root, + &tmp_rec); + if (rec) + return EOK; + + rec = ext4_calloc(1, sizeof(struct jbd_revoke_rec)); + if (!rec) + return ENOMEM; + + rec->lba = lba; + RB_INSERT(jbd_revoke_tree, &trans->revoke_root, rec); + return EOK; +} + +/**@brief Try to add block to be revoked to a transaction. + * If @lba still remains in an transaction on checkpoint + * queue, add @lba as a revoked block to the transaction. + * @param trans transaction + * @param lba logical block address + * @return standard error code*/ +int jbd_trans_try_revoke_block(struct jbd_trans *trans, + ext4_fsblk_t lba) +{ + struct jbd_journal *journal = trans->journal; + struct jbd_block_rec *block_rec = + jbd_trans_block_rec_lookup(journal, lba); + + if (block_rec) { + if (block_rec->trans == trans) { + struct jbd_buf *jbd_buf = + TAILQ_LAST(&block_rec->dirty_buf_queue, + jbd_buf_dirty); + /* If there are still unwritten buffers. */ + if (TAILQ_FIRST(&block_rec->dirty_buf_queue) != + jbd_buf) + jbd_trans_revoke_block(trans, lba); + + } else + jbd_trans_revoke_block(trans, lba); + } + + return EOK; +} + +/**@brief Free a transaction + * @param journal current journal session + * @param trans transaction + * @param abort discard all the modifications on the block? + * @return standard error code*/ +void jbd_journal_free_trans(struct jbd_journal *journal, + struct jbd_trans *trans, + bool abort) +{ + struct jbd_buf *jbd_buf, *tmp; + struct jbd_revoke_rec *rec, *tmp2; + struct jbd_block_rec *block_rec, *tmp3; + struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs; + TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, + tmp) { + block_rec = jbd_buf->block_rec; + if (abort) { + jbd_buf->block.buf->end_write = NULL; + jbd_buf->block.buf->end_write_arg = NULL; + ext4_bcache_clear_dirty(jbd_buf->block.buf); + ext4_block_set(fs->bdev, &jbd_buf->block); + } + + TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue, + jbd_buf, + dirty_buf_node); + jbd_trans_finish_callback(journal, + trans, + block_rec, + abort, + false); + TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node); + ext4_free(jbd_buf); + } + RB_FOREACH_SAFE(rec, jbd_revoke_tree, &trans->revoke_root, + tmp2) { + RB_REMOVE(jbd_revoke_tree, &trans->revoke_root, rec); + ext4_free(rec); + } + LIST_FOREACH_SAFE(block_rec, &trans->tbrec_list, tbrec_node, + tmp3) { + jbd_trans_remove_block_rec(journal, block_rec, trans); + } + + ext4_free(trans); +} + +/**@brief Write commit block for a transaction + * @param trans transaction + * @return standard error code*/ +static int jbd_trans_write_commit_block(struct jbd_trans *trans) +{ + int rc; + struct ext4_block block; + struct jbd_commit_header *header; + uint32_t commit_iblock; + struct jbd_journal *journal = trans->journal; + + commit_iblock = jbd_journal_alloc_block(journal, trans); + + rc = jbd_block_get_noread(journal->jbd_fs, &block, commit_iblock); + if (rc != EOK) + return rc; + + header = (struct jbd_commit_header *)block.data; + jbd_set32(&header->header, magic, JBD_MAGIC_NUMBER); + jbd_set32(&header->header, blocktype, JBD_COMMIT_BLOCK); + jbd_set32(&header->header, sequence, trans->trans_id); + + if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb, + JBD_FEATURE_COMPAT_CHECKSUM)) { + header->chksum_type = JBD_CRC32_CHKSUM; + header->chksum_size = JBD_CRC32_CHKSUM_SIZE; + jbd_set32(header, chksum[0], trans->data_csum); + } + jbd_commit_csum_set(journal->jbd_fs, header); + ext4_bcache_set_dirty(block.buf); + ext4_bcache_set_flag(block.buf, BC_TMP); + rc = jbd_block_set(journal->jbd_fs, &block); + return rc; +} + +/**@brief Write descriptor block for a transaction + * @param journal current journal session + * @param trans transaction + * @return standard error code*/ +static int jbd_journal_prepare(struct jbd_journal *journal, + struct jbd_trans *trans) +{ + int rc = EOK, i = 0; + struct ext4_block desc_block = EXT4_BLOCK_ZERO(), + data_block = EXT4_BLOCK_ZERO(); + int32_t tag_tbl_size = 0; + uint32_t desc_iblock = 0; + uint32_t data_iblock = 0; + char *tag_start = NULL, *tag_ptr = NULL; + struct jbd_buf *jbd_buf, *tmp; + struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs; + uint32_t checksum = EXT4_CRC32_INIT; + struct jbd_bhdr *bhdr = NULL; + void *data; + + /* Try to remove any non-dirty buffers from the tail of + * buf_queue. */ + TAILQ_FOREACH_REVERSE_SAFE(jbd_buf, &trans->buf_queue, + jbd_trans_buf, buf_node, tmp) { + struct jbd_revoke_rec tmp_rec = { + .lba = jbd_buf->block_rec->lba + }; + /* We stop the iteration when we find a dirty buffer. */ + if (ext4_bcache_test_flag(jbd_buf->block.buf, + BC_DIRTY)) + break; + + TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue, + jbd_buf, + dirty_buf_node); + + jbd_buf->block.buf->end_write = NULL; + jbd_buf->block.buf->end_write_arg = NULL; + jbd_trans_finish_callback(journal, + trans, + jbd_buf->block_rec, + true, + RB_FIND(jbd_revoke_tree, + &trans->revoke_root, + &tmp_rec)); + jbd_trans_remove_block_rec(journal, + jbd_buf->block_rec, trans); + trans->data_cnt--; + + ext4_block_set(fs->bdev, &jbd_buf->block); + TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node); + ext4_free(jbd_buf); + } + + TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, tmp) { + struct tag_info tag_info; + bool uuid_exist = false; + bool is_escape = false; + struct jbd_revoke_rec tmp_rec = { + .lba = jbd_buf->block_rec->lba + }; + if (!ext4_bcache_test_flag(jbd_buf->block.buf, + BC_DIRTY)) { + TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue, + jbd_buf, + dirty_buf_node); + + jbd_buf->block.buf->end_write = NULL; + jbd_buf->block.buf->end_write_arg = NULL; + + /* The buffer has not been modified, just release + * that jbd_buf. */ + jbd_trans_finish_callback(journal, + trans, + jbd_buf->block_rec, + true, + RB_FIND(jbd_revoke_tree, + &trans->revoke_root, + &tmp_rec)); + jbd_trans_remove_block_rec(journal, + jbd_buf->block_rec, trans); + trans->data_cnt--; + + ext4_block_set(fs->bdev, &jbd_buf->block); + TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node); + ext4_free(jbd_buf); + continue; + } + checksum = jbd_block_csum(journal->jbd_fs, + jbd_buf->block.data, + checksum, + trans->trans_id); + if (((struct jbd_bhdr *)jbd_buf->block.data)->magic == + to_be32(JBD_MAGIC_NUMBER)) + is_escape = true; + +again: + if (!desc_iblock) { + desc_iblock = jbd_journal_alloc_block(journal, trans); + rc = jbd_block_get_noread(journal->jbd_fs, &desc_block, desc_iblock); + if (rc != EOK) + break; + + bhdr = (struct jbd_bhdr *)desc_block.data; + jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER); + jbd_set32(bhdr, blocktype, JBD_DESCRIPTOR_BLOCK); + jbd_set32(bhdr, sequence, trans->trans_id); + + tag_start = (char *)(bhdr + 1); + tag_ptr = tag_start; + uuid_exist = true; + tag_tbl_size = journal->block_size - + sizeof(struct jbd_bhdr); + + if (jbd_has_csum(&journal->jbd_fs->sb)) + tag_tbl_size -= sizeof(struct jbd_block_tail); + + if (!trans->start_iblock) + trans->start_iblock = desc_iblock; + + ext4_bcache_set_dirty(desc_block.buf); + ext4_bcache_set_flag(desc_block.buf, BC_TMP); + } + tag_info.block = jbd_buf->block.lb_id; + tag_info.uuid_exist = uuid_exist; + tag_info.is_escape = is_escape; + if (i == trans->data_cnt - 1) + tag_info.last_tag = true; + else + tag_info.last_tag = false; + + tag_info.checksum = checksum; + + if (uuid_exist) + memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid, + UUID_SIZE); + + rc = jbd_write_block_tag(journal->jbd_fs, + tag_ptr, + tag_tbl_size, + &tag_info); + if (rc != EOK) { + jbd_meta_csum_set(journal->jbd_fs, bhdr); + desc_iblock = 0; + rc = jbd_block_set(journal->jbd_fs, &desc_block); + if (rc != EOK) + break; + + goto again; + } + + data_iblock = jbd_journal_alloc_block(journal, trans); + rc = jbd_block_get_noread(journal->jbd_fs, &data_block, data_iblock); + if (rc != EOK) { + desc_iblock = 0; + ext4_bcache_clear_dirty(desc_block.buf); + jbd_block_set(journal->jbd_fs, &desc_block); + break; + } + + data = data_block.data; + memcpy(data, jbd_buf->block.data, + journal->block_size); + if (is_escape) + ((struct jbd_bhdr *)data)->magic = 0; + + ext4_bcache_set_dirty(data_block.buf); + ext4_bcache_set_flag(data_block.buf, BC_TMP); + rc = jbd_block_set(journal->jbd_fs, &data_block); + if (rc != EOK) { + desc_iblock = 0; + ext4_bcache_clear_dirty(desc_block.buf); + jbd_block_set(journal->jbd_fs, &desc_block); + break; + } + jbd_buf->jbd_lba = data_iblock; + + tag_ptr += tag_info.tag_bytes; + tag_tbl_size -= tag_info.tag_bytes; + + i++; + } + if (rc == EOK && desc_iblock) { + jbd_meta_csum_set(journal->jbd_fs, + (struct jbd_bhdr *)bhdr); + trans->data_csum = checksum; + rc = jbd_block_set(journal->jbd_fs, &desc_block); + } + + return rc; +} + +/**@brief Write revoke block for a transaction + * @param journal current journal session + * @param trans transaction + * @return standard error code*/ +static int +jbd_journal_prepare_revoke(struct jbd_journal *journal, + struct jbd_trans *trans) +{ + int rc = EOK, i = 0; + struct ext4_block desc_block = EXT4_BLOCK_ZERO(); + int32_t tag_tbl_size = 0; + uint32_t desc_iblock = 0; + char *blocks_entry = NULL; + struct jbd_revoke_rec *rec, *tmp; + struct jbd_revoke_header *header = NULL; + int32_t record_len = 4; + struct jbd_bhdr *bhdr = NULL; + + if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb, + JBD_FEATURE_INCOMPAT_64BIT)) + record_len = 8; + + RB_FOREACH_SAFE(rec, jbd_revoke_tree, &trans->revoke_root, + tmp) { +again: + if (!desc_iblock) { + desc_iblock = jbd_journal_alloc_block(journal, trans); + rc = jbd_block_get_noread(journal->jbd_fs, &desc_block, + desc_iblock); + if (rc != EOK) + break; + + bhdr = (struct jbd_bhdr *)desc_block.data; + jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER); + jbd_set32(bhdr, blocktype, JBD_REVOKE_BLOCK); + jbd_set32(bhdr, sequence, trans->trans_id); + + header = (struct jbd_revoke_header *)bhdr; + blocks_entry = (char *)(header + 1); + tag_tbl_size = journal->block_size - + sizeof(struct jbd_revoke_header); + + if (jbd_has_csum(&journal->jbd_fs->sb)) + tag_tbl_size -= sizeof(struct jbd_block_tail); + + if (!trans->start_iblock) + trans->start_iblock = desc_iblock; + + ext4_bcache_set_dirty(desc_block.buf); + ext4_bcache_set_flag(desc_block.buf, BC_TMP); + } + + if (tag_tbl_size < record_len) { + jbd_set32(header, count, + journal->block_size - tag_tbl_size); + jbd_meta_csum_set(journal->jbd_fs, bhdr); + bhdr = NULL; + desc_iblock = 0; + header = NULL; + rc = jbd_block_set(journal->jbd_fs, &desc_block); + if (rc != EOK) + break; + + goto again; + } + if (record_len == 8) { + uint64_t *blocks = + (uint64_t *)blocks_entry; + *blocks = to_be64(rec->lba); + } else { + uint32_t *blocks = + (uint32_t *)blocks_entry; + *blocks = to_be32((uint32_t)rec->lba); + } + blocks_entry += record_len; + tag_tbl_size -= record_len; + + i++; + } + if (rc == EOK && desc_iblock) { + if (header != NULL) + jbd_set32(header, count, + journal->block_size - tag_tbl_size); + + jbd_meta_csum_set(journal->jbd_fs, bhdr); + rc = jbd_block_set(journal->jbd_fs, &desc_block); + } + + return rc; +} + +/**@brief Put references of block descriptors in a transaction. + * @param journal current journal session + * @param trans transaction*/ +void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans) +{ + struct jbd_buf *jbd_buf, *tmp; + struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs; + TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node, + tmp) { + struct ext4_block block = jbd_buf->block; + ext4_block_set(fs->bdev, &block); + } +} + +/**@brief Update the start block of the journal when + * all the contents in a transaction reach the disk.*/ +static void jbd_trans_end_write(struct ext4_bcache *bc __unused, + struct ext4_buf *buf, + int res, + void *arg) +{ + struct jbd_buf *jbd_buf = arg; + struct jbd_trans *trans = jbd_buf->trans; + struct jbd_block_rec *block_rec = jbd_buf->block_rec; + struct jbd_journal *journal = trans->journal; + bool first_in_queue = + trans == TAILQ_FIRST(&journal->cp_queue); + if (res != EOK) + trans->error = res; + + TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node); + TAILQ_REMOVE(&block_rec->dirty_buf_queue, + jbd_buf, + dirty_buf_node); + + jbd_trans_finish_callback(journal, + trans, + jbd_buf->block_rec, + false, + false); + if (block_rec->trans == trans && buf) { + /* Clear the end_write and end_write_arg fields. */ + buf->end_write = NULL; + buf->end_write_arg = NULL; + } + + ext4_free(jbd_buf); + + trans->written_cnt++; + if (trans->written_cnt == trans->data_cnt) { + /* If it is the first transaction on checkpoint queue, + * we will shift the start of the journal to the next + * transaction, and remove subsequent written + * transactions from checkpoint queue until we find + * an unwritten one. */ + if (first_in_queue) { + journal->start = trans->start_iblock + + trans->alloc_blocks; + wrap(&journal->jbd_fs->sb, journal->start); + journal->trans_id = trans->trans_id + 1; + TAILQ_REMOVE(&journal->cp_queue, trans, trans_node); + jbd_journal_free_trans(journal, trans, false); + + jbd_journal_purge_cp_trans(journal, false, false); + jbd_journal_write_sb(journal); + jbd_write_sb(journal->jbd_fs); + } + } +} + +/**@brief Commit a transaction to the journal immediately. + * @param journal current journal session + * @param trans transaction + * @return standard error code*/ +static int __jbd_journal_commit_trans(struct jbd_journal *journal, + struct jbd_trans *trans) +{ + int rc = EOK; + uint32_t last = journal->last; + struct jbd_revoke_rec *rec, *tmp; + + trans->trans_id = journal->alloc_trans_id; + rc = jbd_journal_prepare(journal, trans); + if (rc != EOK) + goto Finish; + + rc = jbd_journal_prepare_revoke(journal, trans); + if (rc != EOK) + goto Finish; + + if (TAILQ_EMPTY(&trans->buf_queue) && + RB_EMPTY(&trans->revoke_root)) { + /* Since there are no entries in both buffer list + * and revoke entry list, we do not consider trans as + * complete transaction and just return EOK.*/ + jbd_journal_free_trans(journal, trans, false); + goto Finish; + } + + rc = jbd_trans_write_commit_block(trans); + if (rc != EOK) + goto Finish; + + journal->alloc_trans_id++; + + /* Complete the checkpoint of buffers which are revoked. */ + RB_FOREACH_SAFE(rec, jbd_revoke_tree, &trans->revoke_root, + tmp) { + struct jbd_block_rec *block_rec = + jbd_trans_block_rec_lookup(journal, rec->lba); + struct jbd_buf *jbd_buf = NULL; + if (block_rec) + jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue, + jbd_buf_dirty); + if (jbd_buf) { + struct ext4_buf *buf; + struct ext4_block block = EXT4_BLOCK_ZERO(); + /* + * We do this to reset the ext4_buf::end_write and + * ext4_buf::end_write_arg fields so that the checkpoint + * callback won't be triggered again. + */ + buf = ext4_bcache_find_get(journal->jbd_fs->bdev->bc, + &block, + jbd_buf->block_rec->lba); + jbd_trans_end_write(journal->jbd_fs->bdev->bc, + buf, + EOK, + jbd_buf); + if (buf) + ext4_block_set(journal->jbd_fs->bdev, &block); + } + } + + if (TAILQ_EMPTY(&journal->cp_queue)) { + /* + * This transaction is going to be the first object in the + * checkpoint queue. + * When the first transaction in checkpoint queue is completely + * written to disk, we shift the tail of the log to right. + */ + if (trans->data_cnt) { + journal->start = trans->start_iblock; + wrap(&journal->jbd_fs->sb, journal->start); + journal->trans_id = trans->trans_id; + jbd_journal_write_sb(journal); + jbd_write_sb(journal->jbd_fs); + TAILQ_INSERT_TAIL(&journal->cp_queue, trans, + trans_node); + jbd_journal_cp_trans(journal, trans); + } else { + journal->start = trans->start_iblock + + trans->alloc_blocks; + wrap(&journal->jbd_fs->sb, journal->start); + journal->trans_id = trans->trans_id + 1; + jbd_journal_write_sb(journal); + jbd_journal_free_trans(journal, trans, false); + } + } else { + /* No need to do anything to the JBD superblock. */ + TAILQ_INSERT_TAIL(&journal->cp_queue, trans, + trans_node); + if (trans->data_cnt) + jbd_journal_cp_trans(journal, trans); + } +Finish: + if (rc != EOK && rc != ENOSPC) { + journal->last = last; + jbd_journal_free_trans(journal, trans, true); + } + return rc; +} + +/**@brief Allocate a new transaction + * @param journal current journal session + * @return transaction allocated*/ +struct jbd_trans * +jbd_journal_new_trans(struct jbd_journal *journal) +{ + struct jbd_trans *trans = NULL; + trans = ext4_calloc(1, sizeof(struct jbd_trans)); + if (!trans) + return NULL; + + /* We will assign a trans_id to this transaction, + * once it has been committed.*/ + trans->journal = journal; + trans->data_csum = EXT4_CRC32_INIT; + trans->error = EOK; + TAILQ_INIT(&trans->buf_queue); + return trans; +} + +/**@brief Commit a transaction to the journal immediately. + * @param journal current journal session + * @param trans transaction + * @return standard error code*/ +int jbd_journal_commit_trans(struct jbd_journal *journal, + struct jbd_trans *trans) +{ + int r = EOK; + r = __jbd_journal_commit_trans(journal, trans); + return r; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_mbr.c b/Library/lwext4/src/ext4_mbr.c new file mode 100644 index 0000000..0376545 --- /dev/null +++ b/Library/lwext4/src/ext4_mbr.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_mbr.c + * @brief Master boot record parser + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define MBR_SIGNATURE 0xAA55 + +#pragma pack(push, 1) + +struct ext4_part_entry { + uint8_t status; + uint8_t chs1[3]; + uint8_t type; + uint8_t chs2[3]; + uint32_t first_lba; + uint32_t sectors; +}; + +struct ext4_mbr { + uint8_t bootstrap[442]; + uint32_t disk_id; + struct ext4_part_entry part_entry[4]; + uint16_t signature; +}; + +#pragma pack(pop) + +int ext4_mbr_scan(struct ext4_blockdev *parent, struct ext4_mbr_bdevs *bdevs) +{ + int r; + size_t i; + + ext4_dbg(DEBUG_MBR, DBG_INFO "ext4_mbr_scan\n"); + memset(bdevs, 0, sizeof(struct ext4_mbr_bdevs)); + r = ext4_block_init(parent); + if (r != EOK) + return r; + + r = ext4_block_readbytes(parent, 0, parent->bdif->ph_bbuf, 512); + if (r != EOK) { + goto blockdev_fini; + } + + const struct ext4_mbr *mbr = (void *)parent->bdif->ph_bbuf; + + if (to_le16(mbr->signature) != MBR_SIGNATURE) { + ext4_dbg(DEBUG_MBR, DBG_ERROR "ext4_mbr_scan: unknown " + "signature: 0x%x\n", to_le16(mbr->signature)); + r = ENOENT; + goto blockdev_fini; + } + + /*Show bootstrap code*/ + ext4_dbg(DEBUG_MBR, "mbr_part: bootstrap:"); + for (i = 0; i < sizeof(mbr->bootstrap); ++i) { + if (!(i & 0xF)) + ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "\n"); + ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "%02x, ", mbr->bootstrap[i]); + } + + ext4_dbg(DEBUG_MBR | DEBUG_NOPREFIX, "\n\n"); + for (i = 0; i < 4; ++i) { + const struct ext4_part_entry *pe = &mbr->part_entry[i]; + ext4_dbg(DEBUG_MBR, "mbr_part: %d\n", (int)i); + ext4_dbg(DEBUG_MBR, "\tstatus: 0x%x\n", pe->status); + ext4_dbg(DEBUG_MBR, "\ttype 0x%x:\n", pe->type); + ext4_dbg(DEBUG_MBR, "\tfirst_lba: 0x%"PRIx32"\n", pe->first_lba); + ext4_dbg(DEBUG_MBR, "\tsectors: 0x%"PRIx32"\n", pe->sectors); + + if (!pe->sectors) + continue; /*Empty entry*/ + + if (pe->type != 0x83) + continue; /*Unsupported entry. 0x83 - linux native*/ + + bdevs->partitions[i].bdif = parent->bdif; + bdevs->partitions[i].part_offset = + (uint64_t)pe->first_lba * parent->bdif->ph_bsize; + bdevs->partitions[i].part_size = + (uint64_t)pe->sectors * parent->bdif->ph_bsize; + } + + blockdev_fini: + ext4_block_fini(parent); + return r; +} + +int ext4_mbr_write(struct ext4_blockdev *parent, struct ext4_mbr_parts *parts, uint32_t disk_id) +{ + int r; + uint64_t disk_size; + uint32_t division_sum = parts->division[0] + parts->division[1] + + parts->division[2] + parts->division[3]; + + if (division_sum > 100) + return EINVAL; + + ext4_dbg(DEBUG_MBR, DBG_INFO "ext4_mbr_write\n"); + r = ext4_block_init(parent); + if (r != EOK) + return r; + + disk_size = parent->part_size; + + /*Calculate CHS*/ + uint32_t k = 16; + while ((k < 256) && ((disk_size / k / 63) > 1024)) + k *= 2; + + if (k == 256) + --k; + + const uint32_t cyl_size = 63 * k; + const uint32_t cyl_count = disk_size / cyl_size; + + struct ext4_mbr *mbr = (void *)parent->bdif->ph_bbuf; + memset(mbr, 0, sizeof(struct ext4_mbr)); + + mbr->disk_id = disk_id; + + uint32_t cyl_it = 0; + for (int i = 0; i < 4; ++i) { + uint32_t cyl_part = cyl_count * parts->division[i] / 100; + if (!cyl_part) + continue; + + uint32_t part_start = cyl_it * cyl_size; + uint32_t part_size = cyl_part * cyl_size; + + if (i == 0) { + part_start += 63; + part_size -= 63 * parent->bdif->ph_bsize; + } + + uint32_t cyl_end = cyl_part + cyl_it - 1; + + mbr->part_entry[i].status = 0; + mbr->part_entry[i].chs1[0] = i ? 0 : 1;; + mbr->part_entry[i].chs1[1] = (cyl_it >> 2) + 1; + mbr->part_entry[i].chs1[2] = cyl_it; + mbr->part_entry[i].type = 0x83; + mbr->part_entry[i].chs2[0] = k - 1; + mbr->part_entry[i].chs2[1] = (cyl_end >> 2) + 63; + mbr->part_entry[i].chs2[2] = cyl_end; + + mbr->part_entry[i].first_lba = part_start; + mbr->part_entry[i].sectors = part_size / parent->bdif->ph_bsize; + + cyl_it += cyl_part; + } + + mbr->signature = MBR_SIGNATURE; + r = ext4_block_writebytes(parent, 0, parent->bdif->ph_bbuf, 512); + if (r != EOK) + goto blockdev_fini; + + + blockdev_fini: + ext4_block_fini(parent); + return r; +} + +/** + * @} + */ + diff --git a/Library/lwext4/src/ext4_mkfs.c b/Library/lwext4/src/ext4_mkfs.c new file mode 100644 index 0000000..0dfc91f --- /dev/null +++ b/Library/lwext4/src/ext4_mkfs.c @@ -0,0 +1,865 @@ +/* + * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_mkfs.c + * @brief + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct fs_aux_info { + struct ext4_sblock *sb; + uint8_t *bg_desc_blk; + struct xattr_list_element *xattrs; + uint32_t first_data_block; + uint64_t len_blocks; + uint32_t inode_table_blocks; + uint32_t groups; + uint32_t bg_desc_blocks; + uint32_t default_i_flags; + uint32_t blocks_per_ind; + uint32_t blocks_per_dind; + uint32_t blocks_per_tind; +}; + +static inline int log_2(int j) +{ + int i; + + for (i = 0; j > 0; i++) + j >>= 1; + + return i - 1; +} + +static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info) +{ + if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC) + return EINVAL; + + info->block_size = 1024 << to_le32(sb->log_block_size); + info->blocks_per_group = to_le32(sb->blocks_per_group); + info->inodes_per_group = to_le32(sb->inodes_per_group); + info->inode_size = to_le16(sb->inode_size); + info->inodes = to_le32(sb->inodes_count); + info->feat_ro_compat = to_le32(sb->features_read_only); + info->feat_compat = to_le32(sb->features_compatible); + info->feat_incompat = to_le32(sb->features_incompatible); + info->bg_desc_reserve_blocks = to_le16(sb->s_reserved_gdt_blocks); + info->label = sb->volume_name; + info->len = (uint64_t)info->block_size * ext4_sb_get_blocks_cnt(sb); + info->dsc_size = to_le16(sb->desc_size); + memcpy(info->uuid, sb->uuid, UUID_SIZE); + + return EOK; +} + +static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info) +{ + return info->block_size * 8; +} + +static uint32_t compute_inodes(struct ext4_mkfs_info *info) +{ + return (uint32_t)EXT4_DIV_ROUND_UP(info->len, info->block_size) / 4; +} + +static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info) +{ + uint32_t blocks = (uint32_t)EXT4_DIV_ROUND_UP(info->len, info->block_size); + uint32_t block_groups = EXT4_DIV_ROUND_UP(blocks, info->blocks_per_group); + uint32_t inodes = EXT4_DIV_ROUND_UP(info->inodes, block_groups); + inodes = EXT4_ALIGN(inodes, (info->block_size / info->inode_size)); + + /* After properly rounding up the number of inodes/group, + * make sure to update the total inodes field in the info struct. + */ + info->inodes = inodes * block_groups; + + return inodes; +} + + +static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info) +{ + uint32_t journal_blocks = (uint32_t)EXT4_DIV_ROUND_UP(info->len, + info->block_size) / 64; + if (journal_blocks < 1024) + journal_blocks = 1024; + if (journal_blocks > 32768) + journal_blocks = 32768; + return journal_blocks; +} + +static bool has_superblock(struct ext4_mkfs_info *info, uint32_t bgid) +{ + if (!(info->feat_ro_compat & EXT4_FRO_COM_SPARSE_SUPER)) + return true; + + return ext4_sb_sparse(bgid); +} + +static int create_fs_aux_info(struct fs_aux_info *aux_info, + struct ext4_mkfs_info *info) +{ + aux_info->first_data_block = (info->block_size > 1024) ? 0 : 1; + aux_info->len_blocks = info->len / info->block_size; + aux_info->inode_table_blocks = EXT4_DIV_ROUND_UP(info->inodes_per_group * + info->inode_size, info->block_size); + aux_info->groups = (uint32_t)EXT4_DIV_ROUND_UP(aux_info->len_blocks - + aux_info->first_data_block, info->blocks_per_group); + aux_info->blocks_per_ind = info->block_size / sizeof(uint32_t); + aux_info->blocks_per_dind = + aux_info->blocks_per_ind * aux_info->blocks_per_ind; + aux_info->blocks_per_tind = + aux_info->blocks_per_dind * aux_info->blocks_per_dind; + + aux_info->bg_desc_blocks = + EXT4_DIV_ROUND_UP(aux_info->groups * info->dsc_size, + info->block_size); + + aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME; + + uint32_t last_group_size = aux_info->len_blocks % info->blocks_per_group; + uint32_t last_header_size = 2 + aux_info->inode_table_blocks; + if (has_superblock(info, aux_info->groups - 1)) + last_header_size += 1 + aux_info->bg_desc_blocks + + info->bg_desc_reserve_blocks; + + if (last_group_size > 0 && last_group_size < last_header_size) { + aux_info->groups--; + aux_info->len_blocks -= last_group_size; + } + + aux_info->sb = ext4_calloc(1, EXT4_SUPERBLOCK_SIZE); + if (!aux_info->sb) + return ENOMEM; + + aux_info->bg_desc_blk = ext4_calloc(1, info->block_size); + if (!aux_info->bg_desc_blk) + return ENOMEM; + + aux_info->xattrs = NULL; + + + ext4_dbg(DEBUG_MKFS, DBG_INFO "create_fs_aux_info\n"); + ext4_dbg(DEBUG_MKFS, DBG_NONE "first_data_block: %"PRIu32"\n", + aux_info->first_data_block); + ext4_dbg(DEBUG_MKFS, DBG_NONE "len_blocks: %"PRIu64"\n", + aux_info->len_blocks); + ext4_dbg(DEBUG_MKFS, DBG_NONE "inode_table_blocks: %"PRIu32"\n", + aux_info->inode_table_blocks); + ext4_dbg(DEBUG_MKFS, DBG_NONE "groups: %"PRIu32"\n", + aux_info->groups); + ext4_dbg(DEBUG_MKFS, DBG_NONE "bg_desc_blocks: %"PRIu32"\n", + aux_info->bg_desc_blocks); + ext4_dbg(DEBUG_MKFS, DBG_NONE "default_i_flags: %"PRIu32"\n", + aux_info->default_i_flags); + ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_ind: %"PRIu32"\n", + aux_info->blocks_per_ind); + ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_dind: %"PRIu32"\n", + aux_info->blocks_per_dind); + ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_tind: %"PRIu32"\n", + aux_info->blocks_per_tind); + + return EOK; +} + +static void release_fs_aux_info(struct fs_aux_info *aux_info) +{ + if (aux_info->sb) + ext4_free(aux_info->sb); + if (aux_info->bg_desc_blk) + ext4_free(aux_info->bg_desc_blk); +} + + +/* Fill in the superblock memory buffer based on the filesystem parameters */ +static void fill_sb(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info) +{ + struct ext4_sblock *sb = aux_info->sb; + + sb->inodes_count = to_le32(info->inodes_per_group * aux_info->groups); + + ext4_sb_set_blocks_cnt(sb, aux_info->len_blocks); + ext4_sb_set_free_blocks_cnt(sb, aux_info->len_blocks); + sb->free_inodes_count = to_le32(info->inodes_per_group * aux_info->groups); + + sb->reserved_blocks_count_lo = to_le32(0); + sb->first_data_block = to_le32(aux_info->first_data_block); + sb->log_block_size = to_le32(log_2(info->block_size / 1024)); + sb->log_cluster_size = to_le32(log_2(info->block_size / 1024)); + sb->blocks_per_group = to_le32(info->blocks_per_group); + sb->frags_per_group = to_le32(info->blocks_per_group); + sb->inodes_per_group = to_le32(info->inodes_per_group); + sb->mount_time = to_le32(0); + sb->write_time = to_le32(0); + sb->mount_count = to_le16(0); + sb->max_mount_count = to_le16(0xFFFF); + sb->magic = to_le16(EXT4_SUPERBLOCK_MAGIC); + sb->state = to_le16(EXT4_SUPERBLOCK_STATE_VALID_FS); + sb->errors = to_le16(EXT4_SUPERBLOCK_ERRORS_RO); + sb->minor_rev_level = to_le16(0); + sb->last_check_time = to_le32(0); + sb->check_interval = to_le32(0); + sb->creator_os = to_le32(EXT4_SUPERBLOCK_OS_LINUX); + sb->rev_level = to_le32(1); + sb->def_resuid = to_le16(0); + sb->def_resgid = to_le16(0); + + sb->first_inode = to_le32(EXT4_GOOD_OLD_FIRST_INO); + sb->inode_size = to_le16(info->inode_size); + sb->block_group_index = to_le16(0); + + sb->features_compatible = to_le32(info->feat_compat); + sb->features_incompatible = to_le32(info->feat_incompat); + sb->features_read_only = to_le32(info->feat_ro_compat); + + memcpy(sb->uuid, info->uuid, UUID_SIZE); + + memset(sb->volume_name, 0, sizeof(sb->volume_name)); + strncpy(sb->volume_name, info->label, sizeof(sb->volume_name)); + memset(sb->last_mounted, 0, sizeof(sb->last_mounted)); + + sb->algorithm_usage_bitmap = to_le32(0); + sb->s_prealloc_blocks = 0; + sb->s_prealloc_dir_blocks = 0; + sb->s_reserved_gdt_blocks = to_le16(info->bg_desc_reserve_blocks); + + if (info->feat_compat & EXT4_FCOM_HAS_JOURNAL) + sb->journal_inode_number = to_le32(EXT4_JOURNAL_INO); + + sb->journal_backup_type = 1; + sb->journal_dev = to_le32(0); + sb->last_orphan = to_le32(0); + sb->hash_seed[0] = to_le32(0x11111111); + sb->hash_seed[1] = to_le32(0x22222222); + sb->hash_seed[2] = to_le32(0x33333333); + sb->hash_seed[3] = to_le32(0x44444444); + sb->default_hash_version = EXT2_HTREE_HALF_MD4; + sb->checksum_type = 1; + sb->desc_size = to_le16(info->dsc_size); + sb->default_mount_opts = to_le32(0); + sb->first_meta_bg = to_le32(0); + sb->mkfs_time = to_le32(0); + + sb->reserved_blocks_count_hi = to_le32(0); + sb->min_extra_isize = to_le32(sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE); + sb->want_extra_isize = to_le32(sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE); + sb->flags = to_le32(EXT4_SUPERBLOCK_FLAGS_SIGNED_HASH); +} + + +static int write_bgroup_block(struct ext4_blockdev *bd, + struct fs_aux_info *aux_info, + struct ext4_mkfs_info *info, + uint32_t blk) +{ + int r = EOK; + uint32_t j; + struct ext4_block b; + + uint32_t block_size = ext4_sb_get_block_size(aux_info->sb); + + for (j = 0; j < aux_info->groups; j++) { + uint64_t bg_start_block = aux_info->first_data_block + + j * info->blocks_per_group; + uint32_t blk_off = 0; + + blk_off += aux_info->bg_desc_blocks; + if (has_superblock(info, j)) { + bg_start_block++; + blk_off += info->bg_desc_reserve_blocks; + } + + uint64_t dsc_blk = bg_start_block + blk; + + r = ext4_block_get_noread(bd, &b, dsc_blk); + if (r != EOK) + return r; + + memcpy(b.data, aux_info->bg_desc_blk, block_size); + + ext4_bcache_set_dirty(b.buf); + r = ext4_block_set(bd, &b); + if (r != EOK) + return r; + } + + return r; +} + +static int write_bgroups(struct ext4_blockdev *bd, struct fs_aux_info *aux_info, + struct ext4_mkfs_info *info) +{ + int r = EOK; + + struct ext4_block b; + struct ext4_bgroup *bg_desc; + + uint32_t i; + uint32_t bg_free_blk = 0; + uint64_t sb_free_blk = 0; + uint32_t block_size = ext4_sb_get_block_size(aux_info->sb); + uint32_t dsc_size = ext4_sb_get_desc_size(aux_info->sb); + uint32_t dsc_per_block = block_size / dsc_size; + uint32_t k = 0; + + for (i = 0; i < aux_info->groups; i++) { + uint64_t bg_start_block = aux_info->first_data_block + + aux_info->first_data_block + i * info->blocks_per_group; + uint32_t blk_off = 0; + + bg_desc = (void *)(aux_info->bg_desc_blk + k * dsc_size); + bg_free_blk = info->blocks_per_group - + aux_info->inode_table_blocks; + + bg_free_blk -= 2; + blk_off += aux_info->bg_desc_blocks; + + if (i == (aux_info->groups - 1)) + bg_free_blk -= aux_info->first_data_block; + + if (has_superblock(info, i)) { + bg_start_block++; + blk_off += info->bg_desc_reserve_blocks; + bg_free_blk -= info->bg_desc_reserve_blocks + 1; + bg_free_blk -= aux_info->bg_desc_blocks; + } + + ext4_bg_set_block_bitmap(bg_desc, aux_info->sb, + bg_start_block + blk_off + 1); + + ext4_bg_set_inode_bitmap(bg_desc, aux_info->sb, + bg_start_block + blk_off + 2); + + ext4_bg_set_inode_table_first_block(bg_desc, + aux_info->sb, + bg_start_block + blk_off + 3); + + ext4_bg_set_free_blocks_count(bg_desc, aux_info->sb, + bg_free_blk); + + ext4_bg_set_free_inodes_count(bg_desc, + aux_info->sb, to_le32(aux_info->sb->inodes_per_group)); + + ext4_bg_set_used_dirs_count(bg_desc, aux_info->sb, 0); + + ext4_bg_set_flag(bg_desc, + EXT4_BLOCK_GROUP_BLOCK_UNINIT | + EXT4_BLOCK_GROUP_INODE_UNINIT); + + sb_free_blk += bg_free_blk; + + r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 1); + if (r != EOK) + return r; + memset(b.data, 0, block_size); + ext4_bcache_set_dirty(b.buf); + r = ext4_block_set(bd, &b); + if (r != EOK) + return r; + r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 2); + if (r != EOK) + return r; + memset(b.data, 0, block_size); + ext4_bcache_set_dirty(b.buf); + r = ext4_block_set(bd, &b); + if (r != EOK) + return r; + + if (++k != dsc_per_block) + continue; + + k = 0; + r = write_bgroup_block(bd, aux_info, info, i / dsc_per_block); + if (r != EOK) + return r; + + } + + r = write_bgroup_block(bd, aux_info, info, i / dsc_per_block); + if (r != EOK) + return r; + + ext4_sb_set_free_blocks_cnt(aux_info->sb, sb_free_blk); + return r; +} + +static int write_sblocks(struct ext4_blockdev *bd, struct fs_aux_info *aux_info, + struct ext4_mkfs_info *info) +{ + uint64_t offset; + uint32_t i; + int r; + + /* write out the backup superblocks */ + for (i = 1; i < aux_info->groups; i++) { + if (has_superblock(info, i)) { + offset = info->block_size * (aux_info->first_data_block + + i * info->blocks_per_group); + + aux_info->sb->block_group_index = to_le16(i); + r = ext4_block_writebytes(bd, offset, aux_info->sb, + EXT4_SUPERBLOCK_SIZE); + if (r != EOK) + return r; + } + } + + /* write out the primary superblock */ + aux_info->sb->block_group_index = to_le16(0); + return ext4_block_writebytes(bd, 1024, aux_info->sb, + EXT4_SUPERBLOCK_SIZE); +} + + +int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info) +{ + int r; + struct ext4_sblock *sb = NULL; + r = ext4_block_init(bd); + if (r != EOK) + return r; + + sb = ext4_malloc(EXT4_SUPERBLOCK_SIZE); + if (!sb) + goto Finish; + + + r = ext4_sb_read(bd, sb); + if (r != EOK) + goto Finish; + + r = sb2info(sb, info); + +Finish: + if (sb) + ext4_free(sb); + ext4_block_fini(bd); + return r; +} + +static int mkfs_init(struct ext4_blockdev *bd, struct ext4_mkfs_info *info) +{ + int r; + struct fs_aux_info aux_info; + memset(&aux_info, 0, sizeof(struct fs_aux_info)); + + r = create_fs_aux_info(&aux_info, info); + if (r != EOK) + goto Finish; + + fill_sb(&aux_info, info); + + r = write_bgroups(bd, &aux_info, info); + if (r != EOK) + goto Finish; + + r = write_sblocks(bd, &aux_info, info); + if (r != EOK) + goto Finish; + + Finish: + release_fs_aux_info(&aux_info); + return r; +} + +static int init_bgs(struct ext4_fs *fs) +{ + int r = EOK; + struct ext4_block_group_ref ref; + uint32_t i; + uint32_t bg_count = ext4_block_group_cnt(&fs->sb); + for (i = 0; i < bg_count; ++i) { + r = ext4_fs_get_block_group_ref(fs, i, &ref); + if (r != EOK) + break; + + r = ext4_fs_put_block_group_ref(&ref); + if (r != EOK) + break; + } + return r; +} + +static int alloc_inodes(struct ext4_fs *fs) +{ + int r = EOK; + int i; + struct ext4_inode_ref inode_ref; + for (i = 1; i < 12; ++i) { + int filetype = EXT4_DE_REG_FILE; + + switch (i) { + case EXT4_ROOT_INO: + case EXT4_GOOD_OLD_FIRST_INO: + filetype = EXT4_DE_DIR; + break; + default: + break; + } + + r = ext4_fs_alloc_inode(fs, &inode_ref, filetype); + if (r != EOK) + return r; + + ext4_inode_set_mode(&fs->sb, inode_ref.inode, 0); + + switch (i) { + case EXT4_ROOT_INO: + case EXT4_JOURNAL_INO: + ext4_fs_inode_blocks_init(fs, &inode_ref); + break; + } + + ext4_fs_put_inode_ref(&inode_ref); + } + + return r; +} + +static int create_dirs(struct ext4_fs *fs) +{ + int r = EOK; + struct ext4_inode_ref root; + struct ext4_inode_ref child; + + r = ext4_fs_get_inode_ref(fs, EXT4_ROOT_INO, &root); + if (r != EOK) + return r; + + r = ext4_fs_get_inode_ref(fs, EXT4_GOOD_OLD_FIRST_INO, &child); + if (r != EOK) + return r; + + ext4_inode_set_mode(&fs->sb, child.inode, + EXT4_INODE_MODE_DIRECTORY | 0777); + + ext4_inode_set_mode(&fs->sb, root.inode, + EXT4_INODE_MODE_DIRECTORY | 0777); + +#if CONFIG_DIR_INDEX_ENABLE + /* Initialize directory index if supported */ + if (ext4_sb_feature_com(&fs->sb, EXT4_FCOM_DIR_INDEX)) { + r = ext4_dir_dx_init(&root, &root); + if (r != EOK) + return r; + + r = ext4_dir_dx_init(&child, &root); + if (r != EOK) + return r; + + ext4_inode_set_flag(root.inode, EXT4_INODE_FLAG_INDEX); + ext4_inode_set_flag(child.inode, EXT4_INODE_FLAG_INDEX); + } else +#endif + { + r = ext4_dir_add_entry(&root, ".", strlen("."), &root); + if (r != EOK) + return r; + + r = ext4_dir_add_entry(&root, "..", strlen(".."), &root); + if (r != EOK) + return r; + + r = ext4_dir_add_entry(&child, ".", strlen("."), &child); + if (r != EOK) + return r; + + r = ext4_dir_add_entry(&child, "..", strlen(".."), &root); + if (r != EOK) + return r; + } + + r = ext4_dir_add_entry(&root, "lost+found", strlen("lost+found"), &child); + if (r != EOK) + return r; + + ext4_inode_set_links_cnt(root.inode, 3); + ext4_inode_set_links_cnt(child.inode, 2); + + child.dirty = true; + root.dirty = true; + ext4_fs_put_inode_ref(&child); + ext4_fs_put_inode_ref(&root); + return r; +} + +static int create_journal_inode(struct ext4_fs *fs, + struct ext4_mkfs_info *info) +{ + int ret; + struct ext4_inode_ref inode_ref; + uint64_t blocks_count; + + if (!info->journal) + return EOK; + + ret = ext4_fs_get_inode_ref(fs, EXT4_JOURNAL_INO, &inode_ref); + if (ret != EOK) + return ret; + + struct ext4_inode *inode = inode_ref.inode; + + ext4_inode_set_mode(&fs->sb, inode, EXT4_INODE_MODE_FILE | 0600); + ext4_inode_set_links_cnt(inode, 1); + + blocks_count = ext4_inode_get_blocks_count(&fs->sb, inode); + + while (blocks_count++ < info->journal_blocks) + { + ext4_fsblk_t fblock; + ext4_lblk_t iblock; + struct ext4_block blk; + + ret = ext4_fs_append_inode_dblk(&inode_ref, &fblock, &iblock); + if (ret != EOK) + goto Finish; + + if (iblock != 0) + continue; + + ret = ext4_block_get(fs->bdev, &blk, fblock); + if (ret != EOK) + goto Finish; + + + struct jbd_sb * jbd_sb = (struct jbd_sb * )blk.data; + memset(jbd_sb, 0, sizeof(struct jbd_sb)); + + jbd_sb->header.magic = to_be32(JBD_MAGIC_NUMBER); + jbd_sb->header.blocktype = to_be32(JBD_SUPERBLOCK_V2); + jbd_sb->blocksize = to_be32(info->block_size); + jbd_sb->maxlen = to_be32(info->journal_blocks); + jbd_sb->nr_users = to_be32(1); + jbd_sb->first = to_be32(1); + jbd_sb->sequence = to_be32(1); + + ext4_bcache_set_dirty(blk.buf); + ret = ext4_block_set(fs->bdev, &blk); + if (ret != EOK) + goto Finish; + } + + memcpy(fs->sb.journal_blocks, inode->blocks, sizeof(inode->blocks)); + + Finish: + ext4_fs_put_inode_ref(&inode_ref); + + return ret; +} + +int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd, + struct ext4_mkfs_info *info, int fs_type) +{ + int r; + + r = ext4_block_init(bd); + if (r != EOK) + return r; + + bd->fs = fs; + + if (info->len == 0) + info->len = bd->part_size; + + if (info->block_size == 0) + info->block_size = 4096; /*Set block size to default value*/ + + /* Round down the filesystem length to be a multiple of the block size */ + info->len &= ~((uint64_t)info->block_size - 1); + + if (info->journal_blocks == 0) + info->journal_blocks = compute_journal_blocks(info); + + if (info->blocks_per_group == 0) + info->blocks_per_group = compute_blocks_per_group(info); + + if (info->inodes == 0) + info->inodes = compute_inodes(info); + + if (info->inode_size == 0) + info->inode_size = 256; + + if (info->label == NULL) + info->label = ""; + + info->inodes_per_group = compute_inodes_per_group(info); + + switch (fs_type) { + case F_SET_EXT2: + info->feat_compat = EXT2_SUPPORTED_FCOM; + info->feat_ro_compat = EXT2_SUPPORTED_FRO_COM; + info->feat_incompat = EXT2_SUPPORTED_FINCOM; + break; + case F_SET_EXT3: + info->feat_compat = EXT3_SUPPORTED_FCOM; + info->feat_ro_compat = EXT3_SUPPORTED_FRO_COM; + info->feat_incompat = EXT3_SUPPORTED_FINCOM; + break; + case F_SET_EXT4: + info->feat_compat = EXT4_SUPPORTED_FCOM; + info->feat_ro_compat = EXT4_SUPPORTED_FRO_COM; + info->feat_incompat = EXT4_SUPPORTED_FINCOM; + break; + } + + /*TODO: handle this features some day...*/ + info->feat_incompat &= ~EXT4_FINCOM_META_BG; + info->feat_incompat &= ~EXT4_FINCOM_FLEX_BG; + info->feat_incompat &= ~EXT4_FINCOM_64BIT; + + info->feat_ro_compat &= ~EXT4_FRO_COM_METADATA_CSUM; + info->feat_ro_compat &= ~EXT4_FRO_COM_GDT_CSUM; + info->feat_ro_compat &= ~EXT4_FRO_COM_DIR_NLINK; + info->feat_ro_compat &= ~EXT4_FRO_COM_EXTRA_ISIZE; + info->feat_ro_compat &= ~EXT4_FRO_COM_HUGE_FILE; + + if (info->journal) + info->feat_compat |= EXT4_FCOM_HAS_JOURNAL; + + if (info->dsc_size == 0) { + + if (info->feat_incompat & EXT4_FINCOM_64BIT) + info->dsc_size = EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE; + else + info->dsc_size = EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE; + } + + info->bg_desc_reserve_blocks = 0; + + ext4_dbg(DEBUG_MKFS, DBG_INFO "Creating filesystem with parameters:\n"); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Size: %"PRIu64"\n", info->len); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Block size: %"PRIu32"\n", + info->block_size); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Blocks per group: %"PRIu32"\n", + info->blocks_per_group); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes per group: %"PRIu32"\n", + info->inodes_per_group); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Inode size: %"PRIu32"\n", + info->inode_size); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes: %"PRIu32"\n", info->inodes); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Journal blocks: %"PRIu32"\n", + info->journal_blocks); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Features ro_compat: 0x%x\n", + info->feat_ro_compat); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Features compat: 0x%x\n", + info->feat_compat); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Features incompat: 0x%x\n", + info->feat_incompat); + ext4_dbg(DEBUG_MKFS, DBG_NONE "BG desc reserve: %"PRIu32"\n", + info->bg_desc_reserve_blocks); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Descriptor size: %"PRIu16"\n", + info->dsc_size); + ext4_dbg(DEBUG_MKFS, DBG_NONE "journal: %s\n", + info->journal ? "yes" : "no"); + ext4_dbg(DEBUG_MKFS, DBG_NONE "Label: %s\n", info->label); + + struct ext4_bcache bc; + + memset(&bc, 0, sizeof(struct ext4_bcache)); + ext4_block_set_lb_size(bd, info->block_size); + + r = ext4_bcache_init_dynamic(&bc, CONFIG_BLOCK_DEV_CACHE_SIZE, + info->block_size); + if (r != EOK) + goto block_fini; + + /*Bind block cache to block device*/ + r = ext4_block_bind_bcache(bd, &bc); + if (r != EOK) + goto cache_fini; + + r = ext4_block_cache_write_back(bd, 1); + if (r != EOK) + goto cache_fini; + + r = mkfs_init(bd, info); + if (r != EOK) + goto cache_fini; + + r = ext4_fs_init(fs, bd, false); + if (r != EOK) + goto cache_fini; + + r = init_bgs(fs); + if (r != EOK) + goto fs_fini; + + r = alloc_inodes(fs); + if (r != EOK) + goto fs_fini; + + r = create_dirs(fs); + if (r != EOK) + goto fs_fini; + + r = create_journal_inode(fs, info); + if (r != EOK) + goto fs_fini; + + fs_fini: + ext4_fs_fini(fs); + + cache_fini: + ext4_block_cache_write_back(bd, 0); + ext4_bcache_fini_dynamic(&bc); + + block_fini: + ext4_block_fini(bd); + + return r; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_super.c b/Library/lwext4/src/ext4_super.c new file mode 100644 index 0000000..092c38b --- /dev/null +++ b/Library/lwext4/src/ext4_super.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * + * + * HelenOS: + * Copyright (c) 2012 Martin Sucha + * Copyright (c) 2012 Frantisek Princ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_super.h + * @brief Superblock operations. + */ + +#include +#include +#include +#include +#include + +#include +#include + +uint32_t ext4_block_group_cnt(struct ext4_sblock *s) +{ + uint64_t blocks_count = ext4_sb_get_blocks_cnt(s); + uint32_t blocks_per_group = ext4_get32(s, blocks_per_group); + + uint32_t block_groups_count = (uint32_t)(blocks_count / blocks_per_group); + + if (blocks_count % blocks_per_group) + block_groups_count++; + + return block_groups_count; +} + +uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid) +{ + uint32_t block_group_count = ext4_block_group_cnt(s); + uint32_t blocks_per_group = ext4_get32(s, blocks_per_group); + uint64_t total_blocks = ext4_sb_get_blocks_cnt(s); + + if (bgid < block_group_count - 1) + return blocks_per_group; + + return (uint32_t)(total_blocks - ((block_group_count - 1) * blocks_per_group)); +} + +uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid) +{ + uint32_t block_group_count = ext4_block_group_cnt(s); + uint32_t inodes_per_group = ext4_get32(s, inodes_per_group); + uint32_t total_inodes = ext4_get32(s, inodes_count); + + if (bgid < block_group_count - 1) + return inodes_per_group; + + return (total_inodes - ((block_group_count - 1) * inodes_per_group)); +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_sb_csum(struct ext4_sblock *s) +{ + + return ext4_crc32c(EXT4_CRC32_INIT, s, + offsetof(struct ext4_sblock, checksum)); +} +#else +#define ext4_sb_csum(...) 0 +#endif + +static bool ext4_sb_verify_csum(struct ext4_sblock *s) +{ + if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM)) + return true; + + if (s->checksum_type != to_le32(EXT4_CHECKSUM_CRC32C)) + return false; + + return s->checksum == to_le32(ext4_sb_csum(s)); +} + +static void ext4_sb_set_csum(struct ext4_sblock *s) +{ + if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM)) + return; + + s->checksum = to_le32(ext4_sb_csum(s)); +} + +int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s) +{ + ext4_sb_set_csum(s); + return ext4_block_writebytes(bdev, EXT4_SUPERBLOCK_OFFSET, s, + EXT4_SUPERBLOCK_SIZE); +} + +int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s) +{ + return ext4_block_readbytes(bdev, EXT4_SUPERBLOCK_OFFSET, s, + EXT4_SUPERBLOCK_SIZE); +} + +bool ext4_sb_check(struct ext4_sblock *s) +{ + if (ext4_get16(s, magic) != EXT4_SUPERBLOCK_MAGIC) + return false; + + if (ext4_get32(s, inodes_count) == 0) + return false; + + if (ext4_sb_get_blocks_cnt(s) == 0) + return false; + + if (ext4_get32(s, blocks_per_group) == 0) + return false; + + if (ext4_get32(s, inodes_per_group) == 0) + return false; + + if (ext4_get16(s, inode_size) < 128) + return false; + + if (ext4_get32(s, first_inode) < 11) + return false; + + if (ext4_sb_get_desc_size(s) < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE) + return false; + + if (ext4_sb_get_desc_size(s) > EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) + return false; + + if (!ext4_sb_verify_csum(s)) + return false; + + return true; +} + +static inline int is_power_of(uint32_t a, uint32_t b) +{ + while (1) { + if (a < b) + return 0; + if (a == b) + return 1; + if ((a % b) != 0) + return 0; + a = a / b; + } +} + +bool ext4_sb_sparse(uint32_t group) +{ + if (group <= 1) + return 1; + + if (!(group & 1)) + return 0; + + return (is_power_of(group, 7) || is_power_of(group, 5) || + is_power_of(group, 3)); +} + +bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t group) +{ + if (ext4_sb_feature_ro_com(s, EXT4_FRO_COM_SPARSE_SUPER) && + !ext4_sb_sparse(group)) + return false; + return true; +} + +static uint32_t ext4_bg_num_gdb_meta(struct ext4_sblock *s, uint32_t group) +{ + uint32_t dsc_per_block = + ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s); + + uint32_t metagroup = group / dsc_per_block; + uint32_t first = metagroup * dsc_per_block; + uint32_t last = first + dsc_per_block - 1; + + if (group == first || group == first + 1 || group == last) + return 1; + return 0; +} + +static uint32_t ext4_bg_num_gdb_nometa(struct ext4_sblock *s, uint32_t group) +{ + if (!ext4_sb_is_super_in_bg(s, group)) + return 0; + uint32_t dsc_per_block = + ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s); + + uint32_t db_count = + (ext4_block_group_cnt(s) + dsc_per_block - 1) / dsc_per_block; + + if (ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG)) + return ext4_sb_first_meta_bg(s); + + return db_count; +} + +uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group) +{ + uint32_t dsc_per_block = + ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s); + uint32_t first_meta_bg = ext4_sb_first_meta_bg(s); + uint32_t metagroup = group / dsc_per_block; + + if (!ext4_sb_feature_incom(s,EXT4_FINCOM_META_BG) || + metagroup < first_meta_bg) + return ext4_bg_num_gdb_nometa(s, group); + + return ext4_bg_num_gdb_meta(s, group); +} + +uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s, + uint32_t block_group) +{ + uint32_t num; + uint32_t dsc_per_block = + ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s); + + num = ext4_sb_is_super_in_bg(s, block_group); + + if (!ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG) || + block_group < ext4_sb_first_meta_bg(s) * dsc_per_block) { + if (num) { + num += ext4_bg_num_gdb(s, block_group); + num += ext4_get16(s, s_reserved_gdt_blocks); + } + } else { + num += ext4_bg_num_gdb(s, block_group); + } + + uint32_t clustersize = 1024 << ext4_get32(s, log_cluster_size); + uint32_t cluster_ratio = clustersize / ext4_sb_get_block_size(s); + uint32_t v = + (num + cluster_ratio - 1) >> ext4_get32(s, log_cluster_size); + + return v; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_trans.c b/Library/lwext4/src/ext4_trans.c new file mode 100644 index 0000000..f228751 --- /dev/null +++ b/Library/lwext4/src/ext4_trans.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_trans.c + * @brief Ext4 transaction buffer operations. + */ + +#include +#include +#include +#include +#include + +#include +#include + +int ext4_trans_set_block_dirty(struct ext4_buf *buf) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + struct ext4_fs *fs = buf->bc->bdev->fs; + struct ext4_block block = { + .lb_id = buf->lba, + .data = buf->data, + .buf = buf + }; + + if (fs->jbd_journal && fs->curr_trans) { + struct jbd_trans *trans = fs->curr_trans; + return jbd_trans_set_block_dirty(trans, &block); + } +#endif + ext4_bcache_set_dirty(buf); + return r; +} + +int ext4_trans_block_get_noread(struct ext4_blockdev *bdev, + struct ext4_block *b, + uint64_t lba) +{ + int r = ext4_block_get_noread(bdev, b, lba); + if (r != EOK) + return r; + + return r; +} + +int ext4_trans_block_get(struct ext4_blockdev *bdev, + struct ext4_block *b, + uint64_t lba) +{ + int r = ext4_block_get(bdev, b, lba); + if (r != EOK) + return r; + + return r; +} + +int ext4_trans_try_revoke_block(struct ext4_blockdev *bdev __unused, + uint64_t lba __unused) +{ + int r = EOK; +#if CONFIG_JOURNALING_ENABLE + struct ext4_fs *fs = bdev->fs; + if (fs->jbd_journal && fs->curr_trans) { + struct jbd_trans *trans = fs->curr_trans; + r = jbd_trans_try_revoke_block(trans, lba); + } else if (fs->jbd_journal) { + r = ext4_block_flush_lba(fs->bdev, lba); + } +#endif + return r; +} + +/** + * @} + */ diff --git a/Library/lwext4/src/ext4_xattr.c b/Library/lwext4/src/ext4_xattr.c new file mode 100644 index 0000000..f8a5778 --- /dev/null +++ b/Library/lwext4/src/ext4_xattr.c @@ -0,0 +1,1564 @@ +/* + * Copyright (c) 2017 Grzegorz Kostka (kostka.grzegorz@gmail.com) + * Copyright (c) 2017 Kaho Ng (ngkaho1234@gmail.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +/** @addtogroup lwext4 + * @{ + */ +/** + * @file ext4_xattr.c + * @brief Extended Attribute manipulation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if CONFIG_XATTR_ENABLE + +/** + * @file ext4_xattr.c + * @brief Extended Attribute Manipulation + */ + +/* 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 + +#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(sb, raw_inode) \ + ((struct ext4_xattr_ibody_header *)((char *)raw_inode + \ + EXT4_GOOD_OLD_INODE_SIZE + \ + ext4_inode_get_extra_isize( \ + sb, raw_inode))) +#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) + +#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) + + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header, + struct ext4_xattr_entry *entry) +{ + uint32_t hash = 0; + char *name = EXT4_XATTR_NAME(entry); + int n; + + for (n = 0; n < entry->e_name_len; n++) { + hash = (hash << NAME_HASH_SHIFT) ^ + (hash >> (8 * sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++; + } + + if (entry->e_value_block == 0 && entry->e_value_size != 0) { + uint32_t *value = + (uint32_t *)((char *)header + to_le16(entry->e_value_offs)); + for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >> + EXT4_XATTR_PAD_BITS; + n; n--) { + hash = (hash << VALUE_HASH_SHIFT) ^ + (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^ + to_le32(*value++); + } + } + entry->e_hash = to_le32(hash); +} + +#define BLOCK_HASH_SHIFT 16 + +/* + * ext4_xattr_rehash() + * + * Re-compute the extended attribute hash value after an entry has changed. + */ +static void ext4_xattr_rehash(struct ext4_xattr_header *header, + struct ext4_xattr_entry *entry) +{ + struct ext4_xattr_entry *here; + uint32_t hash = 0; + + ext4_xattr_compute_hash(header, entry); + here = EXT4_XATTR_ENTRY(header + 1); + while (!EXT4_XATTR_IS_LAST_ENTRY(here)) { + if (!here->e_hash) { + /* Block is not shared if an entry's hash value == 0 */ + hash = 0; + break; + } + hash = (hash << BLOCK_HASH_SHIFT) ^ + (hash >> (8 * sizeof(hash) - BLOCK_HASH_SHIFT)) ^ + to_le32(here->e_hash); + here = EXT4_XATTR_NEXT(here); + } + header->h_hash = to_le32(hash); +} + +#if CONFIG_META_CSUM_ENABLE +static uint32_t ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t blocknr, + struct ext4_xattr_header *header) +{ + uint32_t checksum = 0; + uint64_t le64_blocknr = blocknr; + struct ext4_sblock *sb = &inode_ref->fs->sb; + + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t orig_checksum; + + /* Preparation: temporarily set bg checksum to 0 */ + orig_checksum = header->h_checksum; + header->h_checksum = 0; + /* First calculate crc32 checksum against fs uuid */ + checksum = + ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); + /* Then calculate crc32 checksum block number */ + checksum = + ext4_crc32c(checksum, &le64_blocknr, sizeof(le64_blocknr)); + /* Finally calculate crc32 checksum against + * the entire xattr block */ + checksum = + ext4_crc32c(checksum, header, ext4_sb_get_block_size(sb)); + header->h_checksum = orig_checksum; + } + return checksum; +} +#else +#define ext4_xattr_block_checksum(...) 0 +#endif + +static void ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref, + ext4_fsblk_t blocknr __unused, + struct ext4_xattr_header *header) +{ + struct ext4_sblock *sb = &inode_ref->fs->sb; + if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + return; + + header->h_checksum = + ext4_xattr_block_checksum(inode_ref, blocknr, header); +} + +struct xattr_prefix { + const char *prefix; + uint8_t name_index; +}; + +static const struct xattr_prefix prefix_tbl[] = { + {"user.", EXT4_XATTR_INDEX_USER}, + {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS}, + {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT}, + {"trusted.", EXT4_XATTR_INDEX_TRUSTED}, + {"security.", EXT4_XATTR_INDEX_SECURITY}, + {"system.", EXT4_XATTR_INDEX_SYSTEM}, + {"system.richacl", EXT4_XATTR_INDEX_RICHACL}, + {NULL, 0}, +}; + +const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len, + uint8_t *name_index, size_t *name_len, + bool *found) +{ + int i; + ext4_assert(name_index); + ext4_assert(found); + + *found = false; + + if (!full_name_len) { + if (name_len) + *name_len = 0; + + return NULL; + } + + for (i = 0; prefix_tbl[i].prefix; i++) { + size_t prefix_len = strlen(prefix_tbl[i].prefix); + if (full_name_len >= prefix_len && + !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) { + bool require_name = + prefix_tbl[i].prefix[prefix_len - 1] == '.'; + *name_index = prefix_tbl[i].name_index; + if (name_len) + *name_len = full_name_len - prefix_len; + + if (!(full_name_len - prefix_len) && require_name) + return NULL; + + *found = true; + if (require_name) + return full_name + prefix_len; + + return NULL; + } + } + if (name_len) + *name_len = 0; + + return NULL; +} + +const char *ext4_get_xattr_name_prefix(uint8_t name_index, + size_t *ret_prefix_len) +{ + int i; + + for (i = 0; prefix_tbl[i].prefix; i++) { + size_t prefix_len = strlen(prefix_tbl[i].prefix); + if (prefix_tbl[i].name_index == name_index) { + if (ret_prefix_len) + *ret_prefix_len = prefix_len; + + return prefix_tbl[i].prefix; + } + } + if (ret_prefix_len) + *ret_prefix_len = 0; + + return NULL; +} + +static const char ext4_xattr_empty_value; + +/** + * @brief Insert/Remove/Modify the given entry + * + * @param i The information of the given EA entry + * @param s Search context block + * @param dry_run Do not modify the content of the buffer + * + * @return Return EOK when finished, ENOSPC when there is no enough space + */ +static int ext4_xattr_set_entry(struct ext4_xattr_info *i, + struct ext4_xattr_search *s, bool dry_run) +{ + struct ext4_xattr_entry *last; + size_t free, min_offs = (char *)s->end - (char *)s->base, + name_len = i->name_len; + + /* + * If the entry is going to be removed but not found, return 0 to + * indicate success. + */ + if (!i->value && s->not_found) + return EOK; + + /* Compute min_offs and last. */ + last = s->first; + for (; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + if (last->e_value_size) { + size_t offs = to_le16(last->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + } + + /* Calculate free space in the block. */ + free = min_offs - ((char *)last - (char *)s->base) - sizeof(uint32_t); + if (!s->not_found) + free += EXT4_XATTR_SIZE(s->here->e_value_size) + + EXT4_XATTR_LEN(s->here->e_name_len); + + if (i->value) { + /* See whether there is enough space to hold new entry */ + if (free < + EXT4_XATTR_SIZE(i->value_len) + EXT4_XATTR_LEN(name_len)) + return ENOSPC; + } + + /* Return EOK now if we do not intend to modify the content. */ + if (dry_run) + return EOK; + + /* First remove the old entry's data part */ + if (!s->not_found) { + size_t value_offs = to_le16(s->here->e_value_offs); + void *value = (char *)s->base + value_offs; + void *first_value = (char *)s->base + min_offs; + size_t value_size = + EXT4_XATTR_SIZE(to_le32(s->here->e_value_size)); + + if (value_offs) { + /* Remove the data part. */ + memmove((char *)first_value + value_size, first_value, + (char *)value - (char *)first_value); + + /* Zero the gap created */ + memset(first_value, 0, value_size); + + /* + * Calculate the new min_offs after removal of the old + * entry's data part + */ + min_offs += value_size; + } + + /* + * Adjust the value offset of entries which has value offset + * prior to the s->here. The offset of these entries won't be + * shifted if the size of the entry we removed is zero. + */ + for (last = s->first; !EXT4_XATTR_IS_LAST_ENTRY(last); + last = EXT4_XATTR_NEXT(last)) { + size_t offs = to_le16(last->e_value_offs); + + /* For zero-value-length entry, offs will be zero. */ + if (offs < value_offs) + last->e_value_offs = to_le16(offs + value_size); + } + } + + /* If caller wants us to insert... */ + if (i->value) { + size_t value_offs; + if (i->value_len) + value_offs = min_offs - EXT4_XATTR_SIZE(i->value_len); + else + value_offs = 0; + + if (!s->not_found) { + struct ext4_xattr_entry *here = s->here; + + /* Reuse the current entry we have got */ + here->e_value_offs = to_le16(value_offs); + here->e_value_size = to_le32(i->value_len); + } else { + /* Insert a new entry */ + last->e_name_len = (uint8_t)name_len; + last->e_name_index = i->name_index; + last->e_value_offs = to_le16(value_offs); + last->e_value_block = 0; + last->e_value_size = to_le32(i->value_len); + memcpy(EXT4_XATTR_NAME(last), i->name, name_len); + + /* Set valid last entry indicator */ + *(uint32_t *)EXT4_XATTR_NEXT(last) = 0; + + s->here = last; + } + + /* Insert the value's part */ + if (value_offs) { + memcpy((char *)s->base + value_offs, i->value, + i->value_len); + + /* Clear the padding bytes if there is */ + if (EXT4_XATTR_SIZE(i->value_len) != i->value_len) + memset((char *)s->base + value_offs + + i->value_len, + 0, EXT4_XATTR_SIZE(i->value_len) - + i->value_len); + } + } else { + size_t shift_offs; + + /* Remove the whole entry */ + shift_offs = (char *)EXT4_XATTR_NEXT(s->here) - (char *)s->here; + memmove(s->here, EXT4_XATTR_NEXT(s->here), + (char *)last + sizeof(uint32_t) - + (char *)EXT4_XATTR_NEXT(s->here)); + + /* Zero the gap created */ + memset((char *)last - shift_offs + sizeof(uint32_t), 0, + shift_offs); + + s->here = NULL; + } + + return EOK; +} + +static inline bool ext4_xattr_is_empty(struct ext4_xattr_search *s) +{ + if (!EXT4_XATTR_IS_LAST_ENTRY(s->first)) + return false; + + return true; +} + +/** + * @brief Find the entry according to given information + * + * @param i The information of the EA entry to be found, + * including name_index, name and the length of name + * @param s Search context block + */ +static void ext4_xattr_find_entry(struct ext4_xattr_info *i, + struct ext4_xattr_search *s) +{ + struct ext4_xattr_entry *entry = NULL; + + s->not_found = true; + s->here = NULL; + + /* + * Find the wanted EA entry by simply comparing the namespace, + * name and the length of name. + */ + for (entry = s->first; !EXT4_XATTR_IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) { + size_t name_len = entry->e_name_len; + const char *name = EXT4_XATTR_NAME(entry); + if (name_len == i->name_len && + entry->e_name_index == i->name_index && + !memcmp(name, i->name, name_len)) { + s->here = entry; + s->not_found = false; + i->value_len = to_le32(entry->e_value_size); + if (i->value_len) + i->value = (char *)s->base + + to_le16(entry->e_value_offs); + else + i->value = NULL; + + return; + } + } +} + +/** + * @brief Check whether the xattr block's content is valid + * + * @param inode_ref Inode reference + * @param block The block buffer to be validated + * + * @return true if @block is valid, false otherwise. + */ +static bool ext4_xattr_is_block_valid(struct ext4_inode_ref *inode_ref, + struct ext4_block *block) +{ + + void *base = block->data, + *end = block->data + ext4_sb_get_block_size(&inode_ref->fs->sb); + size_t min_offs = (char *)end - (char *)base; + struct ext4_xattr_header *header = EXT4_XATTR_BHDR(block); + struct ext4_xattr_entry *entry = EXT4_XATTR_BFIRST(block); + + /* + * Check whether the magic number in the header is correct. + */ + if (header->h_magic != to_le32(EXT4_XATTR_MAGIC)) + return false; + + /* + * The in-kernel filesystem driver only supports 1 block currently. + */ + if (header->h_blocks != to_le32(1)) + return false; + + /* + * Check if those entries are maliciously corrupted to inflict harm + * upon us. + */ + for (; !EXT4_XATTR_IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) { + if (!to_le32(entry->e_value_size) && + to_le16(entry->e_value_offs)) + return false; + + if ((char *)base + to_le16(entry->e_value_offs) + + to_le32(entry->e_value_size) > + (char *)end) + return false; + + /* + * The name length field should also be correct, + * also there should be an 4-byte zero entry at the + * end. + */ + if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) > + (char *)end) + return false; + + if (to_le32(entry->e_value_size)) { + size_t offs = to_le16(entry->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + } + /* + * Entry field and data field do not override each other. + */ + if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t)) + return false; + + return true; +} + +/** + * @brief Check whether the inode buffer's content is valid + * + * @param inode_ref Inode reference + * + * @return true if the inode buffer is valid, false otherwise. + */ +static bool ext4_xattr_is_ibody_valid(struct ext4_inode_ref *inode_ref) +{ + size_t min_offs; + void *base, *end; + struct ext4_fs *fs = inode_ref->fs; + struct ext4_xattr_ibody_header *iheader; + struct ext4_xattr_entry *entry; + size_t inode_size = ext4_get16(&fs->sb, inode_size); + + iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode); + entry = EXT4_XATTR_IFIRST(iheader); + base = iheader; + end = (char *)inode_ref->inode + inode_size; + min_offs = (char *)end - (char *)base; + + /* + * Check whether the magic number in the header is correct. + */ + if (iheader->h_magic != to_le32(EXT4_XATTR_MAGIC)) + return false; + + /* + * Check if those entries are maliciously corrupted to inflict harm + * upon us. + */ + for (; !EXT4_XATTR_IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) { + if (!to_le32(entry->e_value_size) && + to_le16(entry->e_value_offs)) + return false; + + if ((char *)base + to_le16(entry->e_value_offs) + + to_le32(entry->e_value_size) > + (char *)end) + return false; + + /* + * The name length field should also be correct, + * also there should be an 4-byte zero entry at the + * end. + */ + if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) > + (char *)end) + return false; + + if (to_le32(entry->e_value_size)) { + size_t offs = to_le16(entry->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + } + /* + * Entry field and data field do not override each other. + */ + if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t)) + return false; + + return true; +} + +/** + * @brief An EA entry finder for inode buffer + */ +struct ext4_xattr_finder { + /** + * @brief The information of the EA entry to be find + */ + struct ext4_xattr_info i; + + /** + * @brief Search context block of the current search + */ + struct ext4_xattr_search s; + + /** + * @brief Inode reference to the corresponding inode + */ + struct ext4_inode_ref *inode_ref; +}; + +static void ext4_xattr_ibody_initialize(struct ext4_inode_ref *inode_ref) +{ + struct ext4_xattr_ibody_header *header; + struct ext4_fs *fs = inode_ref->fs; + size_t extra_isize = + ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode); + size_t inode_size = ext4_get16(&fs->sb, inode_size); + if (!extra_isize) + return; + + header = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode); + memset(header, 0, inode_size - EXT4_GOOD_OLD_INODE_SIZE - extra_isize); + header->h_magic = to_le32(EXT4_XATTR_MAGIC); + inode_ref->dirty = true; +} + +/** + * @brief Initialize a given xattr block + * + * @param inode_ref Inode reference + * @param block xattr block buffer + */ +static void ext4_xattr_block_initialize(struct ext4_inode_ref *inode_ref, + struct ext4_block *block) +{ + struct ext4_xattr_header *header; + struct ext4_fs *fs = inode_ref->fs; + + memset(block->data, 0, ext4_sb_get_block_size(&fs->sb)); + + header = EXT4_XATTR_BHDR(block); + header->h_magic = to_le32(EXT4_XATTR_MAGIC); + header->h_refcount = to_le32(1); + header->h_blocks = to_le32(1); + + ext4_trans_set_block_dirty(block->buf); +} + +static void ext4_xattr_block_init_search(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_search *s, + struct ext4_block *block) +{ + s->base = block->data; + s->end = block->data + ext4_sb_get_block_size(&inode_ref->fs->sb); + s->first = EXT4_XATTR_BFIRST(block); + s->here = NULL; + s->not_found = true; +} + +/** + * @brief Find an EA entry inside a xattr block + * + * @param inode_ref Inode reference + * @param finder The caller-provided finder block with + * information filled + * @param block The block buffer to be looked into + * + * @return Return EOK no matter the entry is found or not. + * If the IO operation or the buffer validation failed, + * return other value. + */ +static int ext4_xattr_block_find_entry(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_finder *finder, + struct ext4_block *block) +{ + int ret = EOK; + + /* Initialize the caller-given finder */ + finder->inode_ref = inode_ref; + memset(&finder->s, 0, sizeof(finder->s)); + + if (ret != EOK) + return ret; + + /* Check the validity of the buffer */ + if (!ext4_xattr_is_block_valid(inode_ref, block)) + return EIO; + + ext4_xattr_block_init_search(inode_ref, &finder->s, block); + ext4_xattr_find_entry(&finder->i, &finder->s); + return EOK; +} + +/** + * @brief Find an EA entry inside an inode's extra space + * + * @param inode_ref Inode reference + * @param finder The caller-provided finder block with + * information filled + * + * @return Return EOK no matter the entry is found or not. + * If the IO operation or the buffer validation failed, + * return other value. + */ +static int ext4_xattr_ibody_find_entry(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_finder *finder) +{ + struct ext4_fs *fs = inode_ref->fs; + struct ext4_xattr_ibody_header *iheader; + size_t extra_isize = + ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode); + size_t inode_size = ext4_get16(&fs->sb, inode_size); + + /* Initialize the caller-given finder */ + finder->inode_ref = inode_ref; + memset(&finder->s, 0, sizeof(finder->s)); + + /* + * If there is no extra inode space + * set ext4_xattr_ibody_finder::s::not_found to true and return EOK + */ + if (!extra_isize) { + finder->s.not_found = true; + return EOK; + } + + /* Check the validity of the buffer */ + if (!ext4_xattr_is_ibody_valid(inode_ref)) + return EIO; + + iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode); + finder->s.base = EXT4_XATTR_IFIRST(iheader); + finder->s.end = (char *)inode_ref->inode + inode_size; + finder->s.first = EXT4_XATTR_IFIRST(iheader); + ext4_xattr_find_entry(&finder->i, &finder->s); + return EOK; +} + +/** + * @brief Try to allocate a block holding EA entries. + * + * @param inode_ref Inode reference + * + * @return Error code + */ +static int ext4_xattr_try_alloc_block(struct ext4_inode_ref *inode_ref) +{ + int ret = EOK; + + ext4_fsblk_t xattr_block = 0; + xattr_block = + ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb); + + /* + * Only allocate a xattr block when there is no xattr block + * used by the inode. + */ + if (!xattr_block) { + ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref); + + ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block); + if (ret != EOK) + goto Finish; + + ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb, + xattr_block); + } + +Finish: + return ret; +} + +/** + * @brief Try to free a block holding EA entries. + * + * @param inode_ref Inode reference + * + * @return Error code + */ +static void ext4_xattr_try_free_block(struct ext4_inode_ref *inode_ref) +{ + ext4_fsblk_t xattr_block; + xattr_block = + ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb); + /* + * Free the xattr block used by the inode when there is one. + */ + if (xattr_block) { + ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb, + 0); + ext4_balloc_free_block(inode_ref, xattr_block); + inode_ref->dirty = true; + } +} + +/** + * @brief Put a list of EA entries into a caller-provided buffer + * In order to make sure that @list buffer can fit in the data, + * the routine should be called twice. + * + * @param inode_ref Inode reference + * @param list A caller-provided buffer to hold a list of EA entries. + * If list == NULL, list_len will contain the size of + * the buffer required to hold these entries + * @param list_len The length of the data written to @list + * @return Error code + */ +int ext4_xattr_list(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_list_entry *list, size_t *list_len) +{ + int ret = EOK; + size_t buf_len = 0; + struct ext4_fs *fs = inode_ref->fs; + struct ext4_xattr_ibody_header *iheader; + size_t extra_isize = + ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode); + struct ext4_block block; + bool block_loaded = false; + ext4_fsblk_t xattr_block = 0; + struct ext4_xattr_entry *entry; + struct ext4_xattr_list_entry *list_prev = NULL; + xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + + /* + * If there is extra inode space and the xattr buffer in the + * inode is valid. + */ + if (extra_isize && ext4_xattr_is_ibody_valid(inode_ref)) { + iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode); + entry = EXT4_XATTR_IFIRST(iheader); + + /* + * The format of the list should be like this: + * + * name_len indicates the length in bytes of the name + * of the EA entry. The string is null-terminated. + * + * list->name => (char *)(list + 1); + * list->next => (void *)((char *)(list + 1) + name_len + 1); + */ + for (; !EXT4_XATTR_IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) { + size_t name_len = entry->e_name_len; + if (list) { + list->name_index = entry->e_name_index; + list->name_len = name_len; + list->name = (char *)(list + 1); + memcpy(list->name, EXT4_XATTR_NAME(entry), + list->name_len); + + if (list_prev) + list_prev->next = list; + + list_prev = list; + list = (struct ext4_xattr_list_entry + *)(list->name + name_len + 1); + } + + /* + * Size calculation by pointer arithmetics. + */ + buf_len += + (char *)((struct ext4_xattr_list_entry *)0 + 1) + + name_len + 1 - + (char *)(struct ext4_xattr_list_entry *)0; + } + } + + /* + * If there is a xattr block used by the inode + */ + if (xattr_block) { + ret = ext4_trans_block_get(fs->bdev, &block, xattr_block); + if (ret != EOK) + goto out; + + block_loaded = true; + + /* + * As we don't allow the content in the block being invalid, + * bail out. + */ + if (!ext4_xattr_is_block_valid(inode_ref, &block)) { + ret = EIO; + goto out; + } + + entry = EXT4_XATTR_BFIRST(&block); + + /* + * The format of the list should be like this: + * + * name_len indicates the length in bytes of the name + * of the EA entry. The string is null-terminated. + * + * list->name => (char *)(list + 1); + * list->next => (void *)((char *)(list + 1) + name_len + 1); + * + * Same as above actually. + */ + for (; !EXT4_XATTR_IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) { + size_t name_len = entry->e_name_len; + if (list) { + list->name_index = entry->e_name_index; + list->name_len = name_len; + list->name = (char *)(list + 1); + memcpy(list->name, EXT4_XATTR_NAME(entry), + list->name_len); + + if (list_prev) + list_prev->next = list; + + list_prev = list; + list = (struct ext4_xattr_list_entry + *)(list->name + name_len + 1); + } + + /* + * Size calculation by pointer arithmetics. + */ + buf_len += + (char *)((struct ext4_xattr_list_entry *)0 + 1) + + name_len + 1 - + (char *)(struct ext4_xattr_list_entry *)0; + } + } + if (list_prev) + list_prev->next = NULL; +out: + if (ret == EOK && list_len) + *list_len = buf_len; + + if (block_loaded) + ext4_block_set(fs->bdev, &block); + + return ret; +} + +/** + * @brief Query EA entry's value with given name-index and name + * + * @param inode_ref Inode reference + * @param name_index Name-index + * @param name Name of the EA entry to be queried + * @param name_len Length of name in bytes + * @param buf Output buffer to hold content + * @param buf_len Output buffer's length + * @param data_len The length of data of the EA entry found + * + * @return Error code + */ +int ext4_xattr_get(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len, void *buf, size_t buf_len, + size_t *data_len) +{ + int ret = EOK; + struct ext4_xattr_finder ibody_finder; + struct ext4_xattr_finder block_finder; + struct ext4_xattr_info i; + size_t value_len = 0; + size_t value_offs = 0; + struct ext4_fs *fs = inode_ref->fs; + ext4_fsblk_t xattr_block; + xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + + i.name_index = name_index; + i.name = name; + i.name_len = name_len; + i.value = 0; + i.value_len = 0; + if (data_len) + *data_len = 0; + + ibody_finder.i = i; + ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder); + if (ret != EOK) + goto out; + + if (!ibody_finder.s.not_found) { + value_len = to_le32(ibody_finder.s.here->e_value_size); + value_offs = to_le32(ibody_finder.s.here->e_value_offs); + if (buf_len && buf) { + void *data_loc = + (char *)ibody_finder.s.base + value_offs; + memcpy(buf, data_loc, + (buf_len < value_len) ? buf_len : value_len); + } + } else { + struct ext4_block block; + + /* Return ENODATA if there is no EA block */ + if (!xattr_block) { + ret = ENODATA; + goto out; + } + + block_finder.i = i; + ret = ext4_trans_block_get(fs->bdev, &block, xattr_block); + if (ret != EOK) + goto out; + + ret = ext4_xattr_block_find_entry(inode_ref, &block_finder, + &block); + if (ret != EOK) { + ext4_block_set(fs->bdev, &block); + goto out; + } + + /* Return ENODATA if entry is not found */ + if (block_finder.s.not_found) { + ext4_block_set(fs->bdev, &block); + ret = ENODATA; + goto out; + } + + value_len = to_le32(block_finder.s.here->e_value_size); + value_offs = to_le32(block_finder.s.here->e_value_offs); + if (buf_len && buf) { + void *data_loc = + (char *)block_finder.s.base + value_offs; + memcpy(buf, data_loc, + (buf_len < value_len) ? buf_len : value_len); + } + + /* + * Free the xattr block buffer returned by + * ext4_xattr_block_find_entry. + */ + ext4_block_set(fs->bdev, &block); + } + +out: + if (ret == EOK && data_len) + *data_len = value_len; + + return ret; +} + +/** + * @brief Try to copy the content of an xattr block to a newly-allocated + * block. If the operation fails, the block buffer provided by + * caller will be freed + * + * @param inode_ref Inode reference + * @param block The block buffer reference + * @param new_block The newly-allocated block buffer reference + * @param orig_block The block number of @block + * @param allocated a new block is allocated + * + * @return Error code + */ +static int ext4_xattr_copy_new_block(struct ext4_inode_ref *inode_ref, + struct ext4_block *block, + struct ext4_block *new_block, + ext4_fsblk_t *orig_block, bool *allocated) +{ + int ret = EOK; + ext4_fsblk_t xattr_block = 0; + struct ext4_xattr_header *header; + struct ext4_fs *fs = inode_ref->fs; + header = EXT4_XATTR_BHDR(block); + + if (orig_block) + *orig_block = block->lb_id; + + if (allocated) + *allocated = false; + + /* Only do copy when a block is referenced by more than one inode. */ + if (to_le32(header->h_refcount) > 1) { + ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref); + + /* Allocate a new block to be used by this inode */ + ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block); + if (ret != EOK) + goto out; + + ret = ext4_trans_block_get(fs->bdev, new_block, xattr_block); + if (ret != EOK) + goto out; + + /* Copy the content of the whole block */ + memcpy(new_block->data, block->data, + ext4_sb_get_block_size(&inode_ref->fs->sb)); + + /* + * Decrement the reference count of the original xattr block + * by one + */ + header->h_refcount = to_le32(to_le32(header->h_refcount) - 1); + ext4_trans_set_block_dirty(block->buf); + ext4_trans_set_block_dirty(new_block->buf); + + header = EXT4_XATTR_BHDR(new_block); + header->h_refcount = to_le32(1); + + if (allocated) + *allocated = true; + } +out: + if (xattr_block) { + if (ret != EOK) + ext4_balloc_free_block(inode_ref, xattr_block); + else { + /* + * Modify the in-inode pointer to point to the new xattr block + */ + ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, xattr_block); + inode_ref->dirty = true; + } + } + + return ret; +} + +/** + * @brief Given an EA entry's name, remove the EA entry + * + * @param inode_ref Inode reference + * @param name_index Name-index + * @param name Name of the EA entry to be removed + * @param name_len Length of name in bytes + * + * @return Error code + */ +int ext4_xattr_remove(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len) +{ + int ret = EOK; + struct ext4_block block; + struct ext4_xattr_finder ibody_finder; + struct ext4_xattr_finder block_finder; + bool use_block = false; + bool block_loaded = false; + struct ext4_xattr_info i; + struct ext4_fs *fs = inode_ref->fs; + ext4_fsblk_t xattr_block; + + xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + + i.name_index = name_index; + i.name = name; + i.name_len = name_len; + i.value = NULL; + i.value_len = 0; + + ibody_finder.i = i; + block_finder.i = i; + + ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder); + if (ret != EOK) + goto out; + + if (ibody_finder.s.not_found && xattr_block) { + ret = ext4_trans_block_get(fs->bdev, &block, xattr_block); + if (ret != EOK) + goto out; + + block_loaded = true; + block_finder.i = i; + ret = ext4_xattr_block_find_entry(inode_ref, &block_finder, + &block); + if (ret != EOK) + goto out; + + /* Return ENODATA if entry is not found */ + if (block_finder.s.not_found) { + ret = ENODATA; + goto out; + } + use_block = true; + } + + if (use_block) { + bool allocated = false; + struct ext4_block new_block; + + /* + * There will be no effect when the xattr block is only referenced + * once. + */ + ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block, + &xattr_block, &allocated); + if (ret != EOK) + goto out; + + if (!allocated) { + /* Prevent double-freeing */ + block_loaded = false; + new_block = block; + } + + ret = ext4_xattr_block_find_entry(inode_ref, &block_finder, + &new_block); + if (ret != EOK) + goto out; + + /* Now remove the entry */ + ext4_xattr_set_entry(&i, &block_finder.s, false); + + if (ext4_xattr_is_empty(&block_finder.s)) { + ext4_block_set(fs->bdev, &new_block); + ext4_xattr_try_free_block(inode_ref); + } else { + struct ext4_xattr_header *header = + EXT4_XATTR_BHDR(&new_block); + header = EXT4_XATTR_BHDR(&new_block); + ext4_assert(block_finder.s.first); + ext4_xattr_rehash(header, block_finder.s.first); + ext4_xattr_set_block_checksum(inode_ref, + block.lb_id, + header); + + ext4_trans_set_block_dirty(new_block.buf); + ext4_block_set(fs->bdev, &new_block); + } + + } else { + /* Now remove the entry */ + ext4_xattr_set_entry(&i, &block_finder.s, false); + inode_ref->dirty = true; + } +out: + if (block_loaded) + ext4_block_set(fs->bdev, &block); + + return ret; +} + +/** + * @brief Insert/overwrite an EA entry into/in a xattr block + * + * @param inode_ref Inode reference + * @param i The information of the given EA entry + * + * @return Error code + */ +static int ext4_xattr_block_set(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_info *i, + bool no_insert) +{ + int ret = EOK; + bool allocated = false; + struct ext4_fs *fs = inode_ref->fs; + struct ext4_block block, new_block; + ext4_fsblk_t orig_xattr_block; + + orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + + ext4_assert(i->value); + if (!orig_xattr_block) { + struct ext4_xattr_search s; + struct ext4_xattr_header *header; + + /* If insertion of new entry is not allowed... */ + if (no_insert) { + ret = ENODATA; + goto out; + } + + ret = ext4_xattr_try_alloc_block(inode_ref); + if (ret != EOK) + goto out; + + orig_xattr_block = + ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block); + if (ret != EOK) { + ext4_xattr_try_free_block(inode_ref); + goto out; + } + + ext4_xattr_block_initialize(inode_ref, &block); + ext4_xattr_block_init_search(inode_ref, &s, &block); + + ret = ext4_xattr_set_entry(i, &s, false); + if (ret == EOK) { + header = EXT4_XATTR_BHDR(&block); + + ext4_assert(s.here); + ext4_assert(s.first); + ext4_xattr_compute_hash(header, s.here); + ext4_xattr_rehash(header, s.first); + ext4_xattr_set_block_checksum(inode_ref, + block.lb_id, + header); + ext4_trans_set_block_dirty(block.buf); + } + ext4_block_set(fs->bdev, &block); + if (ret != EOK) + ext4_xattr_try_free_block(inode_ref); + + } else { + struct ext4_xattr_finder finder; + struct ext4_xattr_header *header; + finder.i = *i; + ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block); + if (ret != EOK) + goto out; + + header = EXT4_XATTR_BHDR(&block); + + /* + * Consider the following case when insertion of new + * entry is not allowed + */ + if (to_le32(header->h_refcount) > 1 && no_insert) { + /* + * There are other people referencing the + * same xattr block + */ + ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block); + if (ret != EOK) { + ext4_block_set(fs->bdev, &block); + goto out; + } + if (finder.s.not_found) { + ext4_block_set(fs->bdev, &block); + ret = ENODATA; + goto out; + } + } + + /* + * There will be no effect when the xattr block is only referenced + * once. + */ + ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block, + &orig_xattr_block, &allocated); + if (ret != EOK) { + ext4_block_set(fs->bdev, &block); + goto out; + } + + if (allocated) { + ext4_block_set(fs->bdev, &block); + new_block = block; + } + + ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block); + if (ret != EOK) { + ext4_block_set(fs->bdev, &block); + goto out; + } + + ret = ext4_xattr_set_entry(i, &finder.s, false); + if (ret == EOK) { + header = EXT4_XATTR_BHDR(&block); + + ext4_assert(finder.s.here); + ext4_assert(finder.s.first); + ext4_xattr_compute_hash(header, finder.s.here); + ext4_xattr_rehash(header, finder.s.first); + ext4_xattr_set_block_checksum(inode_ref, + block.lb_id, + header); + ext4_trans_set_block_dirty(block.buf); + } + ext4_block_set(fs->bdev, &block); + } +out: + return ret; +} + +/** + * @brief Remove an EA entry from a xattr block + * + * @param inode_ref Inode reference + * @param i The information of the given EA entry + * + * @return Error code + */ +static int ext4_xattr_block_remove(struct ext4_inode_ref *inode_ref, + struct ext4_xattr_info *i) +{ + int ret = EOK; + bool allocated = false; + const void *value = i->value; + struct ext4_fs *fs = inode_ref->fs; + struct ext4_xattr_finder finder; + struct ext4_block block, new_block; + struct ext4_xattr_header *header; + ext4_fsblk_t orig_xattr_block; + orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + + ext4_assert(orig_xattr_block); + ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block); + if (ret != EOK) + goto out; + + /* + * There will be no effect when the xattr block is only referenced + * once. + */ + ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block, + &orig_xattr_block, &allocated); + if (ret != EOK) { + ext4_block_set(fs->bdev, &block); + goto out; + } + + if (allocated) { + ext4_block_set(fs->bdev, &block); + block = new_block; + } + + ext4_xattr_block_find_entry(inode_ref, &finder, &block); + + if (!finder.s.not_found) { + i->value = NULL; + ret = ext4_xattr_set_entry(i, &finder.s, false); + i->value = value; + + header = EXT4_XATTR_BHDR(&block); + ext4_assert(finder.s.first); + ext4_xattr_rehash(header, finder.s.first); + ext4_xattr_set_block_checksum(inode_ref, + block.lb_id, + header); + ext4_trans_set_block_dirty(block.buf); + } + + ext4_block_set(fs->bdev, &block); +out: + return ret; +} + +/** + * @brief Insert an EA entry into a given inode reference + * + * @param inode_ref Inode reference + * @param name_index Name-index + * @param name Name of the EA entry to be inserted + * @param name_len Length of name in bytes + * @param value Input buffer to hold content + * @param value_len Length of input content + * + * @return Error code + */ +int ext4_xattr_set(struct ext4_inode_ref *inode_ref, uint8_t name_index, + const char *name, size_t name_len, const void *value, + size_t value_len) +{ + int ret = EOK; + struct ext4_fs *fs = inode_ref->fs; + struct ext4_xattr_finder ibody_finder; + struct ext4_xattr_info i; + bool block_found = false; + ext4_fsblk_t orig_xattr_block; + size_t extra_isize = + ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode); + + i.name_index = name_index; + i.name = name; + i.name_len = name_len; + i.value = (value_len) ? value : &ext4_xattr_empty_value; + i.value_len = value_len; + + ibody_finder.i = i; + + orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + + /* + * Even if entry is not found, search context block inside the + * finder is still valid and can be used to insert entry. + */ + ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder); + if (ret != EOK) { + ext4_xattr_ibody_initialize(inode_ref); + ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder); + } + + if (ibody_finder.s.not_found) { + if (orig_xattr_block) { + block_found = true; + ret = ext4_xattr_block_set(inode_ref, &i, true); + if (ret == ENOSPC) + goto try_insert; + else if (ret == ENODATA) + goto try_insert; + else if (ret != EOK) + goto out; + + } else + goto try_insert; + + } else { + try_insert: + /* Only try to set entry in ibody if inode is sufficiently large */ + if (extra_isize) + ret = ext4_xattr_set_entry(&i, &ibody_finder.s, false); + else + ret = ENOSPC; + + if (ret == ENOSPC) { + if (!block_found) { + ret = ext4_xattr_block_set(inode_ref, &i, false); + ibody_finder.i.value = NULL; + ext4_xattr_set_entry(&ibody_finder.i, + &ibody_finder.s, false); + inode_ref->dirty = true; + } + + } else if (ret == EOK) { + if (block_found) + ret = ext4_xattr_block_remove(inode_ref, &i); + + inode_ref->dirty = true; + } + } + +out: + return ret; +} + +#endif + +/** + * @} + */ diff --git a/abm.inf b/abm.inf index f628d8a..d51d12b 100644 --- a/abm.inf +++ b/abm.inf @@ -51,6 +51,7 @@ IoLib PerformanceLib LVGLLib + lwext4Lib [LibraryClasses.AARCH64] BaseStackCheckLib