Compare commits

...

89 Commits

Author SHA1 Message Date
Jonas 'Sortie' Termansen 2c9bdb7a9a Revert "Update to perl-5.39.5."
This reverts commit 781ff8880f3e2d29e31460427bccf50cc1ec436e.

File/Spec was no longer being installed when --all-static, breaking
texinfo.
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 06047eafe0 Update to perl-5.39.5. 2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 4396cce838 Revert "Update to python-3.12.1."
This reverts commit 8ae8363167cc195f92803489e2f97391e2527c5f.

The libglib build broke due to no distutils module.
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 718080eedd Update to python-3.12.1. 2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 11e86e277b Revert "Update to texinfo-7.1."
This reverts commit 9813bb1d13c625d48ecd950bfaafc274383ca049.

ffmpeg fails to build natively with the new makeinfo:

perl ./doc/texidep.pl . doc/ffmpeg-utils.texi doc/ffmpeg-utils.html >doc/ffmpeg-utils.html.d
makeinfo --html -I doc --no-split -D config-not-all --init-file=./doc/t2h.pm --output doc/ffmpeg-utils.html doc/ffmpeg-utils.texi
makeinfo: error parsing ./doc/t2h.pm: Undefined subroutine &Texinfo::Config::set_from_init_file called at ./doc/t2h.pm line 24.
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen b9bea5eea1 Update to texinfo-7.1. 2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 8632ee75aa Revert "Update to dash-0.5.12."
This reverts commit 16bedfc9630779c01ebae5513fd307e969c329de.

Something is wrong with the case pattern matching, maybe fnmatch?
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 5b717d7768 Update to dash-0.5.12. 2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen abdfa77ba8 Revert "Update to freetype-2.13.2."
This reverts commit 5ad1e5f6054dd1e44ecb955b9326c1198ef17ff0.

configure runs make and crashes on a stack overflow in make.
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 13a79d908b Update to freetype-2.13.2. 2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 75beb3589d Revert "Update to bison-3.8.2."
This reverts commit b82fae810b42c5426d21c4dc153b32f086dd7fde.
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen e211f0f76f Update to bison-3.8.2. 2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen 62f4d9cf26 Revert "Schedule interactive threads fairly under load."
This reverts commit 47731b91c37933943a73010c5a4494101cce52dc.

There is a rare freeze when the scheduler fails to run anything.
2024-03-25 23:27:52 +01:00
Jonas 'Sortie' Termansen dc86ee0f7c Schedule interactive threads fairly under load.
Preempted threads are now removed from the runnable list until every
other thread has been preempted or the system goes idle. This ensures
threads that roundtrip to other threads get a full chance to perform
their work cooperatively without being starved by CPU intensive threads
whenever they yield.
2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 08d03b2190 Kinda fix pager(1) man bullet points. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen f248b826f8 Fix ESP endian. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 2ee030f409 Support booting with EFI. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen f2a2d24161 Add kernel(7) --firmware option. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 99397d2a51 Add fatfs(8). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen ca98bb2e58 Add getty(8). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen d2fa44828c Add terminal and interrupt support to com(4). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 3f66ddf6d2 Add nyan(1). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen cd84d026f2 Work around pty deadlock. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen fb3b3714ce Add cdrom mounting live environment. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 9d9fc3a114 Revert "Parallelize driver initialization."
This reverts commit 0fef08bbc4.
2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 49fc70c39c Parallelize driver initialization. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 846c29b8ac 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.
2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 7b257f0991 Decrease PS/2 timeouts. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 916dfdbbbc Add uptime(1) -pr options. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 81ee6573d3 Add iso9660 filesystem implementation. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 21dffe6044 Add kernel virtual address space usage debug information. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 468acf2dd4 Debug TCP socket state listing. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 42369fe6ed Add kernel heap allocation tracing debug facility. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 6ec7fc8347 Trianglix 4. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 54a7416e53 Add tix-check(8). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen c81f35da5c Volatile release. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 9d447f48e7 Add tix-upgrade(8). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 1623063a72 Add tix-repository(8).
Support renaming, splitting, and deleting ports via RENAMES.

Unify on RUNTIME_DEPS for runtime dependencies.
2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 382e3a501d Add signify port. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 58b12c9682 Add pty(1). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen 8fdd4b56bc Add getaddrinfo(1). 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen bfb9e17ae2 Enable stack smash protection by default. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen d70a0cf055 Enable undefined behavior sanitization by default. 2024-03-25 23:27:51 +01:00
Jonas 'Sortie' Termansen a7f5ac82aa Update to git-2.43.0. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen 531d0dcc03 Update to vim-9.0. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen 117f9a9888 Update to nginx-1.25.3. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen e6aa160df4 Update to nasm-2.16.01. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen 71bdc34ab7 Update to openssh-9.6p1. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen 2026a283ab Update to xorriso-1.5.6. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen 9b778effb3 Update to wget-1.21.4. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen d461550fca Update to tar-1.35. 2024-03-25 23:22:34 +01:00
Jonas 'Sortie' Termansen 6ca79deb89 Update to sed-4.9. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 8a4d002ecd Update to nano-7.2. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen b2e2ffdb9d Update to patch-2.7.6. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen ef98f7cdf8 Update to make-4.4.1. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen bbc6285588 Update to m4-1.4.19. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen e2bbd2a9af Update to lua-5.4.6. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 0dc27723fe Update to hello-2.12.1. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 5b7ffe763f Update to gzip-1.13. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 35eb3b433d Update to grep-3.11. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 4af0f32860 Update to gawk-5.3.0. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 1876a2be0f Update to e2fsprogs-1.47.0. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 9d9980346b Update to diffutils-3.10. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 10309d67b8 Update to libreadline-8.2. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 9a52eb1d64 Update to libmpc-1.3.1. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 4db0d3dd1d Update to libmpfr-4.2.1. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 9db6b462fc Update to libjpeg-9f. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen cff50a8814 Update to libgmp-6.3.0. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 151cd05316 Update to libffi-3.4.3. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 6ee1916cbe Update to libdbus-1.15.6. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen fca73b3da0 Update to fontconfig-2.15.0. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 3a143de0f6 Update to curl-8.5.0. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 43b490fe08 Update to libressl-3.8.2. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen d67f5588f6 Update to libidn-1.42. 2024-03-25 23:22:33 +01:00
Jonas 'Sortie' Termansen 22af2f1ad3 Update to pixman-0.42.2. 2024-03-25 23:22:32 +01:00
Jonas 'Sortie' Termansen 43ccfe3fa8 Update to libxml2-2.12.4. 2024-03-25 23:22:32 +01:00
Jonas 'Sortie' Termansen 0146e4c9ad Update to xz-5.4.5. 2024-03-25 23:22:32 +01:00
Jonas 'Sortie' Termansen a8bae28082 Update to expat-2.5.0. 2024-03-25 23:22:32 +01:00
Jonas 'Sortie' Termansen 4c697aa447 Update to libpng-1.6.40. 2024-03-25 23:22:32 +01:00
Jonas 'Sortie' Termansen 3152413ff0 Avoid pedantic warnings in POSIX <signal.h>.
The -Wpedantic warnings are only supported in C <signal.h> mode, but the GCC
build and other ports use this kinda-unsupported mode and produce a lot of
these needless warnings, so remove the trailing commas in the enums.
2024-03-25 23:21:39 +01:00
Jonas 'Sortie' Termansen 2955e11211 Upgrading releases from source has been supported for a while. 2024-03-25 23:12:50 +01:00
Jonas 'Sortie' Termansen 1ddb9f9cb7 Simplify sorting ports in build order. 2024-03-25 23:11:37 +01:00
Jonas 'Sortie' Termansen 04b605477e Add IFF_UP to getifaddrs(3). 2024-03-25 23:11:36 +01:00
Jonas 'Sortie' Termansen a2a790a336 Use the old 10_sortix when upgrading without a new grub port. 2024-03-25 23:11:36 +01:00
Jonas 'Sortie' Termansen f1ed3194cb Cache wallpaper in login(8). 2024-03-25 23:11:36 +01:00
Jonas 'Sortie' Termansen 7d25725e0e Fix format string issue in sysmerge(8) conf_load error handler. 2024-03-25 23:11:36 +01:00
Jonas 'Sortie' Termansen dc1fb1ceca Fix update-initrd(8) not atomically replacing the initrd. 2024-03-25 23:11:36 +01:00
Jonas 'Sortie' Termansen fafaf86ddb Fix initrd makefile targets not ensuring the output directory. 2024-03-25 23:11:36 +01:00
Jonas 'Sortie' Termansen dd4e813550 Fix stray backslash warning in manhtml. 2024-02-24 22:07:55 +01:00
216 changed files with 14042 additions and 3025 deletions

View File

@ -20,13 +20,16 @@ display \
dnsconfig \
editor \
ext \
fat \
games \
hostname \
ifconfig \
init \
iso9660 \
kblayout \
kblayout-compiler \
login \
nyan \
ping \
regress \
rw \
@ -68,10 +71,13 @@ else
SORTIX_INCLUDE_SOURCE?=yes
endif
ISO_MOUNT?=no
include build-aux/dirs.mak
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
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
@ -231,6 +237,8 @@ sysroot-system: sysroot-fsh sysroot-base-headers
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
echo /etc/os-release >> "$(SYSROOT)/tix/manifest/system"
find etc | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
cp -RT etc "$(SYSROOT)/etc"
find share | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
cp -RT share "$(SYSROOT)/share"
export SYSROOT="$(SYSROOT)" && \
@ -269,6 +277,7 @@ else ifneq ($(SORTIX_INCLUDE_SOURCE),no)
cp Makefile -t "$(SYSROOT)/src"
cp README -t "$(SYSROOT)/src"
cp -RT build-aux "$(SYSROOT)/src/build-aux"
cp -RT etc "$(SYSROOT)/src/etc"
cp -RT share "$(SYSROOT)/src/share"
(for D in $(MODULES); do (cp -R $$D -t "$(SYSROOT)/src" && $(MAKE) -C "$(SYSROOT)/src/$$D" clean) || exit $$?; done)
endif
@ -441,6 +450,25 @@ release-all-archs:
# Initial ramdisk
$(CHAIN_INITRD).uuid:
mkdir -p `dirname $@`
uuidgen > $@
$(CHAIN_INITRD): $(CHAIN_INITRD).uuid sysroot
mkdir -p `dirname $(CHAIN_INITRD)`
rm -rf $(CHAIN_INITRD).d
mkdir -p $(CHAIN_INITRD).d
mkdir -p $(CHAIN_INITRD).d/etc
echo "UUID=`cat $(CHAIN_INITRD).uuid` / iso9660 ro 0 1" > $(CHAIN_INITRD).d/etc/fstab
mkdir -p $(CHAIN_INITRD).d/etc/init
echo require chain exit-code > $(CHAIN_INITRD).d/etc/init/default
mkdir -p $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/init" $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/iso9660fs" $(CHAIN_INITRD).d/sbin
LC_ALL=C ls -A $(CHAIN_INITRD).d | \
tar -cf $(CHAIN_INITRD) -C $(CHAIN_INITRD).d --numeric-owner --owner=0 --group=0 -T -
rm -rf $(CHAIN_INITRD).d
$(LIVE_INITRD): sysroot
mkdir -p `dirname $(LIVE_INITRD)`
rm -rf $(LIVE_INITRD).d
@ -452,6 +480,9 @@ $(LIVE_INITRD): sysroot
echo "include /etc/default/passwd.d/*" >> $(LIVE_INITRD).d/etc/passwd
echo "root::0:root" > $(LIVE_INITRD).d/etc/group
echo "include /etc/default/group.d/*" >> $(LIVE_INITRD).d/etc/group
(echo 'channel = $(CHANNEL)' && \
echo 'release_key = $(RELEASE_KEY)' && \
echo 'release_sig_url = $(RELEASE_SIG_URL)') > $(LIVE_INITRD).d/etc/upgrade.conf
mkdir -p $(LIVE_INITRD).d/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(LIVE_INITRD).d/root
(echo "You can view the documentation for new users by typing:" && \
@ -468,15 +499,18 @@ $(LIVE_INITRD): sysroot
.PHONY: $(OVERLAY_INITRD)
$(OVERLAY_INITRD): sysroot
mkdir -p `dirname $(OVERLAY_INITRD)`
test ! -d "$(SYSROOT_OVERLAY)" || \
LC_ALL=C ls -A "$(SYSROOT_OVERLAY)" | \
tar -cf $(OVERLAY_INITRD) -C "$(SYSROOT_OVERLAY)" --numeric-owner --owner=0 --group=0 -T -
$(SRC_INITRD): sysroot
mkdir -p `dirname $(SRC_INITRD)`
sed -E 's,^/,,' "$(SYSROOT)/tix/manifest/src" | \
tar -cf $(SRC_INITRD) -C "$(SYSROOT)" --numeric-owner --owner=0 --group=0 --no-recursion -T - tix/manifest/src
$(SYSTEM_INITRD): sysroot
mkdir -p `dirname $(SYSTEM_INITRD)`
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 tix/tixinfo/system
@ -487,6 +521,42 @@ $(SORTIX_BUILDS_DIR):
# Bootable images
ifeq ($(ISO_MOUNT),yes)
$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(CHAIN_INITRD) $(CHAIN_INITRD).uuid $(SORTIX_BUILDS_DIR)
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot
cp "$(SYSROOT)/boot/sortix.bin" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.bin
cp $(CHAIN_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.initrd
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc
echo "UUID=`cat $(CHAIN_INITRD).uuid` / iso9660 ro 0 1" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/fstab
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/init
echo require single-user exit-code > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/init/default
echo "root::0:0:root:/root:sh" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/passwd
echo "include /etc/default/passwd.d/*" >> $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/passwd
echo "root::0:root" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/group
echo "include /etc/default/group.d/*" >> $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/group
(echo 'channel = $(CHANNEL)' && \
echo 'release_key = $(RELEASE_KEY)' && \
echo 'release_sig_url = $(RELEASE_SIG_URL)') > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/upgrade.conf
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root
(echo "You can view the documentation for new users by typing:" && \
echo && \
echo " man user-guide" && \
echo && \
echo "You can view the installation instructions by typing:" && \
echo && \
echo " man installation") > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root/welcome
if [ -d "$(SYSROOT_OVERLAY)" ]; then cp -RT "$(SYSROOT_OVERLAY)" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso; fi
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/grub
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) --mount $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso sysroot $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso -- -volid Sortix -volset_id `cat $(CHAIN_INITRD).uuid`
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else
$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(LIVE_INITRD) $(OVERLAY_INITRD) $(SRC_INITRD) $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
@ -527,6 +597,8 @@ else # none
endif
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
endif
.PHONY: iso
iso: $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso
@ -615,6 +687,7 @@ release-repository: sysroot $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/re
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
tix-repository --generation=3 $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST)
.PHONY: release-scripts
release-scripts: \

View File

@ -101,7 +101,7 @@ if [ "$OPERATION" = build ]; then
DEPENDENCIES="$(PACKAGES="$PACKAGES" \
"$(dirname -- "$0")"/list-packages.sh \
--dependencies $PACKAGE)"
echo "$PACKAGE: $(echo "$DEPENDENCIES" | tr '\n' ' ' | sed -E 's/ +$/\n/')"
echo "$PACKAGE: $(echo "$DEPENDENCIES" | tr '\n' ' ')"
echo " @echo $PACKAGE"
done;
printf ".PHONY:"

View File

@ -124,9 +124,9 @@ endif
DEFAULT_GENERIC_OPTLEVEL_BASE:=-Os -s
DEFAULT_OPTLEVEL_FOR_BUILD:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
ifeq ($(BUILD_IS_SORTIX),1)
DEFAULT_OPTLEVEL_FOR_BUILD+=
DEFAULT_OPTLEVEL_FOR_BUILD+=-fsanitize=undefined -fstack-protector-all
endif
DEFAULT_OPTLEVEL:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
ifeq ($(HOST_IS_SORTIX),1)
DEFAULT_OPTLEVEL+=
DEFAULT_OPTLEVEL+=-fsanitize=undefined -fstack-protector-all
endif

View File

