Add ext2 filesystem implementation.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-05-23 14:39:54 +02:00
parent 0708482d9b
commit b308c764cf
19 changed files with 3997 additions and 1 deletions

View File

@ -3,7 +3,7 @@ MAKEFILE_NOT_MEANT_FOR_SORTIX=1
include compiler.mak
include version.mak
MODULES=libc libm dispd games mkinitrd mxmpp utils bench sortix
MODULES=libc libm dispd games mkinitrd mxmpp utils bench ext sortix
ifndef SYSROOT
SYSROOT:=$(shell pwd)/sysroot

2
ext/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
extfs
*.o

38
ext/Makefile Normal file
View File

@ -0,0 +1,38 @@
include ../compiler.mak
include ../version.mak
include ../dirs.mak
OPTLEVEL?=-g -O2
CXXFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS)
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
LIBS:=$(LIBS)
ifeq ($(HOST_IS_SORTIX),0)
LIBS:=$(LIBS) -lfuse
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
endif
BINARIES:=extfs
INSTALLBINARIES:=$(addprefix $(DESTDIR)$(BINDIR)/,$(BINARIES))
all: $(BINARIES)
.PHONY: all install uninstall clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARIES) $(DESTDIR)$(BINDIR)
uninstall:
rm -f $(DESTDIR)$(INSTALLBINARIES)
extfs: *.cpp *.h
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) $(wildcard *.cpp) -o $@ $(LIBS)
clean:
rm -f $(BINARIES) *.o

115
ext/block.cpp Normal file
View File

@ -0,0 +1,115 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
block.cpp
Blocks in the filesystem.
*******************************************************************************/
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Block::Block(Device* device, uint32_t block_id)
{
this->prev_block = NULL;
this->next_block = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->device = device;
this->reference_count = 1;
this->block_id = block_id;
this->dirty = false;
this->block_data = 0;
}
Block::~Block()
{
Sync();
Unlink();
delete[] block_data;
}
void Block::Refer()
{
reference_count++;
}
void Block::Unref()
{
if ( !--reference_count )
#if 0
delete this;
#else
{};
#endif
}
void Block::Sync()
{
if ( !dirty )
return;
dirty = false;
if ( !device->write )
return;
off_t file_offset = (off_t) device->block_size * (off_t) block_id;
pwriteall(device->fd, block_data, device->block_size, file_offset);
}
void Block::Dirty()
{
dirty = true;
Use();
}
void Block::Use()
{
Unlink();
Prelink();
}
void Block::Unlink()
{
(prev_block ? prev_block->next_block : device->mru_block) = next_block;
(next_block ? next_block->prev_block : device->lru_block) = prev_block;
size_t bin = block_id % DEVICE_HASH_LENGTH;
(prev_hashed ? prev_hashed->next_hashed : device->hash_blocks[bin]) = next_hashed;
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
}
void Block::Prelink()
{
prev_block = NULL;
next_block = device->mru_block;
if ( device->mru_block )
device->mru_block->prev_block = this;
device->mru_block = this;
if ( !device->lru_block )
device->lru_block = this;
size_t bin = block_id % DEVICE_HASH_LENGTH;
prev_hashed = NULL;
next_hashed = device->hash_blocks[bin];
device->hash_blocks[bin] = this;
if ( next_hashed )
next_hashed->prev_hashed = this;
}

56
ext/block.h Normal file
View File

@ -0,0 +1,56 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
block.h
Blocks in the filesystem.
*******************************************************************************/
#ifndef BLOCK_H
#define BLOCK_H
class Device;
class Block
{
public:
Block(Device* device, uint32_t block_id);
~Block();
public:
Block* prev_block;
Block* next_block;
Block* prev_hashed;
Block* next_hashed;
Device* device;
size_t reference_count;
uint32_t block_id;
bool dirty;
uint8_t* block_data;
public:
void Refer();
void Unref();
void Sync();
void Dirty();
void Use();
void Unlink();
void Prelink();
};
#endif

238
ext/blockgroup.cpp Normal file
View File

@ -0,0 +1,238 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
blockgroup.cpp
Filesystem block group.
*******************************************************************************/
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include "ext-constants.h"
#include "ext-structs.h"
#include "block.h"
#include "blockgroup.h"
#include "device.h"
#include "filesystem.h"
#include "util.h"
BlockGroup::BlockGroup(Filesystem* filesystem, uint32_t group_id)
{
this->data_block = NULL;
this->data = NULL;
this->filesystem = filesystem;
this->block_bitmap_chunk = NULL;
this->inode_bitmap_chunk = NULL;
this->reference_count = 1;
this->group_id = group_id;
this->block_alloc_chunk = 0;
this->inode_alloc_chunk = 0;
this->block_bitmap_chunk_i = 0;
// TODO: inode_bitmap_chunk_i
this->first_block_id = filesystem->sb->s_first_data_block +
filesystem->sb->s_blocks_per_group * group_id;
this->first_inode_id = 1 +
filesystem->sb->s_inodes_per_group * group_id;
this->num_blocks = group_id+1== filesystem->num_groups ?
filesystem->num_blocks - first_block_id :
filesystem->sb->s_blocks_per_group;
this->num_inodes = group_id+1== filesystem->num_groups ?
filesystem->num_inodes - first_inode_id :
filesystem->sb->s_inodes_per_group;
size_t num_chunk_bits = filesystem->block_size * 8UL;
this->num_block_bitmap_chunks = divup(num_blocks, (uint32_t) num_chunk_bits);
this->num_inode_bitmap_chunks = divup(num_inodes, (uint32_t) num_chunk_bits);
this->dirty = false;
}
BlockGroup::~BlockGroup()
{
Sync();
if ( data_block )
data_block->Unref();
filesystem->block_groups[group_id] = NULL;
}
uint32_t BlockGroup::AllocateBlock()
{
if ( !data->bg_free_blocks_count )
return errno = ENOSPC, 0;
size_t num_chunk_bits = filesystem->block_size * 8UL;
uint32_t begun_chunk = block_alloc_chunk;
for ( uint32_t i = 0; i < num_block_bitmap_chunks; i++ )
{
block_alloc_chunk = (begun_chunk + i) % num_block_bitmap_chunks;
bool last = block_alloc_chunk + 1 == num_block_bitmap_chunks;
if ( !block_bitmap_chunk )
{
uint32_t block_id = data->bg_block_bitmap + block_alloc_chunk;
block_bitmap_chunk = filesystem->device->GetBlock(block_id);
block_bitmap_chunk_i = 0;
}
uint32_t chunk_offset = block_alloc_chunk * num_chunk_bits;
uint8_t* chunk_bits = block_bitmap_chunk->block_data;
size_t num_bits = last ? num_blocks - chunk_offset : num_chunk_bits;
for ( ; block_bitmap_chunk_i < num_bits; block_bitmap_chunk_i++ )
if ( !checkbit(chunk_bits, block_bitmap_chunk_i) )
{
setbit(chunk_bits, block_bitmap_chunk_i);
block_bitmap_chunk->Dirty();
data->bg_free_blocks_count--;
Dirty();
filesystem->sb->s_free_blocks_count--;
filesystem->Dirty();
uint32_t group_block_id = chunk_offset + block_bitmap_chunk_i++;
uint32_t block_id = first_block_id + group_block_id;
return block_id;
}
block_bitmap_chunk->Sync();
block_bitmap_chunk->Unref();
block_bitmap_chunk = NULL;
}
data->bg_free_blocks_count = 0;
Dirty();
return errno = ENOSPC, 0;
}
uint32_t BlockGroup::AllocateInode()
{
if ( !data->bg_free_inodes_count )
return errno = ENOSPC, 0;
size_t num_chunk_bits = filesystem->block_size * 8UL;
uint32_t begun_chunk = inode_alloc_chunk;
for ( uint32_t i = 0; i < num_inode_bitmap_chunks; i++ )
{
inode_alloc_chunk = (begun_chunk + i) % num_inode_bitmap_chunks;
bool last = inode_alloc_chunk + 1 == num_inode_bitmap_chunks;
if ( !inode_bitmap_chunk )
{
uint32_t block_id = data->bg_inode_bitmap + inode_alloc_chunk;
inode_bitmap_chunk = filesystem->device->GetBlock(block_id);
inode_bitmap_chunk_i = 0;
}
uint32_t chunk_offset = inode_alloc_chunk * num_chunk_bits;
uint8_t* chunk_bits = inode_bitmap_chunk->block_data;
size_t num_bits = last ? num_inodes - chunk_offset : num_chunk_bits;
for ( ; inode_bitmap_chunk_i < num_bits; inode_bitmap_chunk_i++ )
if ( !checkbit(chunk_bits, inode_bitmap_chunk_i) )
{
setbit(chunk_bits, inode_bitmap_chunk_i);
inode_bitmap_chunk->Dirty();
data->bg_free_inodes_count--;
Dirty();
filesystem->sb->s_free_inodes_count--;
filesystem->Dirty();
uint32_t group_inode_id = chunk_offset + inode_bitmap_chunk_i++;
uint32_t inode_id = first_inode_id + group_inode_id;
return inode_id;
}
inode_bitmap_chunk->Sync();
inode_bitmap_chunk->Unref();
inode_bitmap_chunk = NULL;
}
data->bg_free_inodes_count = 0;
Dirty();
return errno = ENOSPC, 0;
}
void BlockGroup::FreeBlock(uint32_t block_id)
{
block_id -= first_block_id;
size_t num_chunk_bits = filesystem->block_size * 8UL;
uint32_t chunk_id = block_id / num_chunk_bits;
uint32_t chunk_bit = block_id % num_chunk_bits;
if ( !block_bitmap_chunk || chunk_id != block_alloc_chunk )
{
if ( block_bitmap_chunk )
block_bitmap_chunk->Sync(),
block_bitmap_chunk->Unref();
block_alloc_chunk = chunk_id;
uint32_t block_id = data->bg_block_bitmap + block_alloc_chunk;
block_bitmap_chunk = filesystem->device->GetBlock(block_id);
block_bitmap_chunk_i = 0;
}
uint8_t* chunk_bits = block_bitmap_chunk->block_data;
clearbit(chunk_bits, chunk_bit);
block_bitmap_chunk->Dirty();
data->bg_free_blocks_count++;
Dirty();
filesystem->sb->s_free_blocks_count++;
filesystem->Dirty();
}
void BlockGroup::FreeInode(uint32_t inode_id)
{
inode_id -= first_inode_id;
size_t num_chunk_bits = filesystem->block_size * 8UL;
uint32_t chunk_id = inode_id / num_chunk_bits;
uint32_t chunk_bit = inode_id % num_chunk_bits;
if ( !inode_bitmap_chunk || chunk_id != inode_alloc_chunk )
{
if ( inode_bitmap_chunk )
inode_bitmap_chunk->Sync(),
inode_bitmap_chunk->Unref();
inode_alloc_chunk = chunk_id;
uint32_t block_id = data->bg_inode_bitmap + inode_alloc_chunk;
inode_bitmap_chunk = filesystem->device->GetBlock(block_id);
inode_bitmap_chunk_i = 0;
}
uint8_t* chunk_bits = inode_bitmap_chunk->block_data;
clearbit(chunk_bits, chunk_bit);
inode_bitmap_chunk->Dirty();
data->bg_free_inodes_count++;
Dirty();
filesystem->sb->s_free_inodes_count++;
filesystem->Dirty();
}
void BlockGroup::Refer()
{
// TODO
}
void BlockGroup::Unref()
{
// TODO
}
void BlockGroup::Sync()
{
if ( block_bitmap_chunk )
block_bitmap_chunk->Sync();
if ( dirty )
data_block->Sync();
dirty = false;
}
void BlockGroup::Dirty()
{
dirty = true;
data_block->Dirty();
Use();
}
void BlockGroup::Use()
{
}

70
ext/blockgroup.h Normal file
View File

@ -0,0 +1,70 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
blockgroup.h
Filesystem block group.
*******************************************************************************/
#ifndef BLOCKGROUP_H
#define BLOCKGROUP_H
class Block;
class Filesystem;
class BlockGroup
{
public:
BlockGroup(Filesystem* filesystem, uint32_t group_id);
~BlockGroup();
public:
Block* data_block;
struct ext_blockgrpdesc* data;
Filesystem* filesystem;
Block* block_bitmap_chunk;
Block* inode_bitmap_chunk;
size_t reference_count;
uint32_t group_id;
uint32_t block_alloc_chunk;
uint32_t inode_alloc_chunk;
uint32_t num_block_bitmap_chunks;
uint32_t num_inode_bitmap_chunks;
uint32_t block_bitmap_chunk_i;
uint32_t inode_bitmap_chunk_i;
uint32_t num_blocks;
uint32_t num_inodes;
uint32_t first_block_id;
uint32_t first_inode_id;
bool dirty;
public:
uint32_t AllocateBlock();
uint32_t AllocateInode();
void FreeBlock(uint32_t block_id);
void FreeInode(uint32_t inode_id);
void Refer();
void Unref();
void Sync();
void Dirty();
void Use();
void Unlink();
void Prelink();
};
#endif

96
ext/device.cpp Normal file
View File

@ -0,0 +1,96 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
device.cpp
Block device.
*******************************************************************************/
#include <sys/stat.h>
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Device::Device(int fd, uint32_t block_size, bool write)
{
this->write = write;
this->fd = fd;
this->block_size = block_size;
struct stat st;
fstat(fd, &st);
this->device_size = st.st_size;
this->mru_block = NULL;
this->lru_block = NULL;
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
hash_blocks[i] = NULL;
}
Device::~Device()
{
Sync();
while ( mru_block )
delete mru_block;
}
Block* Device::GetBlock(uint32_t block_id)
{
if ( Block* block = GetCachedBlock(block_id) )
return block;
Block* block = new Block(this, block_id);
block->block_data = new uint8_t[block_size];
off_t file_offset = (off_t) block_size * (off_t) block_id;
preadall(fd, block->block_data, block_size, file_offset);
block->Prelink();
return block;
}
Block* Device::GetBlockZeroed(uint32_t block_id)
{
if ( Block* block = GetCachedBlock(block_id) )
{
memset(block->block_data, 0, block_size);
block->Dirty();
return block;
}
Block* block = new Block(this, block_id);
block->block_data = new uint8_t[block_size];
memset(block->block_data, 0, block_size);
block->Prelink();
block->Dirty();
return block;
}
Block* Device::GetCachedBlock(uint32_t block_id)
{
size_t bin = block_id % DEVICE_HASH_LENGTH;
for ( Block* iter = hash_blocks[bin]; iter; iter = iter->next_hashed )
if ( iter->block_id == block_id )
return iter->Refer(), iter;
return NULL;
}
void Device::Sync()
{
for ( Block* iter = mru_block; iter; iter = iter->next_block )
iter->Sync();
}

53
ext/device.h Normal file
View File

@ -0,0 +1,53 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
device.h
Block device.
*******************************************************************************/
#ifndef DEVICE_H
#define DEVICE_H
class Block;
const size_t DEVICE_HASH_LENGTH = 1 << 16;
class Device
{
public:
Device(int fd, uint32_t block_size, bool write);
~Device();
public:
Block* mru_block;
Block* lru_block;
Block* hash_blocks[DEVICE_HASH_LENGTH];
off_t device_size;
uint32_t block_size;
int fd;
bool write;
public:
Block* GetBlock(uint32_t block_id);
Block* GetBlockZeroed(uint32_t block_id);
Block* GetCachedBlock(uint32_t block_id);
void Sync();
};
#endif

135
ext/ext-constants.h Normal file
View File

@ -0,0 +1,135 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
ext-constants.h
Constants for the extended filesystem.
*******************************************************************************/
#ifndef EXT_CONSTANTS_H
#define EXT_CONSTANTS_H
const uint16_t EXT2_SUPER_MAGIC = 0xEF53;
const uint16_t EXT2_VALID_FS = 1;
const uint16_t EXT2_ERROR_FS = 2;
const uint16_t EXT2_ERRORS_CONTINUE = 1;
const uint16_t EXT2_ERRORS_RO = 2;
const uint16_t EXT2_ERRORS_PANIC = 3;
const uint32_t EXT2_OS_LINUX = 0;
const uint32_t EXT2_OS_HURD = 1;
const uint32_t EXT2_OS_MASIX = 2;
const uint32_t EXT2_OS_FREEBSD = 3;
const uint32_t EXT2_OS_LITES = 4;
const uint32_t EXT2_GOOD_OLD_REV = 0;
const uint32_t EXT2_DYNAMIC_REV = 1;
const uint16_t EXT2_DEF_RESUID = 0;
const uint16_t EXT2_DEF_RESGID = 0;
const uint16_t EXT2_GOOD_OLD_FIRST_INO = 11;
const uint16_t EXT2_GOOD_OLD_INODE_SIZE = 128;
const uint32_t EXT2_FEATURE_COMPAT_DIR_PREALLOC = 1U << 0U;
const uint32_t EXT2_FEATURE_COMPAT_IMAGIC_INODES = 1U << 1U;
const uint32_t EXT3_FEATURE_COMPAT_HAS_JOURNAL = 1U << 2U;
const uint32_t EXT2_FEATURE_COMPAT_EXT_ATTR = 1U << 3U;
const uint32_t EXT2_FEATURE_COMPAT_RESIZE_INO = 1U << 4U;
const uint32_t EXT2_FEATURE_COMPAT_DIR_INDEX = 1U << 5U;
const uint32_t EXT2_FEATURE_INCOMPAT_COMPRESSION = 1U << 0U;
const uint32_t EXT2_FEATURE_INCOMPAT_FILETYPE = 1U << 1U;
const uint32_t EXT2_FEATURE_INCOMPAT_RECOVER = 1U << 2U;
const uint32_t EXT2_FEATURE_INCOMPAT_JOURNAL_DEV = 1U << 3U;
const uint32_t EXT2_FEATURE_INCOMPAT_META_BG = 1U << 4U;
const uint32_t EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 1U << 0U;
const uint32_t EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 1U << 1U;
const uint32_t EXT2_FEATURE_RO_COMPAT_BTREE_DIR = 1U << 2U;
const uint32_t EXT2_LZV1_ALG = 1U << 0U;
const uint32_t EXT2_LZRW3A_ALG = 1U << 1U;
const uint32_t EXT2_GZIP_ALG = 1U << 2U;
const uint32_t EXT2_BZIP2_ALG = 1U << 3U;
const uint32_t EXT2_LZO_ALG = 1U << 4U;
const uint16_t EXT2_S_IFMT = 0xF000;
const uint16_t EXT2_S_IFSOCK = 0xC000;
const uint16_t EXT2_S_IFLNK = 0xA000;
const uint16_t EXT2_S_IFREG = 0x8000;
const uint16_t EXT2_S_IFBLK = 0x6000;
const uint16_t EXT2_S_IFDIR = 0x4000;
const uint16_t EXT2_S_IFCHR = 0x2000;
const uint16_t EXT2_S_IFIFO = 0x1000;
const uint16_t EXT2_S_ISUID = 0x0800;
const uint16_t EXT2_S_ISGID = 0x0400;
const uint16_t EXT2_S_ISVTX = 0x0200;
const uint16_t EXT2_S_IRUSR = 0x0100;
const uint16_t EXT2_S_IWUSR = 0x0080;
const uint16_t EXT2_S_IXUSR = 0x0040;
const uint16_t EXT2_S_IRGRP = 0x0020;
const uint16_t EXT2_S_IWGRP = 0x0010;
const uint16_t EXT2_S_IXGRP = 0x0008;
const uint16_t EXT2_S_IROTH = 0x0004;
const uint16_t EXT2_S_IWOTH = 0x0002;
const uint16_t EXT2_S_IXOTH = 0x0001;
#define EXT2_S_ISSOCK(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFSOCK)
#define EXT2_S_ISLNK(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFLNK)
#define EXT2_S_ISREG(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFREG)
#define EXT2_S_ISBLK(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFBLK)
#define EXT2_S_ISDIR(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFDIR)
#define EXT2_S_ISCHR(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFCHR)
#define EXT2_S_ISFIFO(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFIFO)
const uint32_t EXT2_SECRM_FL = 0x00000001U;
const uint32_t EXT2_UNRM_FL = 0x00000002U;
const uint32_t EXT2_COMPR_FL = 0x00000004U;
const uint32_t EXT2_SYNC_FL = 0x00000008U;
const uint32_t EXT2_IMMUTABLE_FL = 0x00000010U;
const uint32_t EXT2_APPEND_FL = 0x00000020U;
const uint32_t EXT2_NODUMP_FL = 0x00000040U;
const uint32_t EXT2_NOATIME_FL = 0x00000080U;
const uint32_t EXT2_DIRTY_FL = 0x00000100U;
const uint32_t EXT2_COMPRBLK_FL = 0x00000200U;
const uint32_t EXT2_NOCOMPR_FL = 0x00000400U;
const uint32_t EXT2_ECOMPR_FL = 0x00000800U;
const uint32_t EXT2_BTREE_FL = 0x00001000U;
const uint32_t EXT2_INDEX_FL = 0x00001000U;
const uint32_t EXT2_IMAGIC_FL = 0x00002000U;
const uint32_t EXT3_JOURNAL_DATA_FL = 0x00004000U;
const uint32_t EXT2_RESERVED_FL = 0x80000000U;
const uint32_t EXT2_ROOT_INO = 2;
const uint8_t EXT2_FT_UNKNOWN = 0;
const uint8_t EXT2_FT_REG_FILE = 1;
const uint8_t EXT2_FT_DIR = 2;
const uint8_t EXT2_FT_CHRDEV = 3;
const uint8_t EXT2_FT_BLKDEV = 4;
const uint8_t EXT2_FT_FIFO = 5;
const uint8_t EXT2_FT_SOCK = 6;
const uint8_t EXT2_FT_SYMLINK = 7;
static inline uint8_t EXT2_FT_OF_MODE(mode_t mode)
{
if ( EXT2_S_ISREG(mode) )
return EXT2_FT_REG_FILE;
if ( EXT2_S_ISDIR(mode) )
return EXT2_FT_DIR;
if ( EXT2_S_ISCHR(mode) )
return EXT2_FT_CHRDEV;
if ( EXT2_S_ISBLK(mode) )
return EXT2_FT_BLKDEV;
if ( EXT2_S_ISFIFO(mode) )
return EXT2_FT_FIFO;
if ( EXT2_S_ISSOCK(mode) )
return EXT2_FT_SOCK;
if ( EXT2_S_ISLNK(mode) )
return EXT2_FT_SYMLINK;
return EXT2_FT_UNKNOWN;
}
#endif

131
ext/ext-structs.h Normal file
View File

@ -0,0 +1,131 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
ext-structs.h
Data structures for the extended filesystem.
*******************************************************************************/
#ifndef EXT_STRUCTS_H
#define EXT_STRUCTS_H
struct ext_superblock
{
uint32_t s_inodes_count;
uint32_t s_blocks_count;
uint32_t s_r_blocks_count;
uint32_t s_free_blocks_count;
uint32_t s_free_inodes_count;
uint32_t s_first_data_block;
uint32_t s_log_block_size;
int32_t s_log_frag_size;
uint32_t s_blocks_per_group;
uint32_t s_frags_per_group;
uint32_t s_inodes_per_group;
uint32_t s_mtime;
uint32_t s_wtime;
uint16_t s_mnt_count;
uint16_t s_max_mnt_count;
uint16_t s_magic;
uint16_t s_state;
uint16_t s_errors;
uint16_t s_minor_rev_level;
uint32_t s_lastcheck;
uint32_t s_checkinterval;
uint32_t s_creator_os;
uint32_t s_rev_level;
uint16_t s_def_resuid;
uint16_t s_def_resgid;
// EXT2_DYNAMIC_REV
uint32_t s_first_ino;
uint16_t s_inode_size;
uint16_t s_block_group_nr;
uint32_t s_feature_compat;
uint32_t s_feature_incompat;
uint32_t s_feature_ro_compat;
uint8_t s_uuid[16];
/*uint8_t*/ char s_volume_name[16];
/*uint8_t*/ char s_last_mounted[64];
uint32_t s_algo_bitmap;
// Performance Hints
uint8_t s_prealloc_blocks;
uint8_t s_prealloc_dir_blocks;
uint16_t alignment0;
// Journaling Support
uint8_t s_journal_uuid[16];
uint32_t s_journal_inum;
uint32_t s_journal_dev;
uint32_t s_last_orphan;
// Directory Indexing Support
uint32_t s_hash_seed[4];
uint8_t s_def_hash_version;
uint8_t alignment1[3];
// Other options
uint32_t s_default_mount_options;
uint32_t s_first_meta_bg;
uint8_t alignment2[760];
};
struct ext_blockgrpdesc
{
uint32_t bg_block_bitmap;
uint32_t bg_inode_bitmap;
uint32_t bg_inode_table;
uint16_t bg_free_blocks_count;
uint16_t bg_free_inodes_count;
uint16_t bg_used_dirs_count;
uint16_t alignment0;
uint8_t alignment1[12];
};
struct ext_inode
{
uint16_t i_mode;
uint16_t i_uid;
uint32_t i_size;
uint32_t i_atime;
uint32_t i_ctime;
uint32_t i_mtime;
uint32_t i_dtime;
uint16_t i_gid;
uint16_t i_links_count;
uint32_t i_blocks;
uint32_t i_flags;
uint32_t i_osd1;
uint32_t i_block[15];
uint32_t i_generation;
uint32_t i_file_acl;
uint32_t i_dir_acl;
uint32_t i_faddr;
uint8_t i_frag;
uint8_t i_fsize;
uint16_t i_mode_high;
uint16_t i_uid_high;
uint16_t i_gid_high;
uint32_t i_osd2_alignment0;
};
struct ext_dirent
{
uint32_t inode;
uint16_t reclen;
uint8_t name_len;
uint8_t file_type;
char name[0];
};
#endif

