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
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,74 @@
|
||||
/* lwext4 freestanding allocation shim for NTLite drivers. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddk/memory.h>
|
||||
|
||||
#include "ext4_port.h"
|
||||
|
||||
void* ext4_ntl_malloc(size_t size) {
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
return MmAllocatePool(size);
|
||||
}
|
||||
|
||||
void* ext4_ntl_calloc(size_t count, size_t size) {
|
||||
if (count != 0 && size > SIZE_MAX / count)
|
||||
return (void*)0;
|
||||
|
||||
size_t bytes = count * size;
|
||||
if (bytes == 0)
|
||||
bytes = 1;
|
||||
|
||||
void* ptr = MmAllocatePool(bytes);
|
||||
if (ptr)
|
||||
memset(ptr, 0, bytes);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* ext4_ntl_realloc(void* ptr, size_t size) {
|
||||
if (!ptr)
|
||||
return ext4_ntl_malloc(size);
|
||||
if (size == 0) {
|
||||
MmFreePool(ptr);
|
||||
return (void*)0;
|
||||
}
|
||||
return MmReallocatePool(ptr, size);
|
||||
}
|
||||
|
||||
void ext4_ntl_free(void* ptr) {
|
||||
if (ptr)
|
||||
MmFreePool(ptr);
|
||||
}
|
||||
|
||||
/* lwext4 uses the standard allocation names when CONFIG_USE_USER_MALLOC=0. */
|
||||
void* malloc(size_t size) { return ext4_ntl_malloc(size); }
|
||||
void* calloc(size_t count, size_t size) { return ext4_ntl_calloc(count, size); }
|
||||
void* realloc(void* ptr, size_t size) { return ext4_ntl_realloc(ptr, size); }
|
||||
void free(void* ptr) { ext4_ntl_free(ptr); }
|
||||
/* lwext4 uses qsort for directory-index construction. A small insertion
|
||||
* sort is sufficient here and keeps the freestanding port dependency-free. */
|
||||
void qsort(void* base, size_t count, size_t size,
|
||||
int (*compare)(const void*, const void*)) {
|
||||
if (!base || !compare || size == 0 || count < 2)
|
||||
return;
|
||||
|
||||
unsigned char* bytes = (unsigned char*)base;
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
size_t j = i;
|
||||
while (j != 0) {
|
||||
unsigned char* left = bytes + (j - 1) * size;
|
||||
unsigned char* right = bytes + j * size;
|
||||
if (compare(left, right) <= 0)
|
||||
break;
|
||||
|
||||
for (size_t k = 0; k < size; ++k) {
|
||||
unsigned char temp = left[k];
|
||||
left[k] = right[k];
|
||||
right[k] = temp;
|
||||
}
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void* ext4_ntl_malloc(size_t size);
|
||||
void* ext4_ntl_calloc(size_t count, size_t size);
|
||||
void* ext4_ntl_realloc(void* ptr, size_t size);
|
||||
void ext4_ntl_free(void* ptr);
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
/* Minimal freestanding stdlib surface used by lwext4. */
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void* malloc(size_t size);
|
||||
void* calloc(size_t count, size_t size);
|
||||
void* realloc(void* ptr, size_t size);
|
||||
void free(void* ptr);
|
||||
void qsort(void* base, size_t count, size_t size,
|
||||
int (*compare)(const void*, const void*));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
# NTLite glue and lwext4 sources used by ext4.sys.
|
||||
# Run vendor_lwext4.ps1 once before building.
|
||||
drivers/fs/ext4/ext4_port.c
|
||||
drivers/fs/ext4/ext4_driver.c
|
||||
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_balloc.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_bcache.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_bitmap.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_block_group.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_blockdev.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_crc32.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_debug.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_dir.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_dir_idx.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_extent.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_fs.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_hash.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_ialloc.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_inode.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_journal.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_super.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_trans.c
|
||||
drivers/fs/ext4/third_party/lwext4/src/ext4_xattr.c
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* - The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <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
+51
-2919
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,4 @@
|
||||
// FAT driver internals shared by mount, directory, and file I/O code.
|
||||
// FAT provider entry points shared by the driver build.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
// FAT asynchronous file I/O.
|
||||
|
||||
#include "fat_internal.h"
|
||||
|
||||
// Native async read/write
|
||||
// Metadata work (FAT traversal/allocation and partial-cluster read-modify
|
||||
// preparation) still runs synchronously in the caller/completion context, but
|
||||
// file data transfers are submitted as disk IRPs and completion is chained from
|
||||
// the driver's IRP completion callback instead of the generic I/O worker.
|
||||
|
||||
typedef struct FAT_ASYNC_RW {
|
||||
LIST_ENTRY Link;
|
||||
FAT_VOL* Vol;
|
||||
FAT_FILE* File;
|
||||
uint8_t IsWrite;
|
||||
|
||||
uint64_t Offset;
|
||||
uint32_t OffsetInCluster;
|
||||
uint32_t Cluster;
|
||||
uint32_t LogicalCluster;
|
||||
size_t Remaining;
|
||||
size_t Done;
|
||||
size_t Requested;
|
||||
|
||||
uint8_t* Dst;
|
||||
const uint8_t* Src;
|
||||
uint8_t* RunBuf;
|
||||
|
||||
uint32_t CurrentNext;
|
||||
uint32_t CurrentLast;
|
||||
uint32_t CurrentClusters;
|
||||
uint32_t CurrentOffset;
|
||||
size_t CurrentCopy;
|
||||
|
||||
IRP Irp;
|
||||
DEVICE_OBJECT* CurrentDevice;
|
||||
IO_FS_ASYNC_COMPLETE Complete;
|
||||
void* CompleteContext;
|
||||
} FAT_ASYNC_RW;
|
||||
|
||||
static SPINLOCK gFatAsyncLock = SPINLOCK_INIT;
|
||||
static LIST_ENTRY gFatAsyncQueue;
|
||||
static SEMAPHORE* gFatAsyncSemaphore;
|
||||
static volatile int gFatAsyncInitState;
|
||||
|
||||
static void FatAsyncReadSubmitNext(FAT_ASYNC_RW* ctx);
|
||||
static void FatAsyncWriteSubmitNext(FAT_ASYNC_RW* ctx);
|
||||
|
||||
// Implements the async Worker operation.
|
||||
static void FatAsyncWorker(void* arg) {
|
||||
(void)arg;
|
||||
|
||||
while (true) {
|
||||
KSTATUS s = KeWaitSemaphore(gFatAsyncSemaphore, KE_WAIT_INFINITE);
|
||||
if (!KSUCCESS(s)) {
|
||||
KeYield();
|
||||
continue;
|
||||
}
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gFatAsyncLock);
|
||||
LIST_ENTRY* node = ListPopHead(&gFatAsyncQueue);
|
||||
KeReleaseSpinlock(&gFatAsyncLock, flags);
|
||||
if (!node) continue;
|
||||
|
||||
FAT_ASYNC_RW* ctx = CONTAINING_RECORD(node, FAT_ASYNC_RW, Link);
|
||||
if (ctx->IsWrite)
|
||||
FatAsyncWriteSubmitNext(ctx);
|
||||
else
|
||||
FatAsyncReadSubmitNext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the ensure Async Worker Started operation.
|
||||
static KSTATUS FatEnsureAsyncWorkerStarted(void) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gFatAsyncLock);
|
||||
if (gFatAsyncInitState == 2) {
|
||||
KeReleaseSpinlock(&gFatAsyncLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
if (gFatAsyncInitState == 1) {
|
||||
KeReleaseSpinlock(&gFatAsyncLock, flags);
|
||||
while (gFatAsyncInitState == 1)
|
||||
KeYield();
|
||||
return gFatAsyncInitState == 2 ? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
gFatAsyncInitState = 1;
|
||||
LIST_INIT(&gFatAsyncQueue);
|
||||
KeReleaseSpinlock(&gFatAsyncLock, flags);
|
||||
|
||||
KSTATUS s = KeSemaphoreCreate(0, 0x7fffffff, &gFatAsyncSemaphore);
|
||||
if (KSUCCESS(s)) {
|
||||
THREAD* worker = (THREAD*)0;
|
||||
const KE_THREAD_CREATE_PARAMETERS parameters =
|
||||
KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, "fat.async");
|
||||
s = KeCreateKernelThread((uintptr_t)FatAsyncWorker, 0,
|
||||
¶meters, &worker);
|
||||
(void)worker;
|
||||
}
|
||||
|
||||
flags = KeAcquireSpinlock(&gFatAsyncLock);
|
||||
gFatAsyncInitState = KSUCCESS(s) ? 2 : 0;
|
||||
KeReleaseSpinlock(&gFatAsyncLock, flags);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Implements the async Queue Continuation operation.
|
||||
static void FatAsyncQueueContinuation(FAT_ASYNC_RW* ctx) {
|
||||
if (!ctx) return;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gFatAsyncLock);
|
||||
ListInsertTail(&gFatAsyncQueue, &ctx->Link);
|
||||
KeReleaseSpinlock(&gFatAsyncLock, flags);
|
||||
KeSignalSemaphore(gFatAsyncSemaphore);
|
||||
}
|
||||
|
||||
// Implements the async Finish operation.
|
||||
static void FatAsyncFinish(FAT_ASYNC_RW* ctx, KSTATUS status, size_t information) {
|
||||
if (!ctx) return;
|
||||
if (ctx->CurrentDevice) {
|
||||
IoDereferenceDeviceObject(ctx->CurrentDevice);
|
||||
ctx->CurrentDevice = (DEVICE_OBJECT*)0;
|
||||
}
|
||||
if (ctx->RunBuf) MmFreePool(ctx->RunBuf);
|
||||
IO_FS_ASYNC_COMPLETE complete = ctx->Complete;
|
||||
void* completeContext = ctx->CompleteContext;
|
||||
MmFreePool(ctx);
|
||||
if (complete) complete(completeContext, status, information);
|
||||
}
|
||||
|
||||
// Implements the async Read Irp Complete operation.
|
||||
static void FatAsyncReadIrpComplete(IRP* irp, void* completeCtx) {
|
||||
FAT_ASYNC_RW* ctx = (FAT_ASYNC_RW*)completeCtx;
|
||||
if (!ctx) return;
|
||||
if (ctx->CurrentDevice) {
|
||||
IoDereferenceDeviceObject(ctx->CurrentDevice);
|
||||
ctx->CurrentDevice = (DEVICE_OBJECT*)0;
|
||||
}
|
||||
|
||||
if (!KSUCCESS(irp->Status)) {
|
||||
FatAsyncFinish(ctx, irp->Status, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(ctx->Dst, ctx->RunBuf + ctx->CurrentOffset, ctx->CurrentCopy);
|
||||
ctx->Dst += ctx->CurrentCopy;
|
||||
ctx->Done += ctx->CurrentCopy;
|
||||
ctx->Remaining -= ctx->CurrentCopy;
|
||||
ctx->OffsetInCluster = 0;
|
||||
ctx->LogicalCluster += ctx->CurrentClusters;
|
||||
ctx->Cluster = ctx->CurrentNext;
|
||||
if (ctx->CurrentClusters) {
|
||||
uint32_t sectors = ctx->CurrentClusters * ctx->Vol->SectorsPerCluster;
|
||||
uint32_t merges = ctx->CurrentClusters - 1u;
|
||||
FatStatsAddReadRun(ctx->Vol, sectors, merges);
|
||||
}
|
||||
|
||||
FatAsyncQueueContinuation(ctx);
|
||||
}
|
||||
|
||||
// Implements the async Read Submit Next operation.
|
||||
static void FatAsyncReadSubmitNext(FAT_ASYNC_RW* ctx) {
|
||||
if (ctx->Remaining == 0) {
|
||||
FatAsyncFinish(ctx, NTLSTATUS_SUCCESS, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FatIsDataCluster(ctx->Vol, ctx->Cluster)) {
|
||||
if (ctx->Done < ctx->Requested)
|
||||
memset(ctx->Dst, 0, ctx->Requested - ctx->Done);
|
||||
FatAsyncFinish(ctx, NTLSTATUS_DEVICE_ERROR, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
FAT_VOL* v = ctx->Vol;
|
||||
uint32_t runStart = ctx->Cluster;
|
||||
uint32_t runSectors = 0;
|
||||
uint32_t next = 0;
|
||||
if (!FatBuildReadRun(ctx->File, ctx->LogicalCluster, ctx->Cluster,
|
||||
FAT_READ_RUN_MAX_SECTORS, &runSectors, &next)) {
|
||||
FatAsyncFinish(ctx, NTLSTATUS_DEVICE_ERROR, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - ctx->OffsetInCluster;
|
||||
size_t copy = avail < ctx->Remaining ? avail : ctx->Remaining;
|
||||
|
||||
ctx->CurrentNext = next;
|
||||
ctx->CurrentLast = runStart + (runSectors / v->SectorsPerCluster) - 1u;
|
||||
ctx->CurrentClusters = runSectors / v->SectorsPerCluster;
|
||||
ctx->CurrentOffset = ctx->OffsetInCluster;
|
||||
ctx->CurrentCopy = copy;
|
||||
|
||||
ctx->CurrentDevice = IoReferenceDiskDevice(v->DriveIndex);
|
||||
if (!ctx->CurrentDevice) {
|
||||
FatAsyncFinish(ctx, NTLSTATUS_NOT_FOUND, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
IoBuildReadWriteIrp(&ctx->Irp, IRP_MJ_READ,
|
||||
FatClusterToLba(v, runStart) * 512ull,
|
||||
runSectors * 512u,
|
||||
ctx->RunBuf);
|
||||
ctx->Irp.CompletionFn = FatAsyncReadIrpComplete;
|
||||
ctx->Irp.CompletionCtx = ctx;
|
||||
|
||||
KSTATUS s = IoSubmitIrp(ctx->CurrentDevice, &ctx->Irp);
|
||||
if (s != NTLSTATUS_PENDING) {
|
||||
IoDereferenceDeviceObject(ctx->CurrentDevice);
|
||||
ctx->CurrentDevice = (DEVICE_OBJECT*)0;
|
||||
FatAsyncFinish(ctx, s, ctx->Done);
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS FatReadAsync(IO_VOLUME_PROVIDER* fs, void* fileCtx,
|
||||
uint64_t offset, void* buf, size_t size,
|
||||
IO_FS_ASYNC_COMPLETE complete,
|
||||
void* completeContext) {
|
||||
(void)fs;
|
||||
if (!complete) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!fileCtx || (!buf && size)) {
|
||||
complete(completeContext, NTLSTATUS_INVALID_PARAMETER, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (offset >= f->FileSize || size == 0) {
|
||||
complete(completeContext, NTLSTATUS_SUCCESS, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
if (offset + size > f->FileSize)
|
||||
size = (size_t)(f->FileSize - offset);
|
||||
|
||||
KSTATUS ws = FatEnsureAsyncWorkerStarted();
|
||||
if (!KSUCCESS(ws)) {
|
||||
complete(completeContext, ws, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
FAT_ASYNC_RW* ctx = (FAT_ASYNC_RW*)MmAllocatePool(sizeof(FAT_ASYNC_RW));
|
||||
if (!ctx) {
|
||||
complete(completeContext, NTLSTATUS_NO_MEMORY, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
LIST_INIT(&ctx->Link);
|
||||
|
||||
ctx->RunBuf = (uint8_t*)MmAllocatePool(FAT_READ_RUN_MAX_SECTORS * 512u);
|
||||
if (!ctx->RunBuf) {
|
||||
MmFreePool(ctx);
|
||||
complete(completeContext, NTLSTATUS_NO_MEMORY, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
ctx->Vol = v;
|
||||
ctx->File = f;
|
||||
ctx->IsWrite = 0;
|
||||
ctx->Offset = offset;
|
||||
ctx->OffsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
ctx->LogicalCluster = (uint32_t)(offset / v->BytesPerCluster);
|
||||
ctx->Cluster = FatResolveFileCluster(f, ctx->LogicalCluster);
|
||||
ctx->Remaining = size;
|
||||
ctx->Requested = size;
|
||||
ctx->Dst = (uint8_t*)buf;
|
||||
ctx->Complete = complete;
|
||||
ctx->CompleteContext = completeContext;
|
||||
|
||||
FatAsyncQueueContinuation(ctx);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
// Implements the async Write Irp Complete operation.
|
||||
static void FatAsyncWriteIrpComplete(IRP* irp, void* completeCtx) {
|
||||
FAT_ASYNC_RW* ctx = (FAT_ASYNC_RW*)completeCtx;
|
||||
if (!ctx) return;
|
||||
if (ctx->CurrentDevice) {
|
||||
IoDereferenceDeviceObject(ctx->CurrentDevice);
|
||||
ctx->CurrentDevice = (DEVICE_OBJECT*)0;
|
||||
}
|
||||
|
||||
if (!KSUCCESS(irp->Status)) {
|
||||
FatAsyncFinish(ctx, irp->Status, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->Src += ctx->CurrentCopy;
|
||||
ctx->Done += ctx->CurrentCopy;
|
||||
ctx->Remaining -= ctx->CurrentCopy;
|
||||
ctx->OffsetInCluster = 0;
|
||||
ctx->Cluster = ctx->CurrentNext;
|
||||
|
||||
FatAsyncQueueContinuation(ctx);
|
||||
}
|
||||
|
||||
// Implements the async Write Submit Next operation.
|
||||
static void FatAsyncWriteSubmitNext(FAT_ASYNC_RW* ctx) {
|
||||
if (ctx->Remaining == 0) {
|
||||
uint64_t newEnd = ctx->Offset + ctx->Done;
|
||||
if (ctx->Done > 0) {
|
||||
FAT_DIRENT_UPDATE u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
FatQueryEncodedTime(&u.Timestamp);
|
||||
u.Flags = FAT_DIRENT_UPDATE_WRITE_TIME;
|
||||
if (newEnd > ctx->File->FileSize) {
|
||||
ctx->File->FileSize = (uint32_t)newEnd;
|
||||
u.Flags |= FAT_DIRENT_UPDATE_SIZE;
|
||||
u.Size = ctx->File->FileSize;
|
||||
}
|
||||
FatDirentUpdateMetadata(ctx->Vol, ctx->File->DirentLba,
|
||||
ctx->File->DirentOffset, &u);
|
||||
}
|
||||
FatAsyncFinish(ctx, NTLSTATUS_SUCCESS, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
FAT_VOL* v = ctx->Vol;
|
||||
if (!FatIsDataCluster(ctx->Vol, ctx->Cluster)) {
|
||||
if (FatIsDataCluster(ctx->Vol, ctx->CurrentLast)) {
|
||||
ctx->Cluster = FatAppendCluster(v, ctx->CurrentLast);
|
||||
if (!ctx->Cluster) {
|
||||
FatAsyncFinish(ctx, NTLSTATUS_NO_MEMORY, ctx->Done);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
FatAsyncFinish(ctx, NTLSTATUS_DEVICE_ERROR, ctx->Done);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t runStart = ctx->Cluster;
|
||||
uint32_t runSectors = v->SectorsPerCluster;
|
||||
uint32_t last = ctx->Cluster;
|
||||
uint32_t next = FatEntry(v, last);
|
||||
|
||||
if (ctx->OffsetInCluster == 0) {
|
||||
while (next == last + 1 && FatIsDataCluster(v, next) &&
|
||||
runSectors + v->SectorsPerCluster <= FAT_WRITE_RUN_MAX_SECTORS) {
|
||||
last = next;
|
||||
next = FatEntry(v, last);
|
||||
runSectors += v->SectorsPerCluster;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - ctx->OffsetInCluster;
|
||||
size_t copy = avail < ctx->Remaining ? avail : ctx->Remaining;
|
||||
|
||||
bool partial = (ctx->OffsetInCluster > 0) || (copy < (size_t)runBytes);
|
||||
if (partial) {
|
||||
KSTATUS rs = FatReadSectors(v, FatClusterToLba(v, runStart), runSectors, ctx->RunBuf);
|
||||
if (!KSUCCESS(rs)) {
|
||||
FatAsyncFinish(ctx, rs, ctx->Done);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ctx->RunBuf + ctx->OffsetInCluster, ctx->Src, copy);
|
||||
|
||||
ctx->CurrentNext = next;
|
||||
ctx->CurrentLast = last;
|
||||
ctx->CurrentOffset = ctx->OffsetInCluster;
|
||||
ctx->CurrentCopy = copy;
|
||||
|
||||
ctx->CurrentDevice = IoReferenceDiskDevice(v->DriveIndex);
|
||||
if (!ctx->CurrentDevice) {
|
||||
FatAsyncFinish(ctx, NTLSTATUS_NOT_FOUND, ctx->Done);
|
||||
return;
|
||||
}
|
||||
|
||||
IoBuildReadWriteIrp(&ctx->Irp, IRP_MJ_WRITE,
|
||||
FatClusterToLba(v, runStart) * 512ull,
|
||||
runSectors * 512u,
|
||||
ctx->RunBuf);
|
||||
ctx->Irp.CompletionFn = FatAsyncWriteIrpComplete;
|
||||
ctx->Irp.CompletionCtx = ctx;
|
||||
|
||||
KSTATUS s = IoSubmitIrp(ctx->CurrentDevice, &ctx->Irp);
|
||||
if (s != NTLSTATUS_PENDING) {
|
||||
IoDereferenceDeviceObject(ctx->CurrentDevice);
|
||||
ctx->CurrentDevice = (DEVICE_OBJECT*)0;
|
||||
FatAsyncFinish(ctx, s, ctx->Done);
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS FatWriteAsync(IO_VOLUME_PROVIDER* fs, void* fileCtx,
|
||||
uint64_t offset, const void* buf, size_t size,
|
||||
IO_FS_ASYNC_COMPLETE complete,
|
||||
void* completeContext) {
|
||||
(void)fs;
|
||||
if (!complete) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!fileCtx || (!buf && size)) {
|
||||
complete(completeContext, NTLSTATUS_INVALID_PARAMETER, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
if (size == 0) {
|
||||
complete(completeContext, NTLSTATUS_SUCCESS, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
KSTATUS ws = FatEnsureAsyncWorkerStarted();
|
||||
if (!KSUCCESS(ws)) {
|
||||
complete(completeContext, ws, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
FatFileInvalidateChainCache(f);
|
||||
|
||||
if (f->StartCluster < 2) {
|
||||
uint32_t first = FatAllocCluster(v);
|
||||
if (!first) {
|
||||
complete(completeContext, NTLSTATUS_NO_MEMORY, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
f->StartCluster = first;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, first);
|
||||
}
|
||||
|
||||
uint32_t clusterIndex = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t cluster = f->StartCluster;
|
||||
for (uint32_t i = 0; i < clusterIndex; i++) {
|
||||
uint32_t next = FatEntry(v, cluster);
|
||||
if (!FatIsDataCluster(v, next)) {
|
||||
next = FatAppendCluster(v, cluster);
|
||||
if (!next) {
|
||||
complete(completeContext, NTLSTATUS_NO_MEMORY, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
cluster = next;
|
||||
}
|
||||
|
||||
FAT_ASYNC_RW* ctx = (FAT_ASYNC_RW*)MmAllocatePool(sizeof(FAT_ASYNC_RW));
|
||||
if (!ctx) {
|
||||
complete(completeContext, NTLSTATUS_NO_MEMORY, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
LIST_INIT(&ctx->Link);
|
||||
|
||||
ctx->RunBuf = (uint8_t*)MmAllocatePool(FAT_WRITE_RUN_MAX_SECTORS * 512u);
|
||||
if (!ctx->RunBuf) {
|
||||
MmFreePool(ctx);
|
||||
complete(completeContext, NTLSTATUS_NO_MEMORY, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
ctx->Vol = v;
|
||||
ctx->File = f;
|
||||
ctx->IsWrite = 1;
|
||||
ctx->Offset = offset;
|
||||
ctx->OffsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
ctx->Cluster = cluster;
|
||||
ctx->Remaining = size;
|
||||
ctx->Requested = size;
|
||||
ctx->Src = (const uint8_t*)buf;
|
||||
ctx->Complete = complete;
|
||||
ctx->CompleteContext = completeContext;
|
||||
|
||||
FatAsyncQueueContinuation(ctx);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,298 @@
|
||||
// FAT mount discovery, BPB validation, and driver lifetime.
|
||||
|
||||
#include "fat_internal.h"
|
||||
|
||||
// FAT BPB validation.
|
||||
typedef struct FAT_BPB_LAYOUT {
|
||||
FAT_VARIANT Variant;
|
||||
uint32_t TotalSectors;
|
||||
uint32_t FatSize;
|
||||
uint32_t RootDirSectors;
|
||||
uint32_t DataSectors;
|
||||
uint32_t TotalClusters;
|
||||
} FAT_BPB_LAYOUT;
|
||||
|
||||
static int FatComputeBpbLayout(const BPB* bpb, FAT_BPB_LAYOUT* out) {
|
||||
if (!bpb || !out) return 0;
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (bpb->BytesPerSector != 512) return 0;
|
||||
if (bpb->SectorsPerCluster == 0) return 0;
|
||||
if (bpb->NumFats == 0) return 0;
|
||||
if (bpb->ReservedSectors == 0) return 0;
|
||||
|
||||
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
uint32_t fatSize = bpb->FatSize16 ? bpb->FatSize16 : bpb->FatSize32;
|
||||
if (!totalSectors || !fatSize) return 0;
|
||||
|
||||
uint32_t rootDirSectors = ((uint32_t)bpb->RootEntryCount * sizeof(DIRENT) +
|
||||
(uint32_t)bpb->BytesPerSector - 1u) /
|
||||
(uint32_t)bpb->BytesPerSector;
|
||||
uint32_t metadataSectors = (uint32_t)bpb->ReservedSectors +
|
||||
(uint32_t)bpb->NumFats * fatSize +
|
||||
rootDirSectors;
|
||||
if (totalSectors <= metadataSectors) return 0;
|
||||
|
||||
uint32_t dataSectors = totalSectors - metadataSectors;
|
||||
uint32_t totalClusters = dataSectors / bpb->SectorsPerCluster;
|
||||
FAT_VARIANT variant;
|
||||
if (totalClusters < 4085u)
|
||||
variant = FatVariant12;
|
||||
else if (totalClusters < 65525u)
|
||||
variant = FatVariant16;
|
||||
else
|
||||
variant = FatVariant32;
|
||||
|
||||
if (variant == FatVariant16) {
|
||||
if (bpb->FatSize16 == 0 || bpb->RootEntryCount == 0) return 0;
|
||||
} else if (variant == FatVariant32) {
|
||||
if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) return 0;
|
||||
if (bpb->FatSize32 == 0 || bpb->RootCluster < 2) return 0;
|
||||
}
|
||||
|
||||
out->Variant = variant;
|
||||
out->TotalSectors = totalSectors;
|
||||
out->FatSize = fatSize;
|
||||
out->RootDirSectors = rootDirSectors;
|
||||
out->DataSectors = dataSectors;
|
||||
out->TotalClusters = totalClusters;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Driver entry and dynamic partition-backed mount instances.
|
||||
static LIST_ENTRY gFatMounts;
|
||||
static uint32_t gMountedCount;
|
||||
static MUTEX gFatMountMutex;
|
||||
static SPINLOCK gFatMountMutexInitLock = SPINLOCK_INIT;
|
||||
static int gFatMountMutexReady;
|
||||
|
||||
// Marks only this FAT mount's cache state unusable when its backing volume dies.
|
||||
void FatVolumeDead(IO_VOLUME_PROVIDER* provider, IO_VOLUME_REMOVAL_REASON reason) {
|
||||
(void)reason;
|
||||
if (!provider || !provider->Context)
|
||||
return;
|
||||
FatCacheInvalidateVolume((FAT_VOL*)provider->Context);
|
||||
}
|
||||
|
||||
// Releases one dynamically allocated FAT mount after every volume reference has drained.
|
||||
void FatDestroyVolumeProvider(IO_VOLUME_PROVIDER* provider) {
|
||||
if (!provider || !provider->Context)
|
||||
return;
|
||||
FAT_VOL* volume = (FAT_VOL*)provider->Context;
|
||||
FatCacheInvalidateVolume(volume);
|
||||
memset(volume, 0, sizeof(*volume));
|
||||
MmFreePool(volume);
|
||||
}
|
||||
|
||||
// Initializes the mount-list mutex without allocating kernel objects.
|
||||
static void FatMountEnsureMutex(void) {
|
||||
if (gFatMountMutexReady)
|
||||
return;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gFatMountMutexInitLock);
|
||||
if (!gFatMountMutexReady) {
|
||||
memset(&gFatMountMutex, 0, sizeof(gFatMountMutex));
|
||||
ObInitializeObjectHeader(&gFatMountMutex.Header, ObjectTypeMutex);
|
||||
gFatMountMutex.Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||
gFatMountMutex.Owner = (struct THREAD*)0;
|
||||
LIST_INIT(&gFatMountMutex.WaitList);
|
||||
LIST_INIT(&gFatMountMutex.OwnerLink);
|
||||
gFatMountMutex.DonationClass = KeSchedulingNormal;
|
||||
gFatMountMutex.DonationPriority = PriorityLow;
|
||||
gFatMountMutex.HasDonation = 0;
|
||||
gFatMountMutex.Abandoned = 0;
|
||||
gFatMountMutexReady = 1;
|
||||
}
|
||||
KeReleaseSpinlock(&gFatMountMutexInitLock, flags);
|
||||
}
|
||||
|
||||
// Returns one active FAT mount for an exact partition extent while the mount mutex is held.
|
||||
static FAT_VOL* FatFindMountLocked(const IO_PARTITION_INFO* partition) {
|
||||
if (!partition)
|
||||
return (FAT_VOL*)0;
|
||||
for (LIST_ENTRY* entry = gFatMounts.Fwd; entry != &gFatMounts; entry = entry->Fwd) {
|
||||
FAT_VOL* volume = CONTAINING_RECORD(entry, FAT_VOL, MountLink);
|
||||
if (volume->DriveIndex == partition->DiskIndex &&
|
||||
volume->PartitionLba == partition->StartLba) {
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
return (FAT_VOL*)0;
|
||||
}
|
||||
|
||||
// Probes one I/O-core partition extent and mounts it when its VBR describes supported FAT16/FAT32.
|
||||
static bool FatTryMountPartition(const IO_PARTITION_INFO* partition) {
|
||||
if (!partition || partition->SectorCount == 0 || !IoDiskDevicePresent(partition->DiskIndex))
|
||||
return false;
|
||||
|
||||
FatMountEnsureMutex();
|
||||
if (!KE_MUTEX_ACQUIRE_SUCCEEDED(KeAcquireMutex(&gFatMountMutex, KE_WAIT_INFINITE)))
|
||||
return false;
|
||||
|
||||
if (FatFindMountLocked(partition)) {
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)MmAllocatePool(sizeof(FAT_VOL));
|
||||
if (!v) {
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return false;
|
||||
}
|
||||
memset(v, 0, sizeof(*v));
|
||||
LIST_INIT(&v->MountLink);
|
||||
v->DriveIndex = partition->DiskIndex;
|
||||
v->PartitionLba = partition->StartLba;
|
||||
v->PartitionSize = partition->SectorCount;
|
||||
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(FatReadSectors(v, v->PartitionLba, 1, sector))) {
|
||||
MmFreePool(v);
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
BPB* bpb = (BPB*)sector;
|
||||
FAT_BPB_LAYOUT layout;
|
||||
if (!FatComputeBpbLayout(bpb, &layout) ||
|
||||
(layout.Variant != FatVariant16 && layout.Variant != FatVariant32)) {
|
||||
MmFreePool(v);
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
v->Variant = layout.Variant;
|
||||
v->BytesPerSector = bpb->BytesPerSector;
|
||||
v->SectorsPerCluster = bpb->SectorsPerCluster;
|
||||
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
|
||||
v->ReservedSectors = bpb->ReservedSectors;
|
||||
v->NumFats = bpb->NumFats;
|
||||
v->FatSize = layout.FatSize;
|
||||
v->RootEntryCount = bpb->RootEntryCount;
|
||||
v->RootDirSectors = layout.RootDirSectors;
|
||||
v->RootDirStart = v->PartitionLba + bpb->ReservedSectors +
|
||||
(uint64_t)bpb->NumFats * layout.FatSize;
|
||||
v->RootCluster = v->Variant == FatVariant16 ? FAT_FIXED_ROOT_CLUSTER : bpb->RootCluster;
|
||||
v->DataStart = v->RootDirStart + layout.RootDirSectors;
|
||||
v->NextFreeHint = 2;
|
||||
v->TotalClusters = layout.TotalClusters;
|
||||
if (v->PartitionSize > layout.TotalSectors)
|
||||
v->PartitionSize = layout.TotalSectors;
|
||||
|
||||
FatDebugPrint("FAT: disk=%u lba=%llu %s layout fat=%u root-sectors=%u data=%u clusters=%u\n",
|
||||
v->DriveIndex, (unsigned long long)v->PartitionLba,
|
||||
FatVariantName(v->Variant), v->FatSize,
|
||||
v->RootDirSectors, layout.DataSectors, v->TotalClusters);
|
||||
|
||||
IO_VOLUME_PROVIDER provider;
|
||||
FatInitProvider(&provider, v);
|
||||
IO_VOLUME_SOURCE volParams;
|
||||
volParams.Type = IoVolumeSourcePartition;
|
||||
volParams.DiskIndex = v->DriveIndex;
|
||||
memset(volParams.Reserved, 0, sizeof(volParams.Reserved));
|
||||
volParams.StartLba = v->PartitionLba;
|
||||
volParams.SectorCount = v->PartitionSize;
|
||||
|
||||
IO_VOLUME* volume = (IO_VOLUME*)0;
|
||||
KSTATUS status = IoCreateVolume(&provider, &volParams, &volume);
|
||||
if (!KSUCCESS(status)) {
|
||||
if (volume) {
|
||||
(void)IoRemoveVolume(volume, IoVolumeUnmounted);
|
||||
IoReleaseVolume(volume);
|
||||
} else {
|
||||
MmFreePool(v);
|
||||
}
|
||||
DbgPrint("FAT: disk=%u lba=%llu mount failed status=0x%x\n",
|
||||
partition->DiskIndex, (unsigned long long)partition->StartLba, status);
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retain the IoCreateVolume caller reference while this provider remains on the active mount list.
|
||||
v->Volume = volume;
|
||||
ListInsertTail(&gFatMounts, &v->MountLink);
|
||||
gMountedCount++;
|
||||
uint32_t instanceId = IoGetVolumeInstanceId(volume);
|
||||
uint32_t mountedCount = gMountedCount;
|
||||
|
||||
DbgPrint("FAT: disk=%u lba=%llu %s mounted volume=%u; total=%u\n",
|
||||
v->DriveIndex, (unsigned long long)v->PartitionLba,
|
||||
FatVariantName(v->Variant), instanceId, mountedCount);
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unmounts one exact FAT provider instance when its backing partition disappears.
|
||||
static void FatUnmountPartition(const IO_PARTITION_INFO* partition, IO_PARTITION_CHANGE change) {
|
||||
if (!partition)
|
||||
return;
|
||||
|
||||
FatMountEnsureMutex();
|
||||
if (!KE_MUTEX_ACQUIRE_SUCCEEDED(KeAcquireMutex(&gFatMountMutex, KE_WAIT_INFINITE)))
|
||||
return;
|
||||
|
||||
FAT_VOL* v = FatFindMountLocked(partition);
|
||||
if (!v) {
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
IO_VOLUME* volume = v->Volume;
|
||||
uint32_t instanceId = IoGetVolumeInstanceId(volume);
|
||||
ListRemove(&v->MountLink);
|
||||
v->Volume = (IO_VOLUME*)0;
|
||||
if (gMountedCount)
|
||||
gMountedCount--;
|
||||
uint32_t mountedCount = gMountedCount;
|
||||
KeReleaseMutex(&gFatMountMutex);
|
||||
|
||||
(void)IoRemoveVolume(volume, IoVolumeBackingRemoved);
|
||||
IoReleaseVolume(volume); // Drop the active mount-list reference.
|
||||
DbgPrint("FAT: disk=%u lba=%llu %s; unmounted volume=%u; total=%u\n",
|
||||
partition->DiskIndex, (unsigned long long)partition->StartLba,
|
||||
change == IoPartitionSurpriseRemoved ? "surprise removed" : "removed",
|
||||
instanceId, mountedCount);
|
||||
}
|
||||
|
||||
// Receives immutable partition extents from the I/O core; FAT never parses partition tables.
|
||||
static void FatPartitionChange(const IO_PARTITION_INFO* partition,
|
||||
IO_PARTITION_CHANGE change,
|
||||
void* context) {
|
||||
(void)context;
|
||||
switch (change) {
|
||||
case IoPartitionArrived:
|
||||
(void)FatTryMountPartition(partition);
|
||||
break;
|
||||
case IoPartitionSurpriseRemoved:
|
||||
case IoPartitionRemoved:
|
||||
FatUnmountPartition(partition, change);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes FAT and subscribes to the authoritative kernel partition stream.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("FAT: Init\n");
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)init;
|
||||
|
||||
FatCacheInvalidate();
|
||||
LIST_INIT(&gFatMounts);
|
||||
gMountedCount = 0;
|
||||
FatMountEnsureMutex();
|
||||
|
||||
KSTATUS status = IoRegisterPartitionChangeCallback(FatPartitionChange, NULL);
|
||||
if (!KSUCCESS(status)) {
|
||||
DbgPrint("FAT: partition callback registration failed status=0x%x\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!gMountedCount)
|
||||
DbgPrint("FAT: no mountable FAT partitions found; driver resident for partition changes.\n");
|
||||
DbgPrint("FAT: Init complete; mounted %u volume(s).\n", gMountedCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,755 @@
|
||||
// FAT synchronous file and directory operations.
|
||||
|
||||
#include "fat_internal.h"
|
||||
|
||||
// Open / Close.
|
||||
KSTATUS FatOpen(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)MmAllocatePool(sizeof(FAT_FILE));
|
||||
if (!f) return NTLSTATUS_NO_MEMORY;
|
||||
f->StartCluster = cluster; f->FileSize = size; f->Vol = v;
|
||||
f->DirentLba = direntLba; f->DirentOffset = direntOffset;
|
||||
FatFileInitChainCache(f);
|
||||
*fileCtx = f;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Flushes FAT metadata and the backing device write cache.
|
||||
KSTATUS FatFlush(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
if (!f || !f->Vol)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FatFlushCache(f->Vol);
|
||||
return IoFlushDisk(f->Vol->DriveIndex);
|
||||
}
|
||||
|
||||
// Releases the requested object.
|
||||
void FatClose(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FatFlushCache(f->Vol);
|
||||
MmFreePool(fileCtx);
|
||||
}
|
||||
|
||||
// Read (fix #4: contiguous-run coalescing)
|
||||
// Cluster-chain lookup is cached per open file, and data spans are allowed to
|
||||
// grow beyond the AHCI bounce size because the device layer now splits large
|
||||
// sector requests for the concrete controller.
|
||||
|
||||
KSTATUS FatRead(IO_VOLUME_PROVIDER* fs, void* fileCtx,
|
||||
uint64_t offset, void* buf,
|
||||
size_t size, size_t* bytesRead) {
|
||||
(void)fs;
|
||||
if (!bytesRead) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*bytesRead = 0;
|
||||
if (!fileCtx || !buf) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (offset >= f->FileSize || size == 0) return NTLSTATUS_SUCCESS;
|
||||
if (size > (size_t)(f->FileSize - offset)) size = (size_t)(f->FileSize - offset);
|
||||
size_t wanted = size;
|
||||
|
||||
uint32_t initialOffsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint64_t initialBytesCovered = (uint64_t)initialOffsetInCluster + (uint64_t)size;
|
||||
uint32_t maxUsefulSectors = (uint32_t)((initialBytesCovered + 511u) / 512u);
|
||||
if (maxUsefulSectors < v->SectorsPerCluster) maxUsefulSectors = v->SectorsPerCluster;
|
||||
if (maxUsefulSectors > FAT_READ_RUN_MAX_SECTORS)
|
||||
maxUsefulSectors = FAT_READ_RUN_MAX_SECTORS;
|
||||
|
||||
uint8_t* runBuf = (uint8_t*)MmAllocatePool((size_t)maxUsefulSectors * 512u);
|
||||
if (!runBuf) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
size_t totalRead = 0;
|
||||
uint8_t* out = (uint8_t*)buf;
|
||||
KSTATUS readStatus = NTLSTATUS_SUCCESS;
|
||||
uint32_t logicalCluster = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint32_t cluster = FatResolveFileCluster(f, logicalCluster);
|
||||
|
||||
while (size > 0 && FatIsDataCluster(v, cluster)) {
|
||||
uint64_t bytesCovered = (uint64_t)offsetInCluster + (uint64_t)size;
|
||||
uint32_t usefulSectors = (uint32_t)((bytesCovered + 511u) / 512u);
|
||||
if (usefulSectors < v->SectorsPerCluster) usefulSectors = v->SectorsPerCluster;
|
||||
if (usefulSectors > FAT_READ_RUN_MAX_SECTORS)
|
||||
usefulSectors = FAT_READ_RUN_MAX_SECTORS;
|
||||
if (usefulSectors > maxUsefulSectors) usefulSectors = maxUsefulSectors;
|
||||
|
||||
uint32_t runSectors = 0;
|
||||
uint32_t next = 0;
|
||||
if (!FatBuildReadRun(f, logicalCluster, cluster, usefulSectors,
|
||||
&runSectors, &next)) {
|
||||
readStatus = NTLSTATUS_DEVICE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
KSTATUS rs = FatReadSectors(v, FatClusterToLba(v, cluster), runSectors, runBuf);
|
||||
if (!KSUCCESS(rs)) {
|
||||
readStatus = rs;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - offsetInCluster;
|
||||
size_t copy = avail < size ? avail : size;
|
||||
uint32_t runClusters = runSectors / v->SectorsPerCluster;
|
||||
uint32_t merges = runClusters ? runClusters - 1u : 0;
|
||||
FatStatsAddReadRun(v, runSectors, merges);
|
||||
|
||||
memcpy(out, runBuf + offsetInCluster, copy);
|
||||
out += copy;
|
||||
totalRead += copy;
|
||||
size -= copy;
|
||||
offsetInCluster = 0;
|
||||
logicalCluster += runClusters;
|
||||
cluster = next;
|
||||
}
|
||||
|
||||
if (totalRead != wanted) {
|
||||
// The directory entry says the file has more data than the cluster chain
|
||||
// or disk read actually supplied. Clear the caller's remainder so stale
|
||||
// memory cannot be interpreted as file contents.
|
||||
memset(out, 0, wanted - totalRead);
|
||||
}
|
||||
|
||||
MmFreePool(runBuf);
|
||||
*bytesRead = totalRead;
|
||||
if (totalRead != wanted)
|
||||
return KSUCCESS(readStatus) ? NTLSTATUS_DEVICE_ERROR : readStatus;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Write (fix #4: contiguous-run coalescing)
|
||||
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
|
||||
// write; partial head/tail clusters still require it.
|
||||
|
||||
KSTATUS FatWrite(IO_VOLUME_PROVIDER* fs, void* fileCtx,
|
||||
uint64_t offset, const void* buf,
|
||||
size_t size, size_t* bytesWritten) {
|
||||
(void)fs;
|
||||
if (!size) { *bytesWritten = 0; return NTLSTATUS_SUCCESS; }
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
const uint8_t* src = (const uint8_t*)buf;
|
||||
FatFileInvalidateChainCache(f);
|
||||
|
||||
// Ensure at least one cluster exists.
|
||||
if (f->StartCluster < 2) {
|
||||
uint32_t first = FatAllocCluster(v);
|
||||
if (!first) { *bytesWritten = 0; return NTLSTATUS_NO_MEMORY; }
|
||||
FatFlushCache(v);
|
||||
f->StartCluster = first;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, first);
|
||||
}
|
||||
|
||||
// Walk to the starting cluster, allocating as needed.
|
||||
uint32_t clusterIndex = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint32_t cluster = f->StartCluster;
|
||||
|
||||
for (uint32_t i = 0; i < clusterIndex; i++) {
|
||||
uint32_t next = FatEntry(v, cluster);
|
||||
if (!FatIsDataCluster(v, next)) {
|
||||
next = FatAppendCluster(v, cluster);
|
||||
if (!next) { *bytesWritten = 0; return NTLSTATUS_NO_MEMORY; }
|
||||
}
|
||||
cluster = next;
|
||||
}
|
||||
|
||||
uint8_t* runBuf = (uint8_t*)MmAllocatePool(FAT_WRITE_RUN_MAX_SECTORS * 512u);
|
||||
if (!runBuf) { *bytesWritten = 0; return NTLSTATUS_NO_MEMORY; }
|
||||
|
||||
size_t written = 0, remaining = size;
|
||||
|
||||
while (remaining > 0 && cluster >= 2) {
|
||||
// Build a contiguous run (only when cluster-aligned to avoid a
|
||||
// partial read-modify-write spanning multiple clusters).
|
||||
uint32_t runStart = cluster;
|
||||
uint32_t runSectors = v->SectorsPerCluster;
|
||||
uint32_t next = FatEntry(v, cluster);
|
||||
|
||||
if (offsetInCluster == 0) {
|
||||
while (next == cluster + 1 && FatIsDataCluster(v, next)
|
||||
&& runSectors + v->SectorsPerCluster <= FAT_WRITE_RUN_MAX_SECTORS) {
|
||||
cluster = next;
|
||||
next = FatEntry(v, cluster);
|
||||
runSectors += v->SectorsPerCluster;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - offsetInCluster;
|
||||
size_t copy = avail < remaining ? avail : remaining;
|
||||
|
||||
// Read existing data only for partial-cluster writes.
|
||||
bool partial = (offsetInCluster > 0) || (copy < (size_t)runBytes);
|
||||
if (partial) {
|
||||
if (!KSUCCESS(FatReadSectors(v, FatClusterToLba(v, runStart), runSectors, runBuf)))
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(runBuf + offsetInCluster, src, copy);
|
||||
|
||||
if (!KSUCCESS(FatWriteSectors(v, FatClusterToLba(v, runStart), runSectors, runBuf)))
|
||||
break;
|
||||
|
||||
src += copy;
|
||||
written += copy;
|
||||
remaining -= copy;
|
||||
offsetInCluster = 0;
|
||||
|
||||
if (remaining > 0) {
|
||||
// Advance: if end-of-chain, append a new cluster.
|
||||
cluster = next;
|
||||
if (!FatIsDataCluster(v, cluster)) {
|
||||
// Find the actual last cluster in the run we just wrote.
|
||||
uint32_t last = runStart;
|
||||
for (uint32_t tmp = FatEntry(v, last);
|
||||
FatIsDataCluster(v, tmp);
|
||||
tmp = FatEntry(v, last))
|
||||
last = tmp;
|
||||
cluster = FatAppendCluster(v, last);
|
||||
if (!cluster) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MmFreePool(runBuf);
|
||||
|
||||
uint64_t newEnd = offset + written;
|
||||
if (written > 0) {
|
||||
FAT_DIRENT_UPDATE u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
FatQueryEncodedTime(&u.Timestamp);
|
||||
u.Flags = FAT_DIRENT_UPDATE_WRITE_TIME;
|
||||
if (newEnd > f->FileSize) {
|
||||
f->FileSize = (uint32_t)newEnd;
|
||||
u.Flags |= FAT_DIRENT_UPDATE_SIZE;
|
||||
u.Size = f->FileSize;
|
||||
}
|
||||
KSTATUS ms = FatDirentUpdateMetadata(v, f->DirentLba, f->DirentOffset, &u);
|
||||
if (KSUCCESS(ms))
|
||||
FatPathCacheInvalidate(v);
|
||||
}
|
||||
|
||||
*bytesWritten = written;
|
||||
return (written == size) ? NTLSTATUS_SUCCESS : NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Create.
|
||||
// Creates the requested file and fails if the name already exists.
|
||||
KSTATUS FatCreate(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = FatCreateDirent(v, path, ATTR_ARCHIVE, 0, 0,
|
||||
&direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)MmAllocatePool(sizeof(FAT_FILE));
|
||||
if (!f) return NTLSTATUS_NO_MEMORY;
|
||||
f->StartCluster = 0;
|
||||
f->FileSize = 0;
|
||||
f->Vol = v;
|
||||
f->DirentLba = direntLba;
|
||||
f->DirentOffset = direntOffset;
|
||||
FatFileInitChainCache(f);
|
||||
*fileCtx = f;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Creates a directory and initializes its dot entries.
|
||||
KSTATUS FatCreateDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
KSTATUS s = FatResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
(void)filenameView;
|
||||
|
||||
uint32_t dirCluster = FatAllocCluster(v);
|
||||
if (!dirCluster) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
s = FatInitializeDirectoryCluster(v, dirCluster, parentCluster);
|
||||
if (!KSUCCESS(s)) {
|
||||
FatFreeClusterChain(v, dirCluster);
|
||||
FatFlushCache(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
FatFlushCache(v);
|
||||
|
||||
s = FatCreateDirent(v, path, ATTR_DIRECTORY, dirCluster, 0, NULL, NULL);
|
||||
if (!KSUCCESS(s)) {
|
||||
FatFreeClusterChain(v, dirCluster);
|
||||
FatFlushCache(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
FatFlushCache(v);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Removes an empty directory.
|
||||
KSTATUS FatRemoveDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = 0;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, &attr,
|
||||
&direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!direntLba) return NTLSTATUS_ACCESS_DENIED;
|
||||
if (!(attr & ATTR_DIRECTORY)) return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
int empty = 0;
|
||||
s = FatDirectoryIsEmpty(v, cluster, &empty);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!empty) return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
s = FatResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
s = FatDeleteDirentRunV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
if (cluster >= 2) FatFreeClusterChain(v, cluster);
|
||||
FatFlushCache(v);
|
||||
FatPathCacheInvalidate(v);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Truncate.
|
||||
KSTATUS FatTruncate(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t newSize, uint32_t fileFlags) {
|
||||
(void)fs;
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
FatFileInvalidateChainCache(f);
|
||||
int zeroNewClusters = (fileFlags & IO_FILE_FLAG_SKIP_ZERO_EXTEND) ? 0 : 1;
|
||||
if ((uint32_t)newSize == f->FileSize) {
|
||||
FAT_DIRENT_UPDATE u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
FatQueryEncodedTime(&u.Timestamp);
|
||||
u.Flags = FAT_DIRENT_UPDATE_WRITE_TIME;
|
||||
KSTATUS s = FatDirentUpdateMetadata(v, f->DirentLba, f->DirentOffset, &u);
|
||||
if (KSUCCESS(s)) FatPathCacheInvalidate(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (newSize == 0) {
|
||||
if (f->StartCluster >= 2) {
|
||||
FatFreeClusterChain(v, f->StartCluster);
|
||||
f->StartCluster = 0;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, 0);
|
||||
}
|
||||
f->FileSize = 0;
|
||||
KSTATUS s = FatDirentUpdateSize(v, f->DirentLba, f->DirentOffset, 0);
|
||||
FatFlushCache(v);
|
||||
if (KSUCCESS(s)) FatPathCacheInvalidate(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
uint32_t newClusters = (uint32_t)
|
||||
((newSize + v->BytesPerCluster - 1) / v->BytesPerCluster);
|
||||
|
||||
if (newSize < f->FileSize) {
|
||||
if (f->StartCluster < 2) goto update_size;
|
||||
uint32_t cluster = f->StartCluster, lastGood = cluster;
|
||||
for (uint32_t i = 1; i < newClusters && FatIsDataCluster(v, cluster); i++) {
|
||||
lastGood = cluster; cluster = FatEntry(v, cluster);
|
||||
}
|
||||
uint32_t tail = FatEntry(v, lastGood);
|
||||
FatWriteEntry(v, lastGood, FatEocMark(v));
|
||||
FatFreeClusterChain(v, tail);
|
||||
} else {
|
||||
uint32_t curClusters = f->FileSize
|
||||
? (f->FileSize + v->BytesPerCluster - 1) / v->BytesPerCluster : 0;
|
||||
uint32_t lastCluster = 0;
|
||||
if (f->StartCluster < 2) {
|
||||
uint32_t first = FatAllocClusterEx(v, zeroNewClusters);
|
||||
if (!first) return NTLSTATUS_NO_MEMORY;
|
||||
f->StartCluster = first;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, first);
|
||||
lastCluster = first; curClusters = 1;
|
||||
} else {
|
||||
lastCluster = FatGetChainEnd(v, f->StartCluster, NULL);
|
||||
}
|
||||
for (uint32_t i = curClusters; i < newClusters; i++) {
|
||||
uint32_t next = FatAppendClusterEx(v, lastCluster, zeroNewClusters);
|
||||
if (!next) return NTLSTATUS_NO_MEMORY;
|
||||
lastCluster = next;
|
||||
}
|
||||
}
|
||||
|
||||
update_size:
|
||||
f->FileSize = (uint32_t)newSize;
|
||||
{ KSTATUS s = FatDirentUpdateSize(v, f->DirentLba, f->DirentOffset, (uint32_t)newSize);
|
||||
FatFlushCache(v);
|
||||
if (KSUCCESS(s)) FatPathCacheInvalidate(v);
|
||||
return s; }
|
||||
}
|
||||
|
||||
// Delete.
|
||||
KSTATUS FatDelete(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!direntLba) return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
s = FatResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
// Delete the complete entry run so long-name metadata cannot survive the
|
||||
// short entry and keep writable vvfat mirrors visibly present.
|
||||
s = FatDeleteDirentRunV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
if (cluster >= 2) FatFreeClusterChain(v, cluster);
|
||||
FatFlushCache(v);
|
||||
FatPathCacheInvalidate(v);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns size.
|
||||
uint64_t FatGetSize(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs; return ((FAT_FILE*)fileCtx)->FileSize;
|
||||
}
|
||||
|
||||
// Converts FAT attributes to the public file type.
|
||||
static uint32_t FatFileTypeFromAttr(uint8_t attr) {
|
||||
return (attr & ATTR_DIRECTORY) ? SYS_FILE_TYPE_DIRECTORY : SYS_FILE_TYPE_REGULAR;
|
||||
}
|
||||
|
||||
// Fills public metadata from one FAT directory entry.
|
||||
static void FatFillStatFromDirent(FAT_VOL* v, const DIRENT* d,
|
||||
uint64_t direntLba, uint32_t direntOffset,
|
||||
SYS_FILE_STAT* out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = FatFileTypeFromAttr(d->Attr);
|
||||
out->Attributes = d->Attr;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = d->FileSize;
|
||||
|
||||
uint32_t firstCluster = FatDirentFirstCluster(v, d);
|
||||
out->FileId = direntLba ^
|
||||
((uint64_t)direntOffset << 32) ^
|
||||
((uint64_t)firstCluster << 1);
|
||||
|
||||
FatDecodeDateTime(d->CrtDate, d->CrtTime, d->CrtTimeTenth, &out->CreationTime);
|
||||
FatDecodeAccessDate(d->LastAccDate, &out->LastAccessTime);
|
||||
FatDecodeDateTime(d->WrtDate, d->WrtTime, 0, &out->LastWriteTime);
|
||||
out->ChangeTime = out->LastWriteTime;
|
||||
}
|
||||
|
||||
// Fills public metadata for the filesystem root.
|
||||
static void FatFillRootStat(FAT_VOL* v, SYS_FILE_STAT* out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = SYS_FILE_TYPE_DIRECTORY;
|
||||
out->Attributes = SYS_FILE_ATTR_DIRECTORY;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = 0;
|
||||
out->FileId = ((uint64_t)v->RootCluster << 1) ^ 0x524f4f54ULL;
|
||||
}
|
||||
|
||||
// Queries metadata for an open FAT file.
|
||||
KSTATUS FatQueryInfo(IO_VOLUME_PROVIDER* fs, void* fileCtx, SYS_FILE_STAT* out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (!v) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
if (f->DirentLba == 0) {
|
||||
if (f->StartCluster == v->RootCluster && f->FileSize == 0) {
|
||||
FatFillRootStat(v, out);
|
||||
} else {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = SYS_FILE_TYPE_REGULAR;
|
||||
out->Attributes = SYS_FILE_ATTR_ARCHIVE;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = f->FileSize;
|
||||
out->FileId = ((uint64_t)f->StartCluster << 1);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t* sec = (uint8_t*)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
KSTATUS s = FatReadSectors(v, f->DirentLba, 1, sec);
|
||||
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
|
||||
|
||||
DIRENT* d = (DIRENT*)(sec + f->DirentOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
FatFillStatFromDirent(v, d, f->DirentLba, f->DirentOffset, out);
|
||||
f->StartCluster = FatDirentFirstCluster(v, d);
|
||||
f->FileSize = d->FileSize;
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Queries metadata for a FAT path.
|
||||
KSTATUS FatStat(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, SYS_FILE_STAT* out) {
|
||||
if (!fs || !out || !SysSvIsValid(path)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
if (!v) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t cluster = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = ATTR_DIRECTORY;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, &attr, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
(void)cluster;
|
||||
|
||||
if (direntLba == 0) {
|
||||
FatFillRootStat(v, out);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t* sec = (uint8_t*)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
s = FatReadSectors(v, direntLba, 1, sec);
|
||||
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
|
||||
|
||||
DIRENT* d = (DIRENT*)(sec + direntOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
FatFillStatFromDirent(v, d, direntLba, direntOffset, out);
|
||||
out->Type = FatFileTypeFromAttr(attr);
|
||||
out->FileSize = size;
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Mixes FAT directory metadata into a per-file content generation.
|
||||
static uint64_t FatDirentGeneration(FAT_VOL* v, const DIRENT* d) {
|
||||
if (!d)
|
||||
return 0;
|
||||
|
||||
uint32_t firstCluster = FatDirentFirstCluster(v, d);
|
||||
uint64_t generation = ((uint64_t)d->WrtDate << 48) |
|
||||
((uint64_t)d->WrtTime << 32) |
|
||||
((uint64_t)d->LastAccDate << 16) |
|
||||
(uint64_t)d->Attr;
|
||||
generation ^= ((uint64_t)d->CrtDate << 40);
|
||||
generation ^= ((uint64_t)d->CrtTime << 24);
|
||||
generation ^= ((uint64_t)d->CrtTimeTenth << 16);
|
||||
generation ^= ((uint64_t)firstCluster << 1);
|
||||
generation ^= ((uint64_t)d->FileSize << 17);
|
||||
return generation ? generation : 1;
|
||||
}
|
||||
|
||||
// Returns a stable FAT file identity for cross-open image-section caches.
|
||||
KSTATUS FatQueryIdentity(IO_VOLUME_PROVIDER* fs, void* fileCtx, IO_FILE_IDENTITY* out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (!v)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t startCluster = f->StartCluster;
|
||||
uint32_t fileSize = f->FileSize;
|
||||
uint64_t generation = ((uint64_t)startCluster << 1) ^
|
||||
((uint64_t)fileSize << 17);
|
||||
|
||||
if (f->DirentLba != 0) {
|
||||
uint8_t* sec = (uint8_t*)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
KSTATUS s = FatReadSectors(v, f->DirentLba, 1, sec);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreePool(sec);
|
||||
return s;
|
||||
}
|
||||
|
||||
DIRENT* d = (DIRENT*)(sec + f->DirentOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
startCluster = FatDirentFirstCluster(v, d);
|
||||
fileSize = d->FileSize;
|
||||
generation = FatDirentGeneration(v, d);
|
||||
MmFreePool(sec);
|
||||
}
|
||||
|
||||
out->FileId = f->DirentLba ^
|
||||
((uint64_t)f->DirentOffset << 32) ^
|
||||
((uint64_t)startCluster << 1);
|
||||
out->Generation = generation ? generation : 1;
|
||||
out->Size = fileSize;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Directory enumeration (fix #2: cluster buffer cached in context)
|
||||
// Previously: allocate + FatReadSectors + return 1 entry + free, every call.
|
||||
// Now: buffer lives in FAT_DIR_CTX; FatReadSectors fires only on cluster
|
||||
// boundaries.
|
||||
|
||||
typedef struct {
|
||||
FAT_VOL* Vol;
|
||||
uint32_t DirCluster;
|
||||
uint32_t Cursor;
|
||||
uint32_t LoadedCursor;
|
||||
uint32_t LoadedSectors;
|
||||
uint64_t LoadedLba;
|
||||
uint32_t EntryIndex;
|
||||
char LfnBuf[FAT_MAX_LFN];
|
||||
int HasLfn;
|
||||
uint8_t* ClusterBuf; // allocated once in OpenDir, freed in CloseDir
|
||||
} FAT_DIR_CTX;
|
||||
|
||||
// Opens dir.
|
||||
KSTATUS FatOpenDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW dirPath, void** dirCtx) {
|
||||
if (!fs || !dirCtx || !SysSvIsValid(dirPath))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster;
|
||||
if (dirPath.Length == 0) {
|
||||
cluster = v->RootCluster;
|
||||
} else {
|
||||
uint32_t size;
|
||||
KSTATUS s = FatTraverseV(v, dirPath, &cluster, &size);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
}
|
||||
|
||||
uint32_t cursor; uint64_t lba; uint32_t sectors;
|
||||
if (!FatDirectoryFirstSegment(v, cluster, &cursor, &lba, §ors))
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
|
||||
FAT_DIR_CTX* ctx = (FAT_DIR_CTX*)MmAllocatePool(sizeof(FAT_DIR_CTX));
|
||||
if (!ctx) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
ctx->ClusterBuf = (uint8_t*)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!ctx->ClusterBuf) { MmFreePool(ctx); return NTLSTATUS_NO_MEMORY; }
|
||||
|
||||
ctx->Vol = v;
|
||||
ctx->DirCluster = cluster;
|
||||
ctx->Cursor = cursor;
|
||||
ctx->LoadedCursor = (uint32_t)-1;
|
||||
ctx->LoadedSectors = sectors;
|
||||
ctx->LoadedLba = lba;
|
||||
ctx->EntryIndex = 0;
|
||||
ctx->HasLfn = 0;
|
||||
memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf));
|
||||
*dirCtx = ctx;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns dir.
|
||||
KSTATUS FatReadDir(IO_VOLUME_PROVIDER* fs, void* dirCtx, KDIR_ENTRY* out) {
|
||||
(void)fs;
|
||||
FAT_DIR_CTX* ctx = (FAT_DIR_CTX*)dirCtx;
|
||||
FAT_VOL* v = ctx->Vol;
|
||||
|
||||
while (true) {
|
||||
if (ctx->Cursor != ctx->LoadedCursor) {
|
||||
if (!KSUCCESS(FatReadSectors(v, ctx->LoadedLba, ctx->LoadedSectors, ctx->ClusterBuf)))
|
||||
return NTLSTATUS_NO_MORE_FILES;
|
||||
ctx->LoadedCursor = ctx->Cursor;
|
||||
}
|
||||
|
||||
uint32_t epc = (ctx->LoadedSectors * v->BytesPerSector) / sizeof(DIRENT);
|
||||
DIRENT* dir = (DIRENT*)ctx->ClusterBuf;
|
||||
while (ctx->EntryIndex < epc) {
|
||||
DIRENT* e = &dir[ctx->EntryIndex++];
|
||||
|
||||
if (e->Name[0] == 0x00) return NTLSTATUS_NO_MORE_FILES;
|
||||
if ((uint8_t)e->Name[0] == 0xE5) { ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf)); continue; }
|
||||
if (e->Attr == ATTR_LFN) { FatLfnAppend(ctx->LfnBuf, (LFN_ENTRY*)e); ctx->HasLfn = 1; continue; }
|
||||
if (e->Attr & 0x08 && !(e->Attr & ATTR_DIRECTORY)) { ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf)); continue; }
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (ctx->HasLfn && ctx->LfnBuf[0]) {
|
||||
size_t len = strlen(ctx->LfnBuf);
|
||||
if (len >= KDIR_NAME_MAX) len = KDIR_NAME_MAX - 1;
|
||||
memcpy(out->Name, ctx->LfnBuf, len); out->Name[len] = '\0';
|
||||
} else {
|
||||
char name83[13]; int n = 0;
|
||||
for (int i = 0; i < 8 && e->Name[i] != ' '; i++)
|
||||
name83[n++] = (char)(e->Name[i] >= 'A' && e->Name[i] <= 'Z' ? e->Name[i]+32 : e->Name[i]);
|
||||
if (e->Ext[0] != ' ') {
|
||||
name83[n++] = '.';
|
||||
for (int i = 0; i < 3 && e->Ext[i] != ' '; i++)
|
||||
name83[n++] = (char)(e->Ext[i] >= 'A' && e->Ext[i] <= 'Z' ? e->Ext[i]+32 : e->Ext[i]);
|
||||
}
|
||||
name83[n] = '\0';
|
||||
size_t len = (size_t)n; if (len >= KDIR_NAME_MAX) len = KDIR_NAME_MAX - 1;
|
||||
memcpy(out->Name, name83, len); out->Name[len] = '\0';
|
||||
}
|
||||
|
||||
out->Attr = e->Attr;
|
||||
out->FileSize = e->FileSize;
|
||||
FatDecodeDateTime(e->CrtDate, e->CrtTime, e->CrtTimeTenth, &out->CreateTime);
|
||||
FatDecodeAccessDate(e->LastAccDate, &out->LastAccessTime);
|
||||
FatDecodeDateTime(e->WrtDate, e->WrtTime, 0, &out->LastWriteTime);
|
||||
ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf));
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!FatDirectoryNextSegment(v, ctx->DirCluster, &ctx->Cursor,
|
||||
&ctx->LoadedLba, &ctx->LoadedSectors))
|
||||
return NTLSTATUS_NO_MORE_FILES;
|
||||
ctx->LoadedCursor = (uint32_t)-1;
|
||||
ctx->EntryIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Releases dir.
|
||||
void FatCloseDir(IO_VOLUME_PROVIDER* fs, void* dirCtx) {
|
||||
(void)fs;
|
||||
FAT_DIR_CTX* ctx = (FAT_DIR_CTX*)dirCtx;
|
||||
MmFreePool(ctx->ClusterBuf);
|
||||
MmFreePool(ctx);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// FAT private implementation model shared by the driver's translation units.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fat.h"
|
||||
#include "fat_disk.h"
|
||||
|
||||
#define FAT_PATH_CACHE_SLOTS 64u
|
||||
#define FAT_PATH_CACHE_MAX 512u
|
||||
#define FAT_CHAIN_CACHE_SLOTS 16u
|
||||
#define FAT_WRITE_RUN_MAX_SECTORS 128u
|
||||
#define FAT_READ_RUN_MAX_SECTORS 512u
|
||||
#define FAT_MAX_LFN 256
|
||||
|
||||
#ifndef FAT_DEBUG
|
||||
#define FAT_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define FatDebugPrint(...) do { if (FAT_DEBUG) DbgPrint(__VA_ARGS__); } while (0)
|
||||
|
||||
typedef struct FAT_PATH_CACHE_ENTRY {
|
||||
bool Valid;
|
||||
uint64_t Generation;
|
||||
uint32_t Hash;
|
||||
size_t PathLength;
|
||||
char Path[FAT_PATH_CACHE_MAX];
|
||||
uint32_t Cluster;
|
||||
uint32_t Size;
|
||||
uint8_t Attr;
|
||||
uint64_t DirentLba;
|
||||
uint32_t DirentOffset;
|
||||
} FAT_PATH_CACHE_ENTRY;
|
||||
|
||||
typedef struct FAT_CHAIN_CACHE_ENTRY {
|
||||
bool Valid;
|
||||
uint32_t LogicalStart;
|
||||
uint32_t PhysicalStart;
|
||||
uint32_t Length;
|
||||
uint32_t NextCluster;
|
||||
} FAT_CHAIN_CACHE_ENTRY;
|
||||
|
||||
struct FAT_VOL_S {
|
||||
FAT_VARIANT Variant;
|
||||
uint8_t DriveIndex;
|
||||
uint64_t PartitionLba;
|
||||
uint64_t PartitionSize;
|
||||
uint32_t BytesPerSector;
|
||||
uint32_t SectorsPerCluster;
|
||||
uint32_t BytesPerCluster;
|
||||
uint32_t ReservedSectors;
|
||||
uint32_t NumFats;
|
||||
uint32_t FatSize;
|
||||
uint32_t RootCluster;
|
||||
uint32_t RootEntryCount;
|
||||
uint32_t RootDirSectors;
|
||||
uint64_t RootDirStart;
|
||||
uint64_t DataStart;
|
||||
uint32_t TotalClusters;
|
||||
uint32_t NextFreeHint;
|
||||
SPINLOCK PathCacheLock;
|
||||
uint64_t PathCacheGeneration;
|
||||
IO_FILESYSTEM_STATS Stats;
|
||||
FAT_PATH_CACHE_ENTRY PathCache[FAT_PATH_CACHE_SLOTS];
|
||||
IO_VOLUME* Volume;
|
||||
LIST_ENTRY MountLink;
|
||||
};
|
||||
|
||||
typedef struct FAT_FILE {
|
||||
uint32_t StartCluster;
|
||||
uint32_t FileSize;
|
||||
FAT_VOL* Vol;
|
||||
uint64_t DirentLba;
|
||||
uint32_t DirentOffset;
|
||||
SPINLOCK ChainCacheLock;
|
||||
uint32_t ChainCacheClock;
|
||||
FAT_CHAIN_CACHE_ENTRY ChainCache[FAT_CHAIN_CACHE_SLOTS];
|
||||
} FAT_FILE;
|
||||
|
||||
typedef struct FAT_ENCODED_TIME {
|
||||
uint16_t Date;
|
||||
uint16_t Time;
|
||||
uint8_t CreateTimeTenth;
|
||||
} FAT_ENCODED_TIME;
|
||||
|
||||
typedef struct FAT_DIRENT_UPDATE {
|
||||
uint32_t Flags;
|
||||
uint32_t Size;
|
||||
uint32_t FirstCluster;
|
||||
FAT_ENCODED_TIME Timestamp;
|
||||
} FAT_DIRENT_UPDATE;
|
||||
|
||||
#define FAT_DIRENT_UPDATE_SIZE 0x00000001u
|
||||
#define FAT_DIRENT_UPDATE_FIRST_CLUSTER 0x00000002u
|
||||
#define FAT_DIRENT_UPDATE_CREATE_TIME 0x00000004u
|
||||
#define FAT_DIRENT_UPDATE_WRITE_TIME 0x00000008u
|
||||
#define FAT_DIRENT_UPDATE_ACCESS_DATE 0x00000010u
|
||||
|
||||
KSTATUS FatReadSectors(FAT_VOL* volume, uint64_t lba, uint32_t count, void* buffer);
|
||||
KSTATUS FatWriteSectors(FAT_VOL* volume, uint64_t lba, uint32_t count, const void* buffer);
|
||||
const char* FatVariantName(FAT_VARIANT variant);
|
||||
uint32_t FatEocMark(FAT_VOL* volume);
|
||||
int FatIsDataCluster(FAT_VOL* volume, uint32_t cluster);
|
||||
uint32_t FatDirentFirstCluster(FAT_VOL* volume, const DIRENT* dirent);
|
||||
void FatSetDirentFirstCluster(FAT_VOL* volume, DIRENT* dirent, uint32_t cluster);
|
||||
int FatIsFixedRoot(FAT_VOL* volume, uint32_t dirCluster);
|
||||
int FatDirectoryFirstSegment(FAT_VOL* volume, uint32_t dirCluster,
|
||||
uint32_t* cursorOut, uint64_t* lbaOut, uint32_t* sectorsOut);
|
||||
int FatDirectoryNextSegment(FAT_VOL* volume, uint32_t dirCluster,
|
||||
uint32_t* cursorInOut, uint64_t* lbaOut, uint32_t* sectorsOut);
|
||||
uint64_t FatClusterToLba(FAT_VOL* volume, uint32_t cluster);
|
||||
uint32_t FatEntry(FAT_VOL* volume, uint32_t cluster);
|
||||
KSTATUS FatWriteEntry(FAT_VOL* volume, uint32_t cluster, uint32_t value);
|
||||
void FatFlushCache(FAT_VOL* volume);
|
||||
uint32_t FatAllocClusterEx(FAT_VOL* volume, int zeroCluster);
|
||||
uint32_t FatAllocCluster(FAT_VOL* volume);
|
||||
uint32_t FatAppendClusterEx(FAT_VOL* volume, uint32_t lastCluster, int zeroCluster);
|
||||
uint32_t FatAppendCluster(FAT_VOL* volume, uint32_t lastCluster);
|
||||
uint32_t FatGetChainEnd(FAT_VOL* volume, uint32_t start, uint32_t* countOut);
|
||||
void FatFreeClusterChain(FAT_VOL* volume, uint32_t start);
|
||||
void FatQueryEncodedTime(FAT_ENCODED_TIME* out);
|
||||
void FatDecodeDateTime(uint16_t date, uint16_t time, uint8_t createTenth, SYS_SYSTEM_TIME* out);
|
||||
void FatDecodeAccessDate(uint16_t date, SYS_SYSTEM_TIME* out);
|
||||
KSTATUS FatDirentUpdateMetadata(FAT_VOL* volume, uint64_t lba, uint32_t offset,
|
||||
const FAT_DIRENT_UPDATE* update);
|
||||
KSTATUS FatDirentUpdateSize(FAT_VOL* volume, uint64_t lba, uint32_t offset, uint32_t size);
|
||||
KSTATUS FatDirentUpdateFirstCluster(FAT_VOL* volume, uint64_t lba, uint32_t offset, uint32_t cluster);
|
||||
void FatLfnAppend(char* lfn, const LFN_ENTRY* entry);
|
||||
void FatPathCacheInvalidate(FAT_VOL* volume);
|
||||
void FatStatsAddReadRun(FAT_VOL* volume, uint32_t sectors, uint32_t merges);
|
||||
void FatFileInitChainCache(FAT_FILE* file);
|
||||
void FatFileInvalidateChainCache(FAT_FILE* file);
|
||||
uint32_t FatResolveFileCluster(FAT_FILE* file, uint32_t logicalIndex);
|
||||
bool FatBuildReadRun(FAT_FILE* file, uint32_t logicalIndex,
|
||||
uint32_t startCluster, uint32_t maxSectors,
|
||||
uint32_t* runSectorsOut, uint32_t* nextClusterOut);
|
||||
KSTATUS FatLookupPathV(FAT_VOL* volume, SYS_STRING_VIEW path,
|
||||
uint32_t* clusterOut, uint32_t* sizeOut, uint8_t* attrOut,
|
||||
uint64_t* direntLbaOut, uint32_t* direntOffsetOut);
|
||||
KSTATUS FatTraverseV(FAT_VOL* volume, SYS_STRING_VIEW path,
|
||||
uint32_t* clusterOut, uint32_t* sizeOut);
|
||||
KSTATUS FatResolveParentForMutation(FAT_VOL* volume, SYS_STRING_VIEW path,
|
||||
uint32_t* parentClusterOut, SYS_STRING_VIEW* nameOut);
|
||||
KSTATUS FatCreateDirent(FAT_VOL* volume, SYS_STRING_VIEW path,
|
||||
uint8_t attr, uint32_t firstCluster, uint32_t size,
|
||||
uint64_t* direntLbaOut, uint32_t* direntOffsetOut);
|
||||
KSTATUS FatInitializeDirectoryCluster(FAT_VOL* volume, uint32_t dirCluster,
|
||||
uint32_t parentCluster);
|
||||
KSTATUS FatDirectoryIsEmpty(FAT_VOL* volume, uint32_t dirCluster, int* emptyOut);
|
||||
KSTATUS FatDeleteDirentRunV(FAT_VOL* volume, uint32_t parentCluster,
|
||||
SYS_STRING_VIEW name);
|
||||
void FatCacheInvalidate(void);
|
||||
void FatCacheInvalidateVolume(FAT_VOL* volume);
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
#ifndef NDEBUG
|
||||
#define assert(expr) ((expr) ? (void)0 : abort())
|
||||
#else
|
||||
#define assert(expr) ((void)0)
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
/* Minimal autoconf surface for the NTLite libntfs-3g port. */
|
||||
#define PACKAGE "ntfs-3g"
|
||||
#define PACKAGE_NAME "ntfs-3g"
|
||||
#define PACKAGE_VERSION "2026.7.7"
|
||||
#define VERSION "2026.7.7"
|
||||
|
||||
#define HAVE_STDINT_H 1
|
||||
#define HAVE_STDDEF_H 1
|
||||
#define HAVE_CTYPE_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_STDIO_H 1
|
||||
#define HAVE_STDARG_H 1
|
||||
#define HAVE_ERRNO_H 1
|
||||
#define HAVE_STRING_H 1
|
||||
#define HAVE_LIMITS_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_LOCALE_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
#define HAVE_CONFIG_H 1
|
||||
#define NO_NTFS_DEVICE_DEFAULT_IO_OPS 1
|
||||
#define DISABLE_PLUGINS 1
|
||||
#define CACHE_INODE_SIZE 0
|
||||
#define CACHE_NIDATA_SIZE 0
|
||||
#define CACHE_LOOKUP_SIZE 0
|
||||
#define CACHE_SECURID_SIZE 16
|
||||
#define CACHE_LEGACY_SIZE 8
|
||||
|
||||
/* Keep the first port deliberately small. */
|
||||
#undef POSIXACLS
|
||||
#undef XATTR_MAPPINGS
|
||||
#undef ENABLE_NFCONV
|
||||
#undef HAVE_LIBGCRYPT
|
||||
#undef HAVE_OPENSSL
|
||||
#undef HAVE_ICONV
|
||||
#undef HAVE_LANGINFO_H
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#error This config is for the NTLite MSVC build only.
|
||||
#endif
|
||||
|
||||
|
||||
/* MSVC syntax compatibility. Keep this scoped to NTFS-3G through config.h;
|
||||
* do not force-include it into unrelated translation units. Packed on-disk
|
||||
* headers are guarded with #pragma pack in the vendored MSVC port. */
|
||||
#ifdef _MSC_VER
|
||||
#ifndef __attribute__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
#ifndef __inline__
|
||||
#define __inline__ __inline
|
||||
#endif
|
||||
#ifndef __restrict__
|
||||
#define __restrict__ __restrict
|
||||
#endif
|
||||
#ifndef __func__
|
||||
#define __func__ __FUNCTION__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
/* MSVC provides the standard headers; fill only POSIX gaps it does not. */
|
||||
#ifndef O_ACCMODE
|
||||
#define O_ACCMODE 0x0003
|
||||
#endif
|
||||
#ifndef LITTLE_ENDIAN
|
||||
#define LITTLE_ENDIAN 1234
|
||||
#define BIG_ENDIAN 4321
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
static __inline int isascii(int c) { return (unsigned)c <= 0x7fU; }
|
||||
static __inline int isdigit(int c) { return c >= '0' && c <= '9'; }
|
||||
static __inline int isxdigit(int c) {
|
||||
return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
static __inline int islower(int c) { return c >= 'a' && c <= 'z'; }
|
||||
static __inline int isupper(int c) { return c >= 'A' && c <= 'Z'; }
|
||||
static __inline int isalpha(int c) { return islower(c) || isupper(c); }
|
||||
static __inline int isalnum(int c) { return isalpha(c) || isdigit(c); }
|
||||
static __inline int isspace(int c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
||||
}
|
||||
static __inline int isblank(int c) { return c == ' ' || c == '\t'; }
|
||||
static __inline int iscntrl(int c) { return ((unsigned)c < 0x20U) || c == 0x7f; }
|
||||
static __inline int isprint(int c) { return (unsigned)(c - 0x20) < 0x5fU; }
|
||||
static __inline int isgraph(int c) { return (unsigned)(c - 0x21) < 0x5eU; }
|
||||
static __inline int ispunct(int c) { return isgraph(c) && !isalnum(c); }
|
||||
static __inline int tolower(int c) { return isupper(c) ? c + ('a' - 'A') : c; }
|
||||
static __inline int toupper(int c) { return islower(c) ? c - ('a' - 'A') : c; }
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
struct group { char* gr_name; gid_t gr_gid; char** gr_mem; };
|
||||
struct group* getgrnam(const char* name);
|
||||
struct group* getgrgid(gid_t gid);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
struct passwd { char* pw_name; uid_t pw_uid; gid_t pw_gid; char* pw_dir; char* pw_shell; };
|
||||
struct passwd* getpwnam(const char* name);
|
||||
struct passwd* getpwuid(uid_t uid);
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <common/printf.h>
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
typedef struct NTL_NTFS3G_STDIO_FILE { int unused; } FILE;
|
||||
extern FILE* stderr;
|
||||
extern FILE* stdout;
|
||||
extern FILE* stdin;
|
||||
|
||||
int fprintf(FILE* stream, const char* format, ...);
|
||||
int vfprintf(FILE* stream, const char* format, va_list args);
|
||||
int fputs(const char* text, FILE* stream);
|
||||
int puts(const char* text);
|
||||
|
||||
int fflush(FILE* stream);
|
||||
int snprintf(char* buffer, size_t size, const char* format, ...);
|
||||
int vsnprintf(char* buffer, size_t size, const char* format, va_list args);
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void* malloc(size_t size);
|
||||
void* calloc(size_t count, size_t size);
|
||||
void* realloc(void* ptr, size_t size);
|
||||
void free(void* ptr);
|
||||
char* strdup(const char* text);
|
||||
char* strndup(const char* text, size_t limit);
|
||||
void qsort(void* base, size_t count, size_t size,
|
||||
int (*compare)(const void*, const void*));
|
||||
void* bsearch(const void* key, const void* base, size_t count, size_t size,
|
||||
int (*compare)(const void*, const void*));
|
||||
int abs(int value);
|
||||
long labs(long value);
|
||||
long long llabs(long long value);
|
||||
long strtol(const char* text, char** end, int base);
|
||||
unsigned long strtoul(const char* text, char** end, int base);
|
||||
long long strtoll(const char* text, char** end, int base);
|
||||
unsigned long long strtoull(const char* text, char** end, int base);
|
||||
char* getenv(const char* name);
|
||||
void abort(void);
|
||||
|
||||
int ffs(int value);
|
||||
void srandom(unsigned int seed);
|
||||
long random(void);
|
||||
|
||||
int atoi(const char* text);
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
int strcasecmp(const char* a, const char* b);
|
||||
int strncasecmp(const char* a, const char* b, size_t count);
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#define S_IFMT 0170000
|
||||
#define S_IFREG 0100000
|
||||
#define S_IFDIR 0040000
|
||||
#define S_IFBLK 0060000
|
||||
#define S_IFCHR 0020000
|
||||
#define S_IFLNK 0120000
|
||||
#define S_IFIFO 0010000
|
||||
#define S_IFSOCK 0140000
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
|
||||
struct stat {
|
||||
dev_t st_dev;
|
||||
uint64_t st_ino;
|
||||
mode_t st_mode;
|
||||
uint32_t st_nlink;
|
||||
uid_t st_uid;
|
||||
gid_t st_gid;
|
||||
dev_t st_rdev;
|
||||
off_t st_size;
|
||||
blksize_t st_blksize;
|
||||
blkcnt_t st_blocks;
|
||||
time_t st_atime;
|
||||
time_t st_mtime;
|
||||
time_t st_ctime;
|
||||
};
|
||||
|
||||
#define S_IRUSR 0400
|
||||
#define S_IWUSR 0200
|
||||
#define S_IXUSR 0100
|
||||
#define S_IRGRP 0040
|
||||
#define S_IWGRP 0020
|
||||
#define S_IXGRP 0010
|
||||
#define S_IROTH 0004
|
||||
#define S_IWOTH 0002
|
||||
#define S_IXOTH 0001
|
||||
#define S_IRWXU 0700
|
||||
#define S_IRWXG 0070
|
||||
#define S_IRWXO 0007
|
||||
#define S_ISUID 04000
|
||||
#define S_ISGID 02000
|
||||
#define S_ISVTX 01000
|
||||
#define S_IEXEC (S_IXUSR | S_IXGRP | S_IXOTH)
|
||||
|
||||
#define S_IREAD S_IRUSR
|
||||
#define S_IWRITE S_IWUSR
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef int64_t off_t;
|
||||
typedef int64_t ssize_t;
|
||||
typedef uint32_t mode_t;
|
||||
typedef uint32_t uid_t;
|
||||
typedef uint32_t gid_t;
|
||||
typedef uint64_t dev_t;
|
||||
typedef int64_t blkcnt_t;
|
||||
typedef int64_t blksize_t;
|
||||
|
||||
typedef int32_t pid_t;
|
||||
|
||||
#define major(dev) ((unsigned int)(((uint64_t)(dev) >> 32) & 0xffffffffu))
|
||||
#define minor(dev) ((unsigned int)((uint64_t)(dev) & 0xffffffffu))
|
||||
#define makedev(maj, min) ((((uint64_t)(maj)) << 32) | (uint32_t)(min))
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <stdarg.h>
|
||||
#define LOG_PID 0x01
|
||||
#define LOG_USER (1 << 3)
|
||||
#define LOG_NOTICE 5
|
||||
void openlog(const char* ident, int option, int facility);
|
||||
void closelog(void);
|
||||
void syslog(int priority, const char* format, ...);
|
||||
void vsyslog(int priority, const char* format, va_list args);
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
/* Minimal time surface for the freestanding NTFS-3G driver. */
|
||||
#ifndef _TIME_T_DEFINED
|
||||
#define _TIME_T_DEFINED
|
||||
typedef __int64 time_t;
|
||||
#endif
|
||||
|
||||
#ifndef __timespec_defined
|
||||
#define __timespec_defined 1
|
||||
struct timespec {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
};
|
||||
#endif
|
||||
|
||||
time_t __cdecl time(time_t* out);
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
uid_t getuid(void);
|
||||
uid_t geteuid(void);
|
||||
gid_t getgid(void);
|
||||
gid_t getegid(void);
|
||||
int fsync(int fd);
|
||||
|
||||
int fork(void);
|
||||
void _exit(int status);
|
||||
pid_t setsid(void);
|
||||
int chdir(const char* path);
|
||||
int open(const char* path, int flags, ...);
|
||||
int dup2(int oldfd, int newfd);
|
||||
int close(int fd);
|
||||
int daemon(int nochdir, int noclose);
|
||||
|
||||
pid_t getpid(void);
|
||||
ssize_t read(int fd, void* buffer, size_t count);
|
||||
@@ -0,0 +1,206 @@
|
||||
/* NTLite block-device backend for libntfs-3g. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddk/memory.h>
|
||||
|
||||
#include <ntfs-3g/device.h>
|
||||
#include "ntfs3g_device.h"
|
||||
|
||||
#define NTFS3G_SECTOR_SIZE 512u
|
||||
|
||||
static NTL_NTFS3G_DEVICE_CONTEXT* Ntfs3gContext(struct ntfs_device* dev) {
|
||||
return dev ? (NTL_NTFS3G_DEVICE_CONTEXT*)dev->d_private : NULL;
|
||||
}
|
||||
|
||||
static int Ntfs3gDeviceOpen(struct ntfs_device* dev, int flags) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
if (!ctx) { errno = EINVAL; return -1; }
|
||||
if (NDevOpen(dev)) { errno = EBUSY; return -1; }
|
||||
if ((flags & O_ACCMODE) != O_RDWR) NDevSetReadOnly(dev);
|
||||
else NDevClearReadOnly(dev);
|
||||
NDevSetBlock(dev);
|
||||
NDevSetOpen(dev);
|
||||
ctx->Position = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Ntfs3gDeviceClose(struct ntfs_device* dev) {
|
||||
if (!dev || !NDevOpen(dev)) { errno = EBADF; return -1; }
|
||||
NDevClearOpen(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s64 Ntfs3gDeviceSeek(struct ntfs_device* dev, s64 offset, int whence) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
if (!ctx) { errno = EINVAL; return -1; }
|
||||
int64_t base;
|
||||
if (whence == SEEK_SET) base = 0;
|
||||
else if (whence == SEEK_CUR) base = ctx->Position;
|
||||
else if (whence == SEEK_END) {
|
||||
uint64_t bytes = ctx->PartitionSectors * (uint64_t)NTFS3G_SECTOR_SIZE;
|
||||
if (bytes > INT64_MAX) { errno = EOVERFLOW; return -1; }
|
||||
base = (int64_t)bytes;
|
||||
} else { errno = EINVAL; return -1; }
|
||||
if ((offset < 0 && base < -offset) || (offset > 0 && base > INT64_MAX - offset)) {
|
||||
errno = EOVERFLOW; return -1;
|
||||
}
|
||||
base += offset;
|
||||
if (base < 0) { errno = EINVAL; return -1; }
|
||||
ctx->Position = base;
|
||||
return base;
|
||||
}
|
||||
|
||||
static int Ntfs3gBounds(NTL_NTFS3G_DEVICE_CONTEXT* ctx, s64 offset, s64 count) {
|
||||
if (!ctx || offset < 0 || count < 0) return 0;
|
||||
uint64_t bytes = ctx->PartitionSectors * (uint64_t)NTFS3G_SECTOR_SIZE;
|
||||
uint64_t uoff = (uint64_t)offset, ucount = (uint64_t)count;
|
||||
return uoff <= bytes && ucount <= bytes - uoff;
|
||||
}
|
||||
|
||||
static s64 Ntfs3gPread(struct ntfs_device* dev, void* buffer, s64 count, s64 offset) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
if (!buffer && count) { errno = EINVAL; return -1; }
|
||||
if (!Ntfs3gBounds(ctx, offset, count)) { errno = EIO; return -1; }
|
||||
if (!count) return 0;
|
||||
|
||||
uint8_t* out = (uint8_t*)buffer;
|
||||
uint64_t pos = (uint64_t)offset;
|
||||
uint64_t left = (uint64_t)count;
|
||||
uint8_t* bounce = NULL;
|
||||
|
||||
while (left) {
|
||||
uint64_t sector = pos / NTFS3G_SECTOR_SIZE;
|
||||
uint32_t inSector = (uint32_t)(pos % NTFS3G_SECTOR_SIZE);
|
||||
if (!inSector && left >= NTFS3G_SECTOR_SIZE) {
|
||||
uint64_t whole = left / NTFS3G_SECTOR_SIZE;
|
||||
uint32_t batch = whole > 0x10000u ? 0x10000u : (uint32_t)whole;
|
||||
KSTATUS s = IoReadDiskSectors(ctx->DiskIndex, ctx->PartitionLba + sector, batch, out);
|
||||
if (!KSUCCESS(s)) { errno = EIO; if (bounce) MmFreePool(bounce); return -1; }
|
||||
uint64_t bytes = (uint64_t)batch * NTFS3G_SECTOR_SIZE;
|
||||
pos += bytes; out += bytes; left -= bytes;
|
||||
continue;
|
||||
}
|
||||
if (!bounce) {
|
||||
bounce = (uint8_t*)MmAllocatePool(NTFS3G_SECTOR_SIZE);
|
||||
if (!bounce) { errno = ENOMEM; return -1; }
|
||||
}
|
||||
KSTATUS s = IoReadDiskSectors(ctx->DiskIndex, ctx->PartitionLba + sector, 1, bounce);
|
||||
if (!KSUCCESS(s)) { errno = EIO; MmFreePool(bounce); return -1; }
|
||||
uint32_t chunk = NTFS3G_SECTOR_SIZE - inSector;
|
||||
if ((uint64_t)chunk > left) chunk = (uint32_t)left;
|
||||
memcpy(out, bounce + inSector, chunk);
|
||||
pos += chunk; out += chunk; left -= chunk;
|
||||
}
|
||||
if (bounce) MmFreePool(bounce);
|
||||
return count;
|
||||
}
|
||||
|
||||
static s64 Ntfs3gPwrite(struct ntfs_device* dev, const void* buffer, s64 count, s64 offset) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
if (!buffer && count) { errno = EINVAL; return -1; }
|
||||
if (NDevReadOnly(dev)) { errno = EROFS; return -1; }
|
||||
if (!Ntfs3gBounds(ctx, offset, count)) { errno = EIO; return -1; }
|
||||
if (!count) return 0;
|
||||
|
||||
const uint8_t* in = (const uint8_t*)buffer;
|
||||
uint64_t pos = (uint64_t)offset;
|
||||
uint64_t left = (uint64_t)count;
|
||||
uint8_t* bounce = NULL;
|
||||
|
||||
while (left) {
|
||||
uint64_t sector = pos / NTFS3G_SECTOR_SIZE;
|
||||
uint32_t inSector = (uint32_t)(pos % NTFS3G_SECTOR_SIZE);
|
||||
if (!inSector && left >= NTFS3G_SECTOR_SIZE) {
|
||||
uint64_t whole = left / NTFS3G_SECTOR_SIZE;
|
||||
uint32_t batch = whole > 0x10000u ? 0x10000u : (uint32_t)whole;
|
||||
KSTATUS s = IoWriteDiskSectors(ctx->DiskIndex, ctx->PartitionLba + sector, batch, in);
|
||||
if (!KSUCCESS(s)) { errno = EIO; if (bounce) MmFreePool(bounce); return -1; }
|
||||
uint64_t bytes = (uint64_t)batch * NTFS3G_SECTOR_SIZE;
|
||||
pos += bytes; in += bytes; left -= bytes;
|
||||
continue;
|
||||
}
|
||||
if (!bounce) {
|
||||
bounce = (uint8_t*)MmAllocatePool(NTFS3G_SECTOR_SIZE);
|
||||
if (!bounce) { errno = ENOMEM; return -1; }
|
||||
}
|
||||
KSTATUS s = IoReadDiskSectors(ctx->DiskIndex, ctx->PartitionLba + sector, 1, bounce);
|
||||
if (!KSUCCESS(s)) { errno = EIO; MmFreePool(bounce); return -1; }
|
||||
uint32_t chunk = NTFS3G_SECTOR_SIZE - inSector;
|
||||
if ((uint64_t)chunk > left) chunk = (uint32_t)left;
|
||||
memcpy(bounce + inSector, in, chunk);
|
||||
s = IoWriteDiskSectors(ctx->DiskIndex, ctx->PartitionLba + sector, 1, bounce);
|
||||
if (!KSUCCESS(s)) { errno = EIO; MmFreePool(bounce); return -1; }
|
||||
pos += chunk; in += chunk; left -= chunk;
|
||||
}
|
||||
if (bounce) MmFreePool(bounce);
|
||||
NDevSetDirty(dev);
|
||||
return count;
|
||||
}
|
||||
|
||||
static s64 Ntfs3gRead(struct ntfs_device* dev, void* buffer, s64 count) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
s64 done = Ntfs3gPread(dev, buffer, count, ctx ? ctx->Position : 0);
|
||||
if (done > 0 && ctx) ctx->Position += done;
|
||||
return done;
|
||||
}
|
||||
|
||||
static s64 Ntfs3gWrite(struct ntfs_device* dev, const void* buffer, s64 count) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
s64 done = Ntfs3gPwrite(dev, buffer, count, ctx ? ctx->Position : 0);
|
||||
if (done > 0 && ctx) ctx->Position += done;
|
||||
return done;
|
||||
}
|
||||
|
||||
static int Ntfs3gSync(struct ntfs_device* dev) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
if (!ctx) { errno = EINVAL; return -1; }
|
||||
KSTATUS s = IoFlushDisk(ctx->DiskIndex);
|
||||
if (!KSUCCESS(s)) { errno = EIO; return -1; }
|
||||
NDevClearDirty(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Ntfs3gStat(struct ntfs_device* dev, struct stat* st) {
|
||||
NTL_NTFS3G_DEVICE_CONTEXT* ctx = Ntfs3gContext(dev);
|
||||
if (!ctx || !st) { errno = EINVAL; return -1; }
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->st_mode = S_IFBLK;
|
||||
st->st_size = (off_t)(ctx->PartitionSectors * (uint64_t)NTFS3G_SECTOR_SIZE);
|
||||
st->st_blksize = NTFS3G_SECTOR_SIZE;
|
||||
st->st_blocks = (blkcnt_t)ctx->PartitionSectors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Ntfs3gIoctl(struct ntfs_device* dev, unsigned long request, void* argp) {
|
||||
(void)dev; (void)request; (void)argp;
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct ntfs_device_operations gNtfs3gOps = {
|
||||
Ntfs3gDeviceOpen,
|
||||
Ntfs3gDeviceClose,
|
||||
Ntfs3gDeviceSeek,
|
||||
Ntfs3gRead,
|
||||
Ntfs3gWrite,
|
||||
Ntfs3gPread,
|
||||
Ntfs3gPwrite,
|
||||
Ntfs3gSync,
|
||||
Ntfs3gStat,
|
||||
Ntfs3gIoctl,
|
||||
};
|
||||
|
||||
struct ntfs_device* Ntfs3gCreateDevice(NTL_NTFS3G_DEVICE_CONTEXT* context) {
|
||||
if (!context) { errno = EINVAL; return NULL; }
|
||||
return ntfs_device_alloc("ntlite-ntfs", 0, &gNtfs3gOps, context);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <ntfs-3g/device.h>
|
||||
|
||||
typedef struct NTL_NTFS3G_DEVICE_CONTEXT {
|
||||
uint8_t DiskIndex;
|
||||
uint64_t PartitionLba;
|
||||
uint64_t PartitionSectors;
|
||||
int64_t Position;
|
||||
} NTL_NTFS3G_DEVICE_CONTEXT;
|
||||
|
||||
struct ntfs_device* Ntfs3gCreateDevice(NTL_NTFS3G_DEVICE_CONTEXT* context);
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,899 @@
|
||||
diff --git a/include/ntfs-3g/cache.h b/include/ntfs-3g/cache.h
|
||||
index be63b1a..195222c 100644
|
||||
--- a/include/ntfs-3g/cache.h
|
||||
+++ b/include/ntfs-3g/cache.h
|
||||
@@ -29,7 +29,7 @@ struct CACHED_GENERIC {
|
||||
struct CACHED_GENERIC *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
- union ALIGNMENT payload[0];
|
||||
+ union ALIGNMENT payload[1];
|
||||
} ;
|
||||
|
||||
struct CACHED_INODE {
|
||||
@@ -37,7 +37,7 @@ struct CACHED_INODE {
|
||||
struct CACHED_INODE *previous;
|
||||
const char *pathname;
|
||||
size_t varsize;
|
||||
- union ALIGNMENT payload[0];
|
||||
+ /* zero-length generic payload omitted for MSVC */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 inum;
|
||||
} ;
|
||||
@@ -47,7 +47,7 @@ struct CACHED_NIDATA {
|
||||
struct CACHED_NIDATA *previous;
|
||||
const char *pathname; /* not used */
|
||||
size_t varsize; /* not used */
|
||||
- union ALIGNMENT payload[0];
|
||||
+ /* zero-length generic payload omitted for MSVC */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 inum;
|
||||
ntfs_inode *ni;
|
||||
@@ -58,7 +58,7 @@ struct CACHED_LOOKUP {
|
||||
struct CACHED_LOOKUP *previous;
|
||||
const char *name;
|
||||
size_t namesize;
|
||||
- union ALIGNMENT payload[0];
|
||||
+ /* zero-length generic payload omitted for MSVC */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 parent;
|
||||
u64 inum;
|
||||
@@ -93,7 +93,7 @@ struct CACHE_HEADER {
|
||||
unsigned long hits;
|
||||
int fixed_size;
|
||||
int max_hash;
|
||||
- struct CACHED_GENERIC entry[0];
|
||||
+ struct CACHED_GENERIC entry[1];
|
||||
} ;
|
||||
|
||||
/* cast to generic, avoiding gcc warnings */
|
||||
diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h
|
||||
index ab6d453..05605c7 100644
|
||||
--- a/include/ntfs-3g/layout.h
|
||||
+++ b/include/ntfs-3g/layout.h
|
||||
@@ -1,6 +1,14 @@
|
||||
-#ifdef _MSC_VER
|
||||
-#pragma pack(push, 1)
|
||||
-#endif
|
||||
+#ifdef _MSC_VER
|
||||
+#pragma pack(push, 1)
|
||||
+#endif
|
||||
+#ifdef _MSC_VER
|
||||
+#define NTFS_PACKED_ENUM_BEGIN(name, storage) enum
|
||||
+#define NTFS_PACKED_ENUM_END(name, storage) ; typedef storage name
|
||||
+#else
|
||||
+#define NTFS_PACKED_ENUM_BEGIN(name, storage) typedef enum
|
||||
+#define NTFS_PACKED_ENUM_END(name, storage) __attribute__((__packed__)) name
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* layout.h - Ntfs on-disk layout structures. Originated from the Linux-NTFS project.
|
||||
*
|
||||
@@ -263,14 +271,14 @@ typedef enum {
|
||||
* index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other
|
||||
* than "$I30". It is unknown if it is limited to metadata files only.
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(MFT_RECORD_FLAGS, u16) {
|
||||
MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001),
|
||||
MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002),
|
||||
MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004),
|
||||
MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008),
|
||||
MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags
|
||||
16-bit. */
|
||||
-} __attribute__((__packed__)) MFT_RECORD_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(MFT_RECORD_FLAGS, u16);
|
||||
|
||||
/*
|
||||
* mft references (aka file references or file record segment references) are
|
||||
@@ -631,14 +639,14 @@ typedef struct {
|
||||
/**
|
||||
* enum ATTR_FLAGS - Attribute flags (16-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(ATTR_FLAGS, u16) {
|
||||
ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001),
|
||||
ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression
|
||||
method mask. Also, first
|
||||
illegal value. */
|
||||
ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000),
|
||||
ATTR_IS_SPARSE = const_cpu_to_le16(0x8000),
|
||||
-} __attribute__((__packed__)) ATTR_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(ATTR_FLAGS, u16);
|
||||
|
||||
/*
|
||||
* Attribute compression.
|
||||
@@ -710,11 +718,11 @@ typedef enum {
|
||||
/**
|
||||
* enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(RESIDENT_ATTR_FLAGS, u8) {
|
||||
RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index
|
||||
(has implications for deleting and
|
||||
modifying the attribute). */
|
||||
-} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(RESIDENT_ATTR_FLAGS, u8);
|
||||
|
||||
/**
|
||||
* struct ATTR_RECORD - Attribute record header.
|
||||
@@ -759,7 +767,7 @@ typedef struct {
|
||||
/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */
|
||||
/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte
|
||||
boundary. */
|
||||
-/* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD,
|
||||
+/* 24 */ void *resident_end[1]; /* Use offsetof(ATTR_RECORD,
|
||||
resident_end) to get size of
|
||||
a resident attribute. */
|
||||
} __attribute__((__packed__));
|
||||
@@ -805,17 +813,17 @@ typedef struct {
|
||||
/* 56*/ sle64 initialized_size; /* Byte size of initialized
|
||||
portion of the attribute value. Usually equals
|
||||
data_size. */
|
||||
-/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD,
|
||||
- non_resident_end) to get
|
||||
- size of a non resident
|
||||
- attribute. */
|
||||
+/* 64 */ union {
|
||||
+ void *non_resident_end[1]; /* Marker for offsetof(). */
|
||||
+ sle64 compressed_size;
|
||||
+ };
|
||||
/* sizeof(uncompressed attr) = 64*/
|
||||
-/* 64*/ sle64 compressed_size; /* Byte size of the attribute
|
||||
+/* 64*/ /* compressed_size is overlaid above */ /* Byte size of the attribute
|
||||
value after compression. Only present when
|
||||
compressed. Always is a multiple of the
|
||||
cluster size. Represents the actual amount of
|
||||
disk space being used on the disk. */
|
||||
-/* 72 */ void *compressed_end[0];
|
||||
+/* 72 */ void *compressed_end[1];
|
||||
/* Use offsetof(ATTR_RECORD, compressed_end) to
|
||||
get size of a compressed attribute. */
|
||||
/* sizeof(compressed attr) = 72*/
|
||||
@@ -828,7 +836,7 @@ typedef ATTR_RECORD ATTR_REC;
|
||||
/**
|
||||
* enum FILE_ATTR_FLAGS - File attribute flags (32-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(FILE_ATTR_FLAGS, u32) {
|
||||
/*
|
||||
* These flags are only present in the STANDARD_INFORMATION attribute
|
||||
* (in the field file_attributes).
|
||||
@@ -891,7 +899,7 @@ typedef enum {
|
||||
* $FILE_NAME attributes.
|
||||
*/
|
||||
FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000),
|
||||
-} __attribute__((__packed__)) FILE_ATTR_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(FILE_ATTR_FLAGS, u32);
|
||||
|
||||
/*
|
||||
* NOTE on times in NTFS: All times are in MS standard time format, i.e. they
|
||||
@@ -932,7 +940,7 @@ typedef struct {
|
||||
struct {
|
||||
/* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte
|
||||
boundary. */
|
||||
- /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */
|
||||
+ /* 48 */ void *v1_end[1]; /* Marker for offsetof(). */
|
||||
} __attribute__((__packed__));
|
||||
/* sizeof() = 48 bytes */
|
||||
/* NTFS 3.0 */
|
||||
@@ -986,7 +994,7 @@ typedef struct {
|
||||
partition. This, in contrast to disabling the
|
||||
journal is a very fast process, so the user
|
||||
won't even notice it. */
|
||||
- /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */
|
||||
+ /* 72*/ void *v3_end[1]; /* Marker for offsetof(). */
|
||||
} __attribute__((__packed__));
|
||||
} __attribute__((__packed__));
|
||||
/* sizeof() = 72 bytes (NTFS 3.0) */
|
||||
@@ -1047,7 +1055,7 @@ typedef struct {
|
||||
attribute value. */
|
||||
/* 24*/ le16 instance; /* If lowest_vcn = 0, the instance of the
|
||||
attribute being referenced; otherwise 0. */
|
||||
-/* 26*/ ntfschar name[0]; /* Use when creating only. When reading use
|
||||
+/* 26*/ ntfschar name[1]; /* Use when creating only. When reading use
|
||||
name_offset to determine the location of the
|
||||
name. */
|
||||
/* sizeof() = 26 + (attribute_name_length * 2) bytes */
|
||||
@@ -1062,7 +1070,7 @@ typedef struct {
|
||||
* enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs.
|
||||
* (8-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(FILE_NAME_TYPE_FLAGS, u8) {
|
||||
FILE_NAME_POSIX = 0x00,
|
||||
/* This is the largest namespace. It is case sensitive and
|
||||
allows all Unicode characters except for: '\0' and '/'.
|
||||
@@ -1088,7 +1096,7 @@ typedef enum {
|
||||
/* 3 means that both the Win32 and the DOS filenames are
|
||||
identical and hence have been saved in this single filename
|
||||
record. */
|
||||
-} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(FILE_NAME_TYPE_FLAGS, u8);
|
||||
|
||||
/**
|
||||
* struct FILE_NAME_ATTR - Attribute: Filename (0x30).
|
||||
@@ -1141,7 +1149,7 @@ typedef struct {
|
||||
/* 40*/ u8 file_name_length; /* Length of file name in
|
||||
(Unicode) characters. */
|
||||
/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/
|
||||
-/* 42*/ ntfschar file_name[0]; /* File name in Unicode. */
|
||||
+/* 42*/ ntfschar file_name[1]; /* File name in Unicode. */
|
||||
} __attribute__((__packed__)) FILE_NAME_ATTR;
|
||||
|
||||
/**
|
||||
@@ -1392,7 +1400,7 @@ typedef union {
|
||||
* revision = 1,
|
||||
* sub_authority_count = 2,
|
||||
* identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY
|
||||
- * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID
|
||||
+ * sub_authority[1] = 32, // SECURITY_BUILTIN_DOMAIN_RID
|
||||
* sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS
|
||||
*/
|
||||
typedef struct {
|
||||
@@ -1415,7 +1423,7 @@ typedef enum {
|
||||
/**
|
||||
* enum ACE_TYPES - The predefined ACE types (8-bit, see below).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(ACE_TYPES, u8) {
|
||||
ACCESS_MIN_MS_ACE_TYPE = 0,
|
||||
ACCESS_ALLOWED_ACE_TYPE = 0,
|
||||
ACCESS_DENIED_ACE_TYPE = 1,
|
||||
@@ -1458,7 +1466,7 @@ typedef enum {
|
||||
|
||||
/* Windows 10 and later */
|
||||
SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE = 20,
|
||||
-} __attribute__((__packed__)) ACE_TYPES;
|
||||
+} NTFS_PACKED_ENUM_END(ACE_TYPES, u8);
|
||||
|
||||
/**
|
||||
* enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance.
|
||||
@@ -1470,7 +1478,7 @@ typedef enum {
|
||||
* FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types
|
||||
* to indicate that a message is generated (in Windows!) for failed accesses.
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(ACE_FLAGS, u8) {
|
||||
/* The inheritance flags. */
|
||||
OBJECT_INHERIT_ACE = 0x01,
|
||||
CONTAINER_INHERIT_ACE = 0x02,
|
||||
@@ -1482,7 +1490,7 @@ typedef enum {
|
||||
/* The audit flags. */
|
||||
SUCCESSFUL_ACCESS_ACE_FLAG = 0x40,
|
||||
FAILED_ACCESS_ACE_FLAG = 0x80,
|
||||
-} __attribute__((__packed__)) ACE_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(ACE_FLAGS, u8);
|
||||
|
||||
/**
|
||||
* struct ACE_HEADER -
|
||||
@@ -1784,7 +1792,7 @@ typedef enum {
|
||||
* and all pointer fields are expressed as offsets from the
|
||||
* beginning of the security descriptor.
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(SECURITY_DESCRIPTOR_CONTROL, u16) {
|
||||
SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001),
|
||||
SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002),
|
||||
SE_DACL_PRESENT = const_cpu_to_le16(0x0004),
|
||||
@@ -1799,7 +1807,7 @@ typedef enum {
|
||||
SE_SACL_PROTECTED = const_cpu_to_le16(0x2000),
|
||||
SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000),
|
||||
SE_SELF_RELATIVE = const_cpu_to_le16(0x8000),
|
||||
-} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL;
|
||||
+} NTFS_PACKED_ENUM_END(SECURITY_DESCRIPTOR_CONTROL, u16);
|
||||
|
||||
/**
|
||||
* struct SECURITY_DESCRIPTOR_RELATIVE -
|
||||
@@ -2007,13 +2015,13 @@ typedef struct {
|
||||
* NOTE: Present only in FILE_Volume.
|
||||
*/
|
||||
typedef struct {
|
||||
- ntfschar name[0]; /* The name of the volume in Unicode. */
|
||||
+ ntfschar name[1]; /* The name of the volume in Unicode. */
|
||||
} __attribute__((__packed__)) VOLUME_NAME;
|
||||
|
||||
/**
|
||||
* enum VOLUME_FLAGS - Possible flags for the volume (16-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(VOLUME_FLAGS, u16) {
|
||||
VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001),
|
||||
VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002),
|
||||
VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004),
|
||||
@@ -2023,7 +2031,7 @@ typedef enum {
|
||||
VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000),
|
||||
VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000),
|
||||
VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f),
|
||||
-} __attribute__((__packed__)) VOLUME_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(VOLUME_FLAGS, u16);
|
||||
|
||||
/**
|
||||
* struct VOLUME_INFORMATION - Attribute: Volume information (0x70).
|
||||
@@ -2048,13 +2056,13 @@ typedef struct {
|
||||
* Data contents of a file (i.e. the unnamed stream) or of a named stream.
|
||||
*/
|
||||
typedef struct {
|
||||
- u8 data[0]; /* The file's data contents. */
|
||||
+ u8 data[1]; /* The file's data contents. */
|
||||
} __attribute__((__packed__)) DATA_ATTR;
|
||||
|
||||
/**
|
||||
* enum INDEX_HEADER_FLAGS - Index header flags (8-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(INDEX_HEADER_FLAGS, u8) {
|
||||
/* When index header is in an index root attribute: */
|
||||
SMALL_INDEX = 0, /* The index is small enough to fit inside the
|
||||
index root attribute and there is no index
|
||||
@@ -2071,7 +2079,7 @@ typedef enum {
|
||||
INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a
|
||||
leaf node. */
|
||||
NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */
|
||||
-} __attribute__((__packed__)) INDEX_HEADER_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(INDEX_HEADER_FLAGS, u8);
|
||||
|
||||
/**
|
||||
* struct INDEX_HEADER -
|
||||
@@ -2282,7 +2290,7 @@ typedef enum {
|
||||
/**
|
||||
* enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(INDEX_ENTRY_FLAGS, u16) {
|
||||
INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a
|
||||
sub-node, i.e. a reference to an index
|
||||
block in form of a virtual cluster
|
||||
@@ -2292,7 +2300,7 @@ typedef enum {
|
||||
entry does not represent a file but it
|
||||
can point to a sub-node. */
|
||||
INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */
|
||||
-} __attribute__((__packed__)) INDEX_ENTRY_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(INDEX_ENTRY_FLAGS, u16);
|
||||
|
||||
/**
|
||||
* struct INDEX_ENTRY_HEADER - This the index entry header (see below).
|
||||
@@ -2395,7 +2403,7 @@ typedef struct {
|
||||
* number of clusters in the index allocation attribute.
|
||||
*/
|
||||
typedef struct {
|
||||
- u8 bitmap[0]; /* Array of bits. */
|
||||
+ u8 bitmap[1]; /* Array of bits. */
|
||||
} __attribute__((__packed__)) BITMAP_ATTR;
|
||||
|
||||
/**
|
||||
@@ -2469,7 +2477,7 @@ typedef struct {
|
||||
le32 reparse_tag; /* Reparse point type (inc. flags). */
|
||||
le16 reparse_data_length; /* Byte size of reparse data. */
|
||||
le16 reserved; /* Align to 8-byte boundary. */
|
||||
- u8 reparse_data[0]; /* Meaning depends on reparse_tag. */
|
||||
+ u8 reparse_data[1]; /* Meaning depends on reparse_tag. */
|
||||
} __attribute__((__packed__)) REPARSE_POINT;
|
||||
|
||||
/**
|
||||
@@ -2492,12 +2500,12 @@ typedef struct {
|
||||
/**
|
||||
* enum EA_FLAGS - Extended attribute flags (8-bit).
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(EA_FLAGS, u8) {
|
||||
NEED_EA = 0x80, /* Indicate that the file to which the EA
|
||||
belongs cannot be interpreted without
|
||||
understanding the associated extended
|
||||
attributes. */
|
||||
-} __attribute__((__packed__)) EA_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(EA_FLAGS, u8);
|
||||
|
||||
/**
|
||||
* struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0).
|
||||
@@ -2514,8 +2522,8 @@ typedef struct {
|
||||
u8 name_length; /* Length of the name of the extended
|
||||
attribute in bytes. */
|
||||
le16 value_length; /* Byte size of the EA's value. */
|
||||
- u8 name[0]; /* Name of the EA. */
|
||||
- u8 value[0]; /* The value of the EA. Immediately
|
||||
+ u8 name[1]; /* Name of the EA. */
|
||||
+ u8 value[1]; /* The value of the EA. Immediately
|
||||
follows the name. */
|
||||
} __attribute__((__packed__)) EA_ATTR;
|
||||
|
||||
@@ -2526,7 +2534,7 @@ typedef struct {
|
||||
* NTFS 3.0 during beta testing.
|
||||
*/
|
||||
typedef struct {
|
||||
- /* Irrelevant as feature unused. */
|
||||
+ u8 _ntlite_unused;
|
||||
} __attribute__((__packed__)) PROPERTY_SET;
|
||||
|
||||
/**
|
||||
@@ -2541,7 +2549,7 @@ typedef struct {
|
||||
* attribute with the name $EFS. See below for the relevant structures.
|
||||
*/
|
||||
typedef struct {
|
||||
- /* Can be anything the creator chooses. */
|
||||
+ u8 _ntlite_unused;
|
||||
} __attribute__((__packed__)) LOGGED_UTILITY_STREAM;
|
||||
|
||||
/*
|
||||
@@ -2684,6 +2692,15 @@ typedef struct {
|
||||
|
||||
typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER;
|
||||
|
||||
+#ifdef _MSC_VER
|
||||
+typedef le64 INTX_FILE_TYPES;
|
||||
+#define INTX_SYMBOLIC_LINK \
|
||||
+ ((INTX_FILE_TYPES)const_cpu_to_le64(0x014B4E4C78746E49ULL)) /* "IntxLNK\1" */
|
||||
+#define INTX_CHARACTER_DEVICE \
|
||||
+ ((INTX_FILE_TYPES)const_cpu_to_le64(0x0052484378746E49ULL)) /* "IntxCHR\0" */
|
||||
+#define INTX_BLOCK_DEVICE \
|
||||
+ ((INTX_FILE_TYPES)const_cpu_to_le64(0x004B4C4278746E49ULL)) /* "IntxBLK\0" */
|
||||
+#else
|
||||
typedef enum {
|
||||
INTX_SYMBOLIC_LINK =
|
||||
const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */
|
||||
@@ -2692,6 +2709,7 @@ typedef enum {
|
||||
INTX_BLOCK_DEVICE =
|
||||
const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */
|
||||
} INTX_FILE_TYPES;
|
||||
+#endif
|
||||
|
||||
typedef struct {
|
||||
INTX_FILE_TYPES magic; /* Intx file magic. */
|
||||
@@ -2700,15 +2718,15 @@ typedef struct {
|
||||
struct {
|
||||
le64 major; /* Major device number. */
|
||||
le64 minor; /* Minor device number. */
|
||||
- void *device_end[0]; /* Marker for offsetof(). */
|
||||
+ void *device_end[1]; /* Marker for offsetof(). */
|
||||
} __attribute__((__packed__));
|
||||
/* For symbolic links. */
|
||||
- ntfschar target[0];
|
||||
+ ntfschar target[1];
|
||||
} __attribute__((__packed__));
|
||||
} __attribute__((__packed__)) INTX_FILE;
|
||||
|
||||
#endif /* defined _NTFS_LAYOUT_H */
|
||||
-
|
||||
-#ifdef _MSC_VER
|
||||
-#pragma pack(pop)
|
||||
-#endif
|
||||
+
|
||||
+#ifdef _MSC_VER
|
||||
+#pragma pack(pop)
|
||||
+#endif
|
||||
diff --git a/include/ntfs-3g/logfile.h b/include/ntfs-3g/logfile.h
|
||||
index d1f1222..97c99c4 100644
|
||||
--- a/include/ntfs-3g/logfile.h
|
||||
+++ b/include/ntfs-3g/logfile.h
|
||||
@@ -1,3 +1,6 @@
|
||||
+#ifdef _MSC_VER
|
||||
+#pragma pack(push, 1)
|
||||
+#endif
|
||||
/*
|
||||
* logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
@@ -335,7 +338,7 @@ typedef struct {
|
||||
*
|
||||
* (Or is it log record pages?)
|
||||
*/
|
||||
-typedef enum {
|
||||
+NTFS_PACKED_ENUM_BEGIN(LOG_RECORD_FLAGS, u16) {
|
||||
LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */
|
||||
/* The flags below were introduced in Windows 10 */
|
||||
LOG_RECORD_DELETING = const_cpu_to_le16(0x0002),
|
||||
@@ -343,7 +346,7 @@ typedef enum {
|
||||
LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff,
|
||||
/* This has nothing to do with the log record. It is only so
|
||||
gcc knows to make the flags 16-bit. */
|
||||
-} __attribute__((__packed__)) LOG_RECORD_FLAGS;
|
||||
+} NTFS_PACKED_ENUM_END(LOG_RECORD_FLAGS, u16);
|
||||
|
||||
/**
|
||||
* struct LOG_CLIENT_ID - The log client id structure identifying a log client.
|
||||
@@ -413,7 +416,7 @@ typedef struct {
|
||||
ATTRIBUTE_FLAGS attribute_flags;
|
||||
leVCN target_vcn;
|
||||
/* Now at ofs 0x50. */
|
||||
- leLCN lcn_list[0]; /* Only present if lcns_to_follow
|
||||
+ leLCN lcn_list[1]; /* Only present if lcns_to_follow
|
||||
is not 0. */
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
@@ -421,7 +424,7 @@ typedef struct {
|
||||
leLSN attributes_lsn;
|
||||
leLSN names_lsn;
|
||||
leLSN dirty_pages_lsn;
|
||||
- le64 unknown_list[0];
|
||||
+ le64 unknown_list[1];
|
||||
} __attribute__((__packed__));
|
||||
} __attribute__((__packed__));
|
||||
} __attribute__((__packed__)) LOG_RECORD;
|
||||
@@ -468,3 +471,7 @@ extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp);
|
||||
extern int ntfs_empty_logfile(ntfs_attr *na);
|
||||
|
||||
#endif /* defined _NTFS_LOGFILE_H */
|
||||
+
|
||||
+#ifdef _MSC_VER
|
||||
+#pragma pack(pop)
|
||||
+#endif
|
||||
diff --git a/include/ntfs-3g/logging.h b/include/ntfs-3g/logging.h
|
||||
index 82f39fe..8c9538c 100644
|
||||
--- a/include/ntfs-3g/logging.h
|
||||
+++ b/include/ntfs-3g/logging.h
|
||||
@@ -87,32 +87,28 @@ int ntfs_log_redirect(const char *function, const char *file, int line,
|
||||
#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */
|
||||
#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */
|
||||
|
||||
-/* Macros to simplify logging. One for each level defined above.
|
||||
- * Note, ntfs_log_debug/trace have effect only if DEBUG is defined.
|
||||
- */
|
||||
-#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS)
|
||||
-
|
||||
-/* By default debug and trace messages are compiled into the program,
|
||||
- * but not displayed.
|
||||
- */
|
||||
+/* Macros to simplify logging. Use standard C99 variadic macros so this
|
||||
+ * header also parses with MSVC's C front-end. */
|
||||
+#define ntfs_log_critical(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_error(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_info(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_perror(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_progress(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_quiet(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_verbose(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_warning(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,__VA_ARGS__)
|
||||
+
|
||||
#ifdef DEBUG
|
||||
-#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS)
|
||||
-#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS)
|
||||
+#define ntfs_log_debug(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_trace(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_enter(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,__VA_ARGS__)
|
||||
+#define ntfs_log_leave(...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,__VA_ARGS__)
|
||||
#else
|
||||
-#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0)
|
||||
-#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0)
|
||||
-#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0)
|
||||
-#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0)
|
||||
-#endif /* DEBUG */
|
||||
+#define ntfs_log_debug(...) do {} while (0)
|
||||
+#define ntfs_log_trace(...) do {} while (0)
|
||||
+#define ntfs_log_enter(...) do {} while (0)
|
||||
+#define ntfs_log_leave(...) do {} while (0)
|
||||
+#endif
|
||||
|
||||
void ntfs_log_early_error(const char *format, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h
|
||||
index ebc8a0a..231dc76 100644
|
||||
--- a/include/ntfs-3g/param.h
|
||||
+++ b/include/ntfs-3g/param.h
|
||||
@@ -22,11 +22,21 @@
|
||||
#ifndef _NTFS_PARAM_H
|
||||
#define _NTFS_PARAM_H
|
||||
|
||||
+#ifndef CACHE_INODE_SIZE
|
||||
#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */
|
||||
+#endif
|
||||
+#ifndef CACHE_NIDATA_SIZE
|
||||
#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */
|
||||
+#endif
|
||||
+#ifndef CACHE_LOOKUP_SIZE
|
||||
#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */
|
||||
+#endif
|
||||
+#ifndef CACHE_SECURID_SIZE
|
||||
#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */
|
||||
+#endif
|
||||
+#ifndef CACHE_LEGACY_SIZE
|
||||
#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */
|
||||
+#endif
|
||||
|
||||
#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */
|
||||
#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */
|
||||
diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h
|
||||
index c76ce9f..b747b61 100644
|
||||
--- a/include/ntfs-3g/security.h
|
||||
+++ b/include/ntfs-3g/security.h
|
||||
@@ -74,7 +74,7 @@ struct CACHED_PERMISSIONS_LEGACY {
|
||||
struct CACHED_PERMISSIONS_LEGACY *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
- union ALIGNMENT payload[0];
|
||||
+ /* zero-length generic payload omitted for MSVC */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 mft_no;
|
||||
struct CACHED_PERMISSIONS perm;
|
||||
@@ -89,7 +89,7 @@ struct CACHED_SECURID {
|
||||
struct CACHED_SECURID *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
- union ALIGNMENT payload[0];
|
||||
+ /* zero-length generic payload omitted for MSVC */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
@@ -164,7 +164,7 @@ struct POSIX_ACL {
|
||||
u8 version;
|
||||
u8 flags;
|
||||
u16 filler;
|
||||
- struct POSIX_ACE ace[0];
|
||||
+ struct POSIX_ACE ace[1];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct POSIX_SECURITY {
|
||||
diff --git a/include/ntfs-3g/support.h b/include/ntfs-3g/support.h
|
||||
index 6af4761..eadc08d 100644
|
||||
--- a/include/ntfs-3g/support.h
|
||||
+++ b/include/ntfs-3g/support.h
|
||||
@@ -67,19 +67,13 @@
|
||||
#define set_bit(bit, var) (var) |= 1 << (bit)
|
||||
#define clear_bit(bit, var) (var) &= ~(1 << (bit))
|
||||
|
||||
-#define test_and_set_bit(bit, var) \
|
||||
-({ \
|
||||
- const BOOL old_state = test_bit(bit, var); \
|
||||
- set_bit(bit, var); \
|
||||
- old_state; \
|
||||
-})
|
||||
+#define test_and_set_bit(bit, var) \
|
||||
+ (test_bit((bit), (var)) ? (set_bit((bit), (var)), TRUE) : \
|
||||
+ (set_bit((bit), (var)), FALSE))
|
||||
|
||||
-#define test_and_clear_bit(bit, var) \
|
||||
-({ \
|
||||
- const BOOL old_state = test_bit(bit, var); \
|
||||
- clear_bit(bit, var); \
|
||||
- old_state; \
|
||||
-})
|
||||
+#define test_and_clear_bit(bit, var) \
|
||||
+ (test_bit((bit), (var)) ? (clear_bit((bit), (var)), TRUE) : \
|
||||
+ (clear_bit((bit), (var)), FALSE))
|
||||
|
||||
#endif /* defined _NTFS_SUPPORT_H */
|
||||
|
||||
diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c
|
||||
index 35da32e..b1ab81e 100644
|
||||
--- a/libntfs-3g/attrib.c
|
||||
+++ b/libntfs-3g/attrib.c
|
||||
@@ -6906,7 +6906,7 @@ static int stuff_hole(ntfs_attr *na, const s64 pos)
|
||||
if (!ret && NAttrNonResident(na)) {
|
||||
/* does the hole span over several compression block ? */
|
||||
if ((pos ^ na->initialized_size)
|
||||
- & ~(na->compression_block_size - 1)) {
|
||||
+ & ~((s64)na->compression_block_size - 1)) {
|
||||
begin_size = ((na->initialized_size - 1)
|
||||
| (na->compression_block_size - 1))
|
||||
+ 1 - na->initialized_size;
|
||||
diff --git a/libntfs-3g/attrlist.c b/libntfs-3g/attrlist.c
|
||||
index 29d4085..0cb8473 100644
|
||||
--- a/libntfs-3g/attrlist.c
|
||||
+++ b/libntfs-3g/attrlist.c
|
||||
@@ -135,7 +135,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
||||
}
|
||||
|
||||
/* Determine size and allocate memory for new attribute list. */
|
||||
- entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
|
||||
+ entry_len = (offsetof(ATTR_LIST_ENTRY, name) + sizeof(ntfschar) *
|
||||
attr->name_length + 7) & ~7;
|
||||
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
||||
if (!new_al)
|
||||
diff --git a/libntfs-3g/cache.c b/libntfs-3g/cache.c
|
||||
index 2ad8d35..e577b52 100644
|
||||
--- a/libntfs-3g/cache.c
|
||||
+++ b/libntfs-3g/cache.c
|
||||
@@ -483,7 +483,7 @@ static struct CACHE_HEADER *ntfs_create_cache(const char *name,
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
- size = sizeof(struct CACHE_HEADER) + item_count*full_item_size;
|
||||
+ size = offsetof(struct CACHE_HEADER, entry) + item_count*full_item_size;
|
||||
if (max_hash)
|
||||
size += item_count*sizeof(struct HASH_ENTRY)
|
||||
+ max_hash*sizeof(struct HASH_ENTRY*);
|
||||
@@ -499,7 +499,7 @@ static struct CACHE_HEADER *ntfs_create_cache(const char *name,
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
cache->max_hash = 0;
|
||||
}
|
||||
- cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
|
||||
+ cache->fixed_size = full_item_size - offsetof(struct CACHED_GENERIC, payload);
|
||||
cache->reads = 0;
|
||||
cache->writes = 0;
|
||||
cache->hits = 0;
|
||||
diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c
|
||||
index 5cab5c5..ad82c6f 100644
|
||||
--- a/libntfs-3g/dir.c
|
||||
+++ b/libntfs-3g/dir.c
|
||||
@@ -841,10 +841,6 @@ static const ntfschar dotdot[3] = { const_cpu_to_le16('.'),
|
||||
* union index_union -
|
||||
* More helpers for ntfs_readdir().
|
||||
*/
|
||||
-typedef union {
|
||||
- INDEX_ROOT *ir;
|
||||
- INDEX_ALLOCATION *ia;
|
||||
-} index_union __attribute__((__transparent_union__));
|
||||
|
||||
/**
|
||||
* enum INDEX_TYPE -
|
||||
@@ -957,7 +953,7 @@ static u32 ntfs_dir_entry_type(ntfs_inode *dir_ni, MFT_REF mref,
|
||||
* callback.
|
||||
*/
|
||||
static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits,
|
||||
- const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie,
|
||||
+ const INDEX_TYPE index_type, void *index_block, INDEX_ENTRY *ie,
|
||||
void *dirent, ntfs_filldir_t filldir)
|
||||
{
|
||||
FILE_NAME_ATTR *fn = &ie->key.file_name;
|
||||
@@ -971,11 +967,11 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits,
|
||||
|
||||
/* Advance the position even if going to skip the entry. */
|
||||
if (index_type == INDEX_TYPE_ALLOCATION)
|
||||
- *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu(
|
||||
- iu.ia->index_block_vcn) << ivcn_bits) +
|
||||
+ *pos = (u8*)ie - (u8*)(INDEX_ALLOCATION*)index_block + (sle64_to_cpu(
|
||||
+ ((INDEX_ALLOCATION*)index_block)->index_block_vcn) << ivcn_bits) +
|
||||
dir_ni->vol->mft_record_size;
|
||||
else /* if (index_type == INDEX_TYPE_ROOT) */
|
||||
- *pos = (u8*)ie - (u8*)iu.ir;
|
||||
+ *pos = (u8*)ie - (u8*)(INDEX_ROOT*)index_block;
|
||||
mref = le64_to_cpu(ie->indexed_file);
|
||||
metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user);
|
||||
/* Skip root directory self reference entry. */
|
||||
@@ -1500,7 +1496,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
|
||||
* Write STANDARD_INFORMATION v1.2 (no inheritance) or v3
|
||||
*/
|
||||
if (securid)
|
||||
- si_len = sizeof(STANDARD_INFORMATION);
|
||||
+ si_len = offsetof(STANDARD_INFORMATION, v3_end);
|
||||
else
|
||||
si_len = offsetof(STANDARD_INFORMATION, v1_end);
|
||||
si = ntfs_calloc(si_len);
|
||||
@@ -1686,7 +1682,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
|
||||
free(data);
|
||||
}
|
||||
/* Create FILE_NAME attribute. */
|
||||
- fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
|
||||
+ fn_len = offsetof(FILE_NAME_ATTR, file_name) + name_len * sizeof(ntfschar);
|
||||
fn = ntfs_calloc(fn_len);
|
||||
if (!fn) {
|
||||
err = errno;
|
||||
@@ -2223,7 +2219,7 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name,
|
||||
}
|
||||
|
||||
/* Create FILE_NAME attribute. */
|
||||
- fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
|
||||
+ fn_len = offsetof(FILE_NAME_ATTR, file_name) + name_len * sizeof(ntfschar);
|
||||
fn = ntfs_calloc(fn_len);
|
||||
if (!fn) {
|
||||
err = errno;
|
||||
diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c
|
||||
index 7b88c18..6de0e35 100644
|
||||
--- a/libntfs-3g/index.c
|
||||
+++ b/libntfs-3g/index.c
|
||||
@@ -1704,7 +1704,7 @@ int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref)
|
||||
}
|
||||
|
||||
fn_size = (fn->file_name_length * sizeof(ntfschar)) +
|
||||
- sizeof(FILE_NAME_ATTR);
|
||||
+ offsetof(FILE_NAME_ATTR, file_name);
|
||||
ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7;
|
||||
|
||||
ie = ntfs_calloc(ie_size);
|
||||
diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c
|
||||
index 8819ff1..dc17a4c 100644
|
||||
--- a/libntfs-3g/inode.c
|
||||
+++ b/libntfs-3g/inode.c
|
||||
@@ -857,7 +857,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni)
|
||||
err = errno;
|
||||
continue;
|
||||
}
|
||||
- if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) {
|
||||
+ if (ntfs_index_lookup(fn, offsetof(FILE_NAME_ATTR, file_name), ictx)) {
|
||||
if (!err) {
|
||||
if (errno == ENOENT)
|
||||
err = EIO;
|
||||
@@ -1148,7 +1148,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni)
|
||||
goto put_err_out;
|
||||
}
|
||||
|
||||
- ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
|
||||
+ ale_size = (offsetof(ATTR_LIST_ENTRY, name) + sizeof(ntfschar) *
|
||||
ctx->attr->name_length + 7) & ~7;
|
||||
al_len += ale_size;
|
||||
|
||||
@@ -1165,7 +1165,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni)
|
||||
|
||||
/* Add attribute to attribute list. */
|
||||
ale->type = ctx->attr->type;
|
||||
- ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) +
|
||||
+ ale->length = cpu_to_le16((offsetof(ATTR_LIST_ENTRY, name) +
|
||||
sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7);
|
||||
ale->name_length = ctx->attr->name_length;
|
||||
ale->name_offset = (u8 *)ale->name - (u8 *)ale;
|
||||
diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c
|
||||
index b059a53..be13ede 100644
|
||||
--- a/libntfs-3g/ioctl.c
|
||||
+++ b/libntfs-3g/ioctl.c
|
||||
@@ -409,7 +409,11 @@ int ntfs_ioctl(ntfs_inode *ni, unsigned long cmd,
|
||||
}
|
||||
break;
|
||||
#else
|
||||
+#ifdef _MSC_VER
|
||||
+#pragma message("ntfs-3g: trimming not supported (FITRIM or BLKDISCARD not defined)")
|
||||
+#else
|
||||
#warning Trimming not supported : FITRIM or BLKDISCARD not defined
|
||||
+#endif
|
||||
#endif
|
||||
default :
|
||||
ret = -EINVAL;
|
||||
diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c
|
||||
index 54858ca..fca18a6 100644
|
||||
--- a/libntfs-3g/reparse.c
|
||||
+++ b/libntfs-3g/reparse.c
|
||||
@@ -66,7 +66,7 @@ struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */
|
||||
le16 subst_name_length;
|
||||
le16 print_name_offset;
|
||||
le16 print_name_length;
|
||||
- char path_buffer[0]; /* above data assume this is char array */
|
||||
+ char path_buffer[1]; /* above data assume this is char array */
|
||||
} ;
|
||||
|
||||
struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */
|
||||
@@ -75,12 +75,12 @@ struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */
|
||||
le16 print_name_offset;
|
||||
le16 print_name_length;
|
||||
le32 flags; /* 1 for full target, otherwise 0 */
|
||||
- char path_buffer[0]; /* above data assume this is char array */
|
||||
+ char path_buffer[1]; /* above data assume this is char array */
|
||||
} ;
|
||||
|
||||
struct WSL_LINK_REPARSE_DATA {
|
||||
le32 type;
|
||||
- char link[0];
|
||||
+ char link[1];
|
||||
} ;
|
||||
|
||||
struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */
|
||||
diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c
|
||||
index d0e030e..6e6dc14 100644
|
||||
--- a/libntfs-3g/security.c
|
||||
+++ b/libntfs-3g/security.c
|
||||
@@ -1017,7 +1017,7 @@ static int update_secur_descr(ntfs_volume *vol,
|
||||
if (!test_nino_flag(ni, v3_Extensions)) {
|
||||
/* expand standard information attribute to v3.x */
|
||||
res = ntfs_attr_truncate(na,
|
||||
- (s64)sizeof(STANDARD_INFORMATION));
|
||||
+ (s64)offsetof(STANDARD_INFORMATION, v3_end));
|
||||
ni->owner_id = const_cpu_to_le32(0);
|
||||
ni->quota_charged = const_cpu_to_le64(0);
|
||||
ni->usn = const_cpu_to_le64(0);
|
||||
@@ -1091,7 +1091,7 @@ static int upgrade_secur_desc(ntfs_volume *vol,
|
||||
if (na) {
|
||||
/* expand standard information attribute to v3.x */
|
||||
res = ntfs_attr_truncate(na,
|
||||
- (s64)sizeof(STANDARD_INFORMATION));
|
||||
+ (s64)offsetof(STANDARD_INFORMATION, v3_end));
|
||||
ni->owner_id = const_cpu_to_le32(0);
|
||||
ni->quota_charged = const_cpu_to_le64(0);
|
||||
ni->usn = const_cpu_to_le64(0);
|
||||
diff --git a/libntfs-3g/xattrs.c b/libntfs-3g/xattrs.c
|
||||
index 4e410de..97adb7d 100644
|
||||
--- a/libntfs-3g/xattrs.c
|
||||
+++ b/libntfs-3g/xattrs.c
|
||||
@@ -75,7 +75,7 @@ struct LE_POSIX_ACL {
|
||||
u8 version;
|
||||
u8 flags;
|
||||
le16 filler;
|
||||
- struct LE_POSIX_ACE ace[0];
|
||||
+ struct LE_POSIX_ACE ace[1];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
/* Small CRT/POSIX compatibility surface used by libntfs-3g on NTLite. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <ntlite/ddk/memory.h>
|
||||
#include <ntlite/ddk/time.h>
|
||||
#include <ntlite/ddk/debug.h>
|
||||
|
||||
static int gNtfs3gErrno;
|
||||
int _fltused; /* MSVC floating-point helper anchor required by printf.obj */
|
||||
|
||||
int* __cdecl _errno(void) { return &gNtfs3gErrno; }
|
||||
|
||||
static FILE gNtfs3gStderr;
|
||||
static FILE gNtfs3gStdout;
|
||||
static FILE gNtfs3gStdin;
|
||||
FILE* stderr = &gNtfs3gStderr;
|
||||
FILE* stdout = &gNtfs3gStdout;
|
||||
FILE* stdin = &gNtfs3gStdin;
|
||||
|
||||
void* malloc(size_t size) {
|
||||
if (!size) size = 1;
|
||||
return MmAllocatePool(size);
|
||||
}
|
||||
|
||||
void* calloc(size_t count, size_t size) {
|
||||
if (count && size > SIZE_MAX / count) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
size_t total = count * size;
|
||||
if (!total) total = 1;
|
||||
void* p = MmAllocatePool(total);
|
||||
if (!p) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
memset(p, 0, total);
|
||||
return p;
|
||||
}
|
||||
|
||||
void* realloc(void* ptr, size_t size) {
|
||||
if (!ptr) return malloc(size);
|
||||
if (!size) {
|
||||
MmFreePool(ptr);
|
||||
return NULL;
|
||||
}
|
||||
void* p = MmReallocatePool(ptr, size);
|
||||
if (!p) errno = ENOMEM;
|
||||
return p;
|
||||
}
|
||||
|
||||
void free(void* ptr) {
|
||||
if (ptr) MmFreePool(ptr);
|
||||
}
|
||||
|
||||
char* strdup(const char* text) {
|
||||
if (!text) return NULL;
|
||||
size_t n = strlen(text) + 1;
|
||||
char* p = (char*)malloc(n);
|
||||
if (p) memcpy(p, text, n);
|
||||
return p;
|
||||
}
|
||||
|
||||
char* strndup(const char* text, size_t limit) {
|
||||
if (!text) return NULL;
|
||||
size_t n = 0;
|
||||
while (n < limit && text[n]) ++n;
|
||||
char* p = (char*)malloc(n + 1);
|
||||
if (!p) return NULL;
|
||||
memcpy(p, text, n);
|
||||
p[n] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
void qsort(void* base, size_t count, size_t size,
|
||||
int (*compare)(const void*, const void*)) {
|
||||
if (!base || !compare || size == 0 || count < 2) return;
|
||||
unsigned char* bytes = (unsigned char*)base;
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
size_t j = i;
|
||||
while (j) {
|
||||
unsigned char* a = bytes + (j - 1) * size;
|
||||
unsigned char* b = bytes + j * size;
|
||||
if (compare(a, b) <= 0) break;
|
||||
for (size_t k = 0; k < size; ++k) {
|
||||
unsigned char t = a[k]; a[k] = b[k]; b[k] = t;
|
||||
}
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* bsearch(const void* key, const void* base, size_t count, size_t size,
|
||||
int (*compare)(const void*, const void*)) {
|
||||
if (!key || !base || !compare || !size) return NULL;
|
||||
size_t lo = 0, hi = count;
|
||||
const unsigned char* bytes = (const unsigned char*)base;
|
||||
while (lo < hi) {
|
||||
size_t mid = lo + (hi - lo) / 2;
|
||||
const void* item = bytes + mid * size;
|
||||
int c = compare(key, item);
|
||||
if (c == 0) return (void*)item;
|
||||
if (c < 0) hi = mid; else lo = mid + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int abs(int value) { return value < 0 ? -value : value; }
|
||||
long labs(long value) { return value < 0 ? -value : value; }
|
||||
long long llabs(long long value) { return value < 0 ? -value : value; }
|
||||
|
||||
static int Ntfs3gDigit(int c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned long long Ntfs3gParseUnsigned(const char* text, char** end, int base, bool* negative) {
|
||||
const char* p = text;
|
||||
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') ++p;
|
||||
*negative = false;
|
||||
if (*p == '+' || *p == '-') { *negative = (*p == '-'); ++p; }
|
||||
if (base == 0) {
|
||||
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { base = 16; p += 2; }
|
||||
else if (p[0] == '0') base = 8;
|
||||
else base = 10;
|
||||
} else if (base == 16 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
|
||||
p += 2;
|
||||
}
|
||||
unsigned long long value = 0;
|
||||
const char* first = p;
|
||||
for (;;) {
|
||||
int d = Ntfs3gDigit((unsigned char)*p);
|
||||
if (d < 0 || d >= base) break;
|
||||
value = value * (unsigned)base + (unsigned)d;
|
||||
++p;
|
||||
}
|
||||
if (end) *end = (char*)(p == first ? text : p);
|
||||
return value;
|
||||
}
|
||||
|
||||
long strtol(const char* text, char** end, int base) {
|
||||
bool neg; unsigned long long v = Ntfs3gParseUnsigned(text, end, base, &neg);
|
||||
return neg ? -(long)v : (long)v;
|
||||
}
|
||||
unsigned long strtoul(const char* text, char** end, int base) {
|
||||
bool neg; unsigned long long v = Ntfs3gParseUnsigned(text, end, base, &neg);
|
||||
return neg ? (unsigned long)(0 - (unsigned long)v) : (unsigned long)v;
|
||||
}
|
||||
long long strtoll(const char* text, char** end, int base) {
|
||||
bool neg; unsigned long long v = Ntfs3gParseUnsigned(text, end, base, &neg);
|
||||
return neg ? -(long long)v : (long long)v;
|
||||
}
|
||||
unsigned long long strtoull(const char* text, char** end, int base) {
|
||||
bool neg; unsigned long long v = Ntfs3gParseUnsigned(text, end, base, &neg);
|
||||
return neg ? 0ull - v : v;
|
||||
}
|
||||
|
||||
char* strerror(int error) {
|
||||
switch (error) {
|
||||
case 0: return "success";
|
||||
case EPERM: return "operation not permitted";
|
||||
case ENOENT: return "no such file or directory";
|
||||
case EIO: return "I/O error";
|
||||
case ENOMEM: return "out of memory";
|
||||
case EACCES: return "permission denied";
|
||||
case EBUSY: return "device or resource busy";
|
||||
case EEXIST: return "file exists";
|
||||
case ENODEV: return "no such device";
|
||||
case ENOTDIR: return "not a directory";
|
||||
case EISDIR: return "is a directory";
|
||||
case EINVAL: return "invalid argument";
|
||||
case ENOSPC: return "no space left on device";
|
||||
case EROFS: return "read-only filesystem";
|
||||
case ENOTEMPTY: return "directory not empty";
|
||||
case EOVERFLOW: return "value too large";
|
||||
case ENOTSUP: return "operation not supported";
|
||||
default: return "ntfs-3g error";
|
||||
}
|
||||
}
|
||||
|
||||
char* getenv(const char* name) { (void)name; return NULL; }
|
||||
char* __cdecl setlocale(int category, const char* locale) { (void)category; (void)locale; return "C"; }
|
||||
|
||||
uid_t getuid(void) { return 0; }
|
||||
uid_t geteuid(void) { return 0; }
|
||||
gid_t getgid(void) { return 0; }
|
||||
gid_t getegid(void) { return 0; }
|
||||
int fsync(int fd) { (void)fd; return 0; }
|
||||
|
||||
int strcasecmp(const char* a, const char* b) {
|
||||
while (*a && *b) {
|
||||
unsigned char ca = (unsigned char)*a++, cb = (unsigned char)*b++;
|
||||
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
|
||||
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
|
||||
if (ca != cb) return (int)ca - (int)cb;
|
||||
}
|
||||
return (unsigned char)*a - (unsigned char)*b;
|
||||
}
|
||||
|
||||
int strncasecmp(const char* a, const char* b, size_t count) {
|
||||
while (count--) {
|
||||
unsigned char ca = (unsigned char)*a++, cb = (unsigned char)*b++;
|
||||
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
|
||||
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
|
||||
if (ca != cb || !ca || !cb) return (int)ca - (int)cb;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t __cdecl time(time_t* out) {
|
||||
time_t now = (time_t)KeQueryUnixTimeSeconds();
|
||||
if (out) *out = now;
|
||||
return now;
|
||||
}
|
||||
|
||||
static int Ntfs3gLogV(const char* format, va_list args) {
|
||||
char buffer[512];
|
||||
int n = vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
DbgPrint("ntfs3g: %s", buffer);
|
||||
return n;
|
||||
}
|
||||
|
||||
int vfprintf(FILE* stream, const char* format, va_list args) {
|
||||
(void)stream;
|
||||
return Ntfs3gLogV(format, args);
|
||||
}
|
||||
|
||||
int fprintf(FILE* stream, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int n = vfprintf(stream, format, args);
|
||||
va_end(args);
|
||||
return n;
|
||||
}
|
||||
|
||||
int fflush(FILE* stream) { (void)stream; return 0; }
|
||||
|
||||
int fputs(const char* text, FILE* stream) {
|
||||
(void)stream;
|
||||
if (!text) return -1;
|
||||
DbgPrint("ntfs3g: %s", text);
|
||||
return (int)strlen(text);
|
||||
}
|
||||
|
||||
int puts(const char* text) {
|
||||
int n = fputs(text, stdout);
|
||||
DbgPrint("\n");
|
||||
return n;
|
||||
}
|
||||
|
||||
void abort(void) {
|
||||
DbgPrint("ntfs3g: abort()\n");
|
||||
for (;;) { }
|
||||
}
|
||||
|
||||
|
||||
/* Minimal process/account/syslog compatibility used by libntfs-3g internals.
|
||||
* The kernel driver never intentionally executes the daemon/process paths. */
|
||||
struct passwd;
|
||||
struct group;
|
||||
struct passwd* getpwnam(const char* name) { (void)name; return (struct passwd*)0; }
|
||||
struct passwd* getpwuid(uid_t uid) { (void)uid; return (struct passwd*)0; }
|
||||
struct group* getgrnam(const char* name) { (void)name; return (struct group*)0; }
|
||||
struct group* getgrgid(gid_t gid) { (void)gid; return (struct group*)0; }
|
||||
int fork(void) { errno = ENOSYS; return -1; }
|
||||
void _exit(int status) { (void)status; abort(); }
|
||||
pid_t setsid(void) { errno = ENOSYS; return (pid_t)-1; }
|
||||
int chdir(const char* path) { (void)path; errno = ENOSYS; return -1; }
|
||||
int open(const char* path, int flags, ...) { (void)path; (void)flags; errno = ENOSYS; return -1; }
|
||||
int dup2(int oldfd, int newfd) { (void)oldfd; (void)newfd; errno = ENOSYS; return -1; }
|
||||
int close(int fd) { (void)fd; errno = ENOSYS; return -1; }
|
||||
void openlog(const char* ident, int option, int facility) { (void)ident; (void)option; (void)facility; }
|
||||
void closelog(void) {}
|
||||
void vsyslog(int priority, const char* format, va_list args) {
|
||||
(void)priority;
|
||||
char buffer[512];
|
||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
DbgPrint("ntfs3g: %s", buffer);
|
||||
}
|
||||
void syslog(int priority, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsyslog(priority, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static unsigned long ntfs3g_rand_state = 1;
|
||||
void srandom(unsigned int seed) { ntfs3g_rand_state = seed ? seed : 1; }
|
||||
long random(void) { ntfs3g_rand_state = ntfs3g_rand_state * 1103515245u + 12345u; return (long)((ntfs3g_rand_state >> 1) & 0x7fffffff); }
|
||||
pid_t getpid(void) { return 1; }
|
||||
ssize_t read(int fd, void* buffer, size_t count) { (void)fd; (void)buffer; (void)count; errno = ENOSYS; return -1; }
|
||||
|
||||
int atoi(const char* text) { return (int)strtol(text, (char**)0, 10); }
|
||||
@@ -0,0 +1,36 @@
|
||||
# NTLite port + upstream libntfs-3g. Run vendor_ntfs3g.ps1 first.
|
||||
drivers/fs/ntfs/ntfs3g/ntfs3g_port.c
|
||||
drivers/fs/ntfs/ntfs3g/ntfs3g_device.c
|
||||
drivers/fs/ntfs/ntfs3g/ntfs3g_driver.c
|
||||
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/acls.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/attrib.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/attrlist.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/bitmap.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/bootsect.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/cache.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/collate.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/compat.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/compress.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/debug.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/device.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/dir.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/ea.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/efs.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/index.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/inode.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/ioctl.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/lcnalloc.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/logfile.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/logging.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/mft.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/misc.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/mst.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/object_id.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/realpath.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/reparse.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/runlist.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/security.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/unistr.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/volume.c
|
||||
drivers/fs/ntfs/ntfs3g/third_party/ntfs-3g/libntfs-3g/xattrs.c
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
Present authors of ntfs-3g in alphabetical order:
|
||||
|
||||
Jean-Pierre Andre
|
||||
Alon Bar-Lev
|
||||
Martin Bene
|
||||
Dominique L Bouix
|
||||
Csaba Henk
|
||||
Bernhard Kaindl
|
||||
Erik Larsson
|
||||
Alejandro Pulver
|
||||
Szabolcs Szakacsits
|
||||
Miklos Szeredi
|
||||
|
||||
|
||||
Past authors in alphabetical order:
|
||||
|
||||
Anton Altaparmakov
|
||||
Mario Emmenlauer
|
||||
Yuval Fledel
|
||||
Yura Pakhuchiy
|
||||
Richard Russon
|
||||
|
||||
Vendored
+21
-20
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -303,16 +303,17 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
@@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,482 @@
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the source code distributed need not include anything that is normally
|
||||
distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -0,0 +1,61 @@
|
||||
The following people have contributed directly or indirectly
|
||||
to the ntfs-3g project.
|
||||
|
||||
Please let ntfs-3g-devel@lists.sf.net know if you believe
|
||||
someone is missing, or if you prefer not to be listed.
|
||||
|
||||
Dominique L Bouix
|
||||
Csaba Henk
|
||||
Max Khon
|
||||
Auri Hautamäki
|
||||
Gergely Erdelyi
|
||||
Anton Altaparmakov
|
||||
Peter Boross
|
||||
Don Bright
|
||||
Mario Emmenlauer
|
||||
Yuval Fledel
|
||||
Kano from Kanotix
|
||||
Roland Kletzing
|
||||
Maarten Lankhorst
|
||||
Gergely Madarasz
|
||||
Patrick McLean
|
||||
Florent Mertens
|
||||
Yura Pakhuchiy
|
||||
Miklos Szeredi
|
||||
Bartosz Taudul
|
||||
Zhanglinbao
|
||||
Wade Fitzpatrick
|
||||
Carsten Einig
|
||||
Adam Cecile
|
||||
Bruno Damour
|
||||
Ales Fruman
|
||||
Curt McDowell
|
||||
Thomas Franken
|
||||
Jonatan Lambert
|
||||
Klaus Knopper
|
||||
Zhanglinbao
|
||||
Ismail Donmez
|
||||
Laszlo Dvornik
|
||||
Pallaghy Ajtony
|
||||
Szabolcs Szakacsits
|
||||
Alexei Alexandrov
|
||||
Albert D. Cahalan
|
||||
Russ Christensen
|
||||
Pete Curran
|
||||
Andras Erdei
|
||||
Matthew J. Fanto
|
||||
Marcin Gibuła
|
||||
Christophe Grenier
|
||||
Ian Jackson
|
||||
Carmelo Kintana
|
||||
Jan Kratochvil
|
||||
Lode Leroy
|
||||
David MartÃnez Moreno
|
||||
Giang Nguyen
|
||||
Leonard Norrgård
|
||||
Holger Ohmacht
|
||||
Per Olofsson
|
||||
Yuri Per
|
||||
Richard Russon
|
||||
Erik Sørnes
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ChangeLog can be found at :
|
||||
|
||||
https://github.com/tuxera/ntfs-3g/wiki
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
AUTOMAKE_OPTIONS = gnu
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
EXTRA_DIST = AUTHORS CREDITS COPYING NEWS autogen.sh
|
||||
|
||||
MAINTAINERCLEANFILES=\
|
||||
$(srcdir)/configure \
|
||||
$(srcdir)/Makefile.in \
|
||||
$(srcdir)/aclocal.m4 \
|
||||
$(srcdir)/compile \
|
||||
$(srcdir)/depcomp \
|
||||
$(srcdir)/install-sh \
|
||||
$(srcdir)/ltmain.sh \
|
||||
$(srcdir)/missing \
|
||||
$(srcdir)/config.guess \
|
||||
$(srcdir)/config.sub \
|
||||
$(srcdir)/config.h.in \
|
||||
$(srcdir)/config.h.in~ \
|
||||
$(srcdir)/INSTALL \
|
||||
$(srcdir)/m4/ltsugar.m4 \
|
||||
$(srcdir)/m4/libtool.m4 \
|
||||
$(srcdir)/m4/ltversion.m4 \
|
||||
$(srcdir)/m4/lt~obsolete.m4 \
|
||||
$(srcdir)/m4/ltoptions.m4
|
||||
|
||||
SUBDIRS = include libfuse-lite libntfs-3g ntfsprogs src
|
||||
|
||||
doc_DATA = README
|
||||
|
||||
dist-hook:
|
||||
$(MKDIR_P) "$(distdir)/m4"
|
||||
|
||||
libtool: $(LIBTOOL_DEPS)
|
||||
$(SHELL) ./config.status --recheck
|
||||
|
||||
libs:
|
||||
if FUSE_INTERNAL
|
||||
(cd libfuse-lite && $(MAKE) libs) || exit 1;
|
||||
endif
|
||||
(cd libntfs-3g && $(MAKE) libs) || exit 1;
|
||||
|
||||
libntfs:
|
||||
(cd libntfs-3g && $(MAKE) libs) || exit 1;
|
||||
|
||||
drivers: libs
|
||||
(cd src && $(MAKE) drivers) || exit 1;
|
||||
|
||||
ntfsprogs: libntfs
|
||||
(cd ntfsprogs && $(MAKE)) || exit 1;
|
||||
|
||||
if ENABLE_NTFSPROGS
|
||||
strip:
|
||||
(cd ntfsprogs && $(MAKE) strip) || exit 1;
|
||||
|
||||
extra: extras
|
||||
|
||||
extras: libs
|
||||
(cd ntfsprogs && $(MAKE) extras) || exit 1;
|
||||
endif
|
||||
@@ -0,0 +1,3 @@
|
||||
Project information can be found at :
|
||||
|
||||
https://github.com/tuxera/ntfs-3g/
|
||||
@@ -0,0 +1,166 @@
|
||||
|
||||
INTRODUCTION
|
||||
============
|
||||
|
||||
The NTFS-3G driver is an open source, freely available read/write NTFS driver
|
||||
for Linux, FreeBSD, macOS, NetBSD, OpenIndiana, QNX and Haiku. It provides
|
||||
safe and fast handling of the Windows XP, Windows Server 2003, Windows 2000,
|
||||
Windows Vista, Windows Server 2008, Windows 7, Windows 8, Windows Server 2012,
|
||||
Windows Server 2016, Windows 10 and Windows Server 2019 NTFS file systems.
|
||||
|
||||
The purpose of the project is to develop, quality assurance and support a
|
||||
trustable, featureful and high performance solution for hardware platforms
|
||||
and operating systems whose users need to reliably interoperate with NTFS.
|
||||
Besides this practical goal, the project also aims to explore the limits
|
||||
of the hybrid, kernel/user space filesystem driver approach, performance,
|
||||
reliability and feature richness per invested effort wise.
|
||||
|
||||
Besides the common file system features, NTFS-3G has support for file
|
||||
ownership and permissions, POSIX ACLs, junction points, extended attributes
|
||||
and creating internally compressed files (parameter files in the directory
|
||||
.NTFS-3G may be required to enable them). The new compressed file formats
|
||||
available in Windows 10 can also be read through a plugin.
|
||||
|
||||
News, support answers, problem submission instructions, support and discussion
|
||||
forums, and other information are available on the project web site at
|
||||
|
||||
https://github.com/tuxera/ntfs-3g/wiki
|
||||
|
||||
The project has been funded, supported and maintained since 2008 by Tuxera:
|
||||
|
||||
https://tuxera.com
|
||||
|
||||
|
||||
LICENSES
|
||||
========
|
||||
|
||||
All the NTFS related components: the file system drivers, the ntfsprogs
|
||||
utilities and the shared library libntfs-3g are distributed under the terms
|
||||
of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version. See the included file COPYING.
|
||||
|
||||
The fuse-lite library is distributed under the terms of the GNU LGPLv2.
|
||||
See the included file COPYING.LIB.
|
||||
|
||||
|
||||
QUICK INSTALLATION
|
||||
==================
|
||||
|
||||
Most distributions have an up-to-date NTFS-3G package ready for use, and
|
||||
the recommended way is to install it.
|
||||
|
||||
If you need some specific customization, you can compile and install from
|
||||
the released source code. Make sure you have the basic development tools
|
||||
and the kernel includes the FUSE kernel module. Then unpack the source
|
||||
tarball and type:
|
||||
|
||||
./configure
|
||||
make
|
||||
make install # or 'sudo make install' if you aren't root.
|
||||
|
||||
Please note that NTFS-3G doesn't require the FUSE user space package any
|
||||
more.
|
||||
|
||||
The list of options for building specific configurations is displayed by
|
||||
typing :
|
||||
|
||||
./configure --help
|
||||
|
||||
Below are a few specific options to ./configure :
|
||||
--disable-ntfsprogs : do not build the ntfsprogs tools,
|
||||
--enable-extras : build more ntfsprogs tools,
|
||||
--disable-plugins : disable support for plugins
|
||||
--enable-posix-acls : enable support for Posix ACLs
|
||||
--enable-xattr-mappings : enable system extended attributes mappings
|
||||
--with-fuse=external : use external fuse (overriding Linux default)
|
||||
|
||||
There are also a few make targets for building parts :
|
||||
make libntfs : only build the libntfs-3g library
|
||||
make libs : only build libntfs-3g (and libfuse-lite, if relevant)
|
||||
make drivers : only build drivers and libraries, without ntfsprogs
|
||||
make ntfsprogs : only build ntfsprogs and libntfs-3g, without drivers
|
||||
|
||||
|
||||
USAGE
|
||||
=====
|
||||
|
||||
If there was no error during installation then the NTFS volume can be
|
||||
read-write mounted for everybody the following way as the root user
|
||||
(unmount the volume if it was already mounted, and replace /dev/sda1
|
||||
and /mnt/windows, if needed):
|
||||
|
||||
mount -t ntfs-3g /dev/sda1 /mnt/windows
|
||||
or
|
||||
ntfs-3g /dev/sda1 /mnt/windows
|
||||
|
||||
Please see the ntfs-3g manual page for more options and examples.
|
||||
|
||||
You can also make NTFS to be mounted during boot by putting the below
|
||||
line at the END(!) of the /etc/fstab file:
|
||||
|
||||
/dev/sda1 /mnt/windows ntfs-3g defaults 0 0
|
||||
|
||||
|
||||
TESTING WITHOUT INSTALLING
|
||||
=========================
|
||||
|
||||
Newer versions of ntfs-3g can be tested without installing anything and
|
||||
without disturbing an existing installation. Just configure and make as
|
||||
shown previously. This will create the scripts ntfs-3g and lowntfs-3g
|
||||
in the src directory, which you may activate for testing:
|
||||
|
||||
./configure
|
||||
make
|
||||
|
||||
then, as root:
|
||||
src/ntfs-3g [-o mount-options] /dev/sda1 /mnt/windows
|
||||
|
||||
And, to end the test, unmount the usual way:
|
||||
umount /dev/sda1
|
||||
|
||||
|
||||
NTFS UTILITIES
|
||||
==============
|
||||
|
||||
The ntfsprogs directory includes utilities for doing all required tasks to
|
||||
NTFS partitions. In general, just run a utility without any command line
|
||||
options to display the version number and usage syntax.
|
||||
|
||||
The following utilities are so far implemented:
|
||||
|
||||
ntfsfix - Attempt to fix an NTFS partition and force Windows to check NTFS.
|
||||
|
||||
mkntfs - Format a partition with the NTFS filesystem. See man 8 mkntfs for
|
||||
command line options.
|
||||
|
||||
ntfslabel - Display/change the label of an NTFS partition. See man 8 ntfslabel
|
||||
for details.
|
||||
|
||||
ntfsundelete - Recover deleted files from an NTFS volume. See man 8
|
||||
ntfsundelete for more details.
|
||||
|
||||
ntfsresize - Resize NTFS volumes. See man 8 ntfsresize for details.
|
||||
|
||||
ntfsclone - Efficiently create/restore an image of an NTFS partition. See
|
||||
man 8 ntfsclone for details.
|
||||
|
||||
ntfscluster - Locate the owner of any given sector or cluster on an NTFS
|
||||
partition. See man 8 ntfscluster for details.
|
||||
|
||||
ntfsinfo - Show some information about an NTFS partition or one of the files
|
||||
or directories within it. See man 8 ntfsinfo for details.
|
||||
|
||||
ntfsrecover - Recover updates committed by Windows but interrupted before
|
||||
being synced.
|
||||
|
||||
ntfsls - List information about files in a directory residing on an NTFS
|
||||
partition. See man 8 ntfsls for details.
|
||||
|
||||
ntfscat - Concatenate files and print their contents on the standard output.
|
||||
|
||||
ntfscp - Overwrite files on an NTFS partition.
|
||||
|
||||
ntfssecaudit - Audit the security metadata.
|
||||
|
||||
ntfsusermap - Assistance for building a user mapping file.
|
||||
@@ -0,0 +1,126 @@
|
||||
Please keep in alphabetical order so utilities are easier to find.
|
||||
|
||||
Thanks,
|
||||
Anton
|
||||
|
||||
|
||||
**********
|
||||
* mkntfs *
|
||||
**********
|
||||
|
||||
- Correct support for creating volumes with larger sector sizes (mft record
|
||||
size, cluster size, and index block size must be >= sector size), so for 1k,
|
||||
2k, and 4k sectors, we need to set the default mft record, cluster, and index
|
||||
block size to be at least the sector size.
|
||||
- Correct the odd last partition sector not being accessible under 2.4 kernels
|
||||
by setting the device block size to the sector size (default is 1k on 2.4
|
||||
kernels and they can't cope with partial sectors).
|
||||
- Got a report that creating a floppy with mkntfs failed. Difference between
|
||||
this floppy and the floppy created by the special tool found on the net was
|
||||
said to be that the bitmap is 256kib on the special floppy while mkntfs will
|
||||
make it much smaller. Need to verify this and experiment with the bitmap
|
||||
size to make it work. Note, reporter was using win2k.
|
||||
|
||||
|
||||
*************
|
||||
* ntfsclone *
|
||||
*************
|
||||
|
||||
- get rid of the unneeded lseek()'s during reads/writes (probably it
|
||||
doesn't improve performance much, or any at all)
|
||||
- catch if source and dest are the same
|
||||
- disable consistency check for --metadata (e.g. if the check is crashing)
|
||||
- option: --inode
|
||||
- option: --data
|
||||
- metadata cloning: skip more non-needed inodes
|
||||
- manual: document LFS issues (smbfs' lfs option, nfs)
|
||||
- manual: mention optimized seeks
|
||||
- manual: optimal backup if disks have bad sectors
|
||||
- manual: ntfsclone guarantees the restored image works only
|
||||
if one restores to the exactly same partition. For example,
|
||||
one can not copy system partition to a different partition:
|
||||
minimum "hidden sectors" field and BOOT.INI need modifications.
|
||||
We could do these adjustments optionally.
|
||||
- check if kernel block size = GCD(page size, device size) makes
|
||||
effect on performance (Al Viro says no)
|
||||
- check whether the O_WRONLY -> O_RDWR change made effect on performance
|
||||
|
||||
|
||||
***********
|
||||
* ntfscmp *
|
||||
***********
|
||||
|
||||
- compare mft record headers
|
||||
- exit status is 0 if inputs are the same, 1 if different, other if trouble
|
||||
- optionally ignore less interesting fields (e.g. attribute instance)
|
||||
- new option: --metadata mode
|
||||
- unnamed resident attributes with same type are ignored
|
||||
- code cleanup, remove many cross-util duplicates
|
||||
- handle deleted records
|
||||
- performance: special handling for sparse files
|
||||
|
||||
|
||||
**********
|
||||
* ntfscp *
|
||||
**********
|
||||
|
||||
- add ability to copy multiple files at once.
|
||||
|
||||
|
||||
***********
|
||||
* ntfsfix *
|
||||
***********
|
||||
|
||||
- Cleanup to use ntfs_attr_* API for editing $MFTMirr, $Volume, and $LogFile.
|
||||
This has the immediate benefit of enabling attribute list support and making
|
||||
the code simpler.
|
||||
- On ntfs 3.0+ volumes need to disable the usn journal if it is active. This
|
||||
means deleting file $UsnJrnl from /$Extend directory.
|
||||
- On ntfs 3.0+ volumes need to mark the quota out of date? - Probably, but
|
||||
it shouldn't cause any corruption not doing so for the moment so this is
|
||||
not a showstopper bug for the first release. (AIA)
|
||||
|
||||
|
||||
*************
|
||||
* ntfslabel *
|
||||
*************
|
||||
|
||||
- Support ioctls for kernel driver and ntfsmount for reading/changing the label.
|
||||
|
||||
|
||||
*************
|
||||
* ntfsmount *
|
||||
*************
|
||||
|
||||
|
||||
|
||||
**************
|
||||
* ntfsresize *
|
||||
**************
|
||||
|
||||
High priority
|
||||
- move ntfs consistency check to libntfs (for ntfsck, ntfsclone, etc)
|
||||
- use different exit codes (e.g. corrupt volume detected, unsupported case,
|
||||
bad sectors, etc)
|
||||
|
||||
Medium priority
|
||||
- cope with the rare, unsupported cases, see man ntfsresize 'KNOWN ISSUES'
|
||||
- save $Bitmap if it was modified and an error occures (e.g. bad sector).
|
||||
- handle signals (^C, etc)
|
||||
|
||||
Low priority
|
||||
- fully support disks with bad sectors (attrlist attr, unknown bad sectors)
|
||||
- move volume start
|
||||
|
||||
|
||||
****************
|
||||
* ntfsundelete *
|
||||
****************
|
||||
|
||||
- undelete by name rather than inode number
|
||||
- support for compressed files
|
||||
- support for internationalisation
|
||||
- recover by type?
|
||||
- mass undelete (using wildcards)
|
||||
- display parent directory
|
||||
- name "<none>" to MFTn
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
# Run this to generate configure, Makefile.in's, etc
|
||||
|
||||
(autoreconf --version) < /dev/null > /dev/null 2>&1 || {
|
||||
(autoconf --version) < /dev/null > /dev/null 2>&1 || {
|
||||
echo
|
||||
echo "**Error**: You must have the GNU Build System (autoconf, automake, "
|
||||
echo "libtool, etc) to update the ntfs-3g build system. Download the "
|
||||
echo "appropriate packages for your distribution, or get the source "
|
||||
echo "tar balls from ftp://ftp.gnu.org/pub/gnu/."
|
||||
exit 1
|
||||
}
|
||||
echo
|
||||
echo "**Error**: Your version of autoconf is too old (you need at least 2.57)"
|
||||
echo "to update the ntfs-3g build system. Download the appropriate "
|
||||
echo "updated package for your distribution, or get the source tar ball "
|
||||
echo "from ftp://ftp.gnu.org/pub/gnu/."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo Running autoreconf --verbose --install --force
|
||||
autoreconf --verbose --install --force
|
||||
@@ -0,0 +1,708 @@
|
||||
#
|
||||
# configure.ac - Source file to generate "./configure" to prepare package for
|
||||
# compilation.
|
||||
#
|
||||
# Copyright (c) 2000-2013 Anton Altaparmakov
|
||||
# Copyright (c) 2003 Jan Kratochvil
|
||||
# Copyright (c) 2005-2009 Szabolcs Szakacsits
|
||||
# Copyright (C) 2007-2008 Alon Bar-Lev
|
||||
#
|
||||
# This program/include file is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program/include file is distributed in the hope that it will be
|
||||
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program (in the main directory of the NTFS-3G
|
||||
# distribution in the file COPYING); if not, write to the Free Software
|
||||
# Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
# Autoconf
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT([ntfs-3g],[2026.7.7],[ntfs-3g-devel@lists.sf.net])
|
||||
LIBNTFS_3G_VERSION="90"
|
||||
AC_CONFIG_SRCDIR([src/ntfs-3g.c])
|
||||
|
||||
# Environment
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
# Automake
|
||||
AM_INIT_AUTOMAKE([])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
# Options
|
||||
AC_ARG_ENABLE(
|
||||
[debug],
|
||||
[AS_HELP_STRING([--enable-debug],[enable debugging code and output])],
|
||||
,
|
||||
[enable_debug="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[warnings],
|
||||
[AS_HELP_STRING([--enable-warnings],[enable lots of compiler warnings])],
|
||||
,
|
||||
[enable_warnings="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[pedantic],
|
||||
[AS_HELP_STRING([--enable-pedantic],[enable compile pedantic mode])],
|
||||
,
|
||||
[enable_pedantic="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[really-static],
|
||||
[AS_HELP_STRING([--enable-really-static],[create fully static binaries])],
|
||||
,
|
||||
[enable_really_static="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[mount-helper],
|
||||
[AS_HELP_STRING([--enable-mount-helper],[install mount helper @<:@default=enabled for linux@:>@])],
|
||||
,
|
||||
[
|
||||
case "${target_os}" in
|
||||
linux*) enable_mount_helper="yes" ;;
|
||||
*) enable_mount_helper="no" ;;
|
||||
esac
|
||||
]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[ldscript],
|
||||
[AS_HELP_STRING([--enable-ldscript],[use ldscript instead of .so symlink])],
|
||||
,
|
||||
[enable_ldscript="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[ldconfig],
|
||||
[AS_HELP_STRING([--disable-ldconfig],[do not update dynamic linker cache using ldconfig])],
|
||||
,
|
||||
[enable_ldconfig="yes"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[library],
|
||||
[AS_HELP_STRING([--disable-library],[do not install libntfs-3g but link it into ntfs-3g])],
|
||||
,
|
||||
[enable_library="yes"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[mtab],
|
||||
[AS_HELP_STRING([--disable-mtab],[disable and ignore usage of /etc/mtab])],
|
||||
,
|
||||
[enable_mtab="yes"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[posix-acls],
|
||||
[AS_HELP_STRING([--enable-posix-acls],[enable POSIX ACL support])],
|
||||
,
|
||||
[enable_posix_acls="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[xattr-mappings],
|
||||
[AS_HELP_STRING([--enable-xattr-mappings],[enable system extended attributes mappings])],
|
||||
,
|
||||
[enable_xattr_mappings="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[plugins],
|
||||
[AS_HELP_STRING([--disable-plugins], [Disable external reparse point
|
||||
plugins for the ntfs-3g FUSE driver])],
|
||||
[if test x${enableval} = "xyes"; then disable_plugins="no"; fi],
|
||||
[disable_plugins="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[device-default-io-ops],
|
||||
[AS_HELP_STRING([--disable-device-default-io-ops],[install default IO ops])],
|
||||
,
|
||||
[enable_device_default_io_ops="yes"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[ntfs-3g],
|
||||
[AS_HELP_STRING([--disable-ntfs-3g],[disable the ntfs-3g FUSE driver])],
|
||||
,
|
||||
[enable_ntfs_3g="yes"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[ntfsprogs],
|
||||
[AS_HELP_STRING([--disable-ntfsprogs],[disable ntfsprogs utilities
|
||||
(default=no)])],
|
||||
,
|
||||
[enable_ntfsprogs="yes"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(crypto,
|
||||
AS_HELP_STRING(--enable-crypto,enable crypto related code and utilities
|
||||
(default=no)), ,
|
||||
enable_crypto=no
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[extras],
|
||||
[AS_HELP_STRING([--enable-extras],[enable extra ntfsprogs utilities
|
||||
(default=no)])],
|
||||
,
|
||||
[enable_extras="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[quarantined],
|
||||
[AS_HELP_STRING([--enable-quarantined],[enable quarantined ntfsprogs utilities
|
||||
(default=no)])],
|
||||
,
|
||||
[enable_quarantined="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[nfconv],
|
||||
[AS_HELP_STRING([--disable-nfconv],[disable the 'nfconv' patch, which adds support for Unicode normalization form conversion when built on Mac OS X @<:@default=enabled for Mac OS X@:>@])],
|
||||
[enable_nfconv="no"],
|
||||
[
|
||||
case "${target_os}" in
|
||||
darwin*) enable_nfconv="yes" ;;
|
||||
*) enable_nfconv="no" ;;
|
||||
esac
|
||||
]
|
||||
)
|
||||
|
||||
# pthread_rwlock_t requires _GNU_SOURCE
|
||||
AC_GNU_SOURCE
|
||||
|
||||
# Programs
|
||||
AC_PROG_CC(gcc cc)
|
||||
AC_PROG_LN_S
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
ifdef(
|
||||
[LT_INIT],
|
||||
[LT_INIT],
|
||||
[AC_PROG_LIBTOOL]
|
||||
)
|
||||
|
||||
AC_PROG_INSTALL
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_PATH_PROG([MV], [mv])
|
||||
AC_PATH_PROG([RM], [rm])
|
||||
AC_PATH_PROG([SED], [sed])
|
||||
AC_ARG_VAR([LDCONFIG], [ldconfig utility])
|
||||
AC_PATH_PROG([LDCONFIG], [ldconfig], [true], [/sbin /usr/sbin $PATH])
|
||||
|
||||
# Environment
|
||||
AC_MSG_CHECKING([Windows OS])
|
||||
case "${target}" in
|
||||
*-mingw32*|*-winnt*|*-cygwin*)
|
||||
AC_MSG_RESULT([yes])
|
||||
WINDOWS="yes"
|
||||
AC_DEFINE(
|
||||
[WINDOWS],
|
||||
[1],
|
||||
[Define to 1 if this is a Windows OS]
|
||||
)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT([no])
|
||||
WINDOWS="no"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "x${enable_ntfs_3g}" != "xyes"; then
|
||||
with_fuse="none"
|
||||
elif test "x${with_fuse}" = "x"; then
|
||||
AC_MSG_CHECKING([fuse compatibility])
|
||||
case "${target_os}" in
|
||||
linux*|solaris*)
|
||||
AC_ARG_WITH(
|
||||
[fuse],
|
||||
[AS_HELP_STRING([--with-fuse=<internal|external>],[Select FUSE library: internal or external @<:@default=internal@:>@])],
|
||||
,
|
||||
[with_fuse="internal"]
|
||||
)
|
||||
;;
|
||||
darwin*|netbsd*|kfreebsd*-gnu)
|
||||
with_fuse="external"
|
||||
;;
|
||||
freebsd*)
|
||||
AC_MSG_ERROR([Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([ntfs-3g can be built for Linux, FreeBSD, Mac OS X, NetBSD, and Solaris only.])
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT([${with_fuse}])
|
||||
fi
|
||||
|
||||
case "${target_os}" in
|
||||
solaris*)
|
||||
if test "x$GCC" != "xyes" ; then
|
||||
AC_MSG_ERROR([ntfs-3g can be built only with gcc on Solaris. Install it by 'pkg install gcc-dev' and retry.)])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "${enable_ldscript}" = "yes"; then
|
||||
AC_MSG_CHECKING([Output format])
|
||||
OUTPUT_FORMAT="$(${CC} ${CFLAGS} ${LDFLAGS} -Wl,--verbose 2>&1 | ${SED} -n 's/^OUTPUT_FORMAT("\([[^"]]*\)",.*/\1/p')"
|
||||
if test -z "${OUTPUT_FORMAT}"; then
|
||||
AC_MSG_RESULT([None])
|
||||
else
|
||||
AC_MSG_RESULT([${OUTPUT_FORMAT}])
|
||||
OUTPUT_FORMAT="OUTPUT_FORMAT ( ${OUTPUT_FORMAT} )"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Libraries
|
||||
if test "${with_fuse}" = "internal"; then
|
||||
AC_CHECK_LIB(
|
||||
[pthread],
|
||||
[pthread_create],
|
||||
[LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lpthread"],
|
||||
[AC_MSG_ERROR([Cannot find pthread library])]
|
||||
)
|
||||
AC_DEFINE(
|
||||
[_REENTRANT],
|
||||
[1],
|
||||
[Required define if using POSIX threads]
|
||||
)
|
||||
# required so that we re-compile anything
|
||||
AC_DEFINE(
|
||||
[FUSE_INTERNAL],
|
||||
[1],
|
||||
[Define to 1 if using internal fuse]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING([Solaris OS])
|
||||
AC_LANG_PUSH([C])
|
||||
AC_COMPILE_IFELSE(
|
||||
[
|
||||
AC_LANG_SOURCE(
|
||||
[[#if !((defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__)))]]
|
||||
[[#error "Not a Solaris system."]]
|
||||
[[#endif]]
|
||||
)
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT([yes])
|
||||
LIBFUSE_LITE_CFLAGS="${LIBFUSE_LITE_CFLAGS} -std=c99 -D__SOLARIS__ -D_XOPEN_SOURCE=600 -D__EXTENSIONS__"
|
||||
LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lxnet"
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT([no])
|
||||
]
|
||||
)
|
||||
AC_LANG_POP([C])
|
||||
elif test "${with_fuse}" = "external"; then
|
||||
if test -z "$PKG_CONFIG"; then
|
||||
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
|
||||
fi
|
||||
test "x${PKG_CONFIG}" = "xno" && AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/])
|
||||
# Libraries often install their metadata .pc files in directories
|
||||
# not searched by pkg-config. Let's workaround this.
|
||||
export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/lib/pkgconfig:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig
|
||||
PKG_CHECK_MODULES(
|
||||
[FUSE_MODULE],
|
||||
[fuse >= 2.6.0],
|
||||
,
|
||||
[
|
||||
AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net])
|
||||
]
|
||||
)
|
||||
FUSE_LIB_PATH=`$PKG_CONFIG --libs-only-L fuse | sed -e 's,/[/]*,/,g' -e 's,[ ]*$,,'`
|
||||
fi
|
||||
|
||||
# Autodetect whether we can build crypto stuff or not.
|
||||
compile_crypto=false
|
||||
if test "$enable_crypto" != "no"; then
|
||||
have_libgcrypt=false
|
||||
AM_PATH_LIBGCRYPT(1.2.2, [ have_libgcrypt=true ],
|
||||
[
|
||||
if test "$enable_crypto" = "yes"; then
|
||||
AC_MSG_ERROR([ntfsprogs crypto code requires the gcrypt library.])
|
||||
else
|
||||
AC_MSG_WARN([ntfsprogs crypto code requires the gcrypt library.])
|
||||
fi
|
||||
])
|
||||
have_libgnutls=false
|
||||
PKG_CHECK_MODULES(GNUTLS, gnutls >= 1.4.4, [ have_libgnutls=true ],
|
||||
if test "$enable_crypto" = "yes"; then
|
||||
AC_MSG_ERROR([ntfsprogs crypto code requires the gnutls library.])
|
||||
else
|
||||
AC_MSG_WARN([ntfsprogs crypto code requires the gnutls library.])
|
||||
fi
|
||||
)
|
||||
if test "$have_libgcrypt" = "true"; then
|
||||
if test "$have_libgnutls" = "true"; then
|
||||
compile_crypto=true
|
||||
AC_DEFINE([ENABLE_CRYPTO], 1,
|
||||
[Define this to 1 if you want to enable support of
|
||||
encrypted files in libntfs and utilities.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_CRYPTO, $compile_crypto)
|
||||
|
||||
# add --with-extra-includes and --with-extra-libs switch to ./configure
|
||||
all_libraries="$all_libraries $USER_LDFLAGS"
|
||||
all_includes="$all_includes $USER_INCLUDES"
|
||||
AC_SUBST(all_includes)
|
||||
AC_SUBST(all_libraries)
|
||||
|
||||
# Specify support for generating DCE compliant UUIDs (aka GUIDs). We check if
|
||||
# uuid/uuid.h header is present and the uuid library is present that goes with
|
||||
# it and then check if uuid_generate() is present and usable.
|
||||
#
|
||||
# DCE UUIDs are enabled by default and can be disabled with the --disable-uuid
|
||||
# option to the configure script.
|
||||
AC_ARG_WITH(uuid, [
|
||||
--with-uuid@<:@=PFX@:>@ generate DCE compliant UUIDs, with optional prefix
|
||||
to uuid library and headers @<:@default=detect@:>@
|
||||
--without-uuid do not generate DCE compliant UUIDs],
|
||||
if test "$with_uuid" = "yes"; then
|
||||
extrapath=default
|
||||
elif test "$with_uuid" = "no"; then
|
||||
extrapath=
|
||||
else
|
||||
extrapath=$with_uuid
|
||||
fi,
|
||||
extrapath=default
|
||||
)
|
||||
if test "x$extrapath" != "x"; then
|
||||
if test "x$extrapath" != "xdefault"; then
|
||||
MKNTFS_CPPFLAGS="$MKNTFS_CPPFLAGS -I$extrapath/include"
|
||||
MKNTFS_LIBS="$MKNTFS_LIBS -L$extrapath/lib"
|
||||
fi
|
||||
|
||||
search_for_luuid="yes"
|
||||
AC_CHECK_HEADER([uuid/uuid.h],
|
||||
[],
|
||||
[
|
||||
AC_MSG_WARN([ntfsprogs DCE compliant UUID generation code requires the uuid library.])
|
||||
search_for_luuid="no"
|
||||
],
|
||||
)
|
||||
|
||||
if test "x$search_for_luuid" != "xno"; then
|
||||
# Look for uuid_generate in the standard C library.
|
||||
AC_CHECK_FUNC([uuid_generate],
|
||||
[
|
||||
AC_DEFINE([ENABLE_UUID], 1,
|
||||
[Define this to 1 if you want to enable
|
||||
generation of DCE compliant UUIDs.])
|
||||
search_for_luuid="no"
|
||||
],
|
||||
[],
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$search_for_luuid" != "xno"; then
|
||||
# Look for uuid_generate in the 'uuid' library.
|
||||
AC_CHECK_LIB([uuid], [uuid_generate],
|
||||
[
|
||||
AC_DEFINE([ENABLE_UUID], 1,
|
||||
[Define this to 1 if you want to enable
|
||||
generation of DCE compliant UUIDs.])
|
||||
MKNTFS_LIBS="$MKNTFS_LIBS -luuid"
|
||||
search_for_luuid="no"
|
||||
],
|
||||
[],
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$search_for_luuid" != "xno"; then
|
||||
AC_MSG_WARN([ntfsprogs DCE compliant UUID generation code requires the uuid library.])
|
||||
fi
|
||||
fi
|
||||
|
||||
# Specify support for obtaining the correct BIOS legacy geometry needed for
|
||||
# Windows to boot in CHS mode. We check if hd.h header is present and the hd
|
||||
# library is present that goes with it and then check if the hd_list() function
|
||||
# is present and usable.
|
||||
#
|
||||
# Using the hd library is enabled by default and can be disabled with the
|
||||
# --disable-hd option to the configure script.
|
||||
AC_ARG_WITH(hd, [
|
||||
--with-hd@<:@=PFX@:>@ use Windows compliant disk geometry, with optional
|
||||
prefix to hd library and headers @<:@default=detect@:>@
|
||||
--without-hd do not use Windows compliant disk geometry],
|
||||
if test "$with_hd" = "yes"; then
|
||||
extrapath2=default
|
||||
elif test "$with_hd" = "no"; then
|
||||
extrapath2=
|
||||
else
|
||||
extrapath2=$with_hd
|
||||
fi,
|
||||
extrapath2=default
|
||||
)
|
||||
if test "x$extrapath2" != "x"; then
|
||||
if test "x$extrapath2" != "xdefault"; then
|
||||
LIBNTFS_CPPFLAGS="$LIBNTFS_CPPFLAGS -I$extrapath2/include"
|
||||
LIBNTFS_LIBS="$LIBNTFS_LIBS -L$extrapath2/lib"
|
||||
fi
|
||||
AC_CHECK_HEADER([hd.h],
|
||||
AC_CHECK_LIB([hd], [hd_list],
|
||||
AC_DEFINE([ENABLE_HD], 1,
|
||||
[Define this to 1 if you want to enable use of Windows
|
||||
compliant disk geometry.])
|
||||
LIBNTFS_LIBS="$LIBNTFS_LIBS -lhd"
|
||||
NTFSPROGS_STATIC_LIBS="$NTFSPROGS_STATIC_LIBS -lhd",
|
||||
AC_MSG_WARN([ntfsprogs Windows compliant geometry code requires the hd library.]),
|
||||
),
|
||||
AC_MSG_WARN([ntfsprogs Windows compliant geometry code requires the hd library.]),
|
||||
)
|
||||
fi
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_HEADER_MAJOR
|
||||
AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \
|
||||
mntent.h stddef.h stdint.h stdlib.h stdio.h stdarg.h string.h \
|
||||
strings.h errno.h time.h unistd.h utime.h wchar.h getopt.h features.h \
|
||||
regex.h endian.h byteswap.h sys/byteorder.h sys/disk.h sys/endian.h \
|
||||
sys/param.h sys/ioctl.h sys/mount.h sys/stat.h sys/types.h \
|
||||
sys/vfs.h sys/statvfs.h linux/major.h linux/fd.h \
|
||||
linux/fs.h inttypes.h linux/hdreg.h \
|
||||
machine/endian.h windows.h syslog.h pwd.h malloc.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_HEADER_STDBOOL
|
||||
AC_C_BIGENDIAN(
|
||||
,
|
||||
[
|
||||
AC_DEFINE(
|
||||
[WORDS_LITTLEENDIAN],
|
||||
[1],
|
||||
[Define to 1 if your processor stores words with the least significant
|
||||
byte first (like Intel and VAX, unlike Motorola and SPARC).]
|
||||
)
|
||||
]
|
||||
,
|
||||
)
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_STRUCT_ST_BLOCKS
|
||||
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
||||
AC_CHECK_MEMBERS([struct stat.st_atim])
|
||||
AC_CHECK_MEMBERS([struct stat.st_atimespec])
|
||||
AC_CHECK_MEMBERS([struct stat.st_atimensec])
|
||||
|
||||
# For the 'nfconv' patch (Mac OS X only):
|
||||
case "${target_os}" in
|
||||
darwin*)
|
||||
if test "${enable_nfconv}" = "yes"; then
|
||||
AC_CHECK_HEADER(
|
||||
[CoreFoundation/CoreFoundation.h],
|
||||
[
|
||||
LDFLAGS="${LDFLAGS} -framework CoreFoundation"
|
||||
AC_DEFINE(
|
||||
[ENABLE_NFCONV],
|
||||
[1],
|
||||
[Define to 1 if the nfconv patch should be enabled]
|
||||
)
|
||||
],
|
||||
AC_MSG_ERROR([[Cannot find CoreFoundation required for 'nfconv' functionality Mac OS X. You may use the --disable-nfconv 'configure' option to avoid this error.]])
|
||||
)
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_GETMNTENT
|
||||
AC_FUNC_MBRTOWC
|
||||
AC_FUNC_MEMCMP
|
||||
AC_FUNC_STAT
|
||||
AC_FUNC_STRFTIME
|
||||
AC_FUNC_UTIME_NULL
|
||||
AC_FUNC_VPRINTF
|
||||
AC_CHECK_FUNCS([ \
|
||||
atexit basename daemon dup2 fdatasync ffs getopt_long hasmntopt \
|
||||
mbsinit memmove memset realpath regcomp setlocale setxattr \
|
||||
strcasecmp strchr strdup strerror strnlen strsep strtol strtoul \
|
||||
sysconf utime utimensat gettimeofday clock_gettime fork memcpy random snprintf \
|
||||
])
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
# The dlopen API might be in libc or in libdl. Check libc first, then
|
||||
# fall back to libdl.
|
||||
LIBDL=""
|
||||
if test "x${disable_plugins}" = "xno"; then
|
||||
AC_CHECK_LIB(c, dlopen, ,
|
||||
[AC_CHECK_LIB(dl, dlopen, [LIBDL="-ldl"],
|
||||
[AC_MSG_ERROR(["Unable to find libdl (required for external plugin support)"])])])
|
||||
fi
|
||||
AC_SUBST([LIBDL])
|
||||
|
||||
if test "$GCC" = "yes" ; then
|
||||
# We add -Wall to enable some compiler warnings.
|
||||
CFLAGS="${CFLAGS} -Wall"
|
||||
fi
|
||||
|
||||
if test "${enable_pedantic}" = "yes"; then
|
||||
enable_warnings="yes"
|
||||
CFLAGS="${CFLAGS} -pedantic"
|
||||
fi
|
||||
|
||||
if test "${enable_warnings}" = "yes"; then
|
||||
CFLAGS="${CFLAGS} -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings -Wformat -Wformat-security -Wuninitialized"
|
||||
fi
|
||||
|
||||
if test "${enable_debug}" = "yes"; then
|
||||
CFLAGS="${CFLAGS} -ggdb3 -DDEBUG"
|
||||
AC_DEFINE(
|
||||
[ENABLE_DEBUG],
|
||||
[1],
|
||||
[Define to 1 if debug should be enabled]
|
||||
)
|
||||
fi
|
||||
|
||||
test "${enable_device_default_io_ops}" = "no" && AC_DEFINE(
|
||||
[NO_NTFS_DEVICE_DEFAULT_IO_OPS],
|
||||
[1],
|
||||
[Don't use default IO ops]
|
||||
)
|
||||
|
||||
test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab])
|
||||
test "${enable_posix_acls}" != "no" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support])
|
||||
test "${enable_xattr_mappings}" != "no" && AC_DEFINE([XATTR_MAPPINGS], [1], [system extended attributes mappings])
|
||||
test "${disable_plugins}" != "no" && AC_DEFINE([DISABLE_PLUGINS], [1], [Define to 1 for disabling reparse plugins])
|
||||
|
||||
test "${enable_really_static}" = "yes" && enable_library="no"
|
||||
test "${enable_library}" = "no" && enable_ldconfig="no"
|
||||
|
||||
if test "x${DISTCHECK_HACK}" != "x"; then
|
||||
enable_mount_helper="no"
|
||||
enable_ldconfig="no"
|
||||
fi
|
||||
|
||||
# Settings
|
||||
pkgconfigdir="\$(libdir)/pkgconfig"
|
||||
ntfs3gincludedir="\$(includedir)/ntfs-3g"
|
||||
# Executables should be installed to the root filesystem, otherwise
|
||||
# automounting NTFS volumes can fail during boot if the driver binaries
|
||||
# and their dependencies are on an unmounted partition. Use --exec-prefix
|
||||
# to override this.
|
||||
if test "x${exec_prefix}" = "xNONE"; then
|
||||
rootbindir="/bin"
|
||||
rootsbindir="/sbin"
|
||||
rootlibdir="/lib${libdir##*/lib}"
|
||||
else
|
||||
rootbindir="\$(bindir)"
|
||||
rootsbindir="\$(sbindir)"
|
||||
rootlibdir="\$(libdir)"
|
||||
fi
|
||||
AC_SUBST([pkgconfigdir])
|
||||
AC_SUBST([ntfs3gincludedir])
|
||||
AC_SUBST([rootbindir])
|
||||
AC_SUBST([rootsbindir])
|
||||
AC_SUBST([rootlibdir])
|
||||
AC_SUBST([LIBNTFS_3G_VERSION])
|
||||
AC_SUBST([LIBFUSE_LITE_CFLAGS])
|
||||
AC_SUBST([LIBFUSE_LITE_LIBS])
|
||||
AC_SUBST([MKNTFS_CPPFLAGS])
|
||||
AC_SUBST([MKNTFS_LIBS])
|
||||
AC_SUBST([LIBNTFS_CPPFLAGS])
|
||||
AC_SUBST([LIBNTFS_LIBS])
|
||||
AC_SUBST([NTFSPROGS_STATIC_LIBS])
|
||||
AC_SUBST([OUTPUT_FORMAT])
|
||||
AM_CONDITIONAL([FUSE_INTERNAL], [test "${with_fuse}" = "internal"])
|
||||
AM_CONDITIONAL([GENERATE_LDSCRIPT], [test "${enable_ldscript}" = "yes"])
|
||||
AM_CONDITIONAL([WINDOWS], [test "${WINDOWS}" = "yes"])
|
||||
AM_CONDITIONAL([NTFS_DEVICE_DEFAULT_IO_OPS], [test "${enable_device_default_io_ops}" = "yes"])
|
||||
AM_CONDITIONAL([RUN_LDCONFIG], [test "${enable_ldconfig}" = "yes"])
|
||||
AM_CONDITIONAL([REALLYSTATIC], [test "${enable_really_static}" = "yes"])
|
||||
AM_CONDITIONAL([INSTALL_LIBRARY], [test "${enable_library}" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_MOUNT_HELPER], [test "${enable_mount_helper}" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_NTFS_3G], [test "${enable_ntfs_3g}" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_NTFSPROGS], [test "${enable_ntfsprogs}" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_EXTRAS], [test "${enable_extras}" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_QUARANTINED], [test "${enable_quarantined}" = "yes"])
|
||||
AM_CONDITIONAL([DISABLE_PLUGINS], [test "${disable_plugins}" != "no"])
|
||||
|
||||
# workaround for <autoconf-2.60
|
||||
if test -z "${docdir}"; then
|
||||
docdir="\$(datarootdir)/doc/\$(PACKAGE_NAME)"
|
||||
AC_SUBST([docdir])
|
||||
fi
|
||||
# workaround for <automake-1.10
|
||||
if test -z "${MKDIR_P}"; then
|
||||
MKDIR_P="\$(mkdir_p)"
|
||||
AC_SUBST([MKDIR_P])
|
||||
fi
|
||||
|
||||
# generate files
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
include/Makefile
|
||||
include/fuse-lite/Makefile
|
||||
include/ntfs-3g/Makefile
|
||||
libfuse-lite/Makefile
|
||||
libntfs-3g/Makefile
|
||||
libntfs-3g/libntfs-3g.pc
|
||||
libntfs-3g/libntfs-3g.script.so
|
||||
ntfsprogs/Makefile
|
||||
ntfsprogs/mkntfs.8
|
||||
ntfsprogs/ntfscat.8
|
||||
ntfsprogs/ntfsclone.8
|
||||
ntfsprogs/ntfscluster.8
|
||||
ntfsprogs/ntfscmp.8
|
||||
ntfsprogs/ntfscp.8
|
||||
ntfsprogs/ntfsfix.8
|
||||
ntfsprogs/ntfsinfo.8
|
||||
ntfsprogs/ntfslabel.8
|
||||
ntfsprogs/ntfsls.8
|
||||
ntfsprogs/ntfsprogs.8
|
||||
ntfsprogs/ntfsresize.8
|
||||
ntfsprogs/ntfsundelete.8
|
||||
ntfsprogs/ntfsdecrypt.8
|
||||
ntfsprogs/ntfswipe.8
|
||||
ntfsprogs/ntfstruncate.8
|
||||
ntfsprogs/ntfsfallocate.8
|
||||
ntfsprogs/ntfsrecover.8
|
||||
ntfsprogs/ntfsusermap.8
|
||||
ntfsprogs/ntfssecaudit.8
|
||||
src/Makefile
|
||||
src/ntfs-3g.8
|
||||
src/ntfs-3g.probe.8
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
if test "${with_fuse}" = "external"; then
|
||||
if ! echo "x$FUSE_LIB_PATH" | grep -- "x-L/lib" > /dev/null; then
|
||||
cat <<EOF
|
||||
****************************************************************************
|
||||
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *
|
||||
* The FUSE user space binaries were NOT installed with root directory *
|
||||
* executable prefix. This means that automounting NTFS volumes during boot *
|
||||
* could fail. This can be fixed the below way by reinstalling FUSE using *
|
||||
* the right 'configure' option during FUSE compilation: *
|
||||
* ./configure --exec-prefix=/ *
|
||||
* make && sudo make install *
|
||||
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *
|
||||
****************************************************************************
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "You can type now 'make' to build ntfs-3g."
|
||||
|
||||
Loaded 100 of 441 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user