@ -23,8 +23,9 @@ set -e
this=$(which -- "$0")
thisdir=$(dirname -- "$this")
platform=
directory=
mount=false
platform=
version=
dashdash=
@ -44,8 +45,9 @@ for argument do
case $dashdash$argument in
--) dashdash=yes ;;
--platform=*) platform=$parameter ;;
--platform) previous_option=platform ;;
--platform=*) platform=$parameter ;;
--mount) mount=true ;;
--version=*) version=$parameter ;;
--version) previous_option=version ;;
-*) echo "$0: unrecognized option $argument" >&2
@ -111,14 +113,24 @@ isinset() {
cd "$directory"
kernel=$(maybe_compressed boot/sortix.bin)
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 repository/system.tix.tar)
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//' |
(grep -Ev '^system$' || true))
if $mount; then
initrd=$(maybe_compressed boot/sortix.initrd)
initrds=$initrd
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 repository/system.tix.tar)
initrds="$system_initrd $src_initrd $live_initrd $overlay_initrd"
fi
if $mount; then
ports=
else
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//' |
(grep -Ev '^system$' || true))
fi
mkdir -p boot/grub
mkdir -p boot/grub/init
@ -184,6 +196,12 @@ fi
set version="$version"
set machine="$machine"
set mount=$mount
if \$mount; then
chain='-- /sbin/init --target=chain'
else
chain=
fi
set base_menu_title="Sortix \$version for \$machine"
set menu_title="\$base_menu_title"
set title_single_user='live environment'
@ -205,6 +223,8 @@ set enable_sshd=false
export version
export machine
export mount
export chain
export base_menu_title
export menu_title
export title_single_user
@ -305,9 +325,10 @@ esac
cat << EOF
hook_kernel_pre
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
multiboot /$kernel \$no_random_seed \$enable_network_drivers "\$@"
multiboot /$kernel --firmware=\$grub_platform \$no_random_seed \$enable_network_drivers \$chain "\$@"
echo done
hook_kernel_post
# TODO: Injecting configuration doesn't work for mounted cdroms.
if ! \$enable_dhclient; then
echo -n "Disabling dhclient ... "
module /boot/grub/init/furthermore --create-to /etc/init/network
@ -332,7 +353,7 @@ cat << EOF
echo done
fi
EOF
for initrd in $system_initrd $src_initrd $live_initrd $overlay_initrd; do
for initrd in $initrds; do
if [ "$initrd" = "$src_initrd" ]; then
cat << EOF
if \$enable_src; then
@ -428,9 +449,11 @@ menuentry "\$title_sysupgrade" '-- /sbin/init --target=sysupgrade'
cat << EOF
if ! $mount; then
menuentry "Select ports..." {
configfile /boot/grub/ports.cfg
}
fi
menuentry "Advanced..." {
configfile /boot/grub/advanced.cfg
@ -462,6 +485,7 @@ else
}
fi
if ! $mount; then
if "\$enable_src"; then
menuentry "Disable loading source code" {
enable_src=false
@ -473,6 +497,7 @@ else
configfile /boot/grub/advanced.cfg
}
fi
fi
if [ "\$enable_network_drivers" = --disable-network-drivers ]; then
menuentry "Enable networking drivers" {
@ -486,6 +511,7 @@ else
}
fi
if ! $mount; then
if \$enable_dhclient; then
menuentry "Disable DHCP client" {
enable_dhclient=false
@ -525,6 +551,7 @@ fi
menuentry "Select binary packages..." {
configfile /boot/grub/tix.cfg
}
fi
hook_advanced_menu_post
EOF

View File

@ -359,7 +359,7 @@ for section in $SECTIONS; do
done
(find . -name '*.html' | while read FILE; do
grep -Eoh 'href=\"../man[[:digit:]]/[^/]+\.html\"' "$FILE" || true
grep -Eoh 'href="../man[[:digit:]]/[^/]+\.html"' "$FILE" || true
done) | sort -u | grep -Eo 'man[[:digit:]]/[^/]+\.html' | sed 's/\.html$//' |
while read manpage; do
if ! [ -e "$manpage" ]; then

View File

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

View File

@ -1,2 +1,6 @@
VERSION=1.1dev
CHANNEL?=volatile
RELEASE?=$(VERSION)
RELEASE_MASTER?=https://sortix.org/release
RELEASE_KEY=/etc/signify/sortix-$(VERSION).pub
RELEASE_SIG_URL?=$(RELEASE_MASTER)/$(CHANNEL)/$(HOST_MACHINE).sh.sig

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>
@ -1496,7 +1497,12 @@ static char* display_area_format(void* ctx, size_t row, size_t column)
{
case FILESYSTEM_ERROR_NONE: return strdup("(no error)");
case FILESYSTEM_ERROR_ABSENT: return strdup("none");
case FILESYSTEM_ERROR_UNRECOGNIZED: return strdup("unrecognized");
case FILESYSTEM_ERROR_UNRECOGNIZED:
{
char uuid[UUID_STRING_LENGTH + 1];
uuid_to_string(area->p->gpt_type_guid, uuid);
return strdup(uuid);
}
case FILESYSTEM_ERROR_ERRNO: return strdup("(error)");
}
return strdup("(unknown error)");
@ -1771,9 +1777,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 +1787,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 +1809,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 +2015,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 +2178,68 @@ 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[] =
{
"mkfs.fat",
"-F", // TODO: Force FAT32 until FAT12/16 root dir writing is added.
"32",
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

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWQiTQbFzyZJVobf/pn53Jp3njhRB9DgwkMaNakCpDE9RaTABMjlbz9W

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWQnkSm9lj1YIZYpt1Y3mHYzFsaky82gQF6CrW4lme9OoEYzSIl2ZsIC

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWTGrBXmGvl2zUpCa47ui5EyPsnitKLjsCZ2YZphNY8F3b33t6QWYDs1

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWRTbLQ+3+a9I5yche2BEVP03TRtumGO4Vgq1AQ/5bRj8JAJ1R0+vpxE

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

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

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

275
fat/fatfs.cpp Normal file
View File

@ -0,0 +1,275 @@
/*
* 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
#include "block.h"
#include "device.h"
#include "fat.h"
#include "fatfs.h"
#include "filesystem.h"
#include "inode.h"
#include "ioleast.h"
#include "util.h"
uid_t request_uid;
uid_t request_gid;
// 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");
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

446
fat/filesystem.cpp Normal file
View File

@ -0,0 +1,446 @@
/*
* 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 <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include "fat.h"
#include "block.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;
}
// TODO: Rename tenths to a better name.
void timespec_to_fat(const struct timespec* ts, uint16_t* date, uint16_t* time,
uint8_t* tenths)
{
struct tm tm;
gmtime_r(&ts->tv_sec, &tm);
// TODO: Endian.
*date = tm.tm_mday << 0 | (tm.tm_mon + 1) << 5 | (tm.tm_year - 80) << 9;
*time = (tm.tm_sec / 2) << 0 | tm.tm_min << 5 | tm.tm_hour << 11;
*tenths = ts->tv_nsec / 10000000 + (tm.tm_sec & 1 ? 100 : 0);
}
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->device = device;
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;
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();
}
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::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;
}

88
fat/filesystem.h Normal file
View File

@ -0,0 +1,88 @@
/*
* 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]);
void timespec_to_fat(const struct timespec* ts, uint16_t* date, uint16_t* time,
uint8_t* tenths);
// TODO: Unify into the above.
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 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;
Device* device;
const char* mount_path;
mode_t mode_reg;
mode_t mode_dir;
uid_t uid;
gid_t gid;
uint32_t block_size;
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;
Inode* mru_inode;
Inode* lru_inode;
Inode* dirty_inode;
Inode* hash_inodes[INODE_HASH_LENGTH];
bool dirty;
public:
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 ReadFAT(uint32_t cluster);
bool WriteFAT(uint32_t cluster, uint32_t value);
uint32_t CalculateFreeCount();
void BeginWrite();
void FinishWrite();
void Sync();
};
#endif

858
fat/fsmarshall.cpp Normal file
View File

@ -0,0 +1,858 @@
/*
* 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 "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;
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) )
{
if ( inode->implied_reference )
inode->implied_reference--;
else
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)
{
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)
{
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !inode->ChangeMode(msg->mode) )
RespondError(chl, errno);
else
RespondSuccess(chl);
inode->Unref();
}
void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs)
{
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !inode->ChangeOwner(msg->uid, msg->gid) )
RespondError(chl, errno);
else
RespondSuccess(chl);
inode->Unref();
}
void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs)
{
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
Inode* inode = SafeGetInode(fs, msg->ino);
inode->UTimens(msg->times);
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: Unfortunately Open does not implicitly imply RemoteRefer so we need
// to try and pretend that it does so the inode isn't destroyed early.
result->implied_reference++;
result->RemoteRefer();
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();
}
// TODO: Encapsulate.
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)
{
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)
{
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)
{
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)
{
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)
{
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"
"fat-size\0volume-id\0volume-label\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, "fat-size") )
{
const char* str = fs->fat_type == 32 ? "32" :
fs->fat_type == 16 ? "16" : "12";
RespondTCGetBlob(chl, str, strlen(str));
}
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_label, 11);
}
else
{
memcpy(uuid, &fs->bpb->fat12_volume_id, 4);
memcpy(uuid + 4, &fs->bpb->fat12_volume_label, 11);
}
uuid[15] = '\0';
RespondTCGetBlob(chl, uuid, sizeof(uuid));
}
else if ( !strcmp(name, "volume-id") )
{
if ( fs->fat_type == 32 )
RespondTCGetBlob(chl, &fs->bpb->fat32_volume_id, 4);
else
RespondTCGetBlob(chl, &fs->bpb->fat12_volume_id, 4);
}
else if ( !strcmp(name, "volume-label") )
{
if ( fs->fat_type == 32 )
RespondTCGetBlob(chl, &fs->bpb->fat32_volume_label, 11);
else
RespondTCGetBlob(chl, &fs->bpb->fat12_volume_label, 11);
}
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;
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

1015
fat/inode.cpp Normal file

File diff suppressed because it is too large Load Diff

90
fat/inode.h Normal file
View File

@ -0,0 +1,90 @@
/*
* 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;
struct fat_dirent deleted_dirent;
uint32_t first_cluster;
Filesystem* filesystem;
size_t reference_count;
size_t remote_reference_count;
size_t implied_reference;
uint32_t inode_id;
bool dirty;
bool deleted;
public:
uint32_t Mode();
uint32_t UserId();
uint32_t GroupId();
uint64_t Size();
void UTimens(const struct timespec times[2]);
bool ChangeMode(mode_t mode);
bool ChangeOwner(uid_t uid, gid_t gid);
bool Truncate(uint64_t new_size);
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 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

@ -3101,7 +3101,9 @@ static bool fsck(struct filesystem* fs)
if ( pid == 0 )
{
uninstall_signal_handler();
execlp(fs->fsck, fs->fsck, "-fp", "--", bdev_path, (const char*) NULL);
// TODO: Move to libmount abstraction.
const char* options = !strcmp(fs->fstype_name, "ext2") ? "-fp" : "-p";
execlp(fs->fsck, fs->fsck, options, "--", bdev_path, (const char*) NULL);
warning("%s: Failed to load filesystem checker: %s: %m",
bdev_path, fs->fsck);
_exit(127);

2
iso9660/.gitignore vendored Normal file
View File

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

33
iso9660/Makefile Normal file
View File

@ -0,0 +1,33 @@
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)
LIBS:=$(LIBS) -lfuse
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
endif
BINARIES:=iso9660fs
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
iso9660fs: *.cpp *.h
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
clean:
rm -f $(BINARIES) *.o

108
iso9660/block.cpp Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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 <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->prev_block = NULL;
this->next_block = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->device = device;
this->reference_count = 1;
this->block_id = block_id;
}
Block::~Block()
{
Destruct();
delete[] block_data;
}
void Block::Destruct()
{
Unlink();
}
void Block::Refer()
{
reference_count++;
}
void Block::Unref()
{
if ( !--reference_count )
{
#if 0
device->block_count--;
delete this;
#endif
}
}
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;
}

53
iso9660/block.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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:
Block* prev_block;
Block* next_block;
Block* prev_hashed;
Block* next_hashed;
Device* device;
size_t reference_count;
uint32_t block_id;
uint8_t* block_data;
public:
void Refer();
void Unref();
void Use();
void Unlink();
void Prelink();
};
#endif

107
iso9660/device.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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 <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Device::Device(int fd, const char* path, uint32_t block_size)
{
this->mru_block = NULL;
this->lru_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->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()
{
while ( mru_block )
delete mru_block;
close(fd);
}
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::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;
}

51
iso9660/device.h Normal file
View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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);
~Device();
public:
Block* mru_block;
Block* lru_block;
Block* hash_blocks[DEVICE_HASH_LENGTH];
off_t device_size;
const char* path;
uint32_t block_size;
int fd;
size_t block_count;
size_t block_limit;
public:
Block* AllocateBlock();
Block* GetBlock(uint32_t block_id);
Block* GetCachedBlock(uint32_t block_id);
};
#endif

87
iso9660/filesystem.cpp Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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
* ISO 9660 filesystem implementation.
*/
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <stdio.h> // DEBUG
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "util.h"
Filesystem::Filesystem(Device* device, const char* mount_path,
uint64_t pvd_offset)
{
// TODO: This should be replaced by the . entry within the root directory
// so the Rock Ridge extensions are available.
uint32_t pvd_block_id = pvd_offset / device->block_size;
this->pvd_block = device->GetBlock(pvd_block_id);
assert(pvd_block); // TODO: This can fail.
this->pvd = (struct iso9660_pvd*)
(pvd_block->block_data + pvd_offset % device->block_size);
this->root_ino = pvd_offset + offsetof(struct iso9660_pvd, root_dirent);
this->device = device;
this->mount_path = mount_path;
this->block_size = device->block_size;
this->mru_inode = NULL;
this->lru_inode = NULL;
for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ )
this->hash_inodes[i] = NULL;
}
Filesystem::~Filesystem()
{
while ( mru_inode )
delete mru_inode;
pvd_block->Unref();
}
Inode* Filesystem::GetInode(iso9660_ino_t inode_id)
{
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;
uint32_t block_id = inode_id / block_size;
uint32_t offset = inode_id % block_size;
Block* block = device->GetBlock(block_id);
if ( !block )
return (Inode*) NULL;
Inode* inode = new Inode(this, inode_id);
if ( !inode )
return block->Unref(), (Inode*) NULL;
inode->data_block = block;
uint8_t* buf = inode->data_block->block_data + offset;
inode->data = (struct iso9660_dirent*) buf;
inode->Prelink();
inode->Parse();
return inode;
}

52
iso9660/filesystem.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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
* ISO 9660 filesystem implementation.
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include "iso9660.h"
class Device;
class Inode;
static const size_t INODE_HASH_LENGTH = 1 << 16;
class Filesystem
{
public:
Filesystem(Device* device, const char* mount_path, uint64_t pvd_offset);
~Filesystem();
public:
Block* pvd_block;
struct iso9660_pvd* pvd;
Device* device;
const char* mount_path;
iso9660_ino_t root_ino;
uint32_t block_size;
Inode* mru_inode;
Inode* lru_inode;
Inode* hash_inodes[INODE_HASH_LENGTH];
public:
Inode* GetInode(iso9660_ino_t inode_id);
};
#endif

701
iso9660/fsmarshall.cpp Normal file
View File

@ -0,0 +1,701 @@
/*
* 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 "block.h"
#include "device.h"
#include "filesystem.h"
#include "fsmarshall.h"
#include "fuse.h"
#include "inode.h"
#include "iso9660fs.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;
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 ( (iso9660_ino_t) ino != ino )
return errno = EBADF, (Inode*) ino;
return fs->GetInode((iso9660_ino_t) ino);
}
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
{
(void) chl;
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
{
inode->RemoteRefer();
inode->Unref();
}
}
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
{
(void) chl;
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
{
inode->RemoteUnref();
inode->Unref();
}
}
void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
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*/)
{
RespondError(chl, EROFS);
}
void HandleChangeOwner(int chl, struct fsm_req_chown* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleUTimens(int chl, struct fsm_req_utimens* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleTruncate(int chl, struct fsm_req_truncate* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
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*/)
{
RespondError(chl, EROFS);
}
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, FsModeFromHostMode(msg->mode));
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
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; }
inode->Unref();
RespondError(chl, EROFS);
}
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;
uint8_t padding[sizeof(struct dirent) + 256];
};
memset(&kernel_entry, 0, sizeof(kernel_entry));
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( inode->ReadDirectory(&offset, &block, &block_id,
msg->rec_num ? NULL : name, &file_type,
&inode_id) )
{
if ( !(msg->rec_num--) )
{
size_t name_len = strlen(name);
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
kernel_entry.d_ino = inode_id;
kernel_entry.d_dev = 0;
kernel_entry.d_type = HostDTFromFsDT(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;
}
}
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)
{
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)
{
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)
{
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);
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)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondError(chl, EROFS);
}
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !ISO9660_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; }
size_t count = inode->Size();
uint8_t* buf = (uint8_t*) malloc(count);
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
ssize_t amount = inode->ReadLink(buf, count);
inode->Unref();
if ( amount < 0 ) { RespondError(chl, errno); return; }
RespondReadlink(chl, buf, amount);
free(buf);
}
void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs)
{
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->block_size;
stvfs.f_frsize = fs->block_size;
stvfs.f_blocks = fs->device->device_size / fs->block_size;
stvfs.f_bfree = 0;
stvfs.f_bavail = 0;
stvfs.f_files = 0;
stvfs.f_ffree = 0;
stvfs.f_favail = 0;
stvfs.f_ffree = 0;
stvfs.f_fsid = 0;
stvfs.f_flag = ST_RDONLY;
stvfs.f_namemax = 255;
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";
static const char index[] = "device-path\0filesystem-type\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, "iso9660", strlen("iso9660"));
// TODO: Some kind of unique id.
//else if ( !strcmp(name, "filesystem-uuid") )
// RespondTCGetBlob(chl, fs->sb->s_uuid, sizeof(fs->sb->s_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 )
{
fprintf(stderr, "extfs: 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] )
{
fprintf(stderr, "extfs: 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 inode.
struct stat root_inode_st;
Inode* root_inode = fs->GetInode(fs->root_ino);
if ( !root_inode )
err(1, "GetInode");
StatInode(root_inode, &root_inode_st);
root_inode->Unref();
// Create a filesystem server connected to the kernel that we'll listen on.
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_inode_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();
// Listen for filesystem messages.
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);
}
// 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();
}
close(serverfd);
delete fs;
delete dev;
return 0;
}
#endif

31
iso9660/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

572
iso9660/fuse.cpp Normal file
View File

@ -0,0 +1,572 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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 <ctype.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 "block.h"
#include "device.h"
#include "filesystem.h"
#include "fuse.h"
#include "inode.h"
#include "iso9660fs.h"
#include <stdio.h> // DEBUG
struct iso9660_fuse_ctx
{
Device* dev;
Filesystem* fs;
};
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
#define FUSE_FS (((struct iso9660_fuse_ctx*) (fuse_get_context()->private_data))->fs)
void* iso9660_fuse_init(struct fuse_conn_info* /*conn*/)
{
return fuse_get_context()->private_data;
}
void iso9660_fuse_destroy(void* fs_private)
{
struct iso9660_fuse_ctx* iso9660_fuse_ctx =
(struct iso9660_fuse_ctx*) fs_private;
while ( iso9660_fuse_ctx->fs->mru_inode )
{
Inode* inode = iso9660_fuse_ctx->fs->mru_inode;
if ( inode->remote_reference_count )
inode->RemoteUnref();
else if ( inode->reference_count )
inode->Unref();
}
delete iso9660_fuse_ctx->fs; iso9660_fuse_ctx->fs = NULL;
delete iso9660_fuse_ctx->dev; iso9660_fuse_ctx->dev = NULL;
}
Inode* iso9660_fuse_resolve_path(const char* path)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode(fs->root_ino);
if ( !inode )
return (Inode*) NULL;
while ( path[0] )
{
if ( *path == '/' )
{
if ( !ISO9660_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* iso9660_fuse_parent_dir(const char** path_ptr)
{
const char* path = *path_ptr;
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode(fs->root_ino);
if ( !inode )
return (Inode*) NULL;
while ( strchr(path, '/') )
{
if ( *path == '/' )
{
if ( !ISO9660_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 iso9660_fuse_getattr(const char* path, struct stat* st)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
StatInode(inode, st);
inode->Unref();
return 0;
}
int iso9660_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 iso9660_fuse_readlink(const char* path, char* buf, size_t bufsize)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
if ( !ISO9660_S_ISLNK(inode->Mode()) )
return inode->Unref(), -(errno = EINVAL);
if ( !bufsize )
return inode->Unref(), -(errno = EINVAL);
ssize_t amount = inode->ReadLink((uint8_t*) buf, bufsize);
if ( amount < 0 )
return inode->Unref(), -errno;
buf[(size_t) amount < bufsize ? (size_t) amount : bufsize - 1] = '\0';
inode->Unref();
return 0;
}
int iso9660_fuse_mknod(const char* path, mode_t mode, dev_t dev)
{
(void) path;
(void) mode;
(void) dev;
return -(errno = ENOSYS);
}
int iso9660_fuse_mkdir(const char* path, mode_t /*mode*/)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
inode->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_unlink(const char* path)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
bool success = inode->Unlink(path, false);
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_rmdir(const char* path)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
bool success = inode->RemoveDirectory(path);
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_symlink(const char* /*oldname*/, const char* newname)
{
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return -errno;
newdir->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_rename(const char* oldname, const char* newname)
{
Inode* olddir = iso9660_fuse_parent_dir(&oldname);
if ( !olddir )
return -errno;
Inode* newdir = iso9660_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 iso9660_fuse_link(const char* oldname, const char* newname)
{
Inode* inode = iso9660_fuse_resolve_path(oldname);
if ( !inode )
return -errno;
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return inode->Unref(), -errno;
bool success = inode->Link(newname, inode);
newdir->Unref();
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_chmod(const char* path, mode_t mode)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) mode;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_chown(const char* path, uid_t owner, gid_t group)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) owner;
(void) group;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_truncate(const char* path, off_t size)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) size;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_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;
(void) size;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_open(const char* path, struct fuse_file_info* fi)
{
int flags = fi->flags;
Inode* dir = iso9660_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 iso9660_fuse_access(const char* path, int mode)
{
Inode* dir = iso9660_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 iso9660_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi)
{
int flags = fi->flags | O_CREAT;
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
Inode* result = inode->Open(path, flags, FsModeFromHostMode(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 iso9660_fuse_opendir(const char* path, struct fuse_file_info* fi)
{
return iso9660_fuse_open(path, fi);
}
int iso9660_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 iso9660_fuse_write(const char* /*path*/, const char* /*buf*/,
size_t /*count*/, off_t /*offset*/,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return -(errno = EROFS);
}
int iso9660_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->device->device_size / fs->block_size;
stvfs->f_bfree = 0;
stvfs->f_bavail = 0;
stvfs->f_files = 0;
stvfs->f_ffree = 0;
stvfs->f_favail = 0;
stvfs->f_ffree = 0;
stvfs->f_fsid = 0;
stvfs->f_flag = ST_RDONLY;
stvfs->f_namemax = 255;
return 0;
}
int iso9660_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->Unref();
return 0;
}
int iso9660_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 iso9660_fuse_releasedir(const char* path, struct fuse_file_info* fi)
{
return iso9660_fuse_release(path, fi);
}
int iso9660_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->Unref();
return 0;
}
/*int iso9660_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi)
{
return iso9660_fuse_sync(path, data, fi);
}*/
/*int iso9660_fuse_setxattr(const char *, const char *, const char *, size_t, int)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_getxattr(const char *, const char *, char *, size_t)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_listxattr(const char *, char *, size_t)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_removexattr(const char *, const char *)
{
return -(errno = ENOSYS);
}*/
int iso9660_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((iso9660_ino_t) fi->fh);
if ( !inode )
return -errno;
if ( !S_ISDIR(inode->Mode()) )
return inode->Unref(), -(errno = ENOTDIR);
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( inode->ReadDirectory(&offset, &block, &block_id,
rec_num ? NULL : name, &file_type, &inode_id) )
{
if ( !rec_num || !rec_num-- )
{
if ( filler(buf, name, NULL, 0) )
{
block->Unref();
inode->Unref();
return 0;
}
}
}
int errnum = errno;
if ( block )
block->Unref();
inode->Unref();
if ( errnum )
return -errnum;
return 0;
}
/*int iso9660_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_utimens(const char* path, const struct timespec tv[2])
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) tv;
return inode->Unref(), -(errno = EROFS);
}
/*int iso9660_fuse_bmap(const char*, size_t blocksize, uint64_t* idx)
{
return -(errno = ENOSYS);
}*/
int iso9660_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 = iso9660_fuse_access;
operations.chmod = iso9660_fuse_chmod;
operations.chown = iso9660_fuse_chown;
operations.create = iso9660_fuse_create;
operations.destroy = iso9660_fuse_destroy;
operations.fgetattr = iso9660_fuse_fgetattr;
operations.flush = iso9660_fuse_flush;
operations.fsync = iso9660_fuse_fsync;
operations.ftruncate = iso9660_fuse_ftruncate;
operations.getattr = iso9660_fuse_getattr;
operations.init = iso9660_fuse_init;
operations.link = iso9660_fuse_link;
operations.mkdir = iso9660_fuse_mkdir;
operations.mknod = iso9660_fuse_mknod;
operations.opendir = iso9660_fuse_opendir;
operations.open = iso9660_fuse_open;
operations.readdir = iso9660_fuse_readdir;
operations.read = iso9660_fuse_read;
operations.readlink = iso9660_fuse_readlink;
operations.releasedir = iso9660_fuse_releasedir;
operations.release = iso9660_fuse_release;
operations.rename = iso9660_fuse_rename;
operations.rmdir = iso9660_fuse_rmdir;
operations.statfs = iso9660_fuse_statfs;
operations.symlink = iso9660_fuse_symlink;
operations.truncate = iso9660_fuse_truncate;
operations.unlink = iso9660_fuse_unlink;
operations.utimens = iso9660_fuse_utimens;
operations.write = iso9660_fuse_write;
operations.flag_nullpath_ok = 1;
operations.flag_nopath = 1;
char* argv_fuse[] =
{
(char*) argv0,
(char*) "-s",
(char*) (foreground ? "-f" : mount_path),
(char*) (foreground ? mount_path : NULL),
(char*) NULL,
};
int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1;
struct iso9660_fuse_ctx iso9660_fuse_ctx;
iso9660_fuse_ctx.fs = fs;
iso9660_fuse_ctx.dev = dev;
return fuse_main(argc_fuse, argv_fuse, &operations, &iso9660_fuse_ctx);
}
#endif

32
iso9660/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 iso9660_fuse_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev);
#endif

614
iso9660/inode.cpp Normal file
View File

@ -0,0 +1,614 @@
/*
* Copyright (c) 2013, 2014, 2015, 2018, 2022 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.cpp
* Filesystem inode.
*/
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <endian.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "iso9660fs.h"
#include "util.h"
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
#ifndef O_WRITE
#define O_WRITE (O_WRONLY | O_RDWR)
#endif
Inode::Inode(Filesystem* filesystem, iso9660_ino_t inode_id)
{
this->prev_inode = NULL;
this->next_inode = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->data_block = NULL;
this->data = NULL;
this->filesystem = filesystem;
this->reference_count = 1;
this->remote_reference_count = 0;
this->inode_id = inode_id;
this->parent_inode_id = 0;
}
Inode::~Inode()
{
if ( data_block )
data_block->Unref();
Unlink();
}
void Inode::Parse()
{
const uint8_t* block_data = (const uint8_t*) data;
uid = 0;
gid = 0;
time = 0;
uint8_t file_flags = block_data[25];
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
mode = 0555 | (is_directory ? ISO9660_S_IFDIR : ISO9660_S_IFREG);
uint32_t u32;
memcpy(&u32, block_data + 10, sizeof(u32));
size = le32toh(u32);
nlink = 1;
const uint8_t* time_bytes = block_data + 18;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = time_bytes[0];
tm.tm_mon = time_bytes[1] - 1;
tm.tm_mday = time_bytes[2];
tm.tm_hour = time_bytes[3];
tm.tm_min = time_bytes[4];
tm.tm_sec = time_bytes[5];
// TODO: Is this accurate?
time_t tz_offset = (-48 + time_bytes[6]) * 15 * 60;
// TODO: The timezone offset should've been mixed in with mktime, somehow.
time = mktime(&tm) + tz_offset;
uint8_t dirent_len = block_data[0];
uint8_t name_len = block_data[32];
size_t extended_off = 33 + name_len + !(name_len & 1);
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( len == 36 && field[0] == 'P' && field[1] == 'X' && field[3] == 1 )
{
uint32_t bits;
memcpy(&bits, field + 4, sizeof(bits));
mode = le32toh(bits) & 0xffff;
memcpy(&bits, field + 12, sizeof(bits));
nlink = le32toh(bits);
memcpy(&bits, field + 20, sizeof(bits));
uid = le32toh(bits);
memcpy(&bits, field + 28, sizeof(bits));
gid = le32toh(bits);
}
}
if ( ISO9660_S_ISLNK(mode) )
{
uint8_t buf[256];
size = ReadLink(buf, sizeof(buf));
}
}
uint32_t Inode::Mode()
{
return mode;
}
uint32_t Inode::UserId()
{
return uid;
}
uint32_t Inode::GroupId()
{
return gid;
}
uint64_t Inode::Size()
{
return size;
}
uint64_t Inode::Time()
{
return time;
}
Block* Inode::GetBlock(uint32_t offset)
{
uint32_t lba;
memcpy(&lba, (uint8_t*) data + 2, sizeof(lba));
lba = le32toh(lba);
uint32_t block_id = lba + offset;
return filesystem->device->GetBlock(block_id);
}
bool Inode::FindParentInode(uint64_t parent_lba, uint64_t parent_size)
{
if ( inode_id == filesystem->root_ino )
return parent_inode_id = inode_id, true;
Block* block = NULL;
uint32_t block_size = filesystem->block_size;
uint64_t parent_offset = parent_lba * block_size;
uint64_t block_id = 0;
uint64_t offset = 0;
while ( offset < parent_size )
{
uint64_t entry_block_id = (parent_offset + offset) / block_size;
uint64_t entry_block_offset = (parent_offset + offset) % block_size;
if ( block && block_id != entry_block_id )
{
block->Unref();
block = NULL;
}
if ( !block && !(block = filesystem->device->GetBlock(entry_block_id)) )
return false;
const uint8_t* block_data = block->block_data + entry_block_offset;
uint8_t dirent_len = block_data[0];
if ( !dirent_len )
{
offset = (entry_block_id + 1) * block_size;
continue;
}
uint8_t name_len = block_data[32];
const uint8_t* name_data = block_data + 33;
if ( name_len == 0 || !name_data[0] )
{
parent_inode_id = parent_offset + offset;
return true;
}
// TODO: Can dirent_len be misaligned?
uint64_t reclen = dirent_len + (dirent_len & 1);
if ( !reclen )
return errno = EINVAL, false;
offset += reclen;
}
return errno = EINVAL, false;
}
bool Inode::ReadDirectory(uint64_t* offset_inout,
Block** block_inout,
uint64_t* block_id_inout,
char* name,
uint8_t* file_type_out,
iso9660_ino_t* inode_id_out)
{
uint64_t offset = *offset_inout;
next_block:
uint64_t filesize = Size();
if ( filesize <= offset )
return errno = 0, false;
uint64_t entry_block_id = offset / filesystem->block_size;
uint64_t entry_block_offset = offset % filesystem->block_size;
if ( *block_inout && *block_id_inout != entry_block_id )
{
(*block_inout)->Unref();
(*block_inout) = NULL;
}
if ( !*block_inout &&
!(*block_inout = GetBlock(*block_id_inout = entry_block_id)) )
return false;
const uint8_t* block_data = (*block_inout)->block_data + entry_block_offset;
uint8_t dirent_len = block_data[0];
if ( !dirent_len )
{
offset = (entry_block_id + 1) * filesystem->block_size;
goto next_block;
}
if ( name )
{
uint8_t name_len = block_data[32];
const uint8_t* name_data = block_data + 33;
size_t extended_off = 33 + name_len + !(name_len & 1);
iso9660_ino_t entry_inode_id =
((*block_inout)->block_id * filesystem->block_size) +
entry_block_offset;
// TODO: The root directory inode should be that of its . entry.
if ( name_len == 0 || !name_data[0] )
{
name[0] = '.';
name[1] = '\0';
name_len = 1;
entry_inode_id = inode_id;
}
else if ( name_len == 1 && name_data[0] == 1 )
{
name[0] = '.';
name[1] = '.';
name[2] = '\0';
name_len = 2;
if ( !parent_inode_id )
{
uint32_t parent_lba;
memcpy(&parent_lba, block_data + 2, sizeof(parent_lba));
parent_lba = le32toh(parent_lba);
uint32_t parent_size;
memcpy(&parent_size, block_data + 10, sizeof(parent_size));
parent_size = le32toh(parent_size);
if ( !FindParentInode(parent_lba, parent_size) )
return false;
}
entry_inode_id = parent_inode_id;
}
else
{
for ( size_t i = 0; i < name_len; i++ )
{
if ( name_data[i] == ';' )
{
name_len = i;
break;
}
name[i] = tolower(name_data[i]);
}
name[name_len] = '\0';
}
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( 5 <= len && field[0] == 'N' && field[1] == 'M' &&
field[3] == 1 )
{
uint8_t nm_flags = field[4];
if ( nm_flags & (1 << 0) ) // TODO: Continued names.
break;
name_len = len - 5;
memcpy(name, field + 5, name_len);
name[name_len] = '\0';
}
// TODO: Other extensions.
}
uint8_t file_flags = block_data[25];
// TODO: Rock Ridge.
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
uint8_t file_type = is_directory ? ISO9660_FT_DIR : ISO9660_FT_REG_FILE;
*file_type_out = file_type;
*inode_id_out = entry_inode_id;
}
// TODO: Can dirent_len be misaligned?
uint64_t reclen = dirent_len + (dirent_len & 1);
if ( !reclen )
return errno = EINVAL, false;
offset += reclen;
*offset_inout = offset;
return true;
}
Inode* Inode::Open(const char* elem, int flags, mode_t mode)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, (Inode*) NULL;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
block->Unref();
if ( (flags & O_CREAT) && (flags & O_EXCL) )
return errno = EEXIST, (Inode*) NULL;
if ( (flags & O_DIRECTORY) &&
file_type != ISO9660_FT_UNKNOWN &&
file_type != ISO9660_FT_DIR &&
file_type != ISO9660_FT_SYMLINK )
return errno = ENOTDIR, (Inode*) NULL;
Inode* inode = filesystem->GetInode(inode_id);
if ( !inode )
return (Inode*) NULL;
if ( flags & O_DIRECTORY &&
!ISO9660_S_ISDIR(inode->Mode()) &&
!ISO9660_S_ISLNK(inode->Mode()) )
{
inode->Unref();
return errno = ENOTDIR, (Inode*) NULL;
}
if ( flags & O_WRITE )
{
inode->Unref();
return errno = EROFS, (Inode*) NULL;
}
return inode;
}
}
if ( block )
block->Unref();
if ( flags & O_CREAT )
{
(void) mode;
return errno = EROFS, (Inode*) NULL;
}
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Link(const char* elem, Inode* dest)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, false;
if ( ISO9660_S_ISDIR(dest->Mode()) )
return errno = EISDIR, false;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, false;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
block->Unref();
return errno = EEXIST, false;
}
}
if ( block )
block->Unref();
return errno = EROFS, false;
}
Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, (Inode*) NULL;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
(void) directories;
(void) force;
block->Unref();
return errno = EROFS, (Inode*) NULL;
}
}
if ( block )
block->Unref();
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Unlink(const char* elem, bool directories, bool force)
{
Inode* result = UnlinkKeep(elem, directories, force);
if ( !result )
return false;
result->Unref();
return true;
}
ssize_t Inode::ReadLink(uint8_t* buf, size_t bufsize)
{
size_t result = 0;
const uint8_t* block_data = (const uint8_t*) data;
uint8_t dirent_len = block_data[0];
uint8_t name_len = block_data[32];
size_t extended_off = 33 + name_len + !(name_len & 1);
bool continued = true;
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( 5 <= len && field[0] == 'S' && field[1] == 'L' && field[3] == 1 )
{
for ( size_t n = 5; n < len && 2 <= len - n; )
{
uint8_t comp_flags = field[n + 0];
uint8_t comp_len = field[n + 1];
if ( len - (n + 2) < comp_len )
break;
const char* data = (const char*) (field + n + 2);
size_t datalen = comp_len;
// TODO: How is a trailing slash encoded?
if ( !continued || (comp_flags & (1 << 3) /* root */) )
{
buf[result++] = '/';
if ( result == bufsize )
return (ssize_t) result;
}
if ( comp_flags & (1 << 1) )
{
data = ".";
datalen = 1;
}
else if ( comp_flags & (1 << 2) )
{
data = "..";
datalen = 2;
}
size_t possible = bufsize - result;
size_t count = datalen < possible ? datalen : possible;
memcpy(buf + result, data, count);
result += count;
if ( result == bufsize )
return (ssize_t) result;
continued = comp_flags & (1 << 0);
n += 2 + comp_len;
}
}
}
return (ssize_t) result;
}
ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !ISO9660_S_ISREG(Mode()) && !ISO9660_S_ISLNK(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
if ( SSIZE_MAX < s_count )
s_count = SSIZE_MAX;
uint64_t sofar = 0;
uint64_t count = (uint64_t) s_count;
uint64_t offset = (uint64_t) o_offset;
uint64_t file_size = Size();
if ( file_size <= offset )
return 0;
if ( file_size - offset < count )
count = file_size - offset;
while ( sofar < count )
{
uint64_t block_id = offset / filesystem->block_size;
uint32_t block_offset = offset % filesystem->block_size;
uint32_t block_left = filesystem->block_size - block_offset;
Block* block = GetBlock(block_id);
if ( !block )
return sofar ? sofar : -1;
size_t amount = count - sofar < block_left ? count - sofar : block_left;
memcpy(buf + sofar, block->block_data + block_offset, amount);
sofar += amount;
offset += amount;
block->Unref();
}
return (ssize_t) sofar;
}
bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname)
{
if ( !strcmp(oldname, ".") || !strcmp(oldname, "..") ||
!strcmp(newname, ".") || !strcmp(newname, "..") )
return errno = EPERM, false;
Inode* src_inode = olddir->Open(oldname, O_RDONLY, 0);
if ( !src_inode )
return false;
if ( Inode* dst_inode = Open(newname, O_RDONLY, 0) )
{
bool same_inode = src_inode->inode_id == dst_inode->inode_id;
dst_inode->Unref();
if ( same_inode )
return src_inode->Unref(), true;
}
src_inode->Unref();
return errno = EROFS, false;
}
bool Inode::RemoveDirectory(const char* path)
{
return UnlinkKeep(path, true);
}
void Inode::Refer()
{
reference_count++;
}
void Inode::Unref()
{
assert(0 < reference_count);
reference_count--;
if ( !reference_count && !remote_reference_count )
delete this;
}
void Inode::RemoteRefer()
{
remote_reference_count++;
}
void Inode::RemoteUnref()
{
assert(0 < remote_reference_count);
remote_reference_count--;
if ( !reference_count && !remote_reference_count )
delete this;
}
void Inode::Use()
{
data_block->Use();
Unlink();
Prelink();
}
void Inode::Unlink()
{
(prev_inode ? prev_inode->next_inode : filesystem->mru_inode) = next_inode;
(next_inode ? next_inode->prev_inode : filesystem->lru_inode) = prev_inode;
size_t bin = inode_id % INODE_HASH_LENGTH;
(prev_hashed ? prev_hashed->next_hashed : filesystem->hash_inodes[bin]) = next_hashed;
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
}
void Inode::Prelink()
{
prev_inode = NULL;
next_inode = filesystem->mru_inode;
if ( filesystem->mru_inode )
filesystem->mru_inode->prev_inode = this;
filesystem->mru_inode = this;
if ( !filesystem->lru_inode )
filesystem->lru_inode = this;
size_t bin = inode_id % INODE_HASH_LENGTH;
prev_hashed = NULL;
next_hashed = filesystem->hash_inodes[bin];
filesystem->hash_inodes[bin] = this;
if ( next_hashed )
next_hashed->prev_hashed = this;
}