1481
ext/extfs.cpp Normal file

File diff suppressed because it is too large Load Diff

214
ext/filesystem.cpp Normal file
View File

@ -0,0 +1,214 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
filesystem.cpp
Filesystem.
*******************************************************************************/
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include "ext-constants.h"
#include "ext-structs.h"
#include "block.h"
#include "blockgroup.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "util.h"
Filesystem::Filesystem(Device* device)
{
uint64_t sb_offset = 1024;
uint32_t sb_block_id = sb_offset / device->block_size;
sb_block = device->GetBlock(sb_block_id);
sb = (struct ext_superblock*)
(sb_block->block_data + sb_offset % device->block_size);
this->device = device;
block_groups = NULL;
block_size = device->block_size;
mru_inode = NULL;
lru_inode = NULL;
inode_size = this->sb->s_inode_size;
num_blocks = sb->s_blocks_count;
num_groups = divup(this->sb->s_blocks_count, this->sb->s_blocks_per_group);
num_inodes = this->sb->s_inodes_count;
dirty = false;
struct timespec now_realtime, now_monotonic;
clock_gettime(CLOCK_REALTIME, &now_realtime);
clock_gettime(CLOCK_MONOTONIC, &now_monotonic);
mtime_realtime = now_realtime.tv_sec;
mtime_monotonic = now_monotonic.tv_sec;
sb->s_mtime = mtime_realtime;
sb->s_mnt_count++;
sb->s_state = EXT2_ERROR_FS;
Dirty();
Sync();
}
Filesystem::~Filesystem()
{
Sync();
while ( mru_inode )
delete mru_inode;
for ( size_t i = 0; i < num_groups; i++ )
delete block_groups[i];
delete[] block_groups;
sb->s_state = EXT2_VALID_FS;
Dirty();
Sync();
sb_block->Unref();
}
void Filesystem::Dirty()
{
dirty = true;
sb_block->Dirty();
}
void Filesystem::Sync()
{
for ( Inode* iter = mru_inode; iter; iter = iter->next_inode )
iter->Sync();
for ( size_t i = 0; i < num_groups; i++ )
if ( block_groups && block_groups[i] )
block_groups[i]->Sync();
if ( dirty )
{
// The correct real-time might not have been known when the filesystem
// was mounted (perhaps during early system boot), so find out what time
// it is now, how long ago we were mounted, and subtract to get the
// correct mount time.
struct timespec now_realtime, now_monotonic;
clock_gettime(CLOCK_REALTIME, &now_realtime);
clock_gettime(CLOCK_MONOTONIC, &now_monotonic);
time_t since_boot = now_monotonic.tv_sec - mtime_monotonic;
mtime_realtime = now_realtime.tv_sec - since_boot;
sb->s_wtime = now_realtime.tv_sec;
sb->s_mtime = mtime_realtime;
sb_block->Sync();
dirty = false;
}
device->Sync();
}
BlockGroup* Filesystem::GetBlockGroup(uint32_t group_id)
{
assert(group_id < num_groups);
if ( block_groups[group_id] )
return block_groups[group_id]->Refer(), block_groups[group_id];
BlockGroup* group = new BlockGroup(this, group_id);
size_t group_size = sizeof(ext_blockgrpdesc);
uint32_t first_block_id = sb->s_first_data_block + 1 /* superblock */;
uint32_t block_id = first_block_id + (group_id * group_size) / block_size;
uint32_t offset = (group_id * group_size) % block_size;
Block* block = device->GetBlock(block_id);
group->data_block = block;
uint8_t* buf = group->data_block->block_data + offset;
group->data = (struct ext_blockgrpdesc*) buf;
return block_groups[group_id] = group;
}
Inode* Filesystem::GetInode(uint32_t inode_id)
{
assert(inode_id);
assert(inode_id < num_inodes);
for ( Inode* iter = mru_inode; iter; iter = iter->next_inode )
if ( iter->inode_id == inode_id )
return iter->Refer(), iter;
Inode* inode = new Inode(this, inode_id);
uint32_t group_id = (inode_id-1) / sb->s_inodes_per_group;
uint32_t tabel_index = (inode_id-1) % sb->s_inodes_per_group;
assert(group_id < num_groups);
BlockGroup* group = GetBlockGroup(group_id);
uint32_t tabel_block = group->data->bg_inode_table;
group->Unref();
uint32_t block_id = tabel_block + (tabel_index * inode_size) / block_size;
uint32_t offset = (tabel_index * inode_size) % block_size;
Block* block = device->GetBlock(block_id);
inode->data_block = block;
uint8_t* buf = inode->data_block->block_data + offset;
inode->data = (struct ext_inode*) buf;
inode->Prelink();
return inode;
}
uint32_t Filesystem::AllocateBlock(BlockGroup* preferred)
{
if ( !sb->s_free_blocks_count )
return errno = ENOSPC, 0;
if ( preferred )
if ( uint32_t block_id = preferred->AllocateBlock() )
return block_id;
for ( uint32_t group_id = 0; group_id < num_groups; group_id++ )
if ( uint32_t block_id = GetBlockGroup(group_id)->AllocateBlock() )
return block_id;
sb->s_free_blocks_count--;
Dirty();
return errno = ENOSPC, 0;
}
uint32_t Filesystem::AllocateInode(BlockGroup* preferred)
{
if ( !sb->s_free_inodes_count )
return errno = ENOSPC, 0;
if ( preferred )
if ( uint32_t inode_id = preferred->AllocateInode() )
return inode_id;
for ( uint32_t group_id = 0; group_id < num_groups; group_id++ )
if ( uint32_t inode_id = GetBlockGroup(group_id)->AllocateInode() )
return inode_id;
sb->s_free_inodes_count--;
Dirty();
return errno = ENOSPC, 0;
}
void Filesystem::FreeBlock(uint32_t block_id)
{
assert(block_id < num_blocks);
uint32_t group_id = (block_id - sb->s_first_data_block) / sb->s_blocks_per_group;
assert(group_id < num_groups);
BlockGroup* group = GetBlockGroup(group_id);
group->FreeBlock(block_id);
group->Unref();
}
void Filesystem::FreeInode(uint32_t inode_id)
{
assert(inode_id < num_inodes);
uint32_t group_id = (inode_id-1) / sb->s_inodes_per_group;
assert(group_id < num_groups);
BlockGroup* group = GetBlockGroup(group_id);
group->FreeInode(inode_id);
group->Unref();
}

64
ext/filesystem.h Normal file
View File

@ -0,0 +1,64 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
filesystem.h
Filesystem.
*******************************************************************************/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
class BlockGroup;
class Device;
class Inode;
class Filesystem
{
public:
Filesystem(Device* device);
~Filesystem();
public:
struct ext_superblock* sb;
Block* sb_block;
Device* device;
BlockGroup** block_groups;
uint32_t block_size;
uint32_t inode_size;
uint32_t num_blocks;
uint32_t num_groups;
uint32_t num_inodes;
Inode* mru_inode;
Inode* lru_inode;
time_t mtime_realtime;
time_t mtime_monotonic;
bool dirty;
public:
BlockGroup* GetBlockGroup(uint32_t group_id);
Inode* GetInode(uint32_t inode_id);
uint32_t AllocateBlock(BlockGroup* preferred = NULL);
uint32_t AllocateInode(BlockGroup* preferred = NULL);
void FreeBlock(uint32_t block_id);
void FreeInode(uint32_t inode_id);
void Dirty();
void Sync();
};
#endif

