isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
This commit is contained in:
1 parent
3dacbd32c8
commit
73abd6f90d
441 files changed
+187073
-6549
No files matched your search
@@ -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 <ext4_config.h>
|
||||
#include <ext4_blockdev.h>
|
||||
#include <ext4_errno.h>
|
||||
|
||||
|
||||
/**********************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;
|
||||
}
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -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 <ext4_config.h>
|
||||
#include <ext4_blockdev.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**@brief File blockdev get.*/
|
||||
struct ext4_blockdev *ext4_blockdev_get(void);
|
||||
|
||||
#endif /* BLOCKDEV_H_ */
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files
|
||||
#include <windows.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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_oflags.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_blockdev.h"
|
||||
|
||||
/********************************OS LOCK INFERFACE***************************/
|
||||
|
||||
/**@brief OS dependent lock interface.*/
|
||||
struct ext4_lock {
|
||||
|
||||
/**@brief Lock access to mount point.*/
|
||||
void (*lock)(void);
|
||||
|
||||
/**@brief Unlock access to mount point.*/
|
||||
void (*unlock)(void);
|
||||
};
|
||||
|
||||
/********************************FILE DESCRIPTOR*****************************/
|
||||
|
||||
/**@brief File descriptor. */
|
||||
typedef struct ext4_file {
|
||||
|
||||
/**@brief Mount point handle.*/
|
||||
struct ext4_mountpoint *mp;
|
||||
|
||||
/**@brief File inode id.*/
|
||||
uint32_t inode;
|
||||
|
||||
/**@brief Open flags.*/
|
||||
uint32_t flags;
|
||||
|
||||
/**@brief File size.*/
|
||||
uint64_t fsize;
|
||||
|
||||
/**@brief 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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
|
||||
#include "ext4_fs.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**@brief Compute number of block group from block address.
|
||||
* @param sb superblock pointer.
|
||||
* @param baddr Absolute address of block.
|
||||
* @return Block group index
|
||||
*/
|
||||
uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s,
|
||||
ext4_fsblk_t baddr);
|
||||
|
||||
/**@brief Compute the starting block address of a block group
|
||||
* @param sb superblock pointer.
|
||||
* @param bgid block group index
|
||||
* @return Block address
|
||||
*/
|
||||
ext4_fsblk_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s,
|
||||
uint32_t bgid);
|
||||
|
||||
/**@brief Calculate and set checksum of block bitmap.
|
||||
* @param sb superblock pointer.
|
||||
* @param bg block group
|
||||
* @param bitmap bitmap buffer
|
||||
*/
|
||||
void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb,
|
||||
struct ext4_bgroup *bg,
|
||||
void *bitmap);
|
||||
|
||||
/**@brief Free block from inode.
|
||||
* @param inode_ref inode reference
|
||||
* @param baddr block address
|
||||
* @return standard error code*/
|
||||
int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref,
|
||||
ext4_fsblk_t baddr);
|
||||
|
||||
/**@brief Free blocks from inode.
|
||||
* @param inode_ref inode reference
|
||||
* @param first block address
|
||||
* @param count block count
|
||||
* @return standard error code*/
|
||||
int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref,
|
||||
ext4_fsblk_t first, uint32_t count);
|
||||
|
||||
/**@brief Allocate block procedure.
|
||||
* @param inode_ref inode reference
|
||||
* @param goal
|
||||
* @param baddr allocated block address
|
||||
* @return standard error code*/
|
||||
int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref,
|
||||
ext4_fsblk_t goal,
|
||||
ext4_fsblk_t *baddr);
|
||||
|
||||
/**@brief Try allocate selected block.
|
||||
* @param inode_ref inode reference
|
||||
* @param baddr block address to allocate
|
||||
* @param free if baddr is not allocated
|
||||
* @return standard error code*/
|
||||
int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref,
|
||||
ext4_fsblk_t baddr, bool *free);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_BALLOC_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "misc/tree.h"
|
||||
#include "misc/queue.h"
|
||||
|
||||
#define EXT4_BLOCK_ZERO() \
|
||||
{.lb_id = 0, .data = 0}
|
||||
|
||||
/**@brief Single block descriptor*/
|
||||
struct ext4_block {
|
||||
/**@brief Logical block ID*/
|
||||
uint64_t lb_id;
|
||||
|
||||
/**@brief Buffer */
|
||||
struct ext4_buf *buf;
|
||||
|
||||
/**@brief Data buffer.*/
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct ext4_bcache;
|
||||
|
||||
/**@brief Single block descriptor*/
|
||||
struct ext4_buf {
|
||||
/**@brief Flags*/
|
||||
int flags;
|
||||
|
||||
/**@brief Logical block address*/
|
||||
uint64_t lba;
|
||||
|
||||
/**@brief Data buffer.*/
|
||||
uint8_t *data;
|
||||
|
||||
/**@brief LRU priority. (unused) */
|
||||
uint32_t lru_prio;
|
||||
|
||||
/**@brief LRU id.*/
|
||||
uint32_t lru_id;
|
||||
|
||||
/**@brief Reference count table*/
|
||||
uint32_t refctr;
|
||||
|
||||
/**@brief The block cache this buffer belongs to. */
|
||||
struct ext4_bcache *bc;
|
||||
|
||||
/**@brief Whether or not buffer is on dirty list.*/
|
||||
bool on_dirty_list;
|
||||
|
||||
/**@brief LBA tree node*/
|
||||
RB_ENTRY(ext4_buf) lba_node;
|
||||
|
||||
/**@brief LRU tree node*/
|
||||
RB_ENTRY(ext4_buf) lru_node;
|
||||
|
||||
/**@brief Dirty list node*/
|
||||
SLIST_ENTRY(ext4_buf) dirty_node;
|
||||
|
||||
/**@brief Callback routine after a disk-write operation.
|
||||
* @param bc block cache descriptor
|
||||
* @param buf buffer descriptor
|
||||
* @param standard error code returned by bdev->bwrite()
|
||||
* @param arg argument passed to this routine*/
|
||||
void (*end_write)(struct ext4_bcache *bc,
|
||||
struct ext4_buf *buf,
|
||||
int res,
|
||||
void *arg);
|
||||
|
||||
/**@brief argument passed to end_write() callback.*/
|
||||
void *end_write_arg;
|
||||
};
|
||||
|
||||
/**@brief Block cache descriptor*/
|
||||
struct ext4_bcache {
|
||||
|
||||
/**@brief Item count in block cache*/
|
||||
uint32_t cnt;
|
||||
|
||||
/**@brief Item size in block cache*/
|
||||
uint32_t itemsize;
|
||||
|
||||
/**@brief Last recently used counter*/
|
||||
uint32_t lru_ctr;
|
||||
|
||||
/**@brief Currently referenced datablocks*/
|
||||
uint32_t ref_blocks;
|
||||
|
||||
/**@brief Maximum referenced datablocks*/
|
||||
uint32_t max_ref_blocks;
|
||||
|
||||
/**@brief The blockdev binded to this block cache*/
|
||||
struct ext4_blockdev *bdev;
|
||||
|
||||
/**@brief The cache should not be shaked */
|
||||
bool dont_shake;
|
||||
|
||||
/**@brief A tree holding all bufs*/
|
||||
RB_HEAD(ext4_buf_lba, ext4_buf) lba_root;
|
||||
|
||||
/**@brief A tree holding unreferenced bufs*/
|
||||
RB_HEAD(ext4_buf_lru, ext4_buf) lru_root;
|
||||
|
||||
/**@brief A singly-linked list holding dirty buffers*/
|
||||
SLIST_HEAD(ext4_buf_dirty, ext4_buf) dirty_list;
|
||||
};
|
||||
|
||||
/**@brief buffer state bits
|
||||
*
|
||||
* - BC♡UPTODATE: Buffer contains valid data.
|
||||
* - BC_DIRTY: Buffer is dirty.
|
||||
* - BC_FLUSH: Buffer will be immediately flushed,
|
||||
* when no one references it.
|
||||
* - BC_TMP: Buffer will be dropped once its refctr
|
||||
* reaches zero.
|
||||
*/
|
||||
enum bcache_state_bits {
|
||||
BC_UPTODATE,
|
||||
BC_DIRTY,
|
||||
BC_FLUSH,
|
||||
BC_TMP
|
||||
};
|
||||
|
||||
#define ext4_bcache_set_flag(buf, b) \
|
||||
(buf)->flags |= 1 << (b)
|
||||
|
||||
#define ext4_bcache_clear_flag(buf, b) \
|
||||
(buf)->flags &= ~(1 << (b))
|
||||
|
||||
#define ext4_bcache_test_flag(buf, b) \
|
||||
(((buf)->flags & (1 << (b))) >> (b))
|
||||
|
||||
static inline void ext4_bcache_set_dirty(struct ext4_buf *buf) {
|
||||
ext4_bcache_set_flag(buf, BC_UPTODATE);
|
||||
ext4_bcache_set_flag(buf, BC_DIRTY);
|
||||
}
|
||||
|
||||
static inline void ext4_bcache_clear_dirty(struct ext4_buf *buf) {
|
||||
ext4_bcache_clear_flag(buf, BC_UPTODATE);
|
||||
ext4_bcache_clear_flag(buf, BC_DIRTY);
|
||||
}
|
||||
|
||||
/**@brief Increment reference counter of buf by 1.*/
|
||||
#define ext4_bcache_inc_ref(buf) ((buf)->refctr++)
|
||||
|
||||
/**@brief Decrement reference counter of buf by 1.*/
|
||||
#define ext4_bcache_dec_ref(buf) ((buf)->refctr--)
|
||||
|
||||
/**@brief Insert buffer to dirty cache list
|
||||
* @param bc block cache descriptor
|
||||
* @param buf buffer descriptor */
|
||||
static inline void
|
||||
ext4_bcache_insert_dirty_node(struct ext4_bcache *bc, struct ext4_buf *buf) {
|
||||
if (!buf->on_dirty_list) {
|
||||
SLIST_INSERT_HEAD(&bc->dirty_list, buf, dirty_node);
|
||||
buf->on_dirty_list = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Remove buffer to dirty cache list
|
||||
* @param bc block cache descriptor
|
||||
* @param buf buffer descriptor */
|
||||
static inline void
|
||||
ext4_bcache_remove_dirty_node(struct ext4_bcache *bc, struct ext4_buf *buf) {
|
||||
if (buf->on_dirty_list) {
|
||||
SLIST_REMOVE(&bc->dirty_list, buf, ext4_buf, dirty_node);
|
||||
buf->on_dirty_list = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Dynamic initialization of block cache.
|
||||
* @param bc block cache descriptor
|
||||
* @param cnt items count in block cache
|
||||
* @param itemsize single item size (in bytes)
|
||||
* @return standard error code*/
|
||||
int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
|
||||
uint32_t itemsize);
|
||||
|
||||
/**@brief Do cleanup works on block cache.
|
||||
* @param bc block cache descriptor.*/
|
||||
void ext4_bcache_cleanup(struct ext4_bcache *bc);
|
||||
|
||||
/**@brief Dynamic de-initialization of block cache.
|
||||
* @param bc block cache descriptor
|
||||
* @return standard error code*/
|
||||
int ext4_bcache_fini_dynamic(struct ext4_bcache *bc);
|
||||
|
||||
/**@brief Get a buffer with the lowest LRU counter in bcache.
|
||||
* @param bc block cache descriptor
|
||||
* @return buffer with the lowest LRU counter*/
|
||||
struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc);
|
||||
|
||||
/**@brief Drop unreferenced buffer from bcache.
|
||||
* @param bc block cache descriptor
|
||||
* @param buf buffer*/
|
||||
void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf);
|
||||
|
||||
/**@brief Invalidate a 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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_bitmap.h
|
||||
* @brief Block/inode bitmap allocator.
|
||||
*/
|
||||
|
||||
#ifndef EXT4_BITMAP_H_
|
||||
#define EXT4_BITMAP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**@brief Set bitmap bit.
|
||||
* @param bmap bitmap
|
||||
* @param bit bit to set*/
|
||||
static inline void ext4_bmap_bit_set(uint8_t *bmap, uint32_t bit)
|
||||
{
|
||||
*(bmap + (bit >> 3)) |= (1 << (bit & 7));
|
||||
}
|
||||
|
||||
/**@brief Clear bitmap bit.
|
||||
* @param bmap bitmap buffer
|
||||
* @param bit bit to clear*/
|
||||
static inline void ext4_bmap_bit_clr(uint8_t *bmap, uint32_t bit)
|
||||
{
|
||||
*(bmap + (bit >> 3)) &= ~(1 << (bit & 7));
|
||||
}
|
||||
|
||||
/**@brief Check if the bitmap bit is set.
|
||||
* @param bmap bitmap buffer
|
||||
* @param bit bit to check*/
|
||||
static inline bool ext4_bmap_is_bit_set(uint8_t *bmap, uint32_t bit)
|
||||
{
|
||||
return (*(bmap + (bit >> 3)) & (1 << (bit & 7)));
|
||||
}
|
||||
|
||||
/**@brief Check if the bitmap bit is clear.
|
||||
* @param bmap bitmap buffer
|
||||
* @param bit bit to check*/
|
||||
static inline bool ext4_bmap_is_bit_clr(uint8_t *bmap, uint32_t bit)
|
||||
{
|
||||
return !ext4_bmap_is_bit_set(bmap, bit);
|
||||
}
|
||||
|
||||
/**@brief Free range of bits in bitmap.
|
||||
* @param bmap bitmap buffer
|
||||
* @param sbit start bit
|
||||
* @param bcnt bit count*/
|
||||
void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt);
|
||||
|
||||
/**@brief Find first clear bit in bitmap.
|
||||
* @param sbit start bit of search
|
||||
* @param ebit end bit of search
|
||||
* @param bit_id output parameter (first free bit)
|
||||
* @return standard error code*/
|
||||
int ext4_bmap_bit_find_clr(uint8_t *bmap, uint32_t sbit, uint32_t ebit,
|
||||
uint32_t *bit_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_BITMAP_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_super.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**@brief Get address of block with data block bitmap.
|
||||
* @param bg pointer to block group
|
||||
* @param s pointer to superblock
|
||||
* @return Address of block with block bitmap
|
||||
*/
|
||||
static inline uint64_t ext4_bg_get_block_bitmap(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
uint64_t v = to_le32(bg->block_bitmap_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint64_t)to_le32(bg->block_bitmap_hi) << 32;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set address of block with data block bitmap.
|
||||
* @param bg pointer to block group
|
||||
* @param s pointer to superblock
|
||||
* @param blk block to set
|
||||
* @return Address of block with block bitmap
|
||||
*/
|
||||
static inline void ext4_bg_set_block_bitmap(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s, uint64_t blk)
|
||||
{
|
||||
|
||||
bg->block_bitmap_lo = to_le32((uint32_t)blk);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->block_bitmap_hi = to_le32(blk >> 32);
|
||||
|
||||
}
|
||||
|
||||
/**@brief Get address of block with i-node bitmap.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @return Address of block with i-node bitmap
|
||||
*/
|
||||
static inline uint64_t ext4_bg_get_inode_bitmap(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
|
||||
uint64_t v = to_le32(bg->inode_bitmap_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint64_t)to_le32(bg->inode_bitmap_hi) << 32;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set address of block with i-node bitmap.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @param blk block to set
|
||||
* @return Address of block with i-node bitmap
|
||||
*/
|
||||
static inline void ext4_bg_set_inode_bitmap(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s, uint64_t blk)
|
||||
{
|
||||
bg->inode_bitmap_lo = to_le32((uint32_t)blk);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->inode_bitmap_hi = to_le32(blk >> 32);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**@brief Get address of the first block of the i-node table.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @return Address of first block of i-node table
|
||||
*/
|
||||
static inline uint64_t
|
||||
ext4_bg_get_inode_table_first_block(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
uint64_t v = to_le32(bg->inode_table_first_block_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint64_t)to_le32(bg->inode_table_first_block_hi) << 32;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set address of the first block of the i-node table.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @param blk block to set
|
||||
* @return Address of first block of i-node table
|
||||
*/
|
||||
static inline void
|
||||
ext4_bg_set_inode_table_first_block(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s, uint64_t blk)
|
||||
{
|
||||
bg->inode_table_first_block_lo = to_le32((uint32_t)blk);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->inode_table_first_block_hi = to_le32(blk >> 32);
|
||||
}
|
||||
|
||||
/**@brief Get number of free blocks in block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param sb Pointer to superblock
|
||||
* @return Number of free blocks in block group
|
||||
*/
|
||||
static inline uint32_t ext4_bg_get_free_blocks_count(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
uint32_t v = to_le16(bg->free_blocks_count_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint32_t)to_le16(bg->free_blocks_count_hi) << 16;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set number of free blocks in block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @param cnt Number of free blocks in block group
|
||||
*/
|
||||
static inline void ext4_bg_set_free_blocks_count(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s,
|
||||
uint32_t cnt)
|
||||
{
|
||||
bg->free_blocks_count_lo = to_le16((cnt << 16) >> 16);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->free_blocks_count_hi = to_le16(cnt >> 16);
|
||||
}
|
||||
|
||||
/**@brief Get number of free i-nodes in block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @return Number of free i-nodes in block group
|
||||
*/
|
||||
static inline uint32_t ext4_bg_get_free_inodes_count(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
uint32_t v = to_le16(bg->free_inodes_count_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint32_t)to_le16(bg->free_inodes_count_hi) << 16;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set number of free i-nodes in block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @param cnt Number of free i-nodes in block group
|
||||
*/
|
||||
static inline void ext4_bg_set_free_inodes_count(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s,
|
||||
uint32_t cnt)
|
||||
{
|
||||
bg->free_inodes_count_lo = to_le16((cnt << 16) >> 16);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->free_inodes_count_hi = to_le16(cnt >> 16);
|
||||
}
|
||||
|
||||
/**@brief Get number of used directories in block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @return Number of used directories in block group
|
||||
*/
|
||||
static inline uint32_t ext4_bg_get_used_dirs_count(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
uint32_t v = to_le16(bg->used_dirs_count_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint32_t)to_le16(bg->used_dirs_count_hi) << 16;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set number of used directories in block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @param cnt Number of used directories in block group
|
||||
*/
|
||||
static inline void ext4_bg_set_used_dirs_count(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s,
|
||||
uint32_t cnt)
|
||||
{
|
||||
bg->used_dirs_count_lo = to_le16((cnt << 16) >> 16);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->used_dirs_count_hi = to_le16(cnt >> 16);
|
||||
}
|
||||
|
||||
/**@brief Get number of unused i-nodes.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @return Number of unused i-nodes
|
||||
*/
|
||||
static inline uint32_t ext4_bg_get_itable_unused(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s)
|
||||
{
|
||||
|
||||
uint32_t v = to_le16(bg->itable_unused_lo);
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
v |= (uint32_t)to_le16(bg->itable_unused_hi) << 16;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**@brief Set number of unused i-nodes.
|
||||
* @param bg Pointer to block group
|
||||
* @param s Pointer to superblock
|
||||
* @param cnt Number of unused i-nodes
|
||||
*/
|
||||
static inline void ext4_bg_set_itable_unused(struct ext4_bgroup *bg,
|
||||
struct ext4_sblock *s,
|
||||
uint32_t cnt)
|
||||
{
|
||||
bg->itable_unused_lo = to_le16((cnt << 16) >> 16);
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->itable_unused_hi = to_le16(cnt >> 16);
|
||||
}
|
||||
|
||||
/**@brief Set checksum of block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param crc Cheksum of block group
|
||||
*/
|
||||
static inline void ext4_bg_set_checksum(struct ext4_bgroup *bg, uint16_t crc)
|
||||
{
|
||||
bg->checksum = to_le16(crc);
|
||||
}
|
||||
|
||||
/**@brief Check if block group has a flag.
|
||||
* @param bg Pointer to block group
|
||||
* @param flag Flag to be checked
|
||||
* @return True if flag is set to 1
|
||||
*/
|
||||
static inline bool ext4_bg_has_flag(struct ext4_bgroup *bg, uint32_t f)
|
||||
{
|
||||
return to_le16(bg->flags) & f;
|
||||
}
|
||||
|
||||
/**@brief Set flag of block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param flag Flag to be set
|
||||
*/
|
||||
static inline void ext4_bg_set_flag(struct ext4_bgroup *bg, uint32_t f)
|
||||
{
|
||||
uint16_t flags = to_le16(bg->flags);
|
||||
flags |= f;
|
||||
bg->flags = to_le16(flags);
|
||||
}
|
||||
|
||||
/**@brief Clear flag of block group.
|
||||
* @param bg Pointer to block group
|
||||
* @param flag Flag to be cleared
|
||||
*/
|
||||
static inline void ext4_bg_clear_flag(struct ext4_bgroup *bg, uint32_t f)
|
||||
{
|
||||
uint16_t flags = to_le16(bg->flags);
|
||||
flags &= ~f;
|
||||
bg->flags = to_le16(flags);
|
||||
}
|
||||
|
||||
/**@brief Calculate CRC16 of the block group.
|
||||
* @param crc Init value
|
||||
* @param buffer Input buffer
|
||||
* @param len Sizeof input buffer
|
||||
* @return Computed CRC16*/
|
||||
uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_BLOCK_GROUP_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_blockdev.h
|
||||
* @brief Block device module.
|
||||
*/
|
||||
|
||||
#ifndef EXT4_BLOCKDEV_H_
|
||||
#define EXT4_BLOCKDEV_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_bcache.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct ext4_blockdev_iface {
|
||||
/**@brief Open device function
|
||||
* @param bdev block device.*/
|
||||
int (*open)(struct ext4_blockdev *bdev);
|
||||
|
||||
/**@brief Block read function.
|
||||
* @param bdev block device
|
||||
* @param buf output buffer
|
||||
* @param blk_id block id
|
||||
* @param blk_cnt block count*/
|
||||
int (*bread)(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
|
||||
uint32_t blk_cnt);
|
||||
|
||||
/**@brief Block write function.
|
||||
* @param buf input buffer
|
||||
* @param blk_id block id
|
||||
* @param blk_cnt block count*/
|
||||
int (*bwrite)(struct ext4_blockdev *bdev, const void *buf,
|
||||
uint64_t blk_id, uint32_t blk_cnt);
|
||||
|
||||
/**@brief Close device function.
|
||||
* @param bdev block device.*/
|
||||
int (*close)(struct ext4_blockdev *bdev);
|
||||
|
||||
/**@brief Lock block device. Required in multi partition mode
|
||||
* operations. Not mandatory field.
|
||||
* @param bdev block device.*/
|
||||
int (*lock)(struct ext4_blockdev *bdev);
|
||||
|
||||
/**@brief Unlock block device. Required in multi partition mode
|
||||
* operations. Not mandatory field.
|
||||
* @param bdev block device.*/
|
||||
int (*unlock)(struct ext4_blockdev *bdev);
|
||||
|
||||
/**@brief Block size (bytes): physical*/
|
||||
uint32_t ph_bsize;
|
||||
|
||||
/**@brief Block count: physical*/
|
||||
uint64_t ph_bcnt;
|
||||
|
||||
/**@brief Block size buffer: physical*/
|
||||
uint8_t ph_bbuf[512];
|
||||
|
||||
/**@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 Track/Cylinder : physical*/
|
||||
uint32_t ph_tcnt;
|
||||
|
||||
/**@brief Sector/Track : physical*/
|
||||
uint32_t ph_scnt;
|
||||
|
||||
char fname[512];
|
||||
void* dev_file;
|
||||
};
|
||||
|
||||
/**@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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if !CONFIG_USE_DEFAULT_CFG
|
||||
#include "generated/ext4_config.h"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define F_SET_EXT2 2
|
||||
#define F_SET_EXT3 3
|
||||
#define F_SET_EXT4 4
|
||||
|
||||
#ifndef CONFIG_EXT_FEATURE_SET_LVL
|
||||
#define CONFIG_EXT_FEATURE_SET_LVL F_SET_EXT4
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#if CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT2
|
||||
/*Superblock features flag EXT2*/
|
||||
#define CONFIG_SUPPORTED_FCOM EXT2_SUPPORTED_FCOM
|
||||
#define CONFIG_SUPPORTED_FINCOM (EXT2_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
|
||||
#define CONFIG_SUPPORTED_FRO_COM EXT2_SUPPORTED_FRO_COM
|
||||
|
||||
#elif CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT3
|
||||
/*Superblock features flag EXT3*/
|
||||
#define CONFIG_SUPPORTED_FCOM EXT3_SUPPORTED_FCOM
|
||||
#define CONFIG_SUPPORTED_FINCOM (EXT3_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
|
||||
#define CONFIG_SUPPORTED_FRO_COM EXT3_SUPPORTED_FRO_COM
|
||||
#elif CONFIG_EXT_FEATURE_SET_LVL == F_SET_EXT4
|
||||
/*Superblock features flag EXT4*/
|
||||
#define CONFIG_SUPPORTED_FCOM EXT4_SUPPORTED_FCOM
|
||||
#define CONFIG_SUPPORTED_FINCOM (EXT4_SUPPORTED_FINCOM | EXT_FINCOM_IGNORED)
|
||||
#define CONFIG_SUPPORTED_FRO_COM EXT4_SUPPORTED_FRO_COM
|
||||
#else
|
||||
#define "Unsupported CONFIG_EXT_FEATURE_SET_LVL"
|
||||
#endif
|
||||
|
||||
#define CONFIG_DIR_INDEX_ENABLE (CONFIG_SUPPORTED_FCOM & EXT4_FCOM_DIR_INDEX)
|
||||
#define CONFIG_EXTENT_ENABLE (CONFIG_SUPPORTED_FINCOM & EXT4_FINCOM_EXTENTS)
|
||||
#define CONFIG_META_CSUM_ENABLE (CONFIG_SUPPORTED_FRO_COM & EXT4_FRO_COM_METADATA_CSUM)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**@brief Enable/disable journaling*/
|
||||
#ifndef CONFIG_JOURNALING_ENABLE
|
||||
#define CONFIG_JOURNALING_ENABLE 1
|
||||
#endif
|
||||
|
||||
/**@brief Enable/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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Based on FreeBSD.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_crc32.h
|
||||
* @brief Crc32c routine. Taken from FreeBSD kernel.
|
||||
*/
|
||||
|
||||
#ifndef LWEXT4_EXT4_CRC32C_H_
|
||||
#define LWEXT4_EXT4_CRC32C_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**@brief CRC32 algorithm.
|
||||
* @param crc input feed
|
||||
* @param buf input buffer
|
||||
* @param size input buffer length (bytes)
|
||||
* @return updated crc32 value*/
|
||||
uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size);
|
||||
|
||||
/**@brief CRC32C algorithm.
|
||||
* @param crc input feed
|
||||
* @param buf input buffer
|
||||
* @param length input buffer length (bytes)
|
||||
* @return updated crc32c value*/
|
||||
uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWEXT4_EXT4_CRC32C_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_errno.h"
|
||||
|
||||
#if !CONFIG_HAVE_OWN_ASSERT
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef PRIu64
|
||||
#define PRIu64 "llu"
|
||||
#endif
|
||||
|
||||
#ifndef PRId64
|
||||
#define PRId64 "lld"
|
||||
#endif
|
||||
|
||||
|
||||
#define DEBUG_BALLOC (1ul << 0)
|
||||
#define DEBUG_BCACHE (1ul << 1)
|
||||
#define DEBUG_BITMAP (1ul << 2)
|
||||
#define DEBUG_BLOCK_GROUP (1ul << 3)
|
||||
#define DEBUG_BLOCKDEV (1ul << 4)
|
||||
#define DEBUG_DIR_IDX (1ul << 5)
|
||||
#define DEBUG_DIR (1ul << 6)
|
||||
#define DEBUG_EXTENT (1ul << 7)
|
||||
#define DEBUG_FS (1ul << 8)
|
||||
#define DEBUG_HASH (1ul << 9)
|
||||
#define DEBUG_IALLOC (1ul << 10)
|
||||
#define DEBUG_INODE (1ul << 11)
|
||||
#define DEBUG_SUPER (1ul << 12)
|
||||
#define DEBUG_XATTR (1ul << 13)
|
||||
#define DEBUG_MKFS (1ul << 14)
|
||||
#define DEBUG_EXT4 (1ul << 15)
|
||||
#define DEBUG_JBD (1ul << 16)
|
||||
#define DEBUG_MBR (1ul << 17)
|
||||
|
||||
#define DEBUG_NOPREFIX (1ul << 31)
|
||||
#define DEBUG_ALL (0xFFFFFFFF)
|
||||
|
||||
static inline const char *ext4_dmask_id2str(uint32_t m)
|
||||
{
|
||||
switch(m) {
|
||||
case DEBUG_BALLOC:
|
||||
return "ext4_balloc: ";
|
||||
case DEBUG_BCACHE:
|
||||
return "ext4_bcache: ";
|
||||
case DEBUG_BITMAP:
|
||||
return "ext4_bitmap: ";
|
||||
case DEBUG_BLOCK_GROUP:
|
||||
return "ext4_block_group: ";
|
||||
case DEBUG_BLOCKDEV:
|
||||
return "ext4_blockdev: ";
|
||||
case DEBUG_DIR_IDX:
|
||||
return "ext4_dir_idx: ";
|
||||
case DEBUG_DIR:
|
||||
return "ext4_dir: ";
|
||||
case DEBUG_EXTENT:
|
||||
return "ext4_extent: ";
|
||||
case DEBUG_FS:
|
||||
return "ext4_fs: ";
|
||||
case DEBUG_HASH:
|
||||
return "ext4_hash: ";
|
||||
case DEBUG_IALLOC:
|
||||
return "ext4_ialloc: ";
|
||||
case DEBUG_INODE:
|
||||
return "ext4_inode: ";
|
||||
case DEBUG_SUPER:
|
||||
return "ext4_super: ";
|
||||
case DEBUG_XATTR:
|
||||
return "ext4_xattr: ";
|
||||
case DEBUG_MKFS:
|
||||
return "ext4_mkfs: ";
|
||||
case DEBUG_JBD:
|
||||
return "ext4_jbd: ";
|
||||
case DEBUG_MBR:
|
||||
return "ext4_mbr: ";
|
||||
case DEBUG_EXT4:
|
||||
return "ext4: ";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#define DBG_NONE ""
|
||||
#define DBG_INFO "[info] "
|
||||
#define DBG_WARN "[warn] "
|
||||
#define DBG_ERROR "[error] "
|
||||
|
||||
/**@brief Global mask debug set.
|
||||
* @brief m new debug mask.*/
|
||||
void ext4_dmask_set(uint32_t m);
|
||||
|
||||
/**@brief Global mask debug clear.
|
||||
* @brief m new debug mask.*/
|
||||
void ext4_dmask_clr(uint32_t m);
|
||||
|
||||
/**@brief Global debug mask get.
|
||||
* @return debug mask*/
|
||||
uint32_t ext4_dmask_get(void);
|
||||
|
||||
#if CONFIG_DEBUG_PRINTF
|
||||
#include <stdio.h>
|
||||
|
||||
/**@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 <stdio.h>
|
||||
|
||||
#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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_blockdev.h"
|
||||
#include "ext4_super.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
|
||||
#include "ext4_fs.h"
|
||||
#include "ext4_dir.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_errno.h
|
||||
* @brief Error codes.
|
||||
*/
|
||||
#ifndef EXT4_ERRNO_H_
|
||||
#define EXT4_ERRNO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
|
||||
#if !CONFIG_HAVE_OWN_ERRNO
|
||||
#include <errno.h>
|
||||
#else
|
||||
#define EPERM 1 /* Operation not permitted */
|
||||
#define ENOENT 2 /* No such file or directory */
|
||||
#define EIO 5 /* I/O error */
|
||||
#define ENXIO 6 /* No such device or address */
|
||||
#define E2BIG 7 /* Argument list too long */
|
||||
#define ENOMEM 12 /* Out of memory */
|
||||
#define EACCES 13 /* Permission denied */
|
||||
#define EFAULT 14 /* Bad address */
|
||||
#define EEXIST 17 /* File exists */
|
||||
#define ENODEV 19 /* No such device */
|
||||
#define ENOTDIR 20 /* Not a directory */
|
||||
#define EISDIR 21 /* Is a directory */
|
||||
#define EINVAL 22 /* Invalid argument */
|
||||
#define EFBIG 27 /* File too large */
|
||||
#define ENOSPC 28 /* No space left on device */
|
||||
#define EROFS 30 /* Read-only file system */
|
||||
#define EMLINK 31 /* Too many links */
|
||||
#define ERANGE 34 /* Math result not representable */
|
||||
#define ENOTEMPTY 39 /* Directory not empty */
|
||||
#define ENODATA 61 /* No data available */
|
||||
#define ENOTSUP 95 /* Not supported */
|
||||
#endif
|
||||
|
||||
#ifndef ENODATA
|
||||
#ifdef ENOATTR
|
||||
#define ENODATA ENOATTR
|
||||
#else
|
||||
#define ENODATA 61
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ENOTSUP
|
||||
#define ENOTSUP 95
|
||||
#endif
|
||||
|
||||
#ifndef EOK
|
||||
#define EOK 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_ERRNO_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
*
|
||||
*
|
||||
* HelenOS:
|
||||
* Copyright (c) 2012 Martin Sucha
|
||||
* Copyright (c) 2012 Frantisek Princ
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_extent.h
|
||||
* @brief More complex filesystem functions.
|
||||
*/
|
||||
#ifndef EXT4_EXTENT_H_
|
||||
#define EXT4_EXTENT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_inode.h"
|
||||
|
||||
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_ */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
*
|
||||
*
|
||||
* HelenOS:
|
||||
* Copyright (c) 2012 Martin Sucha
|
||||
* Copyright (c) 2012 Frantisek Princ
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_ialloc.c
|
||||
* @brief Inode allocation procedures.
|
||||
*/
|
||||
|
||||
#ifndef EXT4_IALLOC_H_
|
||||
#define EXT4_IALLOC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
|
||||
/**@brief Calculate and set checksum of inode bitmap.
|
||||
* @param sb superblock pointer.
|
||||
* @param bg block group
|
||||
* @param bitmap bitmap buffer
|
||||
*/
|
||||
void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
|
||||
void *bitmap);
|
||||
|
||||
/**@brief Free i-node number and modify filesystem data structers.
|
||||
* @param fs Filesystem, where the i-node is located
|
||||
* @param index Index of i-node to be release
|
||||
* @param is_dir Flag us for information whether i-node is directory or not
|
||||
*/
|
||||
int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir);
|
||||
|
||||
/**@brief I-node allocation algorithm.
|
||||
* This is more simple algorithm, than Orlov allocator used
|
||||
* in the Linux kernel.
|
||||
* @param fs Filesystem to allocate i-node on
|
||||
* @param index Output value - allocated i-node number
|
||||
* @param is_dir Flag if allocated i-node will be file or directory
|
||||
* @return Error code
|
||||
*/
|
||||
int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *index, bool is_dir);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_IALLOC_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**@brief Get mode of the i-node.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node to load mode from
|
||||
* @return Mode of the i-node
|
||||
*/
|
||||
uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set mode of the i-node.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node to set mode to
|
||||
* @param mode Mode to set to i-node
|
||||
*/
|
||||
void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode,
|
||||
uint32_t mode);
|
||||
|
||||
/**@brief Get ID of the i-node owner (user id).
|
||||
* @param inode I-node to load uid from
|
||||
* @return User ID of the i-node owner
|
||||
*/
|
||||
uint32_t ext4_inode_get_uid(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set ID of the i-node owner.
|
||||
* @param inode I-node to set uid to
|
||||
* @param uid ID of the i-node owner
|
||||
*/
|
||||
void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid);
|
||||
|
||||
/**@brief Get real i-node size.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node to load size from
|
||||
* @return Real size of i-node
|
||||
*/
|
||||
uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set real i-node size.
|
||||
* @param inode I-node to set size to
|
||||
* @param size Size of the i-node
|
||||
*/
|
||||
void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size);
|
||||
|
||||
/**@brief Get time, when i-node was last accessed.
|
||||
* @param inode I-node
|
||||
* @return Time of the last access (POSIX)
|
||||
*/
|
||||
uint32_t ext4_inode_get_access_time(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set time, when i-node was last accessed.
|
||||
* @param inode I-node
|
||||
* @param time Time of the last access (POSIX)
|
||||
*/
|
||||
void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time);
|
||||
|
||||
/**@brief Get time, when i-node was last changed.
|
||||
* @param inode I-node
|
||||
* @return Time of the last change (POSIX)
|
||||
*/
|
||||
uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set time, when i-node was last changed.
|
||||
* @param inode I-node
|
||||
* @param time Time of the last change (POSIX)
|
||||
*/
|
||||
void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time);
|
||||
|
||||
/**@brief Get time, when i-node content was last modified.
|
||||
* @param inode I-node
|
||||
* @return Time of the last content modification (POSIX)
|
||||
*/
|
||||
uint32_t ext4_inode_get_modif_time(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set time, when i-node content was last modified.
|
||||
* @param inode I-node
|
||||
* @param time Time of the last content modification (POSIX)
|
||||
*/
|
||||
void ext4_inode_set_modif_time(struct ext4_inode *inode, uint32_t time);
|
||||
|
||||
/**@brief Get time, when i-node was deleted.
|
||||
* @param inode I-node
|
||||
* @return Time of the delete action (POSIX)
|
||||
*/
|
||||
uint32_t ext4_inode_get_del_time(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set time, when i-node was deleted.
|
||||
* @param inode I-node
|
||||
* @param time Time of the delete action (POSIX)
|
||||
*/
|
||||
void ext4_inode_set_del_time(struct ext4_inode *inode, uint32_t time);
|
||||
|
||||
/**@brief Get ID of the i-node owner's group.
|
||||
* @param inode I-node to load gid from
|
||||
* @return Group ID of the i-node owner
|
||||
*/
|
||||
uint32_t ext4_inode_get_gid(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set ID to the i-node owner's group.
|
||||
* @param inode I-node to set gid to
|
||||
* @param gid Group ID of the i-node owner
|
||||
*/
|
||||
void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid);
|
||||
|
||||
/**@brief Get number of links to i-node.
|
||||
* @param inode I-node to load number of links from
|
||||
* @return Number of links to i-node
|
||||
*/
|
||||
uint16_t ext4_inode_get_links_cnt(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set number of links to i-node.
|
||||
* @param inode I-node to set number of links to
|
||||
* @param count Number of links to i-node
|
||||
*/
|
||||
void ext4_inode_set_links_cnt(struct ext4_inode *inode, uint16_t cnt);
|
||||
|
||||
/**@brief Get number of 512-bytes blocks used for i-node.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node
|
||||
* @return Number of 512-bytes blocks
|
||||
*/
|
||||
uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb,
|
||||
struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set number of 512-bytes blocks used for i-node.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node
|
||||
* @param count Number of 512-bytes blocks
|
||||
* @return Error code
|
||||
*/
|
||||
int ext4_inode_set_blocks_count(struct ext4_sblock *sb,
|
||||
struct ext4_inode *inode, uint64_t cnt);
|
||||
|
||||
/**@brief Get flags (features) of i-node.
|
||||
* @param inode I-node to get flags from
|
||||
* @return Flags (bitmap)
|
||||
*/
|
||||
uint32_t ext4_inode_get_flags(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set flags (features) of i-node.
|
||||
* @param inode I-node to set flags to
|
||||
* @param flags Flags to set to i-node
|
||||
*/
|
||||
void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags);
|
||||
|
||||
/**@brief Get file generation (used by NFS).
|
||||
* @param inode I-node
|
||||
* @return File generation
|
||||
*/
|
||||
uint32_t ext4_inode_get_generation(struct ext4_inode *inode);
|
||||
|
||||
/**@brief Set file generation (used by NFS).
|
||||
* @param inode I-node
|
||||
* @param generation File generation
|
||||
*/
|
||||
void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen);
|
||||
|
||||
/**@brief Get extra I-node size field.
|
||||
* @param 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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "misc/queue.h"
|
||||
#include "misc/tree.h"
|
||||
|
||||
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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_blockdev.h"
|
||||
|
||||
/**@brief Master boot record block devices descriptor*/
|
||||
struct ext4_mbr_bdevs {
|
||||
struct ext4_blockdev partitions[4];
|
||||
};
|
||||
|
||||
int ext4_mbr_scan(struct ext4_blockdev *parent, struct ext4_mbr_bdevs *bdevs);
|
||||
|
||||
/**@brief Master boot record partitions*/
|
||||
struct ext4_mbr_parts {
|
||||
|
||||
/**@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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
#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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
|
||||
#include "ext4_blockdev.h"
|
||||
#include "ext4_fs.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_oflags.h
|
||||
* @brief File opening & seeking flags.
|
||||
*/
|
||||
#ifndef EXT4_OFLAGS_H_
|
||||
#define EXT4_OFLAGS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/********************************FILE OPEN FLAGS*****************************/
|
||||
|
||||
#if CONFIG_HAVE_OWN_OFLAGS
|
||||
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY 00
|
||||
#endif
|
||||
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY 01
|
||||
#endif
|
||||
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR 02
|
||||
#endif
|
||||
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT 0100
|
||||
#endif
|
||||
|
||||
#ifndef O_EXCL
|
||||
#define O_EXCL 0200
|
||||
#endif
|
||||
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC 01000
|
||||
#endif
|
||||
|
||||
#ifndef O_APPEND
|
||||
#define O_APPEND 02000
|
||||
#endif
|
||||
|
||||
/********************************FILE SEEK FLAGS*****************************/
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
#else
|
||||
//#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_OFLAGS_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
|
||||
/**@brief Blocks count get stored in superblock.
|
||||
* @param s superblock descriptor
|
||||
* @return count of blocks*/
|
||||
static inline uint64_t ext4_sb_get_blocks_cnt(struct ext4_sblock *s)
|
||||
{
|
||||
return ((uint64_t)to_le32(s->blocks_count_hi) << 32) |
|
||||
to_le32(s->blocks_count_lo);
|
||||
}
|
||||
|
||||
/**@brief Blocks count set in superblock.
|
||||
* @param s superblock descriptor
|
||||
* @return count of blocks*/
|
||||
static inline void ext4_sb_set_blocks_cnt(struct ext4_sblock *s, uint64_t cnt)
|
||||
{
|
||||
s->blocks_count_lo = to_le32((cnt << 32) >> 32);
|
||||
s->blocks_count_hi = to_le32(cnt >> 32);
|
||||
}
|
||||
|
||||
/**@brief Free blocks count get stored in superblock.
|
||||
* @param s superblock descriptor
|
||||
* @return free blocks*/
|
||||
static inline uint64_t ext4_sb_get_free_blocks_cnt(struct ext4_sblock *s)
|
||||
{
|
||||
return ((uint64_t)to_le32(s->free_blocks_count_hi) << 32) |
|
||||
to_le32(s->free_blocks_count_lo);
|
||||
}
|
||||
|
||||
/**@brief Free blocks count set.
|
||||
* @param s superblock descriptor
|
||||
* @param cnt new value of free blocks*/
|
||||
static inline void ext4_sb_set_free_blocks_cnt(struct ext4_sblock *s,
|
||||
uint64_t cnt)
|
||||
{
|
||||
s->free_blocks_count_lo = to_le32((cnt << 32) >> 32);
|
||||
s->free_blocks_count_hi = to_le32(cnt >> 32);
|
||||
}
|
||||
|
||||
/**@brief Block size get from superblock.
|
||||
* @param s superblock descriptor
|
||||
* @return block size in bytes*/
|
||||
static inline uint32_t ext4_sb_get_block_size(struct ext4_sblock *s)
|
||||
{
|
||||
return 1024 << to_le32(s->log_block_size);
|
||||
}
|
||||
|
||||
/**@brief Block group descriptor size.
|
||||
* @param s superblock descriptor
|
||||
* @return block group descriptor size in bytes*/
|
||||
static inline uint16_t ext4_sb_get_desc_size(struct ext4_sblock *s)
|
||||
{
|
||||
uint16_t size = to_le16(s->desc_size);
|
||||
|
||||
return size < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
|
||||
? EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
|
||||
: size;
|
||||
}
|
||||
|
||||
/*************************Flags and features*********************************/
|
||||
|
||||
/**@brief Support check of flag.
|
||||
* @param s superblock descriptor
|
||||
* @param v flag to check
|
||||
* @return true if flag is supported*/
|
||||
static inline bool ext4_sb_check_flag(struct ext4_sblock *s, uint32_t v)
|
||||
{
|
||||
return to_le32(s->flags) & v;
|
||||
}
|
||||
|
||||
/**@brief Support check of feature compatible.
|
||||
* @param s superblock descriptor
|
||||
* @param v feature to check
|
||||
* @return true if feature is supported*/
|
||||
static inline bool ext4_sb_feature_com(struct ext4_sblock *s, uint32_t v)
|
||||
{
|
||||
return to_le32(s->features_compatible) & v;
|
||||
}
|
||||
|
||||
/**@brief Support check of feature incompatible.
|
||||
* @param s superblock descriptor
|
||||
* @param v feature to check
|
||||
* @return true if feature is supported*/
|
||||
static inline bool ext4_sb_feature_incom(struct ext4_sblock *s, uint32_t v)
|
||||
{
|
||||
return to_le32(s->features_incompatible) & v;
|
||||
}
|
||||
|
||||
/**@brief Support check of read only flag.
|
||||
* @param s superblock descriptor
|
||||
* @param v flag to check
|
||||
* @return true if flag is supported*/
|
||||
static inline bool ext4_sb_feature_ro_com(struct ext4_sblock *s, uint32_t v)
|
||||
{
|
||||
return to_le32(s->features_read_only) & v;
|
||||
}
|
||||
|
||||
/**@brief Block group to flex group.
|
||||
* @param s superblock descriptor
|
||||
* @param block_group block group
|
||||
* @return flex group id*/
|
||||
static inline uint32_t ext4_sb_bg_to_flex(struct ext4_sblock *s,
|
||||
uint32_t block_group)
|
||||
{
|
||||
return block_group >> to_le32(s->log_groups_per_flex);
|
||||
}
|
||||
|
||||
/**@brief Flex block group size.
|
||||
* @param s superblock descriptor
|
||||
* @return flex bg size*/
|
||||
static inline uint32_t ext4_sb_flex_bg_size(struct ext4_sblock *s)
|
||||
{
|
||||
return 1 << to_le32(s->log_groups_per_flex);
|
||||
}
|
||||
|
||||
/**@brief Return first meta block group id.
|
||||
* @param s superblock descriptor
|
||||
* @return first meta_bg id */
|
||||
static inline uint32_t ext4_sb_first_meta_bg(struct ext4_sblock *s)
|
||||
{
|
||||
return to_le32(s->first_meta_bg);
|
||||
}
|
||||
|
||||
/**************************More complex functions****************************/
|
||||
|
||||
/**@brief Returns a block group count.
|
||||
* @param s superblock descriptor
|
||||
* @return count of block groups*/
|
||||
uint32_t ext4_block_group_cnt(struct ext4_sblock *s);
|
||||
|
||||
/**@brief Returns block count in block group
|
||||
* (last block group may have less blocks)
|
||||
* @param s superblock descriptor
|
||||
* @param bgid block group id
|
||||
* @return blocks count*/
|
||||
uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid);
|
||||
|
||||
/**@brief Returns inodes count in block group
|
||||
* (last block group may have less inodes)
|
||||
* @param s superblock descriptor
|
||||
* @param bgid block group id
|
||||
* @return inodes count*/
|
||||
uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid);
|
||||
|
||||
/***************************Read/write/check superblock**********************/
|
||||
|
||||
/**@brief Superblock write.
|
||||
* @param bdev block device descriptor.
|
||||
* @param s superblock descriptor
|
||||
* @return Standard error code */
|
||||
int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s);
|
||||
|
||||
/**@brief Superblock read.
|
||||
* @param bdev block device descriptor.
|
||||
* @param s superblock descriptor
|
||||
* @return Standard error code */
|
||||
int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s);
|
||||
|
||||
/**@brief Superblock simple validation.
|
||||
* @param s superblock descriptor
|
||||
* @return true if OK*/
|
||||
bool ext4_sb_check(struct ext4_sblock *s);
|
||||
|
||||
/**@brief Superblock presence in block group.
|
||||
* @param s superblock descriptor
|
||||
* @param block_group block group id
|
||||
* @return true if block group has superblock*/
|
||||
bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t block_group);
|
||||
|
||||
/**@brief TODO:*/
|
||||
bool ext4_sb_sparse(uint32_t group);
|
||||
|
||||
/**@brief TODO:*/
|
||||
uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group);
|
||||
|
||||
/**@brief TODO:*/
|
||||
uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
|
||||
uint32_t block_group);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_SUPER_H_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_trans.h
|
||||
* @brief Transaction handle functions
|
||||
*/
|
||||
|
||||
#ifndef EXT4_TRANS_H
|
||||
#define EXT4_TRANS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
|
||||
|
||||
/**@brief Mark a buffer dirty and add it to the current transaction.
|
||||
* @param buf buffer
|
||||
* @return standard error code*/
|
||||
int ext4_trans_set_block_dirty(struct ext4_buf *buf);
|
||||
|
||||
/**@brief Block get function (through cache, don't read).
|
||||
* jbd_trans_get_access would be called in order to
|
||||
* get write access to the buffer.
|
||||
* @param bdev block device descriptor
|
||||
* @param b block descriptor
|
||||
* @param lba logical block address
|
||||
* @return standard error code*/
|
||||
int ext4_trans_block_get_noread(struct ext4_blockdev *bdev,
|
||||
struct ext4_block *b,
|
||||
uint64_t lba);
|
||||
|
||||
/**@brief Block get function (through cache).
|
||||
* jbd_trans_get_access would be called in order to
|
||||
* get write access to the buffer.
|
||||
* @param bdev block device descriptor
|
||||
* @param b block descriptor
|
||||
* @param lba logical block address
|
||||
* @return standard error code*/
|
||||
int ext4_trans_block_get(struct ext4_blockdev *bdev,
|
||||
struct ext4_block *b,
|
||||
uint64_t lba);
|
||||
|
||||
/**@brief Try to add block to be revoked to the current transaction.
|
||||
* @param bdev block device descriptor
|
||||
* @param lba logical block address
|
||||
* @return standard error code*/
|
||||
int ext4_trans_try_revoke_block(struct ext4_blockdev *bdev,
|
||||
uint64_t lba);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXT4_TRANS_H */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_blockdev.h"
|
||||
#include "misc/tree.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_inode.h"
|
||||
|
||||
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
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
#define CONFIG_USE_DEFAULT_CONFIG 0
|
||||
#define CONFIG_HAVE_OWN_OFLAGS 0
|
||||
#define CONFIG_HAVE_OWN_ERRNO 0
|
||||
#define CONFIG_HAVE_OWN_ASSERT 0
|
||||
#define CONFIG_BLOCK_DEV_CACHE_SIZE 16
|
||||
@@ -0,0 +1,653 @@
|
||||
#ifndef __GETOPT_H__
|
||||
/**
|
||||
* DISCLAIMER
|
||||
* This file is part of the mingw-w64 runtime package.
|
||||
*
|
||||
* The mingw-w64 runtime package and its code is distributed in the hope that it
|
||||
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
|
||||
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
|
||||
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
||||
*/
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#define __GETOPT_H__
|
||||
|
||||
/* All the headers include this file. */
|
||||
#include <crtdefs.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = '?'; /* character checked for validity */
|
||||
#undef optreset /* see getopt.h */
|
||||
#define optreset __mingw_optreset
|
||||
int optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
#endif
|
||||
|
||||
//extern int optind; /* index of first non-option in argv */
|
||||
//extern int optopt; /* single option character, as parsed */
|
||||
//extern int opterr; /* flag to enable built-in diagnostics... */
|
||||
// /* (user may set to zero, to suppress) */
|
||||
//
|
||||
//extern char *optarg; /* pointer to argument of current option */
|
||||
|
||||
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER (int)1
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
#define __progname __argv[0]
|
||||
#else
|
||||
extern char __declspec(dllimport) *__progname;
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
static char EMSG[] = "";
|
||||
#else
|
||||
#define EMSG ""
|
||||
#endif
|
||||
|
||||
static int getopt_internal(int, char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int parse_long_options(char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char * const *);
|
||||
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
|
||||
/* XXX: set optreset to 1 rather than these two */
|
||||
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||
|
||||
/* Error messages */
|
||||
static const char recargchar[] = "option requires an argument -- %c";
|
||||
static const char recargstring[] = "option requires an argument -- %s";
|
||||
static const char ambig[] = "ambiguous option -- %.*s";
|
||||
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||
static const char illoptchar[] = "unknown option -- %c";
|
||||
static const char illoptstring[] = "unknown option -- %s";
|
||||
|
||||
static void
|
||||
_vwarnx(const char *fmt,va_list ap)
|
||||
{
|
||||
(void)fprintf(stderr,"%s: ",__progname);
|
||||
if (fmt != NULL)
|
||||
(void)vfprintf(stderr,fmt,ap);
|
||||
(void)fprintf(stderr,"\n");
|
||||
}
|
||||
|
||||
static void
|
||||
warnx(const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
_vwarnx(fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
((char **) nargv)[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
((char **)nargv)[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*
|
||||
* [eventually this will replace the BSD getopt]
|
||||
*/
|
||||
int
|
||||
getopt(int nargc, char * const *nargv, const char *options)
|
||||
{
|
||||
|
||||
/*
|
||||
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||
*
|
||||
* Furthermore, since many privileged programs call getopt()
|
||||
* before dropping privileges it makes sense to keep things
|
||||
* as simple (and bug-free) as possible.
|
||||
*/
|
||||
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
|
||||
}
|
||||
#endif /* REPLACE_GETOPT */
|
||||
|
||||
//extern int getopt(int nargc, char * const *nargv, const char *options);
|
||||
|
||||
#ifdef _BSD_SOURCE
|
||||
/*
|
||||
* BSD adds the non-standard `optreset' feature, for reinitialisation
|
||||
* of `getopt' parsing. We support this feature, for applications which
|
||||
* proclaim their BSD heritage, before including this header; however,
|
||||
* to maintain portability, developers are advised to avoid it.
|
||||
*/
|
||||
# define optreset __mingw_optreset
|
||||
extern int optreset;
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* POSIX requires the `getopt' API to be specified in `unistd.h';
|
||||
* thus, `unistd.h' includes this header. However, we do not want
|
||||
* to expose the `getopt_long' or `getopt_long_only' APIs, when
|
||||
* included in this manner. Thus, close the standard __GETOPT_H__
|
||||
* declarations block, and open an additional __GETOPT_LONG_H__
|
||||
* specific block, only when *not* __UNISTD_H_SOURCED__, in which
|
||||
* to declare the extended API.
|
||||
*/
|
||||
#endif /* !defined(__GETOPT_H__) */
|
||||
|
||||
#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
|
||||
#define __GETOPT_LONG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct option /* specification for a long form option... */
|
||||
{
|
||||
const char *name; /* option name, without leading hyphens */
|
||||
int has_arg; /* does it take an argument? */
|
||||
int *flag; /* where to save its status, or NULL */
|
||||
int val; /* its associated status value */
|
||||
};
|
||||
|
||||
enum /* permitted values for its `has_arg' field... */
|
||||
{
|
||||
no_argument = 0, /* option never takes an argument */
|
||||
required_argument, /* option always requires an argument */
|
||||
optional_argument /* option may take an argument */
|
||||
};
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int short_too)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
size_t current_argv_len;
|
||||
int i, ambiguous, match;
|
||||
|
||||
#define IDENTICAL_INTERPRETATION(_x, _y) \
|
||||
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
|
||||
long_options[(_x)].flag == long_options[(_y)].flag && \
|
||||
long_options[(_x)].val == long_options[(_y)].val)
|
||||
|
||||
current_argv = place;
|
||||
match = -1;
|
||||
ambiguous = 0;
|
||||
|
||||
optind++;
|
||||
|
||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
ambiguous = 0;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* partial match */
|
||||
match = i;
|
||||
else if (!IDENTICAL_INTERPRETATION(i, match))
|
||||
ambiguous = 1;
|
||||
}
|
||||
if (ambiguous) {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
warnx(ambig, (int)current_argv_len,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
warnx(noarg, (int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
return (BADARG);
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargstring,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
--optind;
|
||||
return (BADARG);
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--optind;
|
||||
return (-1);
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptstring, current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return (0);
|
||||
} else
|
||||
return (long_options[match].val);
|
||||
#undef IDENTICAL_INTERPRETATION
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
static int posixly_correct = -1;
|
||||
|
||||
if (options == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (optind == 0)
|
||||
optind = optreset = 1;
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||
* string begins with a '+'.
|
||||
*
|
||||
* CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
|
||||
* optreset != 0 for GNU compatibility.
|
||||
*/
|
||||
if (posixly_correct == -1 || optreset != 0)
|
||||
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
optarg = NULL;
|
||||
if (optreset)
|
||||
nonopt_start = nonopt_end = -1;
|
||||
start:
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc) { /* end of argument vector */
|
||||
place = EMSG;
|
||||
if (nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
else if (nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
optind = nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
if (*(place = nargv[optind]) != '-' ||
|
||||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||
place = EMSG; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
return (INORDER);
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
/* do permutation */
|
||||
if (nonopt_start == -1)
|
||||
nonopt_start = optind;
|
||||
else if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
nonopt_start = optind -
|
||||
(nonopt_end - nonopt_start);
|
||||
nonopt_end = -1;
|
||||
}
|
||||
optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (nonopt_start != -1 && nonopt_end == -1)
|
||||
nonopt_end = optind;
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||
optind++;
|
||||
place = EMSG;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && place != nargv[optind] &&
|
||||
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
if (*place == '-')
|
||||
place++; /* --foo long option */
|
||||
else if (*place != ':' && strchr(options, *place) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, short_too);
|
||||
if (optchar != -1) {
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optchar = (int)*place++) == (int)':' ||
|
||||
(optchar == (int)'-' && *place != '\0') ||
|
||||
(oli = (char*)strchr(options, optchar)) == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *place == '\0')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*place) /* no space */
|
||||
/* NOTHING */;
|
||||
else if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else /* white space */
|
||||
place = nargv[optind];
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, 0);
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* takes (optional) argument */
|
||||
optarg = NULL;
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else
|
||||
optarg = nargv[optind];
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return (optchar);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||
}
|
||||
|
||||
//extern int getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
// const struct option *long_options, int *idx);
|
||||
//extern int getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
// const struct option *long_options, int *idx);
|
||||
/*
|
||||
* Previous MinGW implementation had...
|
||||
*/
|
||||
#ifndef HAVE_DECL_GETOPT
|
||||
/*
|
||||
* ...for the long form API only; keep this for compatibility.
|
||||
*/
|
||||
# define HAVE_DECL_GETOPT 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_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_ */
|
||||
@@ -0,0 +1,809 @@
|
||||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_TREE_H_
|
||||
#define _SYS_TREE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../ext4_config.h"
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) do { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \
|
||||
RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \
|
||||
RB_PROTOTYPE_INSERT(name, type, attr); \
|
||||
RB_PROTOTYPE_REMOVE(name, type, attr); \
|
||||
RB_PROTOTYPE_FIND(name, type, attr); \
|
||||
RB_PROTOTYPE_NFIND(name, type, attr); \
|
||||
RB_PROTOTYPE_NEXT(name, type, attr); \
|
||||
RB_PROTOTYPE_PREV(name, type, attr); \
|
||||
RB_PROTOTYPE_MINMAX(name, type, attr);
|
||||
#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *)
|
||||
#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *)
|
||||
#define RB_PROTOTYPE_REMOVE(name, type, attr) \
|
||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *)
|
||||
#define RB_PROTOTYPE_INSERT(name, type, attr) \
|
||||
attr struct type *name##_RB_INSERT(struct name *, struct type *)
|
||||
#define RB_PROTOTYPE_FIND(name, type, attr) \
|
||||
attr struct type *name##_RB_FIND(struct name *, struct type *)
|
||||
#define RB_PROTOTYPE_NFIND(name, type, attr) \
|
||||
attr struct type *name##_RB_NFIND(struct name *, struct type *)
|
||||
#define RB_PROTOTYPE_NEXT(name, type, attr) \
|
||||
attr struct type *name##_RB_NEXT(struct type *)
|
||||
#define RB_PROTOTYPE_PREV(name, type, attr) \
|
||||
attr struct type *name##_RB_PREV(struct type *)
|
||||
#define RB_PROTOTYPE_MINMAX(name, type, attr) \
|
||||
attr struct type *name##_RB_MINMAX(struct name *, int)
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
|
||||
RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \
|
||||
RB_GENERATE_INSERT(name, type, field, cmp, attr) \
|
||||
RB_GENERATE_REMOVE(name, type, field, attr) \
|
||||
RB_GENERATE_FIND(name, type, field, cmp, attr) \
|
||||
RB_GENERATE_NFIND(name, type, field, cmp, attr) \
|
||||
RB_GENERATE_NEXT(name, type, field, attr) \
|
||||
RB_GENERATE_PREV(name, type, field, attr) \
|
||||
RB_GENERATE_MINMAX(name, type, field, attr)
|
||||
|
||||
#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) { \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} else { \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \
|
||||
attr void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) { \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oright, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_REMOVE(name, type, field, attr) \
|
||||
attr struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) { \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm;\
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) { \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
|
||||
#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \
|
||||
/* Inserts a node into the RB tree */ \
|
||||
attr struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) { \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_FIND(name, type, field, cmp, attr) \
|
||||
/* Finds the node with the same key as elm */ \
|
||||
attr struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \
|
||||
/* Finds the first node greater than or equal to the search key */ \
|
||||
attr struct type * \
|
||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *res = NULL; \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) { \
|
||||
res = tmp; \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
} \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (res); \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_NEXT(name, type, field, attr) \
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) { \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_PREV(name, type, field, attr) \
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_PREV(struct type *elm) \
|
||||
{ \
|
||||
if (RB_LEFT(elm, field)) { \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
while (RB_RIGHT(elm, field)) \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
}
|
||||
|
||||
#define RB_GENERATE_MINMAX(name, type, field, attr) \
|
||||
attr struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_TREE_H_ */
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef _UNISTD_H
|
||||
#define _UNISTD_H 1
|
||||
|
||||
/* This is intended as a drop-in replacement for unistd.h on Windows.
|
||||
* Please add functionality as neeeded.
|
||||
* https://stackoverflow.com/a/826027/1202830
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <io.h>
|
||||
#include <getopt.h> /* getopt at: https://gist.github.com/ashelly/7776712 */
|
||||
#include <process.h> /* for getpid() and the exec..() family */
|
||||
#include <direct.h> /* for _getcwd() and _chdir() */
|
||||
|
||||
#define srandom srand
|
||||
#define random rand
|
||||
|
||||
/* Values for the second argument to access.
|
||||
These may be OR'd together. */
|
||||
#define R_OK 4 /* Test for read permission. */
|
||||
#define W_OK 2 /* Test for write permission. */
|
||||
//#define X_OK 1 /* execute permission - unsupported in windows*/
|
||||
#define F_OK 0 /* Test for existence. */
|
||||
|
||||
#define access _access
|
||||
#define dup2 _dup2
|
||||
#define execve _execve
|
||||
#define ftruncate _chsize
|
||||
#define unlink _unlink
|
||||
#define fileno _fileno
|
||||
#define getcwd _getcwd
|
||||
#define chdir _chdir
|
||||
#define isatty _isatty
|
||||
#define lseek _lseek
|
||||
/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */
|
||||
|
||||
#ifdef _WIN64
|
||||
#define ssize_t __int64
|
||||
#else
|
||||
#define ssize_t long
|
||||
#endif
|
||||
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
/* should be in some equivalent to <sys/types.h> */
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#endif /* unistd.h */
|
||||
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{34d8a968-f941-43e7-aa4e-19c264b995b0}</ProjectGuid>
|
||||
<RootNamespace>SharpExt4</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies />
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies />
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies />
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies />
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="include\ext4.h" />
|
||||
<ClInclude Include="include\ext4_balloc.h" />
|
||||
<ClInclude Include="include\ext4_bcache.h" />
|
||||
<ClInclude Include="include\ext4_bitmap.h" />
|
||||
<ClInclude Include="include\ext4_blockdev.h" />
|
||||
<ClInclude Include="include\ext4_block_group.h" />
|
||||
<ClInclude Include="include\ext4_config.h" />
|
||||
<ClInclude Include="include\ext4_crc32.h" />
|
||||
<ClInclude Include="include\ext4_debug.h" />
|
||||
<ClInclude Include="include\ext4_dir.h" />
|
||||
<ClInclude Include="include\ext4_dir_idx.h" />
|
||||
<ClInclude Include="include\ext4_errno.h" />
|
||||
<ClInclude Include="include\ext4_extent.h" />
|
||||
<ClInclude Include="include\ext4_fs.h" />
|
||||
<ClInclude Include="include\ext4_hash.h" />
|
||||
<ClInclude Include="include\ext4_ialloc.h" />
|
||||
<ClInclude Include="include\ext4_inode.h" />
|
||||
<ClInclude Include="include\ext4_journal.h" />
|
||||
<ClInclude Include="include\ext4_mbr.h" />
|
||||
<ClInclude Include="include\ext4_misc.h" />
|
||||
<ClInclude Include="include\ext4_mkfs.h" />
|
||||
<ClInclude Include="include\ext4_oflags.h" />
|
||||
<ClInclude Include="include\ext4_super.h" />
|
||||
<ClInclude Include="include\ext4_trans.h" />
|
||||
<ClInclude Include="include\ext4_types.h" />
|
||||
<ClInclude Include="include\ext4_xattr.h" />
|
||||
<ClInclude Include="include\getopt.h" />
|
||||
<ClInclude Include="include\unistd.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\ext4.c" />
|
||||
<ClCompile Include="src\ext4_balloc.c" />
|
||||
<ClCompile Include="src\ext4_bcache.c" />
|
||||
<ClCompile Include="src\ext4_bitmap.c" />
|
||||
<ClCompile Include="src\ext4_blockdev.c" />
|
||||
<ClCompile Include="src\ext4_block_group.c" />
|
||||
<ClCompile Include="src\ext4_crc32.c" />
|
||||
<ClCompile Include="src\ext4_debug.c" />
|
||||
<ClCompile Include="src\ext4_dir.c" />
|
||||
<ClCompile Include="src\ext4_dir_idx.c" />
|
||||
<ClCompile Include="src\ext4_extent.c" />
|
||||
<ClCompile Include="src\ext4_fs.c" />
|
||||
<ClCompile Include="src\ext4_hash.c" />
|
||||
<ClCompile Include="src\ext4_ialloc.c" />
|
||||
<ClCompile Include="src\ext4_inode.c" />
|
||||
<ClCompile Include="src\ext4_journal.c" />
|
||||
<ClCompile Include="src\ext4_mbr.c" />
|
||||
<ClCompile Include="src\ext4_mkfs.c" />
|
||||
<ClCompile Include="src\ext4_super.c" />
|
||||
<ClCompile Include="src\ext4_trans.c" />
|
||||
<ClCompile Include="src\ext4_xattr.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_balloc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_bcache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_bitmap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_block_group.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_blockdev.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_crc32.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_debug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_dir.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_dir_idx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_errno.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_extent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_fs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_hash.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_ialloc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_inode.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_journal.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_mbr.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_misc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_mkfs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_oflags.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_super.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_trans.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\ext4_xattr.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\getopt.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\unistd.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\ext4.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_balloc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_bcache.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_bitmap.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_block_group.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_blockdev.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_crc32.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_debug.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_dir.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_dir_idx.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_extent.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_fs.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_hash.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_ialloc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_inode.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_journal.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_mbr.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_mkfs.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_super.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_trans.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ext4_xattr.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
+3240
File diff suppressed because it is too large.
Load diff
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_trans.h"
|
||||
#include "ext4_balloc.h"
|
||||
#include "ext4_super.h"
|
||||
#include "ext4_crc32.h"
|
||||
#include "ext4_block_group.h"
|
||||
#include "ext4_fs.h"
|
||||
#include "ext4_bitmap.h"
|
||||
#include "ext4_inode.h"
|
||||
|
||||
/**@brief Compute number of block group from block address.
|
||||
* @param sb superblock pointer.
|
||||
* @param baddr Absolute address of block.
|
||||
* @return Block group index
|
||||
*/
|
||||
uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s,
|
||||
uint64_t baddr)
|
||||
{
|
||||
if (ext4_get32(s, first_data_block) && baddr)
|
||||
baddr--;
|
||||
|
||||
return (uint32_t)(baddr / ext4_get32(s, blocks_per_group));
|
||||
}
|
||||
|
||||
/**@brief Compute the starting block address of a block group
|
||||
* @param sb superblock pointer.
|
||||
* @param bgid block group index
|
||||
* @return Block address
|
||||
*/
|
||||
uint64_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s,
|
||||
uint32_t bgid)
|
||||
{
|
||||
uint64_t baddr = 0;
|
||||
if (ext4_get32(s, first_data_block))
|
||||
baddr++;
|
||||
|
||||
baddr += bgid * ext4_get32(s, blocks_per_group);
|
||||
return baddr;
|
||||
}
|
||||
|
||||
#if CONFIG_META_CSUM_ENABLE
|
||||
static uint32_t ext4_balloc_bitmap_csum(struct ext4_sblock *sb,
|
||||
void *bitmap)
|
||||
{
|
||||
uint32_t checksum = 0;
|
||||
if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
|
||||
uint32_t blocks_per_group = ext4_get32(sb, blocks_per_group);
|
||||
|
||||
/* First calculate crc32 checksum against fs uuid */
|
||||
checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid,
|
||||
sizeof(sb->uuid));
|
||||
/* Then calculate crc32 checksum against block_group_desc */
|
||||
checksum = ext4_crc32c(checksum, bitmap, blocks_per_group / 8);
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
#else
|
||||
#define ext4_balloc_bitmap_csum(...) 0
|
||||
#endif
|
||||
|
||||
void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb,
|
||||
struct ext4_bgroup *bg,
|
||||
void *bitmap __unused)
|
||||
{
|
||||
int desc_size = ext4_sb_get_desc_size(sb);
|
||||
uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap);
|
||||
uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
|
||||
hi_checksum = to_le16(checksum >> 16);
|
||||
|
||||
if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
|
||||
return;
|
||||
|
||||
/* See if we need to assign a 32bit checksum */
|
||||
bg->block_bitmap_csum_lo = lo_checksum;
|
||||
if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->block_bitmap_csum_hi = hi_checksum;
|
||||
|
||||
}
|
||||
|
||||
#if CONFIG_META_CSUM_ENABLE
|
||||
static bool
|
||||
ext4_balloc_verify_bitmap_csum(struct ext4_sblock *sb,
|
||||
struct ext4_bgroup *bg,
|
||||
void *bitmap __unused)
|
||||
{
|
||||
int desc_size = ext4_sb_get_desc_size(sb);
|
||||
uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap);
|
||||
uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
|
||||
hi_checksum = to_le16(checksum >> 16);
|
||||
|
||||
if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
|
||||
return true;
|
||||
|
||||
if (bg->block_bitmap_csum_lo != lo_checksum)
|
||||
return false;
|
||||
|
||||
if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
if (bg->block_bitmap_csum_hi != hi_checksum)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
#define ext4_balloc_verify_bitmap_csum(...) true
|
||||
#endif
|
||||
|
||||
int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref, ext4_fsblk_t baddr)
|
||||
{
|
||||
struct ext4_fs *fs = inode_ref->fs;
|
||||
struct ext4_sblock *sb = &fs->sb;
|
||||
|
||||
uint32_t bg_id = ext4_balloc_get_bgid_of_block(sb, baddr);
|
||||
uint32_t index_in_group = ext4_fs_addr_to_idx_bg(sb, baddr);
|
||||
|
||||
/* Load block group reference */
|
||||
struct ext4_block_group_ref bg_ref;
|
||||
int rc = ext4_fs_get_block_group_ref(fs, bg_id, &bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
struct ext4_bgroup *bg = bg_ref.block_group;
|
||||
|
||||
/* Load block with bitmap */
|
||||
ext4_fsblk_t bitmap_block_addr =
|
||||
ext4_bg_get_block_bitmap(bg, sb);
|
||||
|
||||
struct ext4_block bitmap_block;
|
||||
|
||||
rc = ext4_trans_block_get(fs->bdev, &bitmap_block, bitmap_block_addr);
|
||||
if (rc != EOK) {
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap_block.data)) {
|
||||
ext4_dbg(DEBUG_BALLOC,
|
||||
DBG_WARN "Bitmap checksum failed."
|
||||
"Group: %" PRIu32"\n",
|
||||
bg_ref.index);
|
||||
}
|
||||
|
||||
/* Modify bitmap */
|
||||
ext4_bmap_bit_clr(bitmap_block.data, index_in_group);
|
||||
ext4_balloc_set_bitmap_csum(sb, bg, bitmap_block.data);
|
||||
ext4_trans_set_block_dirty(bitmap_block.buf);
|
||||
|
||||
/* Release block with bitmap */
|
||||
rc = ext4_block_set(fs->bdev, &bitmap_block);
|
||||
if (rc != EOK) {
|
||||
/* Error in saving bitmap */
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint32_t block_size = ext4_sb_get_block_size(sb);
|
||||
|
||||
/* Update superblock free blocks count */
|
||||
uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(sb);
|
||||
sb_free_blocks++;
|
||||
ext4_sb_set_free_blocks_cnt(sb, sb_free_blocks);
|
||||
|
||||
/* Update inode blocks count */
|
||||
uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode_ref->inode);
|
||||
ino_blocks -= block_size / EXT4_INODE_BLOCK_SIZE;
|
||||
ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks);
|
||||
inode_ref->dirty = true;
|
||||
|
||||
/* Update block group free blocks count */
|
||||
uint32_t free_blocks = ext4_bg_get_free_blocks_count(bg, sb);
|
||||
free_blocks++;
|
||||
ext4_bg_set_free_blocks_count(bg, sb, free_blocks);
|
||||
|
||||
bg_ref.dirty = true;
|
||||
|
||||
rc = ext4_trans_try_revoke_block(fs->bdev, baddr);
|
||||
if (rc != EOK) {
|
||||
bg_ref.dirty = false;
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
ext4_bcache_invalidate_lba(fs->bdev->bc, baddr, 1);
|
||||
/* Release block group reference */
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_bcache.h"
|
||||
#include "ext4_blockdev.h"
|
||||
#include "ext4_debug.h"
|
||||
#include "ext4_errno.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int ext4_bcache_lba_compare(struct ext4_buf *a, struct ext4_buf *b)
|
||||
{
|
||||
if (a->lba > b->lba)
|
||||
return 1;
|
||||
else if (a->lba < b->lba)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext4_bcache_lru_compare(struct ext4_buf *a, struct ext4_buf *b)
|
||||
{
|
||||
if (a->lru_id > b->lru_id)
|
||||
return 1;
|
||||
else if (a->lru_id < b->lru_id)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RB_GENERATE_INTERNAL(ext4_buf_lba, ext4_buf, lba_node,
|
||||
ext4_bcache_lba_compare, static inline)
|
||||
RB_GENERATE_INTERNAL(ext4_buf_lru, ext4_buf, lru_node,
|
||||
ext4_bcache_lru_compare, static inline)
|
||||
|
||||
int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
|
||||
uint32_t itemsize)
|
||||
{
|
||||
ext4_assert(bc && cnt && itemsize);
|
||||
|
||||
memset(bc, 0, sizeof(struct ext4_bcache));
|
||||
|
||||
bc->cnt = cnt;
|
||||
bc->itemsize = itemsize;
|
||||
bc->ref_blocks = 0;
|
||||
bc->max_ref_blocks = 0;
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
void ext4_bcache_cleanup(struct ext4_bcache *bc)
|
||||
{
|
||||
struct ext4_buf *buf, *tmp;
|
||||
RB_FOREACH_SAFE(buf, ext4_buf_lba, &bc->lba_root, tmp) {
|
||||
ext4_block_flush_buf(bc->bdev, buf);
|
||||
ext4_bcache_drop_buf(bc, buf);
|
||||
}
|
||||
}
|
||||
|
||||
int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
|
||||
{
|
||||
memset(bc, 0, sizeof(struct ext4_bcache));
|
||||
return EOK;
|
||||
}
|
||||
|
||||
/**@brief:
|
||||
*
|
||||
* This is ext4_bcache, the module handling basic buffer-cache stuff.
|
||||
*
|
||||
* Buffers in a bcache are sorted by their LBA and stored in a
|
||||
* RB-Tree(lba_root).
|
||||
*
|
||||
* Bcache also maintains another RB-Tree(lru_root) right now, where
|
||||
* buffers are sorted by their LRU id.
|
||||
*
|
||||
* A singly-linked list is used to track those dirty buffers which are
|
||||
* ready to be flushed. (Those buffers which are dirty but also referenced
|
||||
* are not considered ready to be flushed.)
|
||||
*
|
||||
* When a buffer is not referenced, it will be stored in both lba_root
|
||||
* and lru_root, while it will only be stored in lba_root when it is
|
||||
* referenced.
|
||||
*/
|
||||
|
||||
static struct ext4_buf *
|
||||
ext4_buf_alloc(struct ext4_bcache *bc, uint64_t lba)
|
||||
{
|
||||
void *data;
|
||||
struct ext4_buf *buf;
|
||||
data = 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_bitmap.h"
|
||||
|
||||
void ext4_bmap_bits_free(uint8_t *bmap, uint32_t sbit, uint32_t bcnt)
|
||||
{
|
||||
uint32_t i = sbit;
|
||||
|
||||
while (i & 7) {
|
||||
|
||||
if (!bcnt)
|
||||
return;
|
||||
|
||||
ext4_bmap_bit_clr(bmap, i);
|
||||
|
||||
bcnt--;
|
||||
i++;
|
||||
}
|
||||
sbit = i;
|
||||
bmap += (sbit >> 3);
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_block_group.h"
|
||||
|
||||
/**@brief CRC-16 look up table*/
|
||||
static uint16_t const crc16_tab[256] = {
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601,
|
||||
0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0,
|
||||
0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81,
|
||||
0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941,
|
||||
0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01,
|
||||
0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0,
|
||||
0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081,
|
||||
0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00,
|
||||
0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0,
|
||||
0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981,
|
||||
0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41,
|
||||
0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700,
|
||||
0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0,
|
||||
0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281,
|
||||
0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01,
|
||||
0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1,
|
||||
0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80,
|
||||
0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541,
|
||||
0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101,
|
||||
0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0,
|
||||
0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481,
|
||||
0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801,
|
||||
0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1,
|
||||
0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581,
|
||||
0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341,
|
||||
0x4100, 0x81C1, 0x8081, 0x4040};
|
||||
|
||||
uint16_t ext4_bg_crc16(uint16_t crc, const uint8_t *buffer, size_t len)
|
||||
{
|
||||
while (len--)
|
||||
|
||||
crc = (((crc >> 8) & 0xffU) ^
|
||||
crc16_tab[(crc ^ *buffer++) & 0xffU]) &
|
||||
0x0000ffffU;
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_blockdev.c
|
||||
* @brief Block device module.
|
||||
*/
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_blockdev.h"
|
||||
#include "ext4_fs.h"
|
||||
#include "ext4_journal.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void ext4_bdif_lock(struct ext4_blockdev *bdev)
|
||||
{
|
||||
if (!bdev->bdif->lock)
|
||||
return;
|
||||
|
||||
int r = bdev->bdif->lock(bdev);
|
||||
ext4_assert(r == EOK);
|
||||
}
|
||||
|
||||
static void ext4_bdif_unlock(struct ext4_blockdev *bdev)
|
||||
{
|
||||
if (!bdev->bdif->unlock)
|
||||
return;
|
||||
|
||||
int r = bdev->bdif->unlock(bdev);
|
||||
ext4_assert(r == EOK);
|
||||
}
|
||||
|
||||
static int ext4_bdif_bread(struct ext4_blockdev *bdev, void *buf,
|
||||
uint64_t blk_id, uint32_t blk_cnt)
|
||||
{
|
||||
ext4_bdif_lock(bdev);
|
||||
int r = bdev->bdif->bread(bdev, buf, blk_id, blk_cnt);
|
||||
bdev->bdif->bread_ctr++;
|
||||
ext4_bdif_unlock(bdev);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ext4_bdif_bwrite(struct ext4_blockdev *bdev, const void *buf,
|
||||
uint64_t blk_id, uint32_t blk_cnt)
|
||||
{
|
||||
ext4_bdif_lock(bdev);
|
||||
int r = bdev->bdif->bwrite(bdev, buf, blk_id, blk_cnt);
|
||||
bdev->bdif->bwrite_ctr++;
|
||||
ext4_bdif_unlock(bdev);
|
||||
return r;
|
||||
}
|
||||
|
||||
int ext4_block_init(struct ext4_blockdev *bdev)
|
||||
{
|
||||
int rc;
|
||||
ext4_assert(bdev);
|
||||
ext4_assert(bdev->bdif);
|
||||
ext4_assert(bdev->bdif->open &&
|
||||
bdev->bdif->close &&
|
||||
bdev->bdif->bread &&
|
||||
bdev->bdif->bwrite);
|
||||
|
||||
if (!bdev->bdif->ph_refctr)
|
||||
{
|
||||
/*Low level block init*/
|
||||
rc = bdev->bdif->open(bdev);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
bdev->bdif->ph_refctr = 1;
|
||||
}
|
||||
else if (bdev->bdif->ph_refctr) {
|
||||
bdev->bdif->ph_refctr++;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_crc32.h"
|
||||
|
||||
static const uint32_t crc32_tab[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
/* */
|
||||
/* CRC LOOKUP TABLE */
|
||||
/* ================ */
|
||||
/* The following CRC lookup table was generated automagically */
|
||||
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
||||
/* Program V1.0 using the following model parameters: */
|
||||
/* */
|
||||
/* Width : 4 bytes. */
|
||||
/* Poly : 0x1EDC6F41L */
|
||||
/* Reverse : TRUE. */
|
||||
/* */
|
||||
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
||||
/* see the document titled "A Painless Guide to CRC Error */
|
||||
/* Detection Algorithms" by Ross Williams */
|
||||
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
||||
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
||||
/* */
|
||||
static const uint32_t crc32c_tab[256] = {
|
||||
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL,
|
||||
0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL,
|
||||
0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L,
|
||||
0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
|
||||
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L,
|
||||
0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL,
|
||||
0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L,
|
||||
0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
|
||||
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL,
|
||||
0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L,
|
||||
0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L,
|
||||
0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
|
||||
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL,
|
||||
0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L,
|
||||
0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L,
|
||||
0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
|
||||
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL,
|
||||
0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL,
|
||||
0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L,
|
||||
0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
|
||||
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL,
|
||||
0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L,
|
||||
0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL,
|
||||
0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
|
||||
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL,
|
||||
0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL,
|
||||
0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L,
|
||||
0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
|
||||
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L,
|
||||
0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL,
|
||||
0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL,
|
||||
0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
|
||||
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L,
|
||||
0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL,
|
||||
0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L,
|
||||
0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
|
||||
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L,
|
||||
0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL,
|
||||
0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L,
|
||||
0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
|
||||
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L,
|
||||
0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L,
|
||||
0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L,
|
||||
0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
|
||||
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL,
|
||||
0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L,
|
||||
0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L,
|
||||
0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
|
||||
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL,
|
||||
0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L,
|
||||
0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L,
|
||||
0xAD7D5351L};
|
||||
|
||||
static inline uint32_t crc32(uint32_t crc, const void *buf, uint32_t size,
|
||||
const uint32_t *tab)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)buf;
|
||||
|
||||
while (size--)
|
||||
crc = tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
|
||||
|
||||
return (crc);
|
||||
}
|
||||
|
||||
uint32_t ext4_crc32(uint32_t crc, const void *buf, uint32_t size)
|
||||
{
|
||||
return crc32(crc, buf, size, crc32_tab);
|
||||
}
|
||||
|
||||
uint32_t ext4_crc32c(uint32_t crc, const void *buf, uint32_t size)
|
||||
{
|
||||
return crc32(crc, buf, size, crc32c_tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static uint32_t debug_mask;
|
||||
|
||||
void ext4_dmask_set(uint32_t m)
|
||||
{
|
||||
debug_mask |= m;
|
||||
}
|
||||
|
||||
void ext4_dmask_clr(uint32_t m)
|
||||
{
|
||||
debug_mask &= ~m;
|
||||
}
|
||||
|
||||
uint32_t ext4_dmask_get(void)
|
||||
{
|
||||
return debug_mask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_trans.h"
|
||||
#include "ext4_dir.h"
|
||||
#include "ext4_dir_idx.h"
|
||||
#include "ext4_crc32.h"
|
||||
#include "ext4_inode.h"
|
||||
#include "ext4_fs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Walk through a dirent block to find a checksum "dirent" at the tail */
|
||||
static struct ext4_dir_entry_tail *
|
||||
ext4_dir_get_tail(struct ext4_inode_ref *inode_ref,
|
||||
struct ext4_dir_en *de)
|
||||
{
|
||||
struct ext4_dir_entry_tail *t;
|
||||
struct ext4_sblock *sb = &inode_ref->fs->sb;
|
||||
|
||||
t = EXT4_DIRENT_TAIL(de, ext4_sb_get_block_size(sb));
|
||||
|
||||
if (t->reserved_zero1 || t->reserved_zero2)
|
||||
return NULL;
|
||||
if (to_le16(t->rec_len) != sizeof(struct ext4_dir_entry_tail))
|
||||
return NULL;
|
||||
if (t->reserved_ft != EXT4_DIRENTRY_DIR_CSUM)
|
||||
return NULL;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
#if CONFIG_META_CSUM_ENABLE
|
||||
static uint32_t ext4_dir_csum(struct ext4_inode_ref *inode_ref,
|
||||
struct ext4_dir_en *dirent, int size)
|
||||
{
|
||||
uint32_t csum;
|
||||
struct ext4_sblock *sb = &inode_ref->fs->sb;
|
||||
uint32_t ino_index = to_le32(inode_ref->index);
|
||||
uint32_t ino_gen = to_le32(ext4_inode_get_generation(inode_ref->inode));
|
||||
|
||||
/* First calculate crc32 checksum against fs uuid */
|
||||
csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
|
||||
/* Then calculate crc32 checksum against inode number
|
||||
* and inode generation */
|
||||
csum = ext4_crc32c(csum, &ino_index, sizeof(ino_index));
|
||||
csum = ext4_crc32c(csum, &ino_gen, sizeof(ino_gen));
|
||||
/* Finally calculate crc32 checksum against directory entries */
|
||||
csum = ext4_crc32c(csum, dirent, size);
|
||||
return csum;
|
||||
}
|
||||
#else
|
||||
#define ext4_dir_csum(...) 0
|
||||
#endif
|
||||
|
||||
bool ext4_dir_csum_verify(struct ext4_inode_ref *inode_ref,
|
||||
struct ext4_dir_en *dirent)
|
||||
{
|
||||
#ifdef CONFIG_META_CSUM_ENABLE
|
||||
struct ext4_dir_entry_tail *t;
|
||||
struct ext4_sblock *sb = &inode_ref->fs->sb;
|
||||
|
||||
/* Compute the checksum only if the filesystem supports it */
|
||||
if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
|
||||
t = ext4_dir_get_tail(inode_ref, dirent);
|
||||
if (!t) {
|
||||
/* There is no space to hold the checksum */
|
||||
return false;
|
||||
}
|
||||
|
||||
ptrdiff_t __unused diff = (char *)t - (char *)dirent;
|
||||
uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff);
|
||||
if (t->checksum != to_le32(csum))
|
||||
return false;
|
||||
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void ext4_dir_init_entry_tail(struct ext4_dir_entry_tail *t)
|
||||
{
|
||||
memset(t, 0, sizeof(struct ext4_dir_entry_tail));
|
||||
t->rec_len = to_le16(sizeof(struct ext4_dir_entry_tail));
|
||||
t->reserved_ft = EXT4_DIRENTRY_DIR_CSUM;
|
||||
}
|
||||
|
||||
void ext4_dir_set_csum(struct ext4_inode_ref *inode_ref,
|
||||
struct ext4_dir_en *dirent)
|
||||
{
|
||||
struct ext4_dir_entry_tail *t;
|
||||
struct ext4_sblock *sb = &inode_ref->fs->sb;
|
||||
|
||||
/* Compute the checksum only if the filesystem supports it */
|
||||
if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
|
||||
t = ext4_dir_get_tail(inode_ref, dirent);
|
||||
if (!t) {
|
||||
/* There is no space to hold the checksum */
|
||||
return;
|
||||
}
|
||||
|
||||
ptrdiff_t __unused diff = (char *)t - (char *)dirent;
|
||||
uint32_t csum = ext4_dir_csum(inode_ref, dirent, diff);
|
||||
t->checksum = to_le32(csum);
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Do some checks before returning iterator.
|
||||
* @param it Iterator to be checked
|
||||
* @param block_size Size of data block
|
||||
* @return Error code
|
||||
*/
|
||||
static int ext4_dir_iterator_set(struct ext4_dir_iter *it,
|
||||
uint32_t block_size)
|
||||
{
|
||||
uint32_t off_in_block = it->curr_off % block_size;
|
||||
struct ext4_sblock *sb = &it->inode_ref->fs->sb;
|
||||
|
||||
it->curr = NULL;
|
||||
|
||||
/* Ensure proper alignment */
|
||||
if ((off_in_block % 4) != 0)
|
||||
return EIO;
|
||||
|
||||
/* Ensure that the core of the entry does not overflow the block */
|
||||
if (off_in_block > block_size - 8)
|
||||
return EIO;
|
||||
|
||||
struct ext4_dir_en *en;
|
||||
en = (void *)(it->curr_blk.data + off_in_block);
|
||||
|
||||
/* Ensure that the whole entry does not overflow the block */
|
||||
uint16_t length = ext4_dir_en_get_entry_len(en);
|
||||
if (off_in_block + length > block_size)
|
||||
return EIO;
|
||||
|
||||
/* Ensure the name length is not too large */
|
||||
if (ext4_dir_en_get_name_len(sb, en) > length - 8)
|
||||
return EIO;
|
||||
|
||||
/* Everything OK - "publish" the entry */
|
||||
it->curr = en;
|
||||
return EOK;
|
||||
}
|
||||
|
||||
/**@brief Seek to next valid directory entry.
|
||||
* Here can be jumped to the next data block.
|
||||
* @param it Initialized iterator
|
||||
* @param pos Position of the next entry
|
||||
* @return Error code
|
||||
*/
|
||||
static int ext4_dir_iterator_seek(struct ext4_dir_iter *it, uint64_t pos)
|
||||
{
|
||||
struct ext4_sblock *sb = &it->inode_ref->fs->sb;
|
||||
struct ext4_inode *inode = it->inode_ref->inode;
|
||||
struct ext4_blockdev *bdev = it->inode_ref->fs->bdev;
|
||||
uint64_t size = ext4_inode_get_size(sb, inode);
|
||||
int r;
|
||||
|
||||
/* The iterator is not valid until we seek to the desired position */
|
||||
it->curr = NULL;
|
||||
|
||||
/* Are we at the end? */
|
||||
if (pos >= size) {
|
||||
if (it->curr_blk.lb_id) {
|
||||
|
||||
r = ext4_block_set(bdev, &it->curr_blk);
|
||||
it->curr_blk.lb_id = 0;
|
||||
if (r != EOK)
|
||||
return r;
|
||||
}
|
||||
|
||||
it->curr_off = pos;
|
||||
return EOK;
|
||||
}
|
||||
|
||||
/* Compute next block address */
|
||||
uint32_t block_size = ext4_sb_get_block_size(sb);
|
||||
uint64_t current_blk_idx = it->curr_off / block_size;
|
||||
uint32_t next_blk_idx = (uint32_t)(pos / block_size);
|
||||
|
||||
/*
|
||||
* If we don't have a block or are moving across block boundary,
|
||||
* we need to get another block
|
||||
*/
|
||||
if ((it->curr_blk.lb_id == 0) ||
|
||||
(current_blk_idx != next_blk_idx)) {
|
||||
if (it->curr_blk.lb_id) {
|
||||
r = ext4_block_set(bdev, &it->curr_blk);
|
||||
it->curr_blk.lb_id = 0;
|
||||
|
||||
if (r != EOK)
|
||||
return r;
|
||||
}
|
||||
|
||||
ext4_fsblk_t next_blk;
|
||||
r = ext4_fs_get_inode_dblk_idx(it->inode_ref, next_blk_idx,
|
||||
&next_blk, false);
|
||||
if (r != EOK)
|
||||
return r;
|
||||
|
||||
r = ext4_trans_block_get(bdev, &it->curr_blk, next_blk);
|
||||
if (r != EOK) {
|
||||
it->curr_blk.lb_id = 0;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
it->curr_off = pos;
|
||||
return ext4_dir_iterator_set(it, block_size);
|
||||
}
|
||||
|
||||
int ext4_dir_iterator_init(struct ext4_dir_iter *it,
|
||||
struct ext4_inode_ref *inode_ref, uint64_t pos)
|
||||
{
|
||||
it->inode_ref = inode_ref;
|
||||
it->curr = 0;
|
||||
it->curr_off = 0;
|
||||
it->curr_blk.lb_id = 0;
|
||||
|
||||
return ext4_dir_iterator_seek(it, pos);
|
||||
}
|
||||
|
||||
int ext4_dir_iterator_next(struct ext4_dir_iter *it)
|
||||
{
|
||||
int r = EOK;
|
||||
uint16_t skip;
|
||||
|
||||
while (r == EOK) {
|
||||
skip = ext4_dir_en_get_entry_len(it->curr);
|
||||
r = ext4_dir_iterator_seek(it, it->curr_off + skip);
|
||||
|
||||
if (!it->curr)
|
||||
break;
|
||||
/*Skip NULL referenced entry*/
|
||||
if (ext4_dir_en_get_inode(it->curr) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int ext4_dir_iterator_fini(struct ext4_dir_iter *it)
|
||||
{
|
||||
it->curr = 0;
|
||||
|
||||
if (it->curr_blk.lb_id)
|
||||
return ext4_block_set(it->inode_ref->fs->bdev, &it->curr_blk);
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
void ext4_dir_write_entry(struct ext4_sblock *sb, struct ext4_dir_en *en,
|
||||
uint16_t entry_len, struct ext4_inode_ref *child,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
/* Check maximum entry length */
|
||||
ext4_assert(entry_len <= ext4_sb_get_block_size(sb));
|
||||
|
||||
/* Set type of entry */
|
||||
switch (ext4_inode_type(sb, child->inode)) {
|
||||
case EXT4_INODE_MODE_DIRECTORY:
|
||||
ext4_dir_en_set_inode_type(sb, en, EXT4_DE_DIR);
|
||||
break;
|
||||
case EXT4_INODE_MODE_FILE:
|
||||
ext4_dir_en_set_inode_type(sb, en, EXT4_DE_REG_FILE);
|
||||
break;
|
||||
case EXT4_INODE_MODE_SOFTLINK:
|
||||
ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SYMLINK);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1751
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
*
|
||||
* FreeBSD:
|
||||
* Copyright (c) 2010, 2013 Zheng Liu <lz@freebsd.org>
|
||||
* Copyright (c) 2012, Vyacheslav Matyushin
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following notice applies to the code in ext2_half_md4():
|
||||
*
|
||||
* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
|
||||
*
|
||||
* License to copy and use this software is granted provided that it
|
||||
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
|
||||
* Algorithm" in all material mentioning or referencing this software
|
||||
* or this function.
|
||||
*
|
||||
* License is also granted to make and use derivative works provided
|
||||
* that such works are identified as "derived from the RSA Data
|
||||
* Security, Inc. MD4 Message-Digest Algorithm" in all material
|
||||
* mentioning or referencing the derived work.
|
||||
*
|
||||
* RSA Data Security, Inc. makes no representations concerning either
|
||||
* the merchantability of this software or the suitability of this
|
||||
* software for any particular purpose. It is provided "as is"
|
||||
* without express or implied warranty of any kind.
|
||||
*
|
||||
* These notices must be retained in any copies of any part of this
|
||||
* documentation and/or software.
|
||||
*/
|
||||
|
||||
/** @addtogroup lwext4
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @file ext4_hash.c
|
||||
* @brief Directory indexing hash functions.
|
||||
*/
|
||||
|
||||
#include "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* F, G, and H are MD4 functions */
|
||||
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||
#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
|
||||
/* ROTATE_LEFT rotates x left n bits */
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/*
|
||||
* FF, GG, and HH are transformations for rounds 1, 2, and 3.
|
||||
* Rotation is separated from addition to prevent recomputation.
|
||||
*/
|
||||
#define FF(a, b, c, d, x, s) \
|
||||
{ \
|
||||
(a) += F((b), (c), (d)) + (x); \
|
||||
(a) = ROTATE_LEFT((a), (s)); \
|
||||
\
|
||||
}
|
||||
|
||||
#define GG(a, b, c, d, x, s) \
|
||||
{ \
|
||||
(a) += G((b), (c), (d)) + (x) + (uint32_t)0x5A827999; \
|
||||
(a) = ROTATE_LEFT((a), (s)); \
|
||||
\
|
||||
}
|
||||
|
||||
#define HH(a, b, c, d, x, s) \
|
||||
{ \
|
||||
(a) += H((b), (c), (d)) + (x) + (uint32_t)0x6ED9EBA1; \
|
||||
(a) = ROTATE_LEFT((a), (s)); \
|
||||
\
|
||||
}
|
||||
|
||||
/*
|
||||
* MD4 basic transformation. It transforms state based on block.
|
||||
*
|
||||
* This is a half md4 algorithm since Linux uses this algorithm for dir
|
||||
* index. This function is derived from the RSA Data Security, Inc. MD4
|
||||
* Message-Digest Algorithm and was modified as necessary.
|
||||
*
|
||||
* The return value of this function is uint32_t in Linux, but actually we don't
|
||||
* need to check this value, so in our version this function doesn't return any
|
||||
* value.
|
||||
*/
|
||||
static void ext2_half_md4(uint32_t hash[4], uint32_t data[8])
|
||||
{
|
||||
uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3];
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, data[0], 3);
|
||||
FF(d, a, b, c, data[1], 7);
|
||||
FF(c, d, a, b, data[2], 11);
|
||||
FF(b, c, d, a, data[3], 19);
|
||||
FF(a, b, c, d, data[4], 3);
|
||||
FF(d, a, b, c, data[5], 7);
|
||||
FF(c, d, a, b, data[6], 11);
|
||||
FF(b, c, d, a, data[7], 19);
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, data[1], 3);
|
||||
GG(d, a, b, c, data[3], 5);
|
||||
GG(c, d, a, b, data[5], 9);
|
||||
GG(b, c, d, a, data[7], 13);
|
||||
GG(a, b, c, d, data[0], 3);
|
||||
GG(d, a, b, c, data[2], 5);
|
||||
GG(c, d, a, b, data[4], 9);
|
||||
GG(b, c, d, a, data[6], 13);
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, data[3], 3);
|
||||
HH(d, a, b, c, data[7], 9);
|
||||
HH(c, d, a, b, data[2], 11);
|
||||
HH(b, c, d, a, data[6], 15);
|
||||
HH(a, b, c, d, data[1], 3);
|
||||
HH(d, a, b, c, data[5], 9);
|
||||
HH(c, d, a, b, data[0], 11);
|
||||
HH(b, c, d, a, data[4], 15);
|
||||
|
||||
hash[0] += a;
|
||||
hash[1] += b;
|
||||
hash[2] += c;
|
||||
hash[3] += d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tiny Encryption Algorithm.
|
||||
*/
|
||||
static void ext2_tea(uint32_t hash[4], uint32_t data[8])
|
||||
{
|
||||
uint32_t tea_delta = 0x9E3779B9;
|
||||
uint32_t sum;
|
||||
uint32_t x = hash[0], y = hash[1];
|
||||
int n = 16;
|
||||
int i = 1;
|
||||
|
||||
while (n-- > 0) {
|
||||
sum = i * tea_delta;
|
||||
x += ((y << 4) + data[0]) ^ (y + sum) ^ ((y >> 5) + data[1]);
|
||||
y += ((x << 4) + data[2]) ^ (x + sum) ^ ((x >> 5) + data[3]);
|
||||
i++;
|
||||
}
|
||||
|
||||
hash[0] += x;
|
||||
hash[1] += y;
|
||||
}
|
||||
|
||||
static uint32_t ext2_legacy_hash(const char *name, int len, int unsigned_char)
|
||||
{
|
||||
uint32_t h0, h1 = 0x12A3FE2D, h2 = 0x37ABE8F9;
|
||||
uint32_t multi = 0x6D22F5;
|
||||
const unsigned char *uname = (const unsigned char *)name;
|
||||
const signed char *sname = (const signed char *)name;
|
||||
int val, i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (unsigned_char)
|
||||
val = (unsigned int)*uname++;
|
||||
else
|
||||
val = (int)*sname++;
|
||||
|
||||
h0 = h2 + (h1 ^ (val * multi));
|
||||
if (h0 & 0x80000000)
|
||||
h0 -= 0x7FFFFFFF;
|
||||
h2 = h1;
|
||||
h1 = h0;
|
||||
}
|
||||
|
||||
return (h1 << 1);
|
||||
}
|
||||
|
||||
static void ext2_prep_hashbuf(const char *src, uint32_t slen, uint32_t *dst,
|
||||
int dlen, int unsigned_char)
|
||||
{
|
||||
uint32_t padding = slen | (slen << 8) | (slen << 16) | (slen << 24);
|
||||
uint32_t buf_val;
|
||||
int len, i;
|
||||
int buf_byte;
|
||||
const unsigned char *ubuf = (const unsigned char *)src;
|
||||
const signed char *sbuf = (const signed char *)src;
|
||||
|
||||
if (slen > (uint32_t)dlen)
|
||||
len = dlen;
|
||||
else
|
||||
len = slen;
|
||||
|
||||
buf_val = padding;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (unsigned_char)
|
||||
buf_byte = (unsigned int)ubuf[i];
|
||||
else
|
||||
buf_byte = (int)sbuf[i];
|
||||
|
||||
if ((i % 4) == 0)
|
||||
buf_val = padding;
|
||||
|
||||
buf_val <<= 8;
|
||||
buf_val += buf_byte;
|
||||
|
||||
if ((i % 4) == 3) {
|
||||
*dst++ = buf_val;
|
||||
dlen -= sizeof(uint32_t);
|
||||
buf_val = padding;
|
||||
}
|
||||
}
|
||||
|
||||
dlen -= sizeof(uint32_t);
|
||||
if (dlen >= 0)
|
||||
*dst++ = buf_val;
|
||||
|
||||
dlen -= sizeof(uint32_t);
|
||||
while (dlen >= 0) {
|
||||
*dst++ = padding;
|
||||
dlen -= sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
|
||||
int ext2_htree_hash(const char *name, int len, const uint32_t *hash_seed,
|
||||
int hash_version, uint32_t *hash_major,
|
||||
uint32_t *hash_minor)
|
||||
{
|
||||
uint32_t hash[4];
|
||||
uint32_t data[8];
|
||||
uint32_t major = 0, minor = 0;
|
||||
int unsigned_char = 0;
|
||||
|
||||
if (!name || !hash_major)
|
||||
return (-1);
|
||||
|
||||
if (len < 1 || len > 255)
|
||||
goto error;
|
||||
|
||||
hash[0] = 0x67452301;
|
||||
hash[1] = 0xEFCDAB89;
|
||||
hash[2] = 0x98BADCFE;
|
||||
hash[3] = 0x10325476;
|
||||
|
||||
if (hash_seed)
|
||||
memcpy(hash, hash_seed, sizeof(hash));
|
||||
|
||||
switch (hash_version) {
|
||||
case EXT2_HTREE_TEA_UNSIGNED:
|
||||
unsigned_char = 1;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_trans.h"
|
||||
#include "ext4_ialloc.h"
|
||||
#include "ext4_super.h"
|
||||
#include "ext4_crc32.h"
|
||||
#include "ext4_fs.h"
|
||||
#include "ext4_blockdev.h"
|
||||
#include "ext4_block_group.h"
|
||||
#include "ext4_bitmap.h"
|
||||
|
||||
/**@brief Convert i-node number to relative index in block group.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node number to be converted
|
||||
* @return Index of the i-node in the block group
|
||||
*/
|
||||
static uint32_t ext4_ialloc_inode_to_bgidx(struct ext4_sblock *sb,
|
||||
uint32_t inode)
|
||||
{
|
||||
uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
|
||||
return (inode - 1) % inodes_per_group;
|
||||
}
|
||||
|
||||
/**@brief Convert relative index of i-node to absolute i-node number.
|
||||
* @param sb Superblock
|
||||
* @param index Index to be converted
|
||||
* @return Absolute number of the i-node
|
||||
*
|
||||
*/
|
||||
static uint32_t ext4_ialloc_bgidx_to_inode(struct ext4_sblock *sb,
|
||||
uint32_t index, uint32_t bgid)
|
||||
{
|
||||
uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
|
||||
return bgid * inodes_per_group + (index + 1);
|
||||
}
|
||||
|
||||
/**@brief Compute block group number from the i-node number.
|
||||
* @param sb Superblock
|
||||
* @param inode I-node number to be found the block group for
|
||||
* @return Block group number computed from i-node number
|
||||
*/
|
||||
static uint32_t ext4_ialloc_get_bgid_of_inode(struct ext4_sblock *sb,
|
||||
uint32_t inode)
|
||||
{
|
||||
uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
|
||||
return (inode - 1) / inodes_per_group;
|
||||
}
|
||||
|
||||
#if CONFIG_META_CSUM_ENABLE
|
||||
static uint32_t ext4_ialloc_bitmap_csum(struct ext4_sblock *sb, void *bitmap)
|
||||
{
|
||||
uint32_t csum = 0;
|
||||
if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
|
||||
uint32_t inodes_per_group =
|
||||
ext4_get32(sb, inodes_per_group);
|
||||
|
||||
/* First calculate crc32 checksum against fs uuid */
|
||||
csum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
|
||||
/* Then calculate crc32 checksum against inode bitmap */
|
||||
csum = ext4_crc32c(csum, bitmap, (inodes_per_group + 7) / 8);
|
||||
}
|
||||
return csum;
|
||||
}
|
||||
#else
|
||||
#define ext4_ialloc_bitmap_csum(...) 0
|
||||
#endif
|
||||
|
||||
void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
|
||||
void *bitmap __unused)
|
||||
{
|
||||
int desc_size = ext4_sb_get_desc_size(sb);
|
||||
uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap);
|
||||
uint16_t lo_csum = to_le16(csum & 0xFFFF),
|
||||
hi_csum = to_le16(csum >> 16);
|
||||
|
||||
if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
|
||||
return;
|
||||
|
||||
/* See if we need to assign a 32bit checksum */
|
||||
bg->inode_bitmap_csum_lo = lo_csum;
|
||||
if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
bg->inode_bitmap_csum_hi = hi_csum;
|
||||
|
||||
}
|
||||
|
||||
#if CONFIG_META_CSUM_ENABLE
|
||||
static bool
|
||||
ext4_ialloc_verify_bitmap_csum(struct ext4_sblock *sb, struct ext4_bgroup *bg,
|
||||
void *bitmap __unused)
|
||||
{
|
||||
|
||||
int desc_size = ext4_sb_get_desc_size(sb);
|
||||
uint32_t csum = ext4_ialloc_bitmap_csum(sb, bitmap);
|
||||
uint16_t lo_csum = to_le16(csum & 0xFFFF),
|
||||
hi_csum = to_le16(csum >> 16);
|
||||
|
||||
if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
|
||||
return true;
|
||||
|
||||
if (bg->inode_bitmap_csum_lo != lo_csum)
|
||||
return false;
|
||||
|
||||
if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
if (bg->inode_bitmap_csum_hi != hi_csum)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
#define ext4_ialloc_verify_bitmap_csum(...) true
|
||||
#endif
|
||||
|
||||
int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir)
|
||||
{
|
||||
struct ext4_sblock *sb = &fs->sb;
|
||||
|
||||
/* Compute index of block group and load it */
|
||||
uint32_t block_group = ext4_ialloc_get_bgid_of_inode(sb, index);
|
||||
|
||||
struct ext4_block_group_ref bg_ref;
|
||||
int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
struct ext4_bgroup *bg = bg_ref.block_group;
|
||||
|
||||
/* Load i-node bitmap */
|
||||
ext4_fsblk_t bitmap_block_addr =
|
||||
ext4_bg_get_inode_bitmap(bg, sb);
|
||||
|
||||
struct ext4_block b;
|
||||
rc = ext4_trans_block_get(fs->bdev, &b, bitmap_block_addr);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) {
|
||||
ext4_dbg(DEBUG_IALLOC,
|
||||
DBG_WARN "Bitmap checksum failed."
|
||||
"Group: %" PRIu32"\n",
|
||||
bg_ref.index);
|
||||
}
|
||||
|
||||
/* Free i-node in the bitmap */
|
||||
uint32_t index_in_group = ext4_ialloc_inode_to_bgidx(sb, index);
|
||||
ext4_bmap_bit_clr(b.data, index_in_group);
|
||||
ext4_ialloc_set_bitmap_csum(sb, bg, b.data);
|
||||
ext4_trans_set_block_dirty(b.buf);
|
||||
|
||||
/* Put back the block with bitmap */
|
||||
rc = ext4_block_set(fs->bdev, &b);
|
||||
if (rc != EOK) {
|
||||
/* Error in saving bitmap */
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* If released i-node is a directory, decrement used directories count
|
||||
*/
|
||||
if (is_dir) {
|
||||
uint32_t bg_used_dirs = ext4_bg_get_used_dirs_count(bg, sb);
|
||||
bg_used_dirs--;
|
||||
ext4_bg_set_used_dirs_count(bg, sb, bg_used_dirs);
|
||||
}
|
||||
|
||||
/* Update block group free inodes count */
|
||||
uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
|
||||
free_inodes++;
|
||||
ext4_bg_set_free_inodes_count(bg, sb, free_inodes);
|
||||
|
||||
bg_ref.dirty = true;
|
||||
|
||||
/* Put back the modified block group */
|
||||
rc = ext4_fs_put_block_group_ref(&bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
/* Update superblock free inodes count */
|
||||
ext4_set32(sb, free_inodes_count,
|
||||
ext4_get32(sb, free_inodes_count) + 1);
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *idx, bool is_dir)
|
||||
{
|
||||
struct ext4_sblock *sb = &fs->sb;
|
||||
|
||||
uint32_t bgid = fs->last_inode_bg_id;
|
||||
uint32_t bg_count = ext4_block_group_cnt(sb);
|
||||
uint32_t sb_free_inodes = ext4_get32(sb, free_inodes_count);
|
||||
bool rewind = false;
|
||||
|
||||
/* Try to find free i-node in all block groups */
|
||||
while (bgid <= bg_count) {
|
||||
|
||||
if (bgid == bg_count) {
|
||||
if (rewind)
|
||||
break;
|
||||
bg_count = fs->last_inode_bg_id;
|
||||
bgid = 0;
|
||||
rewind = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Load block group to check */
|
||||
struct ext4_block_group_ref bg_ref;
|
||||
int rc = ext4_fs_get_block_group_ref(fs, bgid, &bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
struct ext4_bgroup *bg = bg_ref.block_group;
|
||||
|
||||
/* Read necessary values for algorithm */
|
||||
uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
|
||||
uint32_t used_dirs = ext4_bg_get_used_dirs_count(bg, sb);
|
||||
|
||||
/* Check if this block group is good candidate for allocation */
|
||||
if (free_inodes > 0) {
|
||||
/* Load block with bitmap */
|
||||
ext4_fsblk_t bmp_blk_add = ext4_bg_get_inode_bitmap(bg, sb);
|
||||
|
||||
struct ext4_block b;
|
||||
rc = ext4_trans_block_get(fs->bdev, &b, bmp_blk_add);
|
||||
if (rc != EOK) {
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!ext4_ialloc_verify_bitmap_csum(sb, bg, b.data)) {
|
||||
ext4_dbg(DEBUG_IALLOC,
|
||||
DBG_WARN "Bitmap checksum failed."
|
||||
"Group: %" PRIu32"\n",
|
||||
bg_ref.index);
|
||||
}
|
||||
|
||||
/* Try to allocate i-node in the bitmap */
|
||||
uint32_t inodes_in_bg;
|
||||
uint32_t idx_in_bg;
|
||||
|
||||
inodes_in_bg = ext4_inodes_in_group_cnt(sb, bgid);
|
||||
rc = ext4_bmap_bit_find_clr(b.data, 0, inodes_in_bg,
|
||||
&idx_in_bg);
|
||||
/* Block group has not any free i-node */
|
||||
if (rc == ENOSPC) {
|
||||
rc = ext4_block_set(fs->bdev, &b);
|
||||
if (rc != EOK) {
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ext4_fs_put_block_group_ref(&bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ext4_bmap_bit_set(b.data, idx_in_bg);
|
||||
|
||||
/* Free i-node found, save the bitmap */
|
||||
ext4_ialloc_set_bitmap_csum(sb,bg,
|
||||
b.data);
|
||||
ext4_trans_set_block_dirty(b.buf);
|
||||
|
||||
ext4_block_set(fs->bdev, &b);
|
||||
if (rc != EOK) {
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Modify filesystem counters */
|
||||
free_inodes--;
|
||||
ext4_bg_set_free_inodes_count(bg, sb, free_inodes);
|
||||
|
||||
/* Increment used directories counter */
|
||||
if (is_dir) {
|
||||
used_dirs++;
|
||||
ext4_bg_set_used_dirs_count(bg, sb, used_dirs);
|
||||
}
|
||||
|
||||
/* Decrease unused inodes count */
|
||||
uint32_t unused =
|
||||
ext4_bg_get_itable_unused(bg, sb);
|
||||
|
||||
uint32_t free = inodes_in_bg - unused;
|
||||
|
||||
if (idx_in_bg >= free) {
|
||||
unused = inodes_in_bg - (idx_in_bg + 1);
|
||||
ext4_bg_set_itable_unused(bg, sb, unused);
|
||||
}
|
||||
|
||||
/* Save modified block group */
|
||||
bg_ref.dirty = true;
|
||||
|
||||
rc = ext4_fs_put_block_group_ref(&bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
/* Update superblock */
|
||||
sb_free_inodes--;
|
||||
ext4_set32(sb, free_inodes_count, sb_free_inodes);
|
||||
|
||||
/* Compute the absolute i-nodex number */
|
||||
*idx = ext4_ialloc_bgidx_to_inode(sb, idx_in_bg, bgid);
|
||||
|
||||
fs->last_inode_bg_id = bgid;
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
/* Block group not modified, put it and jump to the next block
|
||||
* group */
|
||||
ext4_fs_put_block_group_ref(&bg_ref);
|
||||
if (rc != EOK)
|
||||
return rc;
|
||||
|
||||
++bgid;
|
||||
}
|
||||
|
||||
return ENOSPC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_inode.h"
|
||||
#include "ext4_super.h"
|
||||
|
||||
/**@brief Compute number of bits for block count.
|
||||
* @param block_size Filesystem block_size
|
||||
* @return Number of bits
|
||||
*/
|
||||
static uint32_t ext4_inode_block_bits_count(uint32_t block_size)
|
||||
{
|
||||
uint32_t bits = 8;
|
||||
uint32_t size = block_size;
|
||||
|
||||
do {
|
||||
bits++;
|
||||
size = size >> 1;
|
||||
} while (size > 256);
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_mode(struct ext4_sblock *sb, struct ext4_inode *inode)
|
||||
{
|
||||
uint32_t v = to_le16(inode->mode);
|
||||
|
||||
if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD) {
|
||||
v |= ((uint32_t)to_le16(inode->osd2.hurd2.mode_high)) << 16;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void ext4_inode_set_mode(struct ext4_sblock *sb, struct ext4_inode *inode,
|
||||
uint32_t mode)
|
||||
{
|
||||
inode->mode = to_le16((mode << 16) >> 16);
|
||||
|
||||
if (ext4_get32(sb, creator_os) == EXT4_SUPERBLOCK_OS_HURD)
|
||||
inode->osd2.hurd2.mode_high = to_le16(mode >> 16);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_uid(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->uid);
|
||||
}
|
||||
|
||||
void ext4_inode_set_uid(struct ext4_inode *inode, uint32_t uid)
|
||||
{
|
||||
inode->uid = to_le32(uid);
|
||||
}
|
||||
|
||||
uint64_t ext4_inode_get_size(struct ext4_sblock *sb, struct ext4_inode *inode)
|
||||
{
|
||||
uint64_t v = to_le32(inode->size_lo);
|
||||
|
||||
if ((ext4_get32(sb, rev_level) > 0) &&
|
||||
(ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)))
|
||||
v |= ((uint64_t)to_le32(inode->size_hi)) << 32;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void ext4_inode_set_size(struct ext4_inode *inode, uint64_t size)
|
||||
{
|
||||
inode->size_lo = to_le32((size << 32) >> 32);
|
||||
inode->size_hi = to_le32(size >> 32);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_csum(struct ext4_sblock *sb, struct ext4_inode *inode)
|
||||
{
|
||||
uint16_t inode_size = ext4_get16(sb, inode_size);
|
||||
uint32_t v = to_le16(inode->osd2.linux2.checksum_lo);
|
||||
|
||||
if (inode_size > EXT4_GOOD_OLD_INODE_SIZE)
|
||||
v |= ((uint32_t)to_le16(inode->checksum_hi)) << 16;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void ext4_inode_set_csum(struct ext4_sblock *sb, struct ext4_inode *inode,
|
||||
uint32_t checksum)
|
||||
{
|
||||
uint16_t inode_size = ext4_get16(sb, inode_size);
|
||||
inode->osd2.linux2.checksum_lo =
|
||||
to_le16((checksum << 16) >> 16);
|
||||
|
||||
if (inode_size > EXT4_GOOD_OLD_INODE_SIZE)
|
||||
inode->checksum_hi = to_le16(checksum >> 16);
|
||||
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_access_time(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->access_time);
|
||||
}
|
||||
void ext4_inode_set_access_time(struct ext4_inode *inode, uint32_t time)
|
||||
{
|
||||
inode->access_time = to_le32(time);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_change_inode_time(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->change_inode_time);
|
||||
}
|
||||
void ext4_inode_set_change_inode_time(struct ext4_inode *inode, uint32_t time)
|
||||
{
|
||||
inode->change_inode_time = to_le32(time);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_modif_time(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->modification_time);
|
||||
}
|
||||
|
||||
void ext4_inode_set_modif_time(struct ext4_inode *inode, uint32_t time)
|
||||
{
|
||||
inode->modification_time = to_le32(time);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_del_time(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->deletion_time);
|
||||
}
|
||||
|
||||
void ext4_inode_set_del_time(struct ext4_inode *inode, uint32_t time)
|
||||
{
|
||||
inode->deletion_time = to_le32(time);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_gid(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->gid);
|
||||
}
|
||||
void ext4_inode_set_gid(struct ext4_inode *inode, uint32_t gid)
|
||||
{
|
||||
inode->gid = to_le32(gid);
|
||||
}
|
||||
|
||||
uint16_t ext4_inode_get_links_cnt(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le16(inode->links_count);
|
||||
}
|
||||
void ext4_inode_set_links_cnt(struct ext4_inode *inode, uint16_t cnt)
|
||||
{
|
||||
inode->links_count = to_le16(cnt);
|
||||
}
|
||||
|
||||
uint64_t ext4_inode_get_blocks_count(struct ext4_sblock *sb,
|
||||
struct ext4_inode *inode)
|
||||
{
|
||||
uint64_t cnt = to_le32(inode->blocks_count_lo);
|
||||
|
||||
if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE)) {
|
||||
|
||||
/* 48-bit field */
|
||||
cnt |= (uint64_t)to_le16(inode->osd2.linux2.blocks_high) << 32;
|
||||
|
||||
if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) {
|
||||
|
||||
uint32_t block_count = ext4_sb_get_block_size(sb);
|
||||
uint32_t b = ext4_inode_block_bits_count(block_count);
|
||||
return cnt << (b - 9);
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int ext4_inode_set_blocks_count(struct ext4_sblock *sb,
|
||||
struct ext4_inode *inode, uint64_t count)
|
||||
{
|
||||
/* 32-bit maximum */
|
||||
uint64_t max = 0;
|
||||
max = ~max >> 32;
|
||||
|
||||
if (count <= max) {
|
||||
inode->blocks_count_lo = to_le32((uint32_t)count);
|
||||
inode->osd2.linux2.blocks_high = 0;
|
||||
ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
/* Check if there can be used huge files (many blocks) */
|
||||
if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_HUGE_FILE))
|
||||
return EINVAL;
|
||||
|
||||
/* 48-bit maximum */
|
||||
max = 0;
|
||||
max = ~max >> 16;
|
||||
|
||||
if (count <= max) {
|
||||
inode->blocks_count_lo = to_le32((uint32_t)count);
|
||||
inode->osd2.linux2.blocks_high = to_le16((uint16_t)(count >> 32));
|
||||
ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
|
||||
} else {
|
||||
uint32_t block_count = ext4_sb_get_block_size(sb);
|
||||
uint32_t block_bits =ext4_inode_block_bits_count(block_count);
|
||||
|
||||
ext4_inode_set_flag(inode, EXT4_INODE_FLAG_HUGE_FILE);
|
||||
count = count >> (block_bits - 9);
|
||||
inode->blocks_count_lo = to_le32((uint32_t)count);
|
||||
inode->osd2.linux2.blocks_high = to_le16((uint16_t)(count >> 32));
|
||||
}
|
||||
|
||||
return EOK;
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_flags(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->flags);
|
||||
}
|
||||
void ext4_inode_set_flags(struct ext4_inode *inode, uint32_t flags)
|
||||
{
|
||||
inode->flags = to_le32(flags);
|
||||
}
|
||||
|
||||
uint32_t ext4_inode_get_generation(struct ext4_inode *inode)
|
||||
{
|
||||
return to_le32(inode->generation);
|
||||
}
|
||||
void ext4_inode_set_generation(struct ext4_inode *inode, uint32_t gen)
|
||||
{
|
||||
inode->generation = to_le32(gen);
|
||||
}
|
||||
|
||||
uint16_t ext4_inode_get_extra_isize(struct ext4_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_mbr.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MBR_SIGNATURE 0xAA55
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ext4_part_entry {
|
||||
uint8_t status;
|
||||
uint8_t chs1[3];
|
||||
uint8_t type;
|
||||
uint8_t chs2[3];
|
||||
uint32_t first_lba;
|
||||
uint32_t sectors;
|
||||
};
|
||||
|
||||
struct ext4_mbr {
|
||||
uint8_t bootstrap[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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_super.h"
|
||||
#include "ext4_block_group.h"
|
||||
#include "ext4_dir.h"
|
||||
#include "ext4_dir_idx.h"
|
||||
#include "ext4_fs.h"
|
||||
#include "ext4_inode.h"
|
||||
#include "ext4_ialloc.h"
|
||||
#include "ext4_mkfs.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_super.h"
|
||||
#include "ext4_crc32.h"
|
||||
|
||||
uint32_t ext4_block_group_cnt(struct ext4_sblock *s)
|
||||
{
|
||||
uint64_t blocks_count = ext4_sb_get_blocks_cnt(s);
|
||||
uint32_t blocks_per_group = ext4_get32(s, blocks_per_group);
|
||||
|
||||
uint32_t block_groups_count = (uint32_t)(blocks_count / blocks_per_group);
|
||||
|
||||
if (blocks_count % blocks_per_group)
|
||||
block_groups_count++;
|
||||
|
||||
return block_groups_count;
|
||||
}
|
||||
|
||||
uint32_t ext4_blocks_in_group_cnt(struct ext4_sblock *s, uint32_t bgid)
|
||||
{
|
||||
uint32_t block_group_count = ext4_block_group_cnt(s);
|
||||
uint32_t blocks_per_group = ext4_get32(s, blocks_per_group);
|
||||
uint64_t total_blocks = ext4_sb_get_blocks_cnt(s);
|
||||
|
||||
if (bgid < block_group_count - 1)
|
||||
return blocks_per_group;
|
||||
|
||||
return (uint32_t)(total_blocks - ((block_group_count - 1) * blocks_per_group));
|
||||
}
|
||||
|
||||
uint32_t ext4_inodes_in_group_cnt(struct ext4_sblock *s, uint32_t bgid)
|
||||
{
|
||||
uint32_t block_group_count = ext4_block_group_cnt(s);
|
||||
uint32_t inodes_per_group = ext4_get32(s, inodes_per_group);
|
||||
uint32_t total_inodes = ext4_get32(s, inodes_count);
|
||||
|
||||
if (bgid < block_group_count - 1)
|
||||
return inodes_per_group;
|
||||
|
||||
return (total_inodes - ((block_group_count - 1) * inodes_per_group));
|
||||
}
|
||||
|
||||
#if CONFIG_META_CSUM_ENABLE
|
||||
static uint32_t ext4_sb_csum(struct ext4_sblock *s)
|
||||
{
|
||||
|
||||
return ext4_crc32c(EXT4_CRC32_INIT, s,
|
||||
offsetof(struct ext4_sblock, checksum));
|
||||
}
|
||||
#else
|
||||
#define ext4_sb_csum(...) 0
|
||||
#endif
|
||||
|
||||
static bool ext4_sb_verify_csum(struct ext4_sblock *s)
|
||||
{
|
||||
if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM))
|
||||
return true;
|
||||
|
||||
if (s->checksum_type != to_le32(EXT4_CHECKSUM_CRC32C))
|
||||
return false;
|
||||
|
||||
return s->checksum == to_le32(ext4_sb_csum(s));
|
||||
}
|
||||
|
||||
static void ext4_sb_set_csum(struct ext4_sblock *s)
|
||||
{
|
||||
if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM))
|
||||
return;
|
||||
|
||||
s->checksum = to_le32(ext4_sb_csum(s));
|
||||
}
|
||||
|
||||
int ext4_sb_write(struct ext4_blockdev *bdev, struct ext4_sblock *s)
|
||||
{
|
||||
ext4_sb_set_csum(s);
|
||||
return ext4_block_writebytes(bdev, EXT4_SUPERBLOCK_OFFSET, s,
|
||||
EXT4_SUPERBLOCK_SIZE);
|
||||
}
|
||||
|
||||
int ext4_sb_read(struct ext4_blockdev *bdev, struct ext4_sblock *s)
|
||||
{
|
||||
return ext4_block_readbytes(bdev, EXT4_SUPERBLOCK_OFFSET, s,
|
||||
EXT4_SUPERBLOCK_SIZE);
|
||||
}
|
||||
|
||||
bool ext4_sb_check(struct ext4_sblock *s)
|
||||
{
|
||||
if (ext4_get16(s, magic) != EXT4_SUPERBLOCK_MAGIC)
|
||||
return false;
|
||||
|
||||
if (ext4_get32(s, inodes_count) == 0)
|
||||
return false;
|
||||
|
||||
if (ext4_sb_get_blocks_cnt(s) == 0)
|
||||
return false;
|
||||
|
||||
if (ext4_get32(s, blocks_per_group) == 0)
|
||||
return false;
|
||||
|
||||
if (ext4_get32(s, inodes_per_group) == 0)
|
||||
return false;
|
||||
|
||||
if (ext4_get16(s, inode_size) < 128)
|
||||
return false;
|
||||
|
||||
if (ext4_get32(s, first_inode) < 11)
|
||||
return false;
|
||||
|
||||
if (ext4_sb_get_desc_size(s) < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
return false;
|
||||
|
||||
if (ext4_sb_get_desc_size(s) > EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
|
||||
return false;
|
||||
|
||||
if (!ext4_sb_verify_csum(s))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int is_power_of(uint32_t a, uint32_t b)
|
||||
{
|
||||
while (1) {
|
||||
if (a < b)
|
||||
return 0;
|
||||
if (a == b)
|
||||
return 1;
|
||||
if ((a % b) != 0)
|
||||
return 0;
|
||||
a = a / b;
|
||||
}
|
||||
}
|
||||
|
||||
bool ext4_sb_sparse(uint32_t group)
|
||||
{
|
||||
if (group <= 1)
|
||||
return 1;
|
||||
|
||||
if (!(group & 1))
|
||||
return 0;
|
||||
|
||||
return (is_power_of(group, 7) || is_power_of(group, 5) ||
|
||||
is_power_of(group, 3));
|
||||
}
|
||||
|
||||
bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t group)
|
||||
{
|
||||
if (ext4_sb_feature_ro_com(s, EXT4_FRO_COM_SPARSE_SUPER) &&
|
||||
!ext4_sb_sparse(group))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t ext4_bg_num_gdb_meta(struct ext4_sblock *s, uint32_t group)
|
||||
{
|
||||
uint32_t dsc_per_block =
|
||||
ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
|
||||
|
||||
uint32_t metagroup = group / dsc_per_block;
|
||||
uint32_t first = metagroup * dsc_per_block;
|
||||
uint32_t last = first + dsc_per_block - 1;
|
||||
|
||||
if (group == first || group == first + 1 || group == last)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t ext4_bg_num_gdb_nometa(struct ext4_sblock *s, uint32_t group)
|
||||
{
|
||||
if (!ext4_sb_is_super_in_bg(s, group))
|
||||
return 0;
|
||||
uint32_t dsc_per_block =
|
||||
ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
|
||||
|
||||
uint32_t db_count =
|
||||
(ext4_block_group_cnt(s) + dsc_per_block - 1) / dsc_per_block;
|
||||
|
||||
if (ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG))
|
||||
return ext4_sb_first_meta_bg(s);
|
||||
|
||||
return db_count;
|
||||
}
|
||||
|
||||
uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group)
|
||||
{
|
||||
uint32_t dsc_per_block =
|
||||
ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
|
||||
uint32_t first_meta_bg = ext4_sb_first_meta_bg(s);
|
||||
uint32_t metagroup = group / dsc_per_block;
|
||||
|
||||
if (!ext4_sb_feature_incom(s,EXT4_FINCOM_META_BG) ||
|
||||
metagroup < first_meta_bg)
|
||||
return ext4_bg_num_gdb_nometa(s, group);
|
||||
|
||||
return ext4_bg_num_gdb_meta(s, group);
|
||||
}
|
||||
|
||||
uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
|
||||
uint32_t block_group)
|
||||
{
|
||||
uint32_t num;
|
||||
uint32_t dsc_per_block =
|
||||
ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
|
||||
|
||||
num = ext4_sb_is_super_in_bg(s, block_group);
|
||||
|
||||
if (!ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG) ||
|
||||
block_group < ext4_sb_first_meta_bg(s) * dsc_per_block) {
|
||||
if (num) {
|
||||
num += ext4_bg_num_gdb(s, block_group);
|
||||
num += ext4_get16(s, s_reserved_gdt_blocks);
|
||||
}
|
||||
} else {
|
||||
num += ext4_bg_num_gdb(s, block_group);
|
||||
}
|
||||
|
||||
uint32_t clustersize = 1024 << ext4_get32(s, log_cluster_size);
|
||||
uint32_t cluster_ratio = clustersize / ext4_sb_get_block_size(s);
|
||||
uint32_t v =
|
||||
(num + cluster_ratio - 1) >> ext4_get32(s, log_cluster_size);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -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 "ext4_config.h"
|
||||
#include "ext4_types.h"
|
||||
#include "ext4_misc.h"
|
||||
#include "ext4_errno.h"
|
||||
#include "ext4_debug.h"
|
||||
|
||||
#include "ext4_fs.h"
|
||||
#include "ext4_journal.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user