83
iso9660/inode.h Normal file
View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 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
#include "iso9660.h"
class Block;
class Filesystem;
class Inode
{
public:
Inode(Filesystem* filesystem, iso9660_ino_t inode_id);
~Inode();
public:
Inode* prev_inode;
Inode* next_inode;
Inode* prev_hashed;
Inode* next_hashed;
Block* data_block;
struct iso9660_dirent* data;
Filesystem* filesystem;
size_t reference_count;
size_t remote_reference_count;
iso9660_ino_t inode_id;
iso9660_ino_t parent_inode_id;
uint32_t uid;
uint32_t gid;
uint64_t size;
uint64_t time;
uint32_t mode;
uint32_t nlink;
public:
void Parse();
uint32_t Mode();
uint32_t UserId();
uint32_t GroupId();
uint64_t Size();
uint64_t Time();
Block* GetBlock(uint32_t offset);
bool FindParentInode(uint64_t parent_lba, uint64_t parent_size);
bool ReadDirectory(uint64_t* offset_inout, Block** block_inout,
uint64_t* block_id_inout, char* name,
uint8_t* file_type_out, iso9660_ino_t* inode_id_out);
Inode* Open(const char* elem, int flags, mode_t mode);
bool Link(const char* elem, Inode* dest);
bool Unlink(const char* elem, bool directories, bool force=false);
Inode* UnlinkKeep(const char* elem, bool directories, bool force=false);
ssize_t ReadLink(uint8_t* buf, size_t bufsize);
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
bool Rename(Inode* olddir, const char* oldname, const char* newname);
bool RemoveDirectory(const char* path);
void Refer();
void Unref();
void RemoteRefer();
void RemoteUnref();
void Use();
void Unlink();
void Prelink();
};
#endif

150
iso9660/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

