Add fatfs(8).
This commit is contained in:
parent
f3c7fece10
commit
bb5551bdfe
|
@ -49,6 +49,7 @@
|
||||||
#include <mount/blockdevice.h>
|
#include <mount/blockdevice.h>
|
||||||
#include <mount/devices.h>
|
#include <mount/devices.h>
|
||||||
#include <mount/ext2.h>
|
#include <mount/ext2.h>
|
||||||
|
#include <mount/fat.h>
|
||||||
#include <mount/filesystem.h>
|
#include <mount/filesystem.h>
|
||||||
#include <mount/gpt.h>
|
#include <mount/gpt.h>
|
||||||
#include <mount/harddisk.h>
|
#include <mount/harddisk.h>
|
||||||
|
@ -1771,9 +1772,9 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
bool is_gpt = current_pt_type == PARTITION_TABLE_TYPE_GPT;
|
bool is_gpt = current_pt_type == PARTITION_TABLE_TYPE_GPT;
|
||||||
const char* question = "Format a filesystem? (no/ext2)";
|
const char* question = "Format a filesystem? (no/ext2)";
|
||||||
if ( is_mbr )
|
if ( is_mbr )
|
||||||
question = "Format a filesystem? (no/ext2/extended)";
|
question = "Format a filesystem? (no/ext2/extended/fat)";
|
||||||
else if ( is_gpt )
|
else if ( is_gpt )
|
||||||
question = "Format a filesystem? (no/ext2/biosboot)";
|
question = "Format a filesystem? (no/ext2/biosboot/efi/fat)";
|
||||||
if ( 5 <= argc )
|
if ( 5 <= argc )
|
||||||
strlcpy(fstype, argv[4], sizeof(fstype));
|
strlcpy(fstype, argv[4], sizeof(fstype));
|
||||||
else
|
else
|
||||||
|
@ -1781,7 +1782,9 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
if ( strcmp(fstype, "no") != 0 &&
|
if ( strcmp(fstype, "no") != 0 &&
|
||||||
strcmp(fstype, "ext2") != 0 &&
|
strcmp(fstype, "ext2") != 0 &&
|
||||||
(!is_mbr || strcmp(fstype, "extended") != 0) &&
|
(!is_mbr || strcmp(fstype, "extended") != 0) &&
|
||||||
(!is_gpt || strcmp(fstype, "biosboot") != 0) )
|
(!is_gpt || strcmp(fstype, "biosboot") != 0) &&
|
||||||
|
(!is_gpt || strcmp(fstype, "efi") != 0) &&
|
||||||
|
strcmp(fstype, "fat") != 0 )
|
||||||
{
|
{
|
||||||
command_errorx("%s: %s: Invalid filesystem choice: %s",
|
command_errorx("%s: %s: Invalid filesystem choice: %s",
|
||||||
argv[0], device_name(current_hd->path), fstype);
|
argv[0], device_name(current_hd->path), fstype);
|
||||||
|
@ -1801,14 +1804,18 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
char mountpoint[256] = "";
|
char mountpoint[256] = "";
|
||||||
bool mountable = !strcmp(fstype, "ext2");
|
// TODO: Get this information from libmount.
|
||||||
|
bool mountable = !strcmp(fstype, "ext2") ||
|
||||||
|
!strcmp(fstype, "fat") ||
|
||||||
|
!strcmp(fstype, "efi");
|
||||||
while ( mountable )
|
while ( mountable )
|
||||||
{
|
{
|
||||||
|
const char* def = !strcmp(fstype, "efi") ? "/boot/efi" : "no";
|
||||||
if ( 6 <= argc )
|
if ( 6 <= argc )
|
||||||
strlcpy(mountpoint, argv[5], sizeof(mountpoint));
|
strlcpy(mountpoint, argv[5], sizeof(mountpoint));
|
||||||
else
|
else
|
||||||
prompt(mountpoint, sizeof(mountpoint),
|
prompt(mountpoint, sizeof(mountpoint),
|
||||||
"Where to mount partition? (mountpoint or 'no')", "no");
|
"Where to mount partition? (mountpoint or 'no')", def);
|
||||||
if ( !strcmp(mountpoint, "no") )
|
if ( !strcmp(mountpoint, "no") )
|
||||||
{
|
{
|
||||||
mountpoint[0] = '\0';
|
mountpoint[0] = '\0';
|
||||||
|
@ -2003,6 +2010,10 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
const char* type_uuid_str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
const char* type_uuid_str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
||||||
if ( !strcmp(fstype, "biosboot") )
|
if ( !strcmp(fstype, "biosboot") )
|
||||||
type_uuid_str = BIOSBOOT_GPT_TYPE_UUID;
|
type_uuid_str = BIOSBOOT_GPT_TYPE_UUID;
|
||||||
|
else if ( !strcmp(fstype, "efi") )
|
||||||
|
type_uuid_str = ESP_GPT_TYPE_UUID;
|
||||||
|
else if ( !strcmp(fstype, "fat") )
|
||||||
|
type_uuid_str = BDP_GPT_TYPE_UUID;
|
||||||
uuid_from_string(p.partition_type_guid, type_uuid_str);
|
uuid_from_string(p.partition_type_guid, type_uuid_str);
|
||||||
arc4random_buf(p.unique_partition_guid, sizeof(p.unique_partition_guid));
|
arc4random_buf(p.unique_partition_guid, sizeof(p.unique_partition_guid));
|
||||||
off_t pstart = hole->start + start;
|
off_t pstart = hole->start + start;
|
||||||
|
@ -2162,6 +2173,67 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( !strcmp(fstype, "efi") || !strcmp(fstype, "fat") )
|
||||||
|
{
|
||||||
|
printf("(Formatting %s as %s...)\n", device_name(p->path), fstype);
|
||||||
|
// TODO: Zero superblock?
|
||||||
|
// TODO: Run this in its own foreground process group so ^C works.
|
||||||
|
pid_t child_pid = fork();
|
||||||
|
if ( child_pid < 0 )
|
||||||
|
{
|
||||||
|
command_error("%s: fork", argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char* mkfs_argv[] =
|
||||||
|
{
|
||||||
|
"mformat",
|
||||||
|
"-i",
|
||||||
|
p->path,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
if ( child_pid == 0 )
|
||||||
|
{
|
||||||
|
execvp(mkfs_argv[0], (char* const*) mkfs_argv);
|
||||||
|
warn("%s", mkfs_argv[0]);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
int status;
|
||||||
|
waitpid(child_pid, &status, 0);
|
||||||
|
if ( WIFEXITED(status) && WEXITSTATUS(status) == 127 )
|
||||||
|
{
|
||||||
|
command_errorx("%s: Failed to format filesystem (%s is not installed)",
|
||||||
|
argv[0], mkfs_argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( WIFEXITED(status) && WEXITSTATUS(status) != 0 )
|
||||||
|
{
|
||||||
|
command_errorx("%s: Failed to format filesystem", argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( WIFSIGNALED(status) )
|
||||||
|
{
|
||||||
|
command_errorx("%s: Failed to format filesystem (%s)",
|
||||||
|
argv[0], strsignal(WTERMSIG(status)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("(Formatted %s as %s)\n", device_name(p->path), fstype);
|
||||||
|
scan_partition(p);
|
||||||
|
if ( !p->bdev.fs /* TODO: || !(p->bdev.fs->flags & FILESYSTEM_FLAG_UUID) */ )
|
||||||
|
{
|
||||||
|
command_errorx("%s: %s: Failed to scan expected %s filesystem",
|
||||||
|
argv[0], device_name(p->path), fstype);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( mountpoint[0] )
|
||||||
|
{
|
||||||
|
if ( !add_blockdevice_to_fstab(&p->bdev, mountpoint) )
|
||||||
|
{
|
||||||
|
command_error("%s: %s: Failed to add partition", argv[0], fstab_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_mktable(size_t argc, char** argv)
|
static void on_mktable(size_t argc, char** argv)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
fatfs
|
||||||
|
*.o
|
|
@ -0,0 +1,34 @@
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/version.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||||
|
CXXFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
|
||||||
|
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -fcheck-new
|
||||||
|
|
||||||
|
LIBS:=$(LIBS)
|
||||||
|
|
||||||
|
ifeq ($(HOST_IS_SORTIX),0)
|
||||||
|
PTHREAD_OPTION:=-pthread
|
||||||
|
LIBS:=$(LIBS) -lfuse
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
|
endif
|
||||||
|
|
||||||
|
BINARIES:=fatfs
|
||||||
|
|
||||||
|
all: $(BINARIES)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||||
|
install $(BINARIES) $(DESTDIR)$(SBINDIR)
|
||||||
|
|
||||||
|
fatfs: *.cpp *.h
|
||||||
|
$(CXX) $(PTHREAD_OPTION) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BINARIES) *.o
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* block.cpp
|
||||||
|
* Blocks in the filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "ioleast.h"
|
||||||
|
|
||||||
|
Block::Block()
|
||||||
|
{
|
||||||
|
this->block_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block::Block(Device* device, uint32_t block_id)
|
||||||
|
{
|
||||||
|
Construct(device, block_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Construct(Device* device, uint32_t block_id)
|
||||||
|
{
|
||||||
|
this->modify_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
this->transit_done_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->prev_block = NULL;
|
||||||
|
this->next_block = NULL;
|
||||||
|
this->prev_hashed = NULL;
|
||||||
|
this->next_hashed = NULL;
|
||||||
|
this->prev_dirty = NULL;
|
||||||
|
this->next_dirty = NULL;
|
||||||
|
this->device = device;
|
||||||
|
this->reference_count = 1;
|
||||||
|
this->block_id = block_id;
|
||||||
|
this->dirty = false;
|
||||||
|
this->is_in_transit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block::~Block()
|
||||||
|
{
|
||||||
|
Destruct();
|
||||||
|
delete[] block_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Destruct()
|
||||||
|
{
|
||||||
|
Sync();
|
||||||
|
Unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Refer()
|
||||||
|
{
|
||||||
|
reference_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Unref()
|
||||||
|
{
|
||||||
|
if ( !--reference_count )
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
device->block_count--;
|
||||||
|
delete this;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Sync()
|
||||||
|
{
|
||||||
|
if ( device->has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&device->sync_thread_lock);
|
||||||
|
while ( dirty || is_in_transit )
|
||||||
|
pthread_cond_wait(&transit_done_cond, &device->sync_thread_lock);
|
||||||
|
pthread_mutex_unlock(&device->sync_thread_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !dirty )
|
||||||
|
return;
|
||||||
|
dirty = false;
|
||||||
|
(prev_dirty ? prev_dirty->next_dirty : device->dirty_block) = next_dirty;
|
||||||
|
if ( next_dirty )
|
||||||
|
next_dirty->prev_dirty = prev_dirty;
|
||||||
|
prev_dirty = NULL;
|
||||||
|
next_dirty = NULL;
|
||||||
|
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::BeginWrite()
|
||||||
|
{
|
||||||
|
assert(device->write);
|
||||||
|
pthread_mutex_lock(&modify_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::FinishWrite()
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&modify_lock);
|
||||||
|
pthread_mutex_lock(&device->sync_thread_lock);
|
||||||
|
if ( !dirty )
|
||||||
|
{
|
||||||
|
dirty = true;
|
||||||
|
prev_dirty = NULL;
|
||||||
|
next_dirty = device->dirty_block;
|
||||||
|
if ( next_dirty )
|
||||||
|
next_dirty->prev_dirty = this;
|
||||||
|
device->dirty_block = this;
|
||||||
|
pthread_cond_signal(&device->sync_thread_cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&device->sync_thread_lock);
|
||||||
|
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,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* block.h
|
||||||
|
* Blocks in the filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLOCK_H
|
||||||
|
#define BLOCK_H
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
|
||||||
|
class Block
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Block();
|
||||||
|
Block(Device* device, uint32_t block_id);
|
||||||
|
~Block();
|
||||||
|
void Construct(Device* device, uint32_t block_id);
|
||||||
|
void Destruct();
|
||||||
|
|
||||||
|
public:
|
||||||
|
pthread_mutex_t modify_lock;
|
||||||
|
pthread_cond_t transit_done_cond;
|
||||||
|
Block* prev_block;
|
||||||
|
Block* next_block;
|
||||||
|
Block* prev_hashed;
|
||||||
|
Block* next_hashed;
|
||||||
|
Block* prev_dirty;
|
||||||
|
Block* next_dirty;
|
||||||
|
Device* device;
|
||||||
|
size_t reference_count;
|
||||||
|
uint32_t block_id;
|
||||||
|
bool dirty;
|
||||||
|
bool is_in_transit;
|
||||||
|
uint8_t* block_data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Refer();
|
||||||
|
void Unref();
|
||||||
|
void Sync();
|
||||||
|
void BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Use();
|
||||||
|
void Unlink();
|
||||||
|
void Prelink();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
this->inode_bitmap_chunk_i = 0;
|
||||||
|
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 ( block_bitmap_chunk )
|
||||||
|
block_bitmap_chunk->Unref();
|
||||||
|
if ( inode_bitmap_chunk )
|
||||||
|
inode_bitmap_chunk->Unref();
|
||||||
|
if ( data_block )
|
||||||
|
data_block->Unref();
|
||||||
|
filesystem->block_groups[group_id] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BlockGroup::AllocateBlock()
|
||||||
|
{
|
||||||
|
if ( !filesystem->device->write )
|
||||||
|
return errno = EROFS, 0;
|
||||||
|
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);
|
||||||
|
if ( !block_bitmap_chunk )
|
||||||
|
return 0;
|
||||||
|
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;
|
||||||
|
// TODO: This can be made faster by caching if previous bits were set.
|
||||||
|
for ( ; block_bitmap_chunk_i < num_bits; block_bitmap_chunk_i++ )
|
||||||
|
{
|
||||||
|
if ( !checkbit(chunk_bits, block_bitmap_chunk_i) )
|
||||||
|
{
|
||||||
|
block_bitmap_chunk->BeginWrite();
|
||||||
|
setbit(chunk_bits, block_bitmap_chunk_i);
|
||||||
|
block_bitmap_chunk->FinishWrite();
|
||||||
|
BeginWrite();
|
||||||
|
data->bg_free_blocks_count--;
|
||||||
|
FinishWrite();
|
||||||
|
filesystem->BeginWrite();
|
||||||
|
filesystem->sb->s_free_blocks_count--;
|
||||||
|
filesystem->FinishWrite();
|
||||||
|
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->Unref();
|
||||||
|
block_bitmap_chunk = NULL;
|
||||||
|
}
|
||||||
|
BeginWrite();
|
||||||
|
data->bg_free_blocks_count = 0;
|
||||||
|
FinishWrite();
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BlockGroup::AllocateInode()
|
||||||
|
{
|
||||||
|
if ( !filesystem->device->write )
|
||||||
|
return errno = EROFS, 0;
|
||||||
|
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);
|
||||||
|
if ( !inode_bitmap_chunk )
|
||||||
|
return 0;
|
||||||
|
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;
|
||||||
|
// TODO: This can be made faster by caching if previous bits were set.
|
||||||
|
for ( ; inode_bitmap_chunk_i < num_bits; inode_bitmap_chunk_i++ )
|
||||||
|
{
|
||||||
|
if ( !checkbit(chunk_bits, inode_bitmap_chunk_i) )
|
||||||
|
{
|
||||||
|
inode_bitmap_chunk->BeginWrite();
|
||||||
|
setbit(chunk_bits, inode_bitmap_chunk_i);
|
||||||
|
inode_bitmap_chunk->FinishWrite();
|
||||||
|
BeginWrite();
|
||||||
|
data->bg_free_inodes_count--;
|
||||||
|
FinishWrite();
|
||||||
|
filesystem->BeginWrite();
|
||||||
|
filesystem->sb->s_free_inodes_count--;
|
||||||
|
filesystem->FinishWrite();
|
||||||
|
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->Unref();
|
||||||
|
inode_bitmap_chunk = NULL;
|
||||||
|
}
|
||||||
|
BeginWrite();
|
||||||
|
data->bg_free_inodes_count = 0;
|
||||||
|
FinishWrite();
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::FreeBlock(uint32_t block_id)
|
||||||
|
{
|
||||||
|
assert(filesystem->device->write);
|
||||||
|
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->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_bitmap_chunk->BeginWrite();
|
||||||
|
uint8_t* chunk_bits = block_bitmap_chunk->block_data;
|
||||||
|
clearbit(chunk_bits, chunk_bit);
|
||||||
|
block_bitmap_chunk->FinishWrite();
|
||||||
|
if ( chunk_bit < inode_bitmap_chunk_i )
|
||||||
|
block_bitmap_chunk_i = chunk_bit;
|
||||||
|
BeginWrite();
|
||||||
|
data->bg_free_blocks_count++;
|
||||||
|
FinishWrite();
|
||||||
|
filesystem->BeginWrite();
|
||||||
|
filesystem->sb->s_free_blocks_count++;
|
||||||
|
filesystem->FinishWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::FreeInode(uint32_t inode_id)
|
||||||
|
{
|
||||||
|
assert(filesystem->device->write);
|
||||||
|
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->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
inode_bitmap_chunk->BeginWrite();
|
||||||
|
uint8_t* chunk_bits = inode_bitmap_chunk->block_data;
|
||||||
|
clearbit(chunk_bits, chunk_bit);
|
||||||
|
inode_bitmap_chunk->FinishWrite();
|
||||||
|
if ( chunk_bit < inode_bitmap_chunk_i )
|
||||||
|
inode_bitmap_chunk_i = chunk_bit;
|
||||||
|
BeginWrite();
|
||||||
|
data->bg_free_inodes_count++;
|
||||||
|
FinishWrite();
|
||||||
|
filesystem->BeginWrite();
|
||||||
|
filesystem->sb->s_free_inodes_count++;
|
||||||
|
filesystem->FinishWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::Refer()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::Unref()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::Sync()
|
||||||
|
{
|
||||||
|
if ( block_bitmap_chunk )
|
||||||
|
block_bitmap_chunk->Sync();
|
||||||
|
if ( inode_bitmap_chunk )
|
||||||
|
inode_bitmap_chunk->Sync();
|
||||||
|
if ( dirty )
|
||||||
|
data_block->Sync();
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::BeginWrite()
|
||||||
|
{
|
||||||
|
data_block->BeginWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::FinishWrite()
|
||||||
|
{
|
||||||
|
dirty = true;
|
||||||
|
data_block->FinishWrite();
|
||||||
|
Use();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGroup::Use()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* 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 block_bitmap_chunk_i;
|
||||||
|
uint32_t inode_bitmap_chunk_i;
|
||||||
|
uint32_t first_block_id;
|
||||||
|
uint32_t first_inode_id;
|
||||||
|
uint32_t num_blocks;
|
||||||
|
uint32_t num_inodes;
|
||||||
|
uint32_t num_block_bitmap_chunks;
|
||||||
|
uint32_t num_inode_bitmap_chunks;
|
||||||
|
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 BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Use();
|
||||||
|
void Unlink();
|
||||||
|
void Prelink();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* device.cpp
|
||||||
|
* Block device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "ioleast.h"
|
||||||
|
|
||||||
|
void* Device__SyncThread(void* ctx)
|
||||||
|
{
|
||||||
|
((Device*) ctx)->SyncThread();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::Device(int fd, const char* path, uint32_t block_size, bool write)
|
||||||
|
{
|
||||||
|
// sync_thread unset.
|
||||||
|
this->sync_thread_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->sync_thread_idle_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->sync_thread_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
this->mru_block = NULL;
|
||||||
|
this->lru_block = NULL;
|
||||||
|
this->dirty_block = NULL;
|
||||||
|
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
|
||||||
|
hash_blocks[i] = NULL;
|
||||||
|
struct stat st;
|
||||||
|
fstat(fd, &st);
|
||||||
|
this->device_size = st.st_size;
|
||||||
|
this->path = path;
|
||||||
|
this->block_size = block_size;
|
||||||
|
this->fd = fd;
|
||||||
|
this->write = write;
|
||||||
|
this->has_sync_thread = false;
|
||||||
|
this->sync_thread_should_exit = false;
|
||||||
|
this->sync_in_transit = false;
|
||||||
|
this->block_count = 0;
|
||||||
|
#ifdef __sortix__
|
||||||
|
// TODO: This isn't scaleable if there's multiple filesystems mounted.
|
||||||
|
size_t memory;
|
||||||
|
memstat(NULL, &memory);
|
||||||
|
this->block_limit = (memory / 10) / block_size;
|
||||||
|
#else
|
||||||
|
this->block_limit = 32768;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device()
|
||||||
|
{
|
||||||
|
if ( has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
sync_thread_should_exit = true;
|
||||||
|
pthread_cond_signal(&sync_thread_cond);
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
pthread_join(sync_thread, NULL);
|
||||||
|
has_sync_thread = false;
|
||||||
|
}
|
||||||
|
Sync();
|
||||||
|
while ( mru_block )
|
||||||
|
delete mru_block;
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SpawnSyncThread()
|
||||||
|
{
|
||||||
|
if ( this->has_sync_thread )
|
||||||
|
return;
|
||||||
|
this->has_sync_thread = write &&
|
||||||
|
pthread_create(&this->sync_thread, NULL, Device__SyncThread, this) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Device::AllocateBlock()
|
||||||
|
{
|
||||||
|
if ( block_limit <= block_count )
|
||||||
|
{
|
||||||
|
for ( Block* block = lru_block; block; block = block->prev_block )
|
||||||
|
{
|
||||||
|
if ( block->reference_count )
|
||||||
|
continue;
|
||||||
|
block->Destruct(); // Syncs.
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t* data = new uint8_t[block_size];
|
||||||
|
if ( !data ) // TODO: Use operator new nothrow!
|
||||||
|
return NULL;
|
||||||
|
Block* block = new Block();
|
||||||
|
if ( !block ) // TODO: Use operator new nothrow!
|
||||||
|
return delete[] data, (Block*) NULL;
|
||||||
|
block->block_data = data;
|
||||||
|
block_count++;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Device::GetBlock(uint32_t block_id)
|
||||||
|
{
|
||||||
|
if ( Block* block = GetCachedBlock(block_id) )
|
||||||
|
return block;
|
||||||
|
Block* block = AllocateBlock();
|
||||||
|
if ( !block )
|
||||||
|
return NULL;
|
||||||
|
block->Construct(this, block_id);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
assert(write);
|
||||||
|
if ( Block* block = GetCachedBlock(block_id) )
|
||||||
|
{
|
||||||
|
block->BeginWrite();
|
||||||
|
memset(block->block_data, 0, block_size);
|
||||||
|
block->FinishWrite();
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
Block* block = AllocateBlock();
|
||||||
|
if ( !block )
|
||||||
|
return NULL;
|
||||||
|
block->Construct(this, block_id);
|
||||||
|
memset(block->block_data, 0, block_size);
|
||||||
|
block->Prelink();
|
||||||
|
block->BeginWrite();
|
||||||
|
block->FinishWrite();
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if ( has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
while ( dirty_block || sync_in_transit )
|
||||||
|
pthread_cond_wait(&sync_thread_cond, &sync_thread_lock);
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
fsync(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( dirty_block )
|
||||||
|
dirty_block->Sync();
|
||||||
|
fsync(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SyncThread()
|
||||||
|
{
|
||||||
|
uint8_t transit_block_data[block_size];
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
while ( !(dirty_block || sync_thread_should_exit) )
|
||||||
|
pthread_cond_wait(&sync_thread_cond, &sync_thread_lock);
|
||||||
|
if ( sync_thread_should_exit )
|
||||||
|
break;
|
||||||
|
|
||||||
|
Block* block = dirty_block;
|
||||||
|
|
||||||
|
if ( block->next_dirty )
|
||||||
|
block->next_dirty->prev_dirty = NULL;
|
||||||
|
dirty_block = block->next_dirty;
|
||||||
|
block->next_dirty = NULL;
|
||||||
|
|
||||||
|
block->dirty = false;
|
||||||
|
block->is_in_transit = true;
|
||||||
|
sync_in_transit = true;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&block->modify_lock);
|
||||||
|
memcpy(transit_block_data, block->block_data, block_size);
|
||||||
|
pthread_mutex_unlock(&block->modify_lock);
|
||||||
|
|
||||||
|
off_t offset = (off_t) block_size * (off_t) block->block_id;
|
||||||
|
pwriteall(fd, transit_block_data, block_size, offset);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
block->is_in_transit = false;
|
||||||
|
sync_in_transit = false;
|
||||||
|
pthread_cond_signal(&block->transit_done_cond);
|
||||||
|
if ( !dirty_block )
|
||||||
|
pthread_cond_signal(&sync_thread_idle_cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* device.h
|
||||||
|
* Block device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEVICE_H
|
||||||
|
#define DEVICE_H
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
|
||||||
|
static const size_t DEVICE_HASH_LENGTH = 1 << 16;
|
||||||
|
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Device(int fd, const char* path, uint32_t block_size, bool write);
|
||||||
|
~Device();
|
||||||
|
|
||||||
|
public:
|
||||||
|
pthread_t sync_thread;
|
||||||
|
pthread_cond_t sync_thread_cond;
|
||||||
|
pthread_cond_t sync_thread_idle_cond;
|
||||||
|
pthread_mutex_t sync_thread_lock;
|
||||||
|
Block* mru_block;
|
||||||
|
Block* lru_block;
|
||||||
|
Block* dirty_block;
|
||||||
|
Block* hash_blocks[DEVICE_HASH_LENGTH];
|
||||||
|
off_t device_size;
|
||||||
|
const char* path;
|
||||||
|
uint32_t block_size;
|
||||||
|
int fd;
|
||||||
|
bool write;
|
||||||
|
bool has_sync_thread;
|
||||||
|
bool sync_thread_should_exit;
|
||||||
|
bool sync_in_transit;
|
||||||
|
size_t block_count;
|
||||||
|
size_t block_limit;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SpawnSyncThread();
|
||||||
|
Block* AllocateBlock();
|
||||||
|
Block* GetBlock(uint32_t block_id);
|
||||||
|
Block* GetBlockZeroed(uint32_t block_id);
|
||||||
|
Block* GetCachedBlock(uint32_t block_id);
|
||||||
|
void Sync();
|
||||||
|
void SyncThread();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* ext-constants.h
|
||||||
|
* Constants for the extended filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXT_CONSTANTS_H
|
||||||
|
#define EXT_CONSTANTS_H
|
||||||
|
|
||||||
|
static const uint16_t FAT_SUPER_MAGIC = 0xEF53;
|
||||||
|
static const uint16_t FAT_VALID_FS = 1;
|
||||||
|
static const uint16_t FAT_ERROR_FS = 2;
|
||||||
|
static const uint16_t FAT_ERRORS_CONTINUE = 1;
|
||||||
|
static const uint16_t FAT_ERRORS_RO = 2;
|
||||||
|
static const uint16_t FAT_ERRORS_PANIC = 3;
|
||||||
|
static const uint32_t FAT_OS_LINUX = 0;
|
||||||
|
static const uint32_t FAT_OS_HURD = 1;
|
||||||
|
static const uint32_t FAT_OS_MASIX = 2;
|
||||||
|
static const uint32_t FAT_OS_FREEBSD = 3;
|
||||||
|
static const uint32_t FAT_OS_LITES = 4;
|
||||||
|
static const uint32_t FAT_GOOD_OLD_REV = 0;
|
||||||
|
static const uint32_t FAT_DYNAMIC_REV = 1;
|
||||||
|
static const uint16_t FAT_DEF_RESUID = 0;
|
||||||
|
static const uint16_t FAT_DEF_RESGID = 0;
|
||||||
|
static const uint16_t FAT_GOOD_OLD_FIRST_INO = 11;
|
||||||
|
static const uint16_t FAT_GOOD_OLD_INODE_SIZE = 128;
|
||||||
|
static const uint32_t FAT_FEATURE_COMPAT_DIR_PREALLOC = 1U << 0U;
|
||||||
|
static const uint32_t FAT_FEATURE_COMPAT_IMAGIC_INODES = 1U << 1U;
|
||||||
|
static const uint32_t EXT3_FEATURE_COMPAT_HAS_JOURNAL = 1U << 2U;
|
||||||
|
static const uint32_t FAT_FEATURE_COMPAT_EXT_ATTR = 1U << 3U;
|
||||||
|
static const uint32_t FAT_FEATURE_COMPAT_RESIZE_INO = 1U << 4U;
|
||||||
|
static const uint32_t FAT_FEATURE_COMPAT_DIR_INDEX = 1U << 5U;
|
||||||
|
static const uint32_t FAT_FEATURE_INCOMPAT_COMPRESSION = 1U << 0U;
|
||||||
|
static const uint32_t FAT_FEATURE_INCOMPAT_FILETYPE = 1U << 1U;
|
||||||
|
static const uint32_t FAT_FEATURE_INCOMPAT_RECOVER = 1U << 2U;
|
||||||
|
static const uint32_t FAT_FEATURE_INCOMPAT_JOURNAL_DEV = 1U << 3U;
|
||||||
|
static const uint32_t FAT_FEATURE_INCOMPAT_META_BG = 1U << 4U;
|
||||||
|
static const uint32_t FAT_FEATURE_RO_COMPAT_SPARSE_SUPER = 1U << 0U;
|
||||||
|
static const uint32_t FAT_FEATURE_RO_COMPAT_LARGE_FILE = 1U << 1U;
|
||||||
|
static const uint32_t FAT_FEATURE_RO_COMPAT_BTREE_DIR = 1U << 2U;
|
||||||
|
static const uint32_t FAT_LZV1_ALG = 1U << 0U;
|
||||||
|
static const uint32_t FAT_LZRW3A_ALG = 1U << 1U;
|
||||||
|
static const uint32_t FAT_GZIP_ALG = 1U << 2U;
|
||||||
|
static const uint32_t FAT_BZIP2_ALG = 1U << 3U;
|
||||||
|
static const uint32_t FAT_LZO_ALG = 1U << 4U;
|
||||||
|
static const uint16_t FAT_S_IFMT = 0xF000;
|
||||||
|
static const uint16_t FAT_S_IFSOCK = 0xC000;
|
||||||
|
static const uint16_t FAT_S_IFLNK = 0xA000;
|
||||||
|
static const uint16_t FAT_S_IFREG = 0x8000;
|
||||||
|
static const uint16_t FAT_S_IFBLK = 0x6000;
|
||||||
|
static const uint16_t FAT_S_IFDIR = 0x4000;
|
||||||
|
static const uint16_t FAT_S_IFCHR = 0x2000;
|
||||||
|
static const uint16_t FAT_S_IFIFO = 0x1000;
|
||||||
|
static const uint16_t FAT_S_ISUID = 0x0800;
|
||||||
|
static const uint16_t FAT_S_ISGID = 0x0400;
|
||||||
|
static const uint16_t FAT_S_ISVTX = 0x0200;
|
||||||
|
static const uint16_t FAT_S_IRUSR = 0x0100;
|
||||||
|
static const uint16_t FAT_S_IWUSR = 0x0080;
|
||||||
|
static const uint16_t FAT_S_IXUSR = 0x0040;
|
||||||
|
static const uint16_t FAT_S_IRGRP = 0x0020;
|
||||||
|
static const uint16_t FAT_S_IWGRP = 0x0010;
|
||||||
|
static const uint16_t FAT_S_IXGRP = 0x0008;
|
||||||
|
static const uint16_t FAT_S_IROTH = 0x0004;
|
||||||
|
static const uint16_t FAT_S_IWOTH = 0x0002;
|
||||||
|
static const uint16_t FAT_S_IXOTH = 0x0001;
|
||||||
|
#define FAT_S_ISSOCK(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFSOCK)
|
||||||
|
#define FAT_S_ISLNK(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFLNK)
|
||||||
|
#define FAT_S_ISREG(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFREG)
|
||||||
|
#define FAT_S_ISBLK(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFBLK)
|
||||||
|
#define FAT_S_ISDIR(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFDIR)
|
||||||
|
#define FAT_S_ISCHR(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFCHR)
|
||||||
|
#define FAT_S_ISFIFO(mode) (((mode) & FAT_S_IFMT) == FAT_S_IFIFO)
|
||||||
|
static const uint32_t FAT_SECRM_FL = 0x00000001U;
|
||||||
|
static const uint32_t FAT_UNRM_FL = 0x00000002U;
|
||||||
|
static const uint32_t FAT_COMPR_FL = 0x00000004U;
|
||||||
|
static const uint32_t FAT_SYNC_FL = 0x00000008U;
|
||||||
|
static const uint32_t FAT_IMMUTABLE_FL = 0x00000010U;
|
||||||
|
static const uint32_t FAT_APPEND_FL = 0x00000020U;
|
||||||
|
static const uint32_t FAT_NODUMP_FL = 0x00000040U;
|
||||||
|
static const uint32_t FAT_NOATIME_FL = 0x00000080U;
|
||||||
|
static const uint32_t FAT_DIRTY_FL = 0x00000100U;
|
||||||
|
static const uint32_t FAT_COMPRBLK_FL = 0x00000200U;
|
||||||
|
static const uint32_t FAT_NOCOMPR_FL = 0x00000400U;
|
||||||
|
static const uint32_t FAT_ECOMPR_FL = 0x00000800U;
|
||||||
|
static const uint32_t FAT_BTREE_FL = 0x00001000U;
|
||||||
|
static const uint32_t FAT_INDEX_FL = 0x00001000U;
|
||||||
|
static const uint32_t FAT_IMAGIC_FL = 0x00002000U;
|
||||||
|
static const uint32_t EXT3_JOURNAL_DATA_FL = 0x00004000U;
|
||||||
|
static const uint32_t FAT_RESERVED_FL = 0x80000000U;
|
||||||
|
static const uint32_t FAT_ROOT_INO = 2;
|
||||||
|
static const uint8_t FAT_FT_UNKNOWN = 0;
|
||||||
|
static const uint8_t FAT_FT_REG_FILE = 1;
|
||||||
|
static const uint8_t FAT_FT_DIR = 2;
|
||||||
|
static const uint8_t FAT_FT_CHRDEV = 3;
|
||||||
|
static const uint8_t FAT_FT_BLKDEV = 4;
|
||||||
|
static const uint8_t FAT_FT_FIFO = 5;
|
||||||
|
static const uint8_t FAT_FT_SOCK = 6;
|
||||||
|
static const uint8_t FAT_FT_SYMLINK = 7;
|
||||||
|
|
||||||
|
static inline uint8_t FAT_FT_OF_MODE(mode_t mode)
|
||||||
|
{
|
||||||
|
if ( FAT_S_ISREG(mode) )
|
||||||
|
return FAT_FT_REG_FILE;
|
||||||
|
if ( FAT_S_ISDIR(mode) )
|
||||||
|
return FAT_FT_DIR;
|
||||||
|
if ( FAT_S_ISCHR(mode) )
|
||||||
|
return FAT_FT_CHRDEV;
|
||||||
|
if ( FAT_S_ISBLK(mode) )
|
||||||
|
return FAT_FT_BLKDEV;
|
||||||
|
if ( FAT_S_ISFIFO(mode) )
|
||||||
|
return FAT_FT_FIFO;
|
||||||
|
if ( FAT_S_ISSOCK(mode) )
|
||||||
|
return FAT_FT_SOCK;
|
||||||
|
if ( FAT_S_ISLNK(mode) )
|
||||||
|
return FAT_FT_SYMLINK;
|
||||||
|
return FAT_FT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
// FAT_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
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fat.h
|
||||||
|
* The File Allocation Table (FAT) filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FAT_H
|
||||||
|
#define FAT_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// TODO: Dammit. I forgot to swap endian all across the codebase.
|
||||||
|
|
||||||
|
struct fat_bpb
|
||||||
|
{
|
||||||
|
uint8_t jump[3];
|
||||||
|
char oem[8];
|
||||||
|
uint8_t bytes_per_sector_low;
|
||||||
|
uint8_t bytes_per_sector_high;
|
||||||
|
uint8_t sectors_per_cluster;
|
||||||
|
uint16_t reserved_sectors;
|
||||||
|
uint8_t fat_count;
|
||||||
|
uint8_t root_dirent_count_low;
|
||||||
|
uint8_t root_dirent_count_high;
|
||||||
|
uint8_t total_sectors_low;
|
||||||
|
uint8_t total_sectors_high;
|
||||||
|
uint8_t media_descriptor_type;
|
||||||
|
uint16_t sectors_per_fat;
|
||||||
|
uint16_t sectors_per_track;
|
||||||
|
uint16_t head_count;
|
||||||
|
uint16_t hidden_sectors;
|
||||||
|
uint32_t total_sectors_large;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t fat12_drive_number;
|
||||||
|
uint8_t fat12_reserved;
|
||||||
|
uint8_t fat12_signature;
|
||||||
|
uint8_t fat12_volume_id[4];
|
||||||
|
uint8_t fat12_volume_label[11];
|
||||||
|
uint8_t fat12_system[8];
|
||||||
|
};
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t fat32_sectors_per_fat;
|
||||||
|
uint16_t fat32_flags;
|
||||||
|
uint16_t fat32_version;
|
||||||
|
uint32_t fat32_root_cluster;
|
||||||
|
uint16_t fat32_fsinfo;
|
||||||
|
uint16_t fat32_backup_boot;
|
||||||
|
uint32_t fat32_reserved1[3];
|
||||||
|
uint8_t fat32_drive_number;
|
||||||
|
uint8_t fat32_reserved2;
|
||||||
|
uint8_t fat32_signature;
|
||||||
|
uint8_t fat32_volume_id[4];
|
||||||
|
uint8_t fat32_volume_label[11];
|
||||||
|
uint8_t fat32_system[8];
|
||||||
|
};
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t bootloader[510 - 36];
|
||||||
|
uint8_t boot_signature[2];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct fat_bpb) == 512, "sizeof(struct fat_bpb) == 512");
|
||||||
|
|
||||||
|
struct fat_fsinfo
|
||||||
|
{
|
||||||
|
uint32_t signature1;
|
||||||
|
uint32_t reserved1[120];
|
||||||
|
uint32_t signature2;
|
||||||
|
uint32_t free_count;
|
||||||
|
uint32_t next_free;
|
||||||
|
uint32_t reserved2[3];
|
||||||
|
uint32_t signature3;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct fat_fsinfo) == 512, "sizeof(struct fat_fsinfo) == 512");
|
||||||
|
|
||||||
|
struct fat_dirent
|
||||||
|
{
|
||||||
|
char name[11];
|
||||||
|
uint8_t attributes;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t creation_tenths; // TODO: Misnamed semantically.
|
||||||
|
uint16_t creation_time;
|
||||||
|
uint16_t creation_date;
|
||||||
|
uint16_t access_date;
|
||||||
|
uint16_t cluster_high;
|
||||||
|
uint16_t modified_time;
|
||||||
|
uint16_t modified_date;
|
||||||
|
uint16_t cluster_low;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct fat_dirent) == 32, "sizeof(struct fat_dirent) == 32");
|
||||||
|
|
||||||
|
#define FAT_ATTRIBUTE_READ_ONLY (1 << 0)
|
||||||
|
#define FAT_ATTRIBUTE_HIDDEN (1 << 1)
|
||||||
|
#define FAT_ATTRIBUTE_SYSTEM (1 << 2)
|
||||||
|
#define FAT_ATTRIBUTE_VOLUME_ID (1 << 3)
|
||||||
|
#define FAT_ATTRIBUTE_DIRECTORY (1 << 4)
|
||||||
|
#define FAT_ATTRIBUTE_ARCHIVE (1 << 5)
|
||||||
|
|
||||||
|
#define FAT_ATTRIBUTE_LONG_NAME 0x0F
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,339 @@
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fatfs.cpp
|
||||||
|
* The File Allocation Table (FAT) filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if defined(__sortix__)
|
||||||
|
#include "fsmarshall.h"
|
||||||
|
#else
|
||||||
|
#include "fuse.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: Remove.
|
||||||
|
#include "ext-constants.h"
|
||||||
|
#include "ext-structs.h"
|
||||||
|
|
||||||
|
#include "blockgroup.h"
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "fat.h"
|
||||||
|
#include "fatfs.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "ioleast.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
// These must be kept up to date with libmount/fat.c.
|
||||||
|
static const uint32_t FAT_FEATURE_COMPAT_SUPPORTED = 0;
|
||||||
|
static const uint32_t FAT_FEATURE_INCOMPAT_SUPPORTED = \
|
||||||
|
FAT_FEATURE_INCOMPAT_FILETYPE;
|
||||||
|
static const uint32_t FAT_FEATURE_RO_COMPAT_SUPPORTED = \
|
||||||
|
FAT_FEATURE_RO_COMPAT_LARGE_FILE;
|
||||||
|
|
||||||
|
uid_t request_uid;
|
||||||
|
uid_t request_gid;
|
||||||
|
|
||||||
|
mode_t HostModeFromExtMode(uint32_t extmode)
|
||||||
|
{
|
||||||
|
mode_t hostmode = extmode & 0777;
|
||||||
|
if ( extmode & FAT_S_ISVTX ) hostmode |= S_ISVTX;
|
||||||
|
if ( extmode & FAT_S_ISGID ) hostmode |= S_ISGID;
|
||||||
|
if ( extmode & FAT_S_ISUID ) hostmode |= S_ISUID;
|
||||||
|
if ( FAT_S_ISSOCK(extmode) ) hostmode |= S_IFSOCK;
|
||||||
|
if ( FAT_S_ISLNK(extmode) ) hostmode |= S_IFLNK;
|
||||||
|
if ( FAT_S_ISREG(extmode) ) hostmode |= S_IFREG;
|
||||||
|
if ( FAT_S_ISBLK(extmode) ) hostmode |= S_IFBLK;
|
||||||
|
if ( FAT_S_ISDIR(extmode) ) hostmode |= S_IFDIR;
|
||||||
|
if ( FAT_S_ISCHR(extmode) ) hostmode |= S_IFCHR;
|
||||||
|
if ( FAT_S_ISFIFO(extmode) ) hostmode |= S_IFIFO;
|
||||||
|
return hostmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ExtModeFromHostMode(mode_t hostmode)
|
||||||
|
{
|
||||||
|
uint32_t extmode = hostmode & 0777;
|
||||||
|
if ( hostmode & S_ISVTX ) extmode |= FAT_S_ISVTX;
|
||||||
|
if ( hostmode & S_ISGID ) extmode |= FAT_S_ISGID;
|
||||||
|
if ( hostmode & S_ISUID ) extmode |= FAT_S_ISUID;
|
||||||
|
if ( S_ISSOCK(hostmode) ) extmode |= FAT_S_IFSOCK;
|
||||||
|
if ( S_ISLNK(hostmode) ) extmode |= FAT_S_IFLNK;
|
||||||
|
if ( S_ISREG(hostmode) ) extmode |= FAT_S_IFREG;
|
||||||
|
if ( S_ISBLK(hostmode) ) extmode |= FAT_S_IFBLK;
|
||||||
|
if ( S_ISDIR(hostmode) ) extmode |= FAT_S_IFDIR;
|
||||||
|
if ( S_ISCHR(hostmode) ) extmode |= FAT_S_IFCHR;
|
||||||
|
if ( S_ISFIFO(hostmode) ) extmode |= FAT_S_IFIFO;
|
||||||
|
return extmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HostDTFromExtDT(uint8_t extdt)
|
||||||
|
{
|
||||||
|
switch ( extdt )
|
||||||
|
{
|
||||||
|
case FAT_FT_UNKNOWN: return DT_UNKNOWN;
|
||||||
|
case FAT_FT_REG_FILE: return DT_REG;
|
||||||
|
case FAT_FT_DIR: return DT_DIR;
|
||||||
|
case FAT_FT_CHRDEV: return DT_CHR;
|
||||||
|
case FAT_FT_BLKDEV: return DT_BLK;
|
||||||
|
case FAT_FT_FIFO: return DT_FIFO;
|
||||||
|
case FAT_FT_SOCK: return DT_SOCK;
|
||||||
|
case FAT_FT_SYMLINK: return DT_LNK;
|
||||||
|
}
|
||||||
|
return DT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
void StatInode(Inode* inode, struct stat* st)
|
||||||
|
{
|
||||||
|
memset(st, 0, sizeof(*st));
|
||||||
|
st->st_ino = inode->inode_id;
|
||||||
|
st->st_mode = inode->Mode();
|
||||||
|
st->st_nlink = 1; // TODO: Encapsulate.
|
||||||
|
st->st_uid = inode->UserId();
|
||||||
|
st->st_gid = inode->GroupId();
|
||||||
|
st->st_size = inode->Size();
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
// TODO: For the root dir, mount time, or maybe volume label or first file?
|
||||||
|
// Or maybe the time of the mount point?
|
||||||
|
time_t mtime = 0;
|
||||||
|
if ( inode->dirent )
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
memset(&tm, 0, sizeof(tm));
|
||||||
|
tm.tm_sec = ((inode->dirent->modified_time >> 0) & 0x1F) * 2;
|
||||||
|
tm.tm_min = (inode->dirent->modified_time >> 5) & 0x3F;
|
||||||
|
tm.tm_hour = (inode->dirent->modified_time >> 11) & 0x1F;
|
||||||
|
tm.tm_mday = (inode->dirent->modified_date >> 0) & 0x1F;
|
||||||
|
tm.tm_mon = ((inode->dirent->modified_date >> 5) & 0xF) - 1;
|
||||||
|
tm.tm_year = ((inode->dirent->modified_date >> 9) & 0x7F) + 80;
|
||||||
|
mtime = mktime(&tm);
|
||||||
|
}
|
||||||
|
st->st_atim.tv_sec = mtime; // TODO: The actual accessed time;
|
||||||
|
st->st_atim.tv_nsec = 0;
|
||||||
|
st->st_ctim.tv_sec = mtime; // TODO: Probably fine to keep as modified time.
|
||||||
|
st->st_ctim.tv_nsec = 0;
|
||||||
|
st->st_mtim.tv_sec = mtime;
|
||||||
|
st->st_mtim.tv_nsec = 0;
|
||||||
|
st->st_blksize = inode->filesystem->bytes_per_sector *
|
||||||
|
inode->filesystem->bpb->sectors_per_cluster;
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
st->st_blocks = 0; // TODO inode->data->i_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compact_arguments(int* argc, char*** argv)
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < *argc; i++ )
|
||||||
|
{
|
||||||
|
while ( i < *argc && !(*argv)[i] )
|
||||||
|
{
|
||||||
|
for ( int n = i; n < *argc; n++ )
|
||||||
|
(*argv)[n] = (*argv)[n+1];
|
||||||
|
(*argc)--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help(FILE* fp, const char* argv0)
|
||||||
|
{
|
||||||
|
fprintf(fp, "Usage: %s [OPTION]... DEVICE [MOUNT-POINT]\n", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void version(FILE* fp, const char* argv0)
|
||||||
|
{
|
||||||
|
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const char* argv0 = argv[0];
|
||||||
|
const char* pretend_mount_path = NULL;
|
||||||
|
bool foreground = false;
|
||||||
|
bool read = false;
|
||||||
|
bool write = false;
|
||||||
|
for ( int i = 1; i < argc; i++ )
|
||||||
|
{
|
||||||
|
const char* arg = argv[i];
|
||||||
|
if ( arg[0] != '-' || !arg[1] )
|
||||||
|
continue;
|
||||||
|
argv[i] = NULL;
|
||||||
|
if ( !strcmp(arg, "--") )
|
||||||
|
break;
|
||||||
|
if ( arg[1] != '-' )
|
||||||
|
{
|
||||||
|
while ( char c = *++arg ) switch ( c )
|
||||||
|
{
|
||||||
|
case 'r': read = true; break;
|
||||||
|
case 'w': write = true; break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||||
|
help(stderr, argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !strcmp(arg, "--help") )
|
||||||
|
help(stdout, argv0), exit(0);
|
||||||
|
else if ( !strcmp(arg, "--version") )
|
||||||
|
version(stdout, argv0), exit(0);
|
||||||
|
else if ( !strcmp(arg, "--background") )
|
||||||
|
foreground = false;
|
||||||
|
else if ( !strcmp(arg, "--foreground") )
|
||||||
|
foreground = true;
|
||||||
|
else if ( !strcmp(arg, "--read") )
|
||||||
|
read = true;
|
||||||
|
else if ( !strcmp(arg, "--write") )
|
||||||
|
write = true;
|
||||||
|
else if ( !strncmp(arg, "--pretend-mount-path=", strlen("--pretend-mount-path=")) )
|
||||||
|
pretend_mount_path = arg + strlen("--pretend-mount-path=");
|
||||||
|
else if ( !strcmp(arg, "--pretend-mount-path") )
|
||||||
|
{
|
||||||
|
if ( i+1 == argc )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: --pretend-mount-path: Missing operand\n", argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
pretend_mount_path = argv[++i];
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
||||||
|
help(stderr, argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It doesn't make sense to have a write-only filesystem.
|
||||||
|
read = read || write;
|
||||||
|
|
||||||
|
// Default to read and write filesystem access.
|
||||||
|
if ( !read && !write )
|
||||||
|
read = write = true;
|
||||||
|
|
||||||
|
if ( argc == 1 )
|
||||||
|
{
|
||||||
|
help(stdout, argv0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
compact_arguments(&argc, &argv);
|
||||||
|
|
||||||
|
const char* device_path = 2 <= argc ? argv[1] : NULL;
|
||||||
|
const char* mount_path = 3 <= argc ? argv[2] : NULL;
|
||||||
|
|
||||||
|
if ( !device_path )
|
||||||
|
{
|
||||||
|
help(stderr, argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !pretend_mount_path )
|
||||||
|
pretend_mount_path = mount_path;
|
||||||
|
|
||||||
|
int fd = open(device_path, write ? O_RDWR : O_RDONLY);
|
||||||
|
if ( fd < 0 )
|
||||||
|
err(1, "%s", device_path);
|
||||||
|
|
||||||
|
// Read the bios parameter block from the filesystem so we can verify it.
|
||||||
|
struct fat_bpb bpb;
|
||||||
|
if ( preadall(fd, &bpb, sizeof(bpb), 0) != sizeof(bpb) )
|
||||||
|
{
|
||||||
|
if ( errno == EEOF )
|
||||||
|
errx(1, "%s: Isn't a valid FAT filesystem (too short)", device_path);
|
||||||
|
else
|
||||||
|
err(1, "read: %s", device_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the boot.
|
||||||
|
if ( !(bpb.boot_signature[0] == 0x55 && bpb.boot_signature[1] == 0xAA) )
|
||||||
|
errx(1, "%s: Isn't a valid FAT filesystem (no boot signature)", device_path);
|
||||||
|
|
||||||
|
// Verify the jump instruction at the start of the boot sector.
|
||||||
|
if ( !(bpb.jump[0] == 0xEB && bpb.jump[2] == 0x90) &&
|
||||||
|
!(bpb.jump[0] == 0xE9) )
|
||||||
|
errx(1, "%s: Isn't a valid FAT filesystem (bad jump)", device_path);
|
||||||
|
|
||||||
|
// TODO: Validate all parameters make sense.
|
||||||
|
|
||||||
|
uint16_t bytes_per_sector =
|
||||||
|
bpb.bytes_per_sector_low | bpb.bytes_per_sector_high << 8;
|
||||||
|
uint16_t root_dirent_count =
|
||||||
|
bpb.root_dirent_count_low | bpb.root_dirent_count_high << 8;
|
||||||
|
uint32_t root_dir_sectors =
|
||||||
|
divup<uint32_t>(root_dirent_count * sizeof(fat_dirent), bytes_per_sector);
|
||||||
|
uint32_t sectors_per_fat =
|
||||||
|
bpb.sectors_per_fat ? bpb.sectors_per_fat : bpb.fat32_sectors_per_fat;
|
||||||
|
uint32_t total_sectors =
|
||||||
|
bpb.total_sectors_low | bpb.total_sectors_high << 8;
|
||||||
|
if ( !total_sectors )
|
||||||
|
total_sectors = bpb.total_sectors_large;
|
||||||
|
|
||||||
|
uint32_t data_offset =
|
||||||
|
bpb.reserved_sectors + bpb.fat_count * sectors_per_fat + root_dir_sectors;
|
||||||
|
uint32_t data_sectors = total_sectors - data_offset;
|
||||||
|
uint32_t cluster_count = data_sectors / bpb.sectors_per_cluster;
|
||||||
|
|
||||||
|
uint8_t fat_type =
|
||||||
|
cluster_count < 4085 ? 12 : cluster_count < 65525 ? 16 : 32;
|
||||||
|
|
||||||
|
// Verify the filesystem version.
|
||||||
|
if ( fat_type == 32 && bpb.fat32_version != 0x0000 )
|
||||||
|
errx(1, "%s: Unsupported filesystem version 0x%04x", device_path,
|
||||||
|
bpb.fat32_version);
|
||||||
|
|
||||||
|
// TODO: On FAT16/32 check FAT entry 1 for the high bits to see if fsck is needed.
|
||||||
|
// Check whether the filesystem was unmounted cleanly.
|
||||||
|
//if ( !(fat[1] & FAT_UNMOUNTED) || !(fat[1] & FAT_NO_IO_ERR) )
|
||||||
|
// warn("warning: %s: Filesystem wasn't unmounted cleanly\n", device_path);
|
||||||
|
|
||||||
|
// TODO: The FAT and clusters are not aligned to cluster size so
|
||||||
|
// we can't use the cluster size here. Perhaps refactor the
|
||||||
|
// device so we can deal with whole clusters.
|
||||||
|
Device* dev = new Device(fd, device_path, bytes_per_sector, write);
|
||||||
|
if ( !dev ) // TODO: Use operator new nothrow!
|
||||||
|
err(1, "malloc");
|
||||||
|
Filesystem* fs = new Filesystem(dev, pretend_mount_path);
|
||||||
|
if ( !fs ) // TODO: Use operator new nothrow!
|
||||||
|
err(1, "malloc");
|
||||||
|
|
||||||
|
printf("So far so good, data_offset=%u, data_sectors=%u, cluster_count=%u, sectors_per_cluster=%u, fat_type=%i\n", data_offset, data_sectors, cluster_count, bpb.sectors_per_cluster, fat_type);
|
||||||
|
|
||||||
|
if ( !mount_path )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if defined(__sortix__)
|
||||||
|
return fsmarshall_main(argv0, mount_path, foreground, fs, dev);
|
||||||
|
#else
|
||||||
|
return fat_fuse_main(argv0, mount_path, foreground, fs, dev);
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fatfs.h
|
||||||
|
* Implementation of the extended filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXTFS_H
|
||||||
|
#define EXTFS_H
|
||||||
|
|
||||||
|
extern uid_t request_uid;
|
||||||
|
extern gid_t request_gid;
|
||||||
|
|
||||||
|
class Inode;
|
||||||
|
|
||||||
|
mode_t HostModeFromExtMode(uint32_t extmode);
|
||||||
|
uint32_t ExtModeFromHostMode(mode_t hostmode);
|
||||||
|
uint8_t HostDTFromExtDT(uint8_t extdt);
|
||||||
|
void StatInode(Inode* inode, struct stat* st);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,547 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* filesystem.cpp
|
||||||
|
* Filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <err.h> // TODO: Remove.
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h> // TODO: Debug.
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "ext-constants.h"
|
||||||
|
#include "ext-structs.h"
|
||||||
|
#include "fat.h"
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "blockgroup.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
// TODO: Be more precise.
|
||||||
|
static bool is_8_3_char(char c)
|
||||||
|
{
|
||||||
|
return ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Is this fully precise? What about .FOO?
|
||||||
|
bool is_8_3(const char* name)
|
||||||
|
{
|
||||||
|
if ( !name[0] )
|
||||||
|
return false;
|
||||||
|
size_t b = 0;
|
||||||
|
while ( name[b] && is_8_3_char(name[b]) )
|
||||||
|
b++;
|
||||||
|
if ( 8 < b )
|
||||||
|
return false;
|
||||||
|
if ( !name[b] )
|
||||||
|
return true;
|
||||||
|
if ( name[b] != '.' )
|
||||||
|
return false;
|
||||||
|
size_t e = 0;
|
||||||
|
while ( name[b+1+e] && is_8_3_char(name[b+1+e]) )
|
||||||
|
e++;
|
||||||
|
if ( 3 < e )
|
||||||
|
return false;
|
||||||
|
if ( name[b+1+e] )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_8_3(const char* decoded, char encoded[8 + 3])
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
for ( size_t o = 0; o < 8 + 3; o++ )
|
||||||
|
{
|
||||||
|
char c = ' ';
|
||||||
|
if ( decoded[i] == '.' && o == 8 )
|
||||||
|
i++;
|
||||||
|
if ( decoded[i] && decoded[i] != '.' )
|
||||||
|
c = decoded[i++];
|
||||||
|
if ( (unsigned char) c == 0xE5 )
|
||||||
|
c = 0x05;
|
||||||
|
encoded[o] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void decode_8_3(const char encoded[8 + 3], char decoded[8 + 1 + 3 + 1])
|
||||||
|
{
|
||||||
|
size_t o = 0;
|
||||||
|
for ( size_t i = 0; i < 8; i++ )
|
||||||
|
{
|
||||||
|
char c = encoded[i];
|
||||||
|
if ( !c || c == ' ' )
|
||||||
|
break;
|
||||||
|
if ( c == 0x05 )
|
||||||
|
c = (char) 0xE5;
|
||||||
|
decoded[o++] = c;
|
||||||
|
}
|
||||||
|
for ( size_t i = 8; i < 8 + 3; i++ )
|
||||||
|
{
|
||||||
|
char c = encoded[i];
|
||||||
|
if ( !c || c == ' ' )
|
||||||
|
break;
|
||||||
|
if ( i == 8 )
|
||||||
|
decoded[o++] = '.';
|
||||||
|
if ( c == 0x05 )
|
||||||
|
c = (char) 0xE5;
|
||||||
|
decoded[o++] = c;
|
||||||
|
}
|
||||||
|
decoded[o] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t timespec_to_fat_tenths(struct timespec* ts)
|
||||||
|
{
|
||||||
|
// TODO: Work with a struct tm instead.
|
||||||
|
uint16_t hundreds = ts->tv_nsec / 10000000;
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
if ( tm.tm_sec & 1 )
|
||||||
|
hundreds += 100;
|
||||||
|
return hundreds;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t timespec_to_fat_time(struct timespec* ts)
|
||||||
|
{
|
||||||
|
// TODO: Work with a struct tm instead.
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
return (tm.tm_sec / 2) << 0 | tm.tm_min << 5 | tm.tm_hour << 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t timespec_to_fat_date(struct timespec* ts)
|
||||||
|
{
|
||||||
|
// TODO: Work with a struct tm instead.
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
return tm.tm_mday << 0 | (tm.tm_mon + 1) << 5 | (tm.tm_year - 80) << 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filesystem::Filesystem(Device* device, const char* mount_path)
|
||||||
|
{
|
||||||
|
this->bpb_block = device->GetBlock(0);
|
||||||
|
assert(bpb_block); // TODO: This can fail.
|
||||||
|
this->bpb = (struct fat_bpb*) bpb_block->block_data;
|
||||||
|
this->sb = NULL; // TODO: Remove.
|
||||||
|
this->device = device;
|
||||||
|
this->block_groups = NULL; // TODO: Remove.
|
||||||
|
this->mount_path = mount_path;
|
||||||
|
this->mode_reg = S_IFREG | 0644;
|
||||||
|
this->mode_dir = S_IFDIR | 0755;
|
||||||
|
this->block_size = device->block_size;
|
||||||
|
this->bytes_per_sector =
|
||||||
|
bpb->bytes_per_sector_low | bpb->bytes_per_sector_high << 8;
|
||||||
|
this->root_dirent_count =
|
||||||
|
bpb->root_dirent_count_low | bpb->root_dirent_count_high << 8;
|
||||||
|
uint32_t root_dir_sectors =
|
||||||
|
divup<uint32_t>(root_dirent_count * sizeof(fat_dirent), bytes_per_sector);
|
||||||
|
this->sectors_per_fat =
|
||||||
|
bpb->sectors_per_fat ? bpb->sectors_per_fat : bpb->fat32_sectors_per_fat;
|
||||||
|
this->total_sectors =
|
||||||
|
bpb->total_sectors_low | bpb->total_sectors_high << 8;
|
||||||
|
if ( !this->total_sectors )
|
||||||
|
this->total_sectors = bpb->total_sectors_large;
|
||||||
|
this->fat_sector = bpb->reserved_sectors;
|
||||||
|
this->root_sector = fat_sector + bpb->fat_count * sectors_per_fat;
|
||||||
|
this->data_sector = root_sector + root_dir_sectors;
|
||||||
|
uint32_t data_sectors = total_sectors - data_sector;
|
||||||
|
this->cluster_count = data_sectors / bpb->sectors_per_cluster;
|
||||||
|
this->cluster_size = bpb->sectors_per_cluster * bytes_per_sector;
|
||||||
|
this->fat_type = cluster_count < 4085 ? 12 : cluster_count < 65525 ? 16 : 32;
|
||||||
|
// Use cluster 1 as the root inode on FAT12/FAT16 since it's not a valid
|
||||||
|
// cluster for use in the FAT.
|
||||||
|
this->root_inode_id = fat_type == 32 ? bpb->fat32_root_cluster : 1;
|
||||||
|
// TODO: Okay we actually need to compare with their lower bounds.
|
||||||
|
this->eio_cluster =
|
||||||
|
fat_type == 12 ? 0xFF7 : fat_type == 16 ? 0xFFF7 : 0xFFFFFF7;
|
||||||
|
this->eof_cluster =
|
||||||
|
fat_type == 12 ? 0xFFF : fat_type == 16 ? 0xFFFF : 0xFFFFFFF;
|
||||||
|
// TODO: Obtain and verify this from the fsinfo.
|
||||||
|
this->free_search = 0;
|
||||||
|
// TODO: Update to FAT:
|
||||||
|
//this->num_blocks = this->sb->s_blocks_count;
|
||||||
|
this->mru_inode = NULL;
|
||||||
|
this->lru_inode = NULL;
|
||||||
|
this->dirty_inode = NULL;
|
||||||
|
for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ )
|
||||||
|
this->hash_inodes[i] = NULL;
|
||||||
|
this->dirty = false;
|
||||||
|
|
||||||
|
if ( device->write )
|
||||||
|
{
|
||||||
|
BeginWrite();
|
||||||
|
// TODO: Mark as mounted in fat[1] if FAT16/32.
|
||||||
|
FinishWrite();
|
||||||
|
Sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Filesystem::~Filesystem()
|
||||||
|
{
|
||||||
|
Sync();
|
||||||
|
while ( mru_inode )
|
||||||
|
delete mru_inode;
|
||||||
|
if ( device->write )
|
||||||
|
{
|
||||||
|
BeginWrite();
|
||||||
|
// TODO: Mark as unounted in fat[1] if FAT16/32.
|
||||||
|
FinishWrite();
|
||||||
|
Sync();
|
||||||
|
}
|
||||||
|
bpb_block->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::BeginWrite()
|
||||||
|
{
|
||||||
|
bpb_block->BeginWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::FinishWrite()
|
||||||
|
{
|
||||||
|
dirty = true;
|
||||||
|
bpb_block->FinishWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::Sync()
|
||||||
|
{
|
||||||
|
// TODO: Replacement concept?
|
||||||
|
while ( dirty_inode )
|
||||||
|
dirty_inode->Sync();
|
||||||
|
if ( dirty )
|
||||||
|
{
|
||||||
|
bpb_block->Sync();
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
device->Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockGroup* Filesystem::GetBlockGroup(uint32_t group_id)
|
||||||
|
{
|
||||||
|
err(1, "deleteme: %s\n", __func__);
|
||||||
|
assert(group_id < num_groups);
|
||||||
|
if ( block_groups[group_id] )
|
||||||
|
return block_groups[group_id]->Refer(), block_groups[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);
|
||||||
|
if ( !block )
|
||||||
|
return (BlockGroup*) NULL;
|
||||||
|
BlockGroup* group = new BlockGroup(this, group_id);
|
||||||
|
if ( !group ) // TODO: Use operator new nothrow!
|
||||||
|
return block->Unref(), (BlockGroup*) NULL;
|
||||||
|
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, Block* dirent_block,
|
||||||
|
struct fat_dirent* dirent)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if ( !inode_id || num_inodes <= inode_id )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
if ( !inode_id )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t bin = inode_id % INODE_HASH_LENGTH;
|
||||||
|
for ( Inode* iter = hash_inodes[bin]; iter; iter = iter->next_hashed )
|
||||||
|
if ( iter->inode_id == inode_id )
|
||||||
|
return iter->Refer(), iter;
|
||||||
|
|
||||||
|
if ( inode_id != root_inode_id && !dirent_block )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
|
||||||
|
Inode* inode = new Inode(this, inode_id);
|
||||||
|
if ( !inode )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
inode->first_cluster =
|
||||||
|
inode_id == root_inode_id && fat_type != 32 ? 0 : inode_id;
|
||||||
|
if ( (inode->data_block = dirent_block) )
|
||||||
|
inode->data_block->Refer();
|
||||||
|
inode->dirent = dirent;
|
||||||
|
inode->Prelink();
|
||||||
|
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::AllocateCluster()
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < cluster_count; i++ )
|
||||||
|
{
|
||||||
|
size_t n = 2 + (free_search + i) % cluster_count;
|
||||||
|
if ( !ReadFAT(n) )
|
||||||
|
{
|
||||||
|
free_search = (i + 1) % cluster_count;
|
||||||
|
if ( fat_type == 32 )
|
||||||
|
{
|
||||||
|
Block* block = device->GetBlock(bpb->fat32_fsinfo);
|
||||||
|
if ( block )
|
||||||
|
{
|
||||||
|
struct fat_fsinfo* fsinfo =
|
||||||
|
(struct fat_fsinfo*) block->block_data;
|
||||||
|
block->BeginWrite();
|
||||||
|
uint32_t free_count = le32toh(fsinfo->free_count);
|
||||||
|
if ( free_count )
|
||||||
|
free_count--;
|
||||||
|
fsinfo->free_count = htole32(free_count);
|
||||||
|
fsinfo->next_free = n;
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::FreeCluster(uint32_t cluster)
|
||||||
|
{
|
||||||
|
if ( fat_type != 32 )
|
||||||
|
return;
|
||||||
|
Block* block = device->GetBlock(bpb->fat32_fsinfo);
|
||||||
|
if ( block )
|
||||||
|
return;
|
||||||
|
struct fat_fsinfo* fsinfo = (struct fat_fsinfo*) block->block_data;
|
||||||
|
block->BeginWrite();
|
||||||
|
uint32_t free_count = le32toh(fsinfo->free_count);
|
||||||
|
if ( free_count < cluster_count )
|
||||||
|
free_count++;
|
||||||
|
fsinfo->free_count = htole32(free_count);
|
||||||
|
if ( !fsinfo->free_count || le32toh(fsinfo->next_free) == cluster + 1 )
|
||||||
|
{
|
||||||
|
fsinfo->next_free = htole32(cluster);
|
||||||
|
free_search = cluster - 2;
|
||||||
|
}
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::AllocateBlock(BlockGroup* preferred)
|
||||||
|
{
|
||||||
|
err(1, "deleteme: %s\n", __func__);
|
||||||
|
if ( !device->write )
|
||||||
|
return errno = EROFS, 0;
|
||||||
|
if ( !sb->s_free_blocks_count )
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
if ( preferred )
|
||||||
|
if ( uint32_t block_id = preferred->AllocateBlock() )
|
||||||
|
return block_id;
|
||||||
|
// TODO: This can be made faster by maintaining a linked list of block
|
||||||
|
// groups that definitely have free blocks.
|
||||||
|
for ( uint32_t group_id = 0; group_id < num_groups; group_id++ )
|
||||||
|
if ( uint32_t block_id = GetBlockGroup(group_id)->AllocateBlock() )
|
||||||
|
return block_id;
|
||||||
|
// TODO: This case should only be fit in the event of corruption. We should
|
||||||
|
// rebuild all these values upon filesystem mount instead so we know
|
||||||
|
// this can't happen. That also allows us to make the linked list
|
||||||
|
// requested above.
|
||||||
|
BeginWrite();
|
||||||
|
sb->s_free_blocks_count = 0;
|
||||||
|
FinishWrite();
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::AllocateInode(BlockGroup* preferred)
|
||||||
|
{
|
||||||
|
err(1, "deleteme: %s\n", __func__);
|
||||||
|
if ( !device->write )
|
||||||
|
return errno = EROFS, 0;
|
||||||
|
if ( !sb->s_free_inodes_count )
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
if ( preferred )
|
||||||
|
if ( uint32_t inode_id = preferred->AllocateInode() )
|
||||||
|
return inode_id;
|
||||||
|
// TODO: This can be made faster by maintaining a linked list of block
|
||||||
|
// groups that definitely have free inodes.
|
||||||
|
for ( uint32_t group_id = 0; group_id < num_groups; group_id++ )
|
||||||
|
if ( uint32_t inode_id = GetBlockGroup(group_id)->AllocateInode() )
|
||||||
|
return inode_id;
|
||||||
|
// TODO: This case should only be fit in the event of corruption. We should
|
||||||
|
// rebuild all these values upon filesystem mount instead so we know
|
||||||
|
// this can't happen. That also allows us to make the linked list
|
||||||
|
// requested above.
|
||||||
|
BeginWrite();
|
||||||
|
sb->s_free_inodes_count = 0;
|
||||||
|
FinishWrite();
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::FreeBlock(uint32_t block_id)
|
||||||
|
{
|
||||||
|
err(1, "deleteme: %s\n", __func__);
|
||||||
|
assert(device->write);
|
||||||
|
assert(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);
|
||||||
|
if ( !group )
|
||||||
|
return;
|
||||||
|
group->FreeBlock(block_id);
|
||||||
|
group->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::FreeInode(uint32_t inode_id)
|
||||||
|
{
|
||||||
|
err(1, "deleteme: %s\n", __func__);
|
||||||
|
assert(device->write);
|
||||||
|
assert(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);
|
||||||
|
if ( !group )
|
||||||
|
return;
|
||||||
|
group->FreeInode(inode_id);
|
||||||
|
group->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::ReadFAT(uint32_t cluster)
|
||||||
|
{
|
||||||
|
// TODO: Bounds check.
|
||||||
|
if ( fat_type == 12 )
|
||||||
|
{
|
||||||
|
size_t position = cluster + (cluster / 2);
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t offset = position % bytes_per_sector;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return eio_cluster;
|
||||||
|
uint8_t lower = block->block_data[offset];
|
||||||
|
if ( ++offset == bytes_per_sector )
|
||||||
|
{
|
||||||
|
block->Unref();
|
||||||
|
if ( !(block = device->GetBlock(fat_sector + lba)) )
|
||||||
|
return eio_cluster;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
uint8_t higher = block->block_data[offset];
|
||||||
|
block->Unref();
|
||||||
|
uint16_t value = lower | higher << 8;
|
||||||
|
if ( cluster & 1 )
|
||||||
|
return value >> 4;
|
||||||
|
else
|
||||||
|
return value & 0xFFF;
|
||||||
|
}
|
||||||
|
size_t fat_size = fat_type / 8;
|
||||||
|
size_t position = cluster * fat_size;
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t entry = (position % bytes_per_sector) / fat_size;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return eio_cluster;
|
||||||
|
uint32_t result = 0;
|
||||||
|
if ( fat_type == 16 )
|
||||||
|
result = ((uint16_t*) block->block_data)[entry];
|
||||||
|
else if ( fat_type == 32 )
|
||||||
|
result = ((uint32_t*) block->block_data)[entry] & 0x0FFFFFFF;
|
||||||
|
block->Unref();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filesystem::WriteFAT(uint32_t cluster, uint32_t value)
|
||||||
|
{
|
||||||
|
assert(device->write);
|
||||||
|
// TODO: Bounds check.
|
||||||
|
if ( fat_type == 12 )
|
||||||
|
{
|
||||||
|
size_t position = cluster + (cluster / 2);
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t offset = position % bytes_per_sector;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return false;
|
||||||
|
value = cluster & 1 ? value << 4 : value;
|
||||||
|
uint16_t mask = cluster & 1 ? 0xFFF0 : 0x0FFF;
|
||||||
|
block->BeginWrite();
|
||||||
|
block->block_data[offset] &= ~mask;
|
||||||
|
block->block_data[offset] |= value;
|
||||||
|
if ( ++offset == bytes_per_sector )
|
||||||
|
{
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
if ( !(block = device->GetBlock(fat_sector + lba)) )
|
||||||
|
return false;
|
||||||
|
offset = 0;
|
||||||
|
block->BeginWrite();
|
||||||
|
}
|
||||||
|
block->block_data[offset] &= ~(mask >> 8);
|
||||||
|
block->block_data[offset] |= value >> 8;
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Mirror to the other FATs.
|
||||||
|
size_t fat_size = fat_type / 8;
|
||||||
|
size_t position = cluster * fat_size;
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t entry = (position % bytes_per_sector) / fat_size;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return false;
|
||||||
|
block->BeginWrite();
|
||||||
|
if ( fat_type == 16 )
|
||||||
|
((uint16_t*) block->block_data)[entry] = value;
|
||||||
|
else if ( fat_type == 32 )
|
||||||
|
{
|
||||||
|
uint32_t old = ((uint32_t*) block->block_data)[entry] & 0xF0000000;
|
||||||
|
((uint32_t*) block->block_data)[entry] = value | old;
|
||||||
|
}
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::CalculateFreeCount()
|
||||||
|
{
|
||||||
|
if ( fat_type == 32 )
|
||||||
|
{
|
||||||
|
// TODO: Verify the fsinfo.
|
||||||
|
Block* block = device->GetBlock(bpb->fat32_fsinfo);
|
||||||
|
if ( !block )
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
const struct fat_fsinfo* fsinfo =
|
||||||
|
(const struct fat_fsinfo*) block->block_data;
|
||||||
|
uint32_t result = le32toh(fsinfo->free_count);
|
||||||
|
block->Unref();
|
||||||
|
if ( result != 0xFFFFFFFF )
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// TODO: Cache these.
|
||||||
|
size_t count = 0;
|
||||||
|
for ( size_t i = 0; i < cluster_count; i++ )
|
||||||
|
if ( !ReadFAT(2 + i) )
|
||||||
|
count++;
|
||||||
|
return count;
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* filesystem.h
|
||||||
|
* Filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILESYSTEM_H
|
||||||
|
#define FILESYSTEM_H
|
||||||
|
|
||||||
|
bool is_8_3(const char* name);
|
||||||
|
void encode_8_3(const char* decoded, char encoded[8 + 3]);
|
||||||
|
void decode_8_3(const char encoded[8 + 3], char decoded[8 + 1 + 3 + 1]);
|
||||||
|
uint8_t timespec_to_fat_tenths(struct timespec* ts);
|
||||||
|
uint16_t timespec_to_fat_time(struct timespec* ts);
|
||||||
|
uint16_t timespec_to_fat_date(struct timespec* ts);
|
||||||
|
|
||||||
|
class BlockGroup;
|
||||||
|
class Device;
|
||||||
|
class Inode;
|
||||||
|
|
||||||
|
static const size_t INODE_HASH_LENGTH = 1 << 16;
|
||||||
|
|
||||||
|
class Filesystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Filesystem(Device* device, const char* mount_path);
|
||||||
|
~Filesystem();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Block* bpb_block;
|
||||||
|
struct fat_bpb* bpb;
|
||||||
|
struct ext_superblock* sb; //TODO: Remove.
|
||||||
|
Device* device;
|
||||||
|
BlockGroup** block_groups;
|
||||||
|
const char* mount_path;
|
||||||
|
mode_t mode_reg;
|
||||||
|
mode_t mode_dir;
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
uint32_t block_size;
|
||||||
|
uint32_t inode_size; // TODO: Remove.
|
||||||
|
uint16_t bytes_per_sector;
|
||||||
|
uint16_t root_dirent_count;
|
||||||
|
uint32_t sectors_per_fat;
|
||||||
|
uint32_t root_inode_id;
|
||||||
|
uint32_t total_sectors;
|
||||||
|
uint32_t fat_sector; // TODO: Rename to lba
|
||||||
|
uint32_t root_sector; // TODO: Rename to lba
|
||||||
|
uint32_t data_sector; // TODO: Rename to lba
|
||||||
|
uint32_t cluster_count;
|
||||||
|
uint32_t cluster_size;
|
||||||
|
uint8_t fat_type;
|
||||||
|
uint32_t eio_cluster;
|
||||||
|
uint32_t eof_cluster;
|
||||||
|
uint32_t free_search;
|
||||||
|
uint32_t num_blocks; // TODO: Remove, probably.
|
||||||
|
uint32_t num_groups; // TODO: Remove.
|
||||||
|
uint32_t num_inodes; // TODO: Remove, probably.
|
||||||
|
Inode* mru_inode;
|
||||||
|
Inode* lru_inode;
|
||||||
|
Inode* dirty_inode;
|
||||||
|
Inode* hash_inodes[INODE_HASH_LENGTH];
|
||||||
|
bool dirty;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BlockGroup* GetBlockGroup(uint32_t group_id);
|
||||||
|
Inode* GetInode(uint32_t inode_id, Block* dirent_block = NULL,
|
||||||
|
struct fat_dirent* dirent = NULL);
|
||||||
|
uint32_t AllocateCluster();
|
||||||
|
void FreeCluster(uint32_t cluster);
|
||||||
|
uint32_t AllocateBlock(BlockGroup* preferred = NULL);
|
||||||
|
uint32_t AllocateInode(BlockGroup* preferred = NULL);
|
||||||
|
void FreeBlock(uint32_t block_id);
|
||||||
|
void FreeInode(uint32_t inode_id);
|
||||||
|
uint32_t ReadFAT(uint32_t cluster);
|
||||||
|
bool WriteFAT(uint32_t cluster, uint32_t value);
|
||||||
|
uint32_t CalculateFreeCount();
|
||||||
|
void BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Sync();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,888 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fsmarshall.cpp
|
||||||
|
* Sortix fsmarshall frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__sortix__)
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ioleast.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <timespec.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sortix/dirent.h>
|
||||||
|
|
||||||
|
#include <fsmarshall.h>
|
||||||
|
|
||||||
|
#include "ext-constants.h"
|
||||||
|
#include "ext-structs.h" // TODO: Remove.
|
||||||
|
|
||||||
|
#include "blockgroup.h"
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "fat.h"
|
||||||
|
#include "fatfs.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "fsmarshall.h"
|
||||||
|
#include "fuse.h"
|
||||||
|
#include "inode.h"
|
||||||
|
|
||||||
|
bool RespondData(int chl, const void* ptr, size_t count)
|
||||||
|
{
|
||||||
|
return writeall(chl, ptr, count) == count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondHeader(int chl, size_t type, size_t size)
|
||||||
|
{
|
||||||
|
struct fsm_msg_header hdr;
|
||||||
|
hdr.msgtype = type;
|
||||||
|
hdr.msgsize = size;
|
||||||
|
return RespondData(chl, &hdr, sizeof(hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondMessage(int chl, size_t type, const void* ptr, size_t count)
|
||||||
|
{
|
||||||
|
return RespondHeader(chl, type, count) &&
|
||||||
|
RespondData(chl, ptr, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondError(int chl, int errnum)
|
||||||
|
{
|
||||||
|
struct fsm_resp_error body;
|
||||||
|
body.errnum = errnum;
|
||||||
|
//fprintf(stderr, "fatfs: sending error %i (%s)\n", errnum, strerror(errnum));
|
||||||
|
return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondSuccess(int chl)
|
||||||
|
{
|
||||||
|
struct fsm_resp_success body;
|
||||||
|
return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondStat(int chl, struct stat* st)
|
||||||
|
{
|
||||||
|
struct fsm_resp_stat body;
|
||||||
|
body.st = *st;
|
||||||
|
return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondStatVFS(int chl, struct statvfs* stvfs)
|
||||||
|
{
|
||||||
|
struct fsm_resp_statvfs body;
|
||||||
|
body.stvfs = *stvfs;
|
||||||
|
return RespondMessage(chl, FSM_RESP_STATVFS, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondSeek(int chl, off_t offset)
|
||||||
|
{
|
||||||
|
struct fsm_resp_lseek body;
|
||||||
|
body.offset = offset;
|
||||||
|
return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondRead(int chl, const uint8_t* buf, size_t count)
|
||||||
|
{
|
||||||
|
struct fsm_resp_read body;
|
||||||
|
body.count = count;
|
||||||
|
return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondReadlink(int chl, const uint8_t* buf, size_t count)
|
||||||
|
{
|
||||||
|
struct fsm_resp_readlink body;
|
||||||
|
body.targetlen = count;
|
||||||
|
return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondWrite(int chl, size_t count)
|
||||||
|
{
|
||||||
|
struct fsm_resp_write body;
|
||||||
|
body.count = count;
|
||||||
|
return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondOpen(int chl, ino_t ino, mode_t type)
|
||||||
|
{
|
||||||
|
struct fsm_resp_open body;
|
||||||
|
body.ino = ino;
|
||||||
|
body.type = type;
|
||||||
|
return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondMakeDir(int chl, ino_t ino)
|
||||||
|
{
|
||||||
|
struct fsm_resp_mkdir body;
|
||||||
|
body.ino = ino;
|
||||||
|
return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondReadDir(int chl, struct dirent* dirent)
|
||||||
|
{
|
||||||
|
struct fsm_resp_readdirents body;
|
||||||
|
body.ino = dirent->d_ino;
|
||||||
|
body.type = dirent->d_type;
|
||||||
|
body.namelen = dirent->d_namlen;
|
||||||
|
return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, dirent->d_name, dirent->d_namlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondTCGetBlob(int chl, const void* data, size_t data_size)
|
||||||
|
{
|
||||||
|
struct fsm_resp_tcgetblob body;
|
||||||
|
body.count = data_size;
|
||||||
|
return RespondMessage(chl, FSM_RESP_TCGETBLOB, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, data, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Inode* SafeGetInode(Filesystem* fs, ino_t ino)
|
||||||
|
{
|
||||||
|
if ( (uint32_t) ino != ino )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
// TODO: Should check if the inode is not deleted.
|
||||||
|
return fs->GetInode((uint32_t) ino);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) chl;
|
||||||
|
if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) )
|
||||||
|
{
|
||||||
|
inode->RemoteRefer();
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) chl;
|
||||||
|
if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) )
|
||||||
|
{
|
||||||
|
inode->RemoteUnref();
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
inode->Sync();
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
struct stat st;
|
||||||
|
StatInode(inode, &st);
|
||||||
|
inode->Unref();
|
||||||
|
RespondStat(chl, &st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleChangeMode(int chl, struct fsm_req_chmod* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
uint32_t req_mode = ExtModeFromHostMode(msg->mode);
|
||||||
|
uint32_t old_mode = inode->Mode();
|
||||||
|
uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE);
|
||||||
|
inode->SetMode(new_mode);
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( msg->uid != (uid_t) -1 )
|
||||||
|
inode->SetUserId((uint32_t) msg->uid);
|
||||||
|
if ( msg->gid != (gid_t) -1 )
|
||||||
|
inode->SetGroupId((uint32_t) msg->gid);
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( msg->times[0].tv_nsec != UTIME_OMIT ||
|
||||||
|
msg->times[1].tv_nsec != UTIME_OMIT )
|
||||||
|
{
|
||||||
|
time_t now = time(NULL);
|
||||||
|
inode->BeginWrite();
|
||||||
|
if ( msg->times[0].tv_nsec == UTIME_NOW )
|
||||||
|
inode->data->i_atime = now;
|
||||||
|
else if ( msg->times[0].tv_nsec != UTIME_OMIT )
|
||||||
|
inode->data->i_atime = msg->times[0].tv_sec;
|
||||||
|
if ( msg->times[1].tv_nsec == UTIME_NOW )
|
||||||
|
inode->data->i_mtime = now;
|
||||||
|
else if ( msg->times[1].tv_nsec != UTIME_OMIT )
|
||||||
|
inode->data->i_mtime = msg->times[1].tv_sec;
|
||||||
|
inode->FinishWrite();
|
||||||
|
}
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
if ( msg->size < 0 ) { RespondError(chl, EINVAL); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
inode->Truncate((uint64_t) msg->size);
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( msg->whence == SEEK_SET )
|
||||||
|
RespondSeek(chl, msg->offset);
|
||||||
|
else if ( msg->whence == SEEK_END )
|
||||||
|
{
|
||||||
|
off_t inode_size = inode->Size();
|
||||||
|
if ( (msg->offset < 0 && inode_size + msg->offset < 0) ||
|
||||||
|
(0 <= msg->offset && OFF_MAX - inode_size < msg->offset) )
|
||||||
|
RespondError(chl, EOVERFLOW);
|
||||||
|
else
|
||||||
|
RespondSeek(chl, msg->offset + inode_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
RespondError(chl, EINVAL);
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
uint8_t* buf = (uint8_t*) malloc(msg->count);
|
||||||
|
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||||
|
ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset);
|
||||||
|
inode->Unref();
|
||||||
|
if ( amount < 0 ) { free(buf); RespondError(chl, errno); return; }
|
||||||
|
RespondRead(chl, buf, amount);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleWriteAt(int chl, struct fsm_req_pwrite* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
const uint8_t* buf = (const uint8_t*) &msg[1];
|
||||||
|
ssize_t amount = inode->WriteAt(buf, msg->count, msg->offset);
|
||||||
|
inode->Unref();
|
||||||
|
if ( amount < 0 ) { RespondError(chl, errno); return; }
|
||||||
|
RespondWrite(chl, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
Inode* result = inode->Open(path, msg->flags, msg->mode & 07777);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
|
||||||
|
// TODO: This leaks a reference so the Inode keeps existing with meta data
|
||||||
|
// since unfortunately the automatic Refer() in the kernel is not implied.
|
||||||
|
// idk how to best solve that just yet, maybe add a count to the inode
|
||||||
|
// that causes that many Unrefs() to happen on the next HandleRefer().
|
||||||
|
//result->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
Inode* result = inode->CreateDirectory(path, msg->mode & 07777);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondMakeDir(chl, result->inode_id);
|
||||||
|
result->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( !S_ISDIR(inode->Mode()) )
|
||||||
|
{
|
||||||
|
inode->Unref();
|
||||||
|
RespondError(chl, ENOTDIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct dirent kernel_entry;
|
||||||
|
// TODO: Adjust with LFN's real limit.
|
||||||
|
uint8_t padding[sizeof(struct dirent) + 256];
|
||||||
|
};
|
||||||
|
memset(&kernel_entry, 0, sizeof(kernel_entry));
|
||||||
|
|
||||||
|
if ( inode->inode_id == inode->filesystem->root_inode_id )
|
||||||
|
{
|
||||||
|
if ( msg->rec_num < 2 )
|
||||||
|
{
|
||||||
|
const char* name = msg->rec_num ? ".." : ".";
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
||||||
|
kernel_entry.d_ino = inode->inode_id;
|
||||||
|
kernel_entry.d_dev = 0;
|
||||||
|
kernel_entry.d_type = DT_DIR;
|
||||||
|
kernel_entry.d_namlen = name_len;
|
||||||
|
memcpy(kernel_entry.d_name, name, name_len);
|
||||||
|
size_t dname_offset = offsetof(struct dirent, d_name);
|
||||||
|
padding[dname_offset + kernel_entry.d_namlen] = '\0';
|
||||||
|
RespondReadDir(chl, &kernel_entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg->rec_num -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cluster = inode->first_cluster;
|
||||||
|
uint8_t sector = 0;
|
||||||
|
uint16_t offset = 0;
|
||||||
|
Block* block = NULL;
|
||||||
|
while ( inode->Iterate(&block, &cluster, §or, &offset) )
|
||||||
|
{
|
||||||
|
const uint8_t* block_data = block->block_data + offset;
|
||||||
|
const struct fat_dirent* entry = (const struct fat_dirent*) block_data;
|
||||||
|
if ( !entry->name[0] )
|
||||||
|
break;
|
||||||
|
if ( (unsigned char) entry->name[0] != 0xE5 &&
|
||||||
|
!(entry->attributes & FAT_ATTRIBUTE_VOLUME_ID) &&
|
||||||
|
!(msg->rec_num--) )
|
||||||
|
{
|
||||||
|
char name[8 + 1 + 3 + 1];
|
||||||
|
decode_8_3(entry->name, name);
|
||||||
|
size_t name_len = strnlen(entry->name, sizeof(entry->name));
|
||||||
|
uint8_t file_type =
|
||||||
|
entry->attributes & FAT_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG;
|
||||||
|
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
||||||
|
kernel_entry.d_ino = entry->cluster_low | entry->cluster_high << 16;
|
||||||
|
kernel_entry.d_dev = 0;
|
||||||
|
kernel_entry.d_type = file_type;
|
||||||
|
kernel_entry.d_namlen = name_len;
|
||||||
|
memcpy(kernel_entry.d_name, name, name_len);
|
||||||
|
size_t dname_offset = offsetof(struct dirent, d_name);
|
||||||
|
padding[dname_offset + kernel_entry.d_namlen] = '\0';
|
||||||
|
block->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
RespondReadDir(chl, &kernel_entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offset += sizeof(struct fat_dirent);
|
||||||
|
}
|
||||||
|
int errnum = errno;
|
||||||
|
if ( block )
|
||||||
|
block->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
if ( errnum )
|
||||||
|
{
|
||||||
|
RespondError(chl, errnum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_entry.d_reclen = sizeof(kernel_entry);
|
||||||
|
RespondReadDir(chl, &kernel_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
RespondError(chl, ENOTTY);
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->Unlink(path, false);
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->RemoveDirectory(path);
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
Inode* dest = SafeGetInode(fs, msg->linkino);
|
||||||
|
if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->Link(path, dest, false);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
dest->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
printf("ENOTSUP %s\n", __func__);
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* dest_raw = (char*) &(msg[1]);
|
||||||
|
char* dest = (char*) malloc(msg->targetlen + 1);
|
||||||
|
if ( !dest )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(dest, dest_raw, msg->targetlen);
|
||||||
|
dest[msg->targetlen] = '\0';
|
||||||
|
|
||||||
|
char* path_raw = (char*) dest_raw + msg->targetlen;
|
||||||
|
char* path = (char*) malloc(msg->namelen + 1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
free(dest);
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, path_raw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->Symlink(path, dest);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
free(dest);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) msg;
|
||||||
|
(void) fs;
|
||||||
|
RespondError(chl, EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
RespondError(chl, ENOTSUP); // TODO
|
||||||
|
return;
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1);
|
||||||
|
if ( !path ) { RespondError(chl, errno); return; }
|
||||||
|
memcpy(path, pathraw, msg->oldnamelen);
|
||||||
|
path[msg->oldnamelen] = '\0';
|
||||||
|
memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen);
|
||||||
|
path[msg->oldnamelen + 1 + msg->newnamelen] = '\0';
|
||||||
|
|
||||||
|
const char* oldname = path;
|
||||||
|
const char* newname = path + msg->oldnamelen + 1;
|
||||||
|
|
||||||
|
Inode* olddir = SafeGetInode(fs, msg->olddirino);
|
||||||
|
if ( !olddir ) { free(path); RespondError(chl, errno); return; }
|
||||||
|
Inode* newdir = SafeGetInode(fs, msg->newdirino);
|
||||||
|
if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
bool result = newdir->Rename(olddir, oldname, newname);
|
||||||
|
|
||||||
|
newdir->Unref();
|
||||||
|
olddir->Unref();
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) msg;
|
||||||
|
struct statvfs stvfs;
|
||||||
|
stvfs.f_bsize = fs->cluster_size;
|
||||||
|
stvfs.f_frsize = fs->cluster_size;
|
||||||
|
stvfs.f_blocks = fs->cluster_count;
|
||||||
|
// TODO: Locate FsInfo and count on FAT12/FAT16.
|
||||||
|
stvfs.f_bfree = fs->CalculateFreeCount();
|
||||||
|
stvfs.f_bavail = stvfs.f_bfree;
|
||||||
|
stvfs.f_files = stvfs.f_blocks;
|
||||||
|
stvfs.f_ffree = stvfs.f_bfree;
|
||||||
|
stvfs.f_favail = stvfs.f_files;
|
||||||
|
stvfs.f_fsid = 0;
|
||||||
|
stvfs.f_flag = 0;
|
||||||
|
if ( !fs->device->write )
|
||||||
|
stvfs.f_flag |= ST_RDONLY;
|
||||||
|
stvfs.f_namemax = 8 + 3; // TODO: Long file name support.
|
||||||
|
RespondStatVFS(chl, &stvfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleTCGetBlob(int chl, struct fsm_req_tcgetblob* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
char* nameraw = (char*) &(msg[1]);
|
||||||
|
char* name = (char*) malloc(msg->namelen + 1);
|
||||||
|
if ( !name )
|
||||||
|
return (void) RespondError(chl, errno);
|
||||||
|
memcpy(name, nameraw, msg->namelen);
|
||||||
|
name[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
static const char index[] = "device-path\0filesystem-type\0filesystem-uuid\0mount-path\0";
|
||||||
|
if ( !strcmp(name, "") )
|
||||||
|
RespondTCGetBlob(chl, index, sizeof(index) - 1);
|
||||||
|
else if ( !strcmp(name, "device-path") )
|
||||||
|
RespondTCGetBlob(chl, fs->device->path, strlen(fs->device->path));
|
||||||
|
else if ( !strcmp(name, "filesystem-type") )
|
||||||
|
RespondTCGetBlob(chl, "fat", strlen("fat"));
|
||||||
|
else if ( !strcmp(name, "filesystem-uuid") )
|
||||||
|
{
|
||||||
|
unsigned char uuid[16];
|
||||||
|
if ( fs->fat_type == 32 )
|
||||||
|
{
|
||||||
|
memcpy(uuid, &fs->bpb->fat32_volume_id, 4);
|
||||||
|
memcpy(uuid + 4, &fs->bpb->fat32_volume_id, 11);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(uuid, &fs->bpb->fat12_volume_id, 4);
|
||||||
|
memcpy(uuid + 4, &fs->bpb->fat12_volume_id, 11);
|
||||||
|
}
|
||||||
|
uuid[15] = '\0';
|
||||||
|
RespondTCGetBlob(chl, uuid, sizeof(uuid));
|
||||||
|
}
|
||||||
|
else if ( !strcmp(name, "mount-path") )
|
||||||
|
RespondTCGetBlob(chl, fs->mount_path, strlen(fs->mount_path));
|
||||||
|
else
|
||||||
|
RespondError(chl, ENOENT);
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
|
||||||
|
{
|
||||||
|
request_uid = hdr->uid;
|
||||||
|
request_gid = hdr->gid;
|
||||||
|
if ( (uint16_t) request_uid != request_uid ||
|
||||||
|
(uint16_t) request_gid != request_gid )
|
||||||
|
{
|
||||||
|
warn("id exceeded 16-bit: uid=%ju gid=%ju\n",
|
||||||
|
(uintmax_t) request_uid, (uintmax_t) request_gid);
|
||||||
|
RespondError(chl, EOVERFLOW);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
typedef void (*handler_t)(int, void*, Filesystem*);
|
||||||
|
handler_t handlers[FSM_MSG_NUM] = { NULL };
|
||||||
|
handlers[FSM_REQ_SYNC] = (handler_t) HandleSync;
|
||||||
|
handlers[FSM_REQ_STAT] = (handler_t) HandleStat;
|
||||||
|
handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode;
|
||||||
|
handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner;
|
||||||
|
handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate;
|
||||||
|
handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek;
|
||||||
|
handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt;
|
||||||
|
handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen;
|
||||||
|
handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir;
|
||||||
|
handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt;
|
||||||
|
handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY;
|
||||||
|
handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens;
|
||||||
|
handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir;
|
||||||
|
handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir;
|
||||||
|
handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink;
|
||||||
|
handlers[FSM_REQ_LINK] = (handler_t) HandleLink;
|
||||||
|
handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink;
|
||||||
|
handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink;
|
||||||
|
handlers[FSM_REQ_RENAME] = (handler_t) HandleRename;
|
||||||
|
handlers[FSM_REQ_REFER] = (handler_t) HandleRefer;
|
||||||
|
handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref;
|
||||||
|
handlers[FSM_REQ_STATVFS] = (handler_t) HandleStatVFS;
|
||||||
|
handlers[FSM_REQ_TCGETBLOB] = (handler_t) HandleTCGetBlob;
|
||||||
|
if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] )
|
||||||
|
{
|
||||||
|
warn("message type %zu not supported\n", hdr->msgtype);
|
||||||
|
RespondError(chl, ENOTSUP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t body_buffer[65536];
|
||||||
|
uint8_t* body = body_buffer;
|
||||||
|
if ( sizeof(body_buffer) < hdr->msgsize )
|
||||||
|
{
|
||||||
|
body = (uint8_t*) mmap(NULL, hdr->msgsize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if ( (void*) body == MAP_FAILED )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( readall(chl, body, hdr->msgsize) == hdr->msgsize )
|
||||||
|
handlers[hdr->msgtype](chl, body, fs);
|
||||||
|
else
|
||||||
|
RespondError(chl, errno);
|
||||||
|
if ( sizeof(body_buffer) < hdr->msgsize )
|
||||||
|
munmap(body, hdr->msgsize);
|
||||||
|
}
|
||||||
|
static volatile bool should_terminate = false;
|
||||||
|
|
||||||
|
void TerminationHandler(int)
|
||||||
|
{
|
||||||
|
should_terminate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ready(void)
|
||||||
|
{
|
||||||
|
const char* readyfd_env = getenv("READYFD");
|
||||||
|
if ( !readyfd_env )
|
||||||
|
return;
|
||||||
|
int readyfd = atoi(readyfd_env);
|
||||||
|
char c = '\n';
|
||||||
|
write(readyfd, &c, 1);
|
||||||
|
close(readyfd);
|
||||||
|
unsetenv("READYFD");
|
||||||
|
}
|
||||||
|
|
||||||
|
int fsmarshall_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev)
|
||||||
|
{
|
||||||
|
(void) argv0;
|
||||||
|
|
||||||
|
// Stat the root directory;
|
||||||
|
struct stat root_dir_st;
|
||||||
|
memset(&root_dir_st, 0, sizeof(root_dir_st));
|
||||||
|
root_dir_st.st_ino = fs->root_inode_id;
|
||||||
|
root_dir_st.st_mode = S_IFDIR | 0755;
|
||||||
|
|
||||||
|
// Create a filesystem server connected to the kernel that we'll listen on.
|
||||||
|
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_dir_st, 0);
|
||||||
|
if ( serverfd < 0 )
|
||||||
|
err(1, "%s", mount_path);
|
||||||
|
|
||||||
|
// Make sure the server isn't unexpectedly killed and data is lost.
|
||||||
|
signal(SIGINT, TerminationHandler);
|
||||||
|
signal(SIGTERM, TerminationHandler);
|
||||||
|
signal(SIGQUIT, TerminationHandler);
|
||||||
|
|
||||||
|
// Become a background process in its own process group by default.
|
||||||
|
if ( !foreground )
|
||||||
|
{
|
||||||
|
pid_t child_pid = fork();
|
||||||
|
if ( child_pid < 0 )
|
||||||
|
err(1, "fork");
|
||||||
|
if ( child_pid )
|
||||||
|
exit(0);
|
||||||
|
setpgid(0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ready();
|
||||||
|
|
||||||
|
dev->SpawnSyncThread();
|
||||||
|
|
||||||
|
// Listen for filesystem messages and sync the filesystem every few seconds.
|
||||||
|
struct timespec last_sync_at;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &last_sync_at);
|
||||||
|
int channel;
|
||||||
|
while ( 0 <= (channel = accept(serverfd, NULL, NULL)) )
|
||||||
|
{
|
||||||
|
if ( should_terminate )
|
||||||
|
break;
|
||||||
|
struct fsm_msg_header hdr;
|
||||||
|
size_t amount;
|
||||||
|
if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) )
|
||||||
|
{
|
||||||
|
//warn("incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
|
||||||
|
errno = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
HandleIncomingMessage(channel, &hdr, fs);
|
||||||
|
close(channel);
|
||||||
|
|
||||||
|
if ( dev->write && !dev->has_sync_thread )
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
if ( 5 <= timespec_sub(now, last_sync_at).tv_sec )
|
||||||
|
{
|
||||||
|
fs->Sync();
|
||||||
|
last_sync_at = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with FAT concept.
|
||||||
|
// Garbage collect all open inode references.
|
||||||
|
while ( fs->mru_inode )
|
||||||
|
{
|
||||||
|
Inode* inode = fs->mru_inode;
|
||||||
|
if ( inode->remote_reference_count )
|
||||||
|
inode->RemoteUnref();
|
||||||
|
else if ( inode->reference_count )
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync the filesystem before shutting down.
|
||||||
|
if ( dev->write )
|
||||||
|
fs->Sync();
|
||||||
|
|
||||||
|
close(serverfd);
|
||||||
|
|
||||||
|
delete fs;
|
||||||
|
delete dev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fsmarshall.h
|
||||||
|
* Sortix fsmarshall frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FSMARSHALL_H
|
||||||
|
#define FSMARSHALL_H
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
class Filesystem;
|
||||||
|
|
||||||
|
int fsmarshall_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev);
|
||||||
|
#endif
|
|
@ -0,0 +1,618 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fuse.cpp
|
||||||
|
* FUSE frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__sortix__)
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define FUSE_USE_VERSION 26
|
||||||
|
#include <fuse.h>
|
||||||
|
|
||||||
|
#include "ext-constants.h"
|
||||||
|
#include "ext-structs.h"
|
||||||
|
|
||||||
|
#include "blockgroup.h"
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "fatfs.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "fuse.h"
|
||||||
|
#include "inode.h"
|
||||||
|
|
||||||
|
struct fat_fuse_ctx
|
||||||
|
{
|
||||||
|
Device* dev;
|
||||||
|
Filesystem* fs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef S_SETABLE
|
||||||
|
#define S_SETABLE 02777
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FUSE_FS (((struct fat_fuse_ctx*) (fuse_get_context()->private_data))->fs)
|
||||||
|
|
||||||
|
void* fat_fuse_init(struct fuse_conn_info* /*conn*/)
|
||||||
|
{
|
||||||
|
return fuse_get_context()->private_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fat_fuse_destroy(void* fs_private)
|
||||||
|
{
|
||||||
|
struct fat_fuse_ctx* fat_fuse_ctx = (struct fat_fuse_ctx*) fs_private;
|
||||||
|
while ( fat_fuse_ctx->fs->mru_inode )
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_ctx->fs->mru_inode;
|
||||||
|
if ( inode->remote_reference_count )
|
||||||
|
inode->RemoteUnref();
|
||||||
|
else if ( inode->reference_count )
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
fat_fuse_ctx->fs->Sync();
|
||||||
|
fat_fuse_ctx->dev->Sync();
|
||||||
|
delete fat_fuse_ctx->fs; fat_fuse_ctx->fs = NULL;
|
||||||
|
delete fat_fuse_ctx->dev; fat_fuse_ctx->dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inode* fat_fuse_resolve_path(const char* path)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode(FAT_ROOT_INO);
|
||||||
|
if ( !inode )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
while ( path[0] )
|
||||||
|
{
|
||||||
|
if ( *path == '/' )
|
||||||
|
{
|
||||||
|
if ( !FAT_S_ISDIR(inode->Mode()) )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t elem_len = strcspn(path, "/");
|
||||||
|
char* elem = strndup(path, elem_len);
|
||||||
|
if ( !elem )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path += elem_len;
|
||||||
|
Inode* next = inode->Open(elem, O_RDONLY, 0);
|
||||||
|
free(elem);
|
||||||
|
inode->Unref();
|
||||||
|
if ( !next )
|
||||||
|
return NULL;
|
||||||
|
inode = next;
|
||||||
|
}
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes that the path doesn't end with / unless it's the root directory.
|
||||||
|
Inode* fat_fuse_parent_dir(const char** path_ptr)
|
||||||
|
{
|
||||||
|
const char* path = *path_ptr;
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode(FAT_ROOT_INO);
|
||||||
|
if ( !inode )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
while ( strchr(path, '/') )
|
||||||
|
{
|
||||||
|
if ( *path == '/' )
|
||||||
|
{
|
||||||
|
if ( !FAT_S_ISDIR(inode->Mode()) )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t elem_len = strcspn(path, "/");
|
||||||
|
char* elem = strndup(path, elem_len);
|
||||||
|
if ( !elem )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path += elem_len;
|
||||||
|
Inode* next = inode->Open(elem, O_RDONLY, 0);
|
||||||
|
free(elem);
|
||||||
|
inode->Unref();
|
||||||
|
if ( !next )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
inode = next;
|
||||||
|
}
|
||||||
|
*path_ptr = *path ? path : ".";
|
||||||
|
assert(!strchr(*path_ptr, '/'));
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_getattr(const char* path, struct stat* st)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
StatInode(inode, st);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_fgetattr(const char* /*path*/, struct stat* st,
|
||||||
|
struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
StatInode(inode, st);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_readlink(const char* path, char* buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FAT_S_ISLNK(inode->Mode()) )
|
||||||
|
return inode->Unref(), -(errno = EINVAL);
|
||||||
|
if ( !bufsize )
|
||||||
|
return inode->Unref(), -(errno = EINVAL);
|
||||||
|
ssize_t amount = inode->ReadAt((uint8_t*) buf, bufsize, 0);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return inode->Unref(), -errno;
|
||||||
|
buf[(size_t) amount < bufsize ? (size_t) amount : bufsize - 1] = '\0';
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_mknod(const char* path, mode_t mode, dev_t dev)
|
||||||
|
{
|
||||||
|
(void) path;
|
||||||
|
(void) mode;
|
||||||
|
(void) dev;
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_mkdir(const char* path, mode_t mode)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
Inode* newdir = inode->CreateDirectory(path, ExtModeFromHostMode(mode));
|
||||||
|
inode->Unref();
|
||||||
|
if ( !newdir )
|
||||||
|
return -errno;
|
||||||
|
newdir->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_unlink(const char* path)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
bool success = inode->Unlink(path, false);
|
||||||
|
inode->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_rmdir(const char* path)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
bool success = inode->RemoveDirectory(path);
|
||||||
|
inode->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_symlink(const char* oldname, const char* newname)
|
||||||
|
{
|
||||||
|
Inode* newdir = fat_fuse_parent_dir(&newname);
|
||||||
|
if ( !newdir )
|
||||||
|
return -errno;
|
||||||
|
bool success = newdir->Symlink(newname, oldname);
|
||||||
|
newdir->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_rename(const char* oldname, const char* newname)
|
||||||
|
{
|
||||||
|
Inode* olddir = fat_fuse_parent_dir(&oldname);
|
||||||
|
if ( !olddir )
|
||||||
|
return -errno;
|
||||||
|
Inode* newdir = fat_fuse_parent_dir(&newname);
|
||||||
|
if ( !newdir )
|
||||||
|
return olddir->Unref(), -errno;
|
||||||
|
bool success = newdir->Rename(olddir, oldname, newname);
|
||||||
|
newdir->Unref();
|
||||||
|
olddir->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_link(const char* oldname, const char* newname)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(oldname);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
Inode* newdir = fat_fuse_parent_dir(&newname);
|
||||||
|
if ( !newdir )
|
||||||
|
return inode->Unref(), -errno;
|
||||||
|
bool success = inode->Link(newname, inode, false);
|
||||||
|
newdir->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_chmod(const char* path, mode_t mode)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
uint32_t req_mode = ExtModeFromHostMode(mode);
|
||||||
|
uint32_t old_mode = inode->Mode();
|
||||||
|
uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE);
|
||||||
|
inode->SetMode(new_mode);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_chown(const char* path, uid_t owner, gid_t group)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->SetUserId((uint32_t) owner);
|
||||||
|
inode->SetGroupId((uint32_t) group);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_truncate(const char* path, off_t size)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->Truncate((uint64_t) size);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_ftruncate(const char* /*path*/, off_t size,
|
||||||
|
struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->Truncate((uint64_t) size);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_open(const char* path, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
int flags = fi->flags;
|
||||||
|
Inode* dir = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !dir )
|
||||||
|
return -errno;
|
||||||
|
Inode* result = dir->Open(path, flags, 0);
|
||||||
|
dir->Unref();
|
||||||
|
if ( !result )
|
||||||
|
return -errno;
|
||||||
|
fi->fh = (uint64_t) result->inode_id;
|
||||||
|
fi->keep_cache = 1;
|
||||||
|
result->RemoteRefer();
|
||||||
|
result->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_access(const char* path, int mode)
|
||||||
|
{
|
||||||
|
Inode* dir = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !dir )
|
||||||
|
return -errno;
|
||||||
|
Inode* result = dir->Open(path, O_RDONLY, 0);
|
||||||
|
dir->Unref();
|
||||||
|
if ( !result )
|
||||||
|
return -errno;
|
||||||
|
(void) mode;
|
||||||
|
result->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
int flags = fi->flags | O_CREAT;
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
Inode* result = inode->Open(path, flags, ExtModeFromHostMode(mode));
|
||||||
|
inode->Unref();
|
||||||
|
if ( !result )
|
||||||
|
return -errno;
|
||||||
|
fi->fh = (uint64_t) result->inode_id;
|
||||||
|
fi->keep_cache = 1;
|
||||||
|
result->RemoteRefer();
|
||||||
|
result->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_opendir(const char* path, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
return fat_fuse_open(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_read(const char* /*path*/, char* buf, size_t count, off_t offset,
|
||||||
|
struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
if ( INT_MAX < count )
|
||||||
|
count = INT_MAX;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset);
|
||||||
|
inode->Unref();
|
||||||
|
return 0 <= result ? (int) result : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_write(const char* /*path*/, const char* buf, size_t count,
|
||||||
|
off_t offset, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
if ( INT_MAX < count )
|
||||||
|
count = INT_MAX;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
ssize_t result = inode->WriteAt((const uint8_t*) buf, count, offset);
|
||||||
|
inode->Unref();
|
||||||
|
return 0 <= result ? (int) result : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_statfs(const char* /*path*/, struct statvfs* stvfs)
|
||||||
|
{
|
||||||
|
memset(stvfs, 0, sizeof(*stvfs));
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
stvfs->f_bsize = fs->block_size;
|
||||||
|
stvfs->f_frsize = fs->block_size;
|
||||||
|
stvfs->f_blocks = fs->num_blocks;
|
||||||
|
stvfs->f_bfree = fs->sb->s_free_blocks_count;
|
||||||
|
stvfs->f_bavail = fs->sb->s_free_blocks_count;
|
||||||
|
stvfs->f_files = fs->num_inodes;
|
||||||
|
stvfs->f_ffree = fs->sb->s_free_inodes_count;
|
||||||
|
stvfs->f_favail = fs->sb->s_free_inodes_count;
|
||||||
|
stvfs->f_ffree = fs->sb->s_free_inodes_count;
|
||||||
|
stvfs->f_fsid = 0;
|
||||||
|
stvfs->f_flag = 0;
|
||||||
|
if ( !fs->device->write )
|
||||||
|
stvfs->f_flag |= ST_RDONLY;
|
||||||
|
stvfs->f_namemax = 255;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_flush(const char* /*path*/, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
inode->Sync();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_release(const char* /*path*/, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
inode->RemoteUnref();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_releasedir(const char* path, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
return fat_fuse_release(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
inode->Sync();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int fat_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
return fat_fuse_sync(path, data, fi);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_setxattr(const char *, const char *, const char *, size_t, int)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_getxattr(const char *, const char *, char *, size_t)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_listxattr(const char *, char *, size_t)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_removexattr(const char *, const char *)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fat_fuse_readdir(const char* /*path*/, void* buf, fuse_fill_dir_t filler,
|
||||||
|
off_t rec_num, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !S_ISDIR(inode->Mode()) )
|
||||||
|
return inode->Unref(), -(errno = ENOTDIR);
|
||||||
|
|
||||||
|
uint64_t file_size = inode->Size();
|
||||||
|
uint64_t offset = 0;
|
||||||
|
Block* block = NULL;
|
||||||
|
uint64_t block_id = 0;
|
||||||
|
while ( offset < file_size )
|
||||||
|
{
|
||||||
|
uint64_t entry_block_id = offset / fs->block_size;
|
||||||
|
uint64_t entry_block_offset = offset % fs->block_size;
|
||||||
|
if ( block && block_id != entry_block_id )
|
||||||
|
block->Unref(),
|
||||||
|
block = NULL;
|
||||||
|
if ( !block && !(block = inode->GetBlock(block_id = entry_block_id)) )
|
||||||
|
return inode->Unref(), -errno;
|
||||||
|
const uint8_t* block_data = block->block_data + entry_block_offset;
|
||||||
|
const struct ext_dirent* entry = (const struct ext_dirent*) block_data;
|
||||||
|
if ( entry->inode && entry->name_len && (!rec_num || !rec_num--) )
|
||||||
|
{
|
||||||
|
char* entry_name = strndup(entry->name, entry->name_len);
|
||||||
|
if ( !entry_name )
|
||||||
|
return block->Unref(), inode->Unref(), -errno;
|
||||||
|
memcpy(entry_name, entry->name, entry->name_len);
|
||||||
|
bool full = filler(buf, entry_name, NULL, 0);
|
||||||
|
free(entry_name);
|
||||||
|
if ( full )
|
||||||
|
{
|
||||||
|
block->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += entry->reclen;
|
||||||
|
}
|
||||||
|
if ( block )
|
||||||
|
block->Unref();
|
||||||
|
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int fat_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fat_fuse_utimens(const char* path, const struct timespec tv[2])
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->BeginWrite();
|
||||||
|
inode->data->i_atime = tv[0].tv_sec;
|
||||||
|
inode->data->i_mtime = tv[1].tv_sec;
|
||||||
|
inode->FinishWrite();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int fat_fuse_bmap(const char*, size_t blocksize, uint64_t* idx)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fat_fuse_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev)
|
||||||
|
{
|
||||||
|
struct fuse_operations operations;
|
||||||
|
memset(&operations, 0, sizeof(operations));
|
||||||
|
|
||||||
|
operations.access = fat_fuse_access;
|
||||||
|
operations.chmod = fat_fuse_chmod;
|
||||||
|
operations.chown = fat_fuse_chown;
|
||||||
|
operations.create = fat_fuse_create;
|
||||||
|
operations.destroy = fat_fuse_destroy;
|
||||||
|
operations.fgetattr = fat_fuse_fgetattr;
|
||||||
|
operations.flush = fat_fuse_flush;
|
||||||
|
operations.fsync = fat_fuse_fsync;
|
||||||
|
operations.ftruncate = fat_fuse_ftruncate;
|
||||||
|
operations.getattr = fat_fuse_getattr;
|
||||||
|
operations.init = fat_fuse_init;
|
||||||
|
operations.link = fat_fuse_link;
|
||||||
|
operations.mkdir = fat_fuse_mkdir;
|
||||||
|
operations.mknod = fat_fuse_mknod;
|
||||||
|
operations.opendir = fat_fuse_opendir;
|
||||||
|
operations.open = fat_fuse_open;
|
||||||
|
operations.readdir = fat_fuse_readdir;
|
||||||
|
operations.read = fat_fuse_read;
|
||||||
|
operations.readlink = fat_fuse_readlink;
|
||||||
|
operations.releasedir = fat_fuse_releasedir;
|
||||||
|
operations.release = fat_fuse_release;
|
||||||
|
operations.rename = fat_fuse_rename;
|
||||||
|
operations.rmdir = fat_fuse_rmdir;
|
||||||
|
operations.statfs = fat_fuse_statfs;
|
||||||
|
operations.symlink = fat_fuse_symlink;
|
||||||
|
operations.truncate = fat_fuse_truncate;
|
||||||
|
operations.unlink = fat_fuse_unlink;
|
||||||
|
operations.utimens = fat_fuse_utimens;
|
||||||
|
operations.write = fat_fuse_write;
|
||||||
|
|
||||||
|
operations.flag_nullpath_ok = 1;
|
||||||
|
operations.flag_nopath = 1;
|
||||||
|
|
||||||
|
char* argv_fuse[] =
|
||||||
|
{
|
||||||
|
(char*) argv0,
|
||||||
|
(char*) "-s",
|
||||||
|
(char*) mount_path,
|
||||||
|
(char*) NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1;
|
||||||
|
|
||||||
|
struct fat_fuse_ctx fat_fuse_ctx;
|
||||||
|
fat_fuse_ctx.fs = fs;
|
||||||
|
fat_fuse_ctx.dev = dev;
|
||||||
|
|
||||||
|
(void) foreground;
|
||||||
|
|
||||||
|
return fuse_main(argc_fuse, argv_fuse, &operations, &fat_fuse_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fuse.h
|
||||||
|
* FUSE frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSE_H
|
||||||
|
#define FUSE_H
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
class Filesystem;
|
||||||
|
|
||||||
|
int fat_fuse_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev);
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
Inode* prev_hashed;
|
||||||
|
Inode* next_hashed;
|
||||||
|
Inode* prev_dirty;
|
||||||
|
Inode* next_dirty;
|
||||||
|
Block* data_block;
|
||||||
|
struct fat_dirent* dirent; // TODO: Rename to data?
|
||||||
|
struct ext_inode* data; // TODO: Remove?
|
||||||
|
uint32_t first_cluster;
|
||||||
|
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);
|
||||||
|
bool 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);
|
||||||
|
Block* GetClusterSector(uint32_t cluster, uint8_t sector);
|
||||||
|
bool Iterate(Block** block_ptr, uint32_t* cluster_ptr,
|
||||||
|
uint8_t* sector_ptr, uint16_t* offset);
|
||||||
|
uint32_t SeekCluster(uint32_t cluster_id);
|
||||||
|
Inode* Open(const char* elem, int flags, mode_t mode);
|
||||||
|
bool Link(const char* elem, Inode* dest, bool directories);
|
||||||
|
bool Symlink(const char* elem, const char* dest);
|
||||||
|
bool Unlink(const char* elem, bool directories, bool force=false);
|
||||||
|
Inode* UnlinkKeep(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 UnembedInInode();
|
||||||
|
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 BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Modified();
|
||||||
|
void Use();
|
||||||
|
void Unlink();
|
||||||
|
void Prelink();
|
||||||
|
void Delete();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* 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 <assert.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_ptr, size_t least, size_t max)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = read(fd, buf + done, max - done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t writeleast(int fd, const void* buf_ptr, size_t least, size_t max)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = write(fd, buf + done, max - done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t preadleast(int fd, void* buf_ptr, size_t least, size_t max, off_t off)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = pread(fd, buf + done, max - done, off + done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t pwriteleast(int fd, const void* buf_ptr, size_t least, size_t max, off_t off)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = pwrite(fd, buf + done, max - done, off + done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__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,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool checkbit(const uint8_t* bitmap, size_t bit)
|
||||||
|
{
|
||||||
|
uint8_t bits = bitmap[bit / 8UL];
|
||||||
|
return bits & (1U << (bit % 8UL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void setbit(uint8_t* bitmap, size_t bit)
|
||||||
|
{
|
||||||
|
bitmap[bit / 8UL] |= 1U << (bit % 8UL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void clearbit(uint8_t* bitmap, size_t bit)
|
||||||
|
{
|
||||||
|
bitmap[bit / 8UL] &= ~(1U << (bit % 8UL));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -17,6 +17,7 @@ devices.o \
|
||||||
crc32.o \
|
crc32.o \
|
||||||
ext2.o \
|
ext2.o \
|
||||||
extended.o \
|
extended.o \
|
||||||
|
fat.o \
|
||||||
filesystem.o \
|
filesystem.o \
|
||||||
gpt.o \
|
gpt.o \
|
||||||
harddisk.o \
|
harddisk.o \
|
||||||
|
|
|
@ -99,6 +99,7 @@ static bool ext2_probe(struct blockdevice* bdev,
|
||||||
const unsigned char* leading,
|
const unsigned char* leading,
|
||||||
size_t amount)
|
size_t amount)
|
||||||
{
|
{
|
||||||
|
// TODO: Strongly prefer a UUID indication before probing.
|
||||||
(void) bdev;
|
(void) bdev;
|
||||||
if ( amount < 1024 )
|
if ( amount < 1024 )
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fat.c
|
||||||
|
* File Allocation Table filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <mount/blockdevice.h>
|
||||||
|
#include <mount/fat.h>
|
||||||
|
#include <mount/filesystem.h>
|
||||||
|
#include <mount/partition.h>
|
||||||
|
#include <mount/uuid.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static size_t fat_probe_amount(struct blockdevice* bdev)
|
||||||
|
{
|
||||||
|
(void) bdev;
|
||||||
|
return 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fat_probe(struct blockdevice* bdev,
|
||||||
|
const unsigned char* leading,
|
||||||
|
size_t amount)
|
||||||
|
{
|
||||||
|
(void) leading;
|
||||||
|
(void) amount;
|
||||||
|
// TODO: Relax restriction that this must be a partition? At least for non-EFI.
|
||||||
|
struct partition* p = bdev->p;
|
||||||
|
if ( !p )
|
||||||
|
return false;
|
||||||
|
// TODO: Test for a space padded FAT32 at 0x52 + 8 bytes.
|
||||||
|
if ( p->table_type == PARTITION_TABLE_TYPE_GPT )
|
||||||
|
{
|
||||||
|
unsigned char bdp_uuid[16];
|
||||||
|
uuid_from_string(bdp_uuid, BDP_GPT_TYPE_UUID);
|
||||||
|
unsigned char esp_uuid[16];
|
||||||
|
uuid_from_string(esp_uuid, ESP_GPT_TYPE_UUID);
|
||||||
|
// TODO: Additional probing is needed to detect FAT vs NTFS.
|
||||||
|
return memcmp(p->gpt_type_guid, bdp_uuid, 16) == 0 ||
|
||||||
|
memcmp(p->gpt_type_guid, esp_uuid, 16) == 0;
|
||||||
|
}
|
||||||
|
if ( p->table_type == PARTITION_TABLE_TYPE_MBR )
|
||||||
|
return p->mbr_system_id == 0x01 ||
|
||||||
|
p->mbr_system_id == 0x04 ||
|
||||||
|
p->mbr_system_id == 0x06 ||
|
||||||
|
p->mbr_system_id == 0x04 ||
|
||||||
|
p->mbr_system_id == 0x0C ||
|
||||||
|
p->mbr_system_id == 0x0E ||
|
||||||
|
p->mbr_system_id == 0xEF;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fat_release(struct filesystem* fs)
|
||||||
|
{
|
||||||
|
if ( !fs )
|
||||||
|
return;
|
||||||
|
free(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum filesystem_error fat_inspect(struct filesystem** fs_ptr,
|
||||||
|
struct blockdevice* bdev)
|
||||||
|
{
|
||||||
|
*fs_ptr = NULL;
|
||||||
|
struct filesystem* fs = CALLOC_TYPE(struct filesystem);
|
||||||
|
if ( !fs )
|
||||||
|
return FILESYSTEM_ERROR_ERRNO;
|
||||||
|
fs->bdev = bdev;
|
||||||
|
fs->handler = &fat_handler;
|
||||||
|
fs->handler_private = NULL;
|
||||||
|
unsigned char vbr[512];
|
||||||
|
if ( blockdevice_preadall(bdev, vbr, sizeof(vbr), 0) != sizeof(vbr) )
|
||||||
|
return fat_release(fs), FILESYSTEM_ERROR_ERRNO;
|
||||||
|
// TODO: Report efi instead if ESP.
|
||||||
|
fs->fstype_name = "fat";
|
||||||
|
// TODO: Port a fat fsck.
|
||||||
|
fs->driver = "fatfs";
|
||||||
|
fs->flags |= FILESYSTEM_FLAG_UUID;
|
||||||
|
// Use the serial number + label as the UUID.
|
||||||
|
// TODO: The location varies between FAT12/16 and FAT32. This is the wrong
|
||||||
|
// way to detect the FAT type.
|
||||||
|
if ( vbr[82 + 3] == '3' && vbr[82 + 4] == '2' )
|
||||||
|
memcpy(fs->uuid, vbr + 67, 4 + 11);
|
||||||
|
else
|
||||||
|
memcpy(fs->uuid, vbr + 39, 4 + 11);
|
||||||
|
return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct filesystem_handler fat_handler =
|
||||||
|
{
|
||||||
|
.handler_name = "fat",
|
||||||
|
.probe_amount = fat_probe_amount,
|
||||||
|
.probe = fat_probe,
|
||||||
|
.inspect = fat_inspect,
|
||||||
|
.release = fat_release,
|
||||||
|
};
|
|
@ -31,6 +31,7 @@
|
||||||
#include <mount/blockdevice.h>
|
#include <mount/blockdevice.h>
|
||||||
#include <mount/ext2.h>
|
#include <mount/ext2.h>
|
||||||
#include <mount/extended.h>
|
#include <mount/extended.h>
|
||||||
|
#include <mount/fat.h>
|
||||||
#include <mount/filesystem.h>
|
#include <mount/filesystem.h>
|
||||||
#include <mount/iso9660.h>
|
#include <mount/iso9660.h>
|
||||||
#include <mount/partition.h>
|
#include <mount/partition.h>
|
||||||
|
@ -57,6 +58,7 @@ static const struct filesystem_handler* filesystem_handlers[] =
|
||||||
&extended_handler,
|
&extended_handler,
|
||||||
&ext2_handler,
|
&ext2_handler,
|
||||||
&iso9660_handler,
|
&iso9660_handler,
|
||||||
|
&fat_handler,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* mount/fat.h
|
||||||
|
* File Allocation Table filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_MOUNT_FAT_H
|
||||||
|
#define INCLUDE_MOUNT_FAT_H
|
||||||
|
|
||||||
|
#include <mount/filesystem.h>
|
||||||
|
|
||||||
|
#define BDP_GPT_TYPE_UUID "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"
|
||||||
|
#define ESP_GPT_TYPE_UUID "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const struct filesystem_handler fat_handler;
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue