Add ext2 filesystem implementation.
This commit is contained in:
parent
0708482d9b
commit
b308c764cf
19 changed files with 3997 additions and 1 deletions
2
Makefile
2
Makefile
|
@ -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
2
ext/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
extfs
|
||||
*.o
|
38
ext/Makefile
Normal file
38
ext/Makefile
Normal 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
115
ext/block.cpp
Normal 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
56
ext/block.h
Normal 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
238
ext/blockgroup.cpp
Normal 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
70
ext/blockgroup.h
Normal 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
96
ext/device.cpp
Normal 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
53
ext/device.h
Normal 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
135
ext/ext-constants.h
Normal 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
131
ext/ext-structs.h
Normal 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
1481
ext/extfs.cpp
Normal file
File diff suppressed because it is too large
Load diff
214
ext/filesystem.cpp
Normal file
214
ext/filesystem.cpp
Normal 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
64
ext/filesystem.h
Normal 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
946
ext/inode.cpp
Normal 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
84
ext/inode.h
Normal 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
73
ext/ioleast.cpp
Normal 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
148
ext/ioleast.h
Normal 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
52
ext/util.h
Normal 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
|
Loading…
Add table
Reference in a new issue