946
ext/inode.cpp Normal file
View File

@ -0,0 +1,946 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
inode.cpp
Filesystem inode.
*******************************************************************************/
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include "ext-constants.h"
#include "ext-structs.h"
#include "block.h"
#include "blockgroup.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "util.h"
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
Inode::Inode(Filesystem* filesystem, uint32_t inode_id)
{
this->prev_inode = NULL;
this->next_inode = NULL;
this->filesystem = filesystem;
this->reference_count = 1;
this->inode_id = inode_id;
this->dirty = false;
}
Inode::~Inode()
{
Sync();
if ( data_block )
data_block->Unref();
Unlink();
}
uint32_t Inode::Mode()
{
// TODO: Use i_mode_high.
return data->i_mode;
}
void Inode::SetMode(uint32_t mode)
{
// TODO: Use i_mode_high.
data->i_mode = (uint16_t) mode;
Dirty();
}
uint32_t Inode::UserId()
{
// TODO: Use i_uid_high.
return data->i_uid;
}
void Inode::SetUserId(uint32_t user)
{
// TODO: Use i_uid_high.
data->i_uid = (uint16_t) user;
Dirty();
}
uint32_t Inode::GroupId()
{
// TODO: Use i_gid_high.
return data->i_gid;
}
void Inode::SetGroupId(uint32_t group)
{
// TODO: Use i_gid_high.
data->i_gid = (uint16_t) group;
Dirty();
}
uint64_t Inode::Size()
{
bool largefile = filesystem->sb->s_feature_ro_compat &
EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
if ( !EXT2_S_ISREG(data->i_mode) || !largefile )
return data->i_size;
uint64_t lower = data->i_size;
uint64_t upper = data->i_dir_acl;
uint64_t size = lower | (upper << 32ULL);
return size;
}
void Inode::SetSize(uint64_t new_size)
{
bool largefile = filesystem->sb->s_feature_ro_compat &
EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
uint32_t lower = new_size & 0xFFFFFFFF;
uint32_t upper = new_size >> 32;
// TODO: Enforce filesize limit with no largefile support or non-files.
data->i_size = lower;
// TODO: Figure out these i_blocks semantics and how stuff is reserved.
const uint64_t ENTRIES = filesystem->block_size / sizeof(uint32_t);
uint64_t block_direct = sizeof(data->i_block) / sizeof(uint32_t) - 3;
uint64_t block_singly = ENTRIES;
uint64_t block_doubly = block_singly * block_singly;
uint64_t max_direct = block_direct;
uint64_t max_singly = max_direct + block_singly;
uint64_t max_doubly = max_singly + block_doubly;
uint64_t logical_blocks = divup(new_size, (uint64_t) filesystem->block_size);
uint64_t actual_blocks = logical_blocks;
if ( max_direct <= logical_blocks )
actual_blocks += divup(logical_blocks - max_direct, ENTRIES);
if ( max_singly <= logical_blocks )
actual_blocks += divup(logical_blocks - max_singly, ENTRIES * ENTRIES);
if ( max_doubly <= logical_blocks )
actual_blocks += divup(logical_blocks - max_doubly, ENTRIES * ENTRIES * ENTRIES);
data->i_blocks = (actual_blocks * filesystem->block_size) / 512;
if ( EXT2_S_ISREG(data->i_mode) && largefile )
data->i_dir_acl = upper;
Dirty();
}
void Inode::Linked()
{
data->i_links_count++;
Dirty();
}
void Inode::Unlinked()
{
data->i_links_count--;
Dirty();
}
Block* Inode::GetBlockFromTable(Block* table, uint32_t index)
{
if ( uint32_t block_id = ((uint32_t*) table->block_data)[index] )
return filesystem->device->GetBlock(block_id);
uint32_t group_id = (inode_id - 1) / filesystem->sb->s_inodes_per_group;
assert(group_id < filesystem->num_groups);
BlockGroup* block_group = filesystem->GetBlockGroup(group_id);
uint32_t block_id = filesystem->AllocateBlock(block_group);
block_group->Unref();
if ( block_id )
{
Block* block = filesystem->device->GetBlockZeroed(block_id);
((uint32_t*) table->block_data)[index] = block_id;
table->Dirty();
return block;
}
return NULL;
}
Block* Inode::GetBlock(uint64_t offset)
{
const uint64_t ENTRIES = filesystem->block_size / sizeof(uint32_t);
uint64_t block_direct = sizeof(data->i_block) / sizeof(uint32_t) - 3;
uint64_t block_singly = ENTRIES;
uint64_t block_doubly = block_singly * block_singly;
uint64_t block_triply = block_doubly * block_singly;
uint64_t max_direct = block_direct;
uint64_t max_singly = max_direct + block_singly;
uint64_t max_doubly = max_singly + block_doubly;
uint64_t max_triply = max_doubly + block_triply;
uint32_t index;
Block* table = data_block; table->Refer();
Block* block;
uint32_t inode_offset = (uintptr_t) data - (uintptr_t) data_block->block_data;
uint32_t table_offset = offsetof(struct ext_inode, i_block);
uint32_t inode_block_table_offset = (inode_offset + table_offset) / sizeof(uint32_t);
// TODO: It would seem that somebody needs a lesson in induction. :-)
if ( offset < max_direct )
{
offset -= 0;
offset += inode_block_table_offset * 1;
read_direct:
index = offset;
offset %= 1;
block = GetBlockFromTable(table, index);
table->Unref();
if ( !block )
return NULL;
return block;
}
else if ( offset < max_singly )
{
offset -= max_direct;
offset += (inode_block_table_offset + 12) * ENTRIES;
read_singly:
index = offset / ENTRIES;
offset = offset % ENTRIES;
block = GetBlockFromTable(table, index);
table->Unref();
if ( !block )
return NULL;
table = block;
goto read_direct;
}
else if ( offset < max_doubly )
{
offset -= max_singly;
offset += (inode_block_table_offset + 13) * ENTRIES * ENTRIES;
read_doubly:
index = offset / (ENTRIES * ENTRIES);
offset = offset % (ENTRIES * ENTRIES);
block = GetBlockFromTable(table, index);
table->Unref();
if ( !block )
return NULL;
table = block;
goto read_singly;
}
else if ( offset < max_triply )
{
offset -= max_doubly;
offset += (inode_block_table_offset + 14) * ENTRIES * ENTRIES * ENTRIES;
/*read_triply:*/
index = offset / (ENTRIES * ENTRIES * ENTRIES);
offset = offset % (ENTRIES * ENTRIES * ENTRIES);
block = GetBlockFromTable(table, index);
table->Unref();
if ( !block )
return NULL;
table = block;
goto read_doubly;
}
return NULL;
}
bool Inode::FreeIndirect(uint64_t from, uint64_t offset, uint32_t block_id,
int indirection, uint64_t entry_span)
{
const uint64_t ENTRIES = filesystem->block_size / sizeof(uint32_t);
Block* block = filesystem->device->GetBlock(block_id);
uint32_t* table = (uint32_t*) block->block_data;
bool any_children = false;
for ( uint64_t i = 0; i < ENTRIES; i++ )
{
if ( !table[i] )
continue;
uint64_t entry_offset = offset + entry_span * i;
if ( entry_offset < from ||
(indirection &&
FreeIndirect(from, entry_offset, table[i], indirection-1,
entry_span / ENTRIES)) )
{
any_children = true;
continue;
}
filesystem->FreeBlock(table[i]);
table[i] = 0;
block->Dirty();
}
return any_children;
}
void Inode::Truncate(uint64_t new_size)
{
// TODO: Enforce a filesize limit!
uint64_t old_size = Size();
SetSize(new_size);
if ( old_size <= new_size )
return;
uint64_t old_num_blocks = divup(old_size, (uint64_t) filesystem->block_size);
uint64_t new_num_blocks = divup(new_size, (uint64_t) filesystem->block_size);
// Zero out the rest of the last partial block, if any.
uint32_t partial = new_size % filesystem->block_size;
if ( partial )
{
Block* partial_block = GetBlock(new_num_blocks-1);
uint8_t* data = partial_block->block_data;
memset(data + partial, 0, filesystem->block_size - partial);
partial_block->Dirty();
}
const uint64_t ENTRIES = filesystem->block_size / sizeof(uint32_t);
uint64_t block_direct = sizeof(data->i_block) / sizeof(uint32_t) - 3;
uint64_t block_singly = ENTRIES;
uint64_t block_doubly = block_singly * block_singly;
uint64_t block_triply = block_doubly * block_singly;
uint64_t max_direct = block_direct;
uint64_t max_singly = max_direct + block_singly;
uint64_t max_doubly = max_singly + block_doubly;
uint64_t max_triply = max_doubly + block_triply;
for ( uint64_t i = new_num_blocks; i < old_num_blocks && i < 12; i++ )
{
filesystem->FreeBlock(data->i_block[i]);
data->i_block[i] = 0;
}
if ( data->i_block[12] && !FreeIndirect(new_num_blocks, max_direct, data->i_block[12], 0, 1) )
{
filesystem->FreeBlock(data->i_block[12]);
data->i_block[12] = 0;
}
if ( data->i_block[13] && !FreeIndirect(new_num_blocks, max_singly, data->i_block[13], 1, ENTRIES) )
{
filesystem->FreeBlock(data->i_block[13]);
data->i_block[13] = 0;
}
if ( data->i_block[14] && !FreeIndirect(new_num_blocks, max_doubly, data->i_block[14], 2, ENTRIES * ENTRIES) )
{
filesystem->FreeBlock(data->i_block[14]);
data->i_block[14] = 0;
}
(void) max_triply;
Dirty();
}
Inode* Inode::Open(const char* elem, int flags, mode_t mode)
{
if ( !EXT2_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
uint64_t filesize = Size();
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
while ( offset < filesize )
{
uint64_t entry_block_id = offset / filesystem->block_size;
uint64_t entry_block_offset = offset % filesystem->block_size;
if ( block && block_id != entry_block_id )
block->Unref(),
block = NULL;
if ( !block && !(block = GetBlock(block_id = entry_block_id)) )
return NULL;
const uint8_t* block_data = block->block_data + entry_block_offset;
const struct ext_dirent* entry = (const struct ext_dirent*) block_data;
if ( entry->name_len == elem_length &&
memcmp(elem, entry->name, elem_length) == 0 &&
entry->inode )
{
uint8_t file_type = entry->file_type;
uint32_t inode_id = entry->inode;
assert(inode_id);
block->Unref();
if ( flags & O_EXCL )
return errno = EEXIST, (Inode*) NULL;
if ( flags & O_DIRECTORY && file_type && file_type != EXT2_FT_DIR )
return errno = EEXIST, (Inode*) NULL;
Inode* inode = filesystem->GetInode(inode_id);
if ( flags & O_DIRECTORY && !EXT2_S_ISDIR(inode->Mode()) )
{
inode->Unref();
return errno = EEXIST, (Inode*) NULL;
}
if ( S_ISREG(inode->Mode()) && flags & O_TRUNC )
inode->Truncate(0);
return inode;
}
offset += entry->reclen;
}
if ( block )
block->Unref();
if ( flags & O_CREAT )
{
// TODO: Preferred block group!
uint32_t result_inode_id = filesystem->AllocateInode();
if ( !result_inode_id )
return NULL;
Inode* result = filesystem->GetInode(result_inode_id);
memset(result->data, 0, sizeof(*result->data));
result->SetMode((mode & S_SETABLE) | S_IFREG);
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
result->data->i_atime = now.tv_sec;
result->data->i_ctime = now.tv_sec;
result->data->i_mtime = now.tv_sec;
// TODO: Set all the other inode properties!
if ( !Link(elem, result, false) )
{
memset(result->data, 0, sizeof(*result->data));
// TODO: dtime
result->Unref();
filesystem->FreeInode(result_inode_id);
return NULL;
}
return result;
}
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Link(const char* elem, Inode* dest, bool directories)
{
// TODO: Check if dest has checked the link limit!
if ( !EXT2_S_ISDIR(Mode()) )
return errno = ENOTDIR, false;
if ( directories && !EXT2_S_ISDIR(dest->Mode()) )
return errno = ENOTDIR, false;
if ( !directories && EXT2_S_ISDIR(dest->Mode()) )
return errno = EISDIR, false;
// Search for a hole in which we can store the new directory entry and stop
// if we meet an existing link with the requested name.
size_t elem_length = strlen(elem);
size_t new_entry_size = roundup(sizeof(struct ext_dirent) + elem_length, (size_t) 4);
uint64_t filesize = Size();
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
bool found_hole = false;
bool splitting = false;
uint64_t hole_block_id = 0;
uint64_t hole_block_offset = 0;
while ( offset < filesize )
{
uint64_t entry_block_id = offset / filesystem->block_size;
uint64_t entry_block_offset = offset % filesystem->block_size;
if ( block && block_id != entry_block_id )
block->Unref(),
block = NULL;
if ( !block && !(block = GetBlock(block_id = entry_block_id)) )
return NULL;
const uint8_t* block_data = block->block_data + entry_block_offset;
const struct ext_dirent* entry = (const struct ext_dirent*) block_data;
if ( entry->name_len == elem_length &&
memcmp(elem, entry->name, elem_length) == 0 &&
entry->inode )
{
block->Unref();
return errno = EEXIST, false;
}
if ( !found_hole )
{
size_t entry_size = roundup(sizeof(struct ext_dirent) + entry->name_len, (size_t) 4);
if ( (!entry->name[0] || !entry->inode) && new_entry_size <= entry->reclen )
{
hole_block_id = entry_block_id;
hole_block_offset = entry_block_offset;
new_entry_size = entry->reclen;
found_hole = true;
}
else if ( new_entry_size <= entry->reclen - entry_size )
{
hole_block_id = entry_block_id;
hole_block_offset = entry_block_offset;
new_entry_size = entry->reclen - entry_size;
splitting = true;
found_hole = true;
}
}
offset += entry->reclen;
}
// We'll append another block if we failed to find a suitable hole.
if ( !found_hole )
{
hole_block_id = filesize / filesystem->block_size;
hole_block_offset = filesize % filesystem->block_size;
new_entry_size = filesystem->block_size;
}
if ( block && block_id != hole_block_id )
block->Unref(),
block = NULL;
if ( !block && !(block = GetBlock(block_id = hole_block_id)) )
return NULL;
uint8_t* block_data = block->block_data + hole_block_offset;
struct ext_dirent* entry = (struct ext_dirent*) block_data;
// If we found a directory entry with room at the end, split it!
if ( splitting )
{
entry->reclen = roundup(sizeof(struct ext_dirent) + entry->name_len, (size_t) 4);
assert(entry->reclen);
// Block marked dirty below.
block_data += entry->reclen;
entry = (struct ext_dirent*) block_data;
}
// Write the new directory entry.
entry->inode = dest->inode_id;
entry->reclen = new_entry_size;
entry->name_len = elem_length;
// TODO: Only do this if the filetype feature is on!
entry->file_type = EXT2_FT_OF_MODE(dest->Mode());
strncpy(entry->name, elem, new_entry_size - sizeof(struct ext_dirent));
assert(entry->reclen);
block->Dirty();
dest->Linked();
if ( !found_hole )
SetSize(Size() + filesystem->block_size);
block->Unref();
return true;
}
Inode* Inode::Unlink(const char* elem, bool directories, bool force)
{
if ( !EXT2_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
uint32_t block_size = filesystem->block_size;
uint64_t filesize = Size();
uint64_t num_blocks = divup(filesize, (uint64_t) block_size);
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
struct ext_dirent* last_entry = NULL;
while ( offset < filesize )
{
uint64_t entry_block_id = offset / block_size;
uint64_t entry_block_offset = offset % block_size;
if ( block && block_id != entry_block_id )
last_entry = NULL,
block->Unref(),
block = NULL;
if ( !block && !(block = GetBlock(block_id = entry_block_id)) )
return NULL;
uint8_t* block_data = block->block_data + entry_block_offset;
struct ext_dirent* entry = (struct ext_dirent*) block_data;
assert(entry->reclen);
if ( entry->name_len == elem_length &&
memcmp(elem, entry->name, elem_length) == 0 &&
entry->inode )
{
Inode* inode = filesystem->GetInode(entry->inode);
if ( !force && directories && !EXT2_S_ISDIR(inode->Mode()) )
{
inode->Unref();
block->Unref();
return errno = ENOTDIR, (Inode*) NULL;
}
if ( !force && directories && !inode->IsEmptyDirectory() )
{
inode->Unref();
block->Unref();
return errno = ENOTEMPTY, (Inode*) NULL;
}
if ( !force && !directories && EXT2_S_ISDIR(inode->Mode()) )
{
inode->Unref();
block->Unref();
return errno = EISDIR, (Inode*) NULL;
}
inode->Unlinked();
entry->inode = 0;
entry->name_len = 0;
entry->file_type = 0;
// Merge the current entry with the previous if any.
if ( last_entry )
{
assert(entry->reclen);
last_entry->reclen += entry->reclen;
memset(entry, 0, entry->reclen);
entry = last_entry;
assert(last_entry->reclen);
}
assert(entry->reclen);
strncpy(entry->name + entry->name_len, "",
entry->reclen - sizeof(struct ext_dirent) - entry->name_len);
assert(entry->reclen);
block->Dirty();
// If the entire block is empty, we'll need to remove it.
if ( !entry->name[0] && entry->reclen == block_size )
{
// If this is not the last block, we'll make it. This is faster
// than shifting the entire directory a single block. We don't
// actually copy this block to the end, since we'll truncate it
// regardless.
if ( entry_block_id + 1 != num_blocks )
{
Block* last_block = GetBlock(num_blocks-1);
memcpy(block->block_data, last_block->block_data, block_size);
block->Dirty();
last_block->Unref();
}
Truncate(filesize - block_size);
}
block->Unref();
return inode;
}
offset += entry->reclen;
last_entry = entry;
}
if ( block )
block->Unref();
return errno = ENOENT, (Inode*) NULL;
}
ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !EXT2_S_ISREG(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
if ( SSIZE_MAX < s_count )
s_count = SSIZE_MAX;
uint64_t sofar = 0;
uint64_t count = (uint64_t) s_count;
uint64_t offset = (uint64_t) o_offset;
uint64_t file_size = Size();
if ( file_size <= offset )
return 0;
if ( file_size - offset < count )
count = file_size - offset;
while ( sofar < count )
{
uint64_t block_id = offset / filesystem->block_size;
uint32_t block_offset = offset % filesystem->block_size;
uint32_t block_left = filesystem->block_size - block_offset;
Block* block = GetBlock(block_id);
if ( !block )
return sofar ? sofar : -1;
size_t amount = count - sofar < block_left ? count - sofar : block_left;
memcpy(buf + sofar, block->block_data + block_offset, amount);
sofar += amount;
offset += amount;
block->Unref();
}
return (ssize_t) sofar;
}
ssize_t Inode::WriteAt(const uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !EXT2_S_ISREG(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
if ( SSIZE_MAX < s_count )
s_count = SSIZE_MAX;
uint64_t sofar = 0;
uint64_t count = (uint64_t) s_count;
uint64_t offset = (uint64_t) o_offset;
uint64_t file_size = Size();
uint64_t end_at = offset + count;
if ( offset < end_at )
/* TODO: Overflow! off_t overflow? */{};
if ( file_size < end_at )
Truncate(end_at);
while ( sofar < count )
{
uint64_t block_id = offset / filesystem->block_size;
uint32_t block_offset = offset % filesystem->block_size;
uint32_t block_left = filesystem->block_size - block_offset;
Block* block = GetBlock(block_id);
if ( !block )
return sofar ? sofar : -1;
size_t amount = count - sofar < block_left ? count - sofar : block_left;
memcpy(block->block_data + block_offset, buf + sofar, amount);
block->Dirty();
sofar += amount;
offset += amount;
block->Unref();
}
return (ssize_t) sofar;
}
bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname)
{
if ( !strcmp(oldname, ".") || !strcmp(oldname, "..") ||
!strcmp(newname, ".") || !strcmp(newname, "..") )
return errno = EPERM, false;
Inode* src_inode = olddir->Open(oldname, O_RDONLY, 0);
if ( !src_inode )
return false;
if ( Inode* dst_inode = Open(newname, O_RDONLY, 0) )
{
if ( src_inode->inode_id == dst_inode->inode_id )
return dst_inode->Unref(), src_inode->Unref(), 0;
dst_inode->Unref();
}
// TODO: Prove that this cannot fail and handle such a situation.
if ( EXT2_S_ISDIR(src_inode->Mode()) )
{
if ( !Unlink(newname, true) && errno != ENOENT )
return src_inode->Unref(), false;
Link(newname, src_inode, true);
olddir->Unlink(oldname, true, true);
if ( olddir != this )
{
src_inode->Unlink("..", true, true);
src_inode->Link("..", this, true);
}
}
else
{
if ( !Unlink(newname, false) && errno != ENOENT )
return src_inode->Unref(), false;
Link(newname, src_inode, false);
olddir->Unlink(oldname, false);
}
src_inode->Unref();
return true;
}
Inode* Inode::CreateDirectory(const char* path, mode_t mode)
{
// TODO: Preferred block group!
uint32_t result_inode_id = filesystem->AllocateInode();
if ( !result_inode_id )
return NULL;
Inode* result = filesystem->GetInode(result_inode_id);
memset(result->data, 0, sizeof(*result->data));
result->SetMode((mode & S_SETABLE) | S_IFDIR);
// Increase the directory count statistics.
uint32_t group_id = (result->inode_id - 1) / filesystem->sb->s_inodes_per_group;
assert(group_id < filesystem->num_groups);
BlockGroup* block_group = filesystem->GetBlockGroup(group_id);
block_group->data->bg_used_dirs_count++;
block_group->Dirty();
block_group->Unref();
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
result->data->i_atime = now.tv_sec;
result->data->i_ctime = now.tv_sec;
result->data->i_mtime = now.tv_sec;
// TODO: Set all the other inode properties!
if ( !Link(path, result, true) )
{
error:
result->Truncate(0);
memset(result->data, 0, sizeof(*result->data));
// TODO: dtime
result->Unref();
filesystem->FreeInode(result_inode_id);
return NULL;
}
if ( !result->Link(".", result, true) )
{
Unlink(path, true);
goto error;
}
if ( !result->Link("..", this, true) )
{
result->Unlink(".", true);
Unlink(path, true);
goto error;
}
return result;
}
bool Inode::RemoveDirectory(const char* path)
{
Inode* result = Unlink(path, true);
if ( !result )
return false;
result->Unlink("..", true);
result->Unlink(".", true);
result->Truncate(0);
// Decrease the directory count statistics.
uint32_t group_id = (result->inode_id - 1) / filesystem->sb->s_inodes_per_group;
assert(group_id < filesystem->num_groups);
BlockGroup* block_group = filesystem->GetBlockGroup(group_id);
block_group->data->bg_used_dirs_count--;
block_group->Dirty();
block_group->Unref();
result->Unref();
return true;
}
bool Inode::IsEmptyDirectory()
{
if ( !EXT2_S_ISDIR(Mode()) )
return errno = ENOTDIR, false;
uint32_t block_size = filesystem->block_size;
uint64_t filesize = Size();
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
while ( offset < filesize )
{
uint64_t entry_block_id = offset / block_size;
uint64_t entry_block_offset = offset % block_size;
if ( block && block_id != entry_block_id )
block->Unref(),
block = NULL;
if ( !block && !(block = GetBlock(block_id = entry_block_id)) )
return false;
uint8_t* block_data = block->block_data + entry_block_offset;
struct ext_dirent* entry = (struct ext_dirent*) block_data;
if ( entry->inode &&
!((entry->name_len == 1 && entry->name[0] == '.') ||
(entry->name_len == 2 && entry->name[0] == '.' &&
entry->name[1] == '.' )) )
{
block->Unref();
return false;
}
offset += entry->reclen;
}
if ( block )
block->Unref();
return true;
}
void Inode::Delete()
{
assert(!data->i_links_count);
assert(!reference_count);
assert(!remote_reference_count);
Truncate(0);
uint32_t deleted_inode_id = inode_id;
memset(data, 0, sizeof(*data));
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
data->i_dtime = now.tv_sec;
Dirty();
delete this;
filesystem->FreeInode(deleted_inode_id);
}
void Inode::Refer()
{
reference_count++;
}
void Inode::Unref()
{
reference_count--;
if ( !reference_count && !remote_reference_count )
{
if ( !data->i_links_count )
Delete();
else
delete this;
}
}
void Inode::RemoteRefer()
{
remote_reference_count++;
}
void Inode::RemoteUnref()
{
remote_reference_count--;
if ( !reference_count && !remote_reference_count )
{
if ( !data->i_links_count )
Delete();
else
delete this;
}
}
void Inode::Dirty()
{
dirty = true;
data_block->Dirty();
Use();
}
void Inode::Sync()
{
if ( dirty )
data_block->Sync();
dirty = false;
}
void Inode::Use()
{
data_block->Use();
Unlink();
Prelink();
}
void Inode::Unlink()
{
(prev_inode ? prev_inode->next_inode : filesystem->mru_inode) = next_inode;
(next_inode ? next_inode->prev_inode : filesystem->lru_inode) = prev_inode;
}
void Inode::Prelink()
{
prev_inode = NULL;
next_inode = filesystem->mru_inode;
if ( filesystem->mru_inode )
filesystem->mru_inode->prev_inode = this;
filesystem->mru_inode = this;
if ( !filesystem->lru_inode )
filesystem->lru_inode = this;
}

84
ext/inode.h Normal file
View File

@ -0,0 +1,84 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
inode.h
Filesystem inode.
*******************************************************************************/
#ifndef INODE_H
#define INODE_H
class Block;
class Filesystem;
class Inode
{
public:
Inode(Filesystem* filesystem, uint32_t inode_id);
~Inode();
public:
Inode* prev_inode;
Inode* next_inode;
Block* data_block;
struct ext_inode* data;
Filesystem* filesystem;
size_t reference_count;
size_t remote_reference_count;
uint32_t inode_id;
bool dirty;
public:
uint32_t Mode();
uint32_t UserId();
uint32_t GroupId();
uint64_t Size();
void SetMode(uint32_t mode);
void SetUserId(uint32_t user);
void SetGroupId(uint32_t group);
void SetSize(uint64_t new_size);
void Truncate(uint64_t new_size);
bool FreeIndirect(uint64_t from, uint64_t offset, uint32_t block_id,
int indirection, uint64_t entry_span);
Block* GetBlock(uint64_t offset);
Block* GetBlockFromTable(Block* table, uint32_t index);
Inode* Open(const char* elem, int flags, mode_t mode);
bool Link(const char* elem, Inode* dest, bool directories);
Inode* Unlink(const char* elem, bool directories, bool force=false);
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
ssize_t WriteAt(const uint8_t* buffer, size_t count, off_t offset);
bool Rename(Inode* olddir, const char* oldname, const char* newname);
Inode* CreateDirectory(const char* path, mode_t mode);
bool RemoveDirectory(const char* path);
bool IsEmptyDirectory();
void Refer();
void Unref();
void RemoteRefer();
void RemoteUnref();
void Sync();
void Dirty();
void Use();
void Unlink();
void Prelink();
void Linked();
void Unlinked();
void Delete();
};
#endif

73
ext/ioleast.cpp Normal file
View File

@ -0,0 +1,73 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
ioleast.cpp
Sortix functions for reliable reads and writes.
*******************************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include "ioleast.h"
#if !defined(__sortix__)
size_t preadleast(int fd, void* buf, size_t least, size_t max, off_t off)
{
ssize_t amount = pread(fd, buf, max, off);
if ( amount < 0 ) { return 0; }
if ( least && !amount ) { return 0; }
if ( (size_t) amount < least )
{
void* nextbuf = (uint8_t*) buf + amount;
size_t nextleast = least - amount;
size_t nextmax = max - amount;
off_t nextoff = off + amount;
amount += preadleast(fd, nextbuf, nextleast, nextmax, nextoff);
}
return amount;
}
size_t preadall(int fd, void* buf, size_t count, off_t off)
{
return preadleast(fd, buf, count, count, off);
}
size_t pwriteleast(int fd, const void* buf, size_t least, size_t max, off_t off)
{
ssize_t amount = pwrite(fd, buf, max, off);
if ( amount < 0 ) { return 0; }
if ( least && !amount ) { return 0; }
if ( (size_t) amount < least )
{
const void* nextbuf = (const uint8_t*) buf + amount;
size_t nextleast = least - amount;
size_t nextmax = max - amount;
off_t nextoff = off + amount;
amount += pwriteleast(fd, nextbuf, nextleast, nextmax, nextoff);
}
return amount;
}
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
{
return pwriteleast(fd, buf, count, count, off);
}
#endif

148
ext/ioleast.h Normal file
View File

@ -0,0 +1,148 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012, 2013.
This file is part of the Sortix C Library.
The Sortix C Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Sortix C 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
ioleast.h
Versions of {,p}{read,write} that don't return until it has returned as much
data as requested, end of file, or an error occurs. This is sometimes needed
as read(2) and write(2) is not always guaranteed to fill up the entire
buffer or write it all.
*******************************************************************************/
#ifndef SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
#define SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
#if defined(__sortix__) || defined(__sortix_libc__)
#include_next <ioleast.h>
#else
#include <sys/types.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#if !defined(EEOF) && defined(EIO)
#define EEOF EIO
#endif
__attribute__((unused)) static inline
size_t readleast(int fd, void* buf, size_t least, size_t max)
{
ssize_t amount = read(fd, buf, max);
if ( amount < 0 )
return 0;
if ( least && !amount )
return errno = EEOF, 0;
if ( (size_t) amount < least )
{
void* nextbuf = (uint8_t*) buf + amount;
size_t nextleast = least - amount;
size_t nextmax = max - amount;
amount += readleast(fd, nextbuf, nextleast, nextmax);
}
return amount;
}
__attribute__((unused)) static inline
size_t writeleast(int fd, const void* buf, size_t least, size_t max)
{
ssize_t amount = write(fd, buf, max);
if ( amount < 0 )
return 0;
if ( least && !amount )
return errno = EEOF, 0;
if ( (size_t) amount < least )
{
const void* nextbuf = (const uint8_t*) buf + amount;
size_t nextleast = least - amount;
size_t nextmax = max - amount;
amount += writeleast(fd, nextbuf, nextleast, nextmax);
}
return amount;
}
__attribute__((unused)) static inline
size_t preadleast(int fd, void* buf, size_t least, size_t max, off_t off)
{
ssize_t amount = pread(fd, buf, max, off);
if ( amount < 0 )
return 0;
if ( least && !amount )
return errno = EEOF, 0;
if ( (size_t) amount < least )
{
void* nextbuf = (uint8_t*) buf + amount;
size_t nextleast = least - amount;
size_t nextmax = max - amount;
off_t nextoff = off + amount;
amount += preadleast(fd, nextbuf, nextleast, nextmax, nextoff);
}
return amount;
}
__attribute__((unused)) static inline
size_t pwriteleast(int fd, const void* buf, size_t least, size_t max, off_t off)
{
ssize_t amount = pwrite(fd, buf, max, off);
if ( amount < 0 )
return 0;
if ( least && !amount )
return errno = EEOF, 0;
if ( (size_t) amount < least )
{
const void* nextbuf = (const uint8_t*) buf + amount;
size_t nextleast = least - amount;
size_t nextmax = max - amount;
off_t nextoff = off + amount;
amount += pwriteleast(fd, nextbuf, nextleast, nextmax, nextoff);
}
return amount;
}
__attribute__((unused)) static inline
size_t readall(int fd, void* buf, size_t count)
{
return readleast(fd, buf, count, count);
}
__attribute__((unused)) static inline
size_t writeall(int fd, const void* buf, size_t count)
{
return writeleast(fd, buf, count, count);
}
__attribute__((unused)) static inline
size_t preadall(int fd, void* buf, size_t count, off_t off)
{
return preadleast(fd, buf, count, count, off);
}
__attribute__((unused)) static inline
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
{
return pwriteleast(fd, buf, count, count, off);
}
#endif
#endif

52
ext/util.h Normal file
View File

@ -0,0 +1,52 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program 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. If not, see <http://www.gnu.org/licenses/>.
util.h
Utility functions for the filesystem implementation.
*******************************************************************************/
#ifndef UTIL_H
#define UTIL_H
template <class T> T divup(T a, T b)
{
return a/b + (a % b ? 1 : 0);
}
template <class T> T roundup(T a, T b)
{
return a % b ? a + b - a % b : a;
}
inline bool checkbit(const uint8_t* bitmap, size_t bit)
{
uint8_t bits = bitmap[bit / 8UL];
return bits & (1U << (bit % 8UL));
}
inline void setbit(uint8_t* bitmap, size_t bit)
{
bitmap[bit / 8UL] |= 1U << (bit % 8UL);
}
inline void clearbit(uint8_t* bitmap, size_t bit)
{
bitmap[bit / 8UL] &= ~(1U << (bit % 8UL));
}
#endif