Compare commits
33 commits
7c6f573e04
...
92da8d9ed8
Author | SHA1 | Date | |
---|---|---|---|
|
92da8d9ed8 | ||
|
e78f22ed7c | ||
|
a7c16c8feb | ||
|
c220caa235 | ||
|
5c7cc54884 | ||
|
09fb39876d | ||
|
aeb1b7b47d | ||
|
15b28b8fca | ||
|
872996f2d5 | ||
|
9f835dcc11 | ||
|
03d3125e8a | ||
|
0aa41fa382 | ||
|
7b061e24f2 | ||
|
92b564ab3f | ||
|
f825f2e18d | ||
|
44474801a8 | ||
|
e5185ad14e | ||
|
c948ba8c7e | ||
|
958d9cc0cb | ||
|
5ea356d337 | ||
|
ddfe072afe | ||
|
a0f0bd709c | ||
|
0320f6361a | ||
|
d3f13da544 | ||
|
6ebaef18e1 | ||
|
60da994de7 | ||
|
134cccd8d4 | ||
|
aeb26a561d | ||
|
07c9f6a052 | ||
|
89d29b8815 | ||
|
4533a2ade2 | ||
|
6ac0061380 | ||
|
6303a59439 |
148 changed files with 11675 additions and 2739 deletions
74
Makefile
74
Makefile
|
@ -21,13 +21,16 @@ dnsconfig \
|
||||||
editor \
|
editor \
|
||||||
ext \
|
ext \
|
||||||
games \
|
games \
|
||||||
|
host \
|
||||||
hostname \
|
hostname \
|
||||||
ifconfig \
|
ifconfig \
|
||||||
init \
|
init \
|
||||||
|
irc \
|
||||||
|
iso9660 \
|
||||||
kblayout \
|
kblayout \
|
||||||
kblayout-compiler \
|
kblayout-compiler \
|
||||||
login \
|
login \
|
||||||
mkinitrd \
|
nyan \
|
||||||
ping \
|
ping \
|
||||||
regress \
|
regress \
|
||||||
rw \
|
rw \
|
||||||
|
@ -69,10 +72,13 @@ else
|
||||||
SORTIX_INCLUDE_SOURCE?=yes
|
SORTIX_INCLUDE_SOURCE?=yes
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ISO_MOUNT?=no
|
||||||
|
|
||||||
include build-aux/dirs.mak
|
include build-aux/dirs.mak
|
||||||
|
|
||||||
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
|
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
|
||||||
|
|
||||||
|
CHAIN_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).chain.tar
|
||||||
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.tar
|
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.tar
|
||||||
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.tar
|
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.tar
|
||||||
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.tar
|
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.tar
|
||||||
|
@ -101,7 +107,6 @@ sysmerge-wait: sysroot
|
||||||
clean-build-tools:
|
clean-build-tools:
|
||||||
$(MAKE) -C carray clean
|
$(MAKE) -C carray clean
|
||||||
$(MAKE) -C kblayout-compiler clean
|
$(MAKE) -C kblayout-compiler clean
|
||||||
$(MAKE) -C mkinitrd clean
|
|
||||||
$(MAKE) -C sf clean
|
$(MAKE) -C sf clean
|
||||||
$(MAKE) -C tix clean
|
$(MAKE) -C tix clean
|
||||||
|
|
||||||
|
@ -109,7 +114,6 @@ clean-build-tools:
|
||||||
build-tools:
|
build-tools:
|
||||||
$(MAKE) -C carray
|
$(MAKE) -C carray
|
||||||
$(MAKE) -C kblayout-compiler
|
$(MAKE) -C kblayout-compiler
|
||||||
$(MAKE) -C mkinitrd
|
|
||||||
$(MAKE) -C sf
|
$(MAKE) -C sf
|
||||||
$(MAKE) -C tix
|
$(MAKE) -C tix
|
||||||
|
|
||||||
|
@ -117,7 +121,6 @@ build-tools:
|
||||||
install-build-tools:
|
install-build-tools:
|
||||||
$(MAKE) -C carray install
|
$(MAKE) -C carray install
|
||||||
$(MAKE) -C kblayout-compiler install
|
$(MAKE) -C kblayout-compiler install
|
||||||
$(MAKE) -C mkinitrd install
|
|
||||||
$(MAKE) -C sf install
|
$(MAKE) -C sf install
|
||||||
$(MAKE) -C tix install
|
$(MAKE) -C tix install
|
||||||
|
|
||||||
|
@ -235,6 +238,8 @@ sysroot-system: sysroot-fsh sysroot-base-headers
|
||||||
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
|
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
|
||||||
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
|
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
|
||||||
echo /etc/os-release >> "$(SYSROOT)/tix/manifest/system"
|
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"
|
find share | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
|
||||||
cp -RT share "$(SYSROOT)/share"
|
cp -RT share "$(SYSROOT)/share"
|
||||||
export SYSROOT="$(SYSROOT)" && \
|
export SYSROOT="$(SYSROOT)" && \
|
||||||
|
@ -272,6 +277,7 @@ else ifneq ($(SORTIX_INCLUDE_SOURCE),no)
|
||||||
cp Makefile -t "$(SYSROOT)/src"
|
cp Makefile -t "$(SYSROOT)/src"
|
||||||
cp README -t "$(SYSROOT)/src"
|
cp README -t "$(SYSROOT)/src"
|
||||||
cp -RT build-aux "$(SYSROOT)/src/build-aux"
|
cp -RT build-aux "$(SYSROOT)/src/build-aux"
|
||||||
|
cp -RT etc "$(SYSROOT)/src/etc"
|
||||||
cp -RT share "$(SYSROOT)/src/share"
|
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)
|
(for D in $(MODULES); do (cp -R $$D -t "$(SYSROOT)/src" && $(MAKE) -C "$(SYSROOT)/src/$$D" clean) || exit $$?; done)
|
||||||
endif
|
endif
|
||||||
|
@ -444,6 +450,25 @@ release-all-archs:
|
||||||
|
|
||||||
# Initial ramdisk
|
# 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
|
$(LIVE_INITRD): sysroot
|
||||||
mkdir -p `dirname $(LIVE_INITRD)`
|
mkdir -p `dirname $(LIVE_INITRD)`
|
||||||
rm -rf $(LIVE_INITRD).d
|
rm -rf $(LIVE_INITRD).d
|
||||||
|
@ -455,6 +480,9 @@ $(LIVE_INITRD): sysroot
|
||||||
echo "include /etc/default/passwd.d/*" >> $(LIVE_INITRD).d/etc/passwd
|
echo "include /etc/default/passwd.d/*" >> $(LIVE_INITRD).d/etc/passwd
|
||||||
echo "root::0:root" > $(LIVE_INITRD).d/etc/group
|
echo "root::0:root" > $(LIVE_INITRD).d/etc/group
|
||||||
echo "include /etc/default/group.d/*" >> $(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
|
mkdir -p $(LIVE_INITRD).d/root -m 700
|
||||||
cp -RT "$(SYSROOT)/etc/skel" $(LIVE_INITRD).d/root
|
cp -RT "$(SYSROOT)/etc/skel" $(LIVE_INITRD).d/root
|
||||||
(echo "You can view the documentation for new users by typing:" && \
|
(echo "You can view the documentation for new users by typing:" && \
|
||||||
|
@ -490,6 +518,42 @@ $(SORTIX_BUILDS_DIR):
|
||||||
|
|
||||||
# Bootable images
|
# 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)
|
$(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
|
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
|
||||||
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
|
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
|
||||||
|
@ -530,6 +594,8 @@ else # none
|
||||||
endif
|
endif
|
||||||
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
|
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: iso
|
.PHONY: iso
|
||||||
iso: $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso
|
iso: $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso
|
||||||
|
|
||||||
|
|
|
@ -124,9 +124,9 @@ endif
|
||||||
DEFAULT_GENERIC_OPTLEVEL_BASE:=-Os -s
|
DEFAULT_GENERIC_OPTLEVEL_BASE:=-Os -s
|
||||||
DEFAULT_OPTLEVEL_FOR_BUILD:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
|
DEFAULT_OPTLEVEL_FOR_BUILD:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
|
||||||
ifeq ($(BUILD_IS_SORTIX),1)
|
ifeq ($(BUILD_IS_SORTIX),1)
|
||||||
DEFAULT_OPTLEVEL_FOR_BUILD+=
|
DEFAULT_OPTLEVEL_FOR_BUILD+=-fsanitize=undefined -fstack-protector-all
|
||||||
endif
|
endif
|
||||||
DEFAULT_OPTLEVEL:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
|
DEFAULT_OPTLEVEL:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
|
||||||
ifeq ($(HOST_IS_SORTIX),1)
|
ifeq ($(HOST_IS_SORTIX),1)
|
||||||
DEFAULT_OPTLEVEL+=
|
DEFAULT_OPTLEVEL+=-fsanitize=undefined -fstack-protector-all
|
||||||
endif
|
endif
|
||||||
|
|
21
build-aux/config.mak
Normal file
21
build-aux/config.mak
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
.PHONY: clean-tests
|
||||||
|
clean-tests:
|
||||||
|
rm -rf tests
|
||||||
|
rm -f config.h
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean: clean-tests
|
||||||
|
|
||||||
|
config.h: $(addprefix tests/,$(addsuffix .h,$(TESTS)))
|
||||||
|
cat tests/*.h > config.h
|
||||||
|
|
||||||
|
tests/%.h: ../build-aux/tests/%.c
|
||||||
|
@if [ ! -d tests ]; then mkdir -p tests; fi
|
||||||
|
@ln -sf ../../build-aux/tests/$*.c tests/$*.c
|
||||||
|
@if $(CC) $(CFLAGS) $(CPPFLAGS) -Werror=incompatible-pointer-types -c tests/$*.c -o /dev/null 2>tests/$*.log; then \
|
||||||
|
echo "# tests/$*: Yes" && tail -n 1 $< > $@; \
|
||||||
|
else \
|
||||||
|
echo "# tests/$*: No" && true > $@; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
-include ../build-aux/tests/*.d
|
|
@ -23,8 +23,9 @@ set -e
|
||||||
this=$(which -- "$0")
|
this=$(which -- "$0")
|
||||||
thisdir=$(dirname -- "$this")
|
thisdir=$(dirname -- "$this")
|
||||||
|
|
||||||
platform=
|
|
||||||
directory=
|
directory=
|
||||||
|
mount=false
|
||||||
|
platform=
|
||||||
version=
|
version=
|
||||||
|
|
||||||
dashdash=
|
dashdash=
|
||||||
|
@ -44,8 +45,9 @@ for argument do
|
||||||
|
|
||||||
case $dashdash$argument in
|
case $dashdash$argument in
|
||||||
--) dashdash=yes ;;
|
--) dashdash=yes ;;
|
||||||
--platform=*) platform=$parameter ;;
|
|
||||||
--platform) previous_option=platform ;;
|
--platform) previous_option=platform ;;
|
||||||
|
--platform=*) platform=$parameter ;;
|
||||||
|
--mount) mount=true ;;
|
||||||
--version=*) version=$parameter ;;
|
--version=*) version=$parameter ;;
|
||||||
--version) previous_option=version ;;
|
--version) previous_option=version ;;
|
||||||
-*) echo "$0: unrecognized option $argument" >&2
|
-*) echo "$0: unrecognized option $argument" >&2
|
||||||
|
@ -111,13 +113,23 @@ isinset() {
|
||||||
cd "$directory"
|
cd "$directory"
|
||||||
|
|
||||||
kernel=$(maybe_compressed boot/sortix.bin)
|
kernel=$(maybe_compressed boot/sortix.bin)
|
||||||
live_initrd=$(maybe_compressed boot/live.tar)
|
if $mount; then
|
||||||
overlay_initrd=$(maybe_compressed boot/overlay.tar)
|
initrd=$(maybe_compressed boot/sortix.initrd)
|
||||||
src_initrd=$(maybe_compressed boot/src.tar)
|
initrds=$initrd
|
||||||
system_initrd=$(maybe_compressed boot/system.tar)
|
else
|
||||||
ports=$(ls repository |
|
live_initrd=$(maybe_compressed boot/live.tar)
|
||||||
grep -E '\.tix\.tar\.xz$' |
|
overlay_initrd=$(maybe_compressed boot/overlay.tar)
|
||||||
sed -E 's/\.tix\.tar\.xz$//')
|
src_initrd=$(maybe_compressed boot/src.tar)
|
||||||
|
system_initrd=$(maybe_compressed boot/system.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$//')
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p boot/grub
|
mkdir -p boot/grub
|
||||||
mkdir -p boot/grub/init
|
mkdir -p boot/grub/init
|
||||||
|
@ -183,6 +195,12 @@ fi
|
||||||
|
|
||||||
set version="$version"
|
set version="$version"
|
||||||
set machine="$machine"
|
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 base_menu_title="Sortix \$version for \$machine"
|
||||||
set menu_title="\$base_menu_title"
|
set menu_title="\$base_menu_title"
|
||||||
set title_single_user='live environment'
|
set title_single_user='live environment'
|
||||||
|
@ -204,6 +222,8 @@ set enable_sshd=false
|
||||||
|
|
||||||
export version
|
export version
|
||||||
export machine
|
export machine
|
||||||
|
export mount
|
||||||
|
export chain
|
||||||
export base_menu_title
|
export base_menu_title
|
||||||
export menu_title
|
export menu_title
|
||||||
export title_single_user
|
export title_single_user
|
||||||
|
@ -304,9 +324,10 @@ esac
|
||||||
cat << EOF
|
cat << EOF
|
||||||
hook_kernel_pre
|
hook_kernel_pre
|
||||||
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
|
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
|
||||||
multiboot /$kernel \$no_random_seed \$enable_network_drivers "\$@"
|
multiboot /$kernel \$no_random_seed \$enable_network_drivers \$chain "\$@"
|
||||||
echo done
|
echo done
|
||||||
hook_kernel_post
|
hook_kernel_post
|
||||||
|
# TODO: Injecting configuration doesn't work for mounted cdroms.
|
||||||
if ! \$enable_dhclient; then
|
if ! \$enable_dhclient; then
|
||||||
echo -n "Disabling dhclient ... "
|
echo -n "Disabling dhclient ... "
|
||||||
module /boot/grub/init/furthermore --create-to /etc/init/network
|
module /boot/grub/init/furthermore --create-to /etc/init/network
|
||||||
|
@ -331,7 +352,7 @@ cat << EOF
|
||||||
echo done
|
echo done
|
||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
for initrd in $system_initrd $src_initrd $live_initrd $overlay_initrd; do
|
for initrd in $initrds; do
|
||||||
if [ "$initrd" = "$src_initrd" ]; then
|
if [ "$initrd" = "$src_initrd" ]; then
|
||||||
cat << EOF
|
cat << EOF
|
||||||
if \$enable_src; then
|
if \$enable_src; then
|
||||||
|
@ -427,9 +448,11 @@ menuentry "\$title_sysupgrade" '-- /sbin/init --target=sysupgrade'
|
||||||
|
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
|
||||||
|
if ! $mount; then
|
||||||
menuentry "Select ports..." {
|
menuentry "Select ports..." {
|
||||||
configfile /boot/grub/ports.cfg
|
configfile /boot/grub/ports.cfg
|
||||||
}
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
menuentry "Advanced..." {
|
menuentry "Advanced..." {
|
||||||
configfile /boot/grub/advanced.cfg
|
configfile /boot/grub/advanced.cfg
|
||||||
|
@ -461,6 +484,7 @@ else
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! $mount; then
|
||||||
if "\$enable_src"; then
|
if "\$enable_src"; then
|
||||||
menuentry "Disable loading source code" {
|
menuentry "Disable loading source code" {
|
||||||
enable_src=false
|
enable_src=false
|
||||||
|
@ -472,6 +496,7 @@ else
|
||||||
configfile /boot/grub/advanced.cfg
|
configfile /boot/grub/advanced.cfg
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "\$enable_network_drivers" = --disable-network-drivers ]; then
|
if [ "\$enable_network_drivers" = --disable-network-drivers ]; then
|
||||||
menuentry "Enable networking drivers" {
|
menuentry "Enable networking drivers" {
|
||||||
|
@ -485,6 +510,7 @@ else
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! $mount; then
|
||||||
if \$enable_dhclient; then
|
if \$enable_dhclient; then
|
||||||
menuentry "Disable DHCP client" {
|
menuentry "Disable DHCP client" {
|
||||||
enable_dhclient=false
|
enable_dhclient=false
|
||||||
|
@ -524,6 +550,7 @@ fi
|
||||||
menuentry "Select binary packages..." {
|
menuentry "Select binary packages..." {
|
||||||
configfile /boot/grub/tix.cfg
|
configfile /boot/grub/tix.cfg
|
||||||
}
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
hook_advanced_menu_post
|
hook_advanced_menu_post
|
||||||
EOF
|
EOF
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
set_minimal="cut dash e2fsprogs grep grub mdocml sed xargs"
|
set_minimal="cut dash e2fsprogs grep grub libssl mdocml sed signify tar wget xargs xorriso xz"
|
||||||
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libssl libstdc++ nano ntpd make patch pkg-config python ssh tar vim wget xz xorriso"
|
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libstdc++ make m4 nano ntpd patch perl pkg-config python ssh texinfo vim"
|
||||||
sets="basic minimal"
|
sets="basic minimal"
|
||||||
|
|
8
build-aux/tests/have-explicit_bzero.c
Normal file
8
build-aux/tests/have-explicit_bzero.c
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
void (*ptr)(void*, size_t) = explicit_bzero;
|
||||||
|
return ptr ? 0 : 1;
|
||||||
|
}
|
||||||
|
#define HAVE_EXPLICIT_BZERO 1
|
8
build-aux/tests/have-reallocarray.c
Normal file
8
build-aux/tests/have-reallocarray.c
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
void* (*ptr)(void*, size_t, size_t) = reallocarray;
|
||||||
|
return ptr ? 0 : 1;
|
||||||
|
}
|
||||||
|
#define HAVE_REALLOCARRAY 1
|
8
build-aux/tests/have-strlcat.c
Normal file
8
build-aux/tests/have-strlcat.c
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
size_t (*ptr)(char*, const char*, size_t) = strlcat;
|
||||||
|
return ptr ? 0 : 1;
|
||||||
|
}
|
||||||
|
#define HAVE_STRLCAT 1
|
8
build-aux/tests/have-strlcpy.c
Normal file
8
build-aux/tests/have-strlcpy.c
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
size_t (*ptr)(char*, const char*, size_t) = strlcpy;
|
||||||
|
return ptr ? 0 : 1;
|
||||||
|
}
|
||||||
|
#define HAVE_STRLCPY 1
|
|
@ -1,2 +1,6 @@
|
||||||
VERSION=1.1dev
|
VERSION=1.1dev
|
||||||
|
CHANNEL?=volatile
|
||||||
RELEASE?=$(VERSION)
|
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
|
||||||
|
|
2
etc/signify/sortix-1.1.pub
Normal file
2
etc/signify/sortix-1.1.pub
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
untrusted comment: signify public key
|
||||||
|
RWQiTQbFzyZJVobf/pn53Jp3njhRB9DgwkMaNakCpDE9RaTABMjlbz9W
|
2
etc/signify/sortix-1.1dev.pub
Normal file
2
etc/signify/sortix-1.1dev.pub
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
untrusted comment: signify public key
|
||||||
|
RWQnkSm9lj1YIZYpt1Y3mHYzFsaky82gQF6CrW4lme9OoEYzSIl2ZsIC
|
2
etc/signify/sortix-1.2.pub
Normal file
2
etc/signify/sortix-1.2.pub
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
untrusted comment: signify public key
|
||||||
|
RWTGrBXmGvl2zUpCa47ui5EyPsnitKLjsCZ2YZphNY8F3b33t6QWYDs1
|
2
etc/signify/sortix-1.2dev.pub
Normal file
2
etc/signify/sortix-1.2dev.pub
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
untrusted comment: signify public key
|
||||||
|
RWRTbLQ+3+a9I5yche2BEVP03TRtumGO4Vgq1AQ/5bRj8JAJ1R0+vpxE
|
1
host/.gitignore
vendored
Normal file
1
host/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
host
|
25
host/Makefile
Normal file
25
host/Makefile
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/version.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||||
|
CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CFLAGS += -Wall -Wextra
|
||||||
|
|
||||||
|
BINARIES = host
|
||||||
|
|
||||||
|
all: $(BINARIES)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
install $(BINARIES) $(DESTDIR)$(BINDIR)
|
||||||
|
|
||||||
|
%: %.c
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BINARIES)
|
511
host/host.c
Normal file
511
host/host.c
Normal file
|
@ -0,0 +1,511 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* host.c
|
||||||
|
* Domain name system client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/dnsconfig.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define DNS_SIZE 512
|
||||||
|
#define DNS_NAME_MAX 255
|
||||||
|
#define DNS_LABEL_MAX 64
|
||||||
|
|
||||||
|
struct dns_header
|
||||||
|
{
|
||||||
|
uint16_t id;
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t qdcount;
|
||||||
|
uint16_t ancount;
|
||||||
|
uint16_t nscount;
|
||||||
|
uint16_t arcount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dns_question
|
||||||
|
{
|
||||||
|
uint16_t qtype;
|
||||||
|
uint16_t qclass;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dns_record
|
||||||
|
{
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t class;
|
||||||
|
uint16_t ttl_high;
|
||||||
|
uint16_t ttl_low;
|
||||||
|
uint16_t rdlength;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_MASK (0xF << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_NO (0 << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_FORMAT (1 << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_SERVER (2 << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_NAME (3 << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED (4 << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RCODE_REFUSED (5 << 0)
|
||||||
|
#define DNS_HEADER_FLAGS_RA (1 << 7)
|
||||||
|
#define DNS_HEADER_FLAGS_RD (1 << 8)
|
||||||
|
#define DNS_HEADER_FLAGS_TC (1 << 9)
|
||||||
|
#define DNS_HEADER_FLAGS_AA (1 << 10)
|
||||||
|
#define DNS_HEADER_FLAGS_OPCODE_MASK (0xF << 11)
|
||||||
|
#define DNS_HEADER_FLAGS_OPCODE_QUERY (0 << 11)
|
||||||
|
#define DNS_HEADER_FLAGS_OPCODE_IQUERY (1 << 11)
|
||||||
|
#define DNS_HEADER_FLAGS_OPCODE_STATUS (2 << 11)
|
||||||
|
#define DNS_HEADER_FLAGS_QR (1 << 15)
|
||||||
|
|
||||||
|
#define DNS_TYPE_A 1
|
||||||
|
#define DNS_TYPE_NS 2
|
||||||
|
#define DNS_TYPE_MD 3
|
||||||
|
#define DNS_TYPE_MF 4
|
||||||
|
#define DNS_TYPE_CNAME 5
|
||||||
|
#define DNS_TYPE_SOA 6
|
||||||
|
#define DNS_TYPE_MB 7
|
||||||
|
#define DNS_TYPE_MG 8
|
||||||
|
#define DNS_TYPE_MR 9
|
||||||
|
#define DNS_TYPE_NULL 10
|
||||||
|
#define DNS_TYPE_WKS 11
|
||||||
|
#define DNS_TYPE_PTR 12
|
||||||
|
#define DNS_TYPE_HINFO 13
|
||||||
|
#define DNS_TYPE_MINFO 14
|
||||||
|
#define DNS_TYPE_MX 15
|
||||||
|
#define DNS_TYPE_TXT 16
|
||||||
|
#define DNS_TYPE_AAAA 28
|
||||||
|
|
||||||
|
#define DNS_QTYPE_AXFR 252
|
||||||
|
#define DNS_QTYPE_MAILB 253
|
||||||
|
#define DNS_QTYPE_MAILA 254
|
||||||
|
#define DNS_QTYPE_ANY 255
|
||||||
|
|
||||||
|
#define DNS_CLASS_IN 1
|
||||||
|
#define DNS_CLASS_CS 2
|
||||||
|
#define DNS_CLASS_CH 3
|
||||||
|
#define DNS_CLASS_HS 4
|
||||||
|
|
||||||
|
#define DNS_QCLASS_ANY 255
|
||||||
|
|
||||||
|
static size_t encode_dns_header(unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
const struct dns_header* hdrin)
|
||||||
|
{
|
||||||
|
struct dns_header hdr;
|
||||||
|
if ( DNS_SIZE - offset < sizeof(hdr) )
|
||||||
|
errx(1, "dns message too large");
|
||||||
|
hdr.id = htobe16(hdrin->id);
|
||||||
|
hdr.flags = htobe16(hdrin->flags);
|
||||||
|
hdr.qdcount = htobe16(hdrin->qdcount);
|
||||||
|
hdr.ancount = htobe16(hdrin->ancount);
|
||||||
|
hdr.nscount = htobe16(hdrin->nscount);
|
||||||
|
hdr.arcount = htobe16(hdrin->arcount);
|
||||||
|
memcpy(msg + offset, &hdr, sizeof(hdr));
|
||||||
|
return offset + sizeof(hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t encode_dns_byte(unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
unsigned char byte)
|
||||||
|
{
|
||||||
|
if ( DNS_SIZE - offset < 1 )
|
||||||
|
errx(1, "dns message too large");
|
||||||
|
msg[offset] = byte;
|
||||||
|
return offset + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t encode_dns_name(unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
const char* name)
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
size_t namelen = 0;
|
||||||
|
if ( !name[0] )
|
||||||
|
errx(1, "'%s' is not a valid name (unexpected end of input)", name);
|
||||||
|
while ( name[index] )
|
||||||
|
{
|
||||||
|
if ( !strcmp(name + index, ".") )
|
||||||
|
break;
|
||||||
|
if ( name[index] == '.' )
|
||||||
|
errx(1, "'%s' is not a valid name (empty label)", name);
|
||||||
|
size_t length = strcspn(name + index, ".");
|
||||||
|
if ( DNS_LABEL_MAX <= length )
|
||||||
|
errx(1, "'%s' is not a valid name (label too long)", name);
|
||||||
|
if ( namelen++ == DNS_NAME_MAX )
|
||||||
|
errx(1, "'%s' is not a valid name (name is too long)", name);
|
||||||
|
offset = encode_dns_byte(msg, offset, length & 0xFF);
|
||||||
|
for ( size_t i = 0; i < length; i++ )
|
||||||
|
{
|
||||||
|
if ( namelen++ == DNS_NAME_MAX )
|
||||||
|
errx(1, "'%s' is not a valid name (name is too long)", name);
|
||||||
|
offset = encode_dns_byte(msg, offset, name[index + i]);
|
||||||
|
}
|
||||||
|
index += length;
|
||||||
|
if ( name[index] == '.' )
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if ( namelen++ == DNS_NAME_MAX )
|
||||||
|
errx(1, "'%s' is not a valid name (name is too long)", name);
|
||||||
|
offset = encode_dns_byte(msg, offset, 0);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t encode_dns_question(unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
const char* name,
|
||||||
|
const struct dns_question* qsin)
|
||||||
|
{
|
||||||
|
offset = encode_dns_name(msg, offset, name);
|
||||||
|
struct dns_question qs;
|
||||||
|
if ( DNS_SIZE - offset < sizeof(qs) )
|
||||||
|
errx(1, "dns message too large");
|
||||||
|
qs.qtype = htobe16(qsin->qtype);
|
||||||
|
qs.qclass = htobe16(qsin->qclass);
|
||||||
|
memcpy(msg + offset, &qs, sizeof(qs));
|
||||||
|
return offset + sizeof(qs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t decode_dns_header(const unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
size_t msg_size,
|
||||||
|
struct dns_header* hdrout)
|
||||||
|
{
|
||||||
|
struct dns_header hdr;
|
||||||
|
if ( msg_size - offset < sizeof(hdr) )
|
||||||
|
errx(1, "dns message too small");
|
||||||
|
memcpy(&hdr, msg + offset, sizeof(hdr));
|
||||||
|
hdrout->id = be16toh(hdr.id);
|
||||||
|
hdrout->flags = be16toh(hdr.flags);
|
||||||
|
hdrout->qdcount = be16toh(hdr.qdcount);
|
||||||
|
hdrout->ancount = be16toh(hdr.ancount);
|
||||||
|
hdrout->nscount = be16toh(hdr.nscount);
|
||||||
|
hdrout->arcount = be16toh(hdr.arcount);
|
||||||
|
return offset + sizeof(hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t decode_dns_byte(const unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
size_t msg_size,
|
||||||
|
unsigned char* byte)
|
||||||
|
{
|
||||||
|
if ( msg_size <= offset || msg_size - offset < 1 )
|
||||||
|
errx(1, "dns message too small");
|
||||||
|
*byte = msg[offset];
|
||||||
|
return offset + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t decode_dns_name(const unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
size_t msg_size,
|
||||||
|
char* name)
|
||||||
|
{
|
||||||
|
bool real_offset_set = false;
|
||||||
|
size_t real_offset = 0;
|
||||||
|
size_t index = 0;
|
||||||
|
size_t namelen = 0;
|
||||||
|
uint8_t b;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
if ( namelen++ == DNS_NAME_MAX )
|
||||||
|
errx(1, "name too long");
|
||||||
|
offset = decode_dns_byte(msg, offset, msg_size, &b);
|
||||||
|
if ( 0xC0 & b )
|
||||||
|
{
|
||||||
|
namelen--;
|
||||||
|
size_t ptr = (b & 0x3F) << 8;
|
||||||
|
offset = decode_dns_byte(msg, offset, msg_size, &b);
|
||||||
|
ptr |= b;
|
||||||
|
if ( !real_offset_set )
|
||||||
|
{
|
||||||
|
real_offset = offset;
|
||||||
|
real_offset_set = true;
|
||||||
|
}
|
||||||
|
offset = ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t length = b;
|
||||||
|
if ( DNS_LABEL_MAX <= length )
|
||||||
|
errx(1, "label too long");
|
||||||
|
if ( !length )
|
||||||
|
break;
|
||||||
|
if ( index )
|
||||||
|
name[index++] = '.';
|
||||||
|
for ( size_t i = 0; i < length; i++ )
|
||||||
|
{
|
||||||
|
if ( namelen++ == DNS_NAME_MAX )
|
||||||
|
errx(1, "name too long");
|
||||||
|
offset = decode_dns_byte(msg, offset, msg_size, &b);
|
||||||
|
// TODO: Handle if b == '.'.
|
||||||
|
name[index++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name[index++] = '.';
|
||||||
|
name[index] = '\0';
|
||||||
|
if ( real_offset_set )
|
||||||
|
return real_offset;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t decode_dns_question(const unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
size_t msg_size,
|
||||||
|
char* name,
|
||||||
|
struct dns_question* qsout)
|
||||||
|
{
|
||||||
|
offset = decode_dns_name(msg, offset, msg_size, name);
|
||||||
|
struct dns_question qs;
|
||||||
|
if ( msg_size <= offset || msg_size - offset < sizeof(qs) )
|
||||||
|
errx(1, "dns message too small");
|
||||||
|
memcpy(&qs, msg + offset, sizeof(qs));
|
||||||
|
qsout->qtype = be16toh(qs.qtype);
|
||||||
|
qsout->qclass = be16toh(qs.qclass);
|
||||||
|
return offset + sizeof(qs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t decode_dns_record(const unsigned char* msg,
|
||||||
|
size_t offset,
|
||||||
|
size_t msg_size,
|
||||||
|
char* name,
|
||||||
|
struct dns_record* rrout)
|
||||||
|
{
|
||||||
|
offset = decode_dns_name(msg, offset, msg_size, name);
|
||||||
|
struct dns_record rr;
|
||||||
|
if ( msg_size <= offset || msg_size - offset < sizeof(rr) )
|
||||||
|
errx(1, "dns message too small");
|
||||||
|
memcpy(&rr, msg + offset, sizeof(rr));
|
||||||
|
rrout->type = be16toh(rr.type);
|
||||||
|
rrout->class = be16toh(rr.class);
|
||||||
|
rrout->ttl_high = be16toh(rr.ttl_high);
|
||||||
|
rrout->ttl_low = be16toh(rr.ttl_low);
|
||||||
|
rrout->rdlength = be16toh(rr.rdlength);
|
||||||
|
return offset + sizeof(rr);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int ipv = 4;
|
||||||
|
|
||||||
|
const char* argv0 = argv[0];
|
||||||
|
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] != '-' )
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
while ( (c = *++arg) ) switch ( c )
|
||||||
|
{
|
||||||
|
case '4': ipv = 4; break;
|
||||||
|
case '6': ipv = 6; break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compact_arguments(&argc, &argv);
|
||||||
|
|
||||||
|
if ( argc < 2 )
|
||||||
|
errx(1, "No host given");
|
||||||
|
const char* host = argv[1];
|
||||||
|
|
||||||
|
char nsipstr[INET_ADDRSTRLEN];
|
||||||
|
const char* nameserver;
|
||||||
|
if ( argc < 3 )
|
||||||
|
{
|
||||||
|
struct dnsconfig dnscfg;
|
||||||
|
if ( getdnsconfig(&dnscfg) < 0 )
|
||||||
|
err(1, "dnsconfig");
|
||||||
|
bool found = false;
|
||||||
|
for ( size_t i = 0; !found && i < dnscfg.servers_count; i++ )
|
||||||
|
{
|
||||||
|
if ( dnscfg.servers[i].family != AF_INET )
|
||||||
|
continue;
|
||||||
|
inet_ntop(AF_INET, &dnscfg.servers[i].addr, nsipstr, sizeof(nsipstr));
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if ( !found )
|
||||||
|
errx(1, "No nameserver given and no default configured");
|
||||||
|
nameserver = nsipstr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nameserver = argv[2];
|
||||||
|
|
||||||
|
int port = 4 <= argc ? atoi(argv[3]) : 53;
|
||||||
|
if ( 5 <= argc )
|
||||||
|
errx(1, "Unexpected extra operand");
|
||||||
|
|
||||||
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if ( fd < 0 )
|
||||||
|
err(1, "socket");
|
||||||
|
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htobe16(port);
|
||||||
|
if ( inet_pton(AF_INET, nameserver, &addr.sin_addr) < 1 )
|
||||||
|
errx(1, "invalid ip address: %s", nameserver);
|
||||||
|
|
||||||
|
if ( connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0 )
|
||||||
|
err(1, "connect");
|
||||||
|
|
||||||
|
unsigned char req[DNS_SIZE];
|
||||||
|
size_t req_size = 0;
|
||||||
|
struct dns_header hdr;
|
||||||
|
hdr.id = 0;
|
||||||
|
hdr.flags = DNS_HEADER_FLAGS_RD;
|
||||||
|
hdr.qdcount = 1;
|
||||||
|
hdr.ancount = 0;
|
||||||
|
hdr.nscount = 0;
|
||||||
|
hdr.arcount = 0;
|
||||||
|
req_size = encode_dns_header(req, req_size, &hdr);
|
||||||
|
struct dns_question qs;
|
||||||
|
qs.qtype = ipv == 4 ? DNS_TYPE_A : DNS_TYPE_AAAA;
|
||||||
|
qs.qclass = DNS_CLASS_IN;
|
||||||
|
req_size = encode_dns_question(req, req_size, host, &qs);
|
||||||
|
|
||||||
|
ssize_t amount = send(fd, req, req_size, 0);
|
||||||
|
if ( amount < 0 )
|
||||||
|
err(1, "send");
|
||||||
|
|
||||||
|
// TODO: Use recvfrom to get the server ip.
|
||||||
|
unsigned char resp[DNS_SIZE];
|
||||||
|
ssize_t resp_size = recv(fd, resp, sizeof(resp), 0);
|
||||||
|
if ( resp_size < 0 )
|
||||||
|
err(1, "recv");
|
||||||
|
|
||||||
|
// TODO: Verify the response came from the correct ip.
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
offset = decode_dns_header(resp, offset, resp_size, &hdr);
|
||||||
|
|
||||||
|
// TODO: Verify the response has the correct id.
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("id = %u\n", hdr.id);
|
||||||
|
printf("flags = 0x%X\n", hdr.flags);
|
||||||
|
printf("qdcount = %u\n", hdr.qdcount);
|
||||||
|
printf("ancount = %u\n", hdr.ancount);
|
||||||
|
printf("nscount = %u\n", hdr.nscount);
|
||||||
|
printf("arcount = %u\n", hdr.arcount);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint16_t rcode = hdr.flags & DNS_HEADER_FLAGS_RCODE_MASK;
|
||||||
|
if ( rcode == DNS_HEADER_FLAGS_RCODE_FORMAT )
|
||||||
|
errx(1, "format error");
|
||||||
|
else if ( rcode == DNS_HEADER_FLAGS_RCODE_SERVER )
|
||||||
|
errx(1, "server error");
|
||||||
|
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NAME )
|
||||||
|
errx(1, "no such name");
|
||||||
|
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED )
|
||||||
|
errx(1, "not implemented error");
|
||||||
|
else if ( rcode == DNS_HEADER_FLAGS_RCODE_REFUSED )
|
||||||
|
errx(1, "refused");
|
||||||
|
else if ( rcode != DNS_HEADER_FLAGS_RCODE_NO )
|
||||||
|
errx(1, "unknown error (rcode=0x%X)", rcode);
|
||||||
|
|
||||||
|
if ( hdr.flags & DNS_HEADER_FLAGS_TC )
|
||||||
|
errx(1, "truncated");
|
||||||
|
|
||||||
|
// TODO: Check query bit.
|
||||||
|
|
||||||
|
for ( uint16_t i = 0; i < hdr.qdcount; i++ )
|
||||||
|
{
|
||||||
|
char name[DNS_NAME_MAX + 1];
|
||||||
|
offset = decode_dns_question(resp, offset, resp_size, name, &qs);
|
||||||
|
//printf("%s type=%u class=%u\n", name, qs.qtype, qs.qclass);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( uint16_t i = 0; i < hdr.ancount; i++ )
|
||||||
|
{
|
||||||
|
char name[DNS_NAME_MAX + 1];
|
||||||
|
struct dns_record rr;
|
||||||
|
offset = decode_dns_record(resp, offset, resp_size, name, &rr);
|
||||||
|
uint32_t ttl = (uint32_t) rr.ttl_high << 16 | rr.ttl_low;
|
||||||
|
printf("%s type=%u class=%u ttl=%u ", name, rr.type, rr.class, ttl);
|
||||||
|
if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_A )
|
||||||
|
{
|
||||||
|
unsigned char ip[4];
|
||||||
|
for ( size_t i = 0; i < 4; i++ )
|
||||||
|
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
|
||||||
|
printf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
||||||
|
}
|
||||||
|
else if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_AAAA )
|
||||||
|
{
|
||||||
|
unsigned char ip[16];
|
||||||
|
for ( size_t i = 0; i < 16; i++ )
|
||||||
|
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
|
||||||
|
for ( size_t i = 0; i < 16; i++ )
|
||||||
|
{
|
||||||
|
if ( i && !(i & 1) )
|
||||||
|
putchar(':');
|
||||||
|
printf("%02x", ip[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( rr.type == DNS_TYPE_CNAME )
|
||||||
|
{
|
||||||
|
char cname[DNS_NAME_MAX + 1];
|
||||||
|
offset = decode_dns_name(resp, offset, resp_size, cname);
|
||||||
|
printf("CNAME %s", cname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("0x");
|
||||||
|
fflush(stdout);
|
||||||
|
for ( size_t i = 0; i < rr.rdlength; i++ )
|
||||||
|
{
|
||||||
|
unsigned char b;
|
||||||
|
offset = decode_dns_byte(resp, offset, resp_size, &b);
|
||||||
|
if ( isprint(b) && b != '\'' )
|
||||||
|
printf("'%c'", b);
|
||||||
|
else
|
||||||
|
printf("%02X", b);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
4
irc/.gitignore
vendored
Normal file
4
irc/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
irc
|
||||||
|
*.o
|
||||||
|
config.h
|
||||||
|
tests
|
56
irc/Makefile
Normal file
56
irc/Makefile
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/version.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||||
|
CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CFLAGS += -Wall -Wextra
|
||||||
|
CPPFLAGS += -DVERSIONSTR=\"$(VERSION)\"
|
||||||
|
|
||||||
|
ifeq ($(HOST_IS_SORTIX),0)
|
||||||
|
CPPFLAGS+=-D_GNU_SOURCE
|
||||||
|
endif
|
||||||
|
|
||||||
|
BINARY = irc
|
||||||
|
#MANPAGES1 = irc.1
|
||||||
|
|
||||||
|
OBJS=\
|
||||||
|
compat.o \
|
||||||
|
connection.o \
|
||||||
|
database.o \
|
||||||
|
irc.o \
|
||||||
|
scrollback.o \
|
||||||
|
string.o \
|
||||||
|
ui.o \
|
||||||
|
|
||||||
|
all: $(BINARY)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
$(OBJS): config.h
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(BINARY): $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) -o $(BINARY) $(LIBS)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
install $(BINARY) $(DESTDIR)$(BINDIR)
|
||||||
|
#mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||||
|
#install $(MANPAGES1) $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BINARY)
|
||||||
|
rm -f $(OBJS) *.o
|
||||||
|
|
||||||
|
TESTS=\
|
||||||
|
have-explicit_bzero \
|
||||||
|
have-reallocarray \
|
||||||
|
have-strlcat \
|
||||||
|
have-strlcpy \
|
||||||
|
|
||||||
|
include ../build-aux/config.mak
|
65
irc/compat.c
Normal file
65
irc/compat.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* compat.c
|
||||||
|
* Compatibility.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#ifndef HAVE_EXPLICIT_BZERO
|
||||||
|
void explicit_bzero(void* buffer, size_t size)
|
||||||
|
{
|
||||||
|
memset(buffer, 0, size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_REALLOCARRAY
|
||||||
|
void* reallocarray(void* ptr, size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
if ( size && nmemb && SIZE_MAX / size < nmemb )
|
||||||
|
return errno = ENOMEM, (void*) NULL;
|
||||||
|
return realloc(ptr, nmemb * size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_STRLCAT
|
||||||
|
size_t strlcat(char* restrict dest, const char* restrict src, size_t size)
|
||||||
|
{
|
||||||
|
size_t dest_len = strnlen(dest, size);
|
||||||
|
if ( size <= dest_len )
|
||||||
|
return dest_len + strlen(src);
|
||||||
|
return dest_len + strlcpy(dest + dest_len, src, size - dest_len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_STRLCPY
|
||||||
|
size_t strlcpy(char* restrict dest, const char* restrict src, size_t size)
|
||||||
|
{
|
||||||
|
if ( !size )
|
||||||
|
return strlen(src);
|
||||||
|
size_t result;
|
||||||
|
for ( result = 0; result < size-1 && src[result]; result++ )
|
||||||
|
dest[result] = src[result];
|
||||||
|
dest[result] = '\0';
|
||||||
|
return result + strlen(src + result);
|
||||||
|
}
|
||||||
|
#endif
|
40
irc/compat.h
Normal file
40
irc/compat.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* combat.h
|
||||||
|
* Compatibility.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMPAT_H
|
||||||
|
#define COMPAT_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifndef HAVE_EXPLICIT_BZERO
|
||||||
|
void explicit_bzero(void*, size_t);
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_REALLOCARRAY
|
||||||
|
void* reallocarray(void*, size_t, size_t);
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_STRLCAT
|
||||||
|
size_t strlcat(char* restrict, const char* restrict, size_t);
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_STRLCPY
|
||||||
|
size_t strlcpy(char* restrict, const char* restrict, size_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
549
irc/connection.c
Normal file
549
irc/connection.c
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* connection.c
|
||||||
|
* IRC protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
void dump_error(const char* message,
|
||||||
|
size_t message_size,
|
||||||
|
const struct timespec* when)
|
||||||
|
{
|
||||||
|
// TODO: Send the error somewhere appropriate in the UI.
|
||||||
|
|
||||||
|
fprintf(stderr, "\e[91m");
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&when->tv_sec, &tm);
|
||||||
|
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
|
||||||
|
tm.tm_year + 1900,
|
||||||
|
tm.tm_mon + 1,
|
||||||
|
tm.tm_mday,
|
||||||
|
tm.tm_hour,
|
||||||
|
tm.tm_min,
|
||||||
|
tm.tm_sec,
|
||||||
|
when->tv_nsec);
|
||||||
|
for ( size_t i = 0; i < message_size; i++ )
|
||||||
|
{
|
||||||
|
if ( message[i] == '\r' )
|
||||||
|
continue;
|
||||||
|
else if ( message[i] == '\n' )
|
||||||
|
continue;
|
||||||
|
else if ( (unsigned char) message[i] < 32 )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\e[31m");
|
||||||
|
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
|
||||||
|
fprintf(stderr, "\e[91m");
|
||||||
|
}
|
||||||
|
fputc((unsigned char) message[i], stderr);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\e[m\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_outgoing(const char* message,
|
||||||
|
size_t message_size,
|
||||||
|
const struct timespec* when)
|
||||||
|
{
|
||||||
|
return; // TODO: Remove this, or adopt it for a logging mechanism.
|
||||||
|
|
||||||
|
fprintf(stderr, "\e[92m");
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&when->tv_sec, &tm);
|
||||||
|
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
|
||||||
|
tm.tm_year + 1900,
|
||||||
|
tm.tm_mon + 1,
|
||||||
|
tm.tm_mday,
|
||||||
|
tm.tm_hour,
|
||||||
|
tm.tm_min,
|
||||||
|
tm.tm_sec,
|
||||||
|
when->tv_nsec);
|
||||||
|
for ( size_t i = 0; i < message_size; i++ )
|
||||||
|
{
|
||||||
|
if ( message[i] == '\r' )
|
||||||
|
continue;
|
||||||
|
else if ( message[i] == '\n' )
|
||||||
|
continue;
|
||||||
|
else if ( (unsigned char) message[i] < 32 )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\e[91m");
|
||||||
|
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
|
||||||
|
fprintf(stderr, "\e[92m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fputc((unsigned char) message[i], stderr);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\e[m\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_incoming(const char* message,
|
||||||
|
size_t message_size,
|
||||||
|
const struct timespec* when)
|
||||||
|
{
|
||||||
|
return; // TODO: Remove this, or adopt it for a logging mechanism.
|
||||||
|
|
||||||
|
fprintf(stderr, "\e[93m");
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&when->tv_sec, &tm);
|
||||||
|
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
|
||||||
|
tm.tm_year + 1900,
|
||||||
|
tm.tm_mon + 1,
|
||||||
|
tm.tm_mday,
|
||||||
|
tm.tm_hour,
|
||||||
|
tm.tm_min,
|
||||||
|
tm.tm_sec,
|
||||||
|
when->tv_nsec);
|
||||||
|
for ( size_t i = 0; i < message_size; i++ )
|
||||||
|
{
|
||||||
|
if ( message[i] == '\r' )
|
||||||
|
continue;
|
||||||
|
else if ( message[i] == '\n' )
|
||||||
|
continue;
|
||||||
|
else if ( (unsigned char) message[i] < 32 )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\e[91m");
|
||||||
|
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
|
||||||
|
fprintf(stderr, "\e[93m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fputc((unsigned char) message[i], stderr);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\e[m\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_error_vlinef(const char* format, va_list ap_orig)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
|
||||||
|
va_copy(ap, ap_orig);
|
||||||
|
char* string;
|
||||||
|
if ( 0 <= vasprintf(&string, format, ap) )
|
||||||
|
{
|
||||||
|
va_end(ap);
|
||||||
|
dump_error(string, strlen(string), &now);
|
||||||
|
free(string);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
va_copy(ap, ap_orig);
|
||||||
|
if ( 0 <= vsnprintf(buffer, sizeof(buffer), format, ap) )
|
||||||
|
{
|
||||||
|
va_end(ap);
|
||||||
|
dump_error(buffer, strlen(buffer), &now);
|
||||||
|
dump_error("(vasprintf failed printing that line)",
|
||||||
|
strlen("(vasprintf failed printing that line)"), &now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
dump_error(format, strlen(format), &now);
|
||||||
|
dump_error("(vsnprintf failed printing format string)",
|
||||||
|
strlen("(vsnprintf failed printing that format string)"), &now);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_error_linef(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
irc_error_vlinef(format, ap),
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_transmit(struct irc_connection* irc_connection,
|
||||||
|
const char* message,
|
||||||
|
size_t message_size)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
if ( irc_connection->connectivity_error )
|
||||||
|
return;
|
||||||
|
dump_outgoing(message, message_size, &now);
|
||||||
|
int fd = irc_connection->fd;
|
||||||
|
while ( message_size )
|
||||||
|
{
|
||||||
|
ssize_t amount = 0;
|
||||||
|
amount = send(fd, message, message_size, MSG_NOSIGNAL);
|
||||||
|
if ( amount < 0 || amount == 0 )
|
||||||
|
{
|
||||||
|
warn("send");
|
||||||
|
irc_connection->connectivity_error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
message += amount;
|
||||||
|
message_size -= amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_transmit_message(struct irc_connection* irc_connection,
|
||||||
|
const char* message,
|
||||||
|
size_t message_size)
|
||||||
|
{
|
||||||
|
assert(2 <= message_size);
|
||||||
|
assert(message[message_size - 2] == '\r');
|
||||||
|
assert(message[message_size - 1] == '\n');
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
if ( 512 < message_size )
|
||||||
|
{
|
||||||
|
memcpy(buffer, message, 510);
|
||||||
|
buffer[510] = '\r';
|
||||||
|
buffer[511] = '\n';
|
||||||
|
message = buffer;
|
||||||
|
message_size = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
irc_transmit(irc_connection, message, message_size);
|
||||||
|
|
||||||
|
explicit_bzero(buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_receive_more_bytes(struct irc_connection* irc_connection)
|
||||||
|
{
|
||||||
|
if ( irc_connection->connectivity_error )
|
||||||
|
return;
|
||||||
|
int fd = irc_connection->fd;
|
||||||
|
char* buffer = irc_connection->incoming_buffer;
|
||||||
|
size_t buffer_size = sizeof(irc_connection->incoming_buffer);
|
||||||
|
size_t buffer_used = irc_connection->incoming_amount;
|
||||||
|
size_t buffer_free = buffer_size - buffer_used;
|
||||||
|
if ( buffer_free == 0 )
|
||||||
|
return;
|
||||||
|
// TODO: Use non-blocking IO for transmitting as well so O_NONBLOCK can
|
||||||
|
// always be used.
|
||||||
|
// TODO: Use MSG_DONTWAIT when supported in Sortix.
|
||||||
|
int flags = fcntl(fd, F_GETFL);
|
||||||
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
ssize_t amount = recv(fd, buffer + buffer_used, buffer_free, 0);
|
||||||
|
fcntl(fd, F_SETFL, flags);
|
||||||
|
if ( amount < 0 )
|
||||||
|
{
|
||||||
|
if ( errno == EAGAIN || errno == EWOULDBLOCK )
|
||||||
|
return;
|
||||||
|
warn("recv");
|
||||||
|
irc_connection->connectivity_error = true;
|
||||||
|
}
|
||||||
|
else if ( amount == 0 )
|
||||||
|
{
|
||||||
|
// TODO: Gracefully close the connection.
|
||||||
|
irc_connection->connectivity_error = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
irc_connection->incoming_amount += amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_receive_pop_bytes(struct irc_connection* irc_connection,
|
||||||
|
char* buffer,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
assert(count <= irc_connection->incoming_amount);
|
||||||
|
memcpy(buffer, irc_connection->incoming_buffer, count);
|
||||||
|
explicit_bzero(irc_connection->incoming_buffer, count);
|
||||||
|
memmove(irc_connection->incoming_buffer,
|
||||||
|
irc_connection->incoming_buffer + count,
|
||||||
|
irc_connection->incoming_amount - count);
|
||||||
|
irc_connection->incoming_amount -= count;
|
||||||
|
explicit_bzero(irc_connection->incoming_buffer + irc_connection->incoming_amount,
|
||||||
|
count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool irc_receive_message(struct irc_connection* irc_connection,
|
||||||
|
char message[512],
|
||||||
|
struct timespec* when)
|
||||||
|
{
|
||||||
|
if ( irc_connection->connectivity_error )
|
||||||
|
return false;
|
||||||
|
size_t message_usable = 0;
|
||||||
|
while ( message_usable < irc_connection->incoming_amount &&
|
||||||
|
irc_connection->incoming_buffer[message_usable] != '\r' &&
|
||||||
|
irc_connection->incoming_buffer[message_usable] != '\n' )
|
||||||
|
message_usable++;
|
||||||
|
if ( message_usable < irc_connection->incoming_amount &&
|
||||||
|
irc_connection->incoming_buffer[message_usable] == '\r')
|
||||||
|
{
|
||||||
|
message_usable++;
|
||||||
|
if ( message_usable < irc_connection->incoming_amount &&
|
||||||
|
irc_connection->incoming_buffer[message_usable] == '\n' )
|
||||||
|
{
|
||||||
|
message_usable++;
|
||||||
|
irc_receive_pop_bytes(irc_connection, message, message_usable);
|
||||||
|
assert(message[message_usable-2] == '\r');
|
||||||
|
assert(message[message_usable-1] == '\n');
|
||||||
|
message[message_usable-2] = '\0';
|
||||||
|
message[message_usable-1] = '\0';
|
||||||
|
// TODO: This is not always when the message did arrive.
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
dump_incoming(message, message_usable-2, &now);
|
||||||
|
*when = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( message_usable < irc_connection->incoming_amount )
|
||||||
|
{
|
||||||
|
// TODO: Handle bad newline sequence.
|
||||||
|
warnx("recv: bad IRC newline");
|
||||||
|
irc_connection->connectivity_error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( message_usable < irc_connection->incoming_amount &&
|
||||||
|
irc_connection->incoming_buffer[message_usable] == '\n' )
|
||||||
|
{
|
||||||
|
// TODO: Handle bad newline sequence.
|
||||||
|
warnx("recv: bad IRC newline");
|
||||||
|
irc_connection->connectivity_error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( message_usable == 512 )
|
||||||
|
{
|
||||||
|
// TODO: Handle untruncated lines from the server.
|
||||||
|
warnx("recv: overlong IRC line from server");
|
||||||
|
irc_connection->connectivity_error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_transmit_string(struct irc_connection* irc_connection,
|
||||||
|
const char* string)
|
||||||
|
{
|
||||||
|
char message[512];
|
||||||
|
strncpy(message, string, 510);
|
||||||
|
message[510] = '\0';
|
||||||
|
message[511] = '\0';
|
||||||
|
size_t string_truncated_length = strlen(message);
|
||||||
|
for ( size_t i = 0; i < string_truncated_length; i++ )
|
||||||
|
{
|
||||||
|
if ( message[i] == '\r' )
|
||||||
|
message[i] = ' ';
|
||||||
|
if ( message[i] == '\n' )
|
||||||
|
message[i] = ' ';
|
||||||
|
}
|
||||||
|
message[string_truncated_length + 0] = '\r';
|
||||||
|
message[string_truncated_length + 1] = '\n';
|
||||||
|
size_t message_length = strnlen(message, 512);
|
||||||
|
irc_transmit_message(irc_connection, message, message_length);
|
||||||
|
explicit_bzero(message, sizeof(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((format(printf, 2, 0)))
|
||||||
|
void irc_transmit_vformat(struct irc_connection* irc_connection,
|
||||||
|
const char* format,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
char* string = NULL;
|
||||||
|
if ( vasprintf(&string, format, ap) < 0 )
|
||||||
|
{
|
||||||
|
// TODO: Hmm, what do we do here.
|
||||||
|
warn("vasprintf");
|
||||||
|
// TODO: Should the error condition be set?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
irc_transmit_string(irc_connection, string);
|
||||||
|
explicit_bzero(string, strlen(string));
|
||||||
|
free(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((format(printf, 2, 3)))
|
||||||
|
void irc_transmit_format(struct irc_connection* irc_connection,
|
||||||
|
const char* format,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
irc_transmit_vformat(irc_connection, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_pass(struct irc_connection* irc_connection,
|
||||||
|
const char* password)
|
||||||
|
{
|
||||||
|
irc_transmit_format(irc_connection, "PASS :%s", password);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_nick(struct irc_connection* irc_connection,
|
||||||
|
const char* nick)
|
||||||
|
{
|
||||||
|
irc_transmit_format(irc_connection, "NICK :%s", nick);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_user(struct irc_connection* irc_connection,
|
||||||
|
const char* nick,
|
||||||
|
const char* local_hostname,
|
||||||
|
const char* server_hostname,
|
||||||
|
const char* real_name)
|
||||||
|
{
|
||||||
|
// TODO: What if there are spaces in some of these fields?
|
||||||
|
irc_transmit_format(irc_connection, "USER %s %s %s :%s",
|
||||||
|
nick, local_hostname, server_hostname, real_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_join(struct irc_connection* irc_connection,
|
||||||
|
const char* channel)
|
||||||
|
{
|
||||||
|
irc_transmit_format(irc_connection, "JOIN :%s", channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_part(struct irc_connection* irc_connection,
|
||||||
|
const char* channel)
|
||||||
|
{
|
||||||
|
irc_transmit_format(irc_connection, "PART :%s", channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_privmsg(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what)
|
||||||
|
|
||||||
|
{
|
||||||
|
// TODO: Ensure where is valid.
|
||||||
|
irc_transmit_format(irc_connection, "PRIVMSG %s :%s", where, what);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_privmsgf(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what_format,
|
||||||
|
...)
|
||||||
|
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, what_format);
|
||||||
|
char msg[512];
|
||||||
|
vsnprintf(msg, sizeof(msg), what_format, ap);
|
||||||
|
irc_command_privmsg(irc_connection, where, msg);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_notice(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what)
|
||||||
|
|
||||||
|
{
|
||||||
|
// TODO: Ensure where is valid.
|
||||||
|
irc_transmit_format(irc_connection, "NOTICE %s :%s", where, what);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_noticef(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what_format,
|
||||||
|
...)
|
||||||
|
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, what_format);
|
||||||
|
char msg[512];
|
||||||
|
vsnprintf(msg, sizeof(msg), what_format, ap);
|
||||||
|
irc_command_notice(irc_connection, where, msg);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_kick(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* who,
|
||||||
|
const char* why)
|
||||||
|
{
|
||||||
|
// TODO: Ensure where and who are valid.
|
||||||
|
if ( why )
|
||||||
|
irc_transmit_format(irc_connection, "KICK %s %s :%s", where, who, why);
|
||||||
|
else
|
||||||
|
irc_transmit_format(irc_connection, "KICK %s %s", where, who);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_quit(struct irc_connection* irc_connection,
|
||||||
|
const char* message)
|
||||||
|
{
|
||||||
|
if ( message )
|
||||||
|
irc_transmit_format(irc_connection, "QUIT :%s", message);
|
||||||
|
else
|
||||||
|
irc_transmit_string(irc_connection, "QUIT");
|
||||||
|
shutdown(irc_connection->fd, SHUT_WR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_command_quit_malfunction(struct irc_connection* irc_connection,
|
||||||
|
const char* message)
|
||||||
|
{
|
||||||
|
if ( message )
|
||||||
|
irc_transmit_format(irc_connection, "QUIT :%s", message);
|
||||||
|
else
|
||||||
|
irc_transmit_string(irc_connection, "QUIT");
|
||||||
|
shutdown(irc_connection->fd, SHUT_RDWR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_parse_message_parameter(char* message,
|
||||||
|
char* parameters[16],
|
||||||
|
size_t* num_parameters_ptr)
|
||||||
|
{
|
||||||
|
size_t num_parameters = 0;
|
||||||
|
while ( message[0] != '\0' )
|
||||||
|
{
|
||||||
|
if ( message[0] == ':' || num_parameters == (16-1) -1 )
|
||||||
|
{
|
||||||
|
message++;
|
||||||
|
parameters[num_parameters++] = message;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters[num_parameters++] = message;
|
||||||
|
|
||||||
|
size_t usable = 0;
|
||||||
|
while ( message[usable] != '\0' && message[usable] != ' ' )
|
||||||
|
usable++;
|
||||||
|
|
||||||
|
char lc = message[usable];
|
||||||
|
message[usable] = '\0';
|
||||||
|
|
||||||
|
if ( lc != '\0' )
|
||||||
|
message += usable + 1;
|
||||||
|
else
|
||||||
|
message += usable;
|
||||||
|
}
|
||||||
|
*num_parameters_ptr = num_parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_parse_who(char* full, const char** who, const char** whomask)
|
||||||
|
{
|
||||||
|
size_t bangpos = strcspn(full, "!");
|
||||||
|
if ( full[bangpos] == '!' )
|
||||||
|
{
|
||||||
|
full[bangpos] = '\0';
|
||||||
|
*who = full;
|
||||||
|
*whomask = full + bangpos + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*who = full;
|
||||||
|
*whomask = "";
|
||||||
|
}
|
||||||
|
}
|
102
irc/connection.h
Normal file
102
irc/connection.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* connection.h
|
||||||
|
* IRC protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTION_H
|
||||||
|
#define CONNECTION_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
struct irc_connection
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool connectivity_error;
|
||||||
|
char incoming_buffer[512];
|
||||||
|
size_t incoming_amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((format(printf, 1, 0)))
|
||||||
|
void irc_error_vlinef(const char* format, va_list ap);
|
||||||
|
__attribute__((format(printf, 1, 2)))
|
||||||
|
void irc_error_linef(const char* format, ...);
|
||||||
|
void irc_transmit(struct irc_connection* irc_connection,
|
||||||
|
const char* message,
|
||||||
|
size_t message_size);
|
||||||
|
void irc_transmit_message(struct irc_connection* irc_connection,
|
||||||
|
const char* message,
|
||||||
|
size_t message_size);
|
||||||
|
void irc_transmit_string(struct irc_connection* irc_connection,
|
||||||
|
const char* string);
|
||||||
|
__attribute__((format(printf, 2, 0)))
|
||||||
|
void irc_transmit_vformat(struct irc_connection* irc_connection,
|
||||||
|
const char* format,
|
||||||
|
va_list ap);
|
||||||
|
__attribute__((format(printf, 2, 3)))
|
||||||
|
void irc_transmit_format(struct irc_connection* irc_connection,
|
||||||
|
const char* format,
|
||||||
|
...);
|
||||||
|
void irc_receive_more_bytes(struct irc_connection* irc_connection);
|
||||||
|
bool irc_receive_message(struct irc_connection* irc_connection,
|
||||||
|
char message[512],
|
||||||
|
struct timespec* when);
|
||||||
|
void irc_command_pass(struct irc_connection* irc_connection,
|
||||||
|
const char* password);
|
||||||
|
void irc_command_nick(struct irc_connection* irc_connection,
|
||||||
|
const char* nick);
|
||||||
|
void irc_command_user(struct irc_connection* irc_connection,
|
||||||
|
const char* nick,
|
||||||
|
const char* local_hostname,
|
||||||
|
const char* server_hostname,
|
||||||
|
const char* real_name);
|
||||||
|
void irc_command_join(struct irc_connection* irc_connection,
|
||||||
|
const char* channel);
|
||||||
|
void irc_command_part(struct irc_connection* irc_connection,
|
||||||
|
const char* channel);
|
||||||
|
void irc_command_privmsg(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what);
|
||||||
|
__attribute__((format(printf, 3, 4)))
|
||||||
|
void irc_command_privmsgf(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what_format,
|
||||||
|
...);
|
||||||
|
void irc_command_notice(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what);
|
||||||
|
__attribute__((format(printf, 3, 4)))
|
||||||
|
void irc_command_noticef(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* what_format,
|
||||||
|
...);
|
||||||
|
void irc_command_kick(struct irc_connection* irc_connection,
|
||||||
|
const char* where,
|
||||||
|
const char* who,
|
||||||
|
const char* why);
|
||||||
|
void irc_command_quit(struct irc_connection* irc_connection,
|
||||||
|
const char* message);
|
||||||
|
void irc_command_quit_malfunction(struct irc_connection* irc_connection,
|
||||||
|
const char* message);
|
||||||
|
void irc_parse_message_parameter(char* message,
|
||||||
|
char* parameters[16],
|
||||||
|
size_t* num_parameters_ptr);
|
||||||
|
void irc_parse_who(char* full, const char** who, const char** whomask);
|
||||||
|
|
||||||
|
#endif
|
237
irc/database.c
Normal file
237
irc/database.c
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* database.c
|
||||||
|
* Data structure for keeping track of channels and people.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "database.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
struct channel* find_channel(const struct network* state, const char* channel_name)
|
||||||
|
{
|
||||||
|
assert(channel_name);
|
||||||
|
|
||||||
|
for ( struct channel* channel = state->channels; channel; channel = channel->next_channel )
|
||||||
|
if ( strchannelcmp(channel->name, channel_name) == 0 )
|
||||||
|
return channel;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct channel* add_channel(struct network* state, const char* channel_name)
|
||||||
|
{
|
||||||
|
assert(channel_name);
|
||||||
|
|
||||||
|
assert(!find_channel(state, channel_name));
|
||||||
|
struct channel* channel = (struct channel*) calloc(sizeof(struct channel), 1);
|
||||||
|
if ( !channel )
|
||||||
|
return NULL;
|
||||||
|
channel->name = strdup(channel_name);
|
||||||
|
if ( !channel->name )
|
||||||
|
return free(channel), (struct channel*) NULL;
|
||||||
|
channel->people = NULL;
|
||||||
|
|
||||||
|
channel->prev_channel = NULL;
|
||||||
|
channel->next_channel = state->channels;
|
||||||
|
if ( state->channels )
|
||||||
|
state->channels->prev_channel = channel;
|
||||||
|
state->channels = channel;
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct channel* get_channel(struct network* state, const char* channel_name)
|
||||||
|
{
|
||||||
|
assert(channel_name);
|
||||||
|
|
||||||
|
for ( struct channel* result = find_channel(state, channel_name); result; result = NULL )
|
||||||
|
return result;
|
||||||
|
return add_channel(state, channel_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_channel(struct network* state, struct channel* channel)
|
||||||
|
{
|
||||||
|
while ( channel->people )
|
||||||
|
remove_person_from_channel(state, channel->people);
|
||||||
|
|
||||||
|
if ( channel->prev_channel )
|
||||||
|
channel->prev_channel->next_channel = channel->next_channel;
|
||||||
|
else
|
||||||
|
state->channels = channel->next_channel;
|
||||||
|
|
||||||
|
if ( channel->next_channel )
|
||||||
|
channel->next_channel->prev_channel = channel->prev_channel;
|
||||||
|
|
||||||
|
free(channel->name);
|
||||||
|
free(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct person* find_person(const struct network* state, const char* nick)
|
||||||
|
{
|
||||||
|
assert(nick);
|
||||||
|
|
||||||
|
for ( struct person* person = state->people; person; person = person->next_person )
|
||||||
|
if ( strnickcmp(person->nick, nick) == 0 )
|
||||||
|
return person;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct person* add_person(struct network* state, const char* nick)
|
||||||
|
{
|
||||||
|
assert(nick);
|
||||||
|
|
||||||
|
assert(!find_person(state, nick));
|
||||||
|
struct person* person = (struct person*) calloc(sizeof(struct person), 1);
|
||||||
|
if ( !person )
|
||||||
|
return NULL;
|
||||||
|
person->nick = strdup(nick);
|
||||||
|
if ( !person->nick )
|
||||||
|
return free(person), (struct person*) NULL;
|
||||||
|
|
||||||
|
person->prev_person = NULL;
|
||||||
|
person->next_person = state->people;
|
||||||
|
if ( state->people )
|
||||||
|
state->people->prev_person = person;
|
||||||
|
state->people = person;
|
||||||
|
|
||||||
|
return person;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct person* get_person(struct network* state, const char* nick)
|
||||||
|
{
|
||||||
|
assert(nick);
|
||||||
|
|
||||||
|
for ( struct person* result = find_person(state, nick); result; result = NULL )
|
||||||
|
return result;
|
||||||
|
return add_person(state, nick);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_person(struct network* state, struct person* person)
|
||||||
|
{
|
||||||
|
while ( person->channels )
|
||||||
|
remove_person_from_channel(state, person->channels);
|
||||||
|
|
||||||
|
if ( person->prev_person )
|
||||||
|
person->prev_person->next_person = person->next_person;
|
||||||
|
else
|
||||||
|
state->people = person->next_person;
|
||||||
|
|
||||||
|
if ( person->next_person )
|
||||||
|
person->next_person->prev_person = person->prev_person;
|
||||||
|
|
||||||
|
free(person->nick);
|
||||||
|
free(person);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct channel_person* find_person_in_channel(const struct network* state, const char* nick, const char* channel_name)
|
||||||
|
{
|
||||||
|
assert(nick);
|
||||||
|
assert(channel_name);
|
||||||
|
|
||||||
|
struct channel* channel = find_channel(state, channel_name);
|
||||||
|
if ( !channel )
|
||||||
|
return NULL;
|
||||||
|
for ( struct channel_person* channel_person = channel->people; channel_person; channel_person = channel_person->next_person_in_channel )
|
||||||
|
{
|
||||||
|
assert(channel_person->person);
|
||||||
|
assert(channel_person->person->nick);
|
||||||
|
if ( strnickcmp(channel_person->person->nick, nick) == 0 )
|
||||||
|
return channel_person;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct channel_person* add_person_to_channel(struct network* state, struct person* person, struct channel* channel)
|
||||||
|
{
|
||||||
|
assert(person);
|
||||||
|
assert(channel);
|
||||||
|
|
||||||
|
assert(person->nick);
|
||||||
|
assert(channel->name);
|
||||||
|
|
||||||
|
assert(!find_person_in_channel(state, person->nick, channel->name));
|
||||||
|
struct channel_person* channel_person = (struct channel_person*)
|
||||||
|
calloc(sizeof(struct channel_person), 1);
|
||||||
|
if ( !channel_person )
|
||||||
|
return NULL;
|
||||||
|
channel_person->channel = channel;
|
||||||
|
channel_person->person = person;
|
||||||
|
|
||||||
|
channel_person->prev_channel_person = NULL;
|
||||||
|
channel_person->next_channel_person = state->channel_people;
|
||||||
|
if ( state->channel_people )
|
||||||
|
state->channel_people->prev_channel_person = channel_person;
|
||||||
|
state->channel_people = channel_person;
|
||||||
|
|
||||||
|
channel_person->prev_person_in_channel = NULL;
|
||||||
|
channel_person->next_person_in_channel = channel->people;
|
||||||
|
if ( channel->people )
|
||||||
|
channel->people->prev_person_in_channel = channel_person;
|
||||||
|
channel->people = channel_person;
|
||||||
|
|
||||||
|
channel_person->prev_channel_for_person = NULL;
|
||||||
|
channel_person->next_channel_for_person = person->channels;
|
||||||
|
if ( person->channels )
|
||||||
|
person->channels->prev_channel_for_person = channel_person;
|
||||||
|
person->channels = channel_person;
|
||||||
|
|
||||||
|
return channel_person;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct channel_person* get_person_in_channel(struct network* state, struct person* person, struct channel* channel)
|
||||||
|
{
|
||||||
|
for ( struct channel_person* result =
|
||||||
|
find_person_in_channel(state, person->nick, channel->name); result; result = NULL )
|
||||||
|
return result;
|
||||||
|
return add_person_to_channel(state, person, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_person_from_channel(struct network* state, struct channel_person* channel_person)
|
||||||
|
{
|
||||||
|
if ( state->channel_people != channel_person )
|
||||||
|
channel_person->prev_channel_person->next_channel_person = channel_person->next_channel_person;
|
||||||
|
else
|
||||||
|
state->channel_people = channel_person->next_channel_person;
|
||||||
|
|
||||||
|
if ( channel_person->next_channel_person )
|
||||||
|
channel_person->next_channel_person->prev_channel_person = channel_person->prev_channel_person;
|
||||||
|
|
||||||
|
if ( channel_person->channel->people != channel_person )
|
||||||
|
channel_person->prev_person_in_channel->next_person_in_channel = channel_person->next_person_in_channel;
|
||||||
|
else
|
||||||
|
channel_person->channel->people = channel_person->next_person_in_channel;
|
||||||
|
|
||||||
|
if ( channel_person->next_person_in_channel )
|
||||||
|
channel_person->next_person_in_channel->prev_person_in_channel = channel_person->prev_person_in_channel;
|
||||||
|
|
||||||
|
if ( channel_person->person->channels != channel_person )
|
||||||
|
channel_person->prev_channel_for_person->next_channel_for_person = channel_person->next_channel_for_person;
|
||||||
|
else
|
||||||
|
channel_person->person->channels = channel_person->next_channel_for_person;
|
||||||
|
|
||||||
|
if ( channel_person->next_channel_for_person )
|
||||||
|
channel_person->next_channel_for_person->prev_channel_for_person = channel_person->prev_channel_for_person;
|
||||||
|
|
||||||
|
free(channel_person);
|
||||||
|
}
|
73
irc/database.h
Normal file
73
irc/database.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* database.h
|
||||||
|
* Data structure for keeping track of channels and people.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DATABASE_H
|
||||||
|
#define DATABASE_H
|
||||||
|
|
||||||
|
struct channel;
|
||||||
|
struct channel_person;
|
||||||
|
struct network;
|
||||||
|
struct person;
|
||||||
|
|
||||||
|
struct person
|
||||||
|
{
|
||||||
|
struct person* prev_person;
|
||||||
|
struct person* next_person;
|
||||||
|
char* nick;
|
||||||
|
struct channel_person* channels;
|
||||||
|
bool always_observable; // myself or having private messaged me
|
||||||
|
};
|
||||||
|
|
||||||
|
struct channel_person
|
||||||
|
{
|
||||||
|
struct channel_person* prev_channel_person;
|
||||||
|
struct channel_person* next_channel_person;
|
||||||
|
struct channel_person* prev_person_in_channel;
|
||||||
|
struct channel_person* next_person_in_channel;
|
||||||
|
struct channel_person* prev_channel_for_person;
|
||||||
|
struct channel_person* next_channel_for_person;
|
||||||
|
struct channel* channel;
|
||||||
|
struct person* person;
|
||||||
|
bool is_operator;
|
||||||
|
bool is_voiced;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct channel
|
||||||
|
{
|
||||||
|
struct channel* prev_channel;
|
||||||
|
struct channel* next_channel;
|
||||||
|
char* name;
|
||||||
|
char* topic;
|
||||||
|
struct channel_person* people;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct channel* find_channel(const struct network* state, const char* channel_name);
|
||||||
|
struct channel* add_channel(struct network* state, const char* channel_name);
|
||||||
|
struct channel* get_channel(struct network* state, const char* channel_name);
|
||||||
|
void remove_channel(struct network* state, struct channel* channel);
|
||||||
|
struct person* find_person(const struct network* state, const char* nick);
|
||||||
|
struct person* add_person(struct network* state, const char* nick);
|
||||||
|
struct person* get_person(struct network* state, const char* nick);
|
||||||
|
void remove_person(struct network* state, struct person* person);
|
||||||
|
struct channel_person* find_person_in_channel(const struct network* state, const char* nick, const char* channel_name);
|
||||||
|
struct channel_person* add_person_to_channel(struct network* state, struct person* person, struct channel* channel);
|
||||||
|
struct channel_person* get_person_in_channel(struct network* state, struct person* person, struct channel* channel);
|
||||||
|
void remove_person_from_channel(struct network* state, struct channel_person* channel_person);
|
||||||
|
|
||||||
|
#endif
|
24
irc/network.h
Normal file
24
irc/network.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef NETWORK_H
|
||||||
|
#define NETWORK_H
|
||||||
|
|
||||||
|
struct account;
|
||||||
|
struct channel;
|
||||||
|
struct channel_person;
|
||||||
|
struct person;
|
||||||
|
|
||||||
|
struct network
|
||||||
|
{
|
||||||
|
struct irc_connection* irc_connection;
|
||||||
|
struct account* accounts;
|
||||||
|
struct channel* channels;
|
||||||
|
struct person* people;
|
||||||
|
struct channel_person* channel_people;
|
||||||
|
struct scrollback* scrollbacks;
|
||||||
|
char* nick;
|
||||||
|
char* real_name;
|
||||||
|
char* password;
|
||||||
|
char* server_hostname;
|
||||||
|
const char* autojoin;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
205
irc/scrollback.c
Normal file
205
irc/scrollback.c
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* scrollback.c
|
||||||
|
* Ordered messages for display.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "scrollback.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
void message_free(struct message* msg)
|
||||||
|
{
|
||||||
|
free(msg->who);
|
||||||
|
free(msg->what);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrollback_free(struct scrollback* sb)
|
||||||
|
{
|
||||||
|
if ( sb->network )
|
||||||
|
{
|
||||||
|
if ( sb->scrollback_prev )
|
||||||
|
sb->scrollback_prev->scrollback_next = sb->scrollback_next;
|
||||||
|
else
|
||||||
|
sb->network->scrollbacks = sb->scrollback_next;
|
||||||
|
if ( sb->scrollback_next )
|
||||||
|
sb->scrollback_next->scrollback_prev = sb->scrollback_prev;
|
||||||
|
sb->scrollback_prev = NULL;
|
||||||
|
sb->scrollback_next = NULL;
|
||||||
|
sb->network = NULL;
|
||||||
|
}
|
||||||
|
for ( size_t i = 0; i < sb->messages_count; i++ )
|
||||||
|
message_free(&sb->messages[i]);
|
||||||
|
free(sb->messages);
|
||||||
|
free(sb->name);
|
||||||
|
free(sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scrollback* find_scrollback_network(const struct network* network)
|
||||||
|
{
|
||||||
|
// TODO: The server hostname can be a valid nick, for instance if the
|
||||||
|
// hostname doesn't contain any dot characters.
|
||||||
|
for ( struct scrollback* sb = network->scrollbacks;
|
||||||
|
sb;
|
||||||
|
sb = sb->scrollback_next )
|
||||||
|
{
|
||||||
|
if ( sb->name[0] == '#' )
|
||||||
|
continue;
|
||||||
|
if ( !strnickcmp(network->server_hostname, sb->name) )
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scrollback* find_scrollback(const struct network* network,
|
||||||
|
const char* name)
|
||||||
|
{
|
||||||
|
assert(name);
|
||||||
|
for ( struct scrollback* sb = network->scrollbacks;
|
||||||
|
sb;
|
||||||
|
sb = sb->scrollback_next )
|
||||||
|
{
|
||||||
|
if ( name[0] == '#' && sb->name[0] == '#' )
|
||||||
|
{
|
||||||
|
if ( strchannelcmp(name + 1, sb->name + 1) == 0 )
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
else if ( name[0] != '#' && sb->name[0] != '#' )
|
||||||
|
{
|
||||||
|
if ( strnickcmp(name + 1, sb->name + 1) == 0 )
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scrollback* add_scrollback(struct network* network, const char* name)
|
||||||
|
{
|
||||||
|
struct scrollback* sb =
|
||||||
|
(struct scrollback*) calloc(1, sizeof(struct scrollback));
|
||||||
|
if ( !sb )
|
||||||
|
return NULL;
|
||||||
|
if ( !(sb->name = strdup(name)) )
|
||||||
|
return scrollback_free(sb), (struct scrollback*) NULL;
|
||||||
|
sb->network = network;
|
||||||
|
sb->scrollback_next = sb->network->scrollbacks;
|
||||||
|
if ( sb->scrollback_next )
|
||||||
|
sb->scrollback_next->scrollback_prev = sb;
|
||||||
|
sb->network->scrollbacks = sb;
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scrollback* get_scrollback(struct network* network, const char* name)
|
||||||
|
{
|
||||||
|
struct scrollback* result = find_scrollback(network, name);
|
||||||
|
if ( result )
|
||||||
|
return result;
|
||||||
|
return add_scrollback(network, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scrollback_add_message(struct scrollback* sb,
|
||||||
|
enum activity activity,
|
||||||
|
const struct message* msg)
|
||||||
|
{
|
||||||
|
if ( sb->messages_count == sb->messages_allocated )
|
||||||
|
{
|
||||||
|
size_t new_allocated = 2 * sb->messages_allocated;
|
||||||
|
if ( new_allocated == 0 )
|
||||||
|
new_allocated = 64;
|
||||||
|
struct message* new_messages = (struct message*)
|
||||||
|
reallocarray(sb->messages, new_allocated, sizeof(struct message));
|
||||||
|
if ( !new_messages )
|
||||||
|
return false;
|
||||||
|
sb->messages = new_messages;
|
||||||
|
sb->messages_allocated = new_allocated;
|
||||||
|
}
|
||||||
|
sb->messages[sb->messages_count++] = *msg;
|
||||||
|
size_t who_width = strlen(msg->who); // TODO: Unicode?
|
||||||
|
if ( sb->who_width < who_width )
|
||||||
|
sb->who_width = who_width;
|
||||||
|
if ( sb->activity < activity )
|
||||||
|
sb->activity = activity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void message_timestamp(struct message* msg)
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
localtime_r(&now, &tm);
|
||||||
|
msg->sec = tm.tm_sec;
|
||||||
|
msg->min = tm.tm_min;
|
||||||
|
msg->hour = tm.tm_hour;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scrollback_print(struct scrollback* sb,
|
||||||
|
enum activity activity,
|
||||||
|
const char* who,
|
||||||
|
const char* what)
|
||||||
|
{
|
||||||
|
struct message msg;
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
message_timestamp(&msg);
|
||||||
|
if ( (msg.who = strdup(who)) &&
|
||||||
|
(msg.what = strdup(what)) &&
|
||||||
|
scrollback_add_message(sb, activity, &msg) )
|
||||||
|
return true;
|
||||||
|
message_free(&msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scrollback_printf(struct scrollback* sb,
|
||||||
|
enum activity activity,
|
||||||
|
const char* who,
|
||||||
|
const char* whatf,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
struct message msg;
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
message_timestamp(&msg);
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, whatf);
|
||||||
|
int len = vasprintf(&msg.what, whatf, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if ( (msg.who = strdup(who)) &&
|
||||||
|
0 <= len &&
|
||||||
|
scrollback_add_message(sb, activity, &msg) )
|
||||||
|
return true;
|
||||||
|
message_free(&msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrollback_clear(struct scrollback* sb)
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < sb->messages_count; i++ )
|
||||||
|
{
|
||||||
|
free(sb->messages[i].who);
|
||||||
|
free(sb->messages[i].what);
|
||||||
|
}
|
||||||
|
sb->messages_count = 0;
|
||||||
|
sb->messages_allocated = 0;
|
||||||
|
free(sb->messages);
|
||||||
|
sb->messages = NULL;
|
||||||
|
}
|
82
irc/scrollback.h
Normal file
82
irc/scrollback.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* scrollback.c
|
||||||
|
* Ordered messages for display.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCROLLBACK_H
|
||||||
|
#define SCROLLBACK_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct network;
|
||||||
|
|
||||||
|
enum activity
|
||||||
|
{
|
||||||
|
ACTIVITY_NONE,
|
||||||
|
ACTIVITY_NONTALK,
|
||||||
|
ACTIVITY_TALK,
|
||||||
|
ACTIVITY_HIGHLIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct message
|
||||||
|
{
|
||||||
|
int hour;
|
||||||
|
int min;
|
||||||
|
int sec;
|
||||||
|
char* who;
|
||||||
|
char* what;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scrollback
|
||||||
|
{
|
||||||
|
struct network* network;
|
||||||
|
struct scrollback* scrollback_prev;
|
||||||
|
struct scrollback* scrollback_next;
|
||||||
|
char* name;
|
||||||
|
struct message* messages;
|
||||||
|
size_t messages_count;
|
||||||
|
size_t messages_allocated;
|
||||||
|
size_t who_width;
|
||||||
|
enum activity activity;
|
||||||
|
};
|
||||||
|
|
||||||
|
void message_free(struct message* msg);
|
||||||
|
void scrollback_free(struct scrollback* sb);
|
||||||
|
struct scrollback* find_scrollback_network(const struct network* network);
|
||||||
|
struct scrollback* find_scrollback(const struct network* network,
|
||||||
|
const char* name);
|
||||||
|
struct scrollback* add_scrollback(struct network* network,
|
||||||
|
const char* name);
|
||||||
|
struct scrollback* get_scrollback(struct network* network,
|
||||||
|
const char* name);
|
||||||
|
bool scrollback_add_message(struct scrollback* sb,
|
||||||
|
enum activity activity,
|
||||||
|
const struct message* msg);
|
||||||
|
bool scrollback_print(struct scrollback* sb,
|
||||||
|
enum activity activity,
|
||||||
|
const char* who,
|
||||||
|
const char* what);
|
||||||
|
__attribute__((format(printf, 4, 5)))
|
||||||
|
bool scrollback_printf(struct scrollback* sb,
|
||||||
|
enum activity activity,
|
||||||
|
const char* who,
|
||||||
|
const char* whatf,
|
||||||
|
...);
|
||||||
|
void scrollback_clear(struct scrollback* sb);
|
||||||
|
|
||||||
|
#endif
|
34
irc/string.c
Normal file
34
irc/string.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* string.c
|
||||||
|
* String utility functions and compatibility.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
// TODO: Implement this properly in accordance with IRC RFC rules.
|
||||||
|
int strchannelcmp(const char* a, const char* b)
|
||||||
|
{
|
||||||
|
return strcasecmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this properly in accordance with IRC RFC rules.
|
||||||
|
int strnickcmp(const char* a, const char* b)
|
||||||
|
{
|
||||||
|
return strcasecmp(a, b);
|
||||||
|
}
|
28
irc/string.h
Normal file
28
irc/string.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* string.h
|
||||||
|
* String utility functions and compatibility.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STRING_H
|
||||||
|
#define STRING_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
int strchannelcmp(const char* a, const char* b);
|
||||||
|
int strnickcmp(const char* a, const char* b);
|
||||||
|
|
||||||
|
#endif
|
545
irc/ui.c
Normal file
545
irc/ui.c
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* ui.c
|
||||||
|
* User Interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "scrollback.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
struct cell
|
||||||
|
{
|
||||||
|
wchar_t c;
|
||||||
|
int fgcolor;
|
||||||
|
int bgcolor;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct termios saved_termios;
|
||||||
|
|
||||||
|
void tty_show(struct cell* cells, size_t cols, size_t rows)
|
||||||
|
{
|
||||||
|
printf("\e[H");
|
||||||
|
int fgcolor = -1;
|
||||||
|
int bgcolor = -1;
|
||||||
|
mbstate_t ps;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
for ( size_t r = 0; r < rows; r++ )
|
||||||
|
{
|
||||||
|
for ( size_t c = 0; c < cols; c++ )
|
||||||
|
{
|
||||||
|
struct cell* cell = &cells[r * cols + c];
|
||||||
|
if ( fgcolor != cell->fgcolor )
|
||||||
|
{
|
||||||
|
printf("\e[%im", cell->fgcolor);
|
||||||
|
fgcolor = cell->fgcolor;
|
||||||
|
}
|
||||||
|
if ( bgcolor != cell->bgcolor )
|
||||||
|
{
|
||||||
|
printf("\e[%im", cell->bgcolor);
|
||||||
|
bgcolor = cell->bgcolor;
|
||||||
|
}
|
||||||
|
char mb[MB_CUR_MAX];
|
||||||
|
size_t amount = wcrtomb(mb, cell->c, &ps);
|
||||||
|
if ( amount == (size_t) -1 )
|
||||||
|
continue;
|
||||||
|
fwrite(mb, 1, amount, stdout);
|
||||||
|
}
|
||||||
|
if ( r + 1 != rows )
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_sigquit(int sig)
|
||||||
|
{
|
||||||
|
// TODO: This is not async signal safe.
|
||||||
|
ui_destroy(NULL);
|
||||||
|
// TODO: Use sigaction so the handler only runs once.
|
||||||
|
//raise(sig);
|
||||||
|
(void) sig;
|
||||||
|
raise(SIGKILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_initialize(struct ui* ui, struct network* network)
|
||||||
|
{
|
||||||
|
memset(ui, 0, sizeof(*ui));
|
||||||
|
ui->network = network;
|
||||||
|
ui->current = find_scrollback_network(network);
|
||||||
|
struct winsize ws;
|
||||||
|
if ( ioctl(1, TIOCGWINSZ, &ws) < 0 )
|
||||||
|
err(1, "stdout: ioctl: TIOCGWINSZ");
|
||||||
|
if ( tcgetattr(0, &saved_termios) < 0 )
|
||||||
|
err(1, "stdin: tcgetattr");
|
||||||
|
struct termios tcattr;
|
||||||
|
memcpy(&tcattr, &saved_termios, sizeof(struct termios));
|
||||||
|
tcattr.c_lflag &= ~(ECHO | ICANON | IEXTEN);
|
||||||
|
tcattr.c_iflag |= ICRNL | ISIG;
|
||||||
|
tcattr.c_cc[VMIN] = 1;
|
||||||
|
tcattr.c_cc[VTIME] = 0;
|
||||||
|
signal(SIGINT, SIG_IGN);
|
||||||
|
signal(SIGQUIT, on_sigquit);
|
||||||
|
tcsetattr(0, TCSADRAIN, &tcattr);
|
||||||
|
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
|
||||||
|
{
|
||||||
|
printf("\e[?1049h");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_destroy(struct ui* ui)
|
||||||
|
{
|
||||||
|
// TODO.
|
||||||
|
(void) ui;
|
||||||
|
// TODO: This should be done in an atexit handler as well.
|
||||||
|
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
|
||||||
|
{
|
||||||
|
printf("\e[?1049l");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
tcsetattr(0, TCSADRAIN, &saved_termios);
|
||||||
|
}
|
||||||
|
|
||||||
|
void increment_offset(size_t* o_ptr, size_t* line_ptr, size_t cols)
|
||||||
|
{
|
||||||
|
if ( (*o_ptr)++ == cols )
|
||||||
|
{
|
||||||
|
*o_ptr = 0;
|
||||||
|
(*line_ptr)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_render(struct ui* ui)
|
||||||
|
{
|
||||||
|
mbstate_t ps;
|
||||||
|
struct winsize ws;
|
||||||
|
if ( ioctl(1, TIOCGWINSZ, &ws) < 0 )
|
||||||
|
err(1, "stdout: ioctl: TIOCGWINSZ");
|
||||||
|
size_t cols = ws.ws_col;
|
||||||
|
size_t rows = ws.ws_row;
|
||||||
|
|
||||||
|
struct cell* cells = calloc(sizeof(struct cell) * cols, rows);
|
||||||
|
if ( !cells )
|
||||||
|
err(1, "calloc");
|
||||||
|
|
||||||
|
for ( size_t r = 0; r < rows; r++ )
|
||||||
|
{
|
||||||
|
for ( size_t c = 0; c < cols; c++ )
|
||||||
|
{
|
||||||
|
struct cell* cell = &cells[r * cols + c];
|
||||||
|
cell->c = L' ';
|
||||||
|
cell->fgcolor = 0;
|
||||||
|
cell->bgcolor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: What if the terminal isn't large enough?
|
||||||
|
struct scrollback* sb = ui->current;
|
||||||
|
|
||||||
|
sb->activity = ACTIVITY_NONE;
|
||||||
|
|
||||||
|
size_t title_from = 0;
|
||||||
|
size_t when_offset = 0;
|
||||||
|
size_t when_width = 2 + 1 + 2 + 1 + 2;
|
||||||
|
size_t who_offset = when_offset + when_width + 1;
|
||||||
|
size_t who_width = sb->who_width;
|
||||||
|
size_t div_offset = who_offset + who_width + 1;
|
||||||
|
size_t what_offset = div_offset + 2;
|
||||||
|
size_t what_width = cols - what_offset;
|
||||||
|
size_t input_width = cols;
|
||||||
|
|
||||||
|
size_t input_num_lines = 1;
|
||||||
|
for ( size_t i = 0, o = 0; i < ui->input_used; i++ )
|
||||||
|
{
|
||||||
|
wchar_t wc = ui->input[i];
|
||||||
|
int w = wcwidth(wc);
|
||||||
|
if ( w < 0 || w == 0 )
|
||||||
|
continue;
|
||||||
|
if ( input_width <= o )
|
||||||
|
{
|
||||||
|
input_num_lines++;
|
||||||
|
o = 0;
|
||||||
|
}
|
||||||
|
o += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* title;
|
||||||
|
if ( asprintf(&title, "%s @ %s / %s", ui->network->nick,
|
||||||
|
ui->network->server_hostname, ui->current->name) < 0 )
|
||||||
|
err(1, "asprintf");
|
||||||
|
size_t title_len = strlen(title);
|
||||||
|
size_t title_how_many = cols < title_len ? cols : title_len;
|
||||||
|
size_t title_offset = (cols - title_how_many) / 2;
|
||||||
|
for ( size_t i = 0; i < title_how_many; i++ )
|
||||||
|
{
|
||||||
|
char c = title[i];
|
||||||
|
size_t cell_r = title_from;
|
||||||
|
size_t cell_c = title_offset + i;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = btowc((unsigned char) c);
|
||||||
|
}
|
||||||
|
free(title);
|
||||||
|
|
||||||
|
size_t scrollbacks_from = title_from + 1;
|
||||||
|
size_t scrollbacks_lines = 1;
|
||||||
|
size_t scrollbacks_o = 0;
|
||||||
|
for ( struct scrollback* iter = ui->network->scrollbacks;
|
||||||
|
iter;
|
||||||
|
iter = iter->scrollback_next )
|
||||||
|
{
|
||||||
|
if ( iter->scrollback_prev )
|
||||||
|
{
|
||||||
|
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
|
||||||
|
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
|
||||||
|
}
|
||||||
|
for ( size_t i = 0; iter->name[i]; i++ )
|
||||||
|
{
|
||||||
|
char c = iter->name[i];
|
||||||
|
size_t cell_r = scrollbacks_from + (scrollbacks_lines - 1);
|
||||||
|
size_t cell_c = scrollbacks_o;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
int fgcolor = 0;
|
||||||
|
if ( iter == sb )
|
||||||
|
fgcolor = 1; // TODO: Boldness should be its own property.
|
||||||
|
else if ( iter->activity == ACTIVITY_NONTALK )
|
||||||
|
fgcolor = 31;
|
||||||
|
else if ( iter->activity == ACTIVITY_TALK )
|
||||||
|
fgcolor = 91;
|
||||||
|
else if ( iter->activity == ACTIVITY_HIGHLIGHT )
|
||||||
|
fgcolor = 94;
|
||||||
|
cell->c = btowc((unsigned char) c);
|
||||||
|
cell->fgcolor = fgcolor;
|
||||||
|
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t horhigh_from = scrollbacks_from + scrollbacks_lines;
|
||||||
|
|
||||||
|
for ( size_t c = 0; c < cols; c++ )
|
||||||
|
{
|
||||||
|
size_t cell_r = horhigh_from;
|
||||||
|
size_t cell_c = c;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = c == div_offset ? L'┬' : L'─';
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sb_from = horhigh_from + 1;
|
||||||
|
|
||||||
|
// TODO: What if the input is too big?
|
||||||
|
size_t input_bottom = rows - input_num_lines;
|
||||||
|
size_t input_offset = 0;
|
||||||
|
for ( size_t i = 0, o = 0, line = 0; i < ui->input_used; i++ )
|
||||||
|
{
|
||||||
|
wchar_t wc = ui->input[i];
|
||||||
|
int w = wcwidth(wc);
|
||||||
|
if ( w < 0 || w == 0 )
|
||||||
|
continue;
|
||||||
|
if ( input_width <= o )
|
||||||
|
{
|
||||||
|
line++;
|
||||||
|
o = 0;
|
||||||
|
}
|
||||||
|
// TODO: If 1 < w.
|
||||||
|
size_t cell_r = input_bottom + line;
|
||||||
|
size_t cell_c = input_offset + o;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = wc;
|
||||||
|
o += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t horlow_from = input_bottom - 1;
|
||||||
|
|
||||||
|
for ( size_t c = 0; c < cols; c++ )
|
||||||
|
{
|
||||||
|
size_t cell_r = horlow_from;
|
||||||
|
size_t cell_c = c;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = c == div_offset ? L'┴' : L'─';
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sb_to = horlow_from;
|
||||||
|
|
||||||
|
for ( size_t r = sb_to - 1; r != sb_from - 1; r-- )
|
||||||
|
{
|
||||||
|
size_t cell_r = r;
|
||||||
|
size_t cell_c = div_offset;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = L'│';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( size_t r = sb_to - 1, m = sb->messages_count - 1;
|
||||||
|
r != (sb_from - 1) && m != SIZE_MAX;
|
||||||
|
r--, m-- )
|
||||||
|
{
|
||||||
|
struct message* msg = &sb->messages[m];
|
||||||
|
size_t num_lines = 1;
|
||||||
|
size_t max_lines = sb_from - r + 1;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
for ( size_t i = 0, o = 0; msg->what[i]; )
|
||||||
|
{
|
||||||
|
wchar_t wc;
|
||||||
|
size_t amount = mbrtowc(&wc, msg->what + i, SIZE_MAX, &ps);
|
||||||
|
if ( amount == (size_t) -1 || amount == (size_t) -2 )
|
||||||
|
{
|
||||||
|
// TODO.
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i += amount;
|
||||||
|
int w = wcwidth(wc);
|
||||||
|
if ( w < 0 || w == 0 )
|
||||||
|
continue;
|
||||||
|
if ( what_width <= o )
|
||||||
|
{
|
||||||
|
num_lines++;
|
||||||
|
o = 0;
|
||||||
|
}
|
||||||
|
o += w;
|
||||||
|
}
|
||||||
|
size_t how_many_lines = max_lines < num_lines ? max_lines : num_lines;
|
||||||
|
size_t first_line = num_lines - how_many_lines;
|
||||||
|
if ( 1 < how_many_lines )
|
||||||
|
r -= how_many_lines - 1;
|
||||||
|
if ( first_line == 0 )
|
||||||
|
{
|
||||||
|
char when[2 + 1 + 2 + 1 + 2 + 1 + 1];
|
||||||
|
snprintf(when, sizeof(when), "%02i:%02i:%02i ",
|
||||||
|
msg->hour, msg->min, msg->sec);
|
||||||
|
for ( size_t i = 0; when[i]; i++ )
|
||||||
|
{
|
||||||
|
size_t cell_r = r;
|
||||||
|
size_t cell_c = when_offset + i;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = btowc((unsigned char) when[i]);
|
||||||
|
}
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
size_t msg_who_width = strlen(msg->who);
|
||||||
|
size_t msg_who_how_many = who_width < msg_who_width ? who_width : msg_who_width;
|
||||||
|
size_t msg_who_first = msg_who_width - msg_who_how_many;
|
||||||
|
size_t msg_who_offset = who_width - msg_who_how_many;
|
||||||
|
for ( size_t i = 0; i < msg_who_how_many; i++ )
|
||||||
|
{
|
||||||
|
char c = msg->who[msg_who_first + i];
|
||||||
|
size_t cell_r = r;
|
||||||
|
size_t cell_c = who_offset + msg_who_offset + i;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = btowc((unsigned char) c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( size_t i = 0, o = 0, line = 0; msg->what[i]; )
|
||||||
|
{
|
||||||
|
wchar_t wc;
|
||||||
|
size_t amount = mbrtowc(&wc, msg->what + i, SIZE_MAX, &ps);
|
||||||
|
if ( amount == (size_t) -1 || amount == (size_t) -2 )
|
||||||
|
{
|
||||||
|
// TODO.
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i += amount;
|
||||||
|
int w = wcwidth(wc);
|
||||||
|
if ( w < 0 || w == 0 )
|
||||||
|
continue;
|
||||||
|
if ( what_width <= o )
|
||||||
|
{
|
||||||
|
line++;
|
||||||
|
o = 0;
|
||||||
|
}
|
||||||
|
// TODO: If 1 < w.
|
||||||
|
if ( first_line <= line )
|
||||||
|
{
|
||||||
|
size_t cell_r = r + line - first_line;
|
||||||
|
size_t cell_c = what_offset + o;
|
||||||
|
struct cell* cell = &cells[cell_r * cols + cell_c];
|
||||||
|
cell->c = wc;
|
||||||
|
}
|
||||||
|
o += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) ui;
|
||||||
|
|
||||||
|
tty_show(cells, cols, rows);
|
||||||
|
|
||||||
|
free(cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_command(const char* input,
|
||||||
|
const char* cmd,
|
||||||
|
const char** param)
|
||||||
|
{
|
||||||
|
size_t cmdlen = strlen(cmd);
|
||||||
|
if ( strncmp(input, cmd, cmdlen) != 0 )
|
||||||
|
return false;
|
||||||
|
if ( !input[cmdlen] )
|
||||||
|
{
|
||||||
|
if ( param )
|
||||||
|
*param = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( input[cmdlen] != ' ' )
|
||||||
|
return false;
|
||||||
|
if ( !param )
|
||||||
|
return false;
|
||||||
|
*param = input + cmdlen + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_command_param(const char* input,
|
||||||
|
const char* cmd,
|
||||||
|
const char** param)
|
||||||
|
{
|
||||||
|
if ( !is_command(input, cmd, param) )
|
||||||
|
return false;
|
||||||
|
if ( !*param )
|
||||||
|
return false; // TODO: Help message in scrollback.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_input_char(struct ui* ui, char c)
|
||||||
|
{
|
||||||
|
wchar_t wc;
|
||||||
|
size_t amount = mbrtowc(&wc, &c, 1, &ui->input_ps);
|
||||||
|
if ( amount == (size_t) -2 )
|
||||||
|
return;
|
||||||
|
if ( amount == (size_t) -1 )
|
||||||
|
{
|
||||||
|
// TODO.
|
||||||
|
memset(&ui->input_ps, 0, sizeof(ui->input_ps));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( wc == L'\b' || wc == 127 )
|
||||||
|
{
|
||||||
|
if ( 0 < ui->input_used )
|
||||||
|
ui->input_used--;
|
||||||
|
}
|
||||||
|
else if ( wc == L'\f' /* ^L */ )
|
||||||
|
{
|
||||||
|
scrollback_clear(ui->current);
|
||||||
|
// TODO: Schedule full redraw?
|
||||||
|
}
|
||||||
|
else if ( wc == L'\n' )
|
||||||
|
{
|
||||||
|
char input[4 * sizeof(ui->input) / sizeof(ui->input[0])];
|
||||||
|
mbstate_t ps;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
const wchar_t* wcs = ui->input;
|
||||||
|
size_t amount = wcsnrtombs(input, &wcs, ui->input_used, sizeof(input), &ps);
|
||||||
|
ui->input_used = 0;
|
||||||
|
if ( amount == (size_t) -1 )
|
||||||
|
return;
|
||||||
|
input[amount < sizeof(input) ? amount : amount - 1] = '\0';
|
||||||
|
struct irc_connection* conn = ui->network->irc_connection;
|
||||||
|
const char* who = ui->network->nick;
|
||||||
|
const char* where = ui->current->name;
|
||||||
|
const char* param;
|
||||||
|
if ( input[0] == '/' && input[1] != '/' )
|
||||||
|
{
|
||||||
|
if ( !input[1] )
|
||||||
|
return;
|
||||||
|
if ( is_command_param(input, "/w", ¶m) ||
|
||||||
|
is_command_param(input, "/window", ¶m) )
|
||||||
|
{
|
||||||
|
struct scrollback* sb = find_scrollback(ui->network, param);
|
||||||
|
if ( sb )
|
||||||
|
ui->current = sb;
|
||||||
|
}
|
||||||
|
else if ( is_command_param(input, "/query", ¶m) )
|
||||||
|
{
|
||||||
|
if ( param[0] == '#' )
|
||||||
|
return; // TODO: Help in scrollback.
|
||||||
|
struct scrollback* sb = get_scrollback(ui->network, param);
|
||||||
|
if ( sb )
|
||||||
|
ui->current = sb;
|
||||||
|
}
|
||||||
|
else if ( is_command_param(input, "/join", ¶m) )
|
||||||
|
{
|
||||||
|
irc_command_join(conn, param);
|
||||||
|
struct scrollback* sb = get_scrollback(ui->network, param);
|
||||||
|
if ( sb )
|
||||||
|
ui->current = sb;
|
||||||
|
}
|
||||||
|
// TODO: Make it default to the current channel if any.
|
||||||
|
else if ( is_command_param(input, "/part", ¶m) )
|
||||||
|
{
|
||||||
|
irc_command_part(conn, param);
|
||||||
|
}
|
||||||
|
else if ( is_command(input, "/quit", ¶m) )
|
||||||
|
{
|
||||||
|
irc_command_quit(conn, param ? param : "Quiting");
|
||||||
|
}
|
||||||
|
else if ( is_command_param(input, "/nick", ¶m) )
|
||||||
|
{
|
||||||
|
irc_command_nick(conn, param);
|
||||||
|
}
|
||||||
|
else if ( is_command_param(input, "/raw", ¶m) )
|
||||||
|
{
|
||||||
|
irc_transmit_string(conn, param);
|
||||||
|
}
|
||||||
|
else if ( is_command_param(input, "/me", ¶m) )
|
||||||
|
{
|
||||||
|
scrollback_printf(ui->current, ACTIVITY_NONE, "*", "%s %s",
|
||||||
|
who, param);
|
||||||
|
irc_command_privmsgf(conn, where, "\x01""ACTION %s""\x01",
|
||||||
|
param);
|
||||||
|
}
|
||||||
|
else if ( is_command(input, "/clear", ¶m) )
|
||||||
|
{
|
||||||
|
scrollback_clear(ui->current);
|
||||||
|
}
|
||||||
|
// TODO: /ban
|
||||||
|
// TODO: /ctcp
|
||||||
|
// TODO: /deop
|
||||||
|
// TODO: /devoice
|
||||||
|
// TODO: /kick
|
||||||
|
// TODO: /mode
|
||||||
|
// TODO: /op
|
||||||
|
// TODO: /quiet
|
||||||
|
// TODO: /topic
|
||||||
|
// TODO: /voice
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scrollback_printf(ui->current, ACTIVITY_NONE, "*",
|
||||||
|
"%s :Unknown command", input + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char* what = input;
|
||||||
|
if ( what[0] == '/' )
|
||||||
|
what++;
|
||||||
|
scrollback_print(ui->current, ACTIVITY_NONE, who, what);
|
||||||
|
irc_command_privmsg(conn, where, what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ui->input_used < sizeof(ui->input) / sizeof(ui->input[0]) )
|
||||||
|
ui->input[ui->input_used++] = wc;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -13,30 +13,30 @@
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*
|
*
|
||||||
* rules.h
|
* ui.h
|
||||||
* Determines whether a given path is included in the filesystem.
|
* User Interface.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef RULES_H
|
#ifndef UI_H
|
||||||
#define RULES_H
|
#define UI_H
|
||||||
|
|
||||||
enum InclusionRuleType
|
#include <wchar.h>
|
||||||
|
|
||||||
|
struct network;
|
||||||
|
struct scrollback;
|
||||||
|
|
||||||
|
struct ui
|
||||||
{
|
{
|
||||||
RULE_INCLUDE,
|
struct network* network;
|
||||||
RULE_EXCLUDE,
|
struct scrollback* current;
|
||||||
|
wchar_t input[512];
|
||||||
|
size_t input_used;
|
||||||
|
mbstate_t input_ps;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InclusionRule
|
void ui_initialize(struct ui* ui, struct network* network);
|
||||||
{
|
void ui_render(struct ui* ui);
|
||||||
char* pattern;
|
void ui_input_char(struct ui* ui, char c);
|
||||||
enum InclusionRuleType rule;
|
void ui_destroy(struct ui* ui);
|
||||||
};
|
|
||||||
|
|
||||||
bool IncludesPath(const char* path);
|
|
||||||
bool AddRule(struct InclusionRule* rule);
|
|
||||||
bool AddRulesFromFile(FILE* fp, const char* fpname);
|
|
||||||
bool AddManifestFromFile(FILE* fp, const char* fpname);
|
|
||||||
bool AddManifestPath(const char* path);
|
|
||||||
bool ChangeRulesAmount(size_t newnum);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
2
iso9660/.gitignore
vendored
Normal file
2
iso9660/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
iso9660fs
|
||||||
|
*.o
|
33
iso9660/Makefile
Normal file
33
iso9660/Makefile
Normal 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
108
iso9660/block.cpp
Normal 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
53
iso9660/block.h
Normal 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
107
iso9660/device.cpp
Normal 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
51
iso9660/device.h
Normal 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
87
iso9660/filesystem.cpp
Normal 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
52
iso9660/filesystem.h
Normal 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
701
iso9660/fsmarshall.cpp
Normal 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
31
iso9660/fsmarshall.h
Normal 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
572
iso9660/fuse.cpp
Normal 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
|
|
@ -13,20 +13,20 @@
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*
|
*
|
||||||
* serialize.h
|
* fuse.h
|
||||||
* Import and export binary data structures
|
* FUSE frontend.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SERIALIZE_H
|
#ifndef FUSE_H
|
||||||
#define SERIALIZE_H
|
#define FUSE_H
|
||||||
|
|
||||||
#include <sortix/initrd.h>
|
class Device;
|
||||||
|
class Filesystem;
|
||||||
|
|
||||||
void import_initrd_superblock(struct initrd_superblock* obj);
|
int iso9660_fuse_main(const char* argv0,
|
||||||
void export_initrd_superblock(struct initrd_superblock* obj);
|
const char* mount_path,
|
||||||
void import_initrd_inode(struct initrd_inode* obj);
|
bool foreground,
|
||||||
void export_initrd_inode(struct initrd_inode* obj);
|
Filesystem* fs,
|
||||||
void import_initrd_dirent(struct initrd_dirent* obj);
|
Device* dev);
|
||||||
void export_initrd_dirent(struct initrd_dirent* obj);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
614
iso9660/inode.cpp
Normal file
614
iso9660/inode.cpp
Normal 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
83
iso9660/inode.h
Normal 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
|
128
iso9660/iso9660.h
Normal file
128
iso9660/iso9660.h
Normal 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
278
iso9660/iso9660fs.cpp
Normal 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
33
iso9660/iso9660fs.h
Normal 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
49
iso9660/util.h
Normal 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
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -29,6 +29,7 @@
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
|
|
||||||
size_t aux_allocated = 0;
|
size_t aux_allocated = 0;
|
||||||
|
size_t aux_leaked = 0;
|
||||||
size_t heap_allocated = 0;
|
size_t heap_allocated = 0;
|
||||||
kthread_mutex_t alloc_lock = KTHREAD_MUTEX_INITIALIZER;
|
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;
|
addr_t aux_reached = kmem_from + kmem_size - aux_allocated;
|
||||||
if ( alloc->from == aux_reached )
|
if ( alloc->from == aux_reached )
|
||||||
aux_allocated -= alloc->size;
|
aux_allocated -= alloc->size;
|
||||||
|
else
|
||||||
|
aux_leaked += alloc->size;
|
||||||
|
|
||||||
alloc->from = 0;
|
alloc->from = 0;
|
||||||
alloc->size = 0;
|
alloc->size = 0;
|
||||||
|
@ -98,6 +101,17 @@ void ShrinkHeap(size_t decrease)
|
||||||
heap_allocated -= 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
|
// 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
|
// and it already uses an internal lock, and heap_allocated will not change
|
||||||
// unless the heap calls ExpandHeap.
|
// unless the heap calls ExpandHeap.
|
||||||
|
|
213
kernel/com.cpp
213
kernel/com.cpp
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -17,6 +17,8 @@
|
||||||
* Handles communication to COM serial ports.
|
* Handles communication to COM serial ports.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
#include <sortix/kernel/thread.h>
|
#include <sortix/kernel/thread.h>
|
||||||
|
|
||||||
#include "com.h"
|
#include "com.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
extern "C" unsigned char nullpage[4096];
|
extern "C" unsigned char nullpage[4096];
|
||||||
|
|
||||||
|
@ -136,17 +139,30 @@ static inline bool CanWriteByte(uint16_t port)
|
||||||
return inport8(port + LSR) & LSR_THRE;
|
return inport8(port + LSR) & LSR_THRE;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DevCOMPort : public AbstractInode
|
class DevCOMPort : public TTY
|
||||||
{
|
{
|
||||||
public:
|
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 ~DevCOMPort();
|
||||||
|
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||||
virtual int sync(ioctx_t* ctx);
|
virtual int sync(ioctx_t* ctx);
|
||||||
virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count);
|
virtual void tty_output(const unsigned char* buffer, size_t length);
|
||||||
virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count);
|
|
||||||
|
public:
|
||||||
|
bool Initialize(int interrupt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void InterruptHandler(struct interrupt_context*, void*);
|
||||||
|
static void InterruptWorkHandler(void* context);
|
||||||
|
void OnInterrupt();
|
||||||
|
void InterruptWork();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kthread_mutex_t port_lock;
|
kthread_mutex_t port_lock;
|
||||||
|
struct interrupt_handler irq_registration;
|
||||||
|
struct interrupt_work interrupt_work;
|
||||||
|
struct winsize ws;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
uint8_t pending_input_byte;
|
uint8_t pending_input_byte;
|
||||||
bool has_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,
|
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 = port;
|
||||||
this->port_lock = KTHREAD_MUTEX_INITIALIZER;
|
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;
|
this->has_pending_input_byte = false;
|
||||||
|
interrupt_work.handler = InterruptWorkHandler;
|
||||||
|
interrupt_work.context = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevCOMPort::~DevCOMPort()
|
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*/)
|
int DevCOMPort::sync(ioctx_t* /*ctx*/)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&port_lock);
|
ScopedLock lock(&port_lock);
|
||||||
|
@ -179,57 +263,9 @@ int DevCOMPort::sync(ioctx_t* /*ctx*/)
|
||||||
return 0;
|
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 < length; i++ )
|
||||||
|
|
||||||
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++ )
|
|
||||||
{
|
{
|
||||||
unsigned long attempt = 0;
|
unsigned long attempt = 0;
|
||||||
while ( !CanWriteByte(port) )
|
while ( !CanWriteByte(port) )
|
||||||
|
@ -237,7 +273,7 @@ ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count)
|
||||||
attempt++;
|
attempt++;
|
||||||
if ( attempt <= 10 )
|
if ( attempt <= 10 )
|
||||||
continue;
|
continue;
|
||||||
if ( attempt <= 15 && !(ctx->dflags & O_NONBLOCK) )
|
if ( attempt <= 15 )
|
||||||
{
|
{
|
||||||
kthread_mutex_unlock(&port_lock);
|
kthread_mutex_unlock(&port_lock);
|
||||||
kthread_yield();
|
kthread_yield();
|
||||||
|
@ -245,20 +281,16 @@ ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( i )
|
if ( i )
|
||||||
return (ssize_t) i;
|
return;
|
||||||
if ( ctx->dflags & O_NONBLOCK )
|
// TODO: This is problematic.
|
||||||
return errno = EWOULDBLOCK, -1;
|
|
||||||
if ( Signal::IsPending() )
|
if ( Signal::IsPending() )
|
||||||
return errno = EINTR, -1;
|
{
|
||||||
|
errno = EINTR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
outport8(port + TXR, buffer[i]);
|
||||||
uint8_t value;
|
|
||||||
if ( !ctx->copy_from_src(&value, src + i, sizeof(value)) )
|
|
||||||
return i ? (ssize_t) i : -1;
|
|
||||||
outport8(port + TXR, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ssize_t) count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Ref<DevCOMPort> com_devices[1 + NUM_COM_PORTS];
|
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);
|
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++ )
|
for ( size_t i = 1; i <= NUM_COM_PORTS; i++ )
|
||||||
{
|
{
|
||||||
if ( !com_ports[i] )
|
if ( !com_ports[i] )
|
||||||
|
@ -304,13 +321,17 @@ void Init(const char* devpath, Ref<Descriptor> slashdev)
|
||||||
com_devices[i] = Ref<DevCOMPort>();
|
com_devices[i] = Ref<DevCOMPort>();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
com_devices[i] = Ref<DevCOMPort>(new DevCOMPort(slashdev->dev, 0, 0, 0660, com_ports[i]));
|
char ttyname[TTY_NAME_MAX+1];
|
||||||
if ( !com_devices[i] )
|
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);
|
PanicF("Unable to allocate device for COM port %zu", i);
|
||||||
char name[3 + sizeof(size_t) * 3];
|
com_devices[i] = com;
|
||||||
snprintf(name, sizeof(name), "com%zu", i);
|
int interrupt = i == 1 || i == 3 ? Interrupt::IRQ4 : Interrupt::IRQ3;
|
||||||
if ( LinkInodeInDir(&ctx, slashdev, name, com_devices[i]) != 0 )
|
com->Initialize(interrupt);
|
||||||
PanicF("Unable to link %s/%s to COM port driver.", devpath, name);
|
if ( LinkInodeInDir(&ctx, slashdev, ttyname, com) != 0 )
|
||||||
|
PanicF("Unable to link %s/%s to COM port driver.", devpath, ttyname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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;
|
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);
|
struct timespec delay = timespec_make(0, 400);
|
||||||
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
|
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
|
||||||
clock->SleepDelay(delay);
|
clock->SleepDelay(delay);
|
||||||
|
#else
|
||||||
|
for ( int i = 0; i < 14; i++ )
|
||||||
|
inport8(port_base + REG_STATUS);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel::Channel()
|
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_base + REG_DRIVE_SELECT, value);
|
||||||
//outport8(port_control, value); // TODO: Or is it port_control we use?
|
//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?
|
// 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;
|
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++ )
|
for ( unsigned int i = 0; i < 2; i++ )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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';
|
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);
|
struct timespec delay = timespec_make(0, 400);
|
||||||
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
|
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
|
||||||
clock->SleepDelay(delay);
|
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)
|
Port::Port(Channel* channel, unsigned int port_index)
|
||||||
|
@ -176,7 +182,7 @@ retry_identify_packet:
|
||||||
outport8(channel->port_base + REG_COMMAND,
|
outport8(channel->port_base + REG_COMMAND,
|
||||||
is_packet_interface ? CMD_IDENTIFY_PACKET : CMD_IDENTIFY);
|
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
|
// TODO: The status polling logic should be double-checked against some
|
||||||
// formal specification telling how this should properly be done.
|
// formal specification telling how this should properly be done.
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012, 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.
|
|
||||||
*
|
|
||||||
* sortix/initrd.h
|
|
||||||
* The Sortix init ramdisk filesystem format.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INCLUDE_SORTIX_INITRD_H
|
|
||||||
#define _INCLUDE_SORTIX_INITRD_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define INITRD_ALGO_CRC32 0
|
|
||||||
#define INITRD_ALGO_NONE 1
|
|
||||||
|
|
||||||
#define INITRD_S_IXOTH 01
|
|
||||||
#define INITRD_S_IWOTH 02
|
|
||||||
#define INITRD_S_IROTH 03
|
|
||||||
#define INITRD_S_IRWXO 07
|
|
||||||
#define INITRD_S_IXGRP 010
|
|
||||||
#define INITRD_S_IWGRP 020
|
|
||||||
#define INITRD_S_IRGRP 040
|
|
||||||
#define INITRD_S_IRWXG 070
|
|
||||||
#define INITRD_S_IXUSR 0100
|
|
||||||
#define INITRD_S_IWUSR 0200
|
|
||||||
#define INITRD_S_IRUSR 0400
|
|
||||||
#define INITRD_S_IRWXU 0700
|
|
||||||
#define INITRD_S_IFMT 0xF000
|
|
||||||
#define INITRD_S_IFSOCK 0xC000
|
|
||||||
#define INITRD_S_IFLNK 0xA000
|
|
||||||
#define INITRD_S_IFREG 0x8000
|
|
||||||
#define INITRD_S_IFBLK 0x6000
|
|
||||||
#define INITRD_S_IFDIR 0x4000
|
|
||||||
#define INITRD_S_IFCHR 0x2000
|
|
||||||
#define INITRD_S_IFIFO 0x1000
|
|
||||||
#define INITRD_S_ISUID 0x0800
|
|
||||||
#define INITRD_S_ISGID 0x0400
|
|
||||||
#define INITRD_S_ISVTX 0x0200
|
|
||||||
#define INITRD_S_ISSOCK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFSOCK)
|
|
||||||
#define INITRD_S_ISLNK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFLNK)
|
|
||||||
#define INITRD_S_ISREG(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFREG)
|
|
||||||
#define INITRD_S_ISBLK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFBLK)
|
|
||||||
#define INITRD_S_ISDIR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFDIR)
|
|
||||||
#define INITRD_S_ISCHR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFCHR)
|
|
||||||
#define INITRD_S_ISFIFO(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFIFO)
|
|
||||||
|
|
||||||
typedef struct initrd_superblock
|
|
||||||
{
|
|
||||||
char magic[16]; /* "sortix-initrd-2" */
|
|
||||||
uint32_t fssize;
|
|
||||||
uint32_t revision;
|
|
||||||
uint32_t inodesize;
|
|
||||||
uint32_t inodecount;
|
|
||||||
uint32_t inodeoffset;
|
|
||||||
uint32_t root;
|
|
||||||
uint32_t sumalgorithm;
|
|
||||||
uint32_t sumsize;
|
|
||||||
} initrd_superblock_t;
|
|
||||||
|
|
||||||
typedef struct initrd_inode
|
|
||||||
{
|
|
||||||
uint32_t mode;
|
|
||||||
uint32_t uid;
|
|
||||||
uint32_t gid;
|
|
||||||
uint32_t nlink;
|
|
||||||
uint64_t ctime;
|
|
||||||
uint64_t mtime;
|
|
||||||
uint32_t dataoffset;
|
|
||||||
uint32_t size;
|
|
||||||
} initrd_inode_t;
|
|
||||||
|
|
||||||
typedef struct initrd_dirent
|
|
||||||
{
|
|
||||||
uint32_t inode;
|
|
||||||
uint16_t reclen;
|
|
||||||
uint16_t namelen;
|
|
||||||
char name[0];
|
|
||||||
} initrd_dirent_t;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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 GetHeapLower();
|
||||||
addr_t GetHeapUpper();
|
addr_t GetHeapUpper();
|
||||||
size_t GetHeapSize();
|
size_t GetHeapSize();
|
||||||
|
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked, size_t* total);
|
||||||
|
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -36,4 +36,35 @@ typedef uintptr_t addr_t;
|
||||||
#define CPU X64
|
#define CPU X64
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
#include <sortix/dirent.h>
|
#include <sortix/dirent.h>
|
||||||
#include <sortix/fcntl.h>
|
#include <sortix/fcntl.h>
|
||||||
#include <sortix/initrd.h>
|
|
||||||
#include <sortix/mman.h>
|
#include <sortix/mman.h>
|
||||||
#include <sortix/stat.h>
|
#include <sortix/stat.h>
|
||||||
#include <sortix/tar.h>
|
#include <sortix/tar.h>
|
||||||
|
@ -51,17 +50,12 @@
|
||||||
|
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
|
|
||||||
// TODO: The initrd is not being properly verified.
|
|
||||||
// TODO: The initrd is not handled in an endian-neutral manner.
|
|
||||||
|
|
||||||
struct initrd_context
|
struct initrd_context
|
||||||
{
|
{
|
||||||
uint8_t* initrd;
|
uint8_t* initrd;
|
||||||
size_t initrd_size;
|
size_t initrd_size;
|
||||||
addr_t initrd_unmap_start;
|
addr_t initrd_unmap_start;
|
||||||
addr_t initrd_unmap_end;
|
addr_t initrd_unmap_end;
|
||||||
struct initrd_superblock* sb;
|
|
||||||
Ref<Descriptor> links;
|
|
||||||
ioctx_t ioctx;
|
ioctx_t ioctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,262 +75,6 @@ static void UnmapInitrdPage(struct initrd_context* ctx, addr_t vaddr)
|
||||||
Page::Put(addr, PAGE_USAGE_WASNT_ALLOCATED);
|
Page::Put(addr, PAGE_USAGE_WASNT_ALLOCATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static mode_t initrd_mode_to_host_mode(uint32_t mode)
|
|
||||||
{
|
|
||||||
mode_t result = mode & 0777;
|
|
||||||
if ( INITRD_S_ISVTX & mode ) result |= S_ISVTX;
|
|
||||||
if ( INITRD_S_ISSOCK(mode) ) result |= S_IFSOCK;
|
|
||||||
if ( INITRD_S_ISLNK(mode) ) result |= S_IFLNK;
|
|
||||||
if ( INITRD_S_ISREG(mode) ) result |= S_IFREG;
|
|
||||||
if ( INITRD_S_ISBLK(mode) ) result |= S_IFBLK;
|
|
||||||
if ( INITRD_S_ISDIR(mode) ) result |= S_IFDIR;
|
|
||||||
if ( INITRD_S_ISCHR(mode) ) result |= S_IFCHR;
|
|
||||||
if ( INITRD_S_ISFIFO(mode) ) result |= S_IFIFO;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct initrd_inode* initrd_get_inode(struct initrd_context* ctx,
|
|
||||||
uint32_t inode)
|
|
||||||
{
|
|
||||||
if ( ctx->sb->inodecount <= inode )
|
|
||||||
return errno = EINVAL, (struct initrd_inode*) NULL;
|
|
||||||
uint32_t pos = ctx->sb->inodeoffset + ctx->sb->inodesize * inode;
|
|
||||||
return (struct initrd_inode*) (ctx->initrd + pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t* initrd_inode_get_data(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
size_t* size)
|
|
||||||
{
|
|
||||||
return *size = inode->size, ctx->initrd + inode->dataoffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t initrd_directory_open(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
const char* name)
|
|
||||||
{
|
|
||||||
if ( !INITRD_S_ISDIR(inode->mode) )
|
|
||||||
return errno = ENOTDIR, 0;
|
|
||||||
uint32_t offset = 0;
|
|
||||||
while ( offset < inode->size )
|
|
||||||
{
|
|
||||||
uint32_t pos = inode->dataoffset + offset;
|
|
||||||
struct initrd_dirent* dirent =
|
|
||||||
(struct initrd_dirent*) (ctx->initrd + pos);
|
|
||||||
if ( dirent->namelen && !strcmp(dirent->name, name) )
|
|
||||||
return dirent->inode;
|
|
||||||
offset += dirent->reclen;
|
|
||||||
}
|
|
||||||
return errno = ENOENT, 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* initrd_directory_get_filename(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
size_t index)
|
|
||||||
{
|
|
||||||
if ( !INITRD_S_ISDIR(inode->mode) )
|
|
||||||
return errno = ENOTDIR, (const char*) NULL;
|
|
||||||
uint32_t offset = 0;
|
|
||||||
while ( offset < inode->size )
|
|
||||||
{
|
|
||||||
uint32_t pos = inode->dataoffset + offset;
|
|
||||||
struct initrd_dirent* dirent =
|
|
||||||
(struct initrd_dirent*) (ctx->initrd + pos);
|
|
||||||
if ( index-- == 0 )
|
|
||||||
return dirent->name;
|
|
||||||
offset += dirent->reclen;
|
|
||||||
}
|
|
||||||
return errno = EINVAL, (const char*) NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t initrd_directory_get_num_files(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode)
|
|
||||||
{
|
|
||||||
if ( !INITRD_S_ISDIR(inode->mode) )
|
|
||||||
return errno = ENOTDIR, 0;
|
|
||||||
uint32_t offset = 0;
|
|
||||||
size_t numentries = 0;
|
|
||||||
while ( offset < inode->size )
|
|
||||||
{
|
|
||||||
uint32_t pos = inode->dataoffset + offset;
|
|
||||||
const struct initrd_dirent* dirent =
|
|
||||||
(const struct initrd_dirent*) (ctx->initrd + pos);
|
|
||||||
numentries++;
|
|
||||||
offset += dirent->reclen;
|
|
||||||
}
|
|
||||||
return numentries;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ExtractNode(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
Ref<Descriptor> node);
|
|
||||||
|
|
||||||
static void ExtractFile(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
Ref<Descriptor> file)
|
|
||||||
{
|
|
||||||
size_t filesize;
|
|
||||||
uint8_t* data = initrd_inode_get_data(ctx, inode, &filesize);
|
|
||||||
if ( file->truncate(&ctx->ioctx, filesize) != 0 )
|
|
||||||
PanicF("initrd: truncate: %m");
|
|
||||||
size_t sofar = 0;
|
|
||||||
while ( sofar < filesize )
|
|
||||||
{
|
|
||||||
size_t left = filesize - sofar;
|
|
||||||
size_t chunk = 1024 * 1024;
|
|
||||||
size_t count = left < chunk ? left : chunk;
|
|
||||||
ssize_t numbytes = file->write(&ctx->ioctx, data + sofar, count);
|
|
||||||
if ( numbytes <= 0 )
|
|
||||||
PanicF("initrd: write: %m");
|
|
||||||
sofar += numbytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ExtractDir(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
Ref<Descriptor> dir)
|
|
||||||
{
|
|
||||||
size_t numfiles = initrd_directory_get_num_files(ctx, inode);
|
|
||||||
for ( size_t i = 0; i < numfiles; i++ )
|
|
||||||
{
|
|
||||||
const char* name = initrd_directory_get_filename(ctx, inode, i);
|
|
||||||
if ( !name )
|
|
||||||
PanicF("initrd_directory_get_filename: %m");
|
|
||||||
if ( IsDotOrDotDot(name) )
|
|
||||||
continue;
|
|
||||||
uint32_t childino = initrd_directory_open(ctx, inode, name);
|
|
||||||
if ( !childino )
|
|
||||||
PanicF("initrd_directory_open: %s: %m", name);
|
|
||||||
struct initrd_inode* child =
|
|
||||||
(struct initrd_inode*) initrd_get_inode(ctx, childino);
|
|
||||||
if ( !child )
|
|
||||||
PanicF("initrd_get_inode(%u): %s: %m", childino, name);
|
|
||||||
mode_t mode = initrd_mode_to_host_mode(child->mode);
|
|
||||||
if ( INITRD_S_ISDIR(child->mode) )
|
|
||||||
{
|
|
||||||
if ( dir->mkdir(&ctx->ioctx, name, mode) && errno != EEXIST )
|
|
||||||
PanicF("initrd: mkdir: %s: %m", name);
|
|
||||||
Ref<Descriptor> desc = dir->open(&ctx->ioctx, name,
|
|
||||||
O_SEARCH | O_DIRECTORY, 0);
|
|
||||||
if ( !desc )
|
|
||||||
PanicF("initrd: %s: %m", name);
|
|
||||||
ExtractNode(ctx, child, desc);
|
|
||||||
}
|
|
||||||
if ( INITRD_S_ISREG(child->mode) )
|
|
||||||
{
|
|
||||||
assert(child->nlink != 0);
|
|
||||||
char link_path[sizeof(childino) * 3];
|
|
||||||
snprintf(link_path, sizeof(link_path), "%ju", (uintmax_t) childino);
|
|
||||||
Ref<Descriptor> existing(ctx->links->open(&ctx->ioctx, link_path,
|
|
||||||
O_READ, 0));
|
|
||||||
if ( !existing || dir->link(&ctx->ioctx, name, existing) != 0 )
|
|
||||||
{
|
|
||||||
Ref<Descriptor> desc(dir->open(&ctx->ioctx, name,
|
|
||||||
O_WRITE | O_CREATE, mode));
|
|
||||||
if ( !desc )
|
|
||||||
PanicF("initrd: %s: %m", name);
|
|
||||||
ExtractNode(ctx, child, desc);
|
|
||||||
if ( 2 <= child->nlink )
|
|
||||||
ctx->links->link(&ctx->ioctx, link_path, desc);
|
|
||||||
}
|
|
||||||
if ( --child->nlink == 0 && INITRD_S_ISREG(child->mode) )
|
|
||||||
{
|
|
||||||
size_t filesize;
|
|
||||||
const uint8_t* data =
|
|
||||||
initrd_inode_get_data(ctx, child, &filesize);
|
|
||||||
uintptr_t from = (uintptr_t) data;
|
|
||||||
uintptr_t size = filesize;
|
|
||||||
uintptr_t from_aligned = Page::AlignUp(from);
|
|
||||||
uintptr_t from_distance = from_aligned - from;
|
|
||||||
if ( from_distance <= size )
|
|
||||||
{
|
|
||||||
uintptr_t size_aligned =
|
|
||||||
Page::AlignDown(size - from_distance);
|
|
||||||
for ( size_t i = 0; i < size_aligned; i += Page::Size() )
|
|
||||||
UnmapInitrdPage(ctx, from_aligned + i);
|
|
||||||
Memory::Flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( INITRD_S_ISLNK(child->mode) )
|
|
||||||
{
|
|
||||||
size_t filesize;
|
|
||||||
uint8_t* data = initrd_inode_get_data(ctx, child, &filesize);
|
|
||||||
char* oldname = new char[filesize + 1];
|
|
||||||
if ( !oldname )
|
|
||||||
PanicF("initrd: malloc: %m");
|
|
||||||
memcpy(oldname, data, filesize);
|
|
||||||
oldname[filesize] = '\0';
|
|
||||||
int ret = dir->symlink(&ctx->ioctx, oldname, name);
|
|
||||||
delete[] oldname;
|
|
||||||
if ( ret < 0 )
|
|
||||||
PanicF("initrd: symlink: %s", name);
|
|
||||||
Ref<Descriptor> desc = dir->open(&ctx->ioctx, name,
|
|
||||||
O_READ | O_SYMLINK_NOFOLLOW, 0);
|
|
||||||
if ( desc )
|
|
||||||
ExtractNode(ctx, child, desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ExtractNode(struct initrd_context* ctx,
|
|
||||||
struct initrd_inode* inode,
|
|
||||||
Ref<Descriptor> node)
|
|
||||||
{
|
|
||||||
if ( node->chmod(&ctx->ioctx, initrd_mode_to_host_mode(inode->mode)) < 0 )
|
|
||||||
PanicF("initrd: chmod: %m");
|
|
||||||
if ( node->chown(&ctx->ioctx, inode->uid, inode->gid) < 0 )
|
|
||||||
PanicF("initrd: chown: %m");
|
|
||||||
if ( INITRD_S_ISDIR(inode->mode) )
|
|
||||||
ExtractDir(ctx, inode, node);
|
|
||||||
if ( INITRD_S_ISREG(inode->mode) )
|
|
||||||
ExtractFile(ctx, inode, node);
|
|
||||||
struct timespec times[2];
|
|
||||||
times[0] = timespec_make((time_t) inode->mtime, 0);
|
|
||||||
times[1] = timespec_make((time_t) inode->mtime, 0);
|
|
||||||
if ( node->utimens(&ctx->ioctx, times) < 0 )
|
|
||||||
PanicF("initrd: utimens: %m");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ExtractInitrd(Ref<Descriptor> desc, struct initrd_context* ctx)
|
|
||||||
{
|
|
||||||
ctx->sb = (struct initrd_superblock*) ctx->initrd;
|
|
||||||
|
|
||||||
if ( ctx->initrd_size < ctx->sb->fssize )
|
|
||||||
Panic("Initrd header does not match its size");
|
|
||||||
|
|
||||||
if ( desc->mkdir(&ctx->ioctx, ".initrd-links", 0777) != 0 )
|
|
||||||
PanicF("initrd: .initrd-links: %m");
|
|
||||||
|
|
||||||
if ( !(ctx->links = desc->open(&ctx->ioctx, ".initrd-links",
|
|
||||||
O_READ | O_DIRECTORY, 0)) )
|
|
||||||
PanicF("initrd: .initrd-links: %m");
|
|
||||||
|
|
||||||
struct initrd_inode* root = initrd_get_inode(ctx, ctx->sb->root);
|
|
||||||
if ( !root )
|
|
||||||
PanicF("initrd: initrd_get_inode(%u): %m", ctx->sb->root);
|
|
||||||
ExtractNode(ctx, root, desc);
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct dirent dirent;
|
|
||||||
uint8_t dirent_data[sizeof(struct dirent) + sizeof(uintmax_t) * 3];
|
|
||||||
};
|
|
||||||
|
|
||||||
while ( 0 < ctx->links->readdirents(&ctx->ioctx, &dirent, sizeof(dirent_data)) &&
|
|
||||||
((const char*) dirent.d_name)[0] )
|
|
||||||
{
|
|
||||||
if ( ((const char*) dirent.d_name)[0] == '.' )
|
|
||||||
continue;
|
|
||||||
ctx->links->unlinkat(&ctx->ioctx, dirent.d_name, AT_REMOVEFILE);
|
|
||||||
ctx->links->lseek(&ctx->ioctx, 0, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->links.Reset();
|
|
||||||
|
|
||||||
desc->unlinkat(&ctx->ioctx, ".initrd-links", AT_REMOVEDIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TAR
|
struct TAR
|
||||||
{
|
{
|
||||||
unsigned char* tar_file;
|
unsigned char* tar_file;
|
||||||
|
@ -614,9 +352,10 @@ static void ExtractModule(struct multiboot_mod_list* module,
|
||||||
else if ( !strncmp(cmdline, "--create-to ", strlen("--create-to ")) ||
|
else if ( !strncmp(cmdline, "--create-to ", strlen("--create-to ")) ||
|
||||||
!strncmp(cmdline, "--create-to=", strlen("--create-to=")) )
|
!strncmp(cmdline, "--create-to=", strlen("--create-to=")) )
|
||||||
ExtractTo(desc, ctx, cmdline + strlen("--create-to "), O_EXCL);
|
ExtractTo(desc, ctx, cmdline + strlen("--create-to "), O_EXCL);
|
||||||
else if ( sizeof(struct initrd_superblock) <= ctx->initrd_size &&
|
// TODO: After releasing Sortix 1.1, remove this nice error message.
|
||||||
|
else if ( strlen("sortix-initrd-2") <= ctx->initrd_size &&
|
||||||
!memcmp(ctx->initrd, "sortix-initrd-2", strlen("sortix-initrd-2")) )
|
!memcmp(ctx->initrd, "sortix-initrd-2", strlen("sortix-initrd-2")) )
|
||||||
ExtractInitrd(desc, ctx);
|
Panic("The sortix-initrd-2 format is no longer supported");
|
||||||
else if ( sizeof(struct tar) <= ctx->initrd_size &&
|
else if ( sizeof(struct tar) <= ctx->initrd_size &&
|
||||||
!memcmp(ctx->initrd + offsetof(struct tar, magic), "ustar", 5) )
|
!memcmp(ctx->initrd + offsetof(struct tar, magic), "ustar", 5) )
|
||||||
ExtractTar(desc, ctx);
|
ExtractTar(desc, ctx);
|
||||||
|
@ -640,7 +379,6 @@ static void ExtractModule(struct multiboot_mod_list* module,
|
||||||
UnmapInitrdPage(ctx, mapat + i);
|
UnmapInitrdPage(ctx, mapat + i);
|
||||||
Memory::Flush();
|
Memory::Flush();
|
||||||
|
|
||||||
|
|
||||||
// Free the used virtual address space.
|
// Free the used virtual address space.
|
||||||
FreeKernelAddress(&initrd_addr_alloc);
|
FreeKernelAddress(&initrd_addr_alloc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,8 @@
|
||||||
#include "x86-family/vbox.h"
|
#include "x86-family/vbox.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct kernel_allocation_site* first_kernel_allocation_site = NULL;
|
||||||
|
|
||||||
// Keep the stack size aligned with $CPU/boot.s
|
// Keep the stack size aligned with $CPU/boot.s
|
||||||
const size_t STACK_SIZE = 64*1024;
|
const size_t STACK_SIZE = 64*1024;
|
||||||
extern "C" { __attribute__((aligned(16))) size_t stack[STACK_SIZE / sizeof(size_t)]; }
|
extern "C" { __attribute__((aligned(16))) size_t stack[STACK_SIZE / sizeof(size_t)]; }
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <sortix/kernel/kernel.h>
|
#include <sortix/kernel/kernel.h>
|
||||||
#include <sortix/kernel/syscall.h>
|
#include <sortix/kernel/syscall.h>
|
||||||
|
|
||||||
|
#include "net/tcp.h"
|
||||||
|
|
||||||
#ifndef VERSIONSTR
|
#ifndef VERSIONSTR
|
||||||
#define VERSIONSTR "unknown"
|
#define VERSIONSTR "unknown"
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,6 +52,77 @@ ssize_t sys_kernelinfo(const char* user_req, char* user_resp, size_t resplen)
|
||||||
char* req = GetStringFromUser(user_req);
|
char* req = GetStringFromUser(user_req);
|
||||||
if ( !req )
|
if ( !req )
|
||||||
return -1;
|
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);
|
const char* str = KernelInfo(req);
|
||||||
delete[] req;
|
delete[] req;
|
||||||
if ( !str )
|
if ( !str )
|
||||||
|
|
|
@ -50,6 +50,7 @@ LineBuffer::~LineBuffer()
|
||||||
|
|
||||||
bool LineBuffer::Push(uint32_t unicode)
|
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.
|
// Check if we need to allocate or resize the circular queue.
|
||||||
if ( bufferused == bufferlength )
|
if ( bufferused == bufferlength )
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -120,6 +121,9 @@ static kthread_mutex_t tcp_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
static TCPSocket** bindings_v4;
|
static TCPSocket** bindings_v4;
|
||||||
static TCPSocket** bindings_v6;
|
static TCPSocket** bindings_v6;
|
||||||
|
|
||||||
|
static TCPSocket* all_first_socket;
|
||||||
|
static TCPSocket* all_last_socket;
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
if ( !(bindings_v4 = new TCPSocket*[65536]) ||
|
if ( !(bindings_v4 = new TCPSocket*[65536]) ||
|
||||||
|
@ -218,6 +222,7 @@ public:
|
||||||
int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize);
|
int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
size_t Describe(char* buf, size_t buflen);
|
||||||
void Unreference();
|
void Unreference();
|
||||||
void ProcessPacket(Ref<Packet> pkt, union tcp_sockaddr* pkt_src,
|
void ProcessPacket(Ref<Packet> pkt, union tcp_sockaddr* pkt_src,
|
||||||
union tcp_sockaddr* pkt_dst);
|
union tcp_sockaddr* pkt_dst);
|
||||||
|
@ -278,6 +283,12 @@ public:
|
||||||
// The listening socket this socket is in the listening queue for.
|
// The listening socket this socket is in the listening queue for.
|
||||||
TCPSocket* connecting_parent;
|
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.
|
// Condition variable that is signaled when new data can be received.
|
||||||
kthread_cond_t receive_cond;
|
kthread_cond_t receive_cond;
|
||||||
|
|
||||||
|
@ -461,7 +472,7 @@ void TCPSocket__OnTimer(Clock* /*clock*/, Timer* /*timer*/, void* user)
|
||||||
((TCPSocket*) user)->OnTimer();
|
((TCPSocket*) user)->OnTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
TCPSocket::TCPSocket(int af)
|
TCPSocket::TCPSocket(int af) // DEBUG: tcp_lock taken
|
||||||
{
|
{
|
||||||
prev_socket = NULL;
|
prev_socket = NULL;
|
||||||
next_socket = NULL;
|
next_socket = NULL;
|
||||||
|
@ -517,9 +528,15 @@ TCPSocket::TCPSocket(int af)
|
||||||
shutdown_receive = false;
|
shutdown_receive = false;
|
||||||
memset(incoming, 0, sizeof(incoming));
|
memset(incoming, 0, sizeof(incoming));
|
||||||
memset(outgoing, 0, sizeof(outgoing));
|
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(state == TCP_STATE_CLOSED);
|
||||||
assert(!bound);
|
assert(!bound);
|
||||||
|
@ -539,6 +556,52 @@ TCPSocket::~TCPSocket()
|
||||||
receive_queue = packet->next;
|
receive_queue = packet->next;
|
||||||
packet->next.Reset();
|
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()
|
void TCPSocket::Unreference()
|
||||||
|
@ -2558,6 +2621,7 @@ Ref<Inode> Socket(int af)
|
||||||
{
|
{
|
||||||
if ( !IsSupportedAddressFamily(af) )
|
if ( !IsSupportedAddressFamily(af) )
|
||||||
return errno = EAFNOSUPPORT, Ref<Inode>(NULL);
|
return errno = EAFNOSUPPORT, Ref<Inode>(NULL);
|
||||||
|
ScopedLock lock(&tcp_lock); // DEBUG
|
||||||
TCPSocket* socket = new TCPSocket(af);
|
TCPSocket* socket = new TCPSocket(af);
|
||||||
if ( !socket )
|
if ( !socket )
|
||||||
return Ref<Inode>();
|
return Ref<Inode>();
|
||||||
|
@ -2567,5 +2631,45 @@ Ref<Inode> Socket(int af)
|
||||||
return result;
|
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 TCP
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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,
|
const struct in_addr* dst,
|
||||||
bool dst_broadcast);
|
bool dst_broadcast);
|
||||||
Ref<Inode> Socket(int af);
|
Ref<Inode> Socket(int af);
|
||||||
|
ssize_t Info(char* user_resp, size_t resplen);
|
||||||
|
|
||||||
} // namespace TCP
|
} // namespace TCP
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -24,6 +24,19 @@
|
||||||
#warning "security: -fcheck-new might not work on clang"
|
#warning "security: -fcheck-new might not work on clang"
|
||||||
#endif
|
#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)
|
void* operator new(size_t size)
|
||||||
{
|
{
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
|
@ -33,6 +46,7 @@ void* operator new[](size_t size)
|
||||||
{
|
{
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void operator delete(void* addr)
|
void operator delete(void* addr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -477,6 +477,8 @@ private:
|
||||||
size_t output_offset;
|
size_t output_offset;
|
||||||
size_t output_used;
|
size_t output_used;
|
||||||
static const size_t output_size = 4096;
|
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];
|
uint8_t output[output_size];
|
||||||
int ptynum;
|
int ptynum;
|
||||||
|
|
||||||
|
@ -538,6 +540,16 @@ ssize_t PTY::master_write(ioctx_t* ctx, const uint8_t* buf, size_t count)
|
||||||
ScopedLockSignal lock(&termlock);
|
ScopedLockSignal lock(&termlock);
|
||||||
if ( !lock.IsAcquired() )
|
if ( !lock.IsAcquired() )
|
||||||
return errno = EINTR, -1;
|
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;
|
size_t sofar = 0;
|
||||||
while ( sofar < count )
|
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() )
|
if ( Signal::IsPending() )
|
||||||
return sofar ? (ssize_t) sofar : (errno = EINTR, -1);
|
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]);
|
ProcessByte(input[i]);
|
||||||
|
sofar++;
|
||||||
|
if ( output_probably_safe <= output_used )
|
||||||
|
return (ssize_t) sofar;
|
||||||
}
|
}
|
||||||
sofar += amount;
|
|
||||||
}
|
}
|
||||||
return (ssize_t) sofar;
|
return (ssize_t) sofar;
|
||||||
}
|
}
|
||||||
|
@ -599,7 +615,7 @@ short PTY::PollMasterEventStatus()
|
||||||
short status = 0;
|
short status = 0;
|
||||||
if ( output_used )
|
if ( output_used )
|
||||||
status |= POLLIN | POLLRDNORM;
|
status |= POLLIN | POLLRDNORM;
|
||||||
if ( true /* can always write */ )
|
if ( output_used < output_probably_safe )
|
||||||
status |= POLLOUT | POLLWRNORM;
|
status |= POLLOUT | POLLWRNORM;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,9 +78,7 @@ static const uint8_t DEVICE_CMD_RESET = 0xFF;
|
||||||
|
|
||||||
static const size_t DEVICE_RETRIES = 5;
|
static const size_t DEVICE_RETRIES = 5;
|
||||||
|
|
||||||
// TODO: This is entirely a guess. I don't actually know what timeout is
|
static const unsigned int TIMEOUT_MS = 20;
|
||||||
// suitable. GRUB seems to use 20 ms. I'll pick 50 ms to be safe.
|
|
||||||
static const unsigned int TIMEOUT_MS = 50;
|
|
||||||
|
|
||||||
static bool WaitInput()
|
static bool WaitInput()
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,20 +60,6 @@ malloc/__heap_verify.o \
|
||||||
netinet/if_ether/etheraddr_broadcast.o \
|
netinet/if_ether/etheraddr_broadcast.o \
|
||||||
netinet/in/in6addr_any.o \
|
netinet/in/in6addr_any.o \
|
||||||
netinet/in/in6addr_loopback.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/sigaddset.o \
|
||||||
signal/sigandset.o \
|
signal/sigandset.o \
|
||||||
signal/sigdelset.o \
|
signal/sigdelset.o \
|
||||||
|
@ -86,7 +72,6 @@ signal/sigorset.o \
|
||||||
ssp/__stack_chk_fail.o \
|
ssp/__stack_chk_fail.o \
|
||||||
stdio/asprintf.o \
|
stdio/asprintf.o \
|
||||||
stdio/cbprintf.o \
|
stdio/cbprintf.o \
|
||||||
stdio/cbscanf.o \
|
|
||||||
stdio/clearerr.o \
|
stdio/clearerr.o \
|
||||||
stdio/clearerr_unlocked.o \
|
stdio/clearerr_unlocked.o \
|
||||||
stdio/dprintf.o \
|
stdio/dprintf.o \
|
||||||
|
@ -116,8 +101,6 @@ stdio/fgets.o \
|
||||||
stdio/fgets_unlocked.o \
|
stdio/fgets_unlocked.o \
|
||||||
stdio/fileno_unlocked.o \
|
stdio/fileno_unlocked.o \
|
||||||
stdio/flockfile.o \
|
stdio/flockfile.o \
|
||||||
stdio/fmemopen.o \
|
|
||||||
stdio/fnewfile.o \
|
|
||||||
stdio/fparsemode.o \
|
stdio/fparsemode.o \
|
||||||
stdio/fprintf_unlocked.o \
|
stdio/fprintf_unlocked.o \
|
||||||
stdio/fputc.o \
|
stdio/fputc.o \
|
||||||
|
@ -128,8 +111,6 @@ stdio/fread.o \
|
||||||
stdio/fread_unlocked.o \
|
stdio/fread_unlocked.o \
|
||||||
stdio/fregister.o \
|
stdio/fregister.o \
|
||||||
stdio/fresetfile.o \
|
stdio/fresetfile.o \
|
||||||
stdio/fscanf.o \
|
|
||||||
stdio/fscanf_unlocked.o \
|
|
||||||
stdio/fseek.o \
|
stdio/fseek.o \
|
||||||
stdio/fseeko.o \
|
stdio/fseeko.o \
|
||||||
stdio/fseeko_unlocked.o \
|
stdio/fseeko_unlocked.o \
|
||||||
|
@ -142,28 +123,20 @@ stdio/funlockfile.o \
|
||||||
stdio/funregister.o \
|
stdio/funregister.o \
|
||||||
stdio/fwrite.o \
|
stdio/fwrite.o \
|
||||||
stdio/fwrite_unlocked.o \
|
stdio/fwrite_unlocked.o \
|
||||||
stdio/getdelim.o \
|
|
||||||
stdio/getline.o \
|
|
||||||
stdio/open_memstream.o \
|
|
||||||
stdio/rewind.o \
|
stdio/rewind.o \
|
||||||
stdio/setbuf.o \
|
stdio/setbuf.o \
|
||||||
stdio/setvbuf.o \
|
stdio/setvbuf.o \
|
||||||
stdio/setvbuf_unlocked.o \
|
stdio/setvbuf_unlocked.o \
|
||||||
stdio/snprintf.o \
|
stdio/snprintf.o \
|
||||||
stdio/sprintf.o \
|
stdio/sprintf.o \
|
||||||
stdio/sscanf.o \
|
|
||||||
stdio/ungetc.o \
|
stdio/ungetc.o \
|
||||||
stdio/ungetc_unlocked.o \
|
stdio/ungetc_unlocked.o \
|
||||||
stdio/vasprintf.o \
|
stdio/vasprintf.o \
|
||||||
stdio/vcbprintf.o \
|
stdio/vcbprintf.o \
|
||||||
stdio/vdprintf.o \
|
stdio/vdprintf.o \
|
||||||
stdio/vfprintf_unlocked.o \
|
stdio/vfprintf_unlocked.o \
|
||||||
stdio/vfscanf.o \
|
|
||||||
stdio/vfscanf_unlocked.o \
|
|
||||||
stdio/vcbscanf.o \
|
|
||||||
stdio/vsnprintf.o \
|
stdio/vsnprintf.o \
|
||||||
stdio/vsprintf.o \
|
stdio/vsprintf.o \
|
||||||
stdio/vsscanf.o \
|
|
||||||
stdlib/abort.o \
|
stdlib/abort.o \
|
||||||
stdlib/abs.o \
|
stdlib/abs.o \
|
||||||
stdlib/arc4random_buf.o \
|
stdlib/arc4random_buf.o \
|
||||||
|
@ -278,7 +251,6 @@ wchar/wcscmp.o \
|
||||||
wchar/wcscoll.o \
|
wchar/wcscoll.o \
|
||||||
wchar/wcscpy.o \
|
wchar/wcscpy.o \
|
||||||
wchar/wcscspn.o \
|
wchar/wcscspn.o \
|
||||||
wchar/wcsdup.o \
|
|
||||||
wchar/wcsftime.o \
|
wchar/wcsftime.o \
|
||||||
wchar/wcslcat.o \
|
wchar/wcslcat.o \
|
||||||
wchar/wcslcpy.o \
|
wchar/wcslcpy.o \
|
||||||
|
@ -329,6 +301,34 @@ wctype/towupper.o \
|
||||||
wctype/wctype.o \
|
wctype/wctype.o \
|
||||||
|
|
||||||
HOSTEDOBJS=\
|
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 \
|
blf/blowfish.o \
|
||||||
$(CPUDIR)/fork.o \
|
$(CPUDIR)/fork.o \
|
||||||
$(CPUDIR)/setjmp.o \
|
$(CPUDIR)/setjmp.o \
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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"
|
#warning "You need to implement HEAP_CHUNK_MAGIC for your native word width"
|
||||||
#endif
|
#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
|
/* 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
|
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
|
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));
|
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. */
|
/* Returns the trailing structure following the given chunk. */
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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. */
|
/* Functions copied from elsewhere. */
|
||||||
#if __USE_SORTIX
|
#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, ...)
|
int asprintf(char** __restrict, const char* __restrict, ...)
|
||||||
__attribute__((__format__ (printf, 2, 3)));
|
__attribute__((__format__ (printf, 2, 3)));
|
||||||
|
#endif
|
||||||
void clearerr_unlocked(FILE* stream);
|
void clearerr_unlocked(FILE* stream);
|
||||||
int feof_unlocked(FILE* stream);
|
int feof_unlocked(FILE* stream);
|
||||||
int ferror_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);
|
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 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);
|
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)
|
int vasprintf(char** __restrict, const char* __restrict, __gnuc_va_list)
|
||||||
__attribute__((__format__ (printf, 2, 0)));
|
__attribute__((__format__ (printf, 2, 0)));
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Functions that are Sortix extensions. */
|
/* Functions that are Sortix extensions. */
|
||||||
#if __USE_SORTIX
|
#if __USE_SORTIX
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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*);
|
int atoi(const char*);
|
||||||
long atol(const char*);
|
long atol(const char*);
|
||||||
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
|
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);
|
void* calloc(size_t, size_t);
|
||||||
|
#endif
|
||||||
char* canonicalize_file_name(const char* path);
|
char* canonicalize_file_name(const char* path);
|
||||||
char* canonicalize_file_name_at(int dirfd, const char* path);
|
char* canonicalize_file_name_at(int dirfd, const char* path);
|
||||||
int clearenv(void);
|
int clearenv(void);
|
||||||
|
@ -99,7 +104,12 @@ void free(void*);
|
||||||
char* getenv(const char*);
|
char* getenv(const char*);
|
||||||
long labs(long);
|
long labs(long);
|
||||||
ldiv_t ldiv(long, 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);
|
void* malloc(size_t);
|
||||||
|
#endif
|
||||||
int mblen(const char*, size_t);
|
int mblen(const char*, size_t);
|
||||||
size_t mbstowcs(wchar_t* __restrict, const char* __restrict, size_t);
|
size_t mbstowcs(wchar_t* __restrict, const char* __restrict, size_t);
|
||||||
int mbtowc(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()")))
|
__attribute__((__warning__("rand() isn't random, use arc4random()")))
|
||||||
#endif
|
#endif
|
||||||
int rand(void);
|
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);
|
void* realloc(void*, size_t);
|
||||||
|
#endif
|
||||||
char* realpath(const char* __restrict, char* __restrict);
|
char* realpath(const char* __restrict, char* __restrict);
|
||||||
int setenv(const char*, const char*, int);
|
int setenv(const char*, const char*, int);
|
||||||
#if !defined(__is_sortix_libc) /* not a warning inside libc */
|
#if !defined(__is_sortix_libc) /* not a warning inside libc */
|
||||||
|
@ -189,7 +204,12 @@ int posix_openpt(int);
|
||||||
uint32_t arc4random(void);
|
uint32_t arc4random(void);
|
||||||
void arc4random_buf(void*, size_t);
|
void arc4random_buf(void*, size_t);
|
||||||
uint32_t arc4random_uniform(uint32_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);
|
void* reallocarray(void*, size_t, size_t);
|
||||||
|
#endif
|
||||||
int ptsname_r(int, char*, size_t);
|
int ptsname_r(int, char*, size_t);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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. */
|
/* Functions from XOPEN 420 gone into POSIX 2008. */
|
||||||
#if __USE_SORTIX || 420 <= __USE_XOPEN || 200809L <= __USE_POSIX
|
#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*);
|
char* strdup(const char*);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Functions from POSIX 2001. */
|
/* Functions from POSIX 2001. */
|
||||||
#if __USE_SORTIX || 200112L <= __USE_POSIX
|
#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* stpcpy(char* __restrict, const char* __restrict);
|
||||||
char* stpncpy(char* __restrict, const char* __restrict, size_t);
|
char* stpncpy(char* __restrict, const char* __restrict, size_t);
|
||||||
int strcoll_l(const char*, const char*, locale_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);
|
char* strndup(const char*, size_t);
|
||||||
|
#endif
|
||||||
size_t strnlen(const char*, size_t);
|
size_t strnlen(const char*, size_t);
|
||||||
#if __USE_SORTIX && __SORTIX_STDLIB_REDIRECTS
|
#if __USE_SORTIX && __SORTIX_STDLIB_REDIRECTS
|
||||||
const char* strsignal(int signum) __asm__ ("sortix_strsignal");
|
const char* strsignal(int signum) __asm__ ("sortix_strsignal");
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -128,4 +128,24 @@
|
||||||
is updated to not rely on this macro. */
|
is updated to not rely on this macro. */
|
||||||
#undef __SORTIX_HAS_GETSERVBYNAME__
|
#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
|
#endif
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -20,13 +20,24 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.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,
|
int asprintf(char** restrict result_ptr,
|
||||||
const char* restrict format,
|
const char* restrict format,
|
||||||
...)
|
...)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
va_list list;
|
va_list list;
|
||||||
va_start(list, format);
|
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);
|
int result = vasprintf(result_ptr, format, list);
|
||||||
|
#endif
|
||||||
va_end(list);
|
va_end(list);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -27,6 +27,9 @@ struct vasprintf
|
||||||
char* buffer;
|
char* buffer;
|
||||||
size_t used;
|
size_t used;
|
||||||
size_t size;
|
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)
|
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;
|
size_t new_size = 2 * state->size;
|
||||||
if ( new_size < needed_size )
|
if ( new_size < needed_size )
|
||||||
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);
|
char* new_buffer = (char*) realloc(state->buffer, new_size);
|
||||||
|
#endif
|
||||||
if ( !new_buffer )
|
if ( !new_buffer )
|
||||||
{
|
{
|
||||||
free(state->buffer);
|
free(state->buffer);
|
||||||
|
@ -54,14 +62,26 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
|
||||||
return 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,
|
int vasprintf(char** restrict result_ptr,
|
||||||
const char* restrict format,
|
const char* restrict format,
|
||||||
va_list list)
|
va_list list)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
struct vasprintf state;
|
struct vasprintf state;
|
||||||
state.used = 0;
|
state.used = 0;
|
||||||
state.size = 32;
|
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)) )
|
if ( !(state.buffer = (char*) malloc(state.size)) )
|
||||||
|
#endif
|
||||||
return -1;
|
return -1;
|
||||||
int result = vcbprintf(&state, vasprintf_callback, format, list);
|
int result = vcbprintf(&state, vasprintf_callback, format, list);
|
||||||
if ( !state.buffer )
|
if ( !state.buffer )
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -22,12 +22,21 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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)
|
void* calloc(size_t nmemb, size_t size)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if ( size && nmemb && SIZE_MAX / size < nmemb )
|
if ( size && nmemb && SIZE_MAX / size < nmemb )
|
||||||
return errno = ENOMEM, (void*) NULL;
|
return errno = ENOMEM, (void*) NULL;
|
||||||
size_t total = nmemb * size;
|
size_t total = nmemb * size;
|
||||||
|
#ifdef __TRACE_ALLOCATION_SITES
|
||||||
|
void* result = malloc_trace(allocation_site, total);
|
||||||
|
#else
|
||||||
void* result = malloc(total);
|
void* result = malloc(total);
|
||||||
|
#endif
|
||||||
if ( !result )
|
if ( !result )
|
||||||
return NULL;
|
return NULL;
|
||||||
memset(result, 0, total);
|
memset(result, 0, total);
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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.
|
// Retrieve the chunk that contains this allocation.
|
||||||
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) addr);
|
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.
|
// Return the chunk to the heap.
|
||||||
heap_insert_chunk(chunk);
|
heap_insert_chunk(chunk);
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -31,7 +31,12 @@
|
||||||
|
|
||||||
#if !defined(HEAP_GUARD_DEBUG)
|
#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)
|
void* malloc(size_t original_size)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if ( !heap_size_has_bin(original_size) )
|
if ( !heap_size_has_bin(original_size) )
|
||||||
return errno = ENOMEM, (void*) NULL;
|
return errno = ENOMEM, (void*) NULL;
|
||||||
|
@ -89,6 +94,12 @@ void* malloc(size_t original_size)
|
||||||
if ( heap_can_split_chunk(result_chunk, chunk_size) )
|
if ( heap_can_split_chunk(result_chunk, chunk_size) )
|
||||||
heap_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_verify();
|
||||||
__heap_unlock();
|
__heap_unlock();
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -31,10 +31,20 @@
|
||||||
|
|
||||||
#if !defined(HEAP_GUARD_DEBUG)
|
#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)
|
void* realloc(void* ptr, size_t requested_size)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if ( !ptr )
|
if ( !ptr )
|
||||||
|
#ifdef __TRACE_ALLOCATION_SITES
|
||||||
|
return malloc_trace(new_allocation_site, requested_size);
|
||||||
|
#else
|
||||||
return malloc(requested_size);
|
return malloc(requested_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( !heap_size_has_bin(requested_size) )
|
if ( !heap_size_has_bin(requested_size) )
|
||||||
return errno = ENOMEM, (void*) NULL;
|
return errno = ENOMEM, (void*) NULL;
|
||||||
|
@ -55,7 +65,14 @@ void* realloc(void* ptr, size_t requested_size)
|
||||||
// Retrieve the chunk that contains this allocation.
|
// Retrieve the chunk that contains this allocation.
|
||||||
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) ptr);
|
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_magic == HEAP_CHUNK_MAGIC);
|
||||||
assert(heap_chunk_to_post(chunk)->chunk_size == chunk->chunk_size);
|
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 )
|
if ( requested_chunk_size < chunk->chunk_size )
|
||||||
{
|
{
|
||||||
assert(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) )
|
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
|
||||||
heap_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_verify();
|
||||||
__heap_unlock();
|
__heap_unlock();
|
||||||
return heap_chunk_to_data(chunk);
|
return heap_chunk_to_data(chunk);
|
||||||
|
@ -88,11 +114,20 @@ void* realloc(void* ptr, size_t requested_size)
|
||||||
!heap_chunk_is_used(right) &&
|
!heap_chunk_is_used(right) &&
|
||||||
requested_chunk_size <= chunk->chunk_size + right->chunk_size )
|
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_remove_chunk(right);
|
||||||
heap_chunk_format((uint8_t*) chunk, chunk->chunk_size + right->chunk_size);
|
heap_chunk_format((uint8_t*) chunk, chunk->chunk_size + right->chunk_size);
|
||||||
assert(requested_chunk_size <= chunk->chunk_size);
|
assert(requested_chunk_size <= chunk->chunk_size);
|
||||||
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
|
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
|
||||||
heap_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_verify();
|
||||||
__heap_unlock();
|
__heap_unlock();
|
||||||
return heap_chunk_to_data(chunk);
|
return heap_chunk_to_data(chunk);
|
||||||
|
@ -108,7 +143,11 @@ void* realloc(void* ptr, size_t requested_size)
|
||||||
|
|
||||||
assert(orignal_ptr_size < 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);
|
void* result = malloc(requested_size);
|
||||||
|
#endif
|
||||||
if ( !result )
|
if ( !result )
|
||||||
return (void*) NULL;
|
return (void*) NULL;
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -21,9 +21,18 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.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)
|
void* reallocarray(void* ptr, size_t nmemb, size_t size)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if ( size && nmemb && SIZE_MAX / size < nmemb )
|
if ( size && nmemb && SIZE_MAX / size < nmemb )
|
||||||
return errno = ENOMEM, (void*) NULL;
|
return errno = ENOMEM, (void*) NULL;
|
||||||
|
#ifdef __TRACE_ALLOCATION_SITES
|
||||||
|
return realloc_trace(allocation_site, ptr, nmemb * size);
|
||||||
|
#else
|
||||||
return realloc(ptr, nmemb * size);
|
return realloc(ptr, nmemb * size);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -20,10 +20,18 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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)
|
char* strdup(const char* input)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
size_t input_length = strlen(input);
|
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);
|
char* result = (char*) malloc(input_length + 1);
|
||||||
|
#endif
|
||||||
if ( !result )
|
if ( !result )
|
||||||
return NULL;
|
return NULL;
|
||||||
memcpy(result, input, input_length + 1);
|
memcpy(result, input, input_length + 1);
|
||||||
|
|
|
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -20,10 +20,19 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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)
|
char* strndup(const char* input, size_t n)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
size_t input_size = strnlen(input, n);
|
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);
|
char* result = (char*) malloc(input_size + 1);
|
||||||
|
#endif
|
||||||
if ( !result )
|
if ( !result )
|
||||||
return NULL;
|
return NULL;
|
||||||
memcpy(result, input, input_size);
|
memcpy(result, input, input_size);
|
||||||
|
|
|
@ -20,6 +20,7 @@ extended.o \
|
||||||
filesystem.o \
|
filesystem.o \
|
||||||
gpt.o \
|
gpt.o \
|
||||||
harddisk.o \
|
harddisk.o \
|
||||||
|
iso9660.o \
|
||||||
mbr.o \
|
mbr.o \
|
||||||
partition.o \
|
partition.o \
|
||||||
uuid.o \
|
uuid.o \
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <mount/ext2.h>
|
#include <mount/ext2.h>
|
||||||
#include <mount/extended.h>
|
#include <mount/extended.h>
|
||||||
#include <mount/filesystem.h>
|
#include <mount/filesystem.h>
|
||||||
|
#include <mount/iso9660.h>
|
||||||
#include <mount/partition.h>
|
#include <mount/partition.h>
|
||||||
|
|
||||||
const char* filesystem_error_string(enum filesystem_error error)
|
const char* filesystem_error_string(enum filesystem_error error)
|
||||||
|
@ -55,6 +56,7 @@ static const struct filesystem_handler* filesystem_handlers[] =
|
||||||
&biosboot_handler,
|
&biosboot_handler,
|
||||||
&extended_handler,
|
&extended_handler,
|
||||||
&ext2_handler,
|
&ext2_handler,
|
||||||
|
&iso9660_handler,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
37
libmount/include/mount/iso9660.h
Normal file
37
libmount/include/mount/iso9660.h
Normal 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
98
libmount/iso9660.c
Normal 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,
|
||||||
|
};
|
|
@ -163,6 +163,9 @@ bool blockdevice_probe_partition_table_type(enum partition_table_type* result_ou
|
||||||
memcpy(&mbr, leading, sizeof(mbr));
|
memcpy(&mbr, leading, sizeof(mbr));
|
||||||
if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
|
if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
|
||||||
break;
|
break;
|
||||||
|
// Ignore the partition table on live cdrom images.
|
||||||
|
if ( mbr.partitions[0][4 /*system_id*/] == 0xCD )
|
||||||
|
break;
|
||||||
result = PARTITION_TABLE_TYPE_MBR;
|
result = PARTITION_TABLE_TYPE_MBR;
|
||||||
goto out;
|
goto out;
|
||||||
} while ( 0 );
|
} while ( 0 );
|
||||||
|
|
2
mkinitrd/.gitignore
vendored
2
mkinitrd/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
mkinitrd
|
|
||||||
initrdfs
|
|
|
@ -1,35 +0,0 @@
|
||||||
include ../build-aux/platform.mak
|
|
||||||
include ../build-aux/compiler.mak
|
|
||||||
include ../build-aux/version.mak
|
|
||||||
include ../build-aux/dirs.mak
|
|
||||||
|
|
||||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
|
||||||
CFLAGS?=$(OPTLEVEL)
|
|
||||||
|
|
||||||
SORTIXKERNEL=../kernel
|
|
||||||
|
|
||||||
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\" -I$(SORTIXKERNEL)/include -I.
|
|
||||||
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
|
||||||
|
|
||||||
ifeq ($(HOST_IS_SORTIX),0)
|
|
||||||
CPPFLAGS+=-D_GNU_SOURCE
|
|
||||||
endif
|
|
||||||
|
|
||||||
BINARIES=mkinitrd initrdfs
|
|
||||||
|
|
||||||
all: $(BINARIES)
|
|
||||||
|
|
||||||
.PHONY: all install clean
|
|
||||||
|
|
||||||
%: %.c rules.c serialize.c
|
|
||||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< rules.c serialize.c -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(BINARIES)
|
|
||||||
|
|
||||||
install: all
|
|
||||||
mkdir -p $(DESTDIR)$(SBINDIR)
|
|
||||||
install $(BINARIES) $(DESTDIR)$(SBINDIR)
|
|
||||||
mkdir -p $(DESTDIR)$(MANDIR)/man8
|
|
||||||
cp initrdfs.8 $(DESTDIR)$(MANDIR)/man8/initrdfs.8
|
|
||||||
cp mkinitrd.8 $(DESTDIR)$(MANDIR)/man8/mkinitrd.8
|
|
|
@ -1,67 +0,0 @@
|
||||||
.Dd October 7, 2015
|
|
||||||
.Dt INITRDFS 8
|
|
||||||
.Os
|
|
||||||
.Sh NAME
|
|
||||||
.Nm initrdfs
|
|
||||||
.Nd view initialization ramdisk
|
|
||||||
.Sh SYNOPSIS
|
|
||||||
.Nm
|
|
||||||
.Ar initrd
|
|
||||||
cat
|
|
||||||
.Ar path ...
|
|
||||||
.Nm
|
|
||||||
.Ar initrd
|
|
||||||
extract
|
|
||||||
.Op Fl C Ar destination
|
|
||||||
.Ar path ...
|
|
||||||
.Nm
|
|
||||||
.Ar initrd
|
|
||||||
ls
|
|
||||||
.Op Fl a
|
|
||||||
.Ar path ...
|
|
||||||
.Sh DESCRIPTION
|
|
||||||
.Nm
|
|
||||||
opens a
|
|
||||||
.Xr initrd 7
|
|
||||||
made with
|
|
||||||
.Xr mkinitrd 8
|
|
||||||
and lets you view the stored directories and files.
|
|
||||||
.Pp
|
|
||||||
The options are as follows:
|
|
||||||
.Bl -tag -width "12345678"
|
|
||||||
.It Fl a
|
|
||||||
.Sy ( ls )
|
|
||||||
Include directory entries whose names begin with a
|
|
||||||
dot
|
|
||||||
.Pq Sq \&. .
|
|
||||||
.It Fl C Ar destination
|
|
||||||
.Sy ( extract )
|
|
||||||
Extract to the specified
|
|
||||||
.Ar destination
|
|
||||||
rather than the default current directory.
|
|
||||||
.El
|
|
||||||
.Pp
|
|
||||||
.Nm
|
|
||||||
supports these commands:
|
|
||||||
.Bl -tag -width "12345678"
|
|
||||||
.It Sy cat
|
|
||||||
Show the contents of each
|
|
||||||
.Ar path .
|
|
||||||
.It Sy extract
|
|
||||||
Extract each
|
|
||||||
.Ar path
|
|
||||||
recursively to the current directory.
|
|
||||||
.It Sy ls
|
|
||||||
List each directory
|
|
||||||
.Ar path .
|
|
||||||
.El
|
|
||||||
.Sh EXIT STATUS
|
|
||||||
.Nm
|
|
||||||
will exit 0 on success and non-zero otherwise.
|
|
||||||
.Sh SEE ALSO
|
|
||||||
.Xr initrd 7 ,
|
|
||||||
.Xr mkinitrd 8
|
|
||||||
.Sh BUGS
|
|
||||||
.Nm
|
|
||||||
is feature limited and doesn't let you view all the contained meta information.
|
|
||||||
It's also not a filesystem driver.
|
|
|
@ -1,395 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012, 2015, 2016, 2017 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.
|
|
||||||
*
|
|
||||||
* initrdfs.c
|
|
||||||
* Provides access to filesystems in the Sortix kernel initrd format.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ioleast.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sortix/initrd.h>
|
|
||||||
|
|
||||||
#include "serialize.h"
|
|
||||||
|
|
||||||
char* Substring(const char* str, size_t start, size_t length)
|
|
||||||
{
|
|
||||||
char* result = (char*) malloc(length+1);
|
|
||||||
strncpy(result, str + start, length);
|
|
||||||
result[length] = 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadSuperBlock(int fd, initrd_superblock_t* dest)
|
|
||||||
{
|
|
||||||
if ( preadall(fd, dest, sizeof(*dest), 0) != sizeof(*dest) )
|
|
||||||
return false;
|
|
||||||
import_initrd_superblock(dest);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
initrd_superblock_t* GetSuperBlock(int fd)
|
|
||||||
{
|
|
||||||
size_t sbsize = sizeof(initrd_superblock_t);
|
|
||||||
initrd_superblock_t* sb = (initrd_superblock_t*) malloc(sbsize);
|
|
||||||
if ( !sb ) { return NULL; }
|
|
||||||
if ( !ReadSuperBlock(fd, sb) ) { free(sb); return NULL; }
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadInode(int fd, initrd_superblock_t* sb, uint32_t ino,
|
|
||||||
initrd_inode_t* dest)
|
|
||||||
{
|
|
||||||
uint32_t inodepos = sb->inodeoffset + sb->inodesize * ino;
|
|
||||||
if ( preadall(fd, dest, sizeof(*dest), inodepos) != sizeof(*dest) )
|
|
||||||
return false;
|
|
||||||
import_initrd_inode(dest);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
initrd_inode_t* GetInode(int fd, initrd_superblock_t* sb, uint32_t ino)
|
|
||||||
{
|
|
||||||
initrd_inode_t* inode = (initrd_inode_t*) malloc(sizeof(initrd_inode_t));
|
|
||||||
if ( !inode ) { return NULL; }
|
|
||||||
if ( !ReadInode(fd, sb, ino, inode) ) { free(inode); return NULL; }
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
initrd_inode_t* CloneInode(const initrd_inode_t* src)
|
|
||||||
{
|
|
||||||
initrd_inode_t* result = (initrd_inode_t*) malloc(sizeof(*src));
|
|
||||||
if ( !result ) { return NULL; }
|
|
||||||
memcpy(result, src, sizeof(*src));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode,
|
|
||||||
uint8_t* dest, size_t size, off_t offset)
|
|
||||||
{
|
|
||||||
(void) sb;
|
|
||||||
if ( offset < 0 )
|
|
||||||
return errno = EINVAL, false;
|
|
||||||
if ( inode->size < (uintmax_t) offset )
|
|
||||||
return errno = EINVAL, false;
|
|
||||||
size_t available = inode->size - offset;
|
|
||||||
if ( inode->size < available )
|
|
||||||
return errno = EINVAL, false;
|
|
||||||
return preadall(fd, dest, size, inode->dataoffset + offset) == size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* GetInodeDataSize(int fd, initrd_superblock_t* sb,
|
|
||||||
initrd_inode_t* inode, size_t size)
|
|
||||||
{
|
|
||||||
uint8_t* buf = (uint8_t*) malloc(size);
|
|
||||||
if ( !buf )
|
|
||||||
return NULL;
|
|
||||||
if ( !ReadInodeData(fd, sb, inode, buf, size, 0) )
|
|
||||||
{
|
|
||||||
free(buf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* GetInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode)
|
|
||||||
{
|
|
||||||
return GetInodeDataSize(fd, sb, inode, inode->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Traverse(int fd, initrd_superblock_t* sb, initrd_inode_t* inode,
|
|
||||||
const char* name)
|
|
||||||
{
|
|
||||||
if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; }
|
|
||||||
uint8_t* direntries = GetInodeData(fd, sb, inode);
|
|
||||||
if ( !direntries ) { return 0; }
|
|
||||||
uint32_t result = 0;
|
|
||||||
uint32_t offset = 0;
|
|
||||||
while ( offset < inode->size )
|
|
||||||
{
|
|
||||||
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
|
|
||||||
import_initrd_dirent(dirent);
|
|
||||||
if ( dirent->namelen && !strcmp(dirent->name, name) )
|
|
||||||
{
|
|
||||||
result = dirent->inode;
|
|
||||||
export_initrd_dirent(dirent);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
offset += dirent->reclen;
|
|
||||||
export_initrd_dirent(dirent);
|
|
||||||
}
|
|
||||||
free(direntries);
|
|
||||||
if ( !result ) { errno = ENOENT; }
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
initrd_inode_t* ResolvePath(int fd, initrd_superblock_t* sb,
|
|
||||||
initrd_inode_t* inode, const char* path)
|
|
||||||
{
|
|
||||||
if ( !path[0] ) { return CloneInode(inode); }
|
|
||||||
if ( path[0] == '/' )
|
|
||||||
{
|
|
||||||
if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return NULL; }
|
|
||||||
return ResolvePath(fd, sb, inode, path+1);
|
|
||||||
}
|
|
||||||
size_t elemlen = strcspn(path, "/");
|
|
||||||
char* elem = Substring(path, 0, elemlen);
|
|
||||||
uint32_t ino = Traverse(fd, sb, inode, elem);
|
|
||||||
free(elem);
|
|
||||||
if ( !ino ) { return NULL; }
|
|
||||||
initrd_inode_t* child = GetInode(fd, sb, ino);
|
|
||||||
if ( !child ) { return NULL; }
|
|
||||||
if ( !path[elemlen] ) { return child; }
|
|
||||||
initrd_inode_t* result = ResolvePath(fd, sb, child, path + elemlen);
|
|
||||||
free(child);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ListDirectory(int fd, initrd_superblock_t* sb, initrd_inode_t* dir,
|
|
||||||
bool all)
|
|
||||||
{
|
|
||||||
if ( !INITRD_S_ISDIR(dir->mode) ) { errno = ENOTDIR; return false; }
|
|
||||||
uint8_t* direntries = GetInodeData(fd, sb, dir);
|
|
||||||
if ( !direntries ) { return false; }
|
|
||||||
uint32_t offset = 0;
|
|
||||||
while ( offset < dir->size )
|
|
||||||
{
|
|
||||||
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
|
|
||||||
if ( dirent->namelen && (all || dirent->name[0] != '.'))
|
|
||||||
{
|
|
||||||
printf("%s\n", dirent->name);
|
|
||||||
}
|
|
||||||
offset += dirent->reclen;
|
|
||||||
}
|
|
||||||
free(direntries);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PrintFile(int fd, initrd_superblock_t* sb, initrd_inode_t* inode)
|
|
||||||
{
|
|
||||||
if ( INITRD_S_ISDIR(inode->mode ) ) { errno = EISDIR; return false; }
|
|
||||||
uint32_t sofar = 0;
|
|
||||||
while ( sofar < inode->size )
|
|
||||||
{
|
|
||||||
const size_t BUFFER_SIZE = 16UL * 1024UL;
|
|
||||||
uint8_t buffer[BUFFER_SIZE];
|
|
||||||
uint32_t available = inode->size - sofar;
|
|
||||||
uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE;
|
|
||||||
if ( !ReadInodeData(fd, sb, inode, buffer, count, 0) ) { return false; }
|
|
||||||
if ( writeall(1, buffer, count) != count ) { return false; }
|
|
||||||
sofar += count;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Extract(int fd,
|
|
||||||
const char* fd_path,
|
|
||||||
initrd_superblock_t* sb,
|
|
||||||
initrd_inode_t* inode,
|
|
||||||
const char* out_path,
|
|
||||||
bool verbose)
|
|
||||||
{
|
|
||||||
if ( verbose )
|
|
||||||
printf("%s\n", out_path);
|
|
||||||
if ( INITRD_S_ISLNK(inode->mode) )
|
|
||||||
{
|
|
||||||
char* buffer = (char*) malloc(inode->size + 1);
|
|
||||||
if ( !buffer )
|
|
||||||
err(1, "malloc");
|
|
||||||
if ( !ReadInodeData(fd, sb, inode, (uint8_t*) buffer, inode->size, 0) )
|
|
||||||
err(1, "%s", fd_path);
|
|
||||||
buffer[inode->size] = '\0';
|
|
||||||
// TODO: What if it already exists.
|
|
||||||
if ( symlink(buffer, out_path) < 0 )
|
|
||||||
err(1, "%s", out_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ( INITRD_S_ISREG(inode->mode) )
|
|
||||||
{
|
|
||||||
int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0200);
|
|
||||||
if ( out_fd < 0 )
|
|
||||||
err(1, "%s", out_path);
|
|
||||||
uint32_t sofar = 0;
|
|
||||||
while ( sofar < inode->size )
|
|
||||||
{
|
|
||||||
const size_t BUFFER_SIZE = 16UL * 1024UL;
|
|
||||||
uint8_t buffer[BUFFER_SIZE];
|
|
||||||
uint32_t available = inode->size - sofar;
|
|
||||||
uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE;
|
|
||||||
if ( !ReadInodeData(fd, sb, inode, buffer, count, sofar) )
|
|
||||||
err(1, "%s", fd_path);
|
|
||||||
if ( writeall(out_fd, buffer, count) != count )
|
|
||||||
err(1, "%s", out_path);
|
|
||||||
sofar += count;
|
|
||||||
}
|
|
||||||
if ( fchmod(out_fd, inode->mode & 07777) < 0 )
|
|
||||||
err(1, "%s", out_path);
|
|
||||||
close(out_fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ( !INITRD_S_ISDIR(inode->mode) )
|
|
||||||
errx(1, "%s: Unsupported kind of file", out_path);
|
|
||||||
bool made = true;
|
|
||||||
if ( mkdir(out_path, 0700) < 0 )
|
|
||||||
{
|
|
||||||
if ( errno != EEXIST )
|
|
||||||
err(1, "%s", out_path);
|
|
||||||
made = false;
|
|
||||||
}
|
|
||||||
uint8_t* direntries = GetInodeData(fd, sb, inode);
|
|
||||||
if ( !direntries )
|
|
||||||
err(1, "%s", out_path);
|
|
||||||
uint32_t offset = 0;
|
|
||||||
while ( offset < inode->size )
|
|
||||||
{
|
|
||||||
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
|
|
||||||
offset += dirent->reclen;
|
|
||||||
if ( !dirent->namelen ) // TODO: Possible?
|
|
||||||
continue;
|
|
||||||
if ( !strcmp(dirent->name, ".") || !strcmp(dirent->name, "..") )
|
|
||||||
continue;
|
|
||||||
char* child_path;
|
|
||||||
if ( asprintf(&child_path, "%s/%s", out_path, dirent->name) < 0 )
|
|
||||||
err(1, "asprintf");
|
|
||||||
initrd_inode_t* child_inode = ResolvePath(fd, sb, inode, dirent->name);
|
|
||||||
if ( !child_inode )
|
|
||||||
err(1, "%s: %s", fd_path, out_path);
|
|
||||||
Extract(fd, fd_path, sb, child_inode, child_path, verbose);
|
|
||||||
free(child_path);
|
|
||||||
}
|
|
||||||
free(direntries);
|
|
||||||
// TODO: Time of check to time of use race condition, a concurrent rename
|
|
||||||
// and we may assign the permissions to the wrong file, potentially
|
|
||||||
// exploitable.
|
|
||||||
if ( made && chmod(out_path, inode->mode & 07777) < 0 )
|
|
||||||
err(1, " %s", out_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
bool all = false;
|
|
||||||
const char* destination = ".";
|
|
||||||
bool verbose = 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] != '-' )
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
while ( (c = *++arg) ) switch ( c )
|
|
||||||
{
|
|
||||||
case 'a': all = true; break;
|
|
||||||
case 'C':
|
|
||||||
if ( !*(destination = arg + 1) )
|
|
||||||
{
|
|
||||||
if ( i + 1 == argc )
|
|
||||||
errx(1, "option requires an argument -- 'C'");
|
|
||||||
destination = argv[i+1];
|
|
||||||
argv[++i] = NULL;
|
|
||||||
}
|
|
||||||
arg = "C";
|
|
||||||
break;
|
|
||||||
case 'v': verbose = true; break;
|
|
||||||
default:
|
|
||||||
errx(1, "unknown option -- '%c'", c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
errx(1, "unknown option: %s", arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
compact_arguments(&argc, &argv);
|
|
||||||
|
|
||||||
if ( argc == 1 )
|
|
||||||
errx(1, "No initrd specified");
|
|
||||||
const char* initrd = argv[1];
|
|
||||||
if ( argc == 2 )
|
|
||||||
errx(1, "No command specified");
|
|
||||||
const char* cmd = argv[2];
|
|
||||||
|
|
||||||
int fd = open(initrd, O_RDONLY);
|
|
||||||
if ( fd < 0 )
|
|
||||||
err(1, "open: %s", initrd);
|
|
||||||
|
|
||||||
initrd_superblock_t* sb = GetSuperBlock(fd);
|
|
||||||
if ( !sb )
|
|
||||||
err(1, "read: %s", initrd);
|
|
||||||
|
|
||||||
initrd_inode_t* root = GetInode(fd, sb, sb->root);
|
|
||||||
if ( !root )
|
|
||||||
err(1, "read: %s", initrd);
|
|
||||||
|
|
||||||
for ( int i = 3; i < argc; i++ )
|
|
||||||
{
|
|
||||||
const char* path = argv[i];
|
|
||||||
if ( path[0] != '/' )
|
|
||||||
{
|
|
||||||
errno = ENOENT;
|
|
||||||
errx(1, "%s", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
initrd_inode_t* inode = ResolvePath(fd, sb, root, path+1);
|
|
||||||
if ( !inode )
|
|
||||||
err(1, "%s", path);
|
|
||||||
|
|
||||||
if ( !strcmp(cmd, "cat") )
|
|
||||||
{
|
|
||||||
if ( !PrintFile(fd, sb, inode) )
|
|
||||||
err(1, "%s", path);
|
|
||||||
}
|
|
||||||
else if ( !strcmp(cmd, "ls") )
|
|
||||||
{
|
|
||||||
if ( !ListDirectory(fd, sb, inode, all) )
|
|
||||||
err(1, "%s", path);
|
|
||||||
}
|
|
||||||
else if ( !strcmp(cmd, "extract") )
|
|
||||||
Extract(fd, initrd, sb, inode, destination, verbose);
|
|
||||||
else
|
|
||||||
errx(1, "unrecognized command: %s", cmd);
|
|
||||||
|
|
||||||
free(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
.Dd October 7, 2015
|
|
||||||
.Dt MKINITRD 8
|
|
||||||
.Os
|
|
||||||
.Sh NAME
|
|
||||||
.Nm mkinitrd
|
|
||||||
.Nd make initialization ramdisk
|
|
||||||
.Sh SYNOPSIS
|
|
||||||
.Nm mkinitrd
|
|
||||||
.Op Fl \-filter Ns "=" Ns Ar rules-file
|
|
||||||
.Op Fl \-format Ns "=" Ns Ar format
|
|
||||||
.Op Fl \-manifest Ns "=" Ns Ar manifest-file
|
|
||||||
.Fl o Ar destination
|
|
||||||
.Ar directory ...
|
|
||||||
.Sh DESCRIPTION
|
|
||||||
.Nm
|
|
||||||
produces a
|
|
||||||
.Xr initrd 7
|
|
||||||
for the Sortix
|
|
||||||
.Xr kernel 7
|
|
||||||
at the
|
|
||||||
.Ar destination .
|
|
||||||
It is an archive in the
|
|
||||||
.In sortix/initrd.h
|
|
||||||
format of files and directories.
|
|
||||||
.Pp
|
|
||||||
Every specified
|
|
||||||
.Ar directory
|
|
||||||
is used as a root directory and is recursively searched for files and
|
|
||||||
directories matching the filter.
|
|
||||||
If multiple directories are specified, the directories are merged together.
|
|
||||||
In case two files with the same path conflict, precedence is given to the file
|
|
||||||
in the root directory specified first.
|
|
||||||
.Pp
|
|
||||||
Hardlinks are detected and preserved to avoid data duplication.
|
|
||||||
Inode times are truncated to second precision due to format limitations.
|
|
||||||
Inodes are stored with uid 0 and gid 0 of the root user.
|
|
||||||
The format is not compressed but can be compressed externally if it is
|
|
||||||
decompressed during bootloading.
|
|
||||||
.Pp
|
|
||||||
.Xr initrdfs 8
|
|
||||||
can be used to view the files produced by
|
|
||||||
.Nm .
|
|
||||||
.Pp
|
|
||||||
The options are as follows:
|
|
||||||
.Bl -tag -width "12345678"
|
|
||||||
.It Fl \-filter Ns "=" Ns Ar rule-file
|
|
||||||
Include only files and directories during the recursive search that matches
|
|
||||||
rules in the
|
|
||||||
.Ar rule-file
|
|
||||||
in the format specified under
|
|
||||||
.Sx FILTER RULES .
|
|
||||||
.It Fl \-format Ns "=" Ns Ar format
|
|
||||||
Produce the archive in the specified format.
|
|
||||||
This is for forward compatibility and only
|
|
||||||
.Sy sortix-initrd-2
|
|
||||||
is supported.
|
|
||||||
.Sy default
|
|
||||||
is an alias for the newest format
|
|
||||||
.Sy sortix-initrd-2 .
|
|
||||||
.Nm
|
|
||||||
will default to a newer format when one is introduced and this allows
|
|
||||||
.Nm
|
|
||||||
to support old callers during the transitional period.
|
|
||||||
.It Fl \-manifest Ns "=" Ns Ar manifest-file
|
|
||||||
Include only files and directories during the recursive search whose path
|
|
||||||
exactly matches a line in the
|
|
||||||
.Ar manifest-file .
|
|
||||||
.It Fl o , Fl \-output Ns "=" Ns Ar destination
|
|
||||||
Store the produced
|
|
||||||
.Xr initrd 7
|
|
||||||
at the specified
|
|
||||||
.Ar destination .
|
|
||||||
.El
|
|
||||||
.Sh FILTER RULES
|
|
||||||
The rule format is line based and leading whitespace is skipped.
|
|
||||||
Lines starting with a
|
|
||||||
.Li #
|
|
||||||
character are ignored as comments.
|
|
||||||
The first word on a line must be one of the following commands and the rest of
|
|
||||||
the line is its parameter.
|
|
||||||
Trailing whitespace is not ignored.
|
|
||||||
.Bl -tag -width "12345678"
|
|
||||||
.It Sy default Ar boolean
|
|
||||||
The
|
|
||||||
.Ar boolean
|
|
||||||
parameter is either
|
|
||||||
.Sy true
|
|
||||||
or
|
|
||||||
.Sy false
|
|
||||||
and determines whether a file or directory is included if no other rules match
|
|
||||||
it.
|
|
||||||
This defaults to
|
|
||||||
.Sy true .
|
|
||||||
.It Sy include Ar path
|
|
||||||
Include the file or directory if it matches
|
|
||||||
.Ar path .
|
|
||||||
.It Sy exclude Ar path
|
|
||||||
Exclude the file or directory if it matches
|
|
||||||
.Ar path .
|
|
||||||
.El
|
|
||||||
.Pp
|
|
||||||
The rules are checked on the paths relative to the root directories during the
|
|
||||||
recursive descent.
|
|
||||||
The last rule to match a path decides whether it is included or not.
|
|
||||||
Directory are not descended into if they are excluded.
|
|
||||||
The pattern patch is simple and matches paths exactly.
|
|
||||||
.Sh EXIT STATUS
|
|
||||||
.Nm
|
|
||||||
will exit 0 on success and non-zero otherwise.
|
|
||||||
.Sh EXAMPLES
|
|
||||||
.Bd -literal
|
|
||||||
# By default include everything except these directories:
|
|
||||||
exclude /dev
|
|
||||||
exclude /src/sysroot
|
|
||||||
exclude /tmp
|
|
||||||
.Ed
|
|
||||||
.Sh SEE ALSO
|
|
||||||
.Xr initrd 7 ,
|
|
||||||
.Xr kernel 7 ,
|
|
||||||
.Xr initrdfs 8 ,
|
|
||||||
.Xr update-initrd 8
|
|
||||||
.Sh BUGS
|
|
||||||
The path pattern matching should be upgraded to use
|
|
||||||
.Xr fnmatch 3 .
|
|
||||||
The initrd format does not losslessly represent the Sortix
|
|
||||||
.Li struct stat .
|
|
|
@ -1,776 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012, 2013, 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.
|
|
||||||
*
|
|
||||||
* mkinitrd.c
|
|
||||||
* Produces a simple ramdisk filesystem readable by the Sortix kernel.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 <ioleast.h>
|
|
||||||
#include <stdalign.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sortix/initrd.h>
|
|
||||||
|
|
||||||
#define DEFAULT_FORMAT "sortix-initrd-2"
|
|
||||||
|
|
||||||
#include "rules.h"
|
|
||||||
#include "serialize.h"
|
|
||||||
|
|
||||||
uint32_t HostModeToInitRD(mode_t mode)
|
|
||||||
{
|
|
||||||
uint32_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
|
|
||||||
if ( S_ISVTX & mode ) { result |= INITRD_S_ISVTX; }
|
|
||||||
if ( S_ISSOCK(mode) ) { result |= INITRD_S_IFSOCK; }
|
|
||||||
if ( S_ISLNK(mode) ) { result |= INITRD_S_IFLNK; }
|
|
||||||
if ( S_ISREG(mode) ) { result |= INITRD_S_IFREG; }
|
|
||||||
if ( S_ISBLK(mode) ) { result |= INITRD_S_IFBLK; }
|
|
||||||
if ( S_ISDIR(mode) ) { result |= INITRD_S_IFDIR; }
|
|
||||||
if ( S_ISCHR(mode) ) { result |= INITRD_S_IFCHR; }
|
|
||||||
if ( S_ISFIFO(mode) ) { result |= INITRD_S_IFIFO; }
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
mode_t InitRDModeToHost(uint32_t mode)
|
|
||||||
{
|
|
||||||
mode_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
|
|
||||||
if ( INITRD_S_ISVTX & mode ) { result |= S_ISVTX; }
|
|
||||||
if ( INITRD_S_ISSOCK(mode) ) { result |= S_IFSOCK; }
|
|
||||||
if ( INITRD_S_ISLNK(mode) ) { result |= S_IFLNK; }
|
|
||||||
if ( INITRD_S_ISREG(mode) ) { result |= S_IFREG; }
|
|
||||||
if ( INITRD_S_ISBLK(mode) ) { result |= S_IFBLK; }
|
|
||||||
if ( INITRD_S_ISDIR(mode) ) { result |= S_IFDIR; }
|
|
||||||
if ( INITRD_S_ISCHR(mode) ) { result |= S_IFCHR; }
|
|
||||||
if ( INITRD_S_ISFIFO(mode) ) { result |= S_IFIFO; }
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node;
|
|
||||||
struct DirEntry;
|
|
||||||
|
|
||||||
struct DirEntry
|
|
||||||
{
|
|
||||||
char* name;
|
|
||||||
struct Node* node;
|
|
||||||
};
|
|
||||||
|
|
||||||
int DirEntryCompare(const struct DirEntry* a, const struct DirEntry* b)
|
|
||||||
{
|
|
||||||
return strcmp(a->name, b->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int DirEntryCompareIndirect(const void* a_ptr, const void* b_ptr)
|
|
||||||
{
|
|
||||||
const struct DirEntry* a = (const struct DirEntry*) a_ptr;
|
|
||||||
const struct DirEntry* b = (const struct DirEntry*) b_ptr;
|
|
||||||
return DirEntryCompare(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node
|
|
||||||
{
|
|
||||||
char* path;
|
|
||||||
uint32_t ino;
|
|
||||||
uint32_t nlink;
|
|
||||||
size_t direntsused;
|
|
||||||
size_t direntslength;
|
|
||||||
struct DirEntry* dirents;
|
|
||||||
mode_t mode;
|
|
||||||
time_t ctime;
|
|
||||||
time_t mtime;
|
|
||||||
bool written;
|
|
||||||
size_t refcount;
|
|
||||||
};
|
|
||||||
|
|
||||||
void FreeNode(struct Node* node)
|
|
||||||
{
|
|
||||||
if ( !node )
|
|
||||||
return;
|
|
||||||
if ( 1 < node->nlink ) { node->nlink--; return; }
|
|
||||||
for ( size_t i = 0; i < node->direntsused; i++ )
|
|
||||||
{
|
|
||||||
struct DirEntry* entry = node->dirents + i;
|
|
||||||
if ( !entry->name )
|
|
||||||
continue;
|
|
||||||
if ( strcmp(entry->name, ".") != 0 && strcmp(entry->name, "..") != 0 )
|
|
||||||
{
|
|
||||||
if ( --entry->node->refcount == 0 )
|
|
||||||
FreeNode(entry->node);
|
|
||||||
}
|
|
||||||
free(entry->name);
|
|
||||||
}
|
|
||||||
free(node->dirents);
|
|
||||||
free(node->path);
|
|
||||||
free(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CacheEntry
|
|
||||||
{
|
|
||||||
ino_t ino;
|
|
||||||
dev_t dev;
|
|
||||||
struct Node* node;
|
|
||||||
};
|
|
||||||
|
|
||||||
static size_t cacheused = 0;
|
|
||||||
static size_t cachelen = 0;
|
|
||||||
static struct CacheEntry* cache = NULL;
|
|
||||||
|
|
||||||
struct Node* LookupCache(dev_t dev, ino_t ino)
|
|
||||||
{
|
|
||||||
for ( size_t i = 0; i < cacheused; i++ )
|
|
||||||
if ( cache[i].dev == dev && cache[i].ino == ino )
|
|
||||||
return cache[i].node;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddToCache(struct Node* node, dev_t dev, ino_t ino)
|
|
||||||
{
|
|
||||||
if ( cacheused == cachelen )
|
|
||||||
{
|
|
||||||
size_t newcachelen = cachelen ? 2 * cachelen : 256;
|
|
||||||
size_t newcachesize = newcachelen * sizeof(struct CacheEntry);
|
|
||||||
struct CacheEntry* newcache =
|
|
||||||
(struct CacheEntry*) realloc(cache, newcachesize);
|
|
||||||
if ( !newcache )
|
|
||||||
return false;
|
|
||||||
cache = newcache;
|
|
||||||
cachelen = newcachelen;
|
|
||||||
}
|
|
||||||
size_t index = cacheused++;
|
|
||||||
cache[index].ino = ino;
|
|
||||||
cache[index].dev = dev;
|
|
||||||
cache[index].node = node;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node* RecursiveSearch(const char* real_path, const char* virt_path,
|
|
||||||
uint32_t* ino, struct Node* parent)
|
|
||||||
{
|
|
||||||
printf("%s\n", virt_path);
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
if ( virt_path[0] == '/' && !virt_path[1] )
|
|
||||||
virt_path = "";
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
if ( lstat(real_path, &st) != 0 )
|
|
||||||
{
|
|
||||||
warn("stat: %s", real_path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !S_ISDIR(st.st_mode) && 2 <= st.st_nlink )
|
|
||||||
{
|
|
||||||
struct Node* cached = LookupCache(st.st_dev, st.st_ino);
|
|
||||||
if ( cached )
|
|
||||||
return cached->nlink++, cached->refcount++, cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node* node = (struct Node*) calloc(1, sizeof(struct Node));
|
|
||||||
if ( !node )
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
node->nlink = 1;
|
|
||||||
node->refcount = 1;
|
|
||||||
node->mode = st.st_mode;
|
|
||||||
node->ino = (*ino)++;
|
|
||||||
node->ctime = st.st_ctim.tv_sec;
|
|
||||||
node->mtime = st.st_mtim.tv_sec;
|
|
||||||
|
|
||||||
char* real_path_clone = strdup(real_path);
|
|
||||||
if ( !real_path_clone )
|
|
||||||
{
|
|
||||||
warn("strdup");
|
|
||||||
free(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node->path = real_path_clone;
|
|
||||||
|
|
||||||
if ( !S_ISDIR(st.st_mode))
|
|
||||||
{
|
|
||||||
if ( 2 <= st.st_nlink && !AddToCache(node, st.st_dev, st.st_ino) )
|
|
||||||
{
|
|
||||||
free(real_path_clone);
|
|
||||||
free(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
DIR* dir = opendir(real_path);
|
|
||||||
if ( !dir )
|
|
||||||
{
|
|
||||||
warn("opendir: %s", real_path);
|
|
||||||
FreeNode(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t real_path_len = strlen(real_path);
|
|
||||||
size_t virt_path_len = strlen(virt_path);
|
|
||||||
|
|
||||||
bool successful = true;
|
|
||||||
struct dirent* entry;
|
|
||||||
while ( (entry = readdir(dir)) )
|
|
||||||
{
|
|
||||||
size_t namelen = strlen(entry->d_name);
|
|
||||||
|
|
||||||
size_t virt_subpath_len = virt_path_len + 1 + namelen;
|
|
||||||
char* virt_subpath = (char*) malloc(virt_subpath_len+1);
|
|
||||||
if ( !virt_subpath )
|
|
||||||
{
|
|
||||||
warn("malloc");
|
|
||||||
successful = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stpcpy(stpcpy(stpcpy(virt_subpath, virt_path), "/"), entry->d_name);
|
|
||||||
|
|
||||||
if ( strcmp(entry->d_name, ".") != 0 &&
|
|
||||||
strcmp(entry->d_name, "..") != 0 &&
|
|
||||||
!IncludesPath(virt_subpath) )
|
|
||||||
{
|
|
||||||
free(virt_subpath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t real_subpath_len = real_path_len + 1 + namelen;
|
|
||||||
char* real_subpath = (char*) malloc(real_subpath_len+1);
|
|
||||||
if ( !real_subpath )
|
|
||||||
{
|
|
||||||
free(virt_subpath);
|
|
||||||
warn("malloc");
|
|
||||||
successful = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stpcpy(stpcpy(stpcpy(real_subpath, real_path), "/"), entry->d_name);
|
|
||||||
|
|
||||||
struct Node* child = NULL;
|
|
||||||
if ( !strcmp(entry->d_name, ".") )
|
|
||||||
child = node;
|
|
||||||
if ( !strcmp(entry->d_name, "..") )
|
|
||||||
child = parent ? parent : node;
|
|
||||||
if ( !child )
|
|
||||||
child = RecursiveSearch(real_subpath, virt_subpath, ino, node);
|
|
||||||
free(real_subpath);
|
|
||||||
free(virt_subpath);
|
|
||||||
if ( !child )
|
|
||||||
{
|
|
||||||
successful = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( node->direntsused == node->direntslength )
|
|
||||||
{
|
|
||||||
size_t oldlength = node->direntslength;
|
|
||||||
size_t newlength = oldlength ? 2 * oldlength : 8;
|
|
||||||
size_t newsize = sizeof(struct DirEntry) * newlength;
|
|
||||||
struct DirEntry* newdirents = (struct DirEntry*) realloc(node->dirents, newsize);
|
|
||||||
if ( !newdirents )
|
|
||||||
{
|
|
||||||
warn("realloc");
|
|
||||||
successful = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
node->dirents = newdirents;
|
|
||||||
node->direntslength = newlength;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* nameclone = strdup(entry->d_name);
|
|
||||||
if ( !nameclone )
|
|
||||||
{
|
|
||||||
warn("strdup");
|
|
||||||
successful = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DirEntry* entry = node->dirents + node->direntsused++;
|
|
||||||
|
|
||||||
entry->name = nameclone;
|
|
||||||
entry->node = child;
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dir);
|
|
||||||
if ( !successful )
|
|
||||||
{
|
|
||||||
FreeNode(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
qsort(node->dirents, node->direntsused, sizeof(struct DirEntry),
|
|
||||||
DirEntryCompareIndirect);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node* MergeNodes(struct Node* a, struct Node* b)
|
|
||||||
{
|
|
||||||
if ( !S_ISDIR(a->mode) || !S_ISDIR(b->mode) )
|
|
||||||
{
|
|
||||||
FreeNode(b);
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
size_t dirents_used = 0;
|
|
||||||
size_t dirents_length = a->direntsused + b->direntsused;
|
|
||||||
struct DirEntry* dirents = (struct DirEntry*)
|
|
||||||
malloc(sizeof(struct DirEntry) * dirents_length);
|
|
||||||
if ( !dirents )
|
|
||||||
{
|
|
||||||
warn("malloc");
|
|
||||||
FreeNode(a);
|
|
||||||
FreeNode(b);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
bool failure = false;
|
|
||||||
size_t ai = 0;
|
|
||||||
size_t bi = 0;
|
|
||||||
while ( ai != a->direntsused || bi != b->direntsused )
|
|
||||||
{
|
|
||||||
if ( bi == b->direntsused ||
|
|
||||||
(ai != a->direntsused &&
|
|
||||||
bi != b->direntsused &&
|
|
||||||
DirEntryCompare(&a->dirents[ai], &b->dirents[bi]) < 0) )
|
|
||||||
{
|
|
||||||
dirents[dirents_used++] = a->dirents[ai];
|
|
||||||
a->dirents[ai].name = NULL;
|
|
||||||
a->dirents[ai].node = NULL;
|
|
||||||
ai++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( ai == a->direntsused ||
|
|
||||||
(ai != a->direntsused &&
|
|
||||||
bi != b->direntsused &&
|
|
||||||
DirEntryCompare(&a->dirents[ai], &b->dirents[bi]) > 0) )
|
|
||||||
{
|
|
||||||
dirents[dirents_used++] = b->dirents[bi];
|
|
||||||
for ( size_t i = 0; i < b->dirents[bi].node->direntsused; i++ )
|
|
||||||
{
|
|
||||||
if ( strcmp(b->dirents[bi].node->dirents[i].name, "..") != 0 )
|
|
||||||
continue;
|
|
||||||
b->dirents[bi].node->dirents[i].node = a;
|
|
||||||
}
|
|
||||||
b->dirents[bi].name = NULL;
|
|
||||||
b->dirents[bi].node = NULL;
|
|
||||||
bi++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const char* name = a->dirents[ai].name;
|
|
||||||
dirents[dirents_used].name = a->dirents[ai].name;
|
|
||||||
if ( !strcmp(name, ".") || !strcmp(name, "..") )
|
|
||||||
dirents[dirents_used].node = a->dirents[ai].node;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dirents[dirents_used].node =
|
|
||||||
MergeNodes(a->dirents[ai].node, b->dirents[bi].node);
|
|
||||||
if ( !dirents[dirents_used].node )
|
|
||||||
failure = true;
|
|
||||||
}
|
|
||||||
dirents_used++;
|
|
||||||
a->dirents[ai].name = NULL;
|
|
||||||
a->dirents[ai].node = NULL;
|
|
||||||
ai++;
|
|
||||||
free(b->dirents[bi].name);
|
|
||||||
b->dirents[bi].name = NULL;
|
|
||||||
b->dirents[bi].node = NULL;
|
|
||||||
bi++;
|
|
||||||
}
|
|
||||||
free(a->dirents);
|
|
||||||
a->dirents = dirents;
|
|
||||||
a->direntsused = dirents_used;
|
|
||||||
a->direntslength = dirents_length;
|
|
||||||
b->direntsused = 0;
|
|
||||||
FreeNode(b);
|
|
||||||
if ( failure )
|
|
||||||
return FreeNode(b), (struct Node*) NULL;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriteNode(struct initrd_superblock* sb, int fd, const char* outputname,
|
|
||||||
struct Node* node)
|
|
||||||
{
|
|
||||||
if ( node->written )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint32_t filesize = 0;
|
|
||||||
uint32_t origfssize = sb->fssize;
|
|
||||||
uint32_t dataoff = origfssize;
|
|
||||||
uint32_t filestart = dataoff;
|
|
||||||
|
|
||||||
if ( S_ISLNK(node->mode) ) // Symbolic link
|
|
||||||
{
|
|
||||||
const size_t NAME_SIZE = 1024UL;
|
|
||||||
char name[NAME_SIZE];
|
|
||||||
ssize_t namelen = readlink(node->path, name, NAME_SIZE);
|
|
||||||
if ( namelen < 0 )
|
|
||||||
return warn("readlink: %s", node->path), false;
|
|
||||||
filesize = (uint32_t) namelen;
|
|
||||||
if ( pwriteall(fd, name, filesize, dataoff) < filesize )
|
|
||||||
return warn("read: %s", node->path), false;
|
|
||||||
dataoff += filesize;
|
|
||||||
}
|
|
||||||
else if ( S_ISREG(node->mode) ) // Regular file
|
|
||||||
{
|
|
||||||
int nodefd = open(node->path, O_RDONLY);
|
|
||||||
if ( nodefd < 0 )
|
|
||||||
return warn("open: %s", node->path), false;
|
|
||||||
const size_t BUFFER_SIZE = 16UL * 1024UL;
|
|
||||||
uint8_t buffer[BUFFER_SIZE];
|
|
||||||
ssize_t amount;
|
|
||||||
while ( 0 < (amount = read(nodefd, buffer, BUFFER_SIZE)) )
|
|
||||||
{
|
|
||||||
if ( pwriteall(fd, buffer, amount, dataoff) < (size_t) amount )
|
|
||||||
{
|
|
||||||
close(nodefd);
|
|
||||||
return warn("write: %s", outputname), false;
|
|
||||||
}
|
|
||||||
dataoff += amount;
|
|
||||||
filesize += amount;
|
|
||||||
}
|
|
||||||
close(nodefd);
|
|
||||||
if ( amount < 0 )
|
|
||||||
return warn("read: %s", node->path), false;
|
|
||||||
}
|
|
||||||
else if ( S_ISDIR(node->mode) ) // Directory
|
|
||||||
{
|
|
||||||
for ( size_t i = 0; i < node->direntsused; i++ )
|
|
||||||
{
|
|
||||||
struct DirEntry* entry = node->dirents + i;
|
|
||||||
const char* name = entry->name;
|
|
||||||
size_t namelen = strlen(entry->name);
|
|
||||||
struct initrd_dirent dirent;
|
|
||||||
dirent.inode = entry->node->ino;
|
|
||||||
dirent.namelen = (uint16_t) namelen;
|
|
||||||
dirent.reclen = sizeof(dirent) + dirent.namelen + 1;
|
|
||||||
dirent.reclen = (dirent.reclen+3)/4*4; // Align entries.
|
|
||||||
size_t entsize = sizeof(dirent);
|
|
||||||
export_initrd_dirent(&dirent);
|
|
||||||
assert((dataoff & (alignof(dirent)-1)) == 0 );
|
|
||||||
ssize_t hdramt = pwriteall(fd, &dirent, entsize, dataoff);
|
|
||||||
import_initrd_dirent(&dirent);
|
|
||||||
ssize_t nameamt = pwriteall(fd, name, namelen+1, dataoff + entsize);
|
|
||||||
if ( hdramt < (ssize_t) entsize || nameamt < (ssize_t) (namelen+1) )
|
|
||||||
return warn("write: %s", outputname), false;
|
|
||||||
size_t padding = dirent.reclen - (entsize + (namelen+1));
|
|
||||||
for ( size_t n = 0; n < padding; n++ )
|
|
||||||
{
|
|
||||||
uint8_t nul = 0;
|
|
||||||
if ( pwrite(fd, &nul, 1, dataoff+entsize+namelen+1+n) != 1 )
|
|
||||||
return warn("write: %s", outputname), false;
|
|
||||||
}
|
|
||||||
filesize += dirent.reclen;
|
|
||||||
dataoff += dirent.reclen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct initrd_inode inode;
|
|
||||||
inode.mode = HostModeToInitRD(node->mode);
|
|
||||||
inode.uid = 0;
|
|
||||||
inode.gid = 0;
|
|
||||||
inode.nlink = node->nlink;
|
|
||||||
inode.ctime = (uint64_t) node->ctime;
|
|
||||||
inode.mtime = (uint64_t) node->mtime;
|
|
||||||
inode.dataoffset = filestart;
|
|
||||||
inode.size = filesize;
|
|
||||||
|
|
||||||
uint32_t inodepos = sb->inodeoffset + node->ino * sb->inodesize;
|
|
||||||
uint32_t inodesize = sizeof(inode);
|
|
||||||
export_initrd_inode(&inode);
|
|
||||||
assert((inodepos & (alignof(inode)-1)) == 0 );
|
|
||||||
if ( pwriteall(fd, &inode, inodesize, inodepos) < inodesize )
|
|
||||||
return warn("write: %s", outputname), false;
|
|
||||||
import_initrd_inode(&inode);
|
|
||||||
|
|
||||||
uint32_t increment = dataoff - origfssize;
|
|
||||||
sb->fssize += increment;
|
|
||||||
sb->fssize = (sb->fssize+7)/8*8; // Align upwards.
|
|
||||||
|
|
||||||
return node->written = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriteNodeRecursive(struct initrd_superblock* sb, int fd,
|
|
||||||
const char* outputname, struct Node* node)
|
|
||||||
{
|
|
||||||
if ( !WriteNode(sb, fd, outputname, node) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( !S_ISDIR(node->mode) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < node->direntsused; i++ )
|
|
||||||
{
|
|
||||||
struct DirEntry* entry = node->dirents + i;
|
|
||||||
const char* name = entry->name;
|
|
||||||
struct Node* child = entry->node;
|
|
||||||
if ( !strcmp(name, ".") || !strcmp(name, ".." ) )
|
|
||||||
continue;
|
|
||||||
if ( !WriteNodeRecursive(sb, fd, outputname, child) )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FormatFD(const char* outputname, int fd, uint32_t inodecount,
|
|
||||||
struct Node* root)
|
|
||||||
{
|
|
||||||
struct initrd_superblock sb;
|
|
||||||
memset(&sb, 0, sizeof(sb));
|
|
||||||
strncpy(sb.magic, "sortix-initrd-2", sizeof(sb.magic));
|
|
||||||
sb.revision = 0;
|
|
||||||
sb.fssize = sizeof(sb);
|
|
||||||
sb.inodesize = sizeof(struct initrd_inode);
|
|
||||||
sb.inodeoffset = sizeof(sb);
|
|
||||||
sb.inodecount = inodecount;
|
|
||||||
sb.root = root->ino;
|
|
||||||
|
|
||||||
uint32_t inodebytecount = sb.inodesize * sb.inodecount;
|
|
||||||
sb.fssize += inodebytecount;
|
|
||||||
sb.fssize = (sb.fssize+7)/8*8; // Align upwards.
|
|
||||||
|
|
||||||
if ( !WriteNodeRecursive(&sb, fd, outputname, root) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
sb.sumalgorithm = INITRD_ALGO_NONE;
|
|
||||||
sb.sumsize = 0;
|
|
||||||
sb.fssize = (sb.fssize+3)/4*4; // Align upwards.
|
|
||||||
sb.fssize += sb.sumsize;
|
|
||||||
|
|
||||||
export_initrd_superblock(&sb);
|
|
||||||
if ( pwriteall(fd, &sb, sizeof(sb), 0) < sizeof(sb) )
|
|
||||||
{
|
|
||||||
warn("write: %s", outputname);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
import_initrd_superblock(&sb);
|
|
||||||
|
|
||||||
if ( ftruncate(fd, sb.fssize) < 0 )
|
|
||||||
{
|
|
||||||
warn("truncate: %s", outputname);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Format(const char* pathname, uint32_t inodecount, struct Node* root)
|
|
||||||
{
|
|
||||||
int fd = open(pathname, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
|
||||||
bool result = FormatFD(pathname, fd, inodecount, root);
|
|
||||||
close(fd);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_option_variable(const char* option, char** varptr,
|
|
||||||
const char* arg, int argc, char** argv, int* ip,
|
|
||||||
const char* argv0)
|
|
||||||
{
|
|
||||||
size_t option_len = strlen(option);
|
|
||||||
if ( strncmp(option, arg, option_len) != 0 )
|
|
||||||
return false;
|
|
||||||
if ( arg[option_len] == '=' )
|
|
||||||
{
|
|
||||||
*varptr = strdup(arg + option_len + 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ( arg[option_len] != '\0' )
|
|
||||||
return false;
|
|
||||||
if ( *ip + 1 == argc )
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: expected operand after `%s'\n", argv0, option);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
*varptr = strdup(argv[++*ip]), argv[*ip] = NULL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GET_OPTION_VARIABLE(str, varptr) \
|
|
||||||
get_option_variable(str, varptr, arg, argc, argv, &i, argv0)
|
|
||||||
|
|
||||||
static void help(FILE* fp, const char* argv0)
|
|
||||||
{
|
|
||||||
fprintf(fp, "Usage: %s [OPTION]... ROOT... -o OUTPUT\n", argv0);
|
|
||||||
fprintf(fp, "Creates a init ramdisk for the Sortix kernel.\n");
|
|
||||||
fprintf(fp, "\n");
|
|
||||||
fprintf(fp, "Mandatory arguments to long options are mandatory for short options too.\n");
|
|
||||||
fprintf(fp, " --filter=FILE import filter rules from FILE\n");
|
|
||||||
fprintf(fp, " --format=FORMAT format version [%s]\n", DEFAULT_FORMAT);
|
|
||||||
fprintf(fp, " -o, --output=FILE write result to FILE\n");
|
|
||||||
fprintf(fp, " --help display this help and exit\n");
|
|
||||||
fprintf(fp, " --version output version information and exit\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void version(FILE* fp, const char* argv0)
|
|
||||||
{
|
|
||||||
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
char* arg_filter = NULL;
|
|
||||||
char* arg_format = strdup(DEFAULT_FORMAT);
|
|
||||||
char* arg_manifest = NULL;
|
|
||||||
char* arg_output = NULL;
|
|
||||||
|
|
||||||
const char* argv0 = argv[0];
|
|
||||||
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] != '-' )
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
while ( (c = *++arg) ) switch ( c )
|
|
||||||
{
|
|
||||||
case 'o':
|
|
||||||
free(arg_output);
|
|
||||||
if ( *(arg+1) )
|
|
||||||
arg_output = strdup(arg + 1);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( i + 1 == argc )
|
|
||||||
{
|
|
||||||
warnx("option requires an argument -- 'o'");
|
|
||||||
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
||||||
exit(125);
|
|
||||||
}
|
|
||||||
arg_output = strdup(argv[i+1]);
|
|
||||||
argv[++i] = NULL;
|
|
||||||
}
|
|
||||||
arg = "o";
|
|
||||||
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 ( GET_OPTION_VARIABLE("--filter", &arg_filter) )
|
|
||||||
{
|
|
||||||
FILE* fp = fopen(arg_filter, "r");
|
|
||||||
if ( !fp )
|
|
||||||
err(1, "%s", arg_filter);
|
|
||||||
if ( !AddRulesFromFile(fp, arg_filter) )
|
|
||||||
exit(1);
|
|
||||||
fclose(fp);
|
|
||||||
free(arg_filter);
|
|
||||||
arg_filter = NULL;
|
|
||||||
}
|
|
||||||
else if ( GET_OPTION_VARIABLE("--manifest", &arg_manifest) )
|
|
||||||
{
|
|
||||||
FILE* fp = fopen(arg_manifest, "r");
|
|
||||||
if ( !fp )
|
|
||||||
err(1, "%s", arg_manifest);
|
|
||||||
if ( !AddManifestFromFile(fp, arg_manifest) )
|
|
||||||
exit(1);
|
|
||||||
fclose(fp);
|
|
||||||
free(arg_manifest);
|
|
||||||
arg_manifest = NULL;
|
|
||||||
}
|
|
||||||
else if ( GET_OPTION_VARIABLE("--format", &arg_format) ) { }
|
|
||||||
else if ( GET_OPTION_VARIABLE("--output", &arg_output) ) { }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
|
||||||
help(stderr, argv0);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( argc < 2 )
|
|
||||||
{
|
|
||||||
help(stdout, argv0);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
compact_arguments(&argc, &argv);
|
|
||||||
|
|
||||||
if ( argc < 2 )
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: No root specified\n", argv0),
|
|
||||||
help(stderr, argv0);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !arg_output )
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: No output file specified\n", argv0),
|
|
||||||
help(stderr, argv0);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* format = arg_format;
|
|
||||||
|
|
||||||
if ( !strcmp(format, "default") )
|
|
||||||
format = DEFAULT_FORMAT;
|
|
||||||
|
|
||||||
if ( strcmp(format, "sortix-initrd-2") != 0 )
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: Unsupported format `%s'\n", argv0, format);
|
|
||||||
fprintf(stderr, "Try `%s --help' for more information.\n", argv0);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t inodecount = 1;
|
|
||||||
struct Node* root = NULL;
|
|
||||||
for ( int i = 1; i < argc; i++ )
|
|
||||||
{
|
|
||||||
struct Node* node = RecursiveSearch(argv[i], "/", &inodecount, NULL);
|
|
||||||
if ( !node )
|
|
||||||
exit(1);
|
|
||||||
if ( root )
|
|
||||||
root = MergeNodes(root, node);
|
|
||||||
else
|
|
||||||
root = node;
|
|
||||||
if ( !root )
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !Format(arg_output, inodecount, root) )
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
FreeNode(root);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
317
mkinitrd/rules.c
317
mkinitrd/rules.c
|
@ -1,317 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2013, 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.
|
|
||||||
*
|
|
||||||
* rules.c
|
|
||||||
* Determines whether a given path is included in the filesystem.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <err.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "rules.h"
|
|
||||||
|
|
||||||
static struct InclusionRule** rules;
|
|
||||||
static size_t num_rules;
|
|
||||||
static size_t num_rules_allocated;
|
|
||||||
static bool default_inclusion = true;
|
|
||||||
static bool default_inclusion_determined;
|
|
||||||
static char** manifest;
|
|
||||||
static size_t manifest_used;
|
|
||||||
static size_t manifest_length;
|
|
||||||
|
|
||||||
static const char* SkipCharacters(const char* str, char c)
|
|
||||||
{
|
|
||||||
while ( *str == c)
|
|
||||||
str++;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /usr/bin/foobar match /usr = true
|
|
||||||
// /usr/bin/foobar match usr = false
|
|
||||||
// ///usr////bin//foobar match //usr// = true
|
|
||||||
// ///usr////bin//foobar match //usr//./evince = false
|
|
||||||
// TODO: Should this support . and .. too?
|
|
||||||
static bool PathMatchesPattern(const char* path, const char* pattern)
|
|
||||||
{
|
|
||||||
bool last_was_slash = false;
|
|
||||||
while ( true )
|
|
||||||
{
|
|
||||||
if ( !*pattern )
|
|
||||||
return !*path || last_was_slash;
|
|
||||||
if ( (last_was_slash = *pattern == '/') )
|
|
||||||
{
|
|
||||||
if ( *path == '/' )
|
|
||||||
{
|
|
||||||
path = SkipCharacters(path, '/');
|
|
||||||
pattern = SkipCharacters(pattern, '/');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( *pattern++ != *path++ )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int search_path(const void* a_ptr, const void* b_ptr)
|
|
||||||
{
|
|
||||||
const char* key = (const char*) a_ptr;
|
|
||||||
char* path = *(char**) b_ptr;
|
|
||||||
return strcmp(key, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IncludesPath(const char* path)
|
|
||||||
{
|
|
||||||
bool determined = false;
|
|
||||||
bool included = false;
|
|
||||||
for ( size_t i = 0; i < num_rules; i++ )
|
|
||||||
{
|
|
||||||
struct InclusionRule* rule = rules[i];
|
|
||||||
if ( !PathMatchesPattern(path, rule->pattern) )
|
|
||||||
continue;
|
|
||||||
switch ( rules[i]->rule )
|
|
||||||
{
|
|
||||||
case RULE_INCLUDE:
|
|
||||||
included = true;
|
|
||||||
determined = true;
|
|
||||||
break;
|
|
||||||
case RULE_EXCLUDE:
|
|
||||||
included = false;
|
|
||||||
determined = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !determined )
|
|
||||||
included = default_inclusion;
|
|
||||||
if ( !included )
|
|
||||||
return false;
|
|
||||||
if ( manifest_used &&
|
|
||||||
!bsearch(path, manifest, manifest_used, sizeof(char*), search_path) )
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChangeRulesAmount(size_t new_length)
|
|
||||||
{
|
|
||||||
size_t new_num_rules = new_length < num_rules ? new_length : num_rules;
|
|
||||||
for ( size_t i = new_num_rules; i < num_rules; i++ )
|
|
||||||
{
|
|
||||||
free(rules[i]->pattern);
|
|
||||||
free(rules[i]);
|
|
||||||
}
|
|
||||||
num_rules = new_num_rules;
|
|
||||||
struct InclusionRule** new_rules = (struct InclusionRule**)
|
|
||||||
malloc(sizeof(struct InclusionRule*) * new_length);
|
|
||||||
for ( size_t i = 0; i < new_length && i < num_rules; i++ )
|
|
||||||
new_rules[i] = rules[i];
|
|
||||||
free(rules); rules = new_rules;
|
|
||||||
num_rules_allocated = new_length;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddRule(struct InclusionRule* rule)
|
|
||||||
{
|
|
||||||
if ( num_rules == num_rules_allocated )
|
|
||||||
{
|
|
||||||
size_t new_length = num_rules_allocated ? 2 * num_rules_allocated : 32;
|
|
||||||
if ( !ChangeRulesAmount(new_length) )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rules[num_rules++] = rule;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* SkipWhitespace(const char* line)
|
|
||||||
{
|
|
||||||
while ( *line && isspace((unsigned char) *line) )
|
|
||||||
line++;
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsLineComment(const char* line)
|
|
||||||
{
|
|
||||||
return !*line || *line == '#';
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* IsLineCommand(const char* line, const char* command)
|
|
||||||
{
|
|
||||||
while ( *line && isspace((unsigned char) *line) )
|
|
||||||
line++;
|
|
||||||
size_t cmdlen = strlen(command);
|
|
||||||
if ( strncmp(line, command, cmdlen) != 0 )
|
|
||||||
return NULL;
|
|
||||||
if ( line[cmdlen] && !isspace((unsigned char) line[cmdlen]) )
|
|
||||||
return NULL;
|
|
||||||
while ( line[cmdlen] && isspace((unsigned char) line[cmdlen]) )
|
|
||||||
cmdlen++;
|
|
||||||
return line + cmdlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddRulesFromFile(FILE* fp, const char* fpname)
|
|
||||||
{
|
|
||||||
size_t rules_at_start = num_rules;
|
|
||||||
size_t line_size;
|
|
||||||
size_t line_num = 0;
|
|
||||||
char* mem = NULL;
|
|
||||||
ssize_t line_len;
|
|
||||||
while ( 0 < (line_len = getline(&mem, &line_size, fp)) )
|
|
||||||
{
|
|
||||||
char* line = mem;
|
|
||||||
line_num++;
|
|
||||||
if ( line[line_len-1] == '\n' )
|
|
||||||
line[--line_len] = '\0';
|
|
||||||
line = (char*) SkipWhitespace((char*) line);
|
|
||||||
if ( IsLineComment(line) )
|
|
||||||
continue;
|
|
||||||
const char* parameter;
|
|
||||||
if ( (parameter = IsLineCommand(line, "default")) )
|
|
||||||
{
|
|
||||||
bool value;
|
|
||||||
if ( !strcmp(parameter, "true") )
|
|
||||||
value = true;
|
|
||||||
else if ( !strcmp(parameter, "false") )
|
|
||||||
value = false;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warnx("%s:%zu: not a boolean '%s'", fpname, line_num, parameter);
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
if ( !default_inclusion_determined )
|
|
||||||
default_inclusion = value,
|
|
||||||
default_inclusion_determined = true;
|
|
||||||
else
|
|
||||||
default_inclusion = default_inclusion || value;
|
|
||||||
}
|
|
||||||
else if ( (parameter = IsLineCommand(line, "exclude")) ||
|
|
||||||
(parameter = IsLineCommand(line, "include")) )
|
|
||||||
{
|
|
||||||
if ( !*parameter )
|
|
||||||
{
|
|
||||||
warnx("%s:%zu: no parameter given", fpname, line_num);
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
const char* pattern = parameter;
|
|
||||||
enum InclusionRuleType type = line[0] == 'e' ? RULE_EXCLUDE : RULE_INCLUDE;
|
|
||||||
struct InclusionRule* rule =
|
|
||||||
(struct InclusionRule*) malloc(sizeof(struct InclusionRule));
|
|
||||||
rule->pattern = strdup(pattern);
|
|
||||||
rule->rule = type;
|
|
||||||
if ( !AddRule(rule) )
|
|
||||||
goto error_out_errno;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warnx("%s:%zu: line not understood: '%s'", fpname, line_num, line);
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ferror(fp) )
|
|
||||||
{
|
|
||||||
error_out_errno:
|
|
||||||
warn("%s", fpname);
|
|
||||||
error_out:
|
|
||||||
free(mem);
|
|
||||||
ChangeRulesAmount(rules_at_start);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
free(mem);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare_path(const void* a_ptr, const void* b_ptr)
|
|
||||||
{
|
|
||||||
const char* a = *(const char* const*) a_ptr;
|
|
||||||
const char* b = *(const char* const*) b_ptr;
|
|
||||||
return strcmp(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddManifestPath(const char* path)
|
|
||||||
{
|
|
||||||
if ( manifest_used == manifest_length )
|
|
||||||
{
|
|
||||||
size_t new_length = 2 * manifest_length;
|
|
||||||
if ( new_length == 0 )
|
|
||||||
new_length = 64;
|
|
||||||
size_t new_size = new_length * sizeof(char*);
|
|
||||||
char** new_manifest = (char**) realloc(manifest, new_size);
|
|
||||||
if ( !new_manifest )
|
|
||||||
{
|
|
||||||
warn("malloc");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
manifest = new_manifest;
|
|
||||||
manifest_length = new_length;
|
|
||||||
}
|
|
||||||
char* copy = strdup(path);
|
|
||||||
if ( !copy )
|
|
||||||
{
|
|
||||||
warn("malloc");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
manifest[manifest_used++] = copy;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddManifestFromFile(FILE* fp, const char* fpname)
|
|
||||||
{
|
|
||||||
char* line = NULL;
|
|
||||||
size_t line_size = 0;
|
|
||||||
ssize_t line_len;
|
|
||||||
while ( 0 < (line_len = getline(&line, &line_size, fp)) )
|
|
||||||
{
|
|
||||||
if ( line[line_len-1] == '\n' )
|
|
||||||
line[--line_len] = '\0';
|
|
||||||
if ( !AddManifestPath(line) )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
free(line);
|
|
||||||
if ( ferror(fp) )
|
|
||||||
{
|
|
||||||
warn("%s", fpname);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !AddManifestPath("/") ||
|
|
||||||
!AddManifestPath("/tix") ||
|
|
||||||
!AddManifestPath("/tix/manifest") )
|
|
||||||
return false;
|
|
||||||
char* fpname_copy = strdup(fpname);
|
|
||||||
if ( !fpname_copy )
|
|
||||||
{
|
|
||||||
warn("malloc");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const char* fpname_basename = basename(fpname_copy);
|
|
||||||
char* manifest_path;
|
|
||||||
if ( asprintf(&manifest_path, "/tix/manifest/%s", fpname_basename) < 0 )
|
|
||||||
{
|
|
||||||
free(fpname_copy);
|
|
||||||
warn("malloc");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
free(fpname_copy);
|
|
||||||
if ( !AddManifestPath(manifest_path) )
|
|
||||||
return free(manifest_path), false;
|
|
||||||
free(manifest_path);
|
|
||||||
qsort(manifest, manifest_used, sizeof(char*), compare_path);
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* serialize.c
|
|
||||||
* Import and export binary data structures
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <endian.h>
|
|
||||||
|
|
||||||
#include <sortix/initrd.h>
|
|
||||||
|
|
||||||
#include "serialize.h"
|
|
||||||
|
|
||||||
void import_initrd_superblock(struct initrd_superblock* obj)
|
|
||||||
{
|
|
||||||
obj->fssize = le32toh(obj->fssize);
|
|
||||||
obj->revision = le32toh(obj->revision);
|
|
||||||
obj->inodesize = le32toh(obj->inodesize);
|
|
||||||
obj->inodecount = le32toh(obj->inodecount);
|
|
||||||
obj->inodeoffset = le32toh(obj->inodeoffset);
|
|
||||||
obj->root = le32toh(obj->root);
|
|
||||||
obj->sumalgorithm = le32toh(obj->sumalgorithm);
|
|
||||||
obj->sumsize = le32toh(obj->sumsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void export_initrd_superblock(struct initrd_superblock* obj)
|
|
||||||
{
|
|
||||||
obj->fssize = htole32(obj->fssize);
|
|
||||||
obj->revision = htole32(obj->revision);
|
|
||||||
obj->inodesize = htole32(obj->inodesize);
|
|
||||||
obj->inodecount = htole32(obj->inodecount);
|
|
||||||
obj->inodeoffset = htole32(obj->inodeoffset);
|
|
||||||
obj->root = htole32(obj->root);
|
|
||||||
obj->sumalgorithm = htole32(obj->sumalgorithm);
|
|
||||||
obj->sumsize = htole32(obj->sumsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void import_initrd_inode(struct initrd_inode* obj)
|
|
||||||
{
|
|
||||||
obj->mode = le32toh(obj->mode);
|
|
||||||
obj->uid = le32toh(obj->uid);
|
|
||||||
obj->nlink = le32toh(obj->nlink);
|
|
||||||
obj->ctime = le64toh(obj->ctime);
|
|
||||||
obj->mtime = le64toh(obj->mtime);
|
|
||||||
obj->dataoffset = le32toh(obj->dataoffset);
|
|
||||||
obj->size = le32toh(obj->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void export_initrd_inode(struct initrd_inode* obj)
|
|
||||||
{
|
|
||||||
obj->mode = htole32(obj->mode);
|
|
||||||
obj->uid = htole32(obj->uid);
|
|
||||||
obj->nlink = htole32(obj->nlink);
|
|
||||||
obj->ctime = htole64(obj->ctime);
|
|
||||||
obj->mtime = htole64(obj->mtime);
|
|
||||||
obj->dataoffset = htole32(obj->dataoffset);
|
|
||||||
obj->size = htole32(obj->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void import_initrd_dirent(struct initrd_dirent* obj)
|
|
||||||
{
|
|
||||||
obj->inode = le32toh(obj->inode);
|
|
||||||
obj->reclen = le16toh(obj->reclen);
|
|
||||||
obj->namelen = le16toh(obj->namelen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void export_initrd_dirent(struct initrd_dirent* obj)
|
|
||||||
{
|
|
||||||
obj->inode = htole32(obj->inode);
|
|
||||||
obj->reclen = htole16(obj->reclen);
|
|
||||||
obj->namelen = htole16(obj->namelen);
|
|
||||||
}
|
|
2
nyan/.gitignore
vendored
Normal file
2
nyan/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
nyan
|
||||||
|
*.o
|
33
nyan/Makefile
Normal file
33
nyan/Makefile
Normal 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
181
nyan/nyan.c
Normal 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
859
nyan/nyan.h
Normal 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
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue