Compare commits

..

43 Commits

Author SHA1 Message Date
Jonas 'Sortie' Termansen bb5551bdfe Add fatfs(8). 2023-10-11 01:19:48 +02:00
Jonas 'Sortie' Termansen f3c7fece10 Add mtools port. 2023-10-11 01:19:28 +02:00
Jonas 'Sortie' Termansen d3ae2d886d fixup! Add tix-upgrade(8). 2023-10-11 01:19:11 +02:00
Jonas 'Sortie' Termansen 6b8ec2be0d Fix broken manual references. 2023-09-11 20:38:25 +02:00
Jonas 'Sortie' Termansen 346f202d48 fixup! Add tix-upgrade(8). 2023-09-11 20:37:46 +02:00
Jonas 'Sortie' Termansen 44423e0162 fixup! Package the system as a tix binary package. 2023-09-11 20:36:33 +02:00
Jonas 'Sortie' Termansen d992f098c2 fixup! Add tix-upgrade(8). 2023-09-03 23:03:38 +02:00
Jonas 'Sortie' Termansen fa8dcef53a Package the system as a tix binary package. 2023-09-03 23:03:18 +02:00
Jonas 'Sortie' Termansen 81f8b48a4e Save kernel options upon installation. 2023-09-03 17:24:48 +02:00
Jonas 'Sortie' Termansen 3d9fe6099f Add options to kernelinfo(2). 2023-09-03 17:23:25 +02:00
Jonas 'Sortie' Termansen 93d8b66a12 Add getty(8). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 4a4b797a22 Add terminal and interrupt support to com(4). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 8b8df40a33 Add nyan(1). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 24a7609382 Draft video-player. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 145c2b3bdc Work around pty deadlock. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 7dd4576af7 Add cdrom mounting live environment. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 49d7832bc5 Revert "Parallelize driver initialization."
This reverts commit 0fef08bbc4.
2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 54d00eaa76 Parallelize driver initialization. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 95abd1d012 Speed up ata(4) 400 ns waits.
Waiting for any non-zero duration currently waits for at least one timer
cycle (10 ms), which is especially expensive during early boot.

The current workaround of simply reading the status 14 times seems really
suspicious although the osdev wiki documents it, but let's see how well it
works on real hardware, it's probably good enough.

Try to determine the initial selected drive to save one drive selection.
2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 3e56683384 Decrease PS/2 timeouts. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen e9172b8419 Add uptime(1) -pr options. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 00c46edd45 Add iso9660 filesystem implementation. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 3edf6d241f Add kernel virtual address space usage debug information. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 0896bd7861 Revert "Update to bison-3.8.2."
This reverts commit b82fae810b42c5426d21c4dc153b32f086dd7fde.
2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 49144d640a Update to bison-3.8.2. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen cf27a38487 Debug TCP socket state listing. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 2f67e1a933 Add kernel heap allocation tracing debug facility. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen cc51ba9242 Add m4, perl, and texinfo to the basic ports set. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 77a35e65d0 Trianglix 4. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 5f4669e89b Add tix-check(8). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 6de4339912 Volatile release. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 6aa87bac8b Add tix-upgrade(8). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 9cef9b2a3a Add signify port. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen e92c00c385 Add pty(1). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 977aec5c1f Add irc(1).
Co-authored-by: Juhani Krekelä <juhani@krekelä.fi>
2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen edd95a5b68 Add getaddrinfo(1). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 5a50c5721e Add host(1). 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 08baadb23e Enable stack smash protection by default. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen c827c318ba Enable undefined behavior sanitization by default. 2023-09-03 14:47:44 +02:00
Jonas 'Sortie' Termansen 938f2390dd Support system upgrades and configuration in GRUB.
Move /etc/default/grub to /etc/grub as it's owned by the sysadmin.

Move /etc/grub.d to /etc/default/grub.d as it's owned by the system.

Support /etc/grub's GRUB_CMDLINE_SORTIX in 10_sortix.

Remove the old /etc/grub.d/10_sortix.cache with a compatibility hook as it
has moved to /etc/default/grub.d/10_sortix.cache.
2023-09-03 14:47:08 +02:00
Juhani Krekelä c4b878beb7 Redisable perl support in git.
If git with perl enabled is cross-compiled, the perl commands do not
work correctly. As there is seemingly no easy fix for this, disable perl
suppport again.
2023-08-27 20:22:22 +03:00
Juhani Krekelä 69cc658036 Move perl from git's BUILD_LIBRARIES to RUNTIME_PROGRAMS.
While RUNTIME_PROGRAMS currently does nothing, BUILD_LIBRARIES is not
correct for dependencies that are required at runtime. As git built with
perl support is still able to run without perl, just with limitations,
mark perl as an optional dependency as well.
2023-08-27 16:40:47 +00:00
Juhani Krekelä 6c81317026 Add perl as a dependency for git. 2023-08-27 15:22:57 +00:00
57 changed files with 6088 additions and 170 deletions

View File

@ -82,7 +82,7 @@ CHAIN_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).chain.tar
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.tar
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.tar
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.tar
SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.tar
SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.tix.tar
.PHONY: all
all: sysroot
@ -255,6 +255,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers
|| exit $$?; done)
LC_ALL=C sort -u "$(SYSROOT)/tix/manifest/system" > "$(SYSROOT)/tix/manifest/system.new"
mv "$(SYSROOT)/tix/manifest/system.new" "$(SYSROOT)/tix/manifest/system"
printf 'TIX_VERSION=3\nNAME=system\nPLATFORM=x86_64-sortix\nPREFIX=\nSYSTEM=true\n' > "$(SYSROOT)/tix/tixinfo/system"
.PHONY: sysroot-source
sysroot-source: sysroot-fsh
@ -509,7 +510,7 @@ $(SRC_INITRD): sysroot
$(SYSTEM_INITRD): sysroot
sed -E 's,^/,,' "$(SYSROOT)/tix/manifest/system" | \
tar -cf $(SYSTEM_INITRD) -C "$(SYSROOT)" --numeric-owner --owner=0 --group=0 --no-recursion -T - tix/manifest/system
tar -cf $(SYSTEM_INITRD) -C "$(SYSROOT)" --numeric-owner --owner=0 --group=0 --no-recursion -T - tix/manifest/system tix/tixinfo/system
# Packaging
@ -570,7 +571,7 @@ ifeq ($(SORTIX_ISO_COMPRESSION),xz)
test ! -e "$(OVERLAY_INITRD)" || \
xz -c $(OVERLAY_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.tar.xz
xz -c $(SRC_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.tar.xz
xz -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.tar.xz
xz -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/repository/system.tix.tar.xz
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue --compress=xz -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else ifeq ($(SORTIX_ISO_COMPRESSION),gzip)
@ -579,7 +580,7 @@ else ifeq ($(SORTIX_ISO_COMPRESSION),gzip)
test ! -e "$(OVERLAY_INITRD)" || \
gzip -c $(OVERLAY_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.tar.gz
gzip -c $(SRC_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.tar.gz
gzip -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.tar.gz
gzip -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/repository/system.tix.tar.gz
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue --compress=gz -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else # none
@ -588,7 +589,7 @@ else # none
test ! -e "$(OVERLAY_INITRD)" || \
cp $(OVERLAY_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.tar
cp $(SRC_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.tar
cp $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.tar
cp $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/repository/system.tix.tar
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
endif
@ -633,6 +634,7 @@ $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/overlay.tar.xz: $(OVERLAY_INITR
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/src.tar.xz: $(SRC_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
xz -c $< > $@
# TODO: Temporary compatibility.
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/system.tar.xz: $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
xz -c $< > $@
@ -667,7 +669,7 @@ $(SORTIX_RELEASE_DIR)/$(RELEASE)/man:
$(SORTIX_RELEASE_DIR)/$(RELEASE)/man/ports.list: sysroot $(SORTIX_RELEASE_DIR)/$(RELEASE)/man
for section in 1 2 3 4 5 6 7 8 9; do mkdir -p $(SORTIX_RELEASE_DIR)/$(RELEASE)/man/man$$section; done
for port in system `LC_ALL=C ls "$(SYSROOT)/tix/tixinfo"`; do \
for port in `LC_ALL=C ls "$(SYSROOT)/tix/tixinfo"`; do \
for manpage in `grep -E "^/share/man/man[1-9]/.*\.[1-9]$$" "$(SYSROOT)/tix/manifest/$$port" | \
LC_ALL=C sort | \
tee $(SORTIX_RELEASE_DIR)/$(RELEASE)/man/$$port.list | \
@ -682,8 +684,9 @@ $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST):
mkdir -p $@
.PHONY: release-repository
release-repository: sysroot $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST)
for port in `LC_ALL=C ls "$(SYSROOT)/tix/tixinfo"`; do \
release-repository: sysroot $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST)
xz -c $(SYSTEM_INITRD) > $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST)/system.tix.tar.xz
for port in `LC_ALL=C ls "$(SYSROOT)/tix/tixinfo" | (grep -Ev '^system$$' || true)`; do \
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.tix.tar.xz $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST) && \
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.version $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST); \
done

View File

@ -120,7 +120,7 @@ else
live_initrd=$(maybe_compressed boot/live.tar)
overlay_initrd=$(maybe_compressed boot/overlay.tar)
src_initrd=$(maybe_compressed boot/src.tar)
system_initrd=$(maybe_compressed boot/system.tar)
system_initrd=$(maybe_compressed repository/system.tix.tar)
initrds="$system_initrd $src_initrd $live_initrd $overlay_initrd"
fi
if $mount; then
@ -128,7 +128,8 @@ if $mount; then
else
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//')
sed -E 's/\.tix\.tar\.xz$//' |
(grep -Ev '^system$' || true))
fi
mkdir -p boot/grub

View File

@ -281,7 +281,7 @@ echo "Generating manhtml index"
ep
section "Ports manual pages" "ports"
bp
cat ports.list | sort |
grep -Ev '^system$' ports.list | sort |
while read port; do
if [ -s "$port.list" ]; then
link "$port.html" "$port"
@ -292,7 +292,7 @@ echo "Generating manhtml index"
ep
end_html) | finalize_html index.html
(echo system && cat ports.list) |
(echo system && grep -Ev '^system$' ports.list) |
while read port; do
echo "Generating manhtml index for $port"
(if [ "$port" = system ]; then prettyport="System"; else prettyport=$port; fi

View File

@ -1,3 +1,3 @@
set_minimal="cut dash e2fsprogs grep grub libssl mdocml sed signify tar wget xargs xorriso xz"
set_minimal="cut dash e2fsprogs grep grub libssl mdocml sed signify tar wget xargs xz"
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libstdc++ make m4 nano ntpd patch perl pkg-config python ssh texinfo vim"
sets="basic minimal"

View File

@ -49,6 +49,7 @@
#include <mount/blockdevice.h>
#include <mount/devices.h>
#include <mount/ext2.h>
#include <mount/fat.h>
#include <mount/filesystem.h>
#include <mount/gpt.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;
const char* question = "Format a filesystem? (no/ext2)";
if ( is_mbr )
question = "Format a filesystem? (no/ext2/extended)";
question = "Format a filesystem? (no/ext2/extended/fat)";
else if ( is_gpt )
question = "Format a filesystem? (no/ext2/biosboot)";
question = "Format a filesystem? (no/ext2/biosboot/efi/fat)";
if ( 5 <= argc )
strlcpy(fstype, argv[4], sizeof(fstype));
else
@ -1781,7 +1782,9 @@ static void on_mkpart(size_t argc, char** argv)
if ( strcmp(fstype, "no") != 0 &&
strcmp(fstype, "ext2") != 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",
argv[0], device_name(current_hd->path), fstype);
@ -1801,14 +1804,18 @@ static void on_mkpart(size_t argc, char** argv)
break;
}
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 )
{
const char* def = !strcmp(fstype, "efi") ? "/boot/efi" : "no";
if ( 6 <= argc )
strlcpy(mountpoint, argv[5], sizeof(mountpoint));
else
prompt(mountpoint, sizeof(mountpoint),
"Where to mount partition? (mountpoint or 'no')", "no");
"Where to mount partition? (mountpoint or 'no')", def);
if ( !strcmp(mountpoint, "no") )
{
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";
if ( !strcmp(fstype, "biosboot") )
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);
arc4random_buf(p.unique_partition_guid, sizeof(p.unique_partition_guid));
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)

View File

@ -17,7 +17,7 @@ in windows.
.Pp
The user's preferred startup applications are launched on startup by launching
the
.Xr session
.Xr session 5
program (if set) or otherwise the
.Xr displayrc 5
script in the background.

2
fat/.gitignore vendored Normal file
View File

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

34
fat/Makefile Normal file
View File

@ -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

165
fat/block.cpp Normal file
View File

@ -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;
}

62
fat/block.h Normal file
View File

@ -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

276
fat/blockgroup.cpp Normal file
View File

@ -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()
{
}

68
fat/blockgroup.h Normal file
View File

@ -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

220
fat/device.cpp Normal file
View File

@ -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);
}

64
fat/device.h Normal file
View File

@ -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

132
fat/ext-constants.h Normal file
View File

@ -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

128
fat/ext-structs.h Normal file
View File

@ -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

124
fat/fat.h Normal file
View File

@ -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

339
fat/fatfs.cpp Normal file
View File

@ -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
}

33
fat/fatfs.h Normal file
View File

@ -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

547
fat/filesystem.cpp Normal file
View File

@ -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;
}

97
fat/filesystem.h Normal file
View File

@ -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

888
fat/fsmarshall.cpp Normal file
View File

@ -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, &sector, &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

31
fat/fsmarshall.h Normal file
View File

@ -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

618
fat/fuse.cpp Normal file
View File

@ -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

32
fat/fuse.h Normal file
View File

@ -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

1299
fat/inode.cpp Normal file

File diff suppressed because it is too large Load Diff

94
fat/inode.h Normal file
View File

@ -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

150
fat/ioleast.h Normal file
View File

@ -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

49
fat/util.h Normal file
View File

@ -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

View File

@ -80,6 +80,7 @@
#include "kb/default-kblayout.h"
#include "kb/kblayout.h"
#include "kb/ps2.h"
#include "kernelinfo.h"
#include "logterminal.h"
#include "mouse/ps2.h"
#include "multiboot.h"
@ -230,6 +231,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
FreeKernelAddress(&alloc);
}
if ( !(kernel_options = strdup(cmdline ? cmdline : "")) )
Panic("Failed to allocate kernel command line");
int argmax = 1;
argv = new char*[argmax + 1];
if ( !argv )
@ -253,6 +257,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
}
argv[argc] = NULL;
// Add new once-only options to sysinstall's normalize_kernel_options.
bool no_random_seed = false;
for ( int i = 0; i < argc; i++ )
{

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2012, 2015, 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
@ -24,6 +24,7 @@
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/syscall.h>
#include "kernelinfo.h"
#include "net/tcp.h"
#ifndef VERSIONSTR
@ -32,11 +33,14 @@
namespace Sortix {
char* kernel_options;
static const char* KernelInfo(const char* req)
{
if ( strcmp(req, "name") == 0 ) { return BRAND_KERNEL_NAME; }
if ( strcmp(req, "version") == 0 ) { return VERSIONSTR; }
if ( strcmp(req, "tagline") == 0 ) { return BRAND_RELEASE_TAGLINE; }
if ( strcmp(req, "options") == 0 ) { return kernel_options; }
if ( strcmp(req, "builddate") == 0 ) { return __DATE__; }
if ( strcmp(req, "buildtime") == 0 ) { return __TIME__; }
#if defined(__i386__) || defined(__x86_64__)

29
kernel/kernelinfo.h Normal file
View File

@ -0,0 +1,29 @@
/*
* 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.
*
* kernelinfo.h
* Lets user-space query information about the kernel.
*/
#ifndef KERNELINFO_H
#define KERNELINFO_H
namespace Sortix {
extern char* kernel_options;
} // namespace Sortix
#endif

View File

@ -17,6 +17,7 @@ devices.o \
crc32.o \
ext2.o \
extended.o \
fat.o \
filesystem.o \
gpt.o \
harddisk.o \

View File

@ -99,6 +99,7 @@ static bool ext2_probe(struct blockdevice* bdev,
const unsigned char* leading,
size_t amount)
{
// TODO: Strongly prefer a UUID indication before probing.
(void) bdev;
if ( amount < 1024 )
return false;

112
libmount/fat.c Normal file
View File

@ -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,
};

View File

@ -31,6 +31,7 @@
#include <mount/blockdevice.h>
#include <mount/ext2.h>
#include <mount/extended.h>
#include <mount/fat.h>
#include <mount/filesystem.h>
#include <mount/iso9660.h>
#include <mount/partition.h>
@ -57,6 +58,7 @@ static const struct filesystem_handler* filesystem_handlers[] =
&extended_handler,
&ext2_handler,
&iso9660_handler,
&fat_handler,
NULL,
};

View File

@ -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

View File

@ -16,7 +16,5 @@ CONFIGURE_VARS='ac_cv_fread_reads_directories=no ac_cv_snprintf_returns_bogus=no
# that likes to colorize even when the output device is a pipe, which
# causes trouble here. You can safely remove this sortie has learned the
# error of his ways.
# Perl is disabled because Sortix has no perl port at the moment and I'm not
# confident it cross-compiles properly. It also installs stuff into /usr/local
# when prefix is empty and not sure how that happens.
# Perl is disabled because perl features do not cross-compile correctly.
MAKE_VARS='V=1 MAKE=make NO_PERL=NoThanks'

109
ports/mtools/mtools.patch Normal file
View File

@ -0,0 +1,109 @@
diff -Paur --no-dereference -- mtools.upstream/configure mtools/configure
--- mtools.upstream/configure
+++ mtools/configure
@@ -3964,17 +3964,11 @@
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-/* Override any GCC internal prototype to avoid an error.
- Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
-char iconv ();
+#include <iconv.h>
int
main ()
{
-return iconv ();
+return (int) iconv;
;
return 0;
}
diff -Paur --no-dereference -- mtools.upstream/file.c mtools/file.c
--- mtools.upstream/file.c
+++ mtools/file.c
@@ -522,7 +522,7 @@
extern long timezone;
#endif
tzset();
- tzone = (long) timezone;
+ tzone = (long) 0;
}
#else
tzone = 0;
diff -Paur --no-dereference -- mtools.upstream/mainloop.h mtools/mainloop.h
--- mtools.upstream/mainloop.h
+++ mtools/mainloop.h
@@ -18,7 +18,6 @@
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <sys/param.h>
#include "mtoolsDirentry.h"
typedef struct bounded_string {
diff -Paur --no-dereference -- mtools.upstream/mcopy.c mtools/mcopy.c
--- mtools.upstream/mcopy.c
+++ mtools/mcopy.c
@@ -39,28 +39,12 @@
static void set_mtime(const char *target, time_t mtime)
{
if (target && strcmp(target, "-") && mtime != 0L) {
-#ifdef HAVE_UTIMES
- struct timeval tv[2];
- tv[0].tv_sec = mtime;
- tv[0].tv_usec = 0;
- tv[1].tv_sec = mtime;
- tv[1].tv_usec = 0;
- utimes(target, tv);
-#else
-#ifdef HAVE_UTIME
-#ifndef HAVE_UTIMBUF
- struct utimbuf {
- time_t actime; /* access time */
- time_t modtime; /* modification time */
- };
-#endif
- struct utimbuf utbuf;
-
- utbuf.actime = mtime;
- utbuf.modtime = mtime;
- utime(target, &utbuf);
-#endif
-#endif
+ struct timespec ts[2];
+ ts[0].tv_sec = mtime;
+ ts[0].tv_nsec = 0;
+ ts[1].tv_sec = mtime;
+ ts[1].tv_nsec = 0;
+ utimens(target, ts);
}
return;
}
diff -Paur --no-dereference -- mtools.upstream/sysincludes.h mtools/sysincludes.h
--- mtools.upstream/sysincludes.h
+++ mtools/sysincludes.h
@@ -397,19 +397,11 @@
/* missing functions */
#ifndef HAVE_SRANDOM
-# ifdef OS_mingw32msvc
-# define srandom srand
-# else
-# define srandom srand48
-# endif
+# define srandom
#endif
#ifndef HAVE_RANDOM
-# ifdef OS_mingw32msvc
-# define random (long)rand
-# else
-# define random (long)lrand48
-# endif
+# define random (long)arc4random
#endif
#ifndef HAVE_STRCHR

11
ports/mtools/mtools.port Normal file
View File

@ -0,0 +1,11 @@
NAME=mtools
BUILD_LIBRARIES='libiconv'
VERSION=4.0.43
DISTNAME=$NAME-$VERSION
COMPRESSION=tar.bz2
ARCHIVE=$DISTNAME.$COMPRESSION
SHA256SUM=541e179665dc4e272b9602f2074243591a157da89cc47064da8c5829dbd2b339
UPSTREAM_SITE=https://ftp.gnu.org/gnu/mtools
UPSTREAM_ARCHIVE=$ARCHIVE
LICENSE=GPL-3.0-or-later
BUILD_SYSTEM=configure

View File

@ -0,0 +1,3 @@
rm -rf -- 'mtools.info'
rm -rf -- 'mtools.1'
rm -rf -- 'mtools.5'

View File

@ -55,7 +55,7 @@ The shell is a login shell.
Interactive shells run the
.Xr profile 5
script on startup instead of the
.Xr shrc
.Xr shrc 5
script.
This option is set if the shell is invoked by a name starting with a dash
.Sq - .

View File

@ -154,6 +154,15 @@ question could be answered with
to dynamically hash the bootloader password.
.It Sy grub_password_empty Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy no )
Allow an insecure empty bootloader password typed interactively?
.It Sy kernel_options Ns "=" Ns Ar options
What
.Xr kernel 7
options should be set in
.Xr grub 5
with
.Sy GRUB_CMDLINE_SORTIX ?
This question is asked only the GRUB port is installed, if the kernel was booted
with explicit options or this question is set.
.It Sy disked Ns "=" Ns Ar commands
Commands to create partitions and filesystems with
.Xr disked 8 ?

View File

@ -168,7 +168,7 @@ Like
but runs it in
.Xr terminal 1
inside the
.Xr display 8
.Xr display 1
graphical user interface environment.
This operating system mode is insecure because it boots straight to root access
without a password.

View File

@ -143,7 +143,7 @@ internal layout that can be directly extracted into the filesystem.
The metatdata in
.Pa /tix
is now using the
.Xr tix-vars 1
.Xr tix-vars 8
format in the style of
.Xr port 5 .
.Pp

View File

@ -170,8 +170,8 @@ selecting
.Sy Disable network drivers .
It can be useful to disable the network drivers if it's undesirable to put the
system on the network for security reasons.
You can disable network drivers by default by editing the bootloader
configuration as described below after completing the installation.
You will be asked later if you'd like to save this choice in the kernel
options.
.Pp
By default
.Xr dhclient 8
@ -271,6 +271,18 @@ The password will be hashed and stored in
and is inserted into the GRUB configuration when
.Xr update-grub 8
is run.
.Pp
If the
.Xr kernel 7
was booted with explicit options via the advanced bootloader menu, then you
will be asked if you'd like to make these changes permanent via the
.Sy GRUB_CMDLINE_SORTIX
variable in the
.Pa /etc/grub
configuration file.
Run
.Xr update-grub 8
to apply the changes if you edit this file.
.Ss Partitioning
You will now need to set up a partition for the root filesystem and other
filesystems you wish to use.
@ -495,55 +507,6 @@ The
manual page is a basic overview of the system for new users.
.Pp
Congratulations on your new Sortix system.
.Ss Disabling Networking by Default
To disable networking drivers by default, edit the bootloader configuration to
pass the
.Fl \-disable-network-drivers
option by default on the
.Xr kernel 7
command line.
.Pp
If you are at the final stage of installation, you can answer
.Sy '!'
to get a shell in the live environment and then run
.Sy "chroot -d ."
to enter a shell inside the new installation.
.Pp
For instance, if GRUB is used the bootloader, networking can be disabled by
default by done by editing
.Pa /etc/grub.d/10_sortix
of the new installation.
.Xr editor 1
or any editor can be used to edit the file.
Change the line from
.Bd -literal
multiboot $BOOT_REL/sortix.bin
.Ed
.Pp
to instead be
.Bd -literal
multiboot $BOOT_REL/sortix.bin --disable-network-drivers
.Ed
.Pp
If the included GRUB bootloader is used, after making the above edit, run
.Xr update-grub 8
within the new installation to regenerate the bootloader configuration.
Note that
.Pa /etc/default/grub.d/10_sortix
is part of the GRUB package and local changes will be undone when the GRUB
package is updated or reinstalled, in which case you must make this change again
and run
.Xr update-grub 8
again.
.Pp
If the included GRUB bootloader is not used, but instead the
.Pa /etc/default/grub.d/10_sortix.cache
fragment is spliced into another GRUB installation, make the above change and
then run the
.Pa /etc/default/grub.d/10_sortix
command and use the freshly regenerated
.Pa /etc/default/grub.d/10_sortix.cache
fragment instead.
.Sh SEE ALSO
.Xr chkblayout 1 ,
.Xr chvideomode 1 ,

View File

@ -83,11 +83,6 @@ The format of each multiboot module is automatically detected by default:
.Xr tar 7
archives in the ustar format are extracted into the root directory.
The bootloader must already have decompressed the archive.
If the archive contains the
.Pa tix/tixinfo
file, it is instead installed into the root directory as a
.Xr tix 7
binary package.
.El
.Pp
Each multiboot module has its own command line where the options are as follows:

View File

@ -393,7 +393,7 @@ Ports should never install such files and instead rely on
for locating dependencies.
.Pp
The
.Xr tix-eradicate-libtool-la 1
.Xr tix-eradicate-libtool-la 8
program can be used to remove any installed
.Pa .la
files in the

View File

@ -17,6 +17,7 @@
* File operation utility functions.
*/
#include <sys/kernelinfo.h>
#include <sys/stat.h>
#include <sys/types.h>
@ -222,6 +223,25 @@ char** read_lines_file(const char* path, size_t* out_count)
return lines;
}
char* akernelinfo(const char* request)
{
char* buffer = NULL;
size_t size = 0;
while ( true )
{
errno = 0;
ssize_t needed = kernelinfo(request, buffer, size);
if ( needed < 0 )
return free(buffer), NULL;
if ( errno != ERANGE )
return buffer;
size = (size_t) needed + 1;
free(buffer);
if ( !(buffer = malloc(size)) )
return NULL;
}
}
// TODO: Hack to support installing from a read-only root filesystem.
#undef mkdtemp
char* mkdtemp_hack(char* templ)

View File

@ -27,6 +27,7 @@ void mkdir_or_chmod_or_die(const char* path, mode_t mode);
void write_random_seed(const char* path);
char* read_string_file(const char* path);
char** read_lines_file(const char* path, size_t* out_count);
char* akernelinfo(const char* request);
char* mkdtemp_hack(char* templ);
#define mkdtemp mkdtemp_hack

View File

@ -654,6 +654,8 @@ char** read_installed_list(const char* prefix, size_t* out_count)
{
if ( entry->d_name[0] == '.' )
continue;
if ( !strcmp(entry->d_name, "system") )
continue;
if ( !string_array_append(&installed, &count, &length, entry->d_name) )
{
warn("malloc");

View File

@ -28,6 +28,7 @@
#include <assert.h>
#include <brand.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
@ -350,6 +351,84 @@ static void grub_hash_password(char* buffer, size_t buffer_size, const char* pw)
errx(2, "grub password hash failed");
}
static const char* const ignore_kernel_options[] =
{
"--no-random-seed",
"--random-seed",
NULL,
};
static char* normalize_kernel_options(void)
{
char* options = akernelinfo("options");
if ( !options )
{
warn("kernelinfo: options");
return NULL;
}
size_t i = 0, o = 0;
while ( options[i] )
{
if ( isspace((unsigned char) options[i]) )
{
i++;
continue;
}
if ( options[i] != '-' ) // Imperfect since quoting options is allowed.
break;
if ( !strncmp(options + i, "--", 2) &&
(!options[i + 2] || isspace((unsigned char) options[i + 2])) )
break;
bool ignored = false;
for ( size_t n = 0; ignore_kernel_options[n]; n++ )
{
const char* opt = ignore_kernel_options[n];
size_t len = strlen(opt);
if ( !strncmp(options + i, opt, len) &&
(!options[i + len] ||
isspace((unsigned char) options[i + len])) )
{
i += len;
ignored = true;
break;
}
}
if ( ignored )
continue;
bool singly = false;
bool doubly = false;
bool escaped = false;
for ( ; options[i]; i++ )
{
char c = options[i];
options[o++] = c;
if ( !escaped && !singly && !doubly && isspace((unsigned char) c) )
break;
if ( !escaped && !doubly && c == '\'' )
{
singly = !singly;
continue;
}
if ( !escaped && !singly && c == '"' )
{
doubly = !doubly;
continue;
}
if ( !singly && !escaped && c == '\\' )
{
escaped = true;
continue;
}
escaped = false;
}
}
while ( o && isspace((unsigned char) options[o - 1]) )
o--;
options[o] = '\0';
return options;
}
static pid_t main_pid;
static struct mountpoint* mountpoints;
static size_t mountpoints_used;
@ -772,6 +851,36 @@ int main(void)
text("\n");
}
char* kernel_options = normalize_kernel_options();
if ( (autoconf_has("kernel_options") ||
(kernel_options && kernel_options[0])) &&
!access_or_die("/tix/tixinfo/grub", F_OK) )
{
text("The operating system was booted with explicit kernel(7) options. "
"Would you like set them permanently in /etc/grub?\n\n");
while ( true )
{
char options[1024];
prompt(options, sizeof(options), "kernel_options",
"Kernel options? (OPTIONS/no)", kernel_options);
if ( !strcasecmp(options, "no") )
{
kernel_options = NULL;
break;
}
if ( options[0] )
{
install_configurationf("grub", "w",
"GRUB_CMDLINE_SORTIX='%s'\n", options);
textf("/etc/grub will be made with the kernel options.\n");
}
break;
}
text("\n");
}
free(kernel_options);
// TODO: Offer the user an automatic layout of partitions if the disk is
// empty.

View File

@ -30,7 +30,7 @@ builds a source code directory containing a
of software for this operating system.
.Nm
is usually invoked through the higher level
.Xr tix-port 7 .
.Xr tix-port 8 .
.Pp
The mandatory
.Ar port

View File

@ -35,14 +35,12 @@ output_release_file= # TODO: A better term for this?
output_release_sig_file= # TODO: A better term for this?
output_sha256sum=
patch=false
port=false
porttix=false
package=false
release=false
sha256=false
sha256sum=false
source=false
source_full=false
srctix=false
sysroot=""
toolchain=false
url=false
@ -115,15 +113,12 @@ for argument do
--output-sha256sum) previous_option=output_sha256sum ;;
--output-upgrade-file=*) output_upgrade_file=$parameter ;;
--output-upgrade-file) previous_option=output_upgrade_file ;;
--package) package=true ;;
--patch) patch=true ;;
--port) port=true ;;
--porttix) porttix=true ;;
--release) release=true ;;
--sha256) sha256=true ;;
--sha256sum) sha256sum=true ;;
--source-full) source_full=true ;;
--source) source=true ;;
--srctix) srctix=true ;;
--sysroot) previous_option=sysroot ;;
--sysroot=*) sysroot=$parameter ;;
--toolchain) toolchain=true ;;
@ -158,7 +153,7 @@ conf() {
tix-vars -d "$2" - "$4"
}
tmpdir=$(mktemp -dt tix-fetch-port.XXXXXX)
tmpdir=$(mktemp -dt tix-fetch.XXXXXX)
trap 'rm -rf -- "$tmpdir"' EXIT HUP INT QUIT TERM
upgrade_conf="${collection%/}/etc/upgrade.conf"
@ -428,8 +423,7 @@ fi
# Fetch each of the specified signed files from the mirror.
for REQUEST; do
# If --port then fetch the port by the requested name.
if $port; then
if $package; then
REQUEST="$REQUEST.tix.tar.xz"
REQUESTDIR="repository/$MACHINE-sortix/"
elif $boot; then
@ -444,12 +438,6 @@ for REQUEST; do
elif $normalize; then
REQUEST="$REQUEST.normalize"
REQUESTDIR="patches/"
elif $srctix; then
REQUEST="$REQUEST.srctix.tar.xz"
REQUESTDIR="srctix/"
elif $porttix; then
REQUEST="$REQUEST.porttix.tar.xz"
REQUESTDIR="porttix/"
elif $toolchain; then
REQUESTDIR="toolchain/"
else

View File

@ -24,7 +24,6 @@ clean=false
collection=/
download_only=false
fetch_options=
ports_only=false
sysroot=""
upgrade=--upgrade
upgrade_ports=false
@ -107,6 +106,12 @@ if [ ! -e "$collection/tix/manifest/system" ]; then
upgrade_system=false
fi
case "$upgrade_system$upgrade_ports" in
truefalse) what_to_upgrade=--system;;
falsetrue) what_to_upgrade=--ports;;
*) what_to_upgrade=;;
esac
if [ -z "$cachedir" ]; then
cachedir="${collection%/}/var/cache/tix"
fi
@ -120,7 +125,6 @@ if $cancel; then
sysmerge -t "$collection" --cancel
exit
fi
mkdir -p -- "$cachedir"
mkdir -p -- "$cachedir/new"
@ -164,50 +168,43 @@ if [ -n "$UPGRADE_SIG_URL" ]; then
fi
fi
mkdir -p -- "$cachedir/boot"
# Decide what binary packages to upgrade.
installed_packages=$(LC_ALL=C ls -- "$collection/tix/tixinfo")
if $upgrade_system && $upgrade_ports; then
upgrade_packages="$installed_packages"
else
upgrade_packages=
for package in $installed_packages; do
is_system=$(tix-vars -d false "$collection/tix/tixinfo/$package" SYSTEM)
if ($upgrade_system && [ "$is_system" = true ]) ||
($upgrade_ports && [ "$is_system" = false ]); then
upgrade_packages="$upgrade_packages $package"
fi
done
fi
# Fetch each binary package from the mirror.
# TODO: Handle new mandatory / recommended ports.
# TODO: Handle renamed ports.
# TODO: Tracking sets like minimal/basic/full.
mkdir -p -- "$cachedir/repository"
# TODO: DO NOT SUBMIT: Temporary -d system compatibility until builds roll.
SYSTEM_INITRDS=$(tix-vars -d system "$cachedir/release.sh" SYSTEM_INITRDS)
# TODO: What about the system source code in /src?
if $upgrade_system; then
# Fetch the base system initrds from the mirror.
for initrd in $SYSTEM_INITRDS; do
packages=""
for package in $(LC_ALL=C ls -- "$collection/tix/tixinfo"); do
# The package exists upstream if it has a hash.
sha256=$(tix-fetch $fetch_options \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
--sha256 --package -- $package)
if [ -n "$sha256" ]; then
tix-fetch $fetch_options \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
-c --initrd -O "$cachedir/boot" -- "$initrd"
done
fi
if $upgrade_ports; then
# Fetch each port from the mirror.
# TODO: Handle new mandatory / recommended ports.
# TODO: Handle renamed ports.
mkdir -p "$cachedir/repository"
ports=""
for port in $(LC_ALL=C ls -- "$collection/tix/tixinfo"); do
# The port has a hash if if it exists upstream.
sha256=$(tix-fetch $fetch_options \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
--sha256 --port -- $port)
# If the port exists upstream, get the latest version and note its name down
# for the extraction step.
if [ -n "$sha256" ]; then
tix-fetch $fetch_options \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
-c --port -O "$cachedir/repository" -- $port
ports="$ports $port"
fi
done
fi
-c --package -O "$cachedir/repository" -- $package
packages="$packages $package"
fi
done
# Stop if only downloading.
if $download_only; then
@ -229,35 +226,15 @@ release_sig_url = $UPGRADE_SIG_URL
EOF
fi
# Extract the base system into the sysroot.
if $upgrade_system; then
for initrd in $SYSTEM_INITRDS; do
echo "Extracting $initrd.tar.xz..."
tar -C "$cachedir/sysroot" -xJf "$cachedir/boot/$initrd.tar.xz"
rm -f "$cachedir/boot/$initrd.tar.xz"
done
fi
# Extract the ports into the sysroot.
full=
if $upgrade_ports; then
full=--full
for port in $ports; do
echo "Extracting $port.tix.tar.xz..."
tar -C "$cachedir/sysroot" -xJf "$cachedir/repository/$port.tix.tar.xz"
rm -f "$cachedir/repository/$port.tix.tar.xz"
done
fi
case "$upgrade_system$upgrade_ports" in
truefalse) what_to_upgrade=--system;;
falsetrue) what_to_upgrade=--ports;;
*) what_to_upgrade=;;
esac
# Extract the binary packages into the sysroot.
for package in $packages; do
echo "Extracting $package.tix.tar.xz..."
tar -C "$cachedir/sysroot" -xJf "$cachedir/repository/$package.tix.tar.xz"
rm -f "$cachedir/repository/$package.tix.tar.xz"
done
# Merge the new sysroot onto the installation.
sysmerge -t "$collection" $what_to_upgrade $full $wait "$cachedir/sysroot"
rm -rf -- "$cachedir/boot"
rm -rf -- "$cachedir/repository"
rm -rf -- "$cachedir/sysroot"

View File

@ -6,8 +6,8 @@
.Nd generate initialization ramdisk
.Sh SYNOPSIS
.Nm update-initrd
.Op Fl \-sysmerge
.Op Fl \-sysroot Ns "=" Ns Ar sysroot
TODO: --sysmerge
.Sh DESCRIPTION
.Nm update-initrd
generates the
@ -37,6 +37,15 @@ across incompatible ABI changes.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl \-sysmerge
Generate an
.Xr initrd 7
for the pending upgrade in
.Pa /sysmerge
inside the sysroot.
This must be used in combination with invoking the matching
.Pa /sysmerge/sbin/update-initrd
program directly for forward compatibility.
.It Fl \-sysroot Ns "=" Ns Ar sysroot
Rather than generating an
.Xr initrd 7
@ -44,7 +53,6 @@ for the root filesystem, locate files in the
.Ar sysroot
directory instead and store the result as
.Ar sysroot Ns Pa /boot/sortix.initrd .
TODO: --sysmerge
.El
.Sh FILES
.Bl -tag -width "/sbin/fsck.ext2" -compact

View File

@ -21,6 +21,10 @@ The name of the current kernel.
The version of the current kernel.
.It Sy tagline
The tagline (slogan) of the release.
.It Sy options
The
.Xr kernel 7
options.
.It Sy builddate
The date on which the current kernel was built.
.It Sy buildtime
@ -33,4 +37,5 @@ The firmware of the system (e.g. "bios" or "uefi")
will exit 0 on success and non-zero otherwise.
.Sh SEE ALSO
.Xr uname 1 ,
.Xr kernelinfo 2
.Xr kernelinfo 2 ,
.Xr kernel 7