Add ext2 filesystem implementation.
This commit is contained in:
parent
0708482d9b
commit
b308c764cf
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ MAKEFILE_NOT_MEANT_FOR_SORTIX=1
|
||||||
include compiler.mak
|
include compiler.mak
|
||||||
include version.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
|
ifndef SYSROOT
|
||||||
SYSROOT:=$(shell pwd)/sysroot
|
SYSROOT:=$(shell pwd)/sysroot
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
extfs
|
||||||
|
*.o
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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()
|
||||||
|
{
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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…
Reference in New Issue