128
iso9660/iso9660.h Normal file
View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2022 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.
*
* iso9660.h
* Data structures for the ISO 9660 filesystem.
*/
#ifndef ISO9660_H
#define ISO9660_H
#include <stdint.h>
const uint8_t TYPE_BOOT_RECORD = 0x00;
const uint8_t TYPE_PRIMARY_VOLUME_DESCRIPTOR = 0x01;
const uint8_t TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR = 0xFF;
struct iso9660_pvd /* primary volume descriptor */
{
uint8_t type;
char standard_identifier[5];
uint8_t version;
uint8_t unused1;
char system_identifier[32];
char volume_identifier[32];
uint8_t unused2[8];
uint32_t volume_space_size_le;
uint32_t volume_space_size_be;
uint8_t unused3[32];
uint16_t volume_set_size_le;
uint16_t volume_set_size_be;
uint16_t volume_sequence_number_le;
uint16_t volume_sequence_number_be;
uint16_t logical_block_size_le;
uint16_t logical_block_size_be;
uint32_t path_table_size_le;
uint32_t path_table_size_be;
uint32_t path_table_lba_le;
uint32_t path_table_opt_lba_le;
uint32_t path_table_lba_be;
uint32_t path_table_opt_lba_be;
uint8_t root_dirent[34];
char volume_set_identifier[128];
char publisher_identifier[128];
char data_preparer_identifier[128];
char application_identifier[128];
char copyright_file_identifier[37];
char abstract_file_identifier[37];
char bibliographic_file_identifier[37];
char creation_datetime[17];
char modification_datetime[17];
char expiration_datetime[17];
char effective_datetime[17];
uint8_t file_structure_version;
uint8_t unused4;
uint8_t application_use[512];
uint8_t reserved[653];
};
static_assert(sizeof(struct iso9660_pvd) == 2048,
"sizeof(struct iso9660_pvd) == 2048");
typedef uint64_t iso9660_ino_t;
struct iso9660_dirent
{
uint8_t unused;
};
#define ISO9660_DIRENT_FLAG_DIR (1 << 1)
#define ISO9660_S_IFMT (0170000)
#define ISO9660_S_IFIFO (0010000)
#define ISO9660_S_IFCHR (0020000)
#define ISO9660_S_IFDIR (0040000)
#define ISO9660_S_IFBLK (0060000)
#define ISO9660_S_IFREG (0100000)
#define ISO9660_S_IFLNK (0120000)
#define ISO9660_S_IFSOCK (0140000)
#define ISO9660_S_ISSOCK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFSOCK)
#define ISO9660_S_ISLNK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFLNK)
#define ISO9660_S_ISREG(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFREG)
#define ISO9660_S_ISBLK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFBLK)
#define ISO9660_S_ISDIR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFDIR)
#define ISO9660_S_ISCHR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFCHR)
#define ISO9660_S_ISFIFO(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFIFO)
static const uint8_t ISO9660_FT_UNKNOWN = 0;
static const uint8_t ISO9660_FT_REG_FILE = 1;
static const uint8_t ISO9660_FT_DIR = 2;
static const uint8_t ISO9660_FT_CHRDEV = 3;
static const uint8_t ISO9660_FT_BLKDEV = 4;
static const uint8_t ISO9660_FT_FIFO = 5;
static const uint8_t ISO9660_FT_SOCK = 6;
static const uint8_t ISO9660_FT_SYMLINK = 7;
static inline uint8_t ISO9660_FT_OF_MODE(mode_t mode)
{
if ( ISO9660_S_ISREG(mode) )
return ISO9660_FT_REG_FILE;
if ( ISO9660_S_ISDIR(mode) )
return ISO9660_FT_DIR;
if ( ISO9660_S_ISCHR(mode) )
return ISO9660_FT_CHRDEV;
if ( ISO9660_S_ISBLK(mode) )
return ISO9660_FT_BLKDEV;
if ( ISO9660_S_ISFIFO(mode) )
return ISO9660_FT_FIFO;
if ( ISO9660_S_ISSOCK(mode) )
return ISO9660_FT_SOCK;
if ( ISO9660_S_ISLNK(mode) )
return ISO9660_FT_SYMLINK;
return ISO9660_FT_UNKNOWN;
}
#endif

278
iso9660/iso9660fs.cpp Normal file
View File

@ -0,0 +1,278 @@
/*
* 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.
*
* iso9660fs.cpp
* Implementation of the ISO 9660 filesystem.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <dirent.h>
#include <endian.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 <unistd.h>
#if defined(__sortix__)
#include "fsmarshall.h"
#else
#include "fuse.h"
#endif
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "ioleast.h"
#include "iso9660.h"
#include "iso9660fs.h"
#include "util.h"
uid_t request_uid;
uid_t request_gid;
mode_t HostModeFromFsMode(uint32_t mode)
{
mode_t hostmode = mode & 07777;
if ( ISO9660_S_ISSOCK(mode) ) hostmode |= S_IFSOCK;
if ( ISO9660_S_ISLNK(mode) ) hostmode |= S_IFLNK;
if ( ISO9660_S_ISREG(mode) ) hostmode |= S_IFREG;
if ( ISO9660_S_ISBLK(mode) ) hostmode |= S_IFBLK;
if ( ISO9660_S_ISDIR(mode) ) hostmode |= S_IFDIR;
if ( ISO9660_S_ISCHR(mode) ) hostmode |= S_IFCHR;
if ( ISO9660_S_ISFIFO(mode) ) hostmode |= S_IFIFO;
return hostmode;
}
uint32_t FsModeFromHostMode(mode_t hostmode)
{
uint32_t mode = hostmode & 07777;
if ( S_ISSOCK(hostmode) ) mode |= ISO9660_S_IFSOCK;
if ( S_ISLNK(hostmode) ) mode |= ISO9660_S_IFLNK;
if ( S_ISREG(hostmode) ) mode |= ISO9660_S_IFREG;
if ( S_ISBLK(hostmode) ) mode |= ISO9660_S_IFBLK;
if ( S_ISDIR(hostmode) ) mode |= ISO9660_S_IFDIR;
if ( S_ISCHR(hostmode) ) mode |= ISO9660_S_IFCHR;
if ( S_ISFIFO(hostmode) ) mode |= ISO9660_S_IFIFO;
return mode;
}
uint8_t HostDTFromFsDT(uint8_t fsdt)
{
switch ( fsdt )
{
case ISO9660_FT_UNKNOWN: return DT_UNKNOWN;
case ISO9660_FT_REG_FILE: return DT_REG;
case ISO9660_FT_DIR: return DT_DIR;
case ISO9660_FT_CHRDEV: return DT_CHR;
case ISO9660_FT_BLKDEV: return DT_BLK;
case ISO9660_FT_FIFO: return DT_FIFO;
case ISO9660_FT_SOCK: return DT_SOCK;
case ISO9660_FT_SYMLINK: return DT_LNK;
}
return DT_UNKNOWN;
}
void StatInode(Inode* inode, struct stat* st)
{
memset(st, 0, sizeof(*st));
st->st_ino = inode->inode_id;
st->st_mode = HostModeFromFsMode(inode->Mode());
st->st_nlink = inode->nlink;
st->st_uid = inode->uid;
st->st_gid = inode->gid;
st->st_size = inode->size;
// TODO: Rock Ridge.
st->st_atim.tv_sec = inode->Time();
st->st_atim.tv_nsec = 0;
// TODO: Rock Ridge.
st->st_ctim.tv_sec = inode->Time();
st->st_ctim.tv_nsec = 0;
// TODO: Rock Ridge.
st->st_mtim.tv_sec = inode->Time();
st->st_mtim.tv_nsec = 0;
st->st_blksize = inode->filesystem->block_size;
st->st_blocks = divup(st->st_size, (off_t) 512);
}
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;
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': 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") )
{
}
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);
}
}
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, O_RDONLY);
if ( fd < 0 )
err(1, "%s", device_path);
// Search for the Primary Volume Descriptor.
off_t block_size = 2048;
struct iso9660_pvd pvd;
off_t pvd_offset;
for ( uint32_t i = 16; true; i++ )
{
pvd_offset = block_size * i;
if ( preadall(fd, &pvd, sizeof(pvd), pvd_offset) != sizeof(pvd) )
{
if ( errno == EEOF )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
else
err(1, "read: %s", device_path);
}
if ( memcmp(pvd.standard_identifier, "CD001", 5) != 0 )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
if ( pvd.type == TYPE_PRIMARY_VOLUME_DESCRIPTOR )
break;
if ( pvd.type == TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
}
if ( pvd.version != 1 || pvd.file_structure_version != 1 )
errx(1, "Unsupported ISO 9660 filesystem version: %s", device_path);
// TODO: Test if appropriate power of two and allow.
if ( le32toh(pvd.logical_block_size_le) != block_size )
errx(1, "Unsupported ISO 9660 block size: %s", device_path);
block_size = le32toh(pvd.logical_block_size_le);
Device* dev = new Device(fd, device_path, block_size);
if ( !dev )
err(1, "malloc");
Filesystem* fs = new Filesystem(dev, pretend_mount_path, pvd_offset);
if ( !fs )
err(1, "%s", device_path);
// Change the root inode to be its own . entry instead of the pvd record, so
// parent directories correctly .. to it, and so Rock Ridge extensions work.
Inode* root = fs->GetInode(fs->root_ino);
if ( !root )
err(1, "%s: GetInode", device_path);
fs->root_ino = 0;
uint32_t root_lba;
uint32_t root_size;
memcpy(&root_lba, pvd.root_dirent + 2, sizeof(root_lba));
memcpy(&root_size, pvd.root_dirent + 10, sizeof(root_size));
root->FindParentInode(root_lba, root_size);
fs->root_ino = root->parent_inode_id;
root->Unref();
if ( !mount_path )
return 0;
#if defined(__sortix__)
return fsmarshall_main(argv0, mount_path, foreground, fs, dev);
#else
return iso9660_fuse_main(argv0, mount_path, foreground, fs, dev);
#endif
}

33
iso9660/iso9660fs.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 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.
*
* iso9660fs.h
* Implementation of the ISO 9660 filesystem.
*/
#ifndef ISO9660FS_H
#define ISO9660FS_H
extern uid_t request_uid;
extern gid_t request_gid;
class Inode;
mode_t HostModeFromFsMode(uint32_t fsmode);
uint32_t FsModeFromHostMode(mode_t hostmode);
uint8_t HostDTFromFsDT(uint8_t fsdt);
void StatInode(Inode* inode, struct stat* st);
#endif

49
iso9660/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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2022 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
@ -29,6 +29,7 @@
namespace Sortix {
size_t aux_allocated = 0;
size_t aux_leaked = 0;
size_t heap_allocated = 0;
kthread_mutex_t alloc_lock = KTHREAD_MUTEX_INITIALIZER;
@ -69,6 +70,8 @@ void FreeKernelAddress(addralloc_t* alloc)
addr_t aux_reached = kmem_from + kmem_size - aux_allocated;
if ( alloc->from == aux_reached )
aux_allocated -= alloc->size;
else
aux_leaked += alloc->size;
alloc->from = 0;
alloc->size = 0;
@ -98,6 +101,17 @@ void ShrinkHeap(size_t decrease)
heap_allocated -= decrease;
}
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked,
size_t* total)
{
ScopedLock lock(&alloc_lock);
addr_t kmem_from;
Memory::GetKernelVirtualArea(&kmem_from, total);
*heap = heap_allocated;
*aux = aux_allocated - aux_leaked;
*leaked = aux_leaked;
}
// No need for locks in these three functions, since only the heap calls these
// and it already uses an internal lock, and heap_allocated will not change
// unless the heap calls ExpandHeap.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 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
@ -17,6 +17,8 @@
* Handles communication to COM serial ports.
*/
#include <sys/ioctl.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
@ -38,6 +40,7 @@
#include <sortix/kernel/thread.h>
#include "com.h"
#include "tty.h"
extern "C" unsigned char nullpage[4096];
@ -136,17 +139,30 @@ static inline bool CanWriteByte(uint16_t port)
return inport8(port + LSR) & LSR_THRE;
}
class DevCOMPort : public AbstractInode
class DevCOMPort : public TTY
{
public:
DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, uint16_t port);
DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, uint16_t port,
const char* name);
virtual ~DevCOMPort();
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
virtual int sync(ioctx_t* ctx);
virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count);
virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count);
virtual void tty_output(const unsigned char* buffer, size_t length);
public:
bool Initialize(int interrupt);
private:
static void InterruptHandler(struct interrupt_context*, void*);
static void InterruptWorkHandler(void* context);
void OnInterrupt();
void InterruptWork();
private:
kthread_mutex_t port_lock;
struct interrupt_handler irq_registration;
struct interrupt_work interrupt_work;
struct winsize ws;
uint16_t port;
uint8_t pending_input_byte;
bool has_pending_input_byte;
@ -154,24 +170,92 @@ private:
};
DevCOMPort::DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode,
uint16_t port)
uint16_t port, const char* name) : TTY(dev, ino, mode,
owner, group, name)
{
inode_type = INODE_TYPE_STREAM;
this->port = port;
this->port_lock = KTHREAD_MUTEX_INITIALIZER;
this->stat_uid = owner;
this->stat_gid = group;
this->type = S_IFCHR;
this->stat_mode = (mode & S_SETABLE) | this->type;
this->dev = dev;
this->ino = (ino_t) this;
this->has_pending_input_byte = false;
interrupt_work.handler = InterruptWorkHandler;
interrupt_work.context = this;
}
DevCOMPort::~DevCOMPort()
{
}
bool DevCOMPort::Initialize(int interrupt)
{
uint8_t interrupts = 1;
// TODO: This was 9600.
tio.c_ispeed = B19200;
tio.c_ospeed = B19200;
uint16_t divisor = 115200 / tio.c_ispeed;
outport8(port + FCR, 0);
outport8(port + LCR, LCR_DLAB);
outport8(port + DLL, divisor & 0xFF);
outport8(port + DLM, divisor >> 8);
outport8(port + LCR, LCR_WLEN8); // 8n1
outport8(port + MCR, 0x1 /* DTR */ | 0x2 /* RTS */);
outport8(port + IER, interrupts);
irq_registration.handler = DevCOMPort::InterruptHandler;
irq_registration.context = this;
Interrupt::RegisterHandler(interrupt, &irq_registration);
return true;
}
void DevCOMPort::InterruptHandler(struct interrupt_context*, void* user)
{
((DevCOMPort*) user)->OnInterrupt();
}
void DevCOMPort::OnInterrupt()
{
if ( !IsLineReady(port) )
return;
Interrupt::ScheduleWork(&interrupt_work);
}
void DevCOMPort::InterruptWorkHandler(void* context)
{
((DevCOMPort*) context)->InterruptWork();
}
void DevCOMPort::InterruptWork()
{
ScopedLock lock1(&termlock);
ScopedLock lock2(&port_lock);
while ( IsLineReady(port) )
{
unsigned char byte = inport8(port + RXR);
if ( tio.c_cflag & CREAD )
ProcessByte(byte);
}
}
int DevCOMPort::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
{
ScopedLock lock(&termlock);
if ( hungup )
return errno = EIO, -1;
if ( cmd == TIOCGWINSZ )
{
struct winsize* user_ws = (struct winsize*) arg;
if ( !ctx->copy_to_dest(user_ws, &ws, sizeof(ws)) )
return -1;
return 0;
}
else if ( cmd == TIOCSWINSZ )
{
const struct winsize* user_ws = (const struct winsize*) arg;
if ( !ctx->copy_from_src(&ws, user_ws, sizeof(ws)) )
return -1;
return 0;
}
lock.Reset();
return TTY::ioctl(ctx, cmd, arg);
}
int DevCOMPort::sync(ioctx_t* /*ctx*/)
{
ScopedLock lock(&port_lock);
@ -179,57 +263,9 @@ int DevCOMPort::sync(ioctx_t* /*ctx*/)
return 0;
}
ssize_t DevCOMPort::read(ioctx_t* ctx, uint8_t* dest, size_t count)
void DevCOMPort::tty_output(const unsigned char* buffer, size_t length)
{
ScopedLock lock(&port_lock);
for ( size_t i = 0; i < count; i++ )
{
unsigned long attempt = 0;
while ( !has_pending_input_byte && !IsLineReady(port) )
{
attempt++;
if ( attempt <= 10 )
continue;
if ( attempt <= 15 && !(ctx->dflags & O_NONBLOCK) )
{
kthread_mutex_unlock(&port_lock);
kthread_yield();
kthread_mutex_lock(&port_lock);
continue;
}
if ( i )
return (ssize_t) i;
if ( ctx->dflags & O_NONBLOCK )
return errno = EWOULDBLOCK, -1;
if ( Signal::IsPending() )
return errno = EINTR, -1;
kthread_mutex_unlock(&port_lock);
kthread_yield();
kthread_mutex_lock(&port_lock);
}
uint8_t value = has_pending_input_byte ?
pending_input_byte :
inport8(port + RXR);
if ( !ctx->copy_to_dest(dest + i, &value, sizeof(value)) )
{
has_pending_input_byte = true;
pending_input_byte = value;
return i ? (ssize_t) i : -1;
}
has_pending_input_byte = false;
}
return (ssize_t) count;
}
ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count)
{
ScopedLock lock(&port_lock);
for ( size_t i = 0; i < count; i++ )
for ( size_t i = 0; i < length; i++ )
{
unsigned long attempt = 0;
while ( !CanWriteByte(port) )
@ -237,7 +273,7 @@ ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count)
attempt++;
if ( attempt <= 10 )
continue;
if ( attempt <= 15 && !(ctx->dflags & O_NONBLOCK) )
if ( attempt <= 15 )
{
kthread_mutex_unlock(&port_lock);
kthread_yield();
@ -245,20 +281,16 @@ ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count)
continue;
}
if ( i )
return (ssize_t) i;
if ( ctx->dflags & O_NONBLOCK )
return errno = EWOULDBLOCK, -1;
return;
// TODO: This is problematic.
if ( Signal::IsPending() )
return errno = EINTR, -1;
{
errno = EINTR;
return;
}
}
uint8_t value;
if ( !ctx->copy_from_src(&value, src + i, sizeof(value)) )
return i ? (ssize_t) i : -1;
outport8(port + TXR, value);
outport8(port + TXR, buffer[i]);
}
return (ssize_t) count;
}
static Ref<DevCOMPort> com_devices[1 + NUM_COM_PORTS];
@ -282,21 +314,6 @@ void Init(const char* devpath, Ref<Descriptor> slashdev)
ioctx_t ctx; SetupKernelIOCtx(&ctx);
for ( size_t i = 1; i <= NUM_COM_PORTS; i++ )
{
uint16_t port = com_ports[i];
if ( !port )
continue;
uint8_t interrupts = 0;
outport8(port + FCR, 0);
outport8(port + LCR, 0x80);
outport8(port + DLL, 0xC);
outport8(port + DLM, 0x0);
outport8(port + LCR, 0x3); // 8n1
outport8(port + MCR, 0x3); // DTR + RTS
outport8(port + IER, interrupts);
}
for ( size_t i = 1; i <= NUM_COM_PORTS; i++ )
{
if ( !com_ports[i] )
@ -304,13 +321,17 @@ void Init(const char* devpath, Ref<Descriptor> slashdev)
com_devices[i] = Ref<DevCOMPort>();
continue;
}
com_devices[i] = Ref<DevCOMPort>(new DevCOMPort(slashdev->dev, 0, 0, 0660, com_ports[i]));
if ( !com_devices[i] )
char ttyname[TTY_NAME_MAX+1];
snprintf(ttyname, sizeof(ttyname), "com%zu", i);
Ref<DevCOMPort> com(
new DevCOMPort(slashdev->dev, 0, 0, 0660, com_ports[i], ttyname));
if ( !com )
PanicF("Unable to allocate device for COM port %zu", i);
char name[3 + sizeof(size_t) * 3];
snprintf(name, sizeof(name), "com%zu", i);
if ( LinkInodeInDir(&ctx, slashdev, name, com_devices[i]) != 0 )
PanicF("Unable to link %s/%s to COM port driver.", devpath, name);
com_devices[i] = com;
int interrupt = i == 1 || i == 3 ? Interrupt::IRQ4 : Interrupt::IRQ3;
com->Initialize(interrupt);
if ( LinkInodeInDir(&ctx, slashdev, ttyname, com) != 0 )
PanicF("Unable to link %s/%s to COM port driver.", devpath, ttyname);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2016, 2022 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
@ -49,11 +49,17 @@ static unsigned long AllocateDiskNumber()
return InterlockedIncrement(&next_disk_number).o;
}
static void sleep_400_nanoseconds()
static void sleep_400_nanoseconds(uint16_t port_base)
{
// TODO: The clock granularity of 10 ms slows down the early boot.
#if 0
struct timespec delay = timespec_make(0, 400);
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
clock->SleepDelay(delay);
#else
for ( int i = 0; i < 14; i++ )
inport8(port_base + REG_STATUS);
#endif
}
Channel::Channel()
@ -105,7 +111,7 @@ void Channel::SelectDrive(unsigned int drive_index) // hw_lock locked
outport8(port_base + REG_DRIVE_SELECT, value);
//outport8(port_control, value); // TODO: Or is it port_control we use?
sleep_400_nanoseconds();
sleep_400_nanoseconds(port_base);
// TODO: Do we need to wait for non-busy now? Can this operation fail?
@ -229,7 +235,7 @@ bool Channel::Initialize(Ref<Descriptor> dev, const char* devpath)
busmaster_base = busmasterbar.addr() + 8 * channel_index;
current_drive = (unsigned int) -1; // We don't know.
current_drive = (inport8(port_base + REG_DRIVE_SELECT) >> 4) & 1;
for ( unsigned int i = 0; i < 2; i++ )
{

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2016, 2018, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2016, 2018, 2021, 2022 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
@ -56,11 +56,17 @@ static void copy_ata_string(char* dest, const char* src, size_t length)
dest[length] = '\0';
}
static void sleep_400_nanoseconds()
static void sleep_400_nanoseconds(uint16_t port_base)
{
// TODO: The clock granularity of 10 ms slows down the early boot.
#if 0
struct timespec delay = timespec_make(0, 400);
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
clock->SleepDelay(delay);
#else
for ( int i = 0; i < 14; i++ )
inport8(port_base + REG_STATUS);
#endif
}
Port::Port(Channel* channel, unsigned int port_index)
@ -176,7 +182,7 @@ retry_identify_packet:
outport8(channel->port_base + REG_COMMAND,
is_packet_interface ? CMD_IDENTIFY_PACKET : CMD_IDENTIFY);
sleep_400_nanoseconds();
sleep_400_nanoseconds(channel->port_base);
// TODO: The status polling logic should be double-checked against some
// formal specification telling how this should properly be done.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2022 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
@ -39,6 +39,7 @@ void ShrinkHeap(size_t decrease);
addr_t GetHeapLower();
addr_t GetHeapUpper();
size_t GetHeapSize();
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked, size_t* total);
} // namespace Sortix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2022 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
@ -36,4 +36,35 @@ typedef uintptr_t addr_t;
#define CPU X64
#endif
#ifdef __TRACE_ALLOCATION_SITES
struct kernel_allocation_site
{
struct __allocation_site allocation_site;
struct kernel_allocation_site* next;
bool registered;
};
extern struct kernel_allocation_site* first_kernel_allocation_site;
#undef ALLOCATION_SITE
#define ALLOCATION_SITE \
({ \
static struct kernel_allocation_site site = \
{ { __FILE__, __LINE__, __func__, 0, 0 }, NULL, false }; \
if ( !site.registered ) \
{ \
site.registered = true; \
site.next = first_kernel_allocation_site; \
first_kernel_allocation_site = &site; \
} \
&site.allocation_site; \
})
#include <stddef.h>
void* operator new(size_t size, struct __allocation_site* allocation_site);
void* operator new[](size_t size, struct __allocation_site* allocation_site);
#define new new (ALLOCATION_SITE)
#endif
#endif

View File

@ -72,7 +72,7 @@ enum
#define REG_CR2 REG_CR2
REG_FSBASE,
#define REG_FSBASE REG_FSBASE
REG_GSBASE,
REG_GSBASE
#define REG_GSBASE REG_GSBASE
};
#endif
@ -126,7 +126,7 @@ enum
#define REG_CR2 REG_CR2
REG_FSBASE,
#define REG_FSBASE REG_FSBASE
REG_GSBASE,
REG_GSBASE
#define REG_GSBASE REG_GSBASE
};
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2018, 2021-2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2018, 2021-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
@ -103,6 +103,8 @@
#include "x86-family/vbox.h"
#endif
struct kernel_allocation_site* first_kernel_allocation_site = NULL;
// Keep the stack size aligned with $CPU/boot.s
const size_t STACK_SIZE = 64*1024;
extern "C" { __attribute__((aligned(16))) size_t stack[STACK_SIZE / sizeof(size_t)]; }
@ -231,6 +233,13 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
if ( !(kernel_options = strdup(cmdline ? cmdline : "")) )
Panic("Failed to allocate kernel command line");
#if defined(__i386__) || defined(__x86_64__)
// TODO: Detect EFI.
kernel_firmware = "bios";
#else
#warning "Name your system firmware here"
kernel_firmware = "unknown";
#endif
int argmax = 1;
argv = new char*[argmax + 1];
@ -286,6 +295,20 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
enable_network_drivers = true;
else if ( !strcmp(arg, "--no-random-seed") )
no_random_seed = true;
else if ( !strncmp(arg, "--firmware=", strlen("--firmware=")) )
{
const char* firmware = arg + strlen("--firmware=");
#if defined(__i386__) || defined(__x86_64__)
if ( !strcmp(firmware, "bios") || !strcmp(firmware, "pc") )
kernel_firmware = "bios";
else if ( !strcmp(firmware, "efi") )
kernel_firmware = "efi";
else
#endif
{
PanicF("Unsupported firmware option: %s", firmware);
}
}
else
{
Log::PrintF("\r\e[J");

View File

@ -25,6 +25,7 @@
#include <sortix/kernel/syscall.h>
#include "kernelinfo.h"
#include "net/tcp.h"
#ifndef VERSIONSTR
#define VERSIONSTR "unknown"
@ -33,20 +34,17 @@
namespace Sortix {
char* kernel_options;
const char* kernel_firmware;
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__)
if ( strcmp(req, "firmware") == 0 ) { return "bios"; }
#else
#warning "Name your system firmware here"
#endif
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 ( strcmp(req, "firmware") == 0 ) return kernel_firmware;
return NULL;
}
@ -55,6 +53,77 @@ ssize_t sys_kernelinfo(const char* user_req, char* user_resp, size_t resplen)
char* req = GetStringFromUser(user_req);
if ( !req )
return -1;
// DEBUG
if ( !strcmp(req, "virtual-usage") )
{
delete[] req;
size_t heap;
size_t aux;
size_t leaked;
size_t total;
KernelAddressStatistics(&heap, &aux, &leaked, &total);
char str[4 * (20 + 3 * sizeof(size_t) + 1 + 8)];
snprintf(str, sizeof(str),
"%20zu B heap\n%20zu B aux\n%20zu B leaked\n%20zu B total",
heap, aux, leaked, total);
size_t stringlen = strlen(str);
if ( resplen < stringlen + 1 )
return errno = ERANGE, (ssize_t) stringlen;
if ( !CopyToUser(user_resp, str, sizeof(char) * (stringlen + 1)) )
return -1;
return 0;
}
// DEBUG
if ( !strcmp(req, "tcp") )
{
delete[] req;
return TCP::Info(user_resp, resplen);
}
#ifdef __TRACE_ALLOCATION_SITES
if ( !strcmp(req, "allocations") )
{
delete[] req;
bool exhausted = false;
size_t total_needed = 0;
for ( struct kernel_allocation_site* site = first_kernel_allocation_site;
site;
site = site->next )
{
char str[256];
snprintf(str, sizeof(str), "%20zu B %zu allocations %s:%zu %s %c",
site->allocation_site.current_size,
site->allocation_site.allocations,
site->allocation_site.file,
site->allocation_site.line,
site->allocation_site.function,
site->next ? '\n' : '\0');
size_t stringlen = strlen(str);
total_needed += stringlen;
if ( exhausted )
continue;
if ( resplen < stringlen )
{
exhausted = true;
continue;
}
if ( !CopyToUser(user_resp, str, sizeof(char) * stringlen) )
return -1;
user_resp += stringlen;
resplen -= stringlen;
}
if ( !exhausted && !resplen )
exhausted = true;
if ( !exhausted )
{
char zero = '\0';
if ( !CopyToUser(user_resp, &zero, 1) )
return -1;
}
if ( exhausted )
return errno = ERANGE, (ssize_t) total_needed;
return 0;
}
#endif
const char* str = KernelInfo(req);
delete[] req;
if ( !str )

View File

@ -23,6 +23,7 @@
namespace Sortix {
extern char* kernel_options;
extern const char* kernel_firmware;
} // namespace Sortix

View File

@ -50,6 +50,7 @@ LineBuffer::~LineBuffer()
bool LineBuffer::Push(uint32_t unicode)
{
// TODO: An infinite line buffer seems like a bad idea.
// Check if we need to allocate or resize the circular queue.
if ( bufferused == bufferlength )
{

View File

@ -36,6 +36,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
@ -120,6 +121,9 @@ static kthread_mutex_t tcp_lock = KTHREAD_MUTEX_INITIALIZER;
static TCPSocket** bindings_v4;
static TCPSocket** bindings_v6;
static TCPSocket* all_first_socket;
static TCPSocket* all_last_socket;
void Init()
{
if ( !(bindings_v4 = new TCPSocket*[65536]) ||
@ -218,6 +222,7 @@ public:
int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize);
public:
size_t Describe(char* buf, size_t buflen);
void Unreference();
void ProcessPacket(Ref<Packet> pkt, union tcp_sockaddr* pkt_src,
union tcp_sockaddr* pkt_dst);
@ -278,6 +283,12 @@ public:
// The listening socket this socket is in the listening queue for.
TCPSocket* connecting_parent;
// DEBUG: The previous socket of all sockets.
TCPSocket* all_prev_socket;
// DEBUG: The next socket of all sockets.
TCPSocket* all_next_socket;
// Condition variable that is signaled when new data can be received.
kthread_cond_t receive_cond;
@ -461,7 +472,7 @@ void TCPSocket__OnTimer(Clock* /*clock*/, Timer* /*timer*/, void* user)
((TCPSocket*) user)->OnTimer();
}
TCPSocket::TCPSocket(int af)
TCPSocket::TCPSocket(int af) // DEBUG: tcp_lock taken
{
prev_socket = NULL;
next_socket = NULL;
@ -517,9 +528,15 @@ TCPSocket::TCPSocket(int af)
shutdown_receive = false;
memset(incoming, 0, sizeof(incoming));
memset(outgoing, 0, sizeof(outgoing));
// DEBUG
all_prev_socket = all_last_socket;
all_next_socket = NULL;
(all_last_socket ?
all_last_socket->all_next_socket : all_first_socket) = this;
all_last_socket = this;
}
TCPSocket::~TCPSocket()
TCPSocket::~TCPSocket() // DEBUG: tcp_lock taken
{
assert(state == TCP_STATE_CLOSED);
assert(!bound);
@ -539,6 +556,52 @@ TCPSocket::~TCPSocket()
receive_queue = packet->next;
packet->next.Reset();
}
// DEBUG
(all_prev_socket ?
all_prev_socket->all_next_socket : all_first_socket) = all_next_socket;
(all_next_socket ?
all_next_socket->all_prev_socket : all_last_socket) = all_prev_socket;
all_prev_socket = NULL;
all_next_socket = NULL;
}
// DEBUG
size_t TCPSocket::Describe(char* buf, size_t buflen) // tcp_lock taken
{
const char* const STATE_NAMES[] =
{
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RECV",
"ESTAB",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT",
};
const char* state_name = STATE_NAMES[state];
char local_str[INET_ADDRSTRLEN];
char remote_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &local.in.sin_addr, local_str, sizeof(local_str));
inet_ntop(AF_INET, &remote.in.sin_addr, remote_str, sizeof(remote_str));
char timeout[64] = "none";
if ( timer_armed )
{
struct itimerspec its;
timer.Get(&its);
snprintf(timeout, sizeof(timeout), "%ji.%09li",
(intmax_t) its.it_value.tv_sec, its.it_value.tv_nsec);
}
return snprintf(buf, buflen,
"%s %s %u -> %s %u"
" timeout=%s resends=%u sockerr=%i transmit=%i refed=%i\n",
state_name, local_str, be16toh(local.in.sin_port),
remote_str, be16toh(remote.in.sin_port), timeout,
retransmissions, sockerr, transmit_scheduled,
is_referenced);
}
void TCPSocket::Unreference()
@ -2558,6 +2621,7 @@ Ref<Inode> Socket(int af)
{
if ( !IsSupportedAddressFamily(af) )
return errno = EAFNOSUPPORT, Ref<Inode>(NULL);
ScopedLock lock(&tcp_lock); // DEBUG
TCPSocket* socket = new TCPSocket(af);
if ( !socket )
return Ref<Inode>();
@ -2567,5 +2631,45 @@ Ref<Inode> Socket(int af)
return result;
}
// DEBUG
ssize_t Info(char* user_resp, size_t resplen)
{
ScopedLock lock(&tcp_lock); // DEBUG
bool exhausted = false;
size_t total_needed = 0;
for ( TCPSocket* socket = all_first_socket;
socket;
socket = socket->all_next_socket )
{
char str[256];
size_t stringlen = socket->Describe(str, sizeof(str));
if ( !socket->all_next_socket && stringlen )
stringlen--;
total_needed += stringlen;
if ( exhausted )
continue;
if ( resplen < stringlen )
{
exhausted = true;
continue;
}
if ( !CopyToUser(user_resp, str, sizeof(char) * stringlen) )
return -1;
user_resp += stringlen;
resplen -= stringlen;
}
if ( !exhausted && !resplen )
exhausted = true;
if ( !exhausted )
{
char zero = '\0';
if ( !CopyToUser(user_resp, &zero, 1) )
return -1;
}
if ( exhausted )
return errno = ERANGE, (ssize_t) total_needed;
return 0;
}
} // namespace TCP
} // namespace Sortix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2016, 2017, 2022 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
@ -34,6 +34,7 @@ void HandleIP(Ref<Packet> pkt,
const struct in_addr* dst,
bool dst_broadcast);
Ref<Inode> Socket(int af);
ssize_t Info(char* user_resp, size_t resplen);
} // namespace TCP
} // namespace Sortix

View File

@ -24,6 +24,19 @@
#warning "security: -fcheck-new might not work on clang"
#endif
#ifdef __TRACE_ALLOCATION_SITES
#undef new
void* operator new(size_t size, struct __allocation_site* allocation_site)
{
return malloc_trace(allocation_site, size);
}
void* operator new[](size_t size, struct __allocation_site* allocation_site)
{
return malloc_trace(allocation_site, size);
}
#else
void* operator new(size_t size)
{
return malloc(size);
@ -33,6 +46,7 @@ void* operator new[](size_t size)
{
return malloc(size);
}
#endif
void operator delete(void* addr)
{

View File

@ -477,6 +477,8 @@ private:
size_t output_offset;
size_t output_used;
static const size_t output_size = 4096;
// TODO: This is not safe because ^W can produce unbounded output.
static const size_t output_probably_safe = 2048;
uint8_t output[output_size];
int ptynum;
@ -538,6 +540,16 @@ ssize_t PTY::master_write(ioctx_t* ctx, const uint8_t* buf, size_t count)
ScopedLockSignal lock(&termlock);
if ( !lock.IsAcquired() )
return errno = EINTR, -1;
// TODO: Work around deadlock by refusing writes when the buffer is starting
// to be too full. This can block / "deadlock" too if the caller
// didn't try to read the data written by a previous call.
while ( output_probably_safe <= output_used )
{
if ( ctx->dflags & O_NONBLOCK )
return errno = EWOULDBLOCK, -1;
if ( !kthread_cond_wait_signal(&output_ready_cond, &termlock) )
return errno = EINTR, -1;
}
size_t sofar = 0;
while ( sofar < count )
{
@ -550,9 +562,13 @@ ssize_t PTY::master_write(ioctx_t* ctx, const uint8_t* buf, size_t count)
{
if ( Signal::IsPending() )
return sofar ? (ssize_t) sofar : (errno = EINTR, -1);
// TODO: Unfortunately sequences like ^W can cause an unbounded
// number of tty_output data causing a deadlock.
ProcessByte(input[i]);
sofar++;
if ( output_probably_safe <= output_used )
return (ssize_t) sofar;
}
sofar += amount;
}
return (ssize_t) sofar;
}
@ -599,7 +615,7 @@ short PTY::PollMasterEventStatus()
short status = 0;
if ( output_used )
status |= POLLIN | POLLRDNORM;
if ( true /* can always write */ )
if ( output_used < output_probably_safe )
status |= POLLOUT | POLLWRNORM;
return status;
}

View File

@ -78,9 +78,7 @@ static const uint8_t DEVICE_CMD_RESET = 0xFF;
static const size_t DEVICE_RETRIES = 5;
// TODO: This is entirely a guess. I don't actually know what timeout is
// suitable. GRUB seems to use 20 ms. I'll pick 50 ms to be safe.
static const unsigned int TIMEOUT_MS = 50;
static const unsigned int TIMEOUT_MS = 20;
static bool WaitInput()
{

View File

@ -60,20 +60,6 @@ malloc/__heap_verify.o \
netinet/if_ether/etheraddr_broadcast.o \
netinet/in/in6addr_any.o \
netinet/in/in6addr_loopback.o \
regex/regcomp.o \
regex/regerror.o \
regex/regexec.o \
regex/regfree.o \
sha2/sha224hl.o \
sha2/sha224.o \
sha2/sha256hl.o \
sha2/sha256.o \
sha2/sha384hl.o \
sha2/sha384.o \
sha2/sha512_256hl.o \
sha2/sha512_256.o \
sha2/sha512hl.o \
sha2/sha512.o \
signal/sigaddset.o \
signal/sigandset.o \
signal/sigdelset.o \
@ -86,7 +72,6 @@ signal/sigorset.o \
ssp/__stack_chk_fail.o \
stdio/asprintf.o \
stdio/cbprintf.o \
stdio/cbscanf.o \
stdio/clearerr.o \
stdio/clearerr_unlocked.o \
stdio/dprintf.o \
@ -116,8 +101,6 @@ stdio/fgets.o \
stdio/fgets_unlocked.o \
stdio/fileno_unlocked.o \
stdio/flockfile.o \
stdio/fmemopen.o \
stdio/fnewfile.o \
stdio/fparsemode.o \
stdio/fprintf_unlocked.o \
stdio/fputc.o \
@ -128,8 +111,6 @@ stdio/fread.o \
stdio/fread_unlocked.o \
stdio/fregister.o \
stdio/fresetfile.o \
stdio/fscanf.o \
stdio/fscanf_unlocked.o \
stdio/fseek.o \
stdio/fseeko.o \
stdio/fseeko_unlocked.o \
@ -142,28 +123,20 @@ stdio/funlockfile.o \
stdio/funregister.o \
stdio/fwrite.o \
stdio/fwrite_unlocked.o \
stdio/getdelim.o \
stdio/getline.o \
stdio/open_memstream.o \
stdio/rewind.o \
stdio/setbuf.o \
stdio/setvbuf.o \
stdio/setvbuf_unlocked.o \
stdio/snprintf.o \
stdio/sprintf.o \
stdio/sscanf.o \
stdio/ungetc.o \
stdio/ungetc_unlocked.o \
stdio/vasprintf.o \
stdio/vcbprintf.o \
stdio/vdprintf.o \
stdio/vfprintf_unlocked.o \
stdio/vfscanf.o \
stdio/vfscanf_unlocked.o \
stdio/vcbscanf.o \
stdio/vsnprintf.o \
stdio/vsprintf.o \
stdio/vsscanf.o \
stdlib/abort.o \
stdlib/abs.o \
stdlib/arc4random_buf.o \
@ -278,7 +251,6 @@ wchar/wcscmp.o \
wchar/wcscoll.o \
wchar/wcscpy.o \
wchar/wcscspn.o \
wchar/wcsdup.o \
wchar/wcsftime.o \
wchar/wcslcat.o \
wchar/wcslcpy.o \
@ -329,6 +301,34 @@ wctype/towupper.o \
wctype/wctype.o \
HOSTEDOBJS=\
regex/regcomp.o \
regex/regerror.o \
regex/regexec.o \
regex/regfree.o \
sha2/sha224hl.o \
sha2/sha224.o \
sha2/sha256hl.o \
sha2/sha256.o \
sha2/sha384hl.o \
sha2/sha384.o \
sha2/sha512_256hl.o \
sha2/sha512_256.o \
sha2/sha512hl.o \
sha2/sha512.o \
stdio/cbscanf.o \
stdio/fscanf.o \
stdio/fscanf_unlocked.o \
stdio/sscanf.o \
stdio/vfscanf.o \
stdio/vfscanf_unlocked.o \
stdio/vcbscanf.o \
stdio/vsscanf.o \
stdio/fmemopen.o \
stdio/open_memstream.o \
stdio/getdelim.o \
stdio/getline.o \
stdio/fnewfile.o \
wchar/wcsdup.o \
blf/blowfish.o \
$(CPUDIR)/fork.o \
$(CPUDIR)/setjmp.o \

View File

@ -66,6 +66,7 @@ int getifaddrs(struct ifaddrs** ifas_ptr)
freeifaddrs(ifas);
return -1;
}
struct if_status status;
struct if_config cfg;
if ( ioctl(fd, NIOC_GETCONFIG, &cfg) < 0 )
{
@ -89,7 +90,7 @@ int getifaddrs(struct ifaddrs** ifas_ptr)
ifas = &ifa->pub;
strlcpy(ifa->name, netif->if_name, sizeof(ifa->name));
ifa->pub.ifa_name = ifa->name;
ifa->pub.ifa_flags = 0;
ifa->pub.ifa_flags = status.flags & IF_STATUS_FLAGS_UP ? IFF_UP : 0;
ifa->addr.in.sin_family = AF_INET;
memcpy(&ifa->addr.in.sin_addr, &cfg.inet.address,
sizeof(struct in_addr));

View File

@ -24,6 +24,8 @@
#include <sys/__/types.h>
#define IFF_UP (1 << 0)
#ifndef __socklen_t_defined
#define __socklen_t_defined
typedef __socklen_t socklen_t;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2022 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
@ -117,6 +117,16 @@ struct heap_alloc
#warning "You need to implement HEAP_CHUNK_MAGIC for your native word width"
#endif
#ifdef __TRACE_ALLOCATION_SITES
#define MAGIC_IS_ALLOCATION_SITE(magic)( (((size_t) (magic)) & 0x3) == 0x2)
#define ALLOCATION_SITE_OF_MAGIC(magic) ((struct __allocation_site*) ((magic) & ~0x3UL))
#define MAGIC_OF_ALLOCATION_SITE(magic) (((size_t) (magic)) | 0x2)
#else
#define MAGIC_IS_ALLOCATION_SITE(magic) 0
#define ALLOCATION_SITE_OF_MAGIC(magic) NULL
#define MAGIC_OF_ALLOCATION_SITE(magic) NULL
#endif
/* The heap is split into a number of parts that each consists of a number of
of chunks (used and unused). The heap normally is just a single part, but if
the address space gets too fragmented, it may not be possible to extend the
@ -346,7 +356,8 @@ bool heap_chunk_is_used(struct heap_chunk* chunk)
{
assert(HEAP_IS_POINTER_ALIGNED(chunk, chunk->chunk_size));
return chunk->chunk_magic == HEAP_CHUNK_MAGIC;
return chunk->chunk_magic == HEAP_CHUNK_MAGIC ||
MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic);
}
/* Returns the trailing structure following the given chunk. */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 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
@ -242,8 +242,14 @@ int vdprintf(int fildes, const char* __restrict format, __gnuc_va_list ap)
/* Functions copied from elsewhere. */
#if __USE_SORTIX
#ifdef __TRACE_ALLOCATION_SITES
int asprintf_trace(struct __allocation_site*, char** __restrict, const char* __restrict, ...)
__attribute__((__format__ (printf, 3, 4)));
#define asprintf(a, ...) asprintf_trace(ALLOCATION_SITE, (a), __VA_ARGS__)
#else
int asprintf(char** __restrict, const char* __restrict, ...)
__attribute__((__format__ (printf, 2, 3)));
#endif
void clearerr_unlocked(FILE* stream);
int feof_unlocked(FILE* stream);
int ferror_unlocked(FILE* stream);
@ -255,9 +261,15 @@ int fputc_unlocked(int c, FILE* stream);
int fputs_unlocked(const char* __restrict, FILE* __restrict stream);
size_t fread_unlocked(void* __restrict ptr, size_t size, size_t nitems, FILE* __restrict stream);
size_t fwrite_unlocked(const void* __restrict ptr, size_t size, size_t nitems, FILE* __restrict stream);
#ifdef __TRACE_ALLOCATION_SITES
int vasprintf_trace(struct __allocation_site*, char** __restrict, const char* __restrict, __gnuc_va_list)
__attribute__((__format__ (printf, 3, 0)));
#define vasprintf(a, ...) vasprintf_trace(ALLOCATION_SITE, (a), __VA_ARGS__)
#else
int vasprintf(char** __restrict, const char* __restrict, __gnuc_va_list)
__attribute__((__format__ (printf, 2, 0)));
#endif
#endif
/* Functions that are Sortix extensions. */
#if __USE_SORTIX

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017, 2022 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
@ -88,7 +88,12 @@ double atof(const char* value);
int atoi(const char*);
long atol(const char*);
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
#ifdef __TRACE_ALLOCATION_SITES
void* calloc_trace(struct __allocation_site*, size_t, size_t);
#define calloc(a, b) calloc_trace(ALLOCATION_SITE, (a), (b))
#else
void* calloc(size_t, size_t);
#endif
char* canonicalize_file_name(const char* path);
char* canonicalize_file_name_at(int dirfd, const char* path);
int clearenv(void);
@ -99,7 +104,12 @@ void free(void*);
char* getenv(const char*);
long labs(long);
ldiv_t ldiv(long, long);
#ifdef __TRACE_ALLOCATION_SITES
void* malloc_trace(struct __allocation_site*, size_t);
#define malloc(a) malloc_trace(ALLOCATION_SITE, (a))
#else
void* malloc(size_t);
#endif
int mblen(const char*, size_t);
size_t mbstowcs(wchar_t* __restrict, const char* __restrict, size_t);
int mbtowc(wchar_t *__restrict, const char* __restrict, size_t);
@ -116,7 +126,12 @@ void qsort_r(void*, size_t, size_t, int (*)(const void*, const void*, void*), vo
__attribute__((__warning__("rand() isn't random, use arc4random()")))
#endif
int rand(void);
#ifdef __TRACE_ALLOCATION_SITES
void* realloc_trace(struct __allocation_site*, void*, size_t);
#define realloc(a, b) realloc_trace(ALLOCATION_SITE, (a), (b))
#else
void* realloc(void*, size_t);
#endif
char* realpath(const char* __restrict, char* __restrict);
int setenv(const char*, const char*, int);
#if !defined(__is_sortix_libc) /* not a warning inside libc */
@ -189,7 +204,12 @@ int posix_openpt(int);
uint32_t arc4random(void);
void arc4random_buf(void*, size_t);
uint32_t arc4random_uniform(uint32_t);
#ifdef __TRACE_ALLOCATION_SITES
void* reallocarray_trace(struct __allocation_site*, void*, size_t, size_t);
#define reallocarray(a, b, c) reallocarray_trace(ALLOCATION_SITE, (a), (b), (c))
#else
void* reallocarray(void*, size_t, size_t);
#endif
int ptsname_r(int, char*, size_t);
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2017, 2022 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
@ -91,8 +91,13 @@ void* memccpy(void* __restrict, const void* __restrict, int, size_t);
/* Functions from XOPEN 420 gone into POSIX 2008. */
#if __USE_SORTIX || 420 <= __USE_XOPEN || 200809L <= __USE_POSIX
#ifdef __TRACE_ALLOCATION_SITES
char* strdup_trace(struct __allocation_site*, const char*);
#define strdup(a) strdup_trace(ALLOCATION_SITE, (a))
#else
char* strdup(const char*);
#endif
#endif
/* Functions from POSIX 2001. */
#if __USE_SORTIX || 200112L <= __USE_POSIX
@ -111,7 +116,12 @@ char* strtok_r(char* __restrict, const char* __restrict, char** __restrict);
char* stpcpy(char* __restrict, const char* __restrict);
char* stpncpy(char* __restrict, const char* __restrict, size_t);
int strcoll_l(const char*, const char*, locale_t);
#ifdef __TRACE_ALLOCATION_SITES
char* strndup_trace(struct __allocation_site*, const char*, size_t);
#define strndup(a, b) strndup_trace(ALLOCATION_SITE, (a), (b))
#else
char* strndup(const char*, size_t);
#endif
size_t strnlen(const char*, size_t);
#if __USE_SORTIX && __SORTIX_STDLIB_REDIRECTS
const char* strsignal(int signum) __asm__ ("sortix_strsignal");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2015, 2022 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
@ -128,4 +128,24 @@
is updated to not rely on this macro. */
#undef __SORTIX_HAS_GETSERVBYNAME__
#if (defined(__is_sortix_libk) || defined(__is_sortix_kernel)) && \
defined(__TRACE_KMALLOC)
#undef __TRACE_ALLOCATION_SITES
#define __TRACE_ALLOCATION_SITES
#endif
#ifdef __TRACE_ALLOCATION_SITES
#include <stddef.h>
struct __allocation_site
{
const char* file;
size_t line;
const char* function;
size_t current_size;
size_t allocations;
};
#define __TRACE_ALLOCATION_SITES
#define ALLOCATION_SITE 0
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2022 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
@ -20,13 +20,24 @@
#include <stdarg.h>
#include <stdio.h>
#ifdef __TRACE_ALLOCATION_SITES
int asprintf_trace(struct __allocation_site* allocation_site,
char** restrict result_ptr,
const char* restrict format,
...)
#else
int asprintf(char** restrict result_ptr,
const char* restrict format,
...)
#endif
{
va_list list;
va_start(list, format);
#ifdef __TRACE_ALLOCATION_SITES
int result = vasprintf_trace(allocation_site, result_ptr, format, list);
#else
int result = vasprintf(result_ptr, format, list);
#endif
va_end(list);
return result;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2015, 2022 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
@ -27,6 +27,9 @@ struct vasprintf
char* buffer;
size_t used;
size_t size;
#ifdef __TRACE_ALLOCATION_SITES
struct __allocation_site* allocation_site;
#endif
};
static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
@ -39,7 +42,12 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
size_t new_size = 2 * state->size;
if ( new_size < needed_size )
new_size = needed_size;
#ifdef __TRACE_ALLOCATION_SITES
char* new_buffer = (char*) realloc_trace(state->allocation_site,
state->buffer, new_size);
#else
char* new_buffer = (char*) realloc(state->buffer, new_size);
#endif
if ( !new_buffer )
{
free(state->buffer);
@ -54,14 +62,26 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
return length;
}
#ifdef __TRACE_ALLOCATION_SITES
int vasprintf_trace(struct __allocation_site* allocation_site,
char** restrict result_ptr,
const char* restrict format,
va_list list)
#else
int vasprintf(char** restrict result_ptr,
const char* restrict format,
va_list list)
#endif
{
struct vasprintf state;
state.used = 0;
state.size = 32;
#ifdef __TRACE_ALLOCATION_SITES
state.allocation_site = allocation_site;
if ( !(state.buffer = (char*) malloc_trace(allocation_site, state.size)) )
#else
if ( !(state.buffer = (char*) malloc(state.size)) )
#endif
return -1;
int result = vcbprintf(&state, vasprintf_callback, format, list);
if ( !state.buffer )

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 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
@ -22,12 +22,21 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
void* calloc_trace(struct __allocation_site* allocation_site,
size_t nmemb, size_t size)
#else
void* calloc(size_t nmemb, size_t size)
#endif
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
size_t total = nmemb * size;
#ifdef __TRACE_ALLOCATION_SITES
void* result = malloc_trace(allocation_site, total);
#else
void* result = malloc(total);
#endif
if ( !result )
return NULL;
memset(result, 0, total);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 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
@ -39,6 +39,16 @@ void free(void* addr)
// Retrieve the chunk that contains this allocation.
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) addr);
#ifdef __TRACE_ALLOCATION_SITES
if ( MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic) )
{
struct __allocation_site* allocation_site =
ALLOCATION_SITE_OF_MAGIC(chunk->chunk_magic);
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
}
#endif
// Return the chunk to the heap.
heap_insert_chunk(chunk);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 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
@ -31,7 +31,12 @@
#if !defined(HEAP_GUARD_DEBUG)
#ifdef __TRACE_ALLOCATION_SITES
void* malloc_trace(struct __allocation_site* allocation_site,
size_t original_size)
#else
void* malloc(size_t original_size)
#endif
{
if ( !heap_size_has_bin(original_size) )
return errno = ENOMEM, (void*) NULL;
@ -89,6 +94,12 @@ void* malloc(size_t original_size)
if ( heap_can_split_chunk(result_chunk, chunk_size) )
heap_split_chunk(result_chunk, chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += result_chunk->chunk_size;
allocation_site->allocations++;
result_chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 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
@ -31,10 +31,20 @@
#if !defined(HEAP_GUARD_DEBUG)
#ifdef __TRACE_ALLOCATION_SITES
void* realloc_trace(struct __allocation_site* new_allocation_site,
void* ptr,
size_t requested_size)
#else
void* realloc(void* ptr, size_t requested_size)
#endif
{
if ( !ptr )
#ifdef __TRACE_ALLOCATION_SITES
return malloc_trace(new_allocation_site, requested_size);
#else
return malloc(requested_size);
#endif
if ( !heap_size_has_bin(requested_size) )
return errno = ENOMEM, (void*) NULL;
@ -55,7 +65,14 @@ void* realloc(void* ptr, size_t requested_size)
// Retrieve the chunk that contains this allocation.
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) ptr);
assert(chunk->chunk_magic == HEAP_CHUNK_MAGIC);
#ifdef __TRACE_ALLOCATION_SITES
assert(MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic));
struct __allocation_site* allocation_site =
ALLOCATION_SITE_OF_MAGIC(chunk->chunk_magic);
#endif
assert(chunk->chunk_magic == HEAP_CHUNK_MAGIC ||
MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic));
assert(heap_chunk_to_post(chunk)->chunk_magic == HEAP_CHUNK_MAGIC);
assert(heap_chunk_to_post(chunk)->chunk_size == chunk->chunk_size);
@ -72,8 +89,17 @@ void* realloc(void* ptr, size_t requested_size)
if ( requested_chunk_size < chunk->chunk_size )
{
assert(requested_chunk_size <= chunk->chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
#endif
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
heap_split_chunk(chunk, requested_chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += chunk->chunk_size;
allocation_site->allocations++;
chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();
return heap_chunk_to_data(chunk);
@ -88,11 +114,20 @@ void* realloc(void* ptr, size_t requested_size)
!heap_chunk_is_used(right) &&
requested_chunk_size <= chunk->chunk_size + right->chunk_size )
{
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
#endif
heap_remove_chunk(right);
heap_chunk_format((uint8_t*) chunk, chunk->chunk_size + right->chunk_size);
assert(requested_chunk_size <= chunk->chunk_size);
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
heap_split_chunk(chunk, requested_chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += chunk->chunk_size;
allocation_site->allocations++;
chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();
return heap_chunk_to_data(chunk);
@ -108,7 +143,11 @@ void* realloc(void* ptr, size_t requested_size)
assert(orignal_ptr_size < requested_size);
#ifdef __TRACE_ALLOCATION_SITES
void* result = malloc_trace(allocation_site, requested_size);
#else
void* result = malloc(requested_size);
#endif
if ( !result )
return (void*) NULL;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2022 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
@ -21,9 +21,18 @@
#include <stdint.h>
#include <stdlib.h>
#ifdef __TRACE_ALLOCATION_SITES
void* reallocarray_trace(struct __allocation_site* allocation_site,
void* ptr, size_t nmemb, size_t size)
#else
void* reallocarray(void* ptr, size_t nmemb, size_t size)
#endif
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
#ifdef __TRACE_ALLOCATION_SITES
return realloc_trace(allocation_site, ptr, nmemb * size);
#else
return realloc(ptr, nmemb * size);
#endif
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 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
@ -20,10 +20,18 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
char* strdup_trace(struct __allocation_site* allocation_site, const char* input)
#else
char* strdup(const char* input)
#endif
{
size_t input_length = strlen(input);
#ifdef __TRACE_ALLOCATION_SITES
char* result = (char*) malloc_trace(allocation_site, input_length + 1);
#else
char* result = (char*) malloc(input_length + 1);
#endif
if ( !result )
return NULL;
memcpy(result, input, input_length + 1);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 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
@ -20,10 +20,19 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
char* strndup_trace(struct __allocation_site* allocation_site,
const char* input, size_t n)
#else
char* strndup(const char* input, size_t n)
#endif
{
size_t input_size = strnlen(input, n);
#ifdef __TRACE_ALLOCATION_SITES
char* result = (char*) malloc_trace(allocation_site, input_size + 1);
#else
char* result = (char*) malloc(input_size + 1);
#endif
if ( !result )
return NULL;
memcpy(result, input, input_size);

View File

@ -17,9 +17,11 @@ devices.o \
crc32.o \
ext2.o \
extended.o \
fat.o \
filesystem.o \
gpt.o \
harddisk.o \
iso9660.o \
mbr.o \
partition.o \
uuid.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;

123
libmount/fat.c Normal file
View File

@ -0,0 +1,123 @@
/*
* 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;
// TODO: Test for a space padded FAT32 at 0x52 + 8 bytes.
if ( p && 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;
}
else if ( p && 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 bool fat_is_esp(struct blockdevice* bdev)
{
struct partition* p = bdev->p;
if ( p->table_type == PARTITION_TABLE_TYPE_GPT )
{
unsigned char esp_uuid[16];
uuid_from_string(esp_uuid, ESP_GPT_TYPE_UUID);
return memcmp(p->gpt_type_guid, esp_uuid, 16) == 0;
}
else if ( p->table_type == PARTITION_TABLE_TYPE_MBR )
return 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;
fs->fstype_name = fat_is_esp(fs->bdev) ? "efi": "fat";
fs->fsck = "fsck.fat";
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,7 +31,9 @@
#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>
const char* filesystem_error_string(enum filesystem_error error)
@ -55,6 +57,8 @@ static const struct filesystem_handler* filesystem_handlers[] =
&biosboot_handler,
&extended_handler,
&ext2_handler,
&iso9660_handler,
&fat_handler,
NULL,
};

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2023, 2024 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 "28732ac1-1ff8-d211-ba4b-00a0c93ec93b"
#if defined(__cplusplus)
extern "C" {
#endif
extern const struct filesystem_handler fat_handler;
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 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/iso9660.h
* ISO 9660 filesystem.
*/
#ifndef INCLUDE_MOUNT_ISO9660_H
#define INCLUDE_MOUNT_ISO9660_H
#include <stdint.h>
#include <mount/filesystem.h>
#if defined(__cplusplus)
extern "C" {
#endif
extern const struct filesystem_handler iso9660_handler;
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

98
libmount/iso9660.c Normal file
View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2022 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.
*
* iso9660.c
* iso9660 filesystem.
*/
#include <endian.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <mount/blockdevice.h>
#include <mount/filesystem.h>
#include <mount/iso9660.h>
#include <mount/uuid.h>
#include "util.h"
static size_t iso9660_probe_amount(struct blockdevice* bdev)
{
(void) bdev;
size_t block_size = 2048 /* TODO: Actual block size? */;
return 17 * block_size;
}
static bool iso9660_probe(struct blockdevice* bdev,
const unsigned char* leading,
size_t amount)
{
(void) bdev;
size_t block_size = 2048 /* TODO: Actual block size? */;
if ( amount < 17 * block_size )
return false;
size_t offset = 16 * block_size;
return !memcmp(leading + offset + 1, "CD001", 5);
}
static void iso9660_release(struct filesystem* fs)
{
if ( !fs )
return;
free(fs);
}
static enum filesystem_error iso9660_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 = &iso9660_handler;
fs->handler_private = NULL;
fs->fstype_name = "iso9660";
fs->driver = "iso9660fs";
size_t block_size = 2048 /* TODO: Actual block size? */;
size_t offset = 16 * block_size;
uint8_t pvd[2048];
if ( blockdevice_preadall(bdev, pvd, sizeof(pvd), offset) != sizeof(pvd) )
return iso9660_release(fs), FILESYSTEM_ERROR_ERRNO;
// TODO: LABEL support using the volume name.
char vol_set[128 + 1];
memcpy(vol_set, pvd + 190, 128);
vol_set[128] = '\0';
for ( size_t i = 128; i && vol_set[i-1] == ' '; i-- )
vol_set[i-1] = '\0';
if ( uuid_validate(vol_set) )
{
fs->flags |= FILESYSTEM_FLAG_UUID;
uuid_from_string(fs->uuid, vol_set);
}
return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
}
const struct filesystem_handler iso9660_handler =
{
.handler_name = "iso9660",
.probe_amount = iso9660_probe_amount,
.probe = iso9660_probe,
.inspect = iso9660_inspect,
.release = iso9660_release,
};

View File

@ -163,6 +163,9 @@ bool blockdevice_probe_partition_table_type(enum partition_table_type* result_ou
memcpy(&mbr, leading, sizeof(mbr));
if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
break;
// Ignore the partition table on live cdrom images.
if ( mbr.partitions[0][4 /*system_id*/] == 0xCD )
break;
result = PARTITION_TABLE_TYPE_MBR;
goto out;
} while ( 0 );

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2015, 2016, 2024 Jonas 'Sortie' Termansen.
* Copyright (c) 2021 Juhani 'nortti' Krekelä.
* Copyright (c) 2023 dzwdz.
*
@ -96,6 +96,8 @@ struct glogin
struct check chk;
int fd_mouse;
struct dispmsg_crtc_mode mode;
struct framebuffer wallpaper;
size_t wallpaper_size;
struct framebuffer fade_from_fb;
struct timespec fade_from_begin;
struct timespec fade_from_end;
@ -200,14 +202,8 @@ void render_right_text_if_needed(struct framebuffer fb, const char* str, uint32_
union c { struct { uint8_t b; uint8_t g; uint8_t r; }; uint32_t v; };
static void render_background(struct framebuffer fb)
static void wallpaper(struct framebuffer fb)
{
#if 0
uint32_t bg_color = make_color(0x89 * 2/3, 0xc7 * 2/3, 0xff * 2/3);
for ( size_t y = 0; y < fb.yres; y++ )
for ( size_t x = 0; x < fb.xres; x++ )
framebuffer_set_pixel(fb, x, y, bg_color);
#endif
static uint32_t s;
static uint32_t t;
static bool seeded = false;
@ -256,6 +252,19 @@ static void render_background(struct framebuffer fb)
}
}
static void render_background(struct framebuffer fb)
{
#if 0
uint32_t bg_color = make_color(0x89 * 2/3, 0xc7 * 2/3, 0xff * 2/3);
for ( size_t y = 0; y < damage_rect.height; y++ )
for ( size_t x = 0; x < damage_rect.width; x++ )
framebuffer_set_pixel(fb, damage_rect.left + x, damage_rect.top + y,
bg_color);
#endif
framebuffer_copy_to_framebuffer(fb, state.wallpaper);
}
static void render_pointer(struct framebuffer fb)
{
int p_hwidth = arrow_framebuffer.xres / 2;
@ -576,6 +585,20 @@ static bool begin_render(struct glogin* state, struct framebuffer* fb)
fb->yres = state->mode.view_yres;
fb->pitch = state->mode.view_xres;
size_t size = sizeof(uint32_t) * fb->xres * fb->yres;
if ( state->wallpaper_size != size )
{
if ( !(state->wallpaper.buffer =
realloc(state->wallpaper.buffer, size)) )
{
warn("malloc");
return false;
}
state->wallpaper.xres = state->mode.view_xres;
state->wallpaper.yres = state->mode.view_yres;
state->wallpaper.pitch = state->mode.view_xres;
state->wallpaper_size = size;
wallpaper(state->wallpaper);
}
fb->buffer = (uint32_t*) glogin_malloc_fb_buffer(state, size);
if ( !fb->buffer )
{

2
nyan/.gitignore vendored Normal file
View File

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

33
nyan/Makefile Normal file
View File

@ -0,0 +1,33 @@
SOFTWARE_MEANT_FOR_SORTIX=1
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/dirs.mak
OPTLEVEL?=-g -O2
CFLAGS?=$(OPTLEVEL)
CFLAGS:=$(CFLAGS) -Wall -Wextra
PROGRAM=nyan
OBJS=\
nyan.o \
LIBS:=-lui -ldisplay
all: $(PROGRAM)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(PROGRAM) $(DESTDIR)$(BINDIR)
$(PROGRAM): $(OBJS)
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LIBS)
%.o: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
clean:
rm -f $(PROGRAM) *.o

181
nyan/nyan.c Normal file
View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2014, 2015, 2016 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.
*
* nyan.c
* Window with animated nyancat.
*/
#include <sys/socket.h>
#include <sys/keycodes.h>
#include <sys/un.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <error.h>
#include <ioleast.h>
#include <locale.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#include <display.h>
#include "nyan.h"
#include "pixel.h"
uint32_t WINDOW_ID = 0;
uint32_t WINDOW_WIDTH = 0;
uint32_t WINDOW_HEIGHT = 0;
bool need_redraw = true;
bool need_show = true;
bool need_exit = false;
void on_disconnect(void* ctx)
{
(void) ctx;
need_exit = true;
}
void on_quit(void* ctx, uint32_t window_id)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
need_exit = true;
}
void on_resize(void* ctx, uint32_t window_id, uint32_t width, uint32_t height)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
need_redraw = true;
WINDOW_WIDTH = width;
WINDOW_HEIGHT = height;
}
void on_keyboard(void* ctx, uint32_t window_id, uint32_t codepoint)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
(void) codepoint;
}
int main(int argc, char* argv[])
{
(void) argc;
(void) argv;
setlocale(LC_ALL, "");
setvbuf(stdout, NULL, _IOLBF, 0);
struct display_connection* connection = display_connect_default();
if ( !connection && errno == ECONNREFUSED )
display_spawn(argc, argv);
if ( !connection )
error(1, errno, "Could not connect to display server");
WINDOW_WIDTH = 600;
WINDOW_HEIGHT = 600;
display_create_window(connection, WINDOW_ID);
display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
display_title_window(connection, WINDOW_ID, "Nyanyanyanyanyanyanya...");
struct timespec frame_duration = timespec_make(0, 90 * 1000 * 1000);
struct timespec last_frame;
clock_gettime(CLOCK_MONOTONIC, &last_frame);
int frame_num = 0;
while ( !need_exit )
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec since_last_frame = timespec_sub(now, last_frame);
if ( !need_redraw && timespec_lt(since_last_frame, frame_duration) )
{
struct timespec remainder = timespec_sub(frame_duration, since_last_frame);
if ( timespec_lt(remainder, timespec_make(0, 10 * 1000 * 1000)) )
remainder = timespec_make(0, 10 * 1000 * 1000);
nanosleep(&remainder, NULL);
continue;
}
while ( timespec_le(frame_duration, since_last_frame) )
{
if ( !nyan_frames[++frame_num] )
frame_num = 0;
need_redraw = true;
since_last_frame = timespec_sub(since_last_frame, frame_duration);
}
if ( need_redraw )
{
last_frame = now;
uint32_t* framebuffer = (uint32_t*) malloc(sizeof(uint32_t) * WINDOW_WIDTH * WINDOW_HEIGHT);
const char* const* frame = nyan_frames[frame_num];
for ( size_t y = 0; y < WINDOW_HEIGHT; y++ )
{
int yi = y * NYAN_FRAME_HEIGHT / WINDOW_HEIGHT;
const char* line = frame[yi];
for ( size_t x = 0; x < WINDOW_WIDTH; x++ )
{
int xi = x * NYAN_FRAME_WIDTH / WINDOW_WIDTH;
char elem = line[xi];
const uint8_t* cc = nyan_palette[nyan_palette_of_char(elem)];
framebuffer[y * WINDOW_WIDTH + x] = make_color_a(cc[0], cc[1], cc[2], cc[3]);;
}
}
display_render_window(connection, WINDOW_ID, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, framebuffer);
free(framebuffer);
need_redraw = false;
}
if ( need_show )
{
display_show_window(connection, WINDOW_ID);
need_show = false;
}
struct display_event_handlers handlers;
memset(&handlers, 0, sizeof(handlers));
handlers.disconnect_handler = on_disconnect;
handlers.quit_handler = on_quit;
handlers.resize_handler = on_resize;
handlers.keyboard_handler = on_keyboard;
while ( display_poll_event(connection, &handlers) == 0 );
}
display_disconnect(connection);
return 0;
}

859
nyan/nyan.h Normal file
View File

@ -0,0 +1,859 @@
/*
* Pop Tart Cat animation frames
* Adapted from ToAruOS.
*/
#ifndef INCLUDE_NYAN_H
#define INCLUDE_NYAN_H
#include <stdint.h>
static const char* const nyan_frame0[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&'''++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++**''+'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'**'''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"+++#######++++++++''**''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"####################''''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"###=======########====''@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
"===;;;;;;;.=======;;;;'''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'***''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'**'','*',,,,,'*','**',,,,,,,,,,,,,,,,,,,,,",
";;;,,,,.,,;;;.;;;;,,,'''',,'',,,,,,,'',,'',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame1[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"+++#######++++++++'*''''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################****'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###################''**'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"###=======########==='''@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,,,,,,,,,,,,,,,,,",
";;;,,.,,,,;;;;;;;;,,,,''',,,'',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,..,,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame2[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,..,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"##+++++++########++++++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"######################''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################'''''@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"==#######========#'****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==================='''='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
";;=======;;;;;;;;======'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";.;;;;;;;;;;;;;;;;;;;;;'*'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
".,.;;;;;;,,,,,,,,;;;;;;'**',**',,,,,,**','**',,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame3[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"##+++++++########++++++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"#####################'''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"==#######========##****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"=================='*'=='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=='==='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;;'**','*',,,,,,'*','**',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame4[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'''++'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"+++#######+++++++'**''''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"#################'****''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"##################''''*'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"###=======########==='''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;''@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;''''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***'''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,..,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame5[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$-$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"+++#######++++++++'*''''@$$$$$$$'*************',,,,,,,,,,,,,,,,,",
"###################****'@$$$$$$-'***.'****.'**',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"###=======########==='''@$$$$$$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$$'***''''''**',,,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;''@@@$$$$$$$'*********',,,,,,,,,,,,,,,,,,.",
";;;;;;;;;;;;;;;;;;;;;'*''@@@@@@@@@@''''''''',,,,,,,,,,,,,,,,,,,.",
";;;;;;;;;;;;;;;;;;;;'***''''''''''''''''*',,,,,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,,,,,,,,,,,,,..,.",
",,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame6[] = {
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,..,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&'''&&'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*''+'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'**'''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"##+++++++########++'**''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"####################''''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"==#######========#####''@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;====='''@@@@@@@@@'*********',,,,,,,,,,,.,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'***''''''''''''''''''',,,,,,,,,,.,,,.,,,,,",
";;;;;;;;;;;;;;;;;;;;;'**'','*',,,,,'**,'**',,,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;'''',,'',,,,,,,'',,'',,,,,,,,,,,.,,,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame7[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"##+++++++########+'*''''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################****'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###################''**'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==#######========####'''@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;======''@@@@@@@@@@'*********',,,.,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;;''',,,'',,,,,,''',,''',,.,,,,.,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame8[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,..,...,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"+++#######++++++++#####'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"######################''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################'''''@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###=======########'****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==================='''='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;;'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;'*'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,,,,'**',**',,,,,,**'.'**',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,"};
static const char* const nyan_frame9[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"+++#######++++++++#####'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"#####################'''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###=======########=****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"=================='*'=='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"===;;;;;;;========;';;;'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,,,'**','*',,..,.**','**',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,''',,,'',,,,.,''',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,"};
static const char* const nyan_frame10[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'''++'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"##+++++++########'**''''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"#################'****''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"##################''''*'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"==#######========####'''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=====''@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;''''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***'''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;'**'.'**..,,,,'**''**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,''',''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame11[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$-$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"##+++++++########+'*''''@$$$$$$$'*************',,,,,,,,,,,,,,,,,",
"###################****'@$$$$$$-'***.'****.'**',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"==#######========####'''@$$$$$$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=.===''@@@$$$$$$$'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;.'*''@@@@@@@@@@''''''''',,,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***''''''''''''''''*',,,,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,.;;;'**','**,,,,,,'**''**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const* const nyan_frames[] = {
nyan_frame0,
nyan_frame1,
nyan_frame2,
nyan_frame3,
nyan_frame4,
nyan_frame5,
nyan_frame6,
nyan_frame7,
nyan_frame8,
nyan_frame9,
nyan_frame10,
nyan_frame11,
NULL
};
#define NYAN_FRAME_WIDTH 64
#define NYAN_FRAME_HEIGHT 64
static inline uint8_t nyan_palette_of_char(char c)
{
if ( c == ',' ) return 1;
if ( c == '.' ) return 2;
if ( c == '\'') return 3;
if ( c == '@' ) return 4;
if ( c == '$' ) return 5;
if ( c == '-' ) return 6;
if ( c == '>' ) return 7;
if ( c == '&' ) return 8;
if ( c == '+' ) return 9;
if ( c == '#' ) return 10;
if ( c == '=' ) return 11;
if ( c == ';' ) return 12;
if ( c == '*' ) return 13;
if ( c == '%' ) return 14;
return 0;
}
static const uint8_t nyan_palette[1+14][4] =
{
{ 0, 0, 0, 0 }, // 0; Unused entry
{ 0, 0, 97, 200 }, // 1: , = Blue background
{ 255, 255, 255, 255 }, // 2: . = White stars
{ 46, 52, 54, 255 }, // 3: ' = Black border
{ 255, 255, 215, 255 }, // 4: @ = Tan poptart
{ 215, 135, 175, 255 }, // 5: $ = Pink potart
{ 215, 0, 135, 255 }, // 6: - = Red poptart
{ 239, 41, 41, 255 }, // 7: > = Red rainbow
{ 255, 95, 0, 255 }, // 8: & = Orange rainbow
{ 252, 233, 79, 255 }, // 9: + = Yellow rainbow
{ 138, 226, 52, 255 }, // 10: # = Green rainbow
{ 0, 135, 255, 255 }, // 11: = = Light blue rainbow
{ 0, 0, 175, 255 }, // 12: ; = Dark blue rainbow
{ 85, 87, 83, 255 }, // 13: * = Grey cat face
{ 85, 87, 83, 255 }, // 14: % = Pink cheeks
};
#endif

View File

@ -7,7 +7,7 @@ ARCHIVE=$DISTNAME.$COMPRESSION
SHA256SUM=8ca680399df5ee8846a509c8a67c10ee1d5fc1a94456806a0ff9dd913b961db3
UPSTREAM_SITE=https://geoff.greer.fm/ag/releases
UPSTREAM_ARCHIVE=$ARCHIVE
RUNTIME_PROGRAMS=git
RUNTIME_DEPS=git
BUILD_SYSTEM=configure
CONFIGURE_ARGS=
MAKE_VARS='V=1'

View File

@ -1,11 +1,12 @@
NAME=diffutils
BUILD_LIBRARIES='libiconv? libintl?'
VERSION=3.8
VERSION=3.10
DISTNAME=$NAME-$VERSION
COMPRESSION=tar.xz
ARCHIVE=$DISTNAME.$COMPRESSION
SHA256SUM=a6bdd7d1b31266d11c4f4de6c1b748d4607ab0231af5188fc2533d0ae2438fec
SHA256SUM=90e5e93cc724e4ebe12ede80df1634063c7a855692685919bfe60b556c9bd09e
UPSTREAM_SITE=https://ftp.gnu.org/gnu/diffutils
UPSTREAM_ARCHIVE=$ARCHIVE
LICENSE=GPL-3.0-or-later
BUILD_SYSTEM=configure
MAKE_VARS='V=1'

View File

@ -1,3 +1,48 @@
diff -Paur --no-dereference -- e2fsprogs.upstream/MCONFIG.in e2fsprogs/MCONFIG.in
--- e2fsprogs.upstream/MCONFIG.in
+++ e2fsprogs/MCONFIG.in
@@ -138,7 +138,7 @@
LIBUUID = @LIBUUID@ @SOCKET_LIB@
LIBMAGIC = @MAGIC_LIB@
LIBFUSE = @FUSE_LIB@
-LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@STATIC_LIB_EXT@
+LIBSUPPORT = $(LIB)/libsupport@STATIC_LIB_EXT@ $(LIBINTL)
LIBBLKID = @LIBBLKID@ @PRIVATE_LIBS_CMT@ $(LIBUUID)
LIBINTL = @LIBINTL@
SYSLIBS = @LIBS@ @PTHREAD_LIBS@
@@ -154,7 +154,7 @@
STATIC_LIBE2P = $(LIB)/libe2p@STATIC_LIB_EXT@
STATIC_LIBEXT2FS = $(LIB)/libext2fs@STATIC_LIB_EXT@
STATIC_LIBUUID = @STATIC_LIBUUID@ @SOCKET_LIB@
-STATIC_LIBSUPPORT = $(LIBINTL) $(LIBSUPPORT)
+STATIC_LIBSUPPORT = $(LIBSUPPORT) $(LIBINTL)
STATIC_LIBBLKID = @STATIC_LIBBLKID@ $(STATIC_LIBUUID)
DEPSTATIC_LIBSS = $(LIB)/libss@STATIC_LIB_EXT@
DEPSTATIC_LIBCOM_ERR = $(LIB)/libcom_err@STATIC_LIB_EXT@
@@ -167,7 +167,7 @@
PROFILED_LIBE2P = $(LIB)/libe2p@PROFILED_LIB_EXT@
PROFILED_LIBEXT2FS = $(LIB)/libext2fs@PROFILED_LIB_EXT@
PROFILED_LIBUUID = @PROFILED_LIBUUID@ @SOCKET_LIB@
-PROFILED_LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@PROFILED_LIB_EXT@
+PROFILED_LIBSUPPORT = $(LIB)/libsupport@PROFILED_LIB_EXT@ $(LIBINTL)
PROFILED_LIBBLKID = @PROFILED_LIBBLKID@ $(PROFILED_LIBUUID)
DEPPROFILED_LIBSS = $(LIB)/libss@PROFILED_LIB_EXT@
DEPPROFILED_LIBCOM_ERR = $(LIB)/libcom_err@PROFILED_LIB_EXT@
diff -Paur --no-dereference -- e2fsprogs.upstream/Makefile.in e2fsprogs/Makefile.in
--- e2fsprogs.upstream/Makefile.in
+++ e2fsprogs/Makefile.in
@@ -135,6 +135,11 @@
$(RM) -rf autom4te.cache ext2ed/Makefile po/stamp-po \
asm_types.h config.log public_config.h parse-types.log
$(MAKE) distclean-local
+ $(RM) -f parse-types.log
+ $(RM) -f public_config.h
+ $(RM) -f util/install-symlink
+ $(RM) -f asm_types.h
+ $(RM) -f scrub/Makefile
realclean: realclean-recursive realclean-local
diff -Paur --no-dereference -- e2fsprogs.upstream/config/parse-types.sh e2fsprogs/config/parse-types.sh
--- e2fsprogs.upstream/config/parse-types.sh
+++ e2fsprogs/config/parse-types.sh
@ -23,7 +68,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/config/parse-types.sh e2fsprog
diff -Paur --no-dereference -- e2fsprogs.upstream/debugfs/debugfs.c e2fsprogs/debugfs/debugfs.c
--- e2fsprogs.upstream/debugfs/debugfs.c
+++ e2fsprogs/debugfs/debugfs.c
@@ -1818,7 +1818,11 @@
@@ -1816,7 +1816,11 @@
if (argc != nr)
goto usage;
@ -57,17 +102,17 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/e2fsck/journal.c e2fsprogs/e2f
#include <sys/mount.h>
#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
#endif
@@ -635,11 +634,11 @@
@@ -634,11 +633,11 @@
val + sizeof(struct ext4_fc_dentry_info),
darg->dname_len);
darg->dname[darg->dname_len] = 0;
- jbd_debug(1, "%s: %s, ino %lu, parent %lu\n",
- jbd_debug(1, "%s: %s, ino %u, parent %u\n",
+ jbd_debug(1, "%s: %s, ino %ju, parent %ju\n",
tag == EXT4_FC_TAG_CREAT ? "create" :
(tag == EXT4_FC_TAG_LINK ? "link" :
(tag == EXT4_FC_TAG_UNLINK ? "unlink" : "error")),
- darg->dname, darg->ino, darg->parent_ino);
+ darg->dname, (uintmax_t) darg->ino, (uintmax_t) darg->parent_ino);
le16_to_cpu(tl->fc_tag) == EXT4_FC_TAG_CREAT ? "create" :
(le16_to_cpu(tl->fc_tag) == EXT4_FC_TAG_LINK ? "link" :
(le16_to_cpu(tl->fc_tag) == EXT4_FC_TAG_UNLINK ? "unlink" :
- "error")), darg->dname, darg->ino, darg->parent_ino);
+ "error")), darg->dname, (uintmax_t)darg->ino, (uintmax_t)darg->parent_ino);
return 0;
}
@ -166,7 +211,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/e2fsck/sigcatcher.c e2fsprogs/
diff -Paur --no-dereference -- e2fsprogs.upstream/e2fsck/unix.c e2fsprogs/e2fsck/unix.c
--- e2fsprogs.upstream/e2fsck/unix.c
+++ e2fsprogs/e2fsck/unix.c
@@ -290,7 +290,7 @@
@@ -291,7 +291,7 @@
{
FILE *f;
DIR *d;
@ -175,7 +220,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/e2fsck/unix.c e2fsprogs/e2fsck
unsigned int acflag;
struct dirent* de;
@@ -604,8 +604,6 @@
@@ -605,8 +605,6 @@
return 0;
}
@ -184,7 +229,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/e2fsck/unix.c e2fsprogs/e2fsck
/*
* Make sure 0,1,2 file descriptors are open, so that we don't open
* the filesystem using the same file descriptor as stdout or stderr.
@@ -1129,21 +1127,21 @@
@@ -1130,21 +1128,21 @@
if (cflag) {
char *oldpath = getenv("PATH");
char *newpath;
@ -223,7 +268,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/e2fsck/util.c e2fsprogs/e2fsck
gettimeofday(&track->time_start, 0);
#ifdef HAVE_GETRUSAGE
#ifdef sun
@@ -442,7 +442,7 @@
@@ -451,7 +451,7 @@
} else
#endif
log_out(ctx, _("Memory used: %lluk, "),
@ -420,51 +465,6 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/blkid/devname.c e2fsprogs/
/*
* Find a dev struct in the cache by device name, if available.
*
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/e2p/fgetversion.c e2fsprogs/lib/e2p/fgetversion.c
--- e2fsprogs.upstream/lib/e2p/fgetversion.c
+++ e2fsprogs/lib/e2p/fgetversion.c
@@ -65,8 +65,6 @@
rc = syscall(SYS_fsctl, name, EXT2_IOC_GETVERSION, &ver, 0);
# endif /* !APPLE_DARWIN */
#else /* ! HAVE_EXT2_IOCTLS */
- extern int errno;
-
errno = EOPNOTSUPP;
#endif /* ! HAVE_EXT2_IOCTLS */
if (rc == 0)
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/e2p/fsetversion.c e2fsprogs/lib/e2p/fsetversion.c
--- e2fsprogs.upstream/lib/e2p/fsetversion.c
+++ e2fsprogs/lib/e2p/fsetversion.c
@@ -65,7 +65,6 @@
return syscall(SYS_fsctl, name, EXT2_IOC_SETVERSION, &ver, 0);
#endif
#else /* ! HAVE_EXT2_IOCTLS */
- extern int errno;
errno = EOPNOTSUPP;
return -1;
#endif /* ! HAVE_EXT2_IOCTLS */
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/e2p/getversion.c e2fsprogs/lib/e2p/getversion.c
--- e2fsprogs.upstream/lib/e2p/getversion.c
+++ e2fsprogs/lib/e2p/getversion.c
@@ -35,7 +35,6 @@
*version = ver;
return r;
#else /* ! HAVE_EXT2_IOCTLS */
- extern int errno;
errno = EOPNOTSUPP;
return -1;
#endif /* ! HAVE_EXT2_IOCTLS */
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/e2p/setversion.c e2fsprogs/lib/e2p/setversion.c
--- e2fsprogs.upstream/lib/e2p/setversion.c
+++ e2fsprogs/lib/e2p/setversion.c
@@ -34,7 +34,6 @@
ver = (int) version;
return ioctl (fd, EXT2_IOC_SETVERSION, &ver);
#else /* ! HAVE_EXT2_IOCTLS */
- extern int errno;
errno = EOPNOTSUPP;
return -1;
#endif /* ! HAVE_EXT2_IOCTLS */
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/ext2fs/bitmaps.c e2fsprogs/lib/ext2fs/bitmaps.c
--- e2fsprogs.upstream/lib/ext2fs/bitmaps.c
+++ e2fsprogs/lib/ext2fs/bitmaps.c
@ -779,8 +779,8 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/ext2fs/tdb.c e2fsprogs/lib
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/ss/help.c e2fsprogs/lib/ss/help.c
--- e2fsprogs.upstream/lib/ss/help.c
+++ e2fsprogs/lib/ss/help.c
@@ -24,9 +24,7 @@
extern int errno;
@@ -22,9 +22,7 @@
#include <errno.h>
#endif
#include <fcntl.h>
-#include <sys/param.h>
@ -803,7 +803,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/ss/listen.c e2fsprogs/lib/
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/ss/pager.c e2fsprogs/lib/ss/pager.c
--- e2fsprogs.upstream/lib/ss/pager.c
+++ e2fsprogs/lib/ss/pager.c
@@ -27,7 +27,6 @@
@@ -25,7 +25,6 @@
#include "ss_internal.h"
#include <stdio.h>
#include <sys/types.h>
@ -825,7 +825,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/support/quotaio.c e2fsprog
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/gen_uuid.c e2fsprogs/lib/uuid/gen_uuid.c
--- e2fsprogs.upstream/lib/uuid/gen_uuid.c
+++ e2fsprogs/lib/uuid/gen_uuid.c
@@ -101,9 +101,22 @@
@@ -96,9 +96,22 @@
#include "uuidP.h"
#include "uuidd.h"
@ -851,8 +851,8 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/gen_uuid.c e2fsprogs/
#endif
#ifdef TLS
@@ -138,94 +151,13 @@
#endif
@@ -112,94 +125,13 @@
THREAD_LOCAL unsigned short jrand_seed[3];
#endif
-static int get_random_fd(void)
@ -947,7 +947,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/gen_uuid.c e2fsprogs/
}
/*
@@ -238,7 +170,9 @@
@@ -212,7 +144,9 @@
*/
static int get_node_id(unsigned char *node_id)
{
@ -958,7 +958,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/gen_uuid.c e2fsprogs/
int sd;
struct ifreq ifr, *ifrp;
struct ifconf ifc;
@@ -695,14 +629,10 @@
@@ -669,14 +603,10 @@
/*
* This is the generic front-end to uuid_generate_random and
@ -979,7 +979,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/gen_uuid.c e2fsprogs/
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/tst_uuid.c e2fsprogs/lib/uuid/tst_uuid.c
--- e2fsprogs.upstream/lib/uuid/tst_uuid.c
+++ e2fsprogs/lib/uuid/tst_uuid.c
@@ -143,9 +143,9 @@
@@ -137,9 +137,9 @@
tv.tv_sec = 0;
tv.tv_usec = 0;
time_reg = uuid_time(buf, &tv);
@ -995,7 +995,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/tst_uuid.c e2fsprogs/
diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/uuid_time.c e2fsprogs/lib/uuid/uuid_time.c
--- e2fsprogs.upstream/lib/uuid/uuid_time.c
+++ e2fsprogs/lib/uuid/uuid_time.c
@@ -165,7 +165,7 @@
@@ -159,7 +159,7 @@
printf("Warning: not a time-based UUID, so UUID time "
"decoding will likely not work!\n");
}
@ -1004,132 +1004,6 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/lib/uuid/uuid_time.c e2fsprogs
ctime(&time_reg));
return 0;
diff -Paur --no-dereference -- e2fsprogs.upstream/Makefile.in e2fsprogs/Makefile.in
--- e2fsprogs.upstream/Makefile.in
+++ e2fsprogs/Makefile.in
@@ -135,6 +135,11 @@
$(RM) -rf autom4te.cache ext2ed/Makefile po/stamp-po \
asm_types.h config.log public_config.h parse-types.log
$(MAKE) distclean-local
+ $(RM) -f parse-types.log
+ $(RM) -f public_config.h
+ $(RM) -f util/install-symlink
+ $(RM) -f asm_types.h
+ $(RM) -f scrub/Makefile
realclean: realclean-recursive realclean-local
diff -Paur --no-dereference -- e2fsprogs.upstream/MCONFIG.in e2fsprogs/MCONFIG.in
--- e2fsprogs.upstream/MCONFIG.in
+++ e2fsprogs/MCONFIG.in
@@ -137,7 +137,7 @@
LIBUUID = @LIBUUID@ @SOCKET_LIB@
LIBMAGIC = @MAGIC_LIB@
LIBFUSE = @FUSE_LIB@
-LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@STATIC_LIB_EXT@
+LIBSUPPORT = $(LIB)/libsupport@STATIC_LIB_EXT@ $(LIBINTL)
LIBBLKID = @LIBBLKID@ @PRIVATE_LIBS_CMT@ $(LIBUUID)
LIBINTL = @LIBINTL@
SYSLIBS = @LIBS@ @PTHREAD_LIBS@
@@ -153,7 +153,7 @@
STATIC_LIBE2P = $(LIB)/libe2p@STATIC_LIB_EXT@
STATIC_LIBEXT2FS = $(LIB)/libext2fs@STATIC_LIB_EXT@
STATIC_LIBUUID = @STATIC_LIBUUID@ @SOCKET_LIB@
-STATIC_LIBSUPPORT = $(LIBINTL) $(LIBSUPPORT)
+STATIC_LIBSUPPORT = $(LIBSUPPORT) $(LIBINTL)
STATIC_LIBBLKID = @STATIC_LIBBLKID@ $(STATIC_LIBUUID)
DEPSTATIC_LIBSS = $(LIB)/libss@STATIC_LIB_EXT@
DEPSTATIC_LIBCOM_ERR = $(LIB)/libcom_err@STATIC_LIB_EXT@
@@ -166,7 +166,7 @@
PROFILED_LIBE2P = $(LIB)/libe2p@PROFILED_LIB_EXT@
PROFILED_LIBEXT2FS = $(LIB)/libext2fs@PROFILED_LIB_EXT@
PROFILED_LIBUUID = @PROFILED_LIBUUID@ @SOCKET_LIB@
-PROFILED_LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@PROFILED_LIB_EXT@
+PROFILED_LIBSUPPORT = $(LIB)/libsupport@PROFILED_LIB_EXT@ $(LIBINTL)
PROFILED_LIBBLKID = @PROFILED_LIBBLKID@ $(PROFILED_LIBUUID)
DEPPROFILED_LIBSS = $(LIB)/libss@PROFILED_LIB_EXT@
DEPPROFILED_LIBCOM_ERR = $(LIB)/libcom_err@PROFILED_LIB_EXT@
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/badblocks.c e2fsprogs/misc/badblocks.c
--- e2fsprogs.upstream/misc/badblocks.c
+++ e2fsprogs/misc/badblocks.c
@@ -331,7 +331,7 @@
if (pattern == (unsigned int) ~0) {
for (ptr = buffer; ptr < buffer + n; ptr++) {
- (*ptr) = random() % (1 << (8 * sizeof(char)));
+ (*ptr) = arc4random_uniform(1 << (8 * sizeof(char)));
}
if (s_flag | v_flag)
fputs(_("Testing with random pattern: "), stderr);
@@ -1076,7 +1076,6 @@
textdomain(NLS_CAT_NAME);
set_com_err_gettext(gettext);
#endif
- srandom((unsigned int)time(NULL)); /* simple randomness is enough */
test_func = test_ro;
/* Determine the system page size if possible */
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/chattr.c e2fsprogs/misc/chattr.c
--- e2fsprogs.upstream/misc/chattr.c
+++ e2fsprogs/misc/chattr.c
@@ -31,7 +31,6 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
-#include <sys/param.h>
#include <sys/stat.h>
#include "ext2fs/ext2_fs.h"
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/create_inode.c e2fsprogs/misc/create_inode.c
--- e2fsprogs.upstream/misc/create_inode.c
+++ e2fsprogs/misc/create_inode.c
@@ -310,6 +310,7 @@
inode.i_atime = inode.i_ctime = inode.i_mtime =
fs->now ? fs->now : time(0);
+#ifndef __sortix__
if (filetype != S_IFIFO) {
devmajor = major(st_rdev);
devminor = minor(st_rdev);
@@ -323,6 +324,7 @@
((devminor & ~0xff) << 12);
}
}
+#endif
inode.i_links_count = 1;
retval = ext2fs_write_new_inode(fs, ino, &inode);
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/e2fuzz.c e2fsprogs/misc/e2fuzz.c
--- e2fsprogs.upstream/misc/e2fuzz.c
+++ e2fsprogs/misc/e2fuzz.c
@@ -171,7 +171,7 @@
uint8_t *px = (uint8_t *)&x;
for (i = 0; i < sizeof(x); i++)
- px[i] = random();
+ px[i] = arc4random() & 0xff;
return min + (uint64_t)((double)(max - min) *
(x / ((double) UINT64_MAX + 1.0)));
@@ -259,7 +259,6 @@
perror(fsname);
goto fail;
}
- srandom(getseed());
hsize = fs->blocksize * ext2fs_blocks_count(fs->super);
if (user_corrupt_bytes > 0)
count = user_corrupt_bytes;
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/lsattr.c e2fsprogs/misc/lsattr.c
--- e2fsprogs.upstream/misc/lsattr.c
+++ e2fsprogs/misc/lsattr.c
@@ -36,7 +36,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include "ext2fs/ext2_fs.h"
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/Makefile.in e2fsprogs/misc/Makefile.in
--- e2fsprogs.upstream/misc/Makefile.in
+++ e2fsprogs/misc/Makefile.in
@ -1186,6 +1060,87 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/Makefile.in e2fsprogs/mis
install-strip: install
$(Q) for i in $(SPROGS); do \
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/badblocks.c e2fsprogs/misc/badblocks.c
--- e2fsprogs.upstream/misc/badblocks.c
+++ e2fsprogs/misc/badblocks.c
@@ -331,7 +331,7 @@
if (pattern == (unsigned int) ~0) {
for (ptr = buffer; ptr < buffer + n; ptr++) {
- (*ptr) = random() % (1 << (8 * sizeof(char)));
+ (*ptr) = arc4random_uniform(1 << (8 * sizeof(char)));
}
if (s_flag | v_flag)
fputs(_("Testing with random pattern: "), stderr);
@@ -1079,7 +1079,6 @@
textdomain(NLS_CAT_NAME);
set_com_err_gettext(gettext);
#endif
- srandom((unsigned int)time(NULL)); /* simple randomness is enough */
test_func = test_ro;
/* Determine the system page size if possible */
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/chattr.c e2fsprogs/misc/chattr.c
--- e2fsprogs.upstream/misc/chattr.c
+++ e2fsprogs/misc/chattr.c
@@ -31,7 +31,6 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
-#include <sys/param.h>
#include <sys/stat.h>
#include "ext2fs/ext2_fs.h"
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/create_inode.c e2fsprogs/misc/create_inode.c
--- e2fsprogs.upstream/misc/create_inode.c
+++ e2fsprogs/misc/create_inode.c
@@ -312,6 +312,7 @@
inode.i_atime = inode.i_ctime = inode.i_mtime =
fs->now ? fs->now : time(0);
+#ifndef __sortix__
if (filetype != S_IFIFO) {
devmajor = major(st_rdev);
devminor = minor(st_rdev);
@@ -325,6 +326,7 @@
((devminor & ~0xff) << 12);
}
}
+#endif
inode.i_links_count = 1;
retval = ext2fs_write_new_inode(fs, ino, &inode);
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/e2fuzz.c e2fsprogs/misc/e2fuzz.c
--- e2fsprogs.upstream/misc/e2fuzz.c
+++ e2fsprogs/misc/e2fuzz.c
@@ -171,7 +171,7 @@
uint8_t *px = (uint8_t *)&x;
for (i = 0; i < sizeof(x); i++)
- px[i] = random();
+ px[i] = arc4random() & 0xff;
return min + (uint64_t)((double)(max - min) *
(x / ((double) UINT64_MAX + 1.0)));
@@ -259,7 +259,6 @@
perror(fsname);
goto fail;
}
- srandom(getseed());
hsize = fs->blocksize * ext2fs_blocks_count(fs->super);
if (user_corrupt_bytes > 0)
count = user_corrupt_bytes;
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/lsattr.c e2fsprogs/misc/lsattr.c
--- e2fsprogs.upstream/misc/lsattr.c
+++ e2fsprogs/misc/lsattr.c
@@ -36,7 +36,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include "ext2fs/ext2_fs.h"
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mke2fs.c e2fsprogs/misc/mke2fs.c
--- e2fsprogs.upstream/misc/mke2fs.c
+++ e2fsprogs/misc/mke2fs.c
@ -1207,7 +1162,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mke2fs.c e2fsprogs/misc/m
#include <blkid/blkid.h>
#include "ext2fs/ext2_fs.h"
@@ -709,7 +713,7 @@
@@ -711,7 +715,7 @@
100.0 * ext2fs_r_blocks_count(s) / ext2fs_blocks_count(s));
printf(_("First data block=%u\n"), s->s_first_data_block);
if (root_uid != 0 || root_gid != 0)
@ -1216,7 +1171,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mke2fs.c e2fsprogs/misc/m
if (s->s_reserved_gdt_blocks)
printf(_("Maximum filesystem blocks=%lu\n"),
(s->s_reserved_gdt_blocks + fs->desc_blocks) *
@@ -792,8 +796,6 @@
@@ -794,8 +798,6 @@
return 1;
}
@ -1225,7 +1180,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mke2fs.c e2fsprogs/misc/m
static void parse_extended_opts(struct ext2_super_block *param,
const char *opts)
{
@@ -1196,6 +1198,8 @@
@@ -1220,6 +1222,8 @@
static const char *config_fn[] = { ROOT_SYSCONFDIR "/mke2fs.conf", 0 };
@ -1234,7 +1189,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mke2fs.c e2fsprogs/misc/m
static void edit_feature(const char *str, __u32 *compat_array)
{
if (!str)
@@ -1578,27 +1582,27 @@
@@ -1603,27 +1607,27 @@
int fs_features_size = 0;
int use_bsize;
char *newpath;
@ -1267,7 +1222,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mke2fs.c e2fsprogs/misc/m
/* Determine the system page size if possible */
#ifdef HAVE_SYSCONF
@@ -1616,6 +1620,8 @@
@@ -1644,6 +1648,8 @@
config_fn[0] = tmp;
profile_set_syntax_err_cb(syntax_err_report);
retval = profile_init(config_fn, &profile);
@ -1301,7 +1256,18 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/misc/mklost+found.c e2fsprogs/
diff -Paur --no-dereference -- e2fsprogs.upstream/misc/tune2fs.c e2fsprogs/misc/tune2fs.c
--- e2fsprogs.upstream/misc/tune2fs.c
+++ e2fsprogs/misc/tune2fs.c
@@ -3118,7 +3118,7 @@
@@ -97,6 +97,10 @@
#define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid)
#endif
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
extern int ask_yn(const char *string, int def);
const char *program_name = "tune2fs";
@@ -3363,7 +3367,7 @@
if (c_flag) {
if (max_mount_count == 65536)
max_mount_count = EXT2_DFL_MAX_MNT_COUNT +
@ -1409,7 +1375,7 @@ diff -Paur --no-dereference -- e2fsprogs.upstream/resize/resource_track.c e2fspr
gettimeofday(&track->time_start, 0);
#ifdef HAVE_GETRUSAGE
#ifdef sun
@@ -85,7 +85,7 @@
@@ -92,7 +92,7 @@
kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks));
#else
printf("Memory used: %lu, ",

Some files were not shown because too many files have changed in this diff Show More