Compare commits
62 commits
c6c285a414
...
cef7779f31
Author | SHA1 | Date | |
---|---|---|---|
|
cef7779f31 | ||
|
36a2d0ccc3 | ||
|
a180902247 | ||
|
5633699695 | ||
|
e3af5ae322 | ||
|
63a6509396 | ||
|
9d287d9433 | ||
|
7eb6a5f517 | ||
|
3cb2bf4590 | ||
|
e88c1ca56c | ||
|
035cd9b1a2 | ||
|
189e79d62d | ||
|
08a834d932 | ||
|
504415bc22 | ||
|
7de016539a | ||
|
bc2cf40dab | ||
|
e76b19fd2b | ||
|
5cec2e2f55 | ||
|
4ba880938e | ||
|
005eb40d27 | ||
|
adb08e530d | ||
|
544b52583e | ||
|
8f879f75a3 | ||
|
e5ae1e62e3 | ||
|
6b158f7795 | ||
|
e74efce270 | ||
|
372370f37d | ||
|
8826615b48 | ||
|
1e2a1f155a | ||
|
257b3abe40 | ||
|
4ea8396b5d | ||
|
0871fa71c3 | ||
|
f9a346a6dc | ||
|
ffafa81cd0 | ||
|
0ab8901f8b | ||
|
8f4c3371f5 | ||
|
e9bfbc8d03 | ||
|
456e2ac596 | ||
|
faff59cffc | ||
|
06fcbdb6ae | ||
|
3fe2a32914 | ||
|
a9b15bffe6 | ||
|
35e68109c9 | ||
|
5248220e40 | ||
|
35f4d2f000 | ||
|
9d317c462e | ||
|
250b95616b | ||
|
204576b7a8 | ||
|
8e3e058fd6 | ||
|
bf2d489da1 | ||
|
d3d339b616 | ||
|
83ac75a083 | ||
|
cc182e7912 | ||
|
d75c716891 | ||
|
704ddbe061 | ||
|
96d4615aee | ||
|
12a69346a0 | ||
|
eede0df814 | ||
|
bab61702f3 | ||
|
bf1d15957e | ||
|
ecd5217da8 | ||
|
735dffd029 |
193 changed files with 16498 additions and 896 deletions
72
Makefile
72
Makefile
|
@ -20,13 +20,16 @@ display \
|
||||||
dnsconfig \
|
dnsconfig \
|
||||||
editor \
|
editor \
|
||||||
ext \
|
ext \
|
||||||
|
fat \
|
||||||
games \
|
games \
|
||||||
hostname \
|
hostname \
|
||||||
ifconfig \
|
ifconfig \
|
||||||
init \
|
init \
|
||||||
|
iso9660 \
|
||||||
kblayout \
|
kblayout \
|
||||||
kblayout-compiler \
|
kblayout-compiler \
|
||||||
login \
|
login \
|
||||||
|
nyan \
|
||||||
ping \
|
ping \
|
||||||
regress \
|
regress \
|
||||||
rw \
|
rw \
|
||||||
|
@ -68,10 +71,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
|
||||||
|
@ -226,11 +232,13 @@ sysroot-system: sysroot-fsh sysroot-base-headers
|
||||||
echo 'ID=sortix' && \
|
echo 'ID=sortix' && \
|
||||||
echo 'VERSION_ID="$(VERSION)"' && \
|
echo 'VERSION_ID="$(VERSION)"' && \
|
||||||
echo 'PRETTY_NAME="Sortix $(VERSION)"' && \
|
echo 'PRETTY_NAME="Sortix $(VERSION)"' && \
|
||||||
echo 'SORTIX_ABI=2.0' && \
|
echo 'SORTIX_ABI=2.1' && \
|
||||||
true) > "$(SYSROOT)/etc/sortix-release"
|
true) > "$(SYSROOT)/etc/sortix-release"
|
||||||
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)" && \
|
||||||
|
@ -269,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
|
||||||
|
@ -441,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
|
||||||
|
@ -452,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 +521,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 +597,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
|
||||||
|
|
||||||
|
@ -618,6 +687,7 @@ release-repository: sysroot $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/re
|
||||||
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.tix.tar.xz $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST) && \
|
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.tix.tar.xz $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST) && \
|
||||||
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.version $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST); \
|
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.version $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST); \
|
||||||
done
|
done
|
||||||
|
tix-repository --generation=3 $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST)
|
||||||
|
|
||||||
.PHONY: release-scripts
|
.PHONY: release-scripts
|
||||||
release-scripts: \
|
release-scripts: \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,14 +113,24 @@ 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 repository/system.tix.tar)
|
else
|
||||||
ports=$(ls repository |
|
live_initrd=$(maybe_compressed boot/live.tar)
|
||||||
|
overlay_initrd=$(maybe_compressed boot/overlay.tar)
|
||||||
|
src_initrd=$(maybe_compressed boot/src.tar)
|
||||||
|
system_initrd=$(maybe_compressed repository/system.tix.tar)
|
||||||
|
initrds="$system_initrd $src_initrd $live_initrd $overlay_initrd"
|
||||||
|
fi
|
||||||
|
if $mount; then
|
||||||
|
ports=
|
||||||
|
else
|
||||||
|
ports=$(ls repository |
|
||||||
grep -E '\.tix\.tar\.xz$' |
|
grep -E '\.tix\.tar\.xz$' |
|
||||||
sed -E 's/\.tix\.tar\.xz$//' |
|
sed -E 's/\.tix\.tar\.xz$//' |
|
||||||
(grep -Ev '^system$' || true))
|
(grep -Ev '^system$' || true))
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p boot/grub
|
mkdir -p boot/grub
|
||||||
mkdir -p boot/grub/init
|
mkdir -p boot/grub/init
|
||||||
|
@ -184,6 +196,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'
|
||||||
|
@ -205,6 +223,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
|
||||||
|
@ -305,9 +325,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 --firmware=\$grub_platform \$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
|
||||||
|
@ -332,7 +353,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
|
||||||
|
@ -428,9 +449,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
|
||||||
|
@ -462,6 +485,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
|
||||||
|
@ -473,6 +497,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" {
|
||||||
|
@ -486,6 +511,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
|
||||||
|
@ -525,6 +551,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 dosfstools e2fsprogs grep grub grub-i386-pc grub-i386-efi grub-x86_64-efi libssl mdocml sed signify tar wget xargs xz"
|
||||||
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip irssi libcurl libcurses libssl libstdc++ m4 make nano ntpd patch perl pkg-config python ssh tar texinfo vim wget xz xorriso"
|
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip irssi libcurl libcurses libstdc++ m4 make mtools nano ntpd patch perl pkg-config python ssh texinfo vim xorriso"
|
||||||
sets="basic minimal"
|
sets="basic minimal"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
SOFTWARE_MEANT_FOR_SORTIX=1
|
|
||||||
include ../build-aux/platform.mak
|
include ../build-aux/platform.mak
|
||||||
include ../build-aux/compiler.mak
|
include ../build-aux/compiler.mak
|
||||||
include ../build-aux/version.mak
|
include ../build-aux/version.mak
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2020 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2017, 2020, 2024 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 @@
|
||||||
* Compute and check cryptographic hashes.
|
* Compute and check cryptographic hashes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -97,8 +99,20 @@ static struct hash* hashes[] =
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct checklist
|
||||||
|
{
|
||||||
|
const char* file;
|
||||||
|
uint8_t checksum[DIGEST_MAX_LENGTH];
|
||||||
|
bool initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct checklist** cache = NULL;
|
||||||
|
static size_t cache_used = 0;
|
||||||
|
static size_t cache_length = 0;
|
||||||
|
static struct timespec cache_time;
|
||||||
static struct hash* hash = NULL;
|
static struct hash* hash = NULL;
|
||||||
static const char* algorithm = NULL;
|
static const char* algorithm = NULL;
|
||||||
|
static const char* cache_path = NULL;
|
||||||
static const char* checklist = NULL;
|
static const char* checklist = NULL;
|
||||||
static bool check = false;
|
static bool check = false;
|
||||||
static bool ignore_missing = false;
|
static bool ignore_missing = false;
|
||||||
|
@ -116,19 +130,182 @@ int debase(char c)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printhex(const uint8_t* buffer, size_t size)
|
static void fprinthex(FILE* fp, const uint8_t* buffer, size_t size)
|
||||||
{
|
{
|
||||||
for ( size_t i = 0; i < size; i++ )
|
for ( size_t i = 0; i < size; i++ )
|
||||||
{
|
{
|
||||||
putchar(hexchars[buffer[i] >> 4]);
|
fputc(hexchars[buffer[i] >> 4], fp);
|
||||||
putchar(hexchars[buffer[i] & 0xF]);
|
fputc(hexchars[buffer[i] & 0xF], fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int compare_checklist_file(const void* a_ptr, const void* b_ptr)
|
||||||
|
{
|
||||||
|
struct checklist* a = *(struct checklist**) a_ptr;
|
||||||
|
struct checklist* b = *(struct checklist**) b_ptr;
|
||||||
|
return strcmp(a->file, b->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int search_checklist_file(const void* file_ptr, const void* elem_ptr)
|
||||||
|
{
|
||||||
|
const char* file = (const char*) file_ptr;
|
||||||
|
struct checklist* elem = *(struct checklist**) elem_ptr;
|
||||||
|
return strcmp(file, elem->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct checklist* checklist_lookup(struct checklist** sorted,
|
||||||
|
size_t used,
|
||||||
|
const char* file)
|
||||||
|
{
|
||||||
|
struct checklist** entry_ptr =
|
||||||
|
bsearch(file, sorted, used,
|
||||||
|
sizeof(struct checksum*), search_checklist_file);
|
||||||
|
return entry_ptr ? *entry_ptr : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checklist_add(struct checklist*** checklist_ptr,
|
||||||
|
size_t* used_ptr,
|
||||||
|
size_t* length_ptr,
|
||||||
|
uint8_t digest[DIGEST_MAX_LENGTH],
|
||||||
|
const char* file)
|
||||||
|
{
|
||||||
|
struct checklist* entry = calloc(1, sizeof(struct checklist));
|
||||||
|
if ( !entry || !(entry->file = strdup(file)) )
|
||||||
|
err(1, "malloc");
|
||||||
|
memcpy(entry->checksum, digest, hash->digest_size);
|
||||||
|
if ( *used_ptr == *length_ptr )
|
||||||
|
{
|
||||||
|
struct checklist** new_checklist =
|
||||||
|
reallocarray(*checklist_ptr, *length_ptr,
|
||||||
|
2 * sizeof(struct checklist*));
|
||||||
|
if ( !new_checklist )
|
||||||
|
err(1, "malloc");
|
||||||
|
*checklist_ptr = new_checklist;
|
||||||
|
*length_ptr *= 2;
|
||||||
|
}
|
||||||
|
(*checklist_ptr)[(*used_ptr)++] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checklist_parse(char* line,
|
||||||
|
size_t line_length,
|
||||||
|
struct checklist* entry,
|
||||||
|
const char* path,
|
||||||
|
off_t line_number)
|
||||||
|
{
|
||||||
|
if ( line[line_length - 1] != '\n' )
|
||||||
|
errx(1, "%s:%ji: Line was not terminated with a newline",
|
||||||
|
path, (intmax_t) line_number);
|
||||||
|
line[--line_length] = '\0';
|
||||||
|
if ( (size_t) line_length < 2 * hash->digest_size )
|
||||||
|
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
||||||
|
path, (intmax_t) line_number, hash->name);
|
||||||
|
for ( size_t i = 0; i < hash->digest_size; i++ )
|
||||||
|
{
|
||||||
|
int higher = debase(line[i*2 + 0]);
|
||||||
|
int lower = debase(line[i*2 + 1]);
|
||||||
|
if ( higher == -1 || lower == -1 )
|
||||||
|
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
||||||
|
path, (intmax_t) line_number, hash->name);
|
||||||
|
entry->checksum[i] = higher << 4 | lower;
|
||||||
|
}
|
||||||
|
if ( line[2 * hash->digest_size + 0] != ' ' ||
|
||||||
|
line[2 * hash->digest_size + 1] != ' ' ||
|
||||||
|
line[2 * hash->digest_size + 2] == '\0' )
|
||||||
|
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
||||||
|
path, (intmax_t) line_number, hash->name);
|
||||||
|
entry->file = line + 2 * hash->digest_size + 2;
|
||||||
|
if ( !strcmp(path, "-") && !strcmp(entry->file, "-") )
|
||||||
|
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
||||||
|
path, (intmax_t) line_number, hash->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checklist_read(struct checklist*** ptr,
|
||||||
|
size_t* used_ptr,
|
||||||
|
size_t* length_ptr,
|
||||||
|
struct timespec* time_ptr,
|
||||||
|
const char* path,
|
||||||
|
bool allow_missing)
|
||||||
|
{
|
||||||
|
if ( !(*ptr = malloc(sizeof(struct checklist*) * 4)) )
|
||||||
|
err(1, "malloc");
|
||||||
|
*used_ptr = 0;
|
||||||
|
*length_ptr = 4;
|
||||||
|
FILE* fp = fopen(path, "r");
|
||||||
|
if ( !fp )
|
||||||
|
{
|
||||||
|
if ( allow_missing )
|
||||||
|
return;
|
||||||
|
err(1, "malloc");
|
||||||
|
}
|
||||||
|
struct stat st;
|
||||||
|
fstat(fileno(fp), &st);
|
||||||
|
*time_ptr = st.st_mtim;
|
||||||
|
char* line = NULL;
|
||||||
|
size_t line_size = 0;
|
||||||
|
ssize_t line_length;
|
||||||
|
off_t line_number = 0;
|
||||||
|
struct checklist input;
|
||||||
|
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
|
||||||
|
{
|
||||||
|
line_number++;
|
||||||
|
checklist_parse(line, (size_t) line_length, &input, path, line_number);
|
||||||
|
checklist_add(ptr, used_ptr, length_ptr, input.checksum, input.file);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
if ( ferror(fp) )
|
||||||
|
err(1, "%s", path);
|
||||||
|
fclose(fp);
|
||||||
|
qsort(*ptr, *used_ptr, sizeof(struct checklist*), compare_checklist_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checklist_write(struct checklist** checklist,
|
||||||
|
size_t used,
|
||||||
|
const char* path)
|
||||||
|
{
|
||||||
|
char* out_path;
|
||||||
|
if ( asprintf(&out_path, "%s.XXXXXX", path) < 0 )
|
||||||
|
err(1, "malloc");
|
||||||
|
int out_fd = mkstemp(out_path);
|
||||||
|
if ( out_fd < 0 )
|
||||||
|
err(1, "mkstemp: %s.XXXXXX", path);
|
||||||
|
FILE* out = fdopen(out_fd, "w");
|
||||||
|
if ( !out )
|
||||||
|
{
|
||||||
|
unlink(out_path);
|
||||||
|
err(1, "fdopen");
|
||||||
|
}
|
||||||
|
for ( size_t i = 0; i < used; i++ )
|
||||||
|
{
|
||||||
|
fprinthex(out, checklist[i]->checksum, hash->digest_size);
|
||||||
|
fprintf(out, " %s\n", checklist[i]->file);
|
||||||
|
}
|
||||||
|
if ( ferror(out) || fclose(out) == EOF )
|
||||||
|
{
|
||||||
|
unlink(out_path);
|
||||||
|
err(1, "%s", out_path);
|
||||||
|
}
|
||||||
|
if ( rename(out_path, path) < 0 )
|
||||||
|
err(1, "rename: %s -> %s", out_path, path);
|
||||||
|
free(out_path);
|
||||||
|
}
|
||||||
|
|
||||||
static int digest_fd(uint8_t digest[DIGEST_MAX_LENGTH],
|
static int digest_fd(uint8_t digest[DIGEST_MAX_LENGTH],
|
||||||
int fd,
|
int fd,
|
||||||
const char* path)
|
const char* path)
|
||||||
{
|
{
|
||||||
|
struct checklist* entry = NULL;
|
||||||
|
if ( cache && (entry = checklist_lookup(cache, cache_used, path)) )
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
fstat(fd, &st);
|
||||||
|
if ( st.st_mtim.tv_sec < cache_time.tv_sec ||
|
||||||
|
(st.st_mtim.tv_sec == cache_time.tv_sec &&
|
||||||
|
st.st_mtim.tv_nsec <= cache_time.tv_nsec) )
|
||||||
|
{
|
||||||
|
memcpy(digest, entry->checksum, hash->digest_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
union ctx ctx;
|
union ctx ctx;
|
||||||
hash->init(&ctx);
|
hash->init(&ctx);
|
||||||
ssize_t amount;
|
ssize_t amount;
|
||||||
|
@ -140,6 +317,22 @@ static int digest_fd(uint8_t digest[DIGEST_MAX_LENGTH],
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
hash->final(digest, &ctx);
|
hash->final(digest, &ctx);
|
||||||
|
if ( cache )
|
||||||
|
{
|
||||||
|
if ( entry )
|
||||||
|
memcpy(entry->checksum, digest, hash->digest_size);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
checklist_add(&cache, &cache_used, &cache_length, digest, path);
|
||||||
|
size_t i = cache_used - 1;
|
||||||
|
while ( i && 0 < strcmp(cache[i - 1]->file, cache[i]->file) )
|
||||||
|
{
|
||||||
|
struct checklist* t = cache[i - 1];
|
||||||
|
cache[i - 1] = cache[i];
|
||||||
|
cache[i--] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,27 +368,6 @@ static int verify_path(uint8_t checksum[], const char* path)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct checklist
|
|
||||||
{
|
|
||||||
const char* file;
|
|
||||||
uint8_t checksum[DIGEST_MAX_LENGTH];
|
|
||||||
bool initialized;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int compare_checklist_file(const void* a_ptr, const void* b_ptr)
|
|
||||||
{
|
|
||||||
struct checklist* a = *(struct checklist**) a_ptr;
|
|
||||||
struct checklist* b = *(struct checklist**) b_ptr;
|
|
||||||
return strcmp(a->file, b->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int search_checklist_file(const void* file_ptr, const void* elem_ptr)
|
|
||||||
{
|
|
||||||
const char* file = (const char*) file_ptr;
|
|
||||||
struct checklist* elem = *(struct checklist**) elem_ptr;
|
|
||||||
return strcmp(file, elem->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int checklist_fp(FILE* fp,
|
static int checklist_fp(FILE* fp,
|
||||||
const char* path,
|
const char* path,
|
||||||
size_t files_count,
|
size_t files_count,
|
||||||
|
@ -217,7 +389,6 @@ static int checklist_fp(FILE* fp,
|
||||||
qsort(checklist_sorted, files_count, sizeof(struct checklist*),
|
qsort(checklist_sorted, files_count, sizeof(struct checklist*),
|
||||||
compare_checklist_file);
|
compare_checklist_file);
|
||||||
}
|
}
|
||||||
uint8_t checksum[DIGEST_MAX_LENGTH];
|
|
||||||
bool any = false;
|
bool any = false;
|
||||||
char* line = NULL;
|
char* line = NULL;
|
||||||
size_t line_size = 0;
|
size_t line_size = 0;
|
||||||
|
@ -225,52 +396,29 @@ static int checklist_fp(FILE* fp,
|
||||||
off_t line_number = 0;
|
off_t line_number = 0;
|
||||||
size_t read_failures = 0;
|
size_t read_failures = 0;
|
||||||
size_t check_failures = 0;
|
size_t check_failures = 0;
|
||||||
|
struct checklist input;
|
||||||
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
|
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
|
||||||
{
|
{
|
||||||
line_number++;
|
line_number++;
|
||||||
if ( line[line_length - 1] != '\n' )
|
checklist_parse(line, (size_t) line_length, &input, path, line_number);
|
||||||
errx(1, "%s:%ji: Line was not terminated with a newline",
|
|
||||||
path, (intmax_t) line_number);
|
|
||||||
line[--line_length] = '\0';
|
|
||||||
if ( (size_t) line_length < 2 * hash->digest_size )
|
|
||||||
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
|
||||||
path, (intmax_t) line_number, hash->name);
|
|
||||||
for ( size_t i = 0; i < hash->digest_size; i++ )
|
|
||||||
{
|
|
||||||
int higher = debase(line[i*2 + 0]);
|
|
||||||
int lower = debase(line[i*2 + 1]);
|
|
||||||
if ( higher == -1 || lower == -1 )
|
|
||||||
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
|
||||||
path, (intmax_t) line_number, hash->name);
|
|
||||||
checksum[i] = higher << 4 | lower;
|
|
||||||
}
|
|
||||||
if ( line[2 * hash->digest_size + 0] != ' ' ||
|
|
||||||
line[2 * hash->digest_size + 1] != ' ' ||
|
|
||||||
line[2 * hash->digest_size + 2] == '\0' )
|
|
||||||
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
|
||||||
path, (intmax_t) line_number, hash->name);
|
|
||||||
const char* file = line + 2 * hash->digest_size + 2;
|
|
||||||
if ( !strcmp(path, "-") && !strcmp(file, "-") )
|
|
||||||
errx(1, "%s:%ji: Improperly formatted %s checksum line",
|
|
||||||
path, (intmax_t) line_number, hash->name);
|
|
||||||
if ( files )
|
if ( files )
|
||||||
{
|
{
|
||||||
struct checklist** entry_ptr =
|
struct checklist** entry_ptr =
|
||||||
bsearch(file, checklist_sorted, files_count,
|
bsearch(input.file, checklist_sorted, files_count,
|
||||||
sizeof(struct checksum*), search_checklist_file);
|
sizeof(struct checksum*), search_checklist_file);
|
||||||
if ( entry_ptr )
|
if ( entry_ptr )
|
||||||
{
|
{
|
||||||
struct checklist* entry = *entry_ptr;
|
struct checklist* entry = *entry_ptr;
|
||||||
if ( entry->initialized )
|
if ( entry->initialized )
|
||||||
errx(1, "%s:%ji: Duplicate hash found for: %s", path,
|
errx(1, "%s:%ji: Duplicate hash found for: %s", path,
|
||||||
(intmax_t) line_number, file);
|
(intmax_t) line_number, input.file);
|
||||||
memcpy(entry->checksum, checksum, DIGEST_MAX_LENGTH);
|
memcpy(entry->checksum, input.checksum, DIGEST_MAX_LENGTH);
|
||||||
entry->initialized = true;
|
entry->initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int status = verify_path(checksum, file);
|
int status = verify_path(input.checksum, input.file);
|
||||||
if ( status == 1 )
|
if ( status == 1 )
|
||||||
read_failures++;
|
read_failures++;
|
||||||
else if ( status == 2 )
|
else if ( status == 2 )
|
||||||
|
@ -296,7 +444,7 @@ static int checklist_fp(FILE* fp,
|
||||||
else if ( status == 2 )
|
else if ( status == 2 )
|
||||||
check_failures++;
|
check_failures++;
|
||||||
}
|
}
|
||||||
explicit_bzero(checksum, sizeof(checksum));
|
explicit_bzero(input.checksum, sizeof(input.checksum));
|
||||||
free(checklist);
|
free(checklist);
|
||||||
free(checklist_sorted);
|
free(checklist_sorted);
|
||||||
if ( read_failures )
|
if ( read_failures )
|
||||||
|
@ -391,6 +539,15 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
else if ( !strncmp(arg, "--algorithm=", strlen("--algorithm=")) )
|
else if ( !strncmp(arg, "--algorithm=", strlen("--algorithm=")) )
|
||||||
algorithm = arg + strlen("--algorithm=");
|
algorithm = arg + strlen("--algorithm=");
|
||||||
|
else if ( !strcmp(arg, "--cache") )
|
||||||
|
{
|
||||||
|
if ( i + 1 == argc )
|
||||||
|
errx(1, "option '--cache' requires an argument");
|
||||||
|
cache_path = argv[i+1];
|
||||||
|
argv[++i] = NULL;
|
||||||
|
}
|
||||||
|
else if ( !strncmp(arg, "--cache=", strlen("--cache=")) )
|
||||||
|
cache_path = arg + strlen("--cache=");
|
||||||
else if ( !strcmp(arg, "--check") )
|
else if ( !strcmp(arg, "--check") )
|
||||||
check = true;
|
check = true;
|
||||||
else if ( !strcmp(arg, "--checklist") )
|
else if ( !strcmp(arg, "--checklist") )
|
||||||
|
@ -439,6 +596,14 @@ int main(int argc, char* argv[])
|
||||||
else
|
else
|
||||||
errx(1, "No hash algorithm was specified with -a");
|
errx(1, "No hash algorithm was specified with -a");
|
||||||
|
|
||||||
|
if ( cache_path )
|
||||||
|
{
|
||||||
|
if ( !strcmp(cache_path, "-") )
|
||||||
|
errx(1, "cache cannot be the standard input");
|
||||||
|
checklist_read(&cache, &cache_used, &cache_length, &cache_time,
|
||||||
|
cache_path, true);
|
||||||
|
}
|
||||||
|
|
||||||
bool read_failures = false;
|
bool read_failures = false;
|
||||||
bool check_failures = false;
|
bool check_failures = false;
|
||||||
|
|
||||||
|
@ -464,10 +629,10 @@ int main(int argc, char* argv[])
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t digest[DIGEST_MAX_LENGTH];
|
uint8_t digest[DIGEST_MAX_LENGTH];
|
||||||
int result = digest_fd(digest, 0, "-");
|
int result = digest_path(digest, "-");
|
||||||
if ( result == 0 )
|
if ( result == 0 )
|
||||||
{
|
{
|
||||||
printhex(digest, hash->digest_size);
|
fprinthex(stdout, digest, hash->digest_size);
|
||||||
puts(" -");
|
puts(" -");
|
||||||
explicit_bzero(digest, sizeof(digest));
|
explicit_bzero(digest, sizeof(digest));
|
||||||
}
|
}
|
||||||
|
@ -491,7 +656,7 @@ int main(int argc, char* argv[])
|
||||||
int result = digest_path(digest, argv[i]);
|
int result = digest_path(digest, argv[i]);
|
||||||
if ( result == 0 )
|
if ( result == 0 )
|
||||||
{
|
{
|
||||||
printhex(digest, hash->digest_size);
|
fprinthex(stdout, digest, hash->digest_size);
|
||||||
printf(" %s\n", argv[i]);
|
printf(" %s\n", argv[i]);
|
||||||
explicit_bzero(digest, sizeof(digest));
|
explicit_bzero(digest, sizeof(digest));
|
||||||
}
|
}
|
||||||
|
@ -502,5 +667,9 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
if ( ferror(stdout) || fflush(stdout) == EOF )
|
if ( ferror(stdout) || fflush(stdout) == EOF )
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if ( cache_path)
|
||||||
|
checklist_write(cache, cache_used, cache_path);
|
||||||
|
|
||||||
return read_failures ? 1 : check_failures ? 2 : 0;
|
return read_failures ? 1 : check_failures ? 2 : 0;
|
||||||
}
|
}
|
||||||
|
|
14
checksum/compat/sha2.h
Normal file
14
checksum/compat/sha2.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef SHA2_H
|
||||||
|
#define SHA2_H
|
||||||
|
#if __has_include_next(<sha2.h>)
|
||||||
|
#include_next <sha2.h>
|
||||||
|
#else
|
||||||
|
#include "../../libc/include/sha2.h"
|
||||||
|
#include "../../libc/sha2/sha224.c"
|
||||||
|
#include "../../libc/sha2/sha256.c"
|
||||||
|
#include "../../libc/sha2/sha384.c"
|
||||||
|
#include "../../libc/sha2/sha512_256.c"
|
||||||
|
#include "../../libc/sha2/sha512.c"
|
||||||
|
#define timingsafe_memcmp memcmp
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -49,6 +49,7 @@
|
||||||
#include <mount/blockdevice.h>
|
#include <mount/blockdevice.h>
|
||||||
#include <mount/devices.h>
|
#include <mount/devices.h>
|
||||||
#include <mount/ext2.h>
|
#include <mount/ext2.h>
|
||||||
|
#include <mount/fat.h>
|
||||||
#include <mount/filesystem.h>
|
#include <mount/filesystem.h>
|
||||||
#include <mount/gpt.h>
|
#include <mount/gpt.h>
|
||||||
#include <mount/harddisk.h>
|
#include <mount/harddisk.h>
|
||||||
|
@ -1496,7 +1497,12 @@ static char* display_area_format(void* ctx, size_t row, size_t column)
|
||||||
{
|
{
|
||||||
case FILESYSTEM_ERROR_NONE: return strdup("(no error)");
|
case FILESYSTEM_ERROR_NONE: return strdup("(no error)");
|
||||||
case FILESYSTEM_ERROR_ABSENT: return strdup("none");
|
case FILESYSTEM_ERROR_ABSENT: return strdup("none");
|
||||||
case FILESYSTEM_ERROR_UNRECOGNIZED: return strdup("unrecognized");
|
case FILESYSTEM_ERROR_UNRECOGNIZED:
|
||||||
|
{
|
||||||
|
char uuid[UUID_STRING_LENGTH + 1];
|
||||||
|
uuid_to_string(area->p->gpt_type_guid, uuid);
|
||||||
|
return strdup(uuid);
|
||||||
|
}
|
||||||
case FILESYSTEM_ERROR_ERRNO: return strdup("(error)");
|
case FILESYSTEM_ERROR_ERRNO: return strdup("(error)");
|
||||||
}
|
}
|
||||||
return strdup("(unknown error)");
|
return strdup("(unknown error)");
|
||||||
|
@ -1771,9 +1777,9 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
bool is_gpt = current_pt_type == PARTITION_TABLE_TYPE_GPT;
|
bool is_gpt = current_pt_type == PARTITION_TABLE_TYPE_GPT;
|
||||||
const char* question = "Format a filesystem? (no/ext2)";
|
const char* question = "Format a filesystem? (no/ext2)";
|
||||||
if ( is_mbr )
|
if ( is_mbr )
|
||||||
question = "Format a filesystem? (no/ext2/extended)";
|
question = "Format a filesystem? (no/ext2/extended/fat)";
|
||||||
else if ( is_gpt )
|
else if ( is_gpt )
|
||||||
question = "Format a filesystem? (no/ext2/biosboot)";
|
question = "Format a filesystem? (no/ext2/biosboot/efi/fat)";
|
||||||
if ( 5 <= argc )
|
if ( 5 <= argc )
|
||||||
strlcpy(fstype, argv[4], sizeof(fstype));
|
strlcpy(fstype, argv[4], sizeof(fstype));
|
||||||
else
|
else
|
||||||
|
@ -1781,7 +1787,9 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
if ( strcmp(fstype, "no") != 0 &&
|
if ( strcmp(fstype, "no") != 0 &&
|
||||||
strcmp(fstype, "ext2") != 0 &&
|
strcmp(fstype, "ext2") != 0 &&
|
||||||
(!is_mbr || strcmp(fstype, "extended") != 0) &&
|
(!is_mbr || strcmp(fstype, "extended") != 0) &&
|
||||||
(!is_gpt || strcmp(fstype, "biosboot") != 0) )
|
(!is_gpt || strcmp(fstype, "biosboot") != 0) &&
|
||||||
|
(!is_gpt || strcmp(fstype, "efi") != 0) &&
|
||||||
|
strcmp(fstype, "fat") != 0 )
|
||||||
{
|
{
|
||||||
command_errorx("%s: %s: Invalid filesystem choice: %s",
|
command_errorx("%s: %s: Invalid filesystem choice: %s",
|
||||||
argv[0], device_name(current_hd->path), fstype);
|
argv[0], device_name(current_hd->path), fstype);
|
||||||
|
@ -1801,14 +1809,18 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
char mountpoint[256] = "";
|
char mountpoint[256] = "";
|
||||||
bool mountable = !strcmp(fstype, "ext2");
|
// TODO: Get this information from libmount.
|
||||||
|
bool mountable = !strcmp(fstype, "ext2") ||
|
||||||
|
!strcmp(fstype, "fat") ||
|
||||||
|
!strcmp(fstype, "efi");
|
||||||
while ( mountable )
|
while ( mountable )
|
||||||
{
|
{
|
||||||
|
const char* def = !strcmp(fstype, "efi") ? "/boot/efi" : "no";
|
||||||
if ( 6 <= argc )
|
if ( 6 <= argc )
|
||||||
strlcpy(mountpoint, argv[5], sizeof(mountpoint));
|
strlcpy(mountpoint, argv[5], sizeof(mountpoint));
|
||||||
else
|
else
|
||||||
prompt(mountpoint, sizeof(mountpoint),
|
prompt(mountpoint, sizeof(mountpoint),
|
||||||
"Where to mount partition? (mountpoint or 'no')", "no");
|
"Where to mount partition? (mountpoint or 'no')", def);
|
||||||
if ( !strcmp(mountpoint, "no") )
|
if ( !strcmp(mountpoint, "no") )
|
||||||
{
|
{
|
||||||
mountpoint[0] = '\0';
|
mountpoint[0] = '\0';
|
||||||
|
@ -2003,6 +2015,10 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
const char* type_uuid_str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
const char* type_uuid_str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
||||||
if ( !strcmp(fstype, "biosboot") )
|
if ( !strcmp(fstype, "biosboot") )
|
||||||
type_uuid_str = BIOSBOOT_GPT_TYPE_UUID;
|
type_uuid_str = BIOSBOOT_GPT_TYPE_UUID;
|
||||||
|
else if ( !strcmp(fstype, "efi") )
|
||||||
|
type_uuid_str = ESP_GPT_TYPE_UUID;
|
||||||
|
else if ( !strcmp(fstype, "fat") )
|
||||||
|
type_uuid_str = BDP_GPT_TYPE_UUID;
|
||||||
uuid_from_string(p.partition_type_guid, type_uuid_str);
|
uuid_from_string(p.partition_type_guid, type_uuid_str);
|
||||||
arc4random_buf(p.unique_partition_guid, sizeof(p.unique_partition_guid));
|
arc4random_buf(p.unique_partition_guid, sizeof(p.unique_partition_guid));
|
||||||
off_t pstart = hole->start + start;
|
off_t pstart = hole->start + start;
|
||||||
|
@ -2162,6 +2178,68 @@ static void on_mkpart(size_t argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( !strcmp(fstype, "efi") || !strcmp(fstype, "fat") )
|
||||||
|
{
|
||||||
|
printf("(Formatting %s as %s...)\n", device_name(p->path), fstype);
|
||||||
|
// TODO: Zero superblock?
|
||||||
|
// TODO: Run this in its own foreground process group so ^C works.
|
||||||
|
pid_t child_pid = fork();
|
||||||
|
if ( child_pid < 0 )
|
||||||
|
{
|
||||||
|
command_error("%s: fork", argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char* mkfs_argv[] =
|
||||||
|
{
|
||||||
|
"mkfs.fat",
|
||||||
|
"-F", // TODO: Force FAT32 until FAT12/16 root dir writing is added.
|
||||||
|
"32",
|
||||||
|
p->path,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
if ( child_pid == 0 )
|
||||||
|
{
|
||||||
|
execvp(mkfs_argv[0], (char* const*) mkfs_argv);
|
||||||
|
warn("%s", mkfs_argv[0]);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
int status;
|
||||||
|
waitpid(child_pid, &status, 0);
|
||||||
|
if ( WIFEXITED(status) && WEXITSTATUS(status) == 127 )
|
||||||
|
{
|
||||||
|
command_errorx("%s: Failed to format filesystem (%s is not installed)",
|
||||||
|
argv[0], mkfs_argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( WIFEXITED(status) && WEXITSTATUS(status) != 0 )
|
||||||
|
{
|
||||||
|
command_errorx("%s: Failed to format filesystem", argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( WIFSIGNALED(status) )
|
||||||
|
{
|
||||||
|
command_errorx("%s: Failed to format filesystem (%s)",
|
||||||
|
argv[0], strsignal(WTERMSIG(status)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("(Formatted %s as %s)\n", device_name(p->path), fstype);
|
||||||
|
scan_partition(p);
|
||||||
|
if ( !p->bdev.fs /* TODO: || !(p->bdev.fs->flags & FILESYSTEM_FLAG_UUID) */ )
|
||||||
|
{
|
||||||
|
command_errorx("%s: %s: Failed to scan expected %s filesystem",
|
||||||
|
argv[0], device_name(p->path), fstype);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( mountpoint[0] )
|
||||||
|
{
|
||||||
|
if ( !add_blockdevice_to_fstab(&p->bdev, mountpoint) )
|
||||||
|
{
|
||||||
|
command_error("%s: %s: Failed to add partition", argv[0], fstab_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_mktable(size_t argc, char** argv)
|
static void on_mktable(size_t argc, char** argv)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016, 2023, 2024 Jonas 'Sortie' Termansen.
|
||||||
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
@ -149,15 +149,15 @@ CONNECTION_MESSAGE_HANDLER_NO_AUX(shutdown)
|
||||||
{
|
{
|
||||||
(void) connection;
|
(void) connection;
|
||||||
if ( msg->code == 0 )
|
if ( msg->code == 0 )
|
||||||
exit(0);
|
display_exit(server->display, 0);
|
||||||
else if ( msg->code == 1 )
|
else if ( msg->code == 1 )
|
||||||
exit(1);
|
display_exit(server->display, 1);
|
||||||
else if ( msg->code == 2 )
|
else if ( msg->code == 2 )
|
||||||
exit(2);
|
display_exit(server->display, 2);
|
||||||
else if ( msg->code == 3 )
|
else if ( msg->code == 3 )
|
||||||
exit(3);
|
display_exit(server->display, 3);
|
||||||
else
|
else
|
||||||
exit(0);
|
display_exit(server->display, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_MESSAGE_HANDLER_NO_AUX(create_window)
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(create_window)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016, 2024 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
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include <display-protocol.h>
|
#include <display-protocol.h>
|
||||||
|
|
||||||
|
#include "display.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016, 2018, 2022, 2023 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014-2016, 2018, 2022-2024 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
|
||||||
|
@ -23,6 +23,8 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -47,6 +49,43 @@ void display_initialize(struct display* display)
|
||||||
{
|
{
|
||||||
memset(display, 0, sizeof(*display));
|
memset(display, 0, sizeof(*display));
|
||||||
display->redraw = true;
|
display->redraw = true;
|
||||||
|
display->running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_init_exit_plan(void)
|
||||||
|
{
|
||||||
|
FILE* fp = popen("/sbin/service default exit-code", "r");
|
||||||
|
if ( !fp )
|
||||||
|
return -1;
|
||||||
|
int result = -1;
|
||||||
|
char buffer[sizeof(int) * 3];
|
||||||
|
if ( fgets(buffer, sizeof(buffer), fp) && buffer[0] )
|
||||||
|
result = atoi(buffer);
|
||||||
|
pclose(fp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_exit(struct display* display, int exit_code)
|
||||||
|
{
|
||||||
|
display->running = false;
|
||||||
|
display->exit_code = exit_code;
|
||||||
|
int plan = exit_code == -1 ? get_init_exit_plan() : exit_code;
|
||||||
|
display->announcement = "Exiting...";
|
||||||
|
if ( plan == 0 )
|
||||||
|
display->announcement = "Powering off...";
|
||||||
|
else if ( plan == 1 )
|
||||||
|
display->announcement = "Rebooting...";
|
||||||
|
else if ( plan == 2 )
|
||||||
|
display->announcement = "Halting...";
|
||||||
|
else if ( plan == 3 )
|
||||||
|
display->announcement = "Reinitializing operating system...";
|
||||||
|
else if ( getenv("LOGIN_PID") ) // TODO: display -l?
|
||||||
|
{
|
||||||
|
intmax_t login_pid = strtoimax(getenv("LOGIN_PID"), NULL, 10);
|
||||||
|
if ( login_pid == getppid() )
|
||||||
|
display->announcement = "Logging out...";
|
||||||
|
}
|
||||||
|
display->redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assert_is_well_formed_display_list(struct display* display)
|
void assert_is_well_formed_display_list(struct display* display)
|
||||||
|
@ -281,6 +320,31 @@ static void wallpaper(struct framebuffer fb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void display_render_exit(struct display* display, struct framebuffer fb)
|
||||||
|
{
|
||||||
|
for ( int yoff = -1; yoff <= 1; yoff++ )
|
||||||
|
{
|
||||||
|
for ( int xoff = -1; xoff <= 1; xoff++ )
|
||||||
|
{
|
||||||
|
struct framebuffer msgfb = fb;
|
||||||
|
int y = (fb.yres - FONT_HEIGHT) / 2 + yoff;
|
||||||
|
msgfb = framebuffer_cut_top_y(msgfb, y);
|
||||||
|
int w = strlen(display->announcement) * (FONT_WIDTH+1);
|
||||||
|
int x = (fb.xres - w) / 2 + xoff;
|
||||||
|
msgfb = framebuffer_cut_left_x(msgfb, x);
|
||||||
|
render_text(msgfb, display->announcement, make_color_a(0, 0, 0, 64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct framebuffer msgfb = fb;
|
||||||
|
int y = (fb.yres - FONT_HEIGHT) / 2;
|
||||||
|
msgfb = framebuffer_cut_top_y(msgfb, y);
|
||||||
|
int w = strlen(display->announcement) * (FONT_WIDTH+1);
|
||||||
|
int x = (fb.xres - w) / 2;
|
||||||
|
msgfb = framebuffer_cut_left_x(msgfb, x);
|
||||||
|
render_text(msgfb, display->announcement, make_color(255, 255, 255));
|
||||||
|
}
|
||||||
|
|
||||||
void display_composit(struct display* display, struct framebuffer fb)
|
void display_composit(struct display* display, struct framebuffer fb)
|
||||||
{
|
{
|
||||||
struct damage_rect damage_rect = display->damage_rect;
|
struct damage_rect damage_rect = display->damage_rect;
|
||||||
|
@ -301,6 +365,12 @@ void display_composit(struct display* display, struct framebuffer fb)
|
||||||
|
|
||||||
framebuffer_copy_to_framebuffer(fb, display->wallpaper);
|
framebuffer_copy_to_framebuffer(fb, display->wallpaper);
|
||||||
|
|
||||||
|
if ( display->announcement )
|
||||||
|
{
|
||||||
|
display_render_exit(display, fb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for ( struct window* window = display->bottom_window;
|
for ( struct window* window = display->bottom_window;
|
||||||
window;
|
window;
|
||||||
window = window->above_window )
|
window = window->above_window )
|
||||||
|
@ -482,11 +552,15 @@ void display_keyboard_event(struct display* display, uint32_t codepoint)
|
||||||
case KBKEY_RSUPER: display->key_rsuper = kbkey > 0; break;
|
case KBKEY_RSUPER: display->key_rsuper = kbkey > 0; break;
|
||||||
}
|
}
|
||||||
if ( display->key_lctrl && display->key_lalt && kbkey == KBKEY_DELETE )
|
if ( display->key_lctrl && display->key_lalt && kbkey == KBKEY_DELETE )
|
||||||
exit(0);
|
display_exit(display, -1);
|
||||||
if ( display->key_lctrl && display->key_lalt && kbkey == KBKEY_T )
|
if ( display->key_lctrl && display->key_lalt && kbkey == KBKEY_T )
|
||||||
{
|
{
|
||||||
if ( !fork() )
|
if ( !fork() )
|
||||||
{
|
{
|
||||||
|
sigset_t sigterm;
|
||||||
|
sigemptyset(&sigterm);
|
||||||
|
sigaddset(&sigterm, SIGTERM);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &sigterm, NULL);
|
||||||
execlp("terminal", "terminal", (char*) NULL);
|
execlp("terminal", "terminal", (char*) NULL);
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016, 2017, 2022, 2023 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014-2017, 2022-2024 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
|
||||||
|
@ -119,5 +119,5 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
server_mainloop(&server);
|
server_mainloop(&server);
|
||||||
|
|
||||||
return 0;
|
return display.exit_code;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016, 2022, 2023, 2024 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
|
||||||
|
@ -68,14 +68,19 @@ struct display
|
||||||
bool key_rsuper;
|
bool key_rsuper;
|
||||||
bool codepoint_discard;
|
bool codepoint_discard;
|
||||||
bool redraw;
|
bool redraw;
|
||||||
|
bool running;
|
||||||
int pointer_x;
|
int pointer_x;
|
||||||
int pointer_y;
|
int pointer_y;
|
||||||
enum mouse_state mouse_state;
|
enum mouse_state mouse_state;
|
||||||
size_t mouse_byte_count;
|
size_t mouse_byte_count;
|
||||||
uint8_t mouse_bytes[MOUSE_PACKET_SIZE];
|
uint8_t mouse_bytes[MOUSE_PACKET_SIZE];
|
||||||
|
const char* announcement;
|
||||||
|
int exit_code;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void display_initialize(struct display* display);
|
void display_initialize(struct display* display);
|
||||||
|
void display_exit(struct display* display, int exit_code);
|
||||||
void assert_is_well_formed_display_list(struct display* display);
|
void assert_is_well_formed_display_list(struct display* display);
|
||||||
void assert_is_well_formed_display(struct display* display);
|
void assert_is_well_formed_display(struct display* display);
|
||||||
void display_link_window_at_top(struct display* display, struct window* window);
|
void display_link_window_at_top(struct display* display, struct window* window);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016, 2023, 2024 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
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -40,6 +41,14 @@
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "vgafont.h"
|
#include "vgafont.h"
|
||||||
|
|
||||||
|
static volatile sig_atomic_t got_sigterm;
|
||||||
|
|
||||||
|
static void on_sigterm(int signum)
|
||||||
|
{
|
||||||
|
(void) signum;
|
||||||
|
got_sigterm = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int open_local_server_socket(const char* path, int flags)
|
static int open_local_server_socket(const char* path, int flags)
|
||||||
{
|
{
|
||||||
size_t path_length = strlen(path);
|
size_t path_length = strlen(path);
|
||||||
|
@ -196,9 +205,24 @@ void server_poll(struct server* server)
|
||||||
}
|
}
|
||||||
size_t pfds_used = cpfd_off + connections_polled;
|
size_t pfds_used = cpfd_off + connections_polled;
|
||||||
|
|
||||||
int num_events = ppoll(pfds, pfds_used, NULL, NULL);
|
sigset_t mask;
|
||||||
|
sigprocmask(SIG_SETMASK, NULL, &mask);
|
||||||
|
sigdelset(&mask, SIGTERM);
|
||||||
|
|
||||||
|
int num_events = ppoll(pfds, pfds_used, NULL, &mask);
|
||||||
|
|
||||||
|
if ( got_sigterm )
|
||||||
|
{
|
||||||
|
display_exit(server->display, -1);
|
||||||
|
got_sigterm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ( num_events < 0 )
|
if ( num_events < 0 )
|
||||||
|
{
|
||||||
|
if ( errno == EINTR )
|
||||||
|
return;
|
||||||
err(1, "poll");
|
err(1, "poll");
|
||||||
|
}
|
||||||
|
|
||||||
if ( pfds[0].revents )
|
if ( pfds[0].revents )
|
||||||
{
|
{
|
||||||
|
@ -273,9 +297,20 @@ void server_poll(struct server* server)
|
||||||
|
|
||||||
void server_mainloop(struct server* server)
|
void server_mainloop(struct server* server)
|
||||||
{
|
{
|
||||||
while ( true )
|
sigset_t sigterm, oldset;
|
||||||
|
sigemptyset(&sigterm);
|
||||||
|
sigaddset(&sigterm, SIGTERM);
|
||||||
|
sigprocmask(SIG_BLOCK, &sigterm, &oldset);
|
||||||
|
struct sigaction sa = { .sa_handler = on_sigterm }, old_sa;
|
||||||
|
sigaction(SIGTERM, &sa, &old_sa);
|
||||||
|
|
||||||
|
while ( server->display->running )
|
||||||
{
|
{
|
||||||
display_render(server->display);
|
display_render(server->display);
|
||||||
server_poll(server);
|
server_poll(server);
|
||||||
}
|
}
|
||||||
|
display_render(server->display);
|
||||||
|
|
||||||
|
sigaction(SIGTERM, &old_sa, NULL);
|
||||||
|
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -270,6 +271,14 @@ bool editor_save_file(struct editor* editor, const char* path)
|
||||||
return fclose(fp) != EOF;
|
return fclose(fp) != EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volatile sig_atomic_t had_sigwinch = 0;
|
||||||
|
|
||||||
|
static void sigwinch(int signum)
|
||||||
|
{
|
||||||
|
(void) signum;
|
||||||
|
had_sigwinch = 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
@ -279,6 +288,13 @@ int main(int argc, char* argv[])
|
||||||
if ( !isatty(1) )
|
if ( !isatty(1) )
|
||||||
err(1, "standard output");
|
err(1, "standard output");
|
||||||
|
|
||||||
|
sigset_t sigwinch_set;
|
||||||
|
sigemptyset(&sigwinch_set);
|
||||||
|
sigaddset(&sigwinch_set, SIGWINCH);
|
||||||
|
sigprocmask(SIG_BLOCK, &sigwinch_set, NULL);
|
||||||
|
struct sigaction sa = { .sa_handler = sigwinch };
|
||||||
|
sigaction(SIGWINCH, &sa, NULL);
|
||||||
|
|
||||||
struct editor editor;
|
struct editor editor;
|
||||||
initialize_editor(&editor);
|
initialize_editor(&editor);
|
||||||
|
|
||||||
|
|
|
@ -353,12 +353,22 @@ void editor_input_process_byte(struct editor_input* editor_input,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern volatile sig_atomic_t had_sigwinch;
|
||||||
|
|
||||||
void editor_input_process(struct editor_input* editor_input,
|
void editor_input_process(struct editor_input* editor_input,
|
||||||
struct editor* editor)
|
struct editor* editor)
|
||||||
{
|
{
|
||||||
|
sigset_t sigset;
|
||||||
|
sigemptyset(&sigset);
|
||||||
struct pollfd pfd = { .fd = 0, .events = POLLIN };
|
struct pollfd pfd = { .fd = 0, .events = POLLIN };
|
||||||
do editor_input_process_byte(editor_input, editor);
|
struct timespec timeout = { .tv_sec = 0 };
|
||||||
while ( poll(&pfd, 1, 0) == 1 );
|
struct timespec* timeout_ptr = NULL;
|
||||||
|
while ( !had_sigwinch && ppoll(&pfd, 1, timeout_ptr, &sigset) == 1 )
|
||||||
|
{
|
||||||
|
editor_input_process_byte(editor_input, editor);
|
||||||
|
timeout_ptr = &timeout;
|
||||||
|
}
|
||||||
|
had_sigwinch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_input_end(struct editor_input* editor_input)
|
void editor_input_end(struct editor_input* editor_input)
|
||||||
|
|
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
|
2
fat/.gitignore
vendored
Normal file
2
fat/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
fatfs
|
||||||
|
*.o
|
34
fat/Makefile
Normal file
34
fat/Makefile
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/version.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||||
|
CXXFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
|
||||||
|
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -fcheck-new
|
||||||
|
|
||||||
|
LIBS:=$(LIBS)
|
||||||
|
|
||||||
|
ifeq ($(HOST_IS_SORTIX),0)
|
||||||
|
PTHREAD_OPTION:=-pthread
|
||||||
|
LIBS:=$(LIBS) -lfuse
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
|
endif
|
||||||
|
|
||||||
|
BINARIES:=fatfs
|
||||||
|
|
||||||
|
all: $(BINARIES)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||||
|
install $(BINARIES) $(DESTDIR)$(SBINDIR)
|
||||||
|
|
||||||
|
fatfs: *.cpp *.h
|
||||||
|
$(CXX) $(PTHREAD_OPTION) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BINARIES) *.o
|
165
fat/block.cpp
Normal file
165
fat/block.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* block.cpp
|
||||||
|
* Blocks in the filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "ioleast.h"
|
||||||
|
|
||||||
|
Block::Block()
|
||||||
|
{
|
||||||
|
this->block_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block::Block(Device* device, uint32_t block_id)
|
||||||
|
{
|
||||||
|
Construct(device, block_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Construct(Device* device, uint32_t block_id)
|
||||||
|
{
|
||||||
|
this->modify_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
this->transit_done_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->prev_block = NULL;
|
||||||
|
this->next_block = NULL;
|
||||||
|
this->prev_hashed = NULL;
|
||||||
|
this->next_hashed = NULL;
|
||||||
|
this->prev_dirty = NULL;
|
||||||
|
this->next_dirty = NULL;
|
||||||
|
this->device = device;
|
||||||
|
this->reference_count = 1;
|
||||||
|
this->block_id = block_id;
|
||||||
|
this->dirty = false;
|
||||||
|
this->is_in_transit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block::~Block()
|
||||||
|
{
|
||||||
|
Destruct();
|
||||||
|
delete[] block_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Destruct()
|
||||||
|
{
|
||||||
|
Sync();
|
||||||
|
Unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Refer()
|
||||||
|
{
|
||||||
|
reference_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Unref()
|
||||||
|
{
|
||||||
|
if ( !--reference_count )
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
device->block_count--;
|
||||||
|
delete this;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Sync()
|
||||||
|
{
|
||||||
|
if ( device->has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&device->sync_thread_lock);
|
||||||
|
while ( dirty || is_in_transit )
|
||||||
|
pthread_cond_wait(&transit_done_cond, &device->sync_thread_lock);
|
||||||
|
pthread_mutex_unlock(&device->sync_thread_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !dirty )
|
||||||
|
return;
|
||||||
|
dirty = false;
|
||||||
|
(prev_dirty ? prev_dirty->next_dirty : device->dirty_block) = next_dirty;
|
||||||
|
if ( next_dirty )
|
||||||
|
next_dirty->prev_dirty = prev_dirty;
|
||||||
|
prev_dirty = NULL;
|
||||||
|
next_dirty = NULL;
|
||||||
|
if ( !device->write )
|
||||||
|
return;
|
||||||
|
off_t file_offset = (off_t) device->block_size * (off_t) block_id;
|
||||||
|
pwriteall(device->fd, block_data, device->block_size, file_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::BeginWrite()
|
||||||
|
{
|
||||||
|
assert(device->write);
|
||||||
|
pthread_mutex_lock(&modify_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::FinishWrite()
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&modify_lock);
|
||||||
|
pthread_mutex_lock(&device->sync_thread_lock);
|
||||||
|
if ( !dirty )
|
||||||
|
{
|
||||||
|
dirty = true;
|
||||||
|
prev_dirty = NULL;
|
||||||
|
next_dirty = device->dirty_block;
|
||||||
|
if ( next_dirty )
|
||||||
|
next_dirty->prev_dirty = this;
|
||||||
|
device->dirty_block = this;
|
||||||
|
pthread_cond_signal(&device->sync_thread_cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&device->sync_thread_lock);
|
||||||
|
Use();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Use()
|
||||||
|
{
|
||||||
|
Unlink();
|
||||||
|
Prelink();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Unlink()
|
||||||
|
{
|
||||||
|
(prev_block ? prev_block->next_block : device->mru_block) = next_block;
|
||||||
|
(next_block ? next_block->prev_block : device->lru_block) = prev_block;
|
||||||
|
size_t bin = block_id % DEVICE_HASH_LENGTH;
|
||||||
|
(prev_hashed ? prev_hashed->next_hashed : device->hash_blocks[bin]) = next_hashed;
|
||||||
|
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::Prelink()
|
||||||
|
{
|
||||||
|
prev_block = NULL;
|
||||||
|
next_block = device->mru_block;
|
||||||
|
if ( device->mru_block )
|
||||||
|
device->mru_block->prev_block = this;
|
||||||
|
device->mru_block = this;
|
||||||
|
if ( !device->lru_block )
|
||||||
|
device->lru_block = this;
|
||||||
|
size_t bin = block_id % DEVICE_HASH_LENGTH;
|
||||||
|
prev_hashed = NULL;
|
||||||
|
next_hashed = device->hash_blocks[bin];
|
||||||
|
device->hash_blocks[bin] = this;
|
||||||
|
if ( next_hashed )
|
||||||
|
next_hashed->prev_hashed = this;
|
||||||
|
}
|
62
fat/block.h
Normal file
62
fat/block.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* block.h
|
||||||
|
* Blocks in the filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLOCK_H
|
||||||
|
#define BLOCK_H
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
|
||||||
|
class Block
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Block();
|
||||||
|
Block(Device* device, uint32_t block_id);
|
||||||
|
~Block();
|
||||||
|
void Construct(Device* device, uint32_t block_id);
|
||||||
|
void Destruct();
|
||||||
|
|
||||||
|
public:
|
||||||
|
pthread_mutex_t modify_lock;
|
||||||
|
pthread_cond_t transit_done_cond;
|
||||||
|
Block* prev_block;
|
||||||
|
Block* next_block;
|
||||||
|
Block* prev_hashed;
|
||||||
|
Block* next_hashed;
|
||||||
|
Block* prev_dirty;
|
||||||
|
Block* next_dirty;
|
||||||
|
Device* device;
|
||||||
|
size_t reference_count;
|
||||||
|
uint32_t block_id;
|
||||||
|
bool dirty;
|
||||||
|
bool is_in_transit;
|
||||||
|
uint8_t* block_data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Refer();
|
||||||
|
void Unref();
|
||||||
|
void Sync();
|
||||||
|
void BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Use();
|
||||||
|
void Unlink();
|
||||||
|
void Prelink();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
220
fat/device.cpp
Normal file
220
fat/device.cpp
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* device.cpp
|
||||||
|
* Block device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "ioleast.h"
|
||||||
|
|
||||||
|
void* Device__SyncThread(void* ctx)
|
||||||
|
{
|
||||||
|
((Device*) ctx)->SyncThread();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::Device(int fd, const char* path, uint32_t block_size, bool write)
|
||||||
|
{
|
||||||
|
// sync_thread unset.
|
||||||
|
this->sync_thread_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->sync_thread_idle_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->sync_thread_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
this->mru_block = NULL;
|
||||||
|
this->lru_block = NULL;
|
||||||
|
this->dirty_block = NULL;
|
||||||
|
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
|
||||||
|
hash_blocks[i] = NULL;
|
||||||
|
struct stat st;
|
||||||
|
fstat(fd, &st);
|
||||||
|
this->device_size = st.st_size;
|
||||||
|
this->path = path;
|
||||||
|
this->block_size = block_size;
|
||||||
|
this->fd = fd;
|
||||||
|
this->write = write;
|
||||||
|
this->has_sync_thread = false;
|
||||||
|
this->sync_thread_should_exit = false;
|
||||||
|
this->sync_in_transit = false;
|
||||||
|
this->block_count = 0;
|
||||||
|
#ifdef __sortix__
|
||||||
|
// TODO: This isn't scaleable if there's multiple filesystems mounted.
|
||||||
|
size_t memory;
|
||||||
|
memstat(NULL, &memory);
|
||||||
|
this->block_limit = (memory / 10) / block_size;
|
||||||
|
#else
|
||||||
|
this->block_limit = 32768;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device()
|
||||||
|
{
|
||||||
|
if ( has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
sync_thread_should_exit = true;
|
||||||
|
pthread_cond_signal(&sync_thread_cond);
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
pthread_join(sync_thread, NULL);
|
||||||
|
has_sync_thread = false;
|
||||||
|
}
|
||||||
|
Sync();
|
||||||
|
while ( mru_block )
|
||||||
|
delete mru_block;
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SpawnSyncThread()
|
||||||
|
{
|
||||||
|
if ( this->has_sync_thread )
|
||||||
|
return;
|
||||||
|
this->has_sync_thread = write &&
|
||||||
|
pthread_create(&this->sync_thread, NULL, Device__SyncThread, this) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Device::AllocateBlock()
|
||||||
|
{
|
||||||
|
if ( block_limit <= block_count )
|
||||||
|
{
|
||||||
|
for ( Block* block = lru_block; block; block = block->prev_block )
|
||||||
|
{
|
||||||
|
if ( block->reference_count )
|
||||||
|
continue;
|
||||||
|
block->Destruct(); // Syncs.
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t* data = new uint8_t[block_size];
|
||||||
|
if ( !data ) // TODO: Use operator new nothrow!
|
||||||
|
return NULL;
|
||||||
|
Block* block = new Block();
|
||||||
|
if ( !block ) // TODO: Use operator new nothrow!
|
||||||
|
return delete[] data, (Block*) NULL;
|
||||||
|
block->block_data = data;
|
||||||
|
block_count++;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Device::GetBlock(uint32_t block_id)
|
||||||
|
{
|
||||||
|
if ( Block* block = GetCachedBlock(block_id) )
|
||||||
|
return block;
|
||||||
|
Block* block = AllocateBlock();
|
||||||
|
if ( !block )
|
||||||
|
return NULL;
|
||||||
|
block->Construct(this, block_id);
|
||||||
|
off_t file_offset = (off_t) block_size * (off_t) block_id;
|
||||||
|
preadall(fd, block->block_data, block_size, file_offset);
|
||||||
|
block->Prelink();
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Device::GetBlockZeroed(uint32_t block_id)
|
||||||
|
{
|
||||||
|
assert(write);
|
||||||
|
if ( Block* block = GetCachedBlock(block_id) )
|
||||||
|
{
|
||||||
|
block->BeginWrite();
|
||||||
|
memset(block->block_data, 0, block_size);
|
||||||
|
block->FinishWrite();
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
Block* block = AllocateBlock();
|
||||||
|
if ( !block )
|
||||||
|
return NULL;
|
||||||
|
block->Construct(this, block_id);
|
||||||
|
memset(block->block_data, 0, block_size);
|
||||||
|
block->Prelink();
|
||||||
|
block->BeginWrite();
|
||||||
|
block->FinishWrite();
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Device::GetCachedBlock(uint32_t block_id)
|
||||||
|
{
|
||||||
|
size_t bin = block_id % DEVICE_HASH_LENGTH;
|
||||||
|
for ( Block* iter = hash_blocks[bin]; iter; iter = iter->next_hashed )
|
||||||
|
if ( iter->block_id == block_id )
|
||||||
|
return iter->Refer(), iter;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::Sync()
|
||||||
|
{
|
||||||
|
if ( has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
while ( dirty_block || sync_in_transit )
|
||||||
|
pthread_cond_wait(&sync_thread_cond, &sync_thread_lock);
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
fsync(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( dirty_block )
|
||||||
|
dirty_block->Sync();
|
||||||
|
fsync(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SyncThread()
|
||||||
|
{
|
||||||
|
uint8_t transit_block_data[block_size];
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
while ( !(dirty_block || sync_thread_should_exit) )
|
||||||
|
pthread_cond_wait(&sync_thread_cond, &sync_thread_lock);
|
||||||
|
if ( sync_thread_should_exit )
|
||||||
|
break;
|
||||||
|
|
||||||
|
Block* block = dirty_block;
|
||||||
|
|
||||||
|
if ( block->next_dirty )
|
||||||
|
block->next_dirty->prev_dirty = NULL;
|
||||||
|
dirty_block = block->next_dirty;
|
||||||
|
block->next_dirty = NULL;
|
||||||
|
|
||||||
|
block->dirty = false;
|
||||||
|
block->is_in_transit = true;
|
||||||
|
sync_in_transit = true;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&block->modify_lock);
|
||||||
|
memcpy(transit_block_data, block->block_data, block_size);
|
||||||
|
pthread_mutex_unlock(&block->modify_lock);
|
||||||
|
|
||||||
|
off_t offset = (off_t) block_size * (off_t) block->block_id;
|
||||||
|
pwriteall(fd, transit_block_data, block_size, offset);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
block->is_in_transit = false;
|
||||||
|
sync_in_transit = false;
|
||||||
|
pthread_cond_signal(&block->transit_done_cond);
|
||||||
|
if ( !dirty_block )
|
||||||
|
pthread_cond_signal(&sync_thread_idle_cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
}
|
64
fat/device.h
Normal file
64
fat/device.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* device.h
|
||||||
|
* Block device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEVICE_H
|
||||||
|
#define DEVICE_H
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
|
||||||
|
static const size_t DEVICE_HASH_LENGTH = 1 << 16;
|
||||||
|
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Device(int fd, const char* path, uint32_t block_size, bool write);
|
||||||
|
~Device();
|
||||||
|
|
||||||
|
public:
|
||||||
|
pthread_t sync_thread;
|
||||||
|
pthread_cond_t sync_thread_cond;
|
||||||
|
pthread_cond_t sync_thread_idle_cond;
|
||||||
|
pthread_mutex_t sync_thread_lock;
|
||||||
|
Block* mru_block;
|
||||||
|
Block* lru_block;
|
||||||
|
Block* dirty_block;
|
||||||
|
Block* hash_blocks[DEVICE_HASH_LENGTH];
|
||||||
|
off_t device_size;
|
||||||
|
const char* path;
|
||||||
|
uint32_t block_size;
|
||||||
|
int fd;
|
||||||
|
bool write;
|
||||||
|
bool has_sync_thread;
|
||||||
|
bool sync_thread_should_exit;
|
||||||
|
bool sync_in_transit;
|
||||||
|
size_t block_count;
|
||||||
|
size_t block_limit;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SpawnSyncThread();
|
||||||
|
Block* AllocateBlock();
|
||||||
|
Block* GetBlock(uint32_t block_id);
|
||||||
|
Block* GetBlockZeroed(uint32_t block_id);
|
||||||
|
Block* GetCachedBlock(uint32_t block_id);
|
||||||
|
void Sync();
|
||||||
|
void SyncThread();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
124
fat/fat.h
Normal file
124
fat/fat.h
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fat.h
|
||||||
|
* The File Allocation Table (FAT) filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FAT_H
|
||||||
|
#define FAT_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// TODO: Dammit. I forgot to swap endian all across the codebase.
|
||||||
|
|
||||||
|
struct fat_bpb
|
||||||
|
{
|
||||||
|
uint8_t jump[3];
|
||||||
|
char oem[8];
|
||||||
|
uint8_t bytes_per_sector_low;
|
||||||
|
uint8_t bytes_per_sector_high;
|
||||||
|
uint8_t sectors_per_cluster;
|
||||||
|
uint16_t reserved_sectors;
|
||||||
|
uint8_t fat_count;
|
||||||
|
uint8_t root_dirent_count_low;
|
||||||
|
uint8_t root_dirent_count_high;
|
||||||
|
uint8_t total_sectors_low;
|
||||||
|
uint8_t total_sectors_high;
|
||||||
|
uint8_t media_descriptor_type;
|
||||||
|
uint16_t sectors_per_fat;
|
||||||
|
uint16_t sectors_per_track;
|
||||||
|
uint16_t head_count;
|
||||||
|
uint16_t hidden_sectors;
|
||||||
|
uint32_t total_sectors_large;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t fat12_drive_number;
|
||||||
|
uint8_t fat12_reserved;
|
||||||
|
uint8_t fat12_signature;
|
||||||
|
uint8_t fat12_volume_id[4];
|
||||||
|
uint8_t fat12_volume_label[11];
|
||||||
|
uint8_t fat12_system[8];
|
||||||
|
};
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t fat32_sectors_per_fat;
|
||||||
|
uint16_t fat32_flags;
|
||||||
|
uint16_t fat32_version;
|
||||||
|
uint32_t fat32_root_cluster;
|
||||||
|
uint16_t fat32_fsinfo;
|
||||||
|
uint16_t fat32_backup_boot;
|
||||||
|
uint32_t fat32_reserved1[3];
|
||||||
|
uint8_t fat32_drive_number;
|
||||||
|
uint8_t fat32_reserved2;
|
||||||
|
uint8_t fat32_signature;
|
||||||
|
uint8_t fat32_volume_id[4];
|
||||||
|
uint8_t fat32_volume_label[11];
|
||||||
|
uint8_t fat32_system[8];
|
||||||
|
};
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t bootloader[510 - 36];
|
||||||
|
uint8_t boot_signature[2];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct fat_bpb) == 512, "sizeof(struct fat_bpb) == 512");
|
||||||
|
|
||||||
|
struct fat_fsinfo
|
||||||
|
{
|
||||||
|
uint32_t signature1;
|
||||||
|
uint32_t reserved1[120];
|
||||||
|
uint32_t signature2;
|
||||||
|
uint32_t free_count;
|
||||||
|
uint32_t next_free;
|
||||||
|
uint32_t reserved2[3];
|
||||||
|
uint32_t signature3;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct fat_fsinfo) == 512, "sizeof(struct fat_fsinfo) == 512");
|
||||||
|
|
||||||
|
struct fat_dirent
|
||||||
|
{
|
||||||
|
char name[11];
|
||||||
|
uint8_t attributes;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t creation_tenths; // TODO: Misnamed semantically.
|
||||||
|
uint16_t creation_time;
|
||||||
|
uint16_t creation_date;
|
||||||
|
uint16_t access_date;
|
||||||
|
uint16_t cluster_high;
|
||||||
|
uint16_t modified_time;
|
||||||
|
uint16_t modified_date;
|
||||||
|
uint16_t cluster_low;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct fat_dirent) == 32, "sizeof(struct fat_dirent) == 32");
|
||||||
|
|
||||||
|
#define FAT_ATTRIBUTE_READ_ONLY (1 << 0)
|
||||||
|
#define FAT_ATTRIBUTE_HIDDEN (1 << 1)
|
||||||
|
#define FAT_ATTRIBUTE_SYSTEM (1 << 2)
|
||||||
|
#define FAT_ATTRIBUTE_VOLUME_ID (1 << 3)
|
||||||
|
#define FAT_ATTRIBUTE_DIRECTORY (1 << 4)
|
||||||
|
#define FAT_ATTRIBUTE_ARCHIVE (1 << 5)
|
||||||
|
|
||||||
|
#define FAT_ATTRIBUTE_LONG_NAME 0x0F
|
||||||
|
|
||||||
|
#endif
|
275
fat/fatfs.cpp
Normal file
275
fat/fatfs.cpp
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fatfs.cpp
|
||||||
|
* The File Allocation Table (FAT) filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if defined(__sortix__)
|
||||||
|
#include "fsmarshall.h"
|
||||||
|
#else
|
||||||
|
#include "fuse.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "fat.h"
|
||||||
|
#include "fatfs.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "ioleast.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
uid_t request_uid;
|
||||||
|
uid_t request_gid;
|
||||||
|
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
void StatInode(Inode* inode, struct stat* st)
|
||||||
|
{
|
||||||
|
memset(st, 0, sizeof(*st));
|
||||||
|
st->st_ino = inode->inode_id;
|
||||||
|
st->st_mode = inode->Mode();
|
||||||
|
st->st_nlink = 1; // TODO: Encapsulate.
|
||||||
|
st->st_uid = inode->UserId();
|
||||||
|
st->st_gid = inode->GroupId();
|
||||||
|
st->st_size = inode->Size();
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
// TODO: For the root dir, mount time, or maybe volume label or first file?
|
||||||
|
// Or maybe the time of the mount point?
|
||||||
|
time_t mtime = 0;
|
||||||
|
if ( inode->dirent )
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
memset(&tm, 0, sizeof(tm));
|
||||||
|
tm.tm_sec = ((inode->dirent->modified_time >> 0) & 0x1F) * 2;
|
||||||
|
tm.tm_min = (inode->dirent->modified_time >> 5) & 0x3F;
|
||||||
|
tm.tm_hour = (inode->dirent->modified_time >> 11) & 0x1F;
|
||||||
|
tm.tm_mday = (inode->dirent->modified_date >> 0) & 0x1F;
|
||||||
|
tm.tm_mon = ((inode->dirent->modified_date >> 5) & 0xF) - 1;
|
||||||
|
tm.tm_year = ((inode->dirent->modified_date >> 9) & 0x7F) + 80;
|
||||||
|
mtime = mktime(&tm);
|
||||||
|
}
|
||||||
|
st->st_atim.tv_sec = mtime; // TODO: The actual accessed time;
|
||||||
|
st->st_atim.tv_nsec = 0;
|
||||||
|
st->st_ctim.tv_sec = mtime; // TODO: Probably fine to keep as modified time.
|
||||||
|
st->st_ctim.tv_nsec = 0;
|
||||||
|
st->st_mtim.tv_sec = mtime;
|
||||||
|
st->st_mtim.tv_nsec = 0;
|
||||||
|
st->st_blksize = inode->filesystem->bytes_per_sector *
|
||||||
|
inode->filesystem->bpb->sectors_per_cluster;
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
st->st_blocks = 0; // TODO inode->data->i_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compact_arguments(int* argc, char*** argv)
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < *argc; i++ )
|
||||||
|
{
|
||||||
|
while ( i < *argc && !(*argv)[i] )
|
||||||
|
{
|
||||||
|
for ( int n = i; n < *argc; n++ )
|
||||||
|
(*argv)[n] = (*argv)[n+1];
|
||||||
|
(*argc)--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help(FILE* fp, const char* argv0)
|
||||||
|
{
|
||||||
|
fprintf(fp, "Usage: %s [OPTION]... DEVICE [MOUNT-POINT]\n", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void version(FILE* fp, const char* argv0)
|
||||||
|
{
|
||||||
|
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const char* argv0 = argv[0];
|
||||||
|
const char* pretend_mount_path = NULL;
|
||||||
|
bool foreground = false;
|
||||||
|
bool read = false;
|
||||||
|
bool write = false;
|
||||||
|
for ( int i = 1; i < argc; i++ )
|
||||||
|
{
|
||||||
|
const char* arg = argv[i];
|
||||||
|
if ( arg[0] != '-' || !arg[1] )
|
||||||
|
continue;
|
||||||
|
argv[i] = NULL;
|
||||||
|
if ( !strcmp(arg, "--") )
|
||||||
|
break;
|
||||||
|
if ( arg[1] != '-' )
|
||||||
|
{
|
||||||
|
while ( char c = *++arg ) switch ( c )
|
||||||
|
{
|
||||||
|
case 'r': read = true; break;
|
||||||
|
case 'w': write = true; break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||||
|
help(stderr, argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !strcmp(arg, "--help") )
|
||||||
|
help(stdout, argv0), exit(0);
|
||||||
|
else if ( !strcmp(arg, "--version") )
|
||||||
|
version(stdout, argv0), exit(0);
|
||||||
|
else if ( !strcmp(arg, "--background") )
|
||||||
|
foreground = false;
|
||||||
|
else if ( !strcmp(arg, "--foreground") )
|
||||||
|
foreground = true;
|
||||||
|
else if ( !strcmp(arg, "--read") )
|
||||||
|
read = true;
|
||||||
|
else if ( !strcmp(arg, "--write") )
|
||||||
|
write = true;
|
||||||
|
else if ( !strncmp(arg, "--pretend-mount-path=", strlen("--pretend-mount-path=")) )
|
||||||
|
pretend_mount_path = arg + strlen("--pretend-mount-path=");
|
||||||
|
else if ( !strcmp(arg, "--pretend-mount-path") )
|
||||||
|
{
|
||||||
|
if ( i+1 == argc )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: --pretend-mount-path: Missing operand\n", argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
pretend_mount_path = argv[++i];
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
||||||
|
help(stderr, argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It doesn't make sense to have a write-only filesystem.
|
||||||
|
read = read || write;
|
||||||
|
|
||||||
|
// Default to read and write filesystem access.
|
||||||
|
if ( !read && !write )
|
||||||
|
read = write = true;
|
||||||
|
|
||||||
|
if ( argc == 1 )
|
||||||
|
{
|
||||||
|
help(stdout, argv0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
compact_arguments(&argc, &argv);
|
||||||
|
|
||||||
|
const char* device_path = 2 <= argc ? argv[1] : NULL;
|
||||||
|
const char* mount_path = 3 <= argc ? argv[2] : NULL;
|
||||||
|
|
||||||
|
if ( !device_path )
|
||||||
|
{
|
||||||
|
help(stderr, argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !pretend_mount_path )
|
||||||
|
pretend_mount_path = mount_path;
|
||||||
|
|
||||||
|
int fd = open(device_path, write ? O_RDWR : O_RDONLY);
|
||||||
|
if ( fd < 0 )
|
||||||
|
err(1, "%s", device_path);
|
||||||
|
|
||||||
|
// Read the bios parameter block from the filesystem so we can verify it.
|
||||||
|
struct fat_bpb bpb;
|
||||||
|
if ( preadall(fd, &bpb, sizeof(bpb), 0) != sizeof(bpb) )
|
||||||
|
{
|
||||||
|
if ( errno == EEOF )
|
||||||
|
errx(1, "%s: Isn't a valid FAT filesystem (too short)", device_path);
|
||||||
|
else
|
||||||
|
err(1, "read: %s", device_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the boot.
|
||||||
|
if ( !(bpb.boot_signature[0] == 0x55 && bpb.boot_signature[1] == 0xAA) )
|
||||||
|
errx(1, "%s: Isn't a valid FAT filesystem (no boot signature)", device_path);
|
||||||
|
|
||||||
|
// Verify the jump instruction at the start of the boot sector.
|
||||||
|
if ( !(bpb.jump[0] == 0xEB && bpb.jump[2] == 0x90) &&
|
||||||
|
!(bpb.jump[0] == 0xE9) )
|
||||||
|
errx(1, "%s: Isn't a valid FAT filesystem (bad jump)", device_path);
|
||||||
|
|
||||||
|
// TODO: Validate all parameters make sense.
|
||||||
|
|
||||||
|
uint16_t bytes_per_sector =
|
||||||
|
bpb.bytes_per_sector_low | bpb.bytes_per_sector_high << 8;
|
||||||
|
uint16_t root_dirent_count =
|
||||||
|
bpb.root_dirent_count_low | bpb.root_dirent_count_high << 8;
|
||||||
|
uint32_t root_dir_sectors =
|
||||||
|
divup<uint32_t>(root_dirent_count * sizeof(fat_dirent), bytes_per_sector);
|
||||||
|
uint32_t sectors_per_fat =
|
||||||
|
bpb.sectors_per_fat ? bpb.sectors_per_fat : bpb.fat32_sectors_per_fat;
|
||||||
|
uint32_t total_sectors =
|
||||||
|
bpb.total_sectors_low | bpb.total_sectors_high << 8;
|
||||||
|
if ( !total_sectors )
|
||||||
|
total_sectors = bpb.total_sectors_large;
|
||||||
|
|
||||||
|
uint32_t data_offset =
|
||||||
|
bpb.reserved_sectors + bpb.fat_count * sectors_per_fat + root_dir_sectors;
|
||||||
|
uint32_t data_sectors = total_sectors - data_offset;
|
||||||
|
uint32_t cluster_count = data_sectors / bpb.sectors_per_cluster;
|
||||||
|
|
||||||
|
uint8_t fat_type =
|
||||||
|
cluster_count < 4085 ? 12 : cluster_count < 65525 ? 16 : 32;
|
||||||
|
|
||||||
|
// Verify the filesystem version.
|
||||||
|
if ( fat_type == 32 && bpb.fat32_version != 0x0000 )
|
||||||
|
errx(1, "%s: Unsupported filesystem version 0x%04x", device_path,
|
||||||
|
bpb.fat32_version);
|
||||||
|
|
||||||
|
// TODO: On FAT16/32 check FAT entry 1 for the high bits to see if fsck is needed.
|
||||||
|
// Check whether the filesystem was unmounted cleanly.
|
||||||
|
//if ( !(fat[1] & FAT_UNMOUNTED) || !(fat[1] & FAT_NO_IO_ERR) )
|
||||||
|
// warn("warning: %s: Filesystem wasn't unmounted cleanly\n", device_path);
|
||||||
|
|
||||||
|
// TODO: The FAT and clusters are not aligned to cluster size so
|
||||||
|
// we can't use the cluster size here. Perhaps refactor the
|
||||||
|
// device so we can deal with whole clusters.
|
||||||
|
Device* dev = new Device(fd, device_path, bytes_per_sector, write);
|
||||||
|
if ( !dev ) // TODO: Use operator new nothrow!
|
||||||
|
err(1, "malloc");
|
||||||
|
Filesystem* fs = new Filesystem(dev, pretend_mount_path);
|
||||||
|
if ( !fs ) // TODO: Use operator new nothrow!
|
||||||
|
err(1, "malloc");
|
||||||
|
|
||||||
|
if ( !mount_path )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if defined(__sortix__)
|
||||||
|
return fsmarshall_main(argv0, mount_path, foreground, fs, dev);
|
||||||
|
#else
|
||||||
|
return fat_fuse_main(argv0, mount_path, foreground, fs, dev);
|
||||||
|
#endif
|
||||||
|
}
|
33
fat/fatfs.h
Normal file
33
fat/fatfs.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fatfs.h
|
||||||
|
* Implementation of the extended filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXTFS_H
|
||||||
|
#define EXTFS_H
|
||||||
|
|
||||||
|
extern uid_t request_uid;
|
||||||
|
extern gid_t request_gid;
|
||||||
|
|
||||||
|
class Inode;
|
||||||
|
|
||||||
|
mode_t HostModeFromExtMode(uint32_t extmode);
|
||||||
|
uint32_t ExtModeFromHostMode(mode_t hostmode);
|
||||||
|
uint8_t HostDTFromExtDT(uint8_t extdt);
|
||||||
|
void StatInode(Inode* inode, struct stat* st);
|
||||||
|
|
||||||
|
#endif
|
446
fat/filesystem.cpp
Normal file
446
fat/filesystem.cpp
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* filesystem.cpp
|
||||||
|
* Filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "fat.h"
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
// TODO: Be more precise.
|
||||||
|
static bool is_8_3_char(char c)
|
||||||
|
{
|
||||||
|
return ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Is this fully precise? What about .FOO?
|
||||||
|
bool is_8_3(const char* name)
|
||||||
|
{
|
||||||
|
if ( !name[0] )
|
||||||
|
return false;
|
||||||
|
size_t b = 0;
|
||||||
|
while ( name[b] && is_8_3_char(name[b]) )
|
||||||
|
b++;
|
||||||
|
if ( 8 < b )
|
||||||
|
return false;
|
||||||
|
if ( !name[b] )
|
||||||
|
return true;
|
||||||
|
if ( name[b] != '.' )
|
||||||
|
return false;
|
||||||
|
size_t e = 0;
|
||||||
|
while ( name[b+1+e] && is_8_3_char(name[b+1+e]) )
|
||||||
|
e++;
|
||||||
|
if ( 3 < e )
|
||||||
|
return false;
|
||||||
|
if ( name[b+1+e] )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_8_3(const char* decoded, char encoded[8 + 3])
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
for ( size_t o = 0; o < 8 + 3; o++ )
|
||||||
|
{
|
||||||
|
char c = ' ';
|
||||||
|
if ( decoded[i] == '.' && o == 8 )
|
||||||
|
i++;
|
||||||
|
if ( decoded[i] && decoded[i] != '.' )
|
||||||
|
c = decoded[i++];
|
||||||
|
if ( (unsigned char) c == 0xE5 )
|
||||||
|
c = 0x05;
|
||||||
|
encoded[o] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void decode_8_3(const char encoded[8 + 3], char decoded[8 + 1 + 3 + 1])
|
||||||
|
{
|
||||||
|
size_t o = 0;
|
||||||
|
for ( size_t i = 0; i < 8; i++ )
|
||||||
|
{
|
||||||
|
char c = encoded[i];
|
||||||
|
if ( !c || c == ' ' )
|
||||||
|
break;
|
||||||
|
if ( c == 0x05 )
|
||||||
|
c = (char) 0xE5;
|
||||||
|
decoded[o++] = c;
|
||||||
|
}
|
||||||
|
for ( size_t i = 8; i < 8 + 3; i++ )
|
||||||
|
{
|
||||||
|
char c = encoded[i];
|
||||||
|
if ( !c || c == ' ' )
|
||||||
|
break;
|
||||||
|
if ( i == 8 )
|
||||||
|
decoded[o++] = '.';
|
||||||
|
if ( c == 0x05 )
|
||||||
|
c = (char) 0xE5;
|
||||||
|
decoded[o++] = c;
|
||||||
|
}
|
||||||
|
decoded[o] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t timespec_to_fat_tenths(struct timespec* ts)
|
||||||
|
{
|
||||||
|
// TODO: Work with a struct tm instead.
|
||||||
|
uint16_t hundreds = ts->tv_nsec / 10000000;
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
if ( tm.tm_sec & 1 )
|
||||||
|
hundreds += 100;
|
||||||
|
return hundreds;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t timespec_to_fat_time(struct timespec* ts)
|
||||||
|
{
|
||||||
|
// TODO: Work with a struct tm instead.
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
return (tm.tm_sec / 2) << 0 | tm.tm_min << 5 | tm.tm_hour << 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t timespec_to_fat_date(struct timespec* ts)
|
||||||
|
{
|
||||||
|
// TODO: Work with a struct tm instead.
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
return tm.tm_mday << 0 | (tm.tm_mon + 1) << 5 | (tm.tm_year - 80) << 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Rename tenths to a better name.
|
||||||
|
void timespec_to_fat(const struct timespec* ts, uint16_t* date, uint16_t* time,
|
||||||
|
uint8_t* tenths)
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ts->tv_sec, &tm);
|
||||||
|
// TODO: Endian.
|
||||||
|
*date = tm.tm_mday << 0 | (tm.tm_mon + 1) << 5 | (tm.tm_year - 80) << 9;
|
||||||
|
*time = (tm.tm_sec / 2) << 0 | tm.tm_min << 5 | tm.tm_hour << 11;
|
||||||
|
*tenths = ts->tv_nsec / 10000000 + (tm.tm_sec & 1 ? 100 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Filesystem::Filesystem(Device* device, const char* mount_path)
|
||||||
|
{
|
||||||
|
this->bpb_block = device->GetBlock(0);
|
||||||
|
assert(bpb_block); // TODO: This can fail.
|
||||||
|
this->bpb = (struct fat_bpb*) bpb_block->block_data;
|
||||||
|
this->device = device;
|
||||||
|
this->mount_path = mount_path;
|
||||||
|
this->mode_reg = S_IFREG | 0644;
|
||||||
|
this->mode_dir = S_IFDIR | 0755;
|
||||||
|
this->block_size = device->block_size;
|
||||||
|
this->bytes_per_sector =
|
||||||
|
bpb->bytes_per_sector_low | bpb->bytes_per_sector_high << 8;
|
||||||
|
this->root_dirent_count =
|
||||||
|
bpb->root_dirent_count_low | bpb->root_dirent_count_high << 8;
|
||||||
|
uint32_t root_dir_sectors =
|
||||||
|
divup<uint32_t>(root_dirent_count * sizeof(fat_dirent), bytes_per_sector);
|
||||||
|
this->sectors_per_fat =
|
||||||
|
bpb->sectors_per_fat ? bpb->sectors_per_fat : bpb->fat32_sectors_per_fat;
|
||||||
|
this->total_sectors =
|
||||||
|
bpb->total_sectors_low | bpb->total_sectors_high << 8;
|
||||||
|
if ( !this->total_sectors )
|
||||||
|
this->total_sectors = bpb->total_sectors_large;
|
||||||
|
this->fat_sector = bpb->reserved_sectors;
|
||||||
|
this->root_sector = fat_sector + bpb->fat_count * sectors_per_fat;
|
||||||
|
this->data_sector = root_sector + root_dir_sectors;
|
||||||
|
uint32_t data_sectors = total_sectors - data_sector;
|
||||||
|
this->cluster_count = data_sectors / bpb->sectors_per_cluster;
|
||||||
|
this->cluster_size = bpb->sectors_per_cluster * bytes_per_sector;
|
||||||
|
this->fat_type = cluster_count < 4085 ? 12 : cluster_count < 65525 ? 16 : 32;
|
||||||
|
// Use cluster 1 as the root inode on FAT12/FAT16 since it's not a valid
|
||||||
|
// cluster for use in the FAT.
|
||||||
|
this->root_inode_id = fat_type == 32 ? bpb->fat32_root_cluster : 1;
|
||||||
|
// TODO: Okay we actually need to compare with their lower bounds.
|
||||||
|
this->eio_cluster =
|
||||||
|
fat_type == 12 ? 0xFF7 : fat_type == 16 ? 0xFFF7 : 0xFFFFFF7;
|
||||||
|
this->eof_cluster =
|
||||||
|
fat_type == 12 ? 0xFFF : fat_type == 16 ? 0xFFFF : 0xFFFFFFF;
|
||||||
|
// TODO: Obtain and verify this from the fsinfo.
|
||||||
|
this->free_search = 0;
|
||||||
|
this->mru_inode = NULL;
|
||||||
|
this->lru_inode = NULL;
|
||||||
|
this->dirty_inode = NULL;
|
||||||
|
for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ )
|
||||||
|
this->hash_inodes[i] = NULL;
|
||||||
|
this->dirty = false;
|
||||||
|
|
||||||
|
if ( device->write )
|
||||||
|
{
|
||||||
|
BeginWrite();
|
||||||
|
// TODO: Mark as mounted in fat[1] if FAT16/32.
|
||||||
|
FinishWrite();
|
||||||
|
Sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Filesystem::~Filesystem()
|
||||||
|
{
|
||||||
|
Sync();
|
||||||
|
while ( mru_inode )
|
||||||
|
delete mru_inode;
|
||||||
|
if ( device->write )
|
||||||
|
{
|
||||||
|
BeginWrite();
|
||||||
|
// TODO: Mark as unounted in fat[1] if FAT16/32.
|
||||||
|
FinishWrite();
|
||||||
|
Sync();
|
||||||
|
}
|
||||||
|
bpb_block->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::BeginWrite()
|
||||||
|
{
|
||||||
|
bpb_block->BeginWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::FinishWrite()
|
||||||
|
{
|
||||||
|
dirty = true;
|
||||||
|
bpb_block->FinishWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::Sync()
|
||||||
|
{
|
||||||
|
// TODO: Replacement concept?
|
||||||
|
while ( dirty_inode )
|
||||||
|
dirty_inode->Sync();
|
||||||
|
if ( dirty )
|
||||||
|
{
|
||||||
|
bpb_block->Sync();
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
device->Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Inode* Filesystem::GetInode(uint32_t inode_id, Block* dirent_block,
|
||||||
|
struct fat_dirent* dirent)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if ( !inode_id || num_inodes <= inode_id )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
if ( !inode_id )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t bin = inode_id % INODE_HASH_LENGTH;
|
||||||
|
for ( Inode* iter = hash_inodes[bin]; iter; iter = iter->next_hashed )
|
||||||
|
if ( iter->inode_id == inode_id )
|
||||||
|
return iter->Refer(), iter;
|
||||||
|
|
||||||
|
if ( inode_id != root_inode_id && !dirent_block )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
|
||||||
|
Inode* inode = new Inode(this, inode_id);
|
||||||
|
if ( !inode )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
inode->first_cluster =
|
||||||
|
inode_id == root_inode_id && fat_type != 32 ? 0 : inode_id;
|
||||||
|
if ( (inode->data_block = dirent_block) )
|
||||||
|
inode->data_block->Refer();
|
||||||
|
inode->dirent = dirent;
|
||||||
|
inode->Prelink();
|
||||||
|
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::AllocateCluster()
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < cluster_count; i++ )
|
||||||
|
{
|
||||||
|
size_t n = 2 + (free_search + i) % cluster_count;
|
||||||
|
if ( !ReadFAT(n) )
|
||||||
|
{
|
||||||
|
free_search = (i + 1) % cluster_count;
|
||||||
|
if ( fat_type == 32 )
|
||||||
|
{
|
||||||
|
Block* block = device->GetBlock(bpb->fat32_fsinfo);
|
||||||
|
if ( block )
|
||||||
|
{
|
||||||
|
struct fat_fsinfo* fsinfo =
|
||||||
|
(struct fat_fsinfo*) block->block_data;
|
||||||
|
block->BeginWrite();
|
||||||
|
uint32_t free_count = le32toh(fsinfo->free_count);
|
||||||
|
if ( free_count )
|
||||||
|
free_count--;
|
||||||
|
fsinfo->free_count = htole32(free_count);
|
||||||
|
fsinfo->next_free = n;
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errno = ENOSPC, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filesystem::FreeCluster(uint32_t cluster)
|
||||||
|
{
|
||||||
|
if ( fat_type != 32 )
|
||||||
|
return;
|
||||||
|
Block* block = device->GetBlock(bpb->fat32_fsinfo);
|
||||||
|
if ( !block )
|
||||||
|
return;
|
||||||
|
struct fat_fsinfo* fsinfo = (struct fat_fsinfo*) block->block_data;
|
||||||
|
block->BeginWrite();
|
||||||
|
uint32_t free_count = le32toh(fsinfo->free_count);
|
||||||
|
if ( free_count < cluster_count )
|
||||||
|
free_count++;
|
||||||
|
fsinfo->free_count = htole32(free_count);
|
||||||
|
if ( !fsinfo->free_count || le32toh(fsinfo->next_free) == cluster + 1 )
|
||||||
|
{
|
||||||
|
fsinfo->next_free = htole32(cluster);
|
||||||
|
free_search = cluster - 2;
|
||||||
|
}
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::ReadFAT(uint32_t cluster)
|
||||||
|
{
|
||||||
|
// TODO: Bounds check.
|
||||||
|
if ( fat_type == 12 )
|
||||||
|
{
|
||||||
|
size_t position = cluster + (cluster / 2);
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t offset = position % bytes_per_sector;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return eio_cluster;
|
||||||
|
uint8_t lower = block->block_data[offset];
|
||||||
|
if ( ++offset == bytes_per_sector )
|
||||||
|
{
|
||||||
|
block->Unref();
|
||||||
|
if ( !(block = device->GetBlock(fat_sector + lba)) )
|
||||||
|
return eio_cluster;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
uint8_t higher = block->block_data[offset];
|
||||||
|
block->Unref();
|
||||||
|
uint16_t value = lower | higher << 8;
|
||||||
|
if ( cluster & 1 )
|
||||||
|
return value >> 4;
|
||||||
|
else
|
||||||
|
return value & 0xFFF;
|
||||||
|
}
|
||||||
|
size_t fat_size = fat_type / 8;
|
||||||
|
size_t position = cluster * fat_size;
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t entry = (position % bytes_per_sector) / fat_size;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return eio_cluster;
|
||||||
|
uint32_t result = 0;
|
||||||
|
if ( fat_type == 16 )
|
||||||
|
result = ((uint16_t*) block->block_data)[entry];
|
||||||
|
else if ( fat_type == 32 )
|
||||||
|
result = ((uint32_t*) block->block_data)[entry] & 0x0FFFFFFF;
|
||||||
|
block->Unref();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filesystem::WriteFAT(uint32_t cluster, uint32_t value)
|
||||||
|
{
|
||||||
|
assert(device->write);
|
||||||
|
// TODO: Bounds check.
|
||||||
|
if ( fat_type == 12 )
|
||||||
|
{
|
||||||
|
size_t position = cluster + (cluster / 2);
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t offset = position % bytes_per_sector;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return false;
|
||||||
|
value = cluster & 1 ? value << 4 : value;
|
||||||
|
uint16_t mask = cluster & 1 ? 0xFFF0 : 0x0FFF;
|
||||||
|
block->BeginWrite();
|
||||||
|
block->block_data[offset] &= ~mask;
|
||||||
|
block->block_data[offset] |= value;
|
||||||
|
if ( ++offset == bytes_per_sector )
|
||||||
|
{
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
if ( !(block = device->GetBlock(fat_sector + lba)) )
|
||||||
|
return false;
|
||||||
|
offset = 0;
|
||||||
|
block->BeginWrite();
|
||||||
|
}
|
||||||
|
block->block_data[offset] &= ~(mask >> 8);
|
||||||
|
block->block_data[offset] |= value >> 8;
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Mirror to the other FATs.
|
||||||
|
size_t fat_size = fat_type / 8;
|
||||||
|
size_t position = cluster * fat_size;
|
||||||
|
size_t lba = position / bytes_per_sector;
|
||||||
|
size_t entry = (position % bytes_per_sector) / fat_size;
|
||||||
|
Block* block = device->GetBlock(fat_sector + lba);
|
||||||
|
if ( !block )
|
||||||
|
return false;
|
||||||
|
block->BeginWrite();
|
||||||
|
if ( fat_type == 16 )
|
||||||
|
((uint16_t*) block->block_data)[entry] = value;
|
||||||
|
else if ( fat_type == 32 )
|
||||||
|
{
|
||||||
|
uint32_t old = ((uint32_t*) block->block_data)[entry] & 0xF0000000;
|
||||||
|
((uint32_t*) block->block_data)[entry] = value | old;
|
||||||
|
}
|
||||||
|
block->FinishWrite();
|
||||||
|
block->Unref();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::CalculateFreeCount()
|
||||||
|
{
|
||||||
|
if ( fat_type == 32 )
|
||||||
|
{
|
||||||
|
// TODO: Verify the fsinfo.
|
||||||
|
Block* block = device->GetBlock(bpb->fat32_fsinfo);
|
||||||
|
if ( !block )
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
const struct fat_fsinfo* fsinfo =
|
||||||
|
(const struct fat_fsinfo*) block->block_data;
|
||||||
|
uint32_t result = le32toh(fsinfo->free_count);
|
||||||
|
block->Unref();
|
||||||
|
if ( result != 0xFFFFFFFF )
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// TODO: Cache these.
|
||||||
|
size_t count = 0;
|
||||||
|
for ( size_t i = 0; i < cluster_count; i++ )
|
||||||
|
if ( !ReadFAT(2 + i) )
|
||||||
|
count++;
|
||||||
|
return count;
|
||||||
|
}
|
88
fat/filesystem.h
Normal file
88
fat/filesystem.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* filesystem.h
|
||||||
|
* Filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILESYSTEM_H
|
||||||
|
#define FILESYSTEM_H
|
||||||
|
|
||||||
|
bool is_8_3(const char* name);
|
||||||
|
void encode_8_3(const char* decoded, char encoded[8 + 3]);
|
||||||
|
void decode_8_3(const char encoded[8 + 3], char decoded[8 + 1 + 3 + 1]);
|
||||||
|
void timespec_to_fat(const struct timespec* ts, uint16_t* date, uint16_t* time,
|
||||||
|
uint8_t* tenths);
|
||||||
|
// TODO: Unify into the above.
|
||||||
|
uint8_t timespec_to_fat_tenths(struct timespec* ts);
|
||||||
|
uint16_t timespec_to_fat_time(struct timespec* ts);
|
||||||
|
uint16_t timespec_to_fat_date(struct timespec* ts);
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
class Inode;
|
||||||
|
|
||||||
|
static const size_t INODE_HASH_LENGTH = 1 << 16;
|
||||||
|
|
||||||
|
class Filesystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Filesystem(Device* device, const char* mount_path);
|
||||||
|
~Filesystem();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Block* bpb_block;
|
||||||
|
struct fat_bpb* bpb;
|
||||||
|
Device* device;
|
||||||
|
const char* mount_path;
|
||||||
|
mode_t mode_reg;
|
||||||
|
mode_t mode_dir;
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
uint32_t block_size;
|
||||||
|
uint16_t bytes_per_sector;
|
||||||
|
uint16_t root_dirent_count;
|
||||||
|
uint32_t sectors_per_fat;
|
||||||
|
uint32_t root_inode_id;
|
||||||
|
uint32_t total_sectors;
|
||||||
|
uint32_t fat_sector; // TODO: Rename to lba
|
||||||
|
uint32_t root_sector; // TODO: Rename to lba
|
||||||
|
uint32_t data_sector; // TODO: Rename to lba
|
||||||
|
uint32_t cluster_count;
|
||||||
|
uint32_t cluster_size;
|
||||||
|
uint8_t fat_type;
|
||||||
|
uint32_t eio_cluster;
|
||||||
|
uint32_t eof_cluster;
|
||||||
|
uint32_t free_search;
|
||||||
|
Inode* mru_inode;
|
||||||
|
Inode* lru_inode;
|
||||||
|
Inode* dirty_inode;
|
||||||
|
Inode* hash_inodes[INODE_HASH_LENGTH];
|
||||||
|
bool dirty;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Inode* GetInode(uint32_t inode_id, Block* dirent_block = NULL,
|
||||||
|
struct fat_dirent* dirent = NULL);
|
||||||
|
uint32_t AllocateCluster();
|
||||||
|
void FreeCluster(uint32_t cluster);
|
||||||
|
uint32_t ReadFAT(uint32_t cluster);
|
||||||
|
bool WriteFAT(uint32_t cluster, uint32_t value);
|
||||||
|
uint32_t CalculateFreeCount();
|
||||||
|
void BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Sync();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
858
fat/fsmarshall.cpp
Normal file
858
fat/fsmarshall.cpp
Normal file
|
@ -0,0 +1,858 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fsmarshall.cpp
|
||||||
|
* Sortix fsmarshall frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__sortix__)
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ioleast.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <timespec.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sortix/dirent.h>
|
||||||
|
|
||||||
|
#include <fsmarshall.h>
|
||||||
|
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "fat.h"
|
||||||
|
#include "fatfs.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "fsmarshall.h"
|
||||||
|
#include "fuse.h"
|
||||||
|
#include "inode.h"
|
||||||
|
|
||||||
|
bool RespondData(int chl, const void* ptr, size_t count)
|
||||||
|
{
|
||||||
|
return writeall(chl, ptr, count) == count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondHeader(int chl, size_t type, size_t size)
|
||||||
|
{
|
||||||
|
struct fsm_msg_header hdr;
|
||||||
|
hdr.msgtype = type;
|
||||||
|
hdr.msgsize = size;
|
||||||
|
return RespondData(chl, &hdr, sizeof(hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondMessage(int chl, size_t type, const void* ptr, size_t count)
|
||||||
|
{
|
||||||
|
return RespondHeader(chl, type, count) &&
|
||||||
|
RespondData(chl, ptr, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondError(int chl, int errnum)
|
||||||
|
{
|
||||||
|
struct fsm_resp_error body;
|
||||||
|
body.errnum = errnum;
|
||||||
|
//fprintf(stderr, "fatfs: sending error %i (%s)\n", errnum, strerror(errnum));
|
||||||
|
return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondSuccess(int chl)
|
||||||
|
{
|
||||||
|
struct fsm_resp_success body;
|
||||||
|
return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondStat(int chl, struct stat* st)
|
||||||
|
{
|
||||||
|
struct fsm_resp_stat body;
|
||||||
|
body.st = *st;
|
||||||
|
return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondStatVFS(int chl, struct statvfs* stvfs)
|
||||||
|
{
|
||||||
|
struct fsm_resp_statvfs body;
|
||||||
|
body.stvfs = *stvfs;
|
||||||
|
return RespondMessage(chl, FSM_RESP_STATVFS, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondSeek(int chl, off_t offset)
|
||||||
|
{
|
||||||
|
struct fsm_resp_lseek body;
|
||||||
|
body.offset = offset;
|
||||||
|
return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondRead(int chl, const uint8_t* buf, size_t count)
|
||||||
|
{
|
||||||
|
struct fsm_resp_read body;
|
||||||
|
body.count = count;
|
||||||
|
return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondReadlink(int chl, const uint8_t* buf, size_t count)
|
||||||
|
{
|
||||||
|
struct fsm_resp_readlink body;
|
||||||
|
body.targetlen = count;
|
||||||
|
return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondWrite(int chl, size_t count)
|
||||||
|
{
|
||||||
|
struct fsm_resp_write body;
|
||||||
|
body.count = count;
|
||||||
|
return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondOpen(int chl, ino_t ino, mode_t type)
|
||||||
|
{
|
||||||
|
struct fsm_resp_open body;
|
||||||
|
body.ino = ino;
|
||||||
|
body.type = type;
|
||||||
|
return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondMakeDir(int chl, ino_t ino)
|
||||||
|
{
|
||||||
|
struct fsm_resp_mkdir body;
|
||||||
|
body.ino = ino;
|
||||||
|
return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondReadDir(int chl, struct dirent* dirent)
|
||||||
|
{
|
||||||
|
struct fsm_resp_readdirents body;
|
||||||
|
body.ino = dirent->d_ino;
|
||||||
|
body.type = dirent->d_type;
|
||||||
|
body.namelen = dirent->d_namlen;
|
||||||
|
return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, dirent->d_name, dirent->d_namlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RespondTCGetBlob(int chl, const void* data, size_t data_size)
|
||||||
|
{
|
||||||
|
struct fsm_resp_tcgetblob body;
|
||||||
|
body.count = data_size;
|
||||||
|
return RespondMessage(chl, FSM_RESP_TCGETBLOB, &body, sizeof(body)) &&
|
||||||
|
RespondData(chl, data, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Inode* SafeGetInode(Filesystem* fs, ino_t ino)
|
||||||
|
{
|
||||||
|
if ( (uint32_t) ino != ino )
|
||||||
|
return errno = EBADF, (Inode*) NULL;
|
||||||
|
return fs->GetInode((uint32_t) ino);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) chl;
|
||||||
|
if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) )
|
||||||
|
{
|
||||||
|
if ( inode->implied_reference )
|
||||||
|
inode->implied_reference--;
|
||||||
|
else
|
||||||
|
inode->RemoteRefer();
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) chl;
|
||||||
|
if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) )
|
||||||
|
{
|
||||||
|
inode->RemoteUnref();
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
inode->Sync();
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
struct stat st;
|
||||||
|
StatInode(inode, &st);
|
||||||
|
inode->Unref();
|
||||||
|
RespondStat(chl, &st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleChangeMode(int chl, struct fsm_req_chmod* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( !inode->ChangeMode(msg->mode) )
|
||||||
|
RespondError(chl, errno);
|
||||||
|
else
|
||||||
|
RespondSuccess(chl);
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( !inode->ChangeOwner(msg->uid, msg->gid) )
|
||||||
|
RespondError(chl, errno);
|
||||||
|
else
|
||||||
|
RespondSuccess(chl);
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
inode->UTimens(msg->times);
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
||||||
|
if ( msg->size < 0 ) { RespondError(chl, EINVAL); return; }
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
inode->Truncate((uint64_t) msg->size);
|
||||||
|
inode->Unref();
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( msg->whence == SEEK_SET )
|
||||||
|
RespondSeek(chl, msg->offset);
|
||||||
|
else if ( msg->whence == SEEK_END )
|
||||||
|
{
|
||||||
|
off_t inode_size = inode->Size();
|
||||||
|
if ( (msg->offset < 0 && inode_size + msg->offset < 0) ||
|
||||||
|
(0 <= msg->offset && OFF_MAX - inode_size < msg->offset) )
|
||||||
|
RespondError(chl, EOVERFLOW);
|
||||||
|
else
|
||||||
|
RespondSeek(chl, msg->offset + inode_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
RespondError(chl, EINVAL);
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
uint8_t* buf = (uint8_t*) malloc(msg->count);
|
||||||
|
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||||
|
ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset);
|
||||||
|
inode->Unref();
|
||||||
|
if ( amount < 0 ) { free(buf); RespondError(chl, errno); return; }
|
||||||
|
RespondRead(chl, buf, amount);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleWriteAt(int chl, struct fsm_req_pwrite* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
const uint8_t* buf = (const uint8_t*) &msg[1];
|
||||||
|
ssize_t amount = inode->WriteAt(buf, msg->count, msg->offset);
|
||||||
|
inode->Unref();
|
||||||
|
if ( amount < 0 ) { RespondError(chl, errno); return; }
|
||||||
|
RespondWrite(chl, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
Inode* result = inode->Open(path, msg->flags, msg->mode & 07777);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
|
||||||
|
// TODO: Unfortunately Open does not implicitly imply RemoteRefer so we need
|
||||||
|
// to try and pretend that it does so the inode isn't destroyed early.
|
||||||
|
result->implied_reference++;
|
||||||
|
result->RemoteRefer();
|
||||||
|
result->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
Inode* result = inode->CreateDirectory(path, msg->mode & 07777);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondMakeDir(chl, result->inode_id);
|
||||||
|
result->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Encapsulate.
|
||||||
|
void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
if ( !S_ISDIR(inode->Mode()) )
|
||||||
|
{
|
||||||
|
inode->Unref();
|
||||||
|
RespondError(chl, ENOTDIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct dirent kernel_entry;
|
||||||
|
// TODO: Adjust with LFN's real limit.
|
||||||
|
uint8_t padding[sizeof(struct dirent) + 256];
|
||||||
|
};
|
||||||
|
memset(&kernel_entry, 0, sizeof(kernel_entry));
|
||||||
|
|
||||||
|
if ( inode->inode_id == inode->filesystem->root_inode_id )
|
||||||
|
{
|
||||||
|
if ( msg->rec_num < 2 )
|
||||||
|
{
|
||||||
|
const char* name = msg->rec_num ? ".." : ".";
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
||||||
|
kernel_entry.d_ino = inode->inode_id;
|
||||||
|
kernel_entry.d_dev = 0;
|
||||||
|
kernel_entry.d_type = DT_DIR;
|
||||||
|
kernel_entry.d_namlen = name_len;
|
||||||
|
memcpy(kernel_entry.d_name, name, name_len);
|
||||||
|
size_t dname_offset = offsetof(struct dirent, d_name);
|
||||||
|
padding[dname_offset + kernel_entry.d_namlen] = '\0';
|
||||||
|
RespondReadDir(chl, &kernel_entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg->rec_num -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cluster = inode->first_cluster;
|
||||||
|
uint8_t sector = 0;
|
||||||
|
uint16_t offset = 0;
|
||||||
|
Block* block = NULL;
|
||||||
|
while ( inode->Iterate(&block, &cluster, §or, &offset) )
|
||||||
|
{
|
||||||
|
const uint8_t* block_data = block->block_data + offset;
|
||||||
|
const struct fat_dirent* entry = (const struct fat_dirent*) block_data;
|
||||||
|
if ( !entry->name[0] )
|
||||||
|
break;
|
||||||
|
if ( (unsigned char) entry->name[0] != 0xE5 &&
|
||||||
|
!(entry->attributes & FAT_ATTRIBUTE_VOLUME_ID) &&
|
||||||
|
!(msg->rec_num--) )
|
||||||
|
{
|
||||||
|
char name[8 + 1 + 3 + 1];
|
||||||
|
decode_8_3(entry->name, name);
|
||||||
|
size_t name_len = strnlen(entry->name, sizeof(entry->name));
|
||||||
|
uint8_t file_type =
|
||||||
|
entry->attributes & FAT_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG;
|
||||||
|
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
||||||
|
kernel_entry.d_ino = entry->cluster_low | entry->cluster_high << 16;
|
||||||
|
kernel_entry.d_dev = 0;
|
||||||
|
kernel_entry.d_type = file_type;
|
||||||
|
kernel_entry.d_namlen = name_len;
|
||||||
|
memcpy(kernel_entry.d_name, name, name_len);
|
||||||
|
size_t dname_offset = offsetof(struct dirent, d_name);
|
||||||
|
padding[dname_offset + kernel_entry.d_namlen] = '\0';
|
||||||
|
block->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
RespondReadDir(chl, &kernel_entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offset += sizeof(struct fat_dirent);
|
||||||
|
}
|
||||||
|
int errnum = errno;
|
||||||
|
if ( block )
|
||||||
|
block->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
if ( errnum )
|
||||||
|
{
|
||||||
|
RespondError(chl, errnum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_entry.d_reclen = sizeof(kernel_entry);
|
||||||
|
RespondReadDir(chl, &kernel_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
RespondError(chl, ENOTTY);
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->Unlink(path, false);
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->RemoveDirectory(path);
|
||||||
|
free(path);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
Inode* dest = SafeGetInode(fs, msg->linkino);
|
||||||
|
if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->namelen+1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, pathraw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->Link(path, dest, false);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
dest->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||||
|
if ( !inode ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
char* dest_raw = (char*) &(msg[1]);
|
||||||
|
char* dest = (char*) malloc(msg->targetlen + 1);
|
||||||
|
if ( !dest )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(dest, dest_raw, msg->targetlen);
|
||||||
|
dest[msg->targetlen] = '\0';
|
||||||
|
|
||||||
|
char* path_raw = (char*) dest_raw + msg->targetlen;
|
||||||
|
char* path = (char*) malloc(msg->namelen + 1);
|
||||||
|
if ( !path )
|
||||||
|
{
|
||||||
|
free(dest);
|
||||||
|
RespondError(chl, errno);
|
||||||
|
inode->Unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, path_raw, msg->namelen);
|
||||||
|
path[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
bool result = inode->Symlink(path, dest);
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
free(dest);
|
||||||
|
inode->Unref();
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) msg;
|
||||||
|
(void) fs;
|
||||||
|
RespondError(chl, EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
char* pathraw = (char*) &(msg[1]);
|
||||||
|
char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1);
|
||||||
|
if ( !path ) { RespondError(chl, errno); return; }
|
||||||
|
memcpy(path, pathraw, msg->oldnamelen);
|
||||||
|
path[msg->oldnamelen] = '\0';
|
||||||
|
memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen);
|
||||||
|
path[msg->oldnamelen + 1 + msg->newnamelen] = '\0';
|
||||||
|
|
||||||
|
const char* oldname = path;
|
||||||
|
const char* newname = path + msg->oldnamelen + 1;
|
||||||
|
|
||||||
|
Inode* olddir = SafeGetInode(fs, msg->olddirino);
|
||||||
|
if ( !olddir ) { free(path); RespondError(chl, errno); return; }
|
||||||
|
Inode* newdir = SafeGetInode(fs, msg->newdirino);
|
||||||
|
if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
bool result = newdir->Rename(olddir, oldname, newname);
|
||||||
|
|
||||||
|
newdir->Unref();
|
||||||
|
olddir->Unref();
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
if ( !result ) { RespondError(chl, errno); return; }
|
||||||
|
|
||||||
|
RespondSuccess(chl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
(void) msg;
|
||||||
|
struct statvfs stvfs;
|
||||||
|
stvfs.f_bsize = fs->cluster_size;
|
||||||
|
stvfs.f_frsize = fs->cluster_size;
|
||||||
|
stvfs.f_blocks = fs->cluster_count;
|
||||||
|
// TODO: Locate FsInfo and count on FAT12/FAT16.
|
||||||
|
stvfs.f_bfree = fs->CalculateFreeCount();
|
||||||
|
stvfs.f_bavail = stvfs.f_bfree;
|
||||||
|
stvfs.f_files = stvfs.f_blocks;
|
||||||
|
stvfs.f_ffree = stvfs.f_bfree;
|
||||||
|
stvfs.f_favail = stvfs.f_files;
|
||||||
|
stvfs.f_fsid = 0;
|
||||||
|
stvfs.f_flag = 0;
|
||||||
|
if ( !fs->device->write )
|
||||||
|
stvfs.f_flag |= ST_RDONLY;
|
||||||
|
stvfs.f_namemax = 8 + 3; // TODO: Long file name support.
|
||||||
|
RespondStatVFS(chl, &stvfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleTCGetBlob(int chl, struct fsm_req_tcgetblob* msg, Filesystem* fs)
|
||||||
|
{
|
||||||
|
char* nameraw = (char*) &(msg[1]);
|
||||||
|
char* name = (char*) malloc(msg->namelen + 1);
|
||||||
|
if ( !name )
|
||||||
|
return (void) RespondError(chl, errno);
|
||||||
|
memcpy(name, nameraw, msg->namelen);
|
||||||
|
name[msg->namelen] = '\0';
|
||||||
|
|
||||||
|
static const char index[] =
|
||||||
|
"device-path\0filesystem-type\0filesystem-uuid\0mount-path\0"
|
||||||
|
"fat-size\0volume-id\0volume-label\0";
|
||||||
|
if ( !strcmp(name, "") )
|
||||||
|
RespondTCGetBlob(chl, index, sizeof(index) - 1);
|
||||||
|
else if ( !strcmp(name, "device-path") )
|
||||||
|
RespondTCGetBlob(chl, fs->device->path, strlen(fs->device->path));
|
||||||
|
else if ( !strcmp(name, "filesystem-type") )
|
||||||
|
RespondTCGetBlob(chl, "fat", strlen("fat"));
|
||||||
|
else if ( !strcmp(name, "fat-size") )
|
||||||
|
{
|
||||||
|
const char* str = fs->fat_type == 32 ? "32" :
|
||||||
|
fs->fat_type == 16 ? "16" : "12";
|
||||||
|
RespondTCGetBlob(chl, str, strlen(str));
|
||||||
|
}
|
||||||
|
else if ( !strcmp(name, "filesystem-uuid") )
|
||||||
|
{
|
||||||
|
unsigned char uuid[16];
|
||||||
|
if ( fs->fat_type == 32 )
|
||||||
|
{
|
||||||
|
memcpy(uuid, &fs->bpb->fat32_volume_id, 4);
|
||||||
|
memcpy(uuid + 4, &fs->bpb->fat32_volume_label, 11);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(uuid, &fs->bpb->fat12_volume_id, 4);
|
||||||
|
memcpy(uuid + 4, &fs->bpb->fat12_volume_label, 11);
|
||||||
|
}
|
||||||
|
uuid[15] = '\0';
|
||||||
|
RespondTCGetBlob(chl, uuid, sizeof(uuid));
|
||||||
|
}
|
||||||
|
else if ( !strcmp(name, "volume-id") )
|
||||||
|
{
|
||||||
|
if ( fs->fat_type == 32 )
|
||||||
|
RespondTCGetBlob(chl, &fs->bpb->fat32_volume_id, 4);
|
||||||
|
else
|
||||||
|
RespondTCGetBlob(chl, &fs->bpb->fat12_volume_id, 4);
|
||||||
|
}
|
||||||
|
else if ( !strcmp(name, "volume-label") )
|
||||||
|
{
|
||||||
|
if ( fs->fat_type == 32 )
|
||||||
|
RespondTCGetBlob(chl, &fs->bpb->fat32_volume_label, 11);
|
||||||
|
else
|
||||||
|
RespondTCGetBlob(chl, &fs->bpb->fat12_volume_label, 11);
|
||||||
|
}
|
||||||
|
else if ( !strcmp(name, "mount-path") )
|
||||||
|
RespondTCGetBlob(chl, fs->mount_path, strlen(fs->mount_path));
|
||||||
|
else
|
||||||
|
RespondError(chl, ENOENT);
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
|
||||||
|
{
|
||||||
|
request_uid = hdr->uid;
|
||||||
|
request_gid = hdr->gid;
|
||||||
|
typedef void (*handler_t)(int, void*, Filesystem*);
|
||||||
|
handler_t handlers[FSM_MSG_NUM] = { NULL };
|
||||||
|
handlers[FSM_REQ_SYNC] = (handler_t) HandleSync;
|
||||||
|
handlers[FSM_REQ_STAT] = (handler_t) HandleStat;
|
||||||
|
handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode;
|
||||||
|
handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner;
|
||||||
|
handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate;
|
||||||
|
handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek;
|
||||||
|
handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt;
|
||||||
|
handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen;
|
||||||
|
handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir;
|
||||||
|
handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt;
|
||||||
|
handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY;
|
||||||
|
handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens;
|
||||||
|
handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir;
|
||||||
|
handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir;
|
||||||
|
handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink;
|
||||||
|
handlers[FSM_REQ_LINK] = (handler_t) HandleLink;
|
||||||
|
handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink;
|
||||||
|
handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink;
|
||||||
|
handlers[FSM_REQ_RENAME] = (handler_t) HandleRename;
|
||||||
|
handlers[FSM_REQ_REFER] = (handler_t) HandleRefer;
|
||||||
|
handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref;
|
||||||
|
handlers[FSM_REQ_STATVFS] = (handler_t) HandleStatVFS;
|
||||||
|
handlers[FSM_REQ_TCGETBLOB] = (handler_t) HandleTCGetBlob;
|
||||||
|
if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] )
|
||||||
|
{
|
||||||
|
warn("message type %zu not supported\n", hdr->msgtype);
|
||||||
|
RespondError(chl, ENOTSUP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t body_buffer[65536];
|
||||||
|
uint8_t* body = body_buffer;
|
||||||
|
if ( sizeof(body_buffer) < hdr->msgsize )
|
||||||
|
{
|
||||||
|
body = (uint8_t*) mmap(NULL, hdr->msgsize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if ( (void*) body == MAP_FAILED )
|
||||||
|
{
|
||||||
|
RespondError(chl, errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( readall(chl, body, hdr->msgsize) == hdr->msgsize )
|
||||||
|
handlers[hdr->msgtype](chl, body, fs);
|
||||||
|
else
|
||||||
|
RespondError(chl, errno);
|
||||||
|
if ( sizeof(body_buffer) < hdr->msgsize )
|
||||||
|
munmap(body, hdr->msgsize);
|
||||||
|
}
|
||||||
|
static volatile bool should_terminate = false;
|
||||||
|
|
||||||
|
void TerminationHandler(int)
|
||||||
|
{
|
||||||
|
should_terminate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ready(void)
|
||||||
|
{
|
||||||
|
const char* readyfd_env = getenv("READYFD");
|
||||||
|
if ( !readyfd_env )
|
||||||
|
return;
|
||||||
|
int readyfd = atoi(readyfd_env);
|
||||||
|
char c = '\n';
|
||||||
|
write(readyfd, &c, 1);
|
||||||
|
close(readyfd);
|
||||||
|
unsetenv("READYFD");
|
||||||
|
}
|
||||||
|
|
||||||
|
int fsmarshall_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev)
|
||||||
|
{
|
||||||
|
(void) argv0;
|
||||||
|
|
||||||
|
// Stat the root directory;
|
||||||
|
struct stat root_dir_st;
|
||||||
|
memset(&root_dir_st, 0, sizeof(root_dir_st));
|
||||||
|
root_dir_st.st_ino = fs->root_inode_id;
|
||||||
|
root_dir_st.st_mode = S_IFDIR | 0755;
|
||||||
|
|
||||||
|
// Create a filesystem server connected to the kernel that we'll listen on.
|
||||||
|
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_dir_st, 0);
|
||||||
|
if ( serverfd < 0 )
|
||||||
|
err(1, "%s", mount_path);
|
||||||
|
|
||||||
|
// Make sure the server isn't unexpectedly killed and data is lost.
|
||||||
|
signal(SIGINT, TerminationHandler);
|
||||||
|
signal(SIGTERM, TerminationHandler);
|
||||||
|
signal(SIGQUIT, TerminationHandler);
|
||||||
|
|
||||||
|
// Become a background process in its own process group by default.
|
||||||
|
if ( !foreground )
|
||||||
|
{
|
||||||
|
pid_t child_pid = fork();
|
||||||
|
if ( child_pid < 0 )
|
||||||
|
err(1, "fork");
|
||||||
|
if ( child_pid )
|
||||||
|
exit(0);
|
||||||
|
setpgid(0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ready();
|
||||||
|
|
||||||
|
dev->SpawnSyncThread();
|
||||||
|
|
||||||
|
// Listen for filesystem messages and sync the filesystem every few seconds.
|
||||||
|
struct timespec last_sync_at;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &last_sync_at);
|
||||||
|
int channel;
|
||||||
|
while ( 0 <= (channel = accept(serverfd, NULL, NULL)) )
|
||||||
|
{
|
||||||
|
if ( should_terminate )
|
||||||
|
break;
|
||||||
|
struct fsm_msg_header hdr;
|
||||||
|
size_t amount;
|
||||||
|
if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) )
|
||||||
|
{
|
||||||
|
//warn("incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
|
||||||
|
errno = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
HandleIncomingMessage(channel, &hdr, fs);
|
||||||
|
close(channel);
|
||||||
|
|
||||||
|
if ( dev->write && !dev->has_sync_thread )
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
if ( 5 <= timespec_sub(now, last_sync_at).tv_sec )
|
||||||
|
{
|
||||||
|
fs->Sync();
|
||||||
|
last_sync_at = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with FAT concept.
|
||||||
|
// Garbage collect all open inode references.
|
||||||
|
while ( fs->mru_inode )
|
||||||
|
{
|
||||||
|
Inode* inode = fs->mru_inode;
|
||||||
|
if ( inode->remote_reference_count )
|
||||||
|
inode->RemoteUnref();
|
||||||
|
else if ( inode->reference_count )
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync the filesystem before shutting down.
|
||||||
|
if ( dev->write )
|
||||||
|
fs->Sync();
|
||||||
|
|
||||||
|
close(serverfd);
|
||||||
|
|
||||||
|
delete fs;
|
||||||
|
delete dev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
31
fat/fsmarshall.h
Normal file
31
fat/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
|
618
fat/fuse.cpp
Normal file
618
fat/fuse.cpp
Normal file
|
@ -0,0 +1,618 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fuse.cpp
|
||||||
|
* FUSE frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__sortix__)
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define FUSE_USE_VERSION 26
|
||||||
|
#include <fuse.h>
|
||||||
|
|
||||||
|
#include "ext-constants.h"
|
||||||
|
#include "ext-structs.h"
|
||||||
|
|
||||||
|
#include "blockgroup.h"
|
||||||
|
#include "block.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "fatfs.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "fuse.h"
|
||||||
|
#include "inode.h"
|
||||||
|
|
||||||
|
struct fat_fuse_ctx
|
||||||
|
{
|
||||||
|
Device* dev;
|
||||||
|
Filesystem* fs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef S_SETABLE
|
||||||
|
#define S_SETABLE 02777
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FUSE_FS (((struct fat_fuse_ctx*) (fuse_get_context()->private_data))->fs)
|
||||||
|
|
||||||
|
void* fat_fuse_init(struct fuse_conn_info* /*conn*/)
|
||||||
|
{
|
||||||
|
return fuse_get_context()->private_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fat_fuse_destroy(void* fs_private)
|
||||||
|
{
|
||||||
|
struct fat_fuse_ctx* fat_fuse_ctx = (struct fat_fuse_ctx*) fs_private;
|
||||||
|
while ( fat_fuse_ctx->fs->mru_inode )
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_ctx->fs->mru_inode;
|
||||||
|
if ( inode->remote_reference_count )
|
||||||
|
inode->RemoteUnref();
|
||||||
|
else if ( inode->reference_count )
|
||||||
|
inode->Unref();
|
||||||
|
}
|
||||||
|
fat_fuse_ctx->fs->Sync();
|
||||||
|
fat_fuse_ctx->dev->Sync();
|
||||||
|
delete fat_fuse_ctx->fs; fat_fuse_ctx->fs = NULL;
|
||||||
|
delete fat_fuse_ctx->dev; fat_fuse_ctx->dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inode* fat_fuse_resolve_path(const char* path)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode(FAT_ROOT_INO);
|
||||||
|
if ( !inode )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
while ( path[0] )
|
||||||
|
{
|
||||||
|
if ( *path == '/' )
|
||||||
|
{
|
||||||
|
if ( !FAT_S_ISDIR(inode->Mode()) )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t elem_len = strcspn(path, "/");
|
||||||
|
char* elem = strndup(path, elem_len);
|
||||||
|
if ( !elem )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path += elem_len;
|
||||||
|
Inode* next = inode->Open(elem, O_RDONLY, 0);
|
||||||
|
free(elem);
|
||||||
|
inode->Unref();
|
||||||
|
if ( !next )
|
||||||
|
return NULL;
|
||||||
|
inode = next;
|
||||||
|
}
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes that the path doesn't end with / unless it's the root directory.
|
||||||
|
Inode* fat_fuse_parent_dir(const char** path_ptr)
|
||||||
|
{
|
||||||
|
const char* path = *path_ptr;
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode(FAT_ROOT_INO);
|
||||||
|
if ( !inode )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
while ( strchr(path, '/') )
|
||||||
|
{
|
||||||
|
if ( *path == '/' )
|
||||||
|
{
|
||||||
|
if ( !FAT_S_ISDIR(inode->Mode()) )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t elem_len = strcspn(path, "/");
|
||||||
|
char* elem = strndup(path, elem_len);
|
||||||
|
if ( !elem )
|
||||||
|
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||||
|
path += elem_len;
|
||||||
|
Inode* next = inode->Open(elem, O_RDONLY, 0);
|
||||||
|
free(elem);
|
||||||
|
inode->Unref();
|
||||||
|
if ( !next )
|
||||||
|
return (Inode*) NULL;
|
||||||
|
inode = next;
|
||||||
|
}
|
||||||
|
*path_ptr = *path ? path : ".";
|
||||||
|
assert(!strchr(*path_ptr, '/'));
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_getattr(const char* path, struct stat* st)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
StatInode(inode, st);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_fgetattr(const char* /*path*/, struct stat* st,
|
||||||
|
struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
StatInode(inode, st);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_readlink(const char* path, char* buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FAT_S_ISLNK(inode->Mode()) )
|
||||||
|
return inode->Unref(), -(errno = EINVAL);
|
||||||
|
if ( !bufsize )
|
||||||
|
return inode->Unref(), -(errno = EINVAL);
|
||||||
|
ssize_t amount = inode->ReadAt((uint8_t*) buf, bufsize, 0);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return inode->Unref(), -errno;
|
||||||
|
buf[(size_t) amount < bufsize ? (size_t) amount : bufsize - 1] = '\0';
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_mknod(const char* path, mode_t mode, dev_t dev)
|
||||||
|
{
|
||||||
|
(void) path;
|
||||||
|
(void) mode;
|
||||||
|
(void) dev;
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_mkdir(const char* path, mode_t mode)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
Inode* newdir = inode->CreateDirectory(path, ExtModeFromHostMode(mode));
|
||||||
|
inode->Unref();
|
||||||
|
if ( !newdir )
|
||||||
|
return -errno;
|
||||||
|
newdir->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_unlink(const char* path)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
bool success = inode->Unlink(path, false);
|
||||||
|
inode->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_rmdir(const char* path)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
bool success = inode->RemoveDirectory(path);
|
||||||
|
inode->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_symlink(const char* oldname, const char* newname)
|
||||||
|
{
|
||||||
|
Inode* newdir = fat_fuse_parent_dir(&newname);
|
||||||
|
if ( !newdir )
|
||||||
|
return -errno;
|
||||||
|
bool success = newdir->Symlink(newname, oldname);
|
||||||
|
newdir->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_rename(const char* oldname, const char* newname)
|
||||||
|
{
|
||||||
|
Inode* olddir = fat_fuse_parent_dir(&oldname);
|
||||||
|
if ( !olddir )
|
||||||
|
return -errno;
|
||||||
|
Inode* newdir = fat_fuse_parent_dir(&newname);
|
||||||
|
if ( !newdir )
|
||||||
|
return olddir->Unref(), -errno;
|
||||||
|
bool success = newdir->Rename(olddir, oldname, newname);
|
||||||
|
newdir->Unref();
|
||||||
|
olddir->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_link(const char* oldname, const char* newname)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(oldname);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
Inode* newdir = fat_fuse_parent_dir(&newname);
|
||||||
|
if ( !newdir )
|
||||||
|
return inode->Unref(), -errno;
|
||||||
|
bool success = inode->Link(newname, inode, false);
|
||||||
|
newdir->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
return success ? 0 : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_chmod(const char* path, mode_t mode)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
uint32_t req_mode = ExtModeFromHostMode(mode);
|
||||||
|
uint32_t old_mode = inode->Mode();
|
||||||
|
uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE);
|
||||||
|
inode->SetMode(new_mode);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_chown(const char* path, uid_t owner, gid_t group)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->SetUserId((uint32_t) owner);
|
||||||
|
inode->SetGroupId((uint32_t) group);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_truncate(const char* path, off_t size)
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->Truncate((uint64_t) size);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_ftruncate(const char* /*path*/, off_t size,
|
||||||
|
struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->Truncate((uint64_t) size);
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_open(const char* path, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
int flags = fi->flags;
|
||||||
|
Inode* dir = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !dir )
|
||||||
|
return -errno;
|
||||||
|
Inode* result = dir->Open(path, flags, 0);
|
||||||
|
dir->Unref();
|
||||||
|
if ( !result )
|
||||||
|
return -errno;
|
||||||
|
fi->fh = (uint64_t) result->inode_id;
|
||||||
|
fi->keep_cache = 1;
|
||||||
|
result->RemoteRefer();
|
||||||
|
result->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_access(const char* path, int mode)
|
||||||
|
{
|
||||||
|
Inode* dir = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !dir )
|
||||||
|
return -errno;
|
||||||
|
Inode* result = dir->Open(path, O_RDONLY, 0);
|
||||||
|
dir->Unref();
|
||||||
|
if ( !result )
|
||||||
|
return -errno;
|
||||||
|
(void) mode;
|
||||||
|
result->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
int flags = fi->flags | O_CREAT;
|
||||||
|
Inode* inode = fat_fuse_parent_dir(&path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
Inode* result = inode->Open(path, flags, ExtModeFromHostMode(mode));
|
||||||
|
inode->Unref();
|
||||||
|
if ( !result )
|
||||||
|
return -errno;
|
||||||
|
fi->fh = (uint64_t) result->inode_id;
|
||||||
|
fi->keep_cache = 1;
|
||||||
|
result->RemoteRefer();
|
||||||
|
result->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_opendir(const char* path, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
return fat_fuse_open(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_read(const char* /*path*/, char* buf, size_t count, off_t offset,
|
||||||
|
struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
if ( INT_MAX < count )
|
||||||
|
count = INT_MAX;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset);
|
||||||
|
inode->Unref();
|
||||||
|
return 0 <= result ? (int) result : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_write(const char* /*path*/, const char* buf, size_t count,
|
||||||
|
off_t offset, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
if ( INT_MAX < count )
|
||||||
|
count = INT_MAX;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
ssize_t result = inode->WriteAt((const uint8_t*) buf, count, offset);
|
||||||
|
inode->Unref();
|
||||||
|
return 0 <= result ? (int) result : -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_statfs(const char* /*path*/, struct statvfs* stvfs)
|
||||||
|
{
|
||||||
|
memset(stvfs, 0, sizeof(*stvfs));
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
stvfs->f_bsize = fs->block_size;
|
||||||
|
stvfs->f_frsize = fs->block_size;
|
||||||
|
stvfs->f_blocks = fs->num_blocks;
|
||||||
|
stvfs->f_bfree = fs->sb->s_free_blocks_count;
|
||||||
|
stvfs->f_bavail = fs->sb->s_free_blocks_count;
|
||||||
|
stvfs->f_files = fs->num_inodes;
|
||||||
|
stvfs->f_ffree = fs->sb->s_free_inodes_count;
|
||||||
|
stvfs->f_favail = fs->sb->s_free_inodes_count;
|
||||||
|
stvfs->f_ffree = fs->sb->s_free_inodes_count;
|
||||||
|
stvfs->f_fsid = 0;
|
||||||
|
stvfs->f_flag = 0;
|
||||||
|
if ( !fs->device->write )
|
||||||
|
stvfs->f_flag |= ST_RDONLY;
|
||||||
|
stvfs->f_namemax = 255;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_flush(const char* /*path*/, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
inode->Sync();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_release(const char* /*path*/, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
inode->RemoteUnref();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_releasedir(const char* path, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
return fat_fuse_release(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
inode->Sync();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int fat_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
return fat_fuse_sync(path, data, fi);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_setxattr(const char *, const char *, const char *, size_t, int)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_getxattr(const char *, const char *, char *, size_t)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_listxattr(const char *, char *, size_t)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*int fat_fuse_removexattr(const char *, const char *)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fat_fuse_readdir(const char* /*path*/, void* buf, fuse_fill_dir_t filler,
|
||||||
|
off_t rec_num, struct fuse_file_info* fi)
|
||||||
|
{
|
||||||
|
Filesystem* fs = FUSE_FS;
|
||||||
|
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !S_ISDIR(inode->Mode()) )
|
||||||
|
return inode->Unref(), -(errno = ENOTDIR);
|
||||||
|
|
||||||
|
uint64_t file_size = inode->Size();
|
||||||
|
uint64_t offset = 0;
|
||||||
|
Block* block = NULL;
|
||||||
|
uint64_t block_id = 0;
|
||||||
|
while ( offset < file_size )
|
||||||
|
{
|
||||||
|
uint64_t entry_block_id = offset / fs->block_size;
|
||||||
|
uint64_t entry_block_offset = offset % fs->block_size;
|
||||||
|
if ( block && block_id != entry_block_id )
|
||||||
|
block->Unref(),
|
||||||
|
block = NULL;
|
||||||
|
if ( !block && !(block = inode->GetBlock(block_id = entry_block_id)) )
|
||||||
|
return inode->Unref(), -errno;
|
||||||
|
const uint8_t* block_data = block->block_data + entry_block_offset;
|
||||||
|
const struct ext_dirent* entry = (const struct ext_dirent*) block_data;
|
||||||
|
if ( entry->inode && entry->name_len && (!rec_num || !rec_num--) )
|
||||||
|
{
|
||||||
|
char* entry_name = strndup(entry->name, entry->name_len);
|
||||||
|
if ( !entry_name )
|
||||||
|
return block->Unref(), inode->Unref(), -errno;
|
||||||
|
memcpy(entry_name, entry->name, entry->name_len);
|
||||||
|
bool full = filler(buf, entry_name, NULL, 0);
|
||||||
|
free(entry_name);
|
||||||
|
if ( full )
|
||||||
|
{
|
||||||
|
block->Unref();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += entry->reclen;
|
||||||
|
}
|
||||||
|
if ( block )
|
||||||
|
block->Unref();
|
||||||
|
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int fat_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fat_fuse_utimens(const char* path, const struct timespec tv[2])
|
||||||
|
{
|
||||||
|
Inode* inode = fat_fuse_resolve_path(path);
|
||||||
|
if ( !inode )
|
||||||
|
return -errno;
|
||||||
|
if ( !FUSE_FS->device->write )
|
||||||
|
return inode->Unref(), -(errno = EROFS);
|
||||||
|
inode->BeginWrite();
|
||||||
|
inode->data->i_atime = tv[0].tv_sec;
|
||||||
|
inode->data->i_mtime = tv[1].tv_sec;
|
||||||
|
inode->FinishWrite();
|
||||||
|
inode->Unref();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int fat_fuse_bmap(const char*, size_t blocksize, uint64_t* idx)
|
||||||
|
{
|
||||||
|
return -(errno = ENOSYS);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fat_fuse_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev)
|
||||||
|
{
|
||||||
|
struct fuse_operations operations;
|
||||||
|
memset(&operations, 0, sizeof(operations));
|
||||||
|
|
||||||
|
operations.access = fat_fuse_access;
|
||||||
|
operations.chmod = fat_fuse_chmod;
|
||||||
|
operations.chown = fat_fuse_chown;
|
||||||
|
operations.create = fat_fuse_create;
|
||||||
|
operations.destroy = fat_fuse_destroy;
|
||||||
|
operations.fgetattr = fat_fuse_fgetattr;
|
||||||
|
operations.flush = fat_fuse_flush;
|
||||||
|
operations.fsync = fat_fuse_fsync;
|
||||||
|
operations.ftruncate = fat_fuse_ftruncate;
|
||||||
|
operations.getattr = fat_fuse_getattr;
|
||||||
|
operations.init = fat_fuse_init;
|
||||||
|
operations.link = fat_fuse_link;
|
||||||
|
operations.mkdir = fat_fuse_mkdir;
|
||||||
|
operations.mknod = fat_fuse_mknod;
|
||||||
|
operations.opendir = fat_fuse_opendir;
|
||||||
|
operations.open = fat_fuse_open;
|
||||||
|
operations.readdir = fat_fuse_readdir;
|
||||||
|
operations.read = fat_fuse_read;
|
||||||
|
operations.readlink = fat_fuse_readlink;
|
||||||
|
operations.releasedir = fat_fuse_releasedir;
|
||||||
|
operations.release = fat_fuse_release;
|
||||||
|
operations.rename = fat_fuse_rename;
|
||||||
|
operations.rmdir = fat_fuse_rmdir;
|
||||||
|
operations.statfs = fat_fuse_statfs;
|
||||||
|
operations.symlink = fat_fuse_symlink;
|
||||||
|
operations.truncate = fat_fuse_truncate;
|
||||||
|
operations.unlink = fat_fuse_unlink;
|
||||||
|
operations.utimens = fat_fuse_utimens;
|
||||||
|
operations.write = fat_fuse_write;
|
||||||
|
|
||||||
|
operations.flag_nullpath_ok = 1;
|
||||||
|
operations.flag_nopath = 1;
|
||||||
|
|
||||||
|
char* argv_fuse[] =
|
||||||
|
{
|
||||||
|
(char*) argv0,
|
||||||
|
(char*) "-s",
|
||||||
|
(char*) mount_path,
|
||||||
|
(char*) NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1;
|
||||||
|
|
||||||
|
struct fat_fuse_ctx fat_fuse_ctx;
|
||||||
|
fat_fuse_ctx.fs = fs;
|
||||||
|
fat_fuse_ctx.dev = dev;
|
||||||
|
|
||||||
|
(void) foreground;
|
||||||
|
|
||||||
|
return fuse_main(argc_fuse, argv_fuse, &operations, &fat_fuse_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
32
fat/fuse.h
Normal file
32
fat/fuse.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fuse.h
|
||||||
|
* FUSE frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSE_H
|
||||||
|
#define FUSE_H
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
class Filesystem;
|
||||||
|
|
||||||
|
int fat_fuse_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev);
|
||||||
|
|
||||||
|
#endif
|
1015
fat/inode.cpp
Normal file
1015
fat/inode.cpp
Normal file
File diff suppressed because it is too large
Load diff
90
fat/inode.h
Normal file
90
fat/inode.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* inode.h
|
||||||
|
* Filesystem inode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INODE_H
|
||||||
|
#define INODE_H
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
class Filesystem;
|
||||||
|
|
||||||
|
class Inode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Inode(Filesystem* filesystem, uint32_t inode_id);
|
||||||
|
~Inode();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Inode* prev_inode;
|
||||||
|
Inode* next_inode;
|
||||||
|
Inode* prev_hashed;
|
||||||
|
Inode* next_hashed;
|
||||||
|
Inode* prev_dirty;
|
||||||
|
Inode* next_dirty;
|
||||||
|
Block* data_block;
|
||||||
|
struct fat_dirent* dirent;
|
||||||
|
struct fat_dirent deleted_dirent;
|
||||||
|
uint32_t first_cluster;
|
||||||
|
Filesystem* filesystem;
|
||||||
|
size_t reference_count;
|
||||||
|
size_t remote_reference_count;
|
||||||
|
size_t implied_reference;
|
||||||
|
uint32_t inode_id;
|
||||||
|
bool dirty;
|
||||||
|
bool deleted;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t Mode();
|
||||||
|
uint32_t UserId();
|
||||||
|
uint32_t GroupId();
|
||||||
|
uint64_t Size();
|
||||||
|
void UTimens(const struct timespec times[2]);
|
||||||
|
bool ChangeMode(mode_t mode);
|
||||||
|
bool ChangeOwner(uid_t uid, gid_t gid);
|
||||||
|
bool Truncate(uint64_t new_size);
|
||||||
|
Block* GetClusterSector(uint32_t cluster, uint8_t sector);
|
||||||
|
bool Iterate(Block** block_ptr, uint32_t* cluster_ptr,
|
||||||
|
uint8_t* sector_ptr, uint16_t* offset);
|
||||||
|
uint32_t SeekCluster(uint32_t cluster_id);
|
||||||
|
Inode* Open(const char* elem, int flags, mode_t mode);
|
||||||
|
bool Link(const char* elem, Inode* dest, bool directories);
|
||||||
|
bool Symlink(const char* elem, const char* dest);
|
||||||
|
bool Unlink(const char* elem, bool directories, bool force=false);
|
||||||
|
Inode* UnlinkKeep(const char* elem, bool directories, bool force=false);
|
||||||
|
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
|
||||||
|
ssize_t WriteAt(const uint8_t* buffer, size_t count, off_t offset);
|
||||||
|
bool Rename(Inode* olddir, const char* oldname, const char* newname);
|
||||||
|
Inode* CreateDirectory(const char* path, mode_t mode);
|
||||||
|
bool RemoveDirectory(const char* path);
|
||||||
|
bool IsEmptyDirectory();
|
||||||
|
void Refer();
|
||||||
|
void Unref();
|
||||||
|
void RemoteRefer();
|
||||||
|
void RemoteUnref();
|
||||||
|
void Sync();
|
||||||
|
void BeginWrite();
|
||||||
|
void FinishWrite();
|
||||||
|
void Modified();
|
||||||
|
void Use();
|
||||||
|
void Unlink();
|
||||||
|
void Prelink();
|
||||||
|
void Delete();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
150
fat/ioleast.h
Normal file
150
fat/ioleast.h
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* ioleast.h
|
||||||
|
* Versions of {,p}{read,write} that don't return until it has returned as much
|
||||||
|
* data as requested, end of file, or an error occurs. This is sometimes needed
|
||||||
|
* as read(2) and write(2) is not always guaranteed to fill up the entire
|
||||||
|
* buffer or write it all.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
|
||||||
|
#define SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
|
||||||
|
|
||||||
|
#if defined(__sortix__) || defined(__sortix_libc__)
|
||||||
|
|
||||||
|
#include_next <ioleast.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if !defined(EEOF) && defined(EIO)
|
||||||
|
#define EEOF EIO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t readleast(int fd, void* buf_ptr, size_t least, size_t max)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = read(fd, buf + done, max - done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t writeleast(int fd, const void* buf_ptr, size_t least, size_t max)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = write(fd, buf + done, max - done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t preadleast(int fd, void* buf_ptr, size_t least, size_t max, off_t off)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = pread(fd, buf + done, max - done, off + done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t pwriteleast(int fd, const void* buf_ptr, size_t least, size_t max, off_t off)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = pwrite(fd, buf + done, max - done, off + done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t readall(int fd, void* buf, size_t count)
|
||||||
|
{
|
||||||
|
return readleast(fd, buf, count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t writeall(int fd, const void* buf, size_t count)
|
||||||
|
{
|
||||||
|
return writeleast(fd, buf, count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t preadall(int fd, void* buf, size_t count, off_t off)
|
||||||
|
{
|
||||||
|
return preadleast(fd, buf, count, count, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
|
||||||
|
{
|
||||||
|
return pwriteleast(fd, buf, count, count, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
49
fat/util.h
Normal file
49
fat/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
init/.gitignore
vendored
1
init/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
init
|
init
|
||||||
|
service
|
||||||
*.o
|
*.o
|
||||||
|
|
|
@ -8,26 +8,26 @@ CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
||||||
|
|
||||||
BINARY=init
|
BINARIES=\
|
||||||
|
init \
|
||||||
|
service \
|
||||||
|
|
||||||
OBJS=\
|
MANPAGES8=\
|
||||||
init.o \
|
init.8 \
|
||||||
|
service.8 \
|
||||||
|
|
||||||
all: $(BINARY)
|
all: $(BINARIES)
|
||||||
|
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean
|
||||||
|
|
||||||
$(BINARY): $(OBJS)
|
%: %.c
|
||||||
$(CC) $(CFLAGS) $(OBJS) -o $(BINARY) -lmount $(LIBS)
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@ -lmount $(LIBS)
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
mkdir -p $(DESTDIR)$(SBINDIR)
|
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||||
install $(BINARY) $(DESTDIR)$(SBINDIR)
|
install $(BINARIES) $(DESTDIR)$(SBINDIR)
|
||||||
mkdir -p $(DESTDIR)$(MANDIR)/man8
|
mkdir -p $(DESTDIR)$(MANDIR)/man8
|
||||||
cp init.8 $(DESTDIR)$(MANDIR)/man8/init.8
|
install $(MANPAGES8) $(DESTDIR)$(MANDIR)/man8
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(BINARY) $(OBJS) *.o
|
rm -f $(BINARIES) $(OBJS) *.o
|
||||||
|
|
|
@ -213,12 +213,9 @@ daemon is meant to start the installation's local daemon requirements.
|
||||||
.Sh ENVIRONMENT
|
.Sh ENVIRONMENT
|
||||||
.Nm
|
.Nm
|
||||||
sets the following environment variables.
|
sets the following environment variables.
|
||||||
.Bl -tag -width "INIT_PID"
|
.Bl -tag -width "LOGNAME"
|
||||||
.It Ev HOME
|
.It Ev HOME
|
||||||
root's home directory
|
root's home directory
|
||||||
.It Ev INIT_PID
|
|
||||||
.Nm Ns 's
|
|
||||||
process id
|
|
||||||
.It Ev LOGNAME
|
.It Ev LOGNAME
|
||||||
root
|
root
|
||||||
.It Ev PATH
|
.It Ev PATH
|
||||||
|
@ -301,6 +298,7 @@ exits with the same exit status as its target session if it terminates normally.
|
||||||
.Xr login 8 ,
|
.Xr login 8 ,
|
||||||
.Xr poweroff 8 ,
|
.Xr poweroff 8 ,
|
||||||
.Xr reboot 8 ,
|
.Xr reboot 8 ,
|
||||||
|
.Xr service 8 ,
|
||||||
.Xr sysmerge 8 ,
|
.Xr sysmerge 8 ,
|
||||||
.Xr update-initrd 8
|
.Xr update-initrd 8
|
||||||
.Sh SECURITY CONSIDERATIONS
|
.Sh SECURITY CONSIDERATIONS
|
||||||
|
|
948
init/init.c
948
init/init.c
File diff suppressed because it is too large
Load diff
391
init/service.8
Normal file
391
init/service.8
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
.Dd May 24, 2024
|
||||||
|
.Dt SERVICE 8
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm service
|
||||||
|
.Nd daemon maintenance
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm service
|
||||||
|
.Op Fl lr
|
||||||
|
.Op Fl \-s Ns "=" Ns source-daemon
|
||||||
|
.Op Fl \-exit-code
|
||||||
|
.Op Fl \-no-await
|
||||||
|
.Op Fl \-no-optional
|
||||||
|
.Op Fl \-raw
|
||||||
|
.Op Fl \-source Ns "=" Ns source-daemon
|
||||||
|
.Ar daemon
|
||||||
|
.Oo
|
||||||
|
.Sy dependents
|
||||||
|
|
|
||||||
|
.Sy disable
|
||||||
|
|
|
||||||
|
.Sy edges
|
||||||
|
|
|
||||||
|
.Sy enable
|
||||||
|
|
|
||||||
|
.Sy exit-code
|
||||||
|
|
|
||||||
|
.Sy kill
|
||||||
|
|
|
||||||
|
.Sy pid
|
||||||
|
|
|
||||||
|
.Sy reconfigure
|
||||||
|
|
|
||||||
|
.Sy reload
|
||||||
|
|
|
||||||
|
.Sy requirements
|
||||||
|
|
|
||||||
|
.Sy restart
|
||||||
|
|
|
||||||
|
.Sy signal
|
||||||
|
|
|
||||||
|
.Sy start
|
||||||
|
|
|
||||||
|
.Sy state
|
||||||
|
|
|
||||||
|
.Sy status
|
||||||
|
|
|
||||||
|
.Sy stop
|
||||||
|
|
|
||||||
|
.Sy terminate
|
||||||
|
.Oc
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
performs maintenance of daemons run by
|
||||||
|
.Xr init 8
|
||||||
|
as configured in
|
||||||
|
.Xr init 5 .
|
||||||
|
The daemons are serviced by connecting to the
|
||||||
|
.Nm init
|
||||||
|
process and writing the requested command to the
|
||||||
|
.Pa /var/run/init
|
||||||
|
filesytem socket.
|
||||||
|
.Pp
|
||||||
|
The options are as follows:
|
||||||
|
.Bl -tag -width "12345678"
|
||||||
|
.It Fl s , Fl \-source-daemon Ns "=" Ns Ar source-daemon
|
||||||
|
When modifying a dependency using the
|
||||||
|
.Sy enable , disable , start
|
||||||
|
and
|
||||||
|
.Sy stop
|
||||||
|
commands, use the
|
||||||
|
.Ar source-daemon
|
||||||
|
as the source daemon in the dependency on the target
|
||||||
|
.Ar daemon .
|
||||||
|
The default the
|
||||||
|
.Sy local
|
||||||
|
daemon which is the parent of the locally configured daemons.
|
||||||
|
.It Fl \-exit-code
|
||||||
|
Set the
|
||||||
|
.Sy exit-code
|
||||||
|
flag on the dependency created in the
|
||||||
|
.Sy enable
|
||||||
|
and
|
||||||
|
.Sy start
|
||||||
|
commands.
|
||||||
|
.It Fl \-no-await
|
||||||
|
Set the
|
||||||
|
.Sy no-await
|
||||||
|
flag on the dependency created in the
|
||||||
|
.Sy enable
|
||||||
|
and
|
||||||
|
.Sy start
|
||||||
|
commands.
|
||||||
|
.It Fl \-no-optional
|
||||||
|
Unset the
|
||||||
|
.Sy optional
|
||||||
|
flag on the dependency created in the
|
||||||
|
.Sy enable
|
||||||
|
and
|
||||||
|
.Sy start
|
||||||
|
commands.
|
||||||
|
The default is to set the
|
||||||
|
.Sy optional
|
||||||
|
flag, which is the opposite of the
|
||||||
|
.Xr init 5
|
||||||
|
.Sy require
|
||||||
|
declaration where dependencies are mandatory by default.
|
||||||
|
.It Fl l , \-list
|
||||||
|
Write a table containing the status of every loaded daemon in the format of the
|
||||||
|
.Sy status
|
||||||
|
command.
|
||||||
|
.It Fl r , \-raw
|
||||||
|
Write the command and additional operands as a raw message without verification
|
||||||
|
on the
|
||||||
|
.Nm init
|
||||||
|
socket
|
||||||
|
and output the raw reply sent from
|
||||||
|
.Nm init.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The commands are as follows:
|
||||||
|
.Bl -tag -width "requirements"
|
||||||
|
.It Sy dependents
|
||||||
|
Write the incoming dependencies on the
|
||||||
|
.Ar daemon
|
||||||
|
in the format of the
|
||||||
|
.Sy edges
|
||||||
|
command, which explains why a daemon is running.
|
||||||
|
.It Sy disable
|
||||||
|
Permanently disable the
|
||||||
|
.Ar daemon
|
||||||
|
by removing the dependency on it from the configuration of the
|
||||||
|
.Ar source-daemon
|
||||||
|
(the
|
||||||
|
.Sy local
|
||||||
|
daemon by default, see
|
||||||
|
.Xr init 5 )
|
||||||
|
and then stopping it using the
|
||||||
|
.Sy stop
|
||||||
|
command.
|
||||||
|
The daemon will continue to run if any other daemon depends on it, which can
|
||||||
|
be diagnosed using the
|
||||||
|
.Sy dependents
|
||||||
|
command.
|
||||||
|
.It Sy edges
|
||||||
|
Write the incoming dependencies on the
|
||||||
|
.Ar daemon
|
||||||
|
and its outgoing dependencies in the format of the
|
||||||
|
.Sy require
|
||||||
|
declaration in
|
||||||
|
.Xr init 5
|
||||||
|
with an extra
|
||||||
|
.Ar source
|
||||||
|
operand:
|
||||||
|
.Pp
|
||||||
|
.Sy require
|
||||||
|
.Ar source
|
||||||
|
.Ar target
|
||||||
|
[exit-code]
|
||||||
|
[no-await]
|
||||||
|
[optional]
|
||||||
|
.It Sy enable
|
||||||
|
Permanently enable the
|
||||||
|
.Ar daemon
|
||||||
|
by adding it to the configuration of the
|
||||||
|
.Ar source-daemon
|
||||||
|
(the
|
||||||
|
.Sy local
|
||||||
|
daemon by default, see
|
||||||
|
.Xr init 5 )
|
||||||
|
with the dependency flags per
|
||||||
|
.Fl \-exit-code ,
|
||||||
|
.Fl \-no-await ,
|
||||||
|
and
|
||||||
|
.Fl \-no-optional
|
||||||
|
and starting it by sending the
|
||||||
|
.Sy start
|
||||||
|
command.
|
||||||
|
The daemon only starts if the
|
||||||
|
.Ar source-daemon
|
||||||
|
is running.
|
||||||
|
.It Sy exit-code
|
||||||
|
Write the exit code of the
|
||||||
|
.Ar daemon ,
|
||||||
|
or the empty string if it has not exited.
|
||||||
|
.It Sy kill
|
||||||
|
Kill the
|
||||||
|
.Ar daemon
|
||||||
|
by sending the
|
||||||
|
.Sy SIGKILL
|
||||||
|
signal as a last resort that may cause data loss.
|
||||||
|
.It Sy pid
|
||||||
|
Write the process id of the
|
||||||
|
.Ar daemon
|
||||||
|
if it is running, or the empty output if it
|
||||||
|
does not have a process.
|
||||||
|
Process ids can be recycled and are subject to inherent race conditions.
|
||||||
|
Prefer to use the other commands in
|
||||||
|
.Nm
|
||||||
|
that addresses the daemon by its symbolic name as
|
||||||
|
.Xr init 8
|
||||||
|
will ensure the command operates on the correct process.
|
||||||
|
.It Sy reconfigure
|
||||||
|
Reread the
|
||||||
|
.Xr init 5
|
||||||
|
configuration for the
|
||||||
|
.Ar daemon
|
||||||
|
and apply it after restarting the daemon.
|
||||||
|
.It Sy reload
|
||||||
|
Request the
|
||||||
|
.Ar daemon
|
||||||
|
gracefully reload its own configuration sending
|
||||||
|
the reload signal (usually
|
||||||
|
.Sy SIGHUP )
|
||||||
|
without restarting the daemon and without reloading the
|
||||||
|
.Xr init 5
|
||||||
|
configuration.
|
||||||
|
.It Sy requirements
|
||||||
|
Write the outgoing dependencies from the
|
||||||
|
.Ar daemon
|
||||||
|
in the format of the
|
||||||
|
.Sy edges
|
||||||
|
command, which explains what daemons the daemon needs.
|
||||||
|
.It Sy restart
|
||||||
|
Restart the
|
||||||
|
.Ar daemon
|
||||||
|
by terminating it and starting it up again afterwards.
|
||||||
|
.It Sy signal Ar signal
|
||||||
|
Send the
|
||||||
|
.Ar signal
|
||||||
|
in the symbolic signal name format (e.g.
|
||||||
|
.Sy SIGUSR1 )
|
||||||
|
to the
|
||||||
|
.Ar daemon .
|
||||||
|
.It Sy start
|
||||||
|
Start the
|
||||||
|
.Ar daemon
|
||||||
|
by asking
|
||||||
|
.Xr init 8
|
||||||
|
to add a runtime dependency from the
|
||||||
|
.Ar source-daemon
|
||||||
|
(the
|
||||||
|
.Sy local
|
||||||
|
daemon by default, see
|
||||||
|
.Xr init 5 )
|
||||||
|
to the daemon with the dependency flags per
|
||||||
|
.Fl \-exit-code ,
|
||||||
|
.Fl \-no-await ,
|
||||||
|
and
|
||||||
|
.Fl \-no-optional
|
||||||
|
and starting it by sending the
|
||||||
|
.Sy start
|
||||||
|
command.
|
||||||
|
The daemon only starts if the
|
||||||
|
.Ar source-daemon
|
||||||
|
is running.
|
||||||
|
.It Sy state
|
||||||
|
Write which the state the
|
||||||
|
.Ar daemon
|
||||||
|
is in.
|
||||||
|
.It Sy status
|
||||||
|
Write the status of the
|
||||||
|
.Ar daemon
|
||||||
|
as a single table row in the format:
|
||||||
|
.Pp
|
||||||
|
.Ar name
|
||||||
|
.Ar state
|
||||||
|
.Li pid Ns "=" Ns Ar pid
|
||||||
|
.Li exit Ns "=" Ns Ar exit-code
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar state
|
||||||
|
is one of
|
||||||
|
.Sy terminated ,
|
||||||
|
.Sy scheduled ,
|
||||||
|
.Sy waiting ,
|
||||||
|
.Sy satisfied ,
|
||||||
|
.Sy starting ,
|
||||||
|
.Sy running ,
|
||||||
|
.Sy terminating ,
|
||||||
|
.Sy finishing ,
|
||||||
|
.Sy finished ,
|
||||||
|
or
|
||||||
|
.Sy failed .
|
||||||
|
The
|
||||||
|
.Ar pid
|
||||||
|
is the process id if any, or
|
||||||
|
.Li 0
|
||||||
|
otherwise.
|
||||||
|
The
|
||||||
|
.Ar exit-code
|
||||||
|
is the exit code of the daemon if it has exited, or the name of a signal that
|
||||||
|
killed it, or
|
||||||
|
.Li n/a
|
||||||
|
if the daemon has not exited.
|
||||||
|
.It Sy stop
|
||||||
|
Stop the
|
||||||
|
.Ar daemon
|
||||||
|
by asking
|
||||||
|
.Xr init 8
|
||||||
|
to remove the dependency from the
|
||||||
|
.Ar source-daemon
|
||||||
|
(the
|
||||||
|
.Sy local
|
||||||
|
daemon by default, see
|
||||||
|
.Xr init 5 )
|
||||||
|
on the daemon.
|
||||||
|
The daemon will continue to run as long if other daemon depends on it, which can
|
||||||
|
be diagnosed using the
|
||||||
|
.Sy dependents
|
||||||
|
command.
|
||||||
|
.It Sy terminate
|
||||||
|
Terminate the
|
||||||
|
.Ar daemon
|
||||||
|
gracefully by sending the
|
||||||
|
.Sy SIGTERM
|
||||||
|
signal and
|
||||||
|
.Sy SIGKILL
|
||||||
|
after a timeout.
|
||||||
|
Prefer the
|
||||||
|
.Sy stop
|
||||||
|
command if possible as the
|
||||||
|
.Sy terminate
|
||||||
|
command bypasses the reference count and may cause data loss if other daemons
|
||||||
|
malfunction when the daemon is unexpectedly terminated.
|
||||||
|
.El
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Bl -tag -width "INIT_SOCKET"
|
||||||
|
.It Ev INIT_SOCKET
|
||||||
|
.Xr init 8 Ns 's
|
||||||
|
filesystem socket for communication,
|
||||||
|
.Pa /var/run/init
|
||||||
|
by default.
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width "/etc/init/local" -compact
|
||||||
|
.It Pa /etc/init/
|
||||||
|
Daemon configuration for the local system (first in search path) (see
|
||||||
|
.Xr init 5 )
|
||||||
|
.It Pa /etc/init/local
|
||||||
|
Configuration for the
|
||||||
|
.Sy local
|
||||||
|
daemon (see
|
||||||
|
.Xr init 5 )
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Nm
|
||||||
|
will exit 0 on success and non-zero otherwise.
|
||||||
|
.Sh EXAMPLES
|
||||||
|
Permanently enable the sshd daemon:
|
||||||
|
.Bd -literal
|
||||||
|
$ service sshd enable
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Permanently disable the ntpd daemon:
|
||||||
|
.Bd -literal
|
||||||
|
$ service ntpd disable
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Temporarily start the nginx daemon without changing
|
||||||
|
.Pa /etc/init/local :
|
||||||
|
.Bd -literal
|
||||||
|
$ service nginx start
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Temporarily disable the sshd daemon without changing
|
||||||
|
.Pa /etc/init/local :
|
||||||
|
.Bd -literal
|
||||||
|
$ service sshd stop
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Temporarily stop the ntpd daemon and diagnose why it kept running due to the
|
||||||
|
.Sy time
|
||||||
|
daemon depending on it:
|
||||||
|
.Bd -literal
|
||||||
|
$ service ntpd stop
|
||||||
|
$ service ntpd state
|
||||||
|
running
|
||||||
|
$ service ntpd dependents
|
||||||
|
require time ntpd exit-code
|
||||||
|
$ service --source-daemon=time ntpd stop
|
||||||
|
$ service ntpd state
|
||||||
|
finished
|
||||||
|
.Ed
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr kill 1 ,
|
||||||
|
.Xr init 5 ,
|
||||||
|
.Xr halt 8 ,
|
||||||
|
.Xr init 8 ,
|
||||||
|
.Xr poweroff 8 ,
|
||||||
|
.Xr reboot 8
|
594
init/service.c
Normal file
594
init/service.c
Normal file
|
@ -0,0 +1,594 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* service.c
|
||||||
|
* Start and stop services.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
static bool array_add(void*** array_ptr,
|
||||||
|
size_t* used_ptr,
|
||||||
|
size_t* length_ptr,
|
||||||
|
void* value)
|
||||||
|
{
|
||||||
|
void** array;
|
||||||
|
memcpy(&array, array_ptr, sizeof(array)); // Strict aliasing.
|
||||||
|
|
||||||
|
if ( *used_ptr == *length_ptr )
|
||||||
|
{
|
||||||
|
size_t length = *length_ptr;
|
||||||
|
if ( !length )
|
||||||
|
length = 4;
|
||||||
|
void** new_array = reallocarray(array, length, 2 * sizeof(void*));
|
||||||
|
if ( !new_array )
|
||||||
|
return false;
|
||||||
|
array = new_array;
|
||||||
|
memcpy(array_ptr, &array, sizeof(array)); // Strict aliasing.
|
||||||
|
*length_ptr = length * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(array + (*used_ptr)++, &value, sizeof(value)); // Strict aliasing.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** tokenize(size_t* out_tokens_used, const char* string)
|
||||||
|
{
|
||||||
|
size_t tokens_used = 0;
|
||||||
|
size_t tokens_length = 0;
|
||||||
|
char** tokens = malloc(sizeof(char*));
|
||||||
|
if ( !tokens )
|
||||||
|
return NULL;
|
||||||
|
bool failed = false;
|
||||||
|
bool invalid = false;
|
||||||
|
while ( *string )
|
||||||
|
{
|
||||||
|
if ( isspace((unsigned char) *string) )
|
||||||
|
{
|
||||||
|
string++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( *string == '#' )
|
||||||
|
break;
|
||||||
|
char* token;
|
||||||
|
size_t token_size;
|
||||||
|
FILE* fp = open_memstream(&token, &token_size);
|
||||||
|
if ( !fp )
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bool singly = false;
|
||||||
|
bool doubly = false;
|
||||||
|
bool escaped = false;
|
||||||
|
while ( *string )
|
||||||
|
{
|
||||||
|
char c = *string++;
|
||||||
|
if ( !escaped && !singly && !doubly && isspace((unsigned char) c) )
|
||||||
|
break;
|
||||||
|
if ( !escaped && !doubly && c == '\'' )
|
||||||
|
{
|
||||||
|
singly = !singly;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !escaped && !singly && c == '"' )
|
||||||
|
{
|
||||||
|
doubly = !doubly;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !singly && !escaped && c == '\\' )
|
||||||
|
{
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( escaped )
|
||||||
|
{
|
||||||
|
switch ( c )
|
||||||
|
{
|
||||||
|
case 'a': c = '\a'; break;
|
||||||
|
case 'b': c = '\b'; break;
|
||||||
|
case 'e': c = '\e'; break;
|
||||||
|
case 'f': c = '\f'; break;
|
||||||
|
case 'n': c = '\n'; break;
|
||||||
|
case 'r': c = '\r'; break;
|
||||||
|
case 't': c = '\t'; break;
|
||||||
|
case 'v': c = '\v'; break;
|
||||||
|
default: break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
if ( fputc((unsigned char) c, fp) == EOF )
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( singly || doubly || escaped )
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
free(token);
|
||||||
|
invalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( fflush(fp) == EOF )
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
free(token);
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if ( !array_add((void***) &tokens, &tokens_used, &tokens_length,
|
||||||
|
token) )
|
||||||
|
{
|
||||||
|
free(token);
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( failed || invalid )
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < tokens_used; i++ )
|
||||||
|
free(tokens[i]);
|
||||||
|
free(tokens);
|
||||||
|
if ( invalid )
|
||||||
|
errno = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char** new_tokens = reallocarray(tokens, tokens_used, sizeof(char*));
|
||||||
|
if ( new_tokens )
|
||||||
|
tokens = new_tokens;
|
||||||
|
*out_tokens_used = tokens_used;
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** receive(FILE* fp, size_t* out_tokens_used)
|
||||||
|
{
|
||||||
|
char* line = NULL;
|
||||||
|
size_t line_size;
|
||||||
|
if ( getline(&line, &line_size, fp) < 0 )
|
||||||
|
{
|
||||||
|
if ( ferror(fp) )
|
||||||
|
errx(1, "receiving reply: Unexpected end of connection");
|
||||||
|
else
|
||||||
|
err(1, "receiving reply");
|
||||||
|
}
|
||||||
|
char** result = tokenize(out_tokens_used, line);
|
||||||
|
free(line);
|
||||||
|
if ( !result )
|
||||||
|
{
|
||||||
|
if ( errno )
|
||||||
|
errx(1, "invalid reply: %s", line);
|
||||||
|
else
|
||||||
|
errx(1, "failed to parse replyt");
|
||||||
|
}
|
||||||
|
if ( !*out_tokens_used )
|
||||||
|
errx(1, "invalid empty reply");
|
||||||
|
if ( !strcmp(result[0], "ok") )
|
||||||
|
return result;
|
||||||
|
else if ( !strcmp(result[0], "error") )
|
||||||
|
errx(1, "error: %s", 2 <= *out_tokens_used ? result[1] : "Unknown");
|
||||||
|
else
|
||||||
|
errx(1, "unknown reply: %s", result[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_local_client_socket(const char* path, int flags)
|
||||||
|
{
|
||||||
|
size_t path_length = strlen(path);
|
||||||
|
size_t addr_size = offsetof(struct sockaddr_un, sun_path) + path_length + 1;
|
||||||
|
struct sockaddr_un* sockaddr = malloc(addr_size);
|
||||||
|
if ( !sockaddr )
|
||||||
|
return -1;
|
||||||
|
sockaddr->sun_family = AF_LOCAL;
|
||||||
|
strcpy(sockaddr->sun_path, path);
|
||||||
|
int fd = socket(AF_LOCAL, SOCK_STREAM | flags, 0);
|
||||||
|
if ( fd < 0 )
|
||||||
|
return free(sockaddr), -1;
|
||||||
|
if ( connect(fd, (const struct sockaddr*) sockaddr, addr_size) < 0 )
|
||||||
|
return close(fd), free(sockaddr), -1;
|
||||||
|
free(sockaddr);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rewrite(const char* path, const char* daemon, const char* flags)
|
||||||
|
{
|
||||||
|
FILE* fp = fopen(path, "r");
|
||||||
|
if ( !fp && errno != ENOENT )
|
||||||
|
err(1, "%s", path);
|
||||||
|
char* out_path;
|
||||||
|
if ( asprintf(&out_path, "%s.XXXXXX", path) < 0 )
|
||||||
|
err(1, "malloc");
|
||||||
|
int out_fd = mkstemp(out_path);
|
||||||
|
if ( out_fd < 0 )
|
||||||
|
err(1, "mkstemp: %s.XXXXXX", path);
|
||||||
|
FILE* out = fdopen(out_fd, "w");
|
||||||
|
if ( !out )
|
||||||
|
{
|
||||||
|
unlink(out_path);
|
||||||
|
err(1, "fdopen");
|
||||||
|
}
|
||||||
|
bool found = false;
|
||||||
|
char* line = NULL;
|
||||||
|
size_t line_size = 0;
|
||||||
|
ssize_t line_length;
|
||||||
|
off_t line_number = 0;
|
||||||
|
while ( fp && 0 < (line_length = getline(&line, &line_size, fp)) )
|
||||||
|
{
|
||||||
|
line_number++;
|
||||||
|
size_t tokenc;
|
||||||
|
char** tokens = tokenize(&tokenc, line);
|
||||||
|
if ( !tokens )
|
||||||
|
{
|
||||||
|
unlink(out_path);
|
||||||
|
if ( errno )
|
||||||
|
err(1, "%s", path);
|
||||||
|
else
|
||||||
|
errx(1, "%s:%ji: Syntax error", path, (intmax_t) line_number);
|
||||||
|
}
|
||||||
|
if ( 2 <= tokenc &&
|
||||||
|
!strcmp(tokens[0], "require") && !strcmp(tokens[1], daemon) )
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
if ( flags )
|
||||||
|
fprintf(out, "require %s%s\n", daemon, flags);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs(line, out);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
if ( !found && flags )
|
||||||
|
fprintf(out, "require %s%s\n", daemon, flags);
|
||||||
|
if ( (fp && ferror(fp)) || ferror(out) || fflush(out) == EOF )
|
||||||
|
{
|
||||||
|
unlink(out_path);
|
||||||
|
err(1, "%s", path);
|
||||||
|
}
|
||||||
|
if ( fp )
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
fstat(fileno(fp), &st);
|
||||||
|
fchmod(out_fd, st.st_mode & 07777);
|
||||||
|
fchown(out_fd, st.st_uid, st.st_gid);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fchmod(out_fd, 0666 & ~getumask());
|
||||||
|
if ( rename(out_path, path) < 0 )
|
||||||
|
{
|
||||||
|
unlink(out_path);
|
||||||
|
err(1, "rename: %s -> %s", out_path, path);
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_daemon_exists_in_dir(const char* dir, const char* daemon)
|
||||||
|
{
|
||||||
|
char* path;
|
||||||
|
if ( asprintf(&path, "%s/%s", dir, daemon) < 0 )
|
||||||
|
err(1, "malloc");
|
||||||
|
bool result = !access(path, F_OK);
|
||||||
|
free(path);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void check_daemon_exists(const char* daemon)
|
||||||
|
{
|
||||||
|
if ( !check_daemon_exists_in_dir("/etc/init", daemon) &&
|
||||||
|
!check_daemon_exists_in_dir("/share/init", daemon) )
|
||||||
|
errx(1, "%s: Daemon does not exist", daemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t string_display_length(const char* str)
|
||||||
|
{
|
||||||
|
size_t display_length = 0;
|
||||||
|
mbstate_t ps;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
wchar_t wc;
|
||||||
|
size_t amount = mbrtowc(&wc, str, SIZE_MAX, &ps);
|
||||||
|
if ( amount == 0 )
|
||||||
|
break;
|
||||||
|
if ( amount == (size_t) -1 || amount == (size_t) -2 )
|
||||||
|
{
|
||||||
|
display_length++;
|
||||||
|
str++;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int width = wcwidth(wc);
|
||||||
|
if ( width < 0 )
|
||||||
|
width = 0;
|
||||||
|
if ( SIZE_MAX - display_length < (size_t) width )
|
||||||
|
display_length = SIZE_MAX;
|
||||||
|
else
|
||||||
|
display_length += (size_t) width;
|
||||||
|
str += amount;
|
||||||
|
}
|
||||||
|
return display_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pad(const char* string, size_t padding)
|
||||||
|
{
|
||||||
|
fputs(string, stdout);
|
||||||
|
for ( size_t length = string_display_length(string);
|
||||||
|
length < padding; padding-- )
|
||||||
|
putchar(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void format_statuses(char** tokens, size_t count)
|
||||||
|
{
|
||||||
|
size_t daemon_length = 0;
|
||||||
|
size_t state_length = 10;
|
||||||
|
for ( size_t i = 0; i < count; i++ )
|
||||||
|
{
|
||||||
|
if ( !strncmp(tokens[i], "daemon=", strlen("daemon=")) )
|
||||||
|
{
|
||||||
|
const char* arg = tokens[i] + strlen("daemon=");
|
||||||
|
size_t length = string_display_length (arg);
|
||||||
|
if ( daemon_length < length )
|
||||||
|
daemon_length = length;
|
||||||
|
}
|
||||||
|
else if ( !strncmp(tokens[i], "state=", strlen("state=")) )
|
||||||
|
{
|
||||||
|
const char* arg = tokens[i] + strlen("state=");
|
||||||
|
size_t length = string_display_length(arg);
|
||||||
|
if ( state_length < length )
|
||||||
|
state_length = length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t i = 0;
|
||||||
|
while ( i < count )
|
||||||
|
{
|
||||||
|
size_t next = i;
|
||||||
|
while ( next < count && strcmp(tokens[next], ",") )
|
||||||
|
next++;
|
||||||
|
const char* daemon = NULL;
|
||||||
|
const char* state = NULL;
|
||||||
|
for ( size_t n = i; n < next; n++ )
|
||||||
|
{
|
||||||
|
if ( !strncmp(tokens[n], "state=", strlen("state=")) )
|
||||||
|
state = tokens[n] + strlen("state=");
|
||||||
|
else if ( !strncmp(tokens[n], "daemon=", strlen("daemon=")) )
|
||||||
|
daemon = tokens[n] + strlen("daemon=");
|
||||||
|
}
|
||||||
|
if ( !state || !daemon )
|
||||||
|
errx(1, "missing information in reply");
|
||||||
|
pad(daemon, daemon_length + 2);
|
||||||
|
pad(state, state_length);
|
||||||
|
for ( size_t n = i; n < next; n++ )
|
||||||
|
{
|
||||||
|
if ( strncmp(tokens[n], "state=", strlen("state=")) &&
|
||||||
|
strncmp(tokens[n], "daemon=", strlen("daemon=")) )
|
||||||
|
printf(" %s", tokens[n]);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
i = next;
|
||||||
|
if ( i < count && !strcmp(tokens[i], ",") )
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const char* init_socket = getenv("INIT_SOCKET");
|
||||||
|
if ( !init_socket )
|
||||||
|
init_socket = "/var/run/init";
|
||||||
|
|
||||||
|
bool exit_code = false;
|
||||||
|
bool list = false;
|
||||||
|
bool no_await = false;
|
||||||
|
bool optional = true;
|
||||||
|
bool raw = false;
|
||||||
|
const char* source = "local";
|
||||||
|
|
||||||
|
const struct option longopts[] =
|
||||||
|
{
|
||||||
|
{"exit-code", no_argument, NULL, 256},
|
||||||
|
{"list", no_argument, NULL, 'l'},
|
||||||
|
{"no-await", no_argument, NULL, 257},
|
||||||
|
{"no-optional", no_argument, NULL, 258},
|
||||||
|
{"source", required_argument, NULL, 's'},
|
||||||
|
{"raw", no_argument, NULL, 'r'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
const char* opts = "lrs:";
|
||||||
|
int opt;
|
||||||
|
while ( (opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1 )
|
||||||
|
{
|
||||||
|
switch ( opt )
|
||||||
|
{
|
||||||
|
case 'l': list = true; break;
|
||||||
|
case 'r': raw = true; break;
|
||||||
|
case 's': source = optarg; break;
|
||||||
|
case 256: exit_code = true; break;
|
||||||
|
case 257: no_await = true; break;
|
||||||
|
case 258: optional = false; break;
|
||||||
|
default: return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = open_local_client_socket(init_socket, 0);
|
||||||
|
if ( fd < 0 )
|
||||||
|
err(1, "%s", init_socket);
|
||||||
|
FILE* fp = fdopen(fd, "r+");
|
||||||
|
if ( !fp )
|
||||||
|
err(1, "fdopen");
|
||||||
|
|
||||||
|
if ( raw )
|
||||||
|
{
|
||||||
|
for ( int i = optind; i < argc; i++ )
|
||||||
|
{
|
||||||
|
if ( fprintf(fp, "%s%c", argv[i], i + 1 == argc ? '\n' : ' ') < 0 )
|
||||||
|
err(1, "%s", init_socket);
|
||||||
|
}
|
||||||
|
size_t tokens_count;
|
||||||
|
char** tokens = receive(fp, &tokens_count);
|
||||||
|
for ( size_t i = 0; i < tokens_count; i++ )
|
||||||
|
printf("%s%c", tokens[i], i + 1 == tokens_count ? '\n' : ' ');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char flags[sizeof(" optional no-await exit-code")];
|
||||||
|
snprintf(flags, sizeof(flags), "%s%s%s",
|
||||||
|
optional ? " optional" : "",
|
||||||
|
no_await ? " no-await" : "",
|
||||||
|
exit_code ? " exit-code" : "");
|
||||||
|
char* source_path;
|
||||||
|
if ( asprintf(&source_path, "/etc/init/%s", source) < 0 )
|
||||||
|
err(1, "malloc");
|
||||||
|
|
||||||
|
bool no_command = list;
|
||||||
|
|
||||||
|
if ( no_command && 0 < argc - optind )
|
||||||
|
errx(1, "unexpected extra operand: %s", argv[optind]);
|
||||||
|
else if ( !no_command && argc - optind < 2 )
|
||||||
|
errx(1, "usage: <daemon> <command>");
|
||||||
|
|
||||||
|
const char* daemon = !no_command ? argv[optind++] : NULL;
|
||||||
|
const char* command = !no_command ? argv[optind++] : NULL;
|
||||||
|
|
||||||
|
if ( command && strcmp(command, "signal") && 0 < argc - optind )
|
||||||
|
errx(1, "unexpected extra operand: %s", argv[optind]);
|
||||||
|
|
||||||
|
if ( list )
|
||||||
|
fprintf(fp, "list\n");
|
||||||
|
else if ( !strcmp(command, "enable") )
|
||||||
|
{
|
||||||
|
check_daemon_exists(daemon);
|
||||||
|
rewrite(source_path, daemon, flags);
|
||||||
|
fprintf(fp, "require %s %s %s start\n", source, daemon, flags);
|
||||||
|
}
|
||||||
|
else if ( !strcmp(command, "disable") )
|
||||||
|
{
|
||||||
|
rewrite(source_path, daemon, NULL);
|
||||||
|
fprintf(fp, "unrequire %s %s\n", source, daemon);
|
||||||
|
}
|
||||||
|
else if ( !strcmp(command, "start") )
|
||||||
|
fprintf(fp, "require %s %s %s start\n", source, daemon, flags);
|
||||||
|
else if ( !strcmp(command, "stop") )
|
||||||
|
fprintf(fp, "unrequire %s %s\n", source, daemon);
|
||||||
|
else if ( !strcmp(command, "restart") )
|
||||||
|
fprintf(fp, "restart %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "reload") )
|
||||||
|
fprintf(fp, "reload %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "reconfigure") )
|
||||||
|
fprintf(fp, "reconfigure %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "terminate") )
|
||||||
|
fprintf(fp, "terminate %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "kill") )
|
||||||
|
fprintf(fp, "kill %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "signal") )
|
||||||
|
{
|
||||||
|
if ( argc - optind < 1 )
|
||||||
|
errx(1, "expected signal name");
|
||||||
|
const char* signal = argv[optind++];
|
||||||
|
if ( 0 < argc - optind )
|
||||||
|
errx(1, "unexpected extra operand: %s", argv[optind]);
|
||||||
|
fprintf(fp, "signal %s %s\n", daemon, signal);
|
||||||
|
}
|
||||||
|
else if ( !strcmp(command, "status") )
|
||||||
|
fprintf(fp, "status %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "state") )
|
||||||
|
fprintf(fp, "status %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "pid") )
|
||||||
|
fprintf(fp, "status %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "exit-code") )
|
||||||
|
fprintf(fp, "status %s\n", daemon);
|
||||||
|
else if ( !strcmp(command, "requirements") ||
|
||||||
|
!strcmp(command, "dependents") ||
|
||||||
|
!strcmp(command, "edges") )
|
||||||
|
fprintf(fp, "%s %s\n", command, daemon);
|
||||||
|
else
|
||||||
|
errx(1, "unknown command: %s", command);
|
||||||
|
|
||||||
|
if ( ferror(fp) || fflush(fp) == EOF )
|
||||||
|
err(1, "%s", init_socket);
|
||||||
|
|
||||||
|
size_t tokens_count;
|
||||||
|
char** tokens = receive(fp, &tokens_count);
|
||||||
|
|
||||||
|
if ( list )
|
||||||
|
format_statuses(tokens + 1, tokens_count - 1);
|
||||||
|
else if ( !strcmp(command, "status") )
|
||||||
|
format_statuses(tokens + 1, tokens_count - 1);
|
||||||
|
else if ( !strcmp(command, "state") )
|
||||||
|
{
|
||||||
|
for ( size_t i = 1; i < tokens_count; i++ )
|
||||||
|
{
|
||||||
|
if ( !strncmp(tokens[i], "state=", strlen("state=")) )
|
||||||
|
{
|
||||||
|
puts(tokens[i] + strlen("state="));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !strcmp(command, "pid") )
|
||||||
|
{
|
||||||
|
for ( size_t i = 1; i < tokens_count; i++ )
|
||||||
|
{
|
||||||
|
if ( !strncmp(tokens[i], "pid=", strlen("pid=")) )
|
||||||
|
{
|
||||||
|
const char* arg = tokens[i] + strlen("pid=");
|
||||||
|
if ( strcmp(arg, "0") )
|
||||||
|
puts(arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !strcmp(command, "exit-code") )
|
||||||
|
{
|
||||||
|
for ( size_t i = 1; i < tokens_count; i++ )
|
||||||
|
{
|
||||||
|
if ( !strncmp(tokens[i], "exit=", strlen("exit=")) )
|
||||||
|
{
|
||||||
|
const char* arg = tokens[i] + strlen("exit=");
|
||||||
|
if ( strcmp(arg, "n/a") )
|
||||||
|
puts(arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !strcmp(command, "requirements") ||
|
||||||
|
!strcmp(command, "dependents") ||
|
||||||
|
!strcmp(command, "edges") )
|
||||||
|
{
|
||||||
|
for ( size_t i = 1; i < tokens_count; i++ )
|
||||||
|
{
|
||||||
|
if ( !strcmp(tokens[i], ",") )
|
||||||
|
continue;
|
||||||
|
size_t eol = i + 1 == tokens_count || !strcmp(tokens[i + 1], ",");
|
||||||
|
printf("%s%c", tokens[i], eol ? '\n' : ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
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
|
32
iso9660/fuse.h
Normal file
32
iso9660/fuse.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* fuse.h
|
||||||
|
* FUSE frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSE_H
|
||||||
|
#define FUSE_H
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
class Filesystem;
|
||||||
|
|
||||||
|
int iso9660_fuse_main(const char* argv0,
|
||||||
|
const char* mount_path,
|
||||||
|
bool foreground,
|
||||||
|
Filesystem* fs,
|
||||||
|
Device* dev);
|
||||||
|
|
||||||
|
#endif
|
614
iso9660/inode.cpp
Normal file
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
|
150
iso9660/ioleast.h
Normal file
150
iso9660/ioleast.h
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* ioleast.h
|
||||||
|
* Versions of {,p}{read,write} that don't return until it has returned as much
|
||||||
|
* data as requested, end of file, or an error occurs. This is sometimes needed
|
||||||
|
* as read(2) and write(2) is not always guaranteed to fill up the entire
|
||||||
|
* buffer or write it all.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
|
||||||
|
#define SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
|
||||||
|
|
||||||
|
#if defined(__sortix__) || defined(__sortix_libc__)
|
||||||
|
|
||||||
|
#include_next <ioleast.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if !defined(EEOF) && defined(EIO)
|
||||||
|
#define EEOF EIO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t readleast(int fd, void* buf_ptr, size_t least, size_t max)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = read(fd, buf + done, max - done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t writeleast(int fd, const void* buf_ptr, size_t least, size_t max)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = write(fd, buf + done, max - done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t preadleast(int fd, void* buf_ptr, size_t least, size_t max, off_t off)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = pread(fd, buf + done, max - done, off + done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t pwriteleast(int fd, const void* buf_ptr, size_t least, size_t max, off_t off)
|
||||||
|
{
|
||||||
|
assert(least <= max);
|
||||||
|
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||||
|
size_t done = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ssize_t amount = pwrite(fd, buf + done, max - done, off + done);
|
||||||
|
if ( amount < 0 )
|
||||||
|
return done;
|
||||||
|
if ( !amount && done < least )
|
||||||
|
return errno = EEOF, done;
|
||||||
|
if ( !amount )
|
||||||
|
break;
|
||||||
|
done += amount;
|
||||||
|
} while ( done < least );
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t readall(int fd, void* buf, size_t count)
|
||||||
|
{
|
||||||
|
return readleast(fd, buf, count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t writeall(int fd, const void* buf, size_t count)
|
||||||
|
{
|
||||||
|
return writeleast(fd, buf, count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t preadall(int fd, void* buf, size_t count, off_t off)
|
||||||
|
{
|
||||||
|
return preadleast(fd, buf, count, count, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused)) static inline
|
||||||
|
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
|
||||||
|
{
|
||||||
|
return pwriteleast(fd, buf, count, count, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
128
iso9660/iso9660.h
Normal file
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.
|
||||||
|
|
214
kernel/com.cpp
214
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,93 @@ 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;
|
||||||
|
winch();
|
||||||
|
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 +264,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 +274,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 +282,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t value;
|
|
||||||
if ( !ctx->copy_from_src(&value, src + i, sizeof(value)) )
|
|
||||||
return i ? (ssize_t) i : -1;
|
|
||||||
outport8(port + TXR, value);
|
|
||||||
}
|
}
|
||||||
|
outport8(port + TXR, buffer[i]);
|
||||||
return (ssize_t) count;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Ref<DevCOMPort> com_devices[1 + NUM_COM_PORTS];
|
static Ref<DevCOMPort> com_devices[1 + NUM_COM_PORTS];
|
||||||
|
@ -282,21 +315,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 +322,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -679,9 +679,9 @@ Ref<Descriptor> Descriptor::open(ioctx_t* ctx, const char* filename, int flags,
|
||||||
mode_t mode)
|
mode_t mode)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
kthread_mutex_lock(&process->idlock);
|
kthread_mutex_lock(&process->id_lock);
|
||||||
mode &= ~process->umask;
|
mode &= ~process->umask;
|
||||||
kthread_mutex_unlock(&process->idlock);
|
kthread_mutex_unlock(&process->id_lock);
|
||||||
|
|
||||||
if ( !filename[0] )
|
if ( !filename[0] )
|
||||||
return errno = ENOENT, Ref<Descriptor>();
|
return errno = ENOENT, Ref<Descriptor>();
|
||||||
|
@ -840,9 +840,9 @@ Ref<Descriptor> Descriptor::open_elem(ioctx_t* ctx, const char* filename,
|
||||||
int Descriptor::mkdir(ioctx_t* ctx, const char* filename, mode_t mode)
|
int Descriptor::mkdir(ioctx_t* ctx, const char* filename, mode_t mode)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
kthread_mutex_lock(&process->idlock);
|
kthread_mutex_lock(&process->id_lock);
|
||||||
mode &= ~process->umask;
|
mode &= ~process->umask;
|
||||||
kthread_mutex_unlock(&process->idlock);
|
kthread_mutex_unlock(&process->id_lock);
|
||||||
|
|
||||||
char* final;
|
char* final;
|
||||||
Ref<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(this),
|
Ref<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(this),
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -29,14 +29,14 @@ namespace Sortix {
|
||||||
uid_t sys_getuid()
|
uid_t sys_getuid()
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
return process->uid;
|
return process->uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sys_setuid(uid_t uid)
|
int sys_setuid(uid_t uid)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
// TODO: Implement security checks in many place across the operating system
|
// TODO: Implement security checks in many place across the operating system
|
||||||
// and until then allow anyone to do this to not pretend to be secure.
|
// and until then allow anyone to do this to not pretend to be secure.
|
||||||
process->uid = uid;
|
process->uid = uid;
|
||||||
|
@ -47,14 +47,14 @@ int sys_setuid(uid_t uid)
|
||||||
gid_t sys_getgid()
|
gid_t sys_getgid()
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
return process->gid;
|
return process->gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sys_setgid(gid_t gid)
|
int sys_setgid(gid_t gid)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
// TODO: Implement security checks in many place across the operating system
|
// TODO: Implement security checks in many place across the operating system
|
||||||
// and until then allow anyone to do this to not pretend to be secure.
|
// and until then allow anyone to do this to not pretend to be secure.
|
||||||
process->gid = gid;
|
process->gid = gid;
|
||||||
|
@ -65,14 +65,14 @@ int sys_setgid(gid_t gid)
|
||||||
uid_t sys_geteuid()
|
uid_t sys_geteuid()
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
return process->euid;
|
return process->euid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sys_seteuid(uid_t euid)
|
int sys_seteuid(uid_t euid)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
// TODO: Implement security checks in many place across the operating system
|
// TODO: Implement security checks in many place across the operating system
|
||||||
// and until then allow anyone to do this to not pretend to be secure.
|
// and until then allow anyone to do this to not pretend to be secure.
|
||||||
process->euid = euid;
|
process->euid = euid;
|
||||||
|
@ -82,14 +82,14 @@ int sys_seteuid(uid_t euid)
|
||||||
gid_t sys_getegid()
|
gid_t sys_getegid()
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
return process->egid;
|
return process->egid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sys_setegid(gid_t egid)
|
int sys_setegid(gid_t egid)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
// TODO: Implement security checks in many place across the operating system
|
// TODO: Implement security checks in many place across the operating system
|
||||||
// and until then allow anyone to do this to not pretend to be secure.
|
// and until then allow anyone to do this to not pretend to be secure.
|
||||||
process->egid = egid;
|
process->egid = egid;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2016, 2017, 2024 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 @@
|
||||||
#define TIOCGNAME __IOCTL(6, __IOCTL_TYPE_PTR)
|
#define TIOCGNAME __IOCTL(6, __IOCTL_TYPE_PTR)
|
||||||
#define TIOCGPTN __IOCTL(7, __IOCTL_TYPE_PTR)
|
#define TIOCGPTN __IOCTL(7, __IOCTL_TYPE_PTR)
|
||||||
#define TIOCGDISPLAYS __IOCTL(8, __IOCTL_TYPE_PTR)
|
#define TIOCGDISPLAYS __IOCTL(8, __IOCTL_TYPE_PTR)
|
||||||
|
#define TIOCUCTTY __IOCTL(9, __IOCTL_TYPE_INT)
|
||||||
|
|
||||||
#define IOC_TYPE(x) ((x) >> 0 & 0xFF)
|
#define IOC_TYPE(x) ((x) >> 0 & 0xFF)
|
||||||
#define IOC_TYPE_BLOCK_DEVICE 1
|
#define IOC_TYPE_BLOCK_DEVICE 1
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2016, 2021 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011-2016, 2021, 2024 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
|
||||||
|
@ -66,17 +66,17 @@ public:
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
kthread_mutex_t nicelock;
|
kthread_mutex_t nice_lock;
|
||||||
int nice;
|
int nice;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
kthread_mutex_t idlock;
|
kthread_mutex_t id_lock;
|
||||||
uid_t uid, euid;
|
uid_t uid, euid;
|
||||||
gid_t gid, egid;
|
gid_t gid, egid;
|
||||||
mode_t umask;
|
mode_t umask;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kthread_mutex_t ptrlock;
|
kthread_mutex_t ptr_lock;
|
||||||
Ref<Descriptor> tty;
|
Ref<Descriptor> tty;
|
||||||
Ref<Descriptor> root;
|
Ref<Descriptor> root;
|
||||||
Ref<Descriptor> cwd;
|
Ref<Descriptor> cwd;
|
||||||
|
@ -112,29 +112,34 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Process* parent;
|
Process* parent;
|
||||||
Process* prevsibling;
|
Process* prev_sibling;
|
||||||
Process* nextsibling;
|
Process* next_sibling;
|
||||||
Process* firstchild;
|
Process* first_child;
|
||||||
Process* zombiechild;
|
Process* zombie_child;
|
||||||
Process* group;
|
Process* group;
|
||||||
Process* groupprev;
|
Process* group_prev;
|
||||||
Process* groupnext;
|
Process* group_next;
|
||||||
Process* groupfirst;
|
Process* group_first;
|
||||||
Process* session;
|
Process* session;
|
||||||
Process* sessionprev;
|
Process* session_prev;
|
||||||
Process* sessionnext;
|
Process* session_next;
|
||||||
Process* sessionfirst;
|
Process* session_first;
|
||||||
kthread_mutex_t childlock;
|
Process* init;
|
||||||
kthread_mutex_t parentlock;
|
Process* init_prev;
|
||||||
kthread_cond_t zombiecond;
|
Process* init_next;
|
||||||
bool iszombie;
|
Process* init_first;
|
||||||
bool nozombify;
|
kthread_mutex_t child_lock;
|
||||||
|
kthread_mutex_t parent_lock;
|
||||||
|
kthread_cond_t zombie_cond;
|
||||||
|
bool is_zombie;
|
||||||
|
bool no_zombify;
|
||||||
bool limbo;
|
bool limbo;
|
||||||
|
bool is_init_exiting;
|
||||||
int exit_code;
|
int exit_code;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Thread* firstthread;
|
Thread* first_thread;
|
||||||
kthread_mutex_t threadlock;
|
kthread_mutex_t thread_lock;
|
||||||
size_t threads_not_exiting_count;
|
size_t threads_not_exiting_count;
|
||||||
bool threads_exiting;
|
bool threads_exiting;
|
||||||
|
|
||||||
|
@ -178,6 +183,7 @@ public:
|
||||||
int prot);
|
int prot);
|
||||||
void GroupRemoveMember(Process* child);
|
void GroupRemoveMember(Process* child);
|
||||||
void SessionRemoveMember(Process* child);
|
void SessionRemoveMember(Process* child);
|
||||||
|
void InitRemoveMember(Process* child);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Process* Fork();
|
Process* Fork();
|
||||||
|
|
|
@ -41,8 +41,6 @@ void SetThreadState(Thread* thread, ThreadState state, bool wake_only = false);
|
||||||
void SetSignalPending(Thread* thread, unsigned long is_pending);
|
void SetSignalPending(Thread* thread, unsigned long is_pending);
|
||||||
ThreadState GetThreadState(Thread* thread);
|
ThreadState GetThreadState(Thread* thread);
|
||||||
void SetIdleThread(Thread* thread);
|
void SetIdleThread(Thread* thread);
|
||||||
void SetInitProcess(Process* init);
|
|
||||||
Process* GetInitProcess();
|
|
||||||
Process* GetKernelProcess();
|
Process* GetKernelProcess();
|
||||||
void InterruptYieldCPU(struct interrupt_context* intctx, void* user);
|
void InterruptYieldCPU(struct interrupt_context* intctx, void* user);
|
||||||
void ThreadExitCPU(struct interrupt_context* intctx, void* user);
|
void ThreadExitCPU(struct interrupt_context* intctx, void* user);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2016, 2021, 2022, 2023 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011-2016, 2021-2024 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
|
||||||
|
@ -97,6 +97,7 @@ int sys_getentropy(void*, size_t);
|
||||||
uid_t sys_geteuid(void);
|
uid_t sys_geteuid(void);
|
||||||
gid_t sys_getgid(void);
|
gid_t sys_getgid(void);
|
||||||
int sys_gethostname(char*, size_t);
|
int sys_gethostname(char*, size_t);
|
||||||
|
pid_t sys_getinit(pid_t);
|
||||||
size_t sys_getpagesize(void);
|
size_t sys_getpagesize(void);
|
||||||
int sys_getpeername(int, void*, size_t*);
|
int sys_getpeername(int, void*, size_t*);
|
||||||
pid_t sys_getpgid(pid_t);
|
pid_t sys_getpgid(pid_t);
|
||||||
|
@ -151,6 +152,7 @@ int sys_setegid(gid_t);
|
||||||
int sys_seteuid(uid_t);
|
int sys_seteuid(uid_t);
|
||||||
int sys_setgid(gid_t);
|
int sys_setgid(gid_t);
|
||||||
int sys_sethostname(const char*, size_t);
|
int sys_sethostname(const char*, size_t);
|
||||||
|
int sys_setinit(void);
|
||||||
int sys_setpgid(pid_t, pid_t);
|
int sys_setpgid(pid_t, pid_t);
|
||||||
int sys_setpriority(int, id_t, int);
|
int sys_setpriority(int, id_t, int);
|
||||||
pid_t sys_setsid(void);
|
pid_t sys_setsid(void);
|
||||||
|
|
|
@ -79,8 +79,8 @@ public:
|
||||||
struct thread_registers registers;
|
struct thread_registers registers;
|
||||||
size_t id;
|
size_t id;
|
||||||
Process* process;
|
Process* process;
|
||||||
Thread* prevsibling;
|
Thread* prev_sibling;
|
||||||
Thread* nextsibling;
|
Thread* next_sibling;
|
||||||
Thread* scheduler_list_prev;
|
Thread* scheduler_list_prev;
|
||||||
Thread* scheduler_list_next;
|
Thread* scheduler_list_next;
|
||||||
volatile ThreadState state;
|
volatile ThreadState state;
|
||||||
|
@ -88,12 +88,12 @@ public:
|
||||||
sigset_t signal_mask;
|
sigset_t signal_mask;
|
||||||
sigset_t saved_signal_mask;
|
sigset_t saved_signal_mask;
|
||||||
stack_t signal_stack;
|
stack_t signal_stack;
|
||||||
addr_t kernelstackpos;
|
addr_t kernel_stack_pos;
|
||||||
size_t kernelstacksize;
|
size_t kernel_stack_size;
|
||||||
size_t signal_count;
|
size_t signal_count;
|
||||||
uintptr_t signal_single_frame;
|
uintptr_t signal_single_frame;
|
||||||
uintptr_t signal_canary;
|
uintptr_t signal_canary;
|
||||||
bool kernelstackmalloced;
|
bool kernel_stack_malloced;
|
||||||
bool pledged_destruction;
|
bool pledged_destruction;
|
||||||
bool force_no_signals;
|
bool force_no_signals;
|
||||||
bool signal_single;
|
bool signal_single;
|
||||||
|
|
|
@ -190,6 +190,8 @@
|
||||||
#define SYSCALL_SETDNSCONFIG 167
|
#define SYSCALL_SETDNSCONFIG 167
|
||||||
#define SYSCALL_FUTEX 168
|
#define SYSCALL_FUTEX 168
|
||||||
#define SYSCALL_MEMUSAGE 169
|
#define SYSCALL_MEMUSAGE 169
|
||||||
#define SYSCALL_MAX_NUM 170 /* index of highest constant + 1 */
|
#define SYSCALL_GETINIT 170
|
||||||
|
#define SYSCALL_SETINIT 171
|
||||||
|
#define SYSCALL_MAX_NUM 172 /* index of highest constant + 1 */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace Sortix {
|
||||||
void SetupUserIOCtx(ioctx_t* ctx)
|
void SetupUserIOCtx(ioctx_t* ctx)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
ctx->uid = ctx->auth_uid = process->uid;
|
ctx->uid = ctx->auth_uid = process->uid;
|
||||||
ctx->gid = ctx->auth_gid = process->gid;
|
ctx->gid = ctx->auth_gid = process->gid;
|
||||||
ctx->copy_to_dest = CopyToUser;
|
ctx->copy_to_dest = CopyToUser;
|
||||||
|
@ -39,7 +39,7 @@ void SetupUserIOCtx(ioctx_t* ctx)
|
||||||
void SetupKernelIOCtx(ioctx_t* ctx)
|
void SetupKernelIOCtx(ioctx_t* ctx)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
ctx->uid = ctx->auth_uid = process->uid;
|
ctx->uid = ctx->auth_uid = process->uid;
|
||||||
ctx->gid = ctx->auth_gid = process->gid;
|
ctx->gid = ctx->auth_gid = process->gid;
|
||||||
ctx->copy_to_dest = CopyToKernel;
|
ctx->copy_to_dest = CopyToKernel;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2018, 2021-2022 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011-2018, 2021-2024 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
|
||||||
|
@ -103,6 +103,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)]; }
|
||||||
|
@ -231,6 +233,13 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
|
||||||
|
|
||||||
if ( !(kernel_options = strdup(cmdline ? cmdline : "")) )
|
if ( !(kernel_options = strdup(cmdline ? cmdline : "")) )
|
||||||
Panic("Failed to allocate kernel command line");
|
Panic("Failed to allocate kernel command line");
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
// TODO: Detect EFI.
|
||||||
|
kernel_firmware = "bios";
|
||||||
|
#else
|
||||||
|
#warning "Name your system firmware here"
|
||||||
|
kernel_firmware = "unknown";
|
||||||
|
#endif
|
||||||
|
|
||||||
int argmax = 1;
|
int argmax = 1;
|
||||||
argv = new char*[argmax + 1];
|
argv = new char*[argmax + 1];
|
||||||
|
@ -286,6 +295,20 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
|
||||||
enable_network_drivers = true;
|
enable_network_drivers = true;
|
||||||
else if ( !strcmp(arg, "--no-random-seed") )
|
else if ( !strcmp(arg, "--no-random-seed") )
|
||||||
no_random_seed = true;
|
no_random_seed = true;
|
||||||
|
else if ( !strncmp(arg, "--firmware=", strlen("--firmware=")) )
|
||||||
|
{
|
||||||
|
const char* firmware = arg + strlen("--firmware=");
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
if ( !strcmp(firmware, "bios") || !strcmp(firmware, "pc") )
|
||||||
|
kernel_firmware = "bios";
|
||||||
|
else if ( !strcmp(firmware, "efi") )
|
||||||
|
kernel_firmware = "efi";
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
PanicF("Unsupported firmware option: %s", firmware);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log::PrintF("\r\e[J");
|
Log::PrintF("\r\e[J");
|
||||||
|
@ -351,13 +374,17 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
|
||||||
ptable.Reset();
|
ptable.Reset();
|
||||||
system->addrspace = Memory::GetAddressSpace();
|
system->addrspace = Memory::GetAddressSpace();
|
||||||
system->group = system;
|
system->group = system;
|
||||||
system->groupprev = NULL;
|
system->group_prev = NULL;
|
||||||
system->groupnext = NULL;
|
system->group_next = NULL;
|
||||||
system->groupfirst = system;
|
system->group_first = system;
|
||||||
system->session = system;
|
system->session = system;
|
||||||
system->sessionprev = NULL;
|
system->session_prev = NULL;
|
||||||
system->sessionnext = NULL;
|
system->session_next = NULL;
|
||||||
system->sessionfirst = system;
|
system->session_first = system;
|
||||||
|
system->init = NULL;
|
||||||
|
system->init_prev = NULL;
|
||||||
|
system->init_next = NULL;
|
||||||
|
system->init_first = NULL;
|
||||||
|
|
||||||
if ( !(system->program_image_path = String::Clone("<kernel process>")) )
|
if ( !(system->program_image_path = String::Clone("<kernel process>")) )
|
||||||
Panic("Unable to clone string for system process name");
|
Panic("Unable to clone string for system process name");
|
||||||
|
@ -369,10 +396,10 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
|
||||||
Thread* idlethread = new Thread();
|
Thread* idlethread = new Thread();
|
||||||
idlethread->name = "idle";
|
idlethread->name = "idle";
|
||||||
idlethread->process = system;
|
idlethread->process = system;
|
||||||
idlethread->kernelstackpos = (addr_t) stack;
|
idlethread->kernel_stack_pos = (addr_t) stack;
|
||||||
idlethread->kernelstacksize = STACK_SIZE;
|
idlethread->kernel_stack_size = STACK_SIZE;
|
||||||
idlethread->kernelstackmalloced = false;
|
idlethread->kernel_stack_malloced = false;
|
||||||
system->firstthread = idlethread;
|
system->first_thread = idlethread;
|
||||||
system->threads_not_exiting_count = 1;
|
system->threads_not_exiting_count = 1;
|
||||||
Scheduler::SetIdleThread(idlethread);
|
Scheduler::SetIdleThread(idlethread);
|
||||||
|
|
||||||
|
@ -640,19 +667,23 @@ static void BootThread(void* /*user*/)
|
||||||
kthread_mutex_lock(&process_family_lock);
|
kthread_mutex_lock(&process_family_lock);
|
||||||
Process* kernel_process = CurrentProcess();
|
Process* kernel_process = CurrentProcess();
|
||||||
init->parent = kernel_process;
|
init->parent = kernel_process;
|
||||||
init->nextsibling = kernel_process->firstchild;
|
init->next_sibling = kernel_process->first_child;
|
||||||
init->prevsibling = NULL;
|
init->prev_sibling = NULL;
|
||||||
if ( kernel_process->firstchild )
|
if ( kernel_process->first_child )
|
||||||
kernel_process->firstchild->prevsibling = init;
|
kernel_process->first_child->prev_sibling = init;
|
||||||
kernel_process->firstchild = init;
|
kernel_process->first_child = init;
|
||||||
init->group = init;
|
init->group = init;
|
||||||
init->groupprev = NULL;
|
init->group_prev = NULL;
|
||||||
init->groupnext = NULL;
|
init->group_next = NULL;
|
||||||
init->groupfirst = init;
|
init->group_first = init;
|
||||||
init->session = init;
|
init->session = init;
|
||||||
init->sessionprev = NULL;
|
init->session_prev = NULL;
|
||||||
init->sessionnext = NULL;
|
init->session_next = NULL;
|
||||||
init->sessionfirst = init;
|
init->session_first = init;
|
||||||
|
init->init = init;
|
||||||
|
init->init_prev = NULL;
|
||||||
|
init->init_next = NULL;
|
||||||
|
init->init_first = init;
|
||||||
kthread_mutex_unlock(&process_family_lock);
|
kthread_mutex_unlock(&process_family_lock);
|
||||||
|
|
||||||
// TODO: Why don't we fork from pid=0 and this is done for us?
|
// TODO: Why don't we fork from pid=0 and this is done for us?
|
||||||
|
@ -662,7 +693,6 @@ static void BootThread(void* /*user*/)
|
||||||
mtable.Reset();
|
mtable.Reset();
|
||||||
init->BootstrapDirectories(droot);
|
init->BootstrapDirectories(droot);
|
||||||
init->addrspace = initaddrspace;
|
init->addrspace = initaddrspace;
|
||||||
Scheduler::SetInitProcess(init);
|
|
||||||
|
|
||||||
Thread* initthread = RunKernelThread(init, InitThread, NULL, "main");
|
Thread* initthread = RunKernelThread(init, InitThread, NULL, "main");
|
||||||
if ( !initthread )
|
if ( !initthread )
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <sortix/kernel/syscall.h>
|
#include <sortix/kernel/syscall.h>
|
||||||
|
|
||||||
#include "kernelinfo.h"
|
#include "kernelinfo.h"
|
||||||
|
#include "net/tcp.h"
|
||||||
|
|
||||||
#ifndef VERSIONSTR
|
#ifndef VERSIONSTR
|
||||||
#define VERSIONSTR "unknown"
|
#define VERSIONSTR "unknown"
|
||||||
|
@ -33,20 +34,17 @@
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
|
|
||||||
char* kernel_options;
|
char* kernel_options;
|
||||||
|
const char* kernel_firmware;
|
||||||
|
|
||||||
static const char* KernelInfo(const char* req)
|
static const char* KernelInfo(const char* req)
|
||||||
{
|
{
|
||||||
if ( strcmp(req, "name") == 0 ) { return BRAND_KERNEL_NAME; }
|
if ( strcmp(req, "name") == 0 ) return BRAND_KERNEL_NAME;
|
||||||
if ( strcmp(req, "version") == 0 ) { return VERSIONSTR; }
|
if ( strcmp(req, "version") == 0 ) return VERSIONSTR;
|
||||||
if ( strcmp(req, "tagline") == 0 ) { return BRAND_RELEASE_TAGLINE; }
|
if ( strcmp(req, "tagline") == 0 ) return BRAND_RELEASE_TAGLINE;
|
||||||
if ( strcmp(req, "options") == 0 ) { return kernel_options; }
|
if ( strcmp(req, "options") == 0 ) return kernel_options;
|
||||||
if ( strcmp(req, "builddate") == 0 ) { return __DATE__; }
|
if ( strcmp(req, "builddate") == 0 ) return __DATE__;
|
||||||
if ( strcmp(req, "buildtime") == 0 ) { return __TIME__; }
|
if ( strcmp(req, "buildtime") == 0 ) return __TIME__;
|
||||||
#if defined(__i386__) || defined(__x86_64__)
|
if ( strcmp(req, "firmware") == 0 ) return kernel_firmware;
|
||||||
if ( strcmp(req, "firmware") == 0 ) { return "bios"; }
|
|
||||||
#else
|
|
||||||
#warning "Name your system firmware here"
|
|
||||||
#endif
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +53,77 @@ ssize_t sys_kernelinfo(const char* user_req, char* user_resp, size_t resplen)
|
||||||
char* req = GetStringFromUser(user_req);
|
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 )
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
|
|
||||||
extern char* kernel_options;
|
extern char* kernel_options;
|
||||||
|
extern const char* kernel_firmware;
|
||||||
|
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
||||||
|
|
|
@ -258,9 +258,9 @@ void kthread_exit()
|
||||||
// only threads in this process, except the initial thread. Otherwise more
|
// only threads in this process, except the initial thread. Otherwise more
|
||||||
// threads may appear, and we can't conclude whether this is the last thread
|
// threads may appear, and we can't conclude whether this is the last thread
|
||||||
// in the process to exit.
|
// in the process to exit.
|
||||||
kthread_mutex_lock(&process->threadlock);
|
kthread_mutex_lock(&process->thread_lock);
|
||||||
bool is_last_to_exit = --process->threads_not_exiting_count == 0;
|
bool is_last_to_exit = --process->threads_not_exiting_count == 0;
|
||||||
kthread_mutex_unlock(&process->threadlock);
|
kthread_mutex_unlock(&process->thread_lock);
|
||||||
// All other threads in the process have committed to exiting, though they
|
// All other threads in the process have committed to exiting, though they
|
||||||
// might not have exited yet. However, we know they are only running the
|
// might not have exited yet. However, we know they are only running the
|
||||||
// below code that schedules thread termination. It's therefore safe to run
|
// below code that schedules thread termination. It's therefore safe to run
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,13 +52,11 @@ namespace NetFS {
|
||||||
class Manager;
|
class Manager;
|
||||||
class StreamSocket;
|
class StreamSocket;
|
||||||
|
|
||||||
class Manager : public AbstractInode
|
class Manager : public Refcountable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Manager(uid_t owner, gid_t group, mode_t mode);
|
Manager();
|
||||||
virtual ~Manager() { }
|
virtual ~Manager();
|
||||||
virtual Ref<Inode> open(ioctx_t* ctx, const char* filename, int flags,
|
|
||||||
mode_t mode);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Bind(StreamSocket* socket, struct sockaddr_un* addr, size_t addrsize);
|
bool Bind(StreamSocket* socket, struct sockaddr_un* addr, size_t addrsize);
|
||||||
|
@ -114,6 +112,7 @@ public:
|
||||||
public: /* For use by Manager. */
|
public: /* For use by Manager. */
|
||||||
PollChannel accept_poll_channel;
|
PollChannel accept_poll_channel;
|
||||||
Ref<Manager> manager;
|
Ref<Manager> manager;
|
||||||
|
Ref<Descriptor> root;
|
||||||
PipeEndpoint incoming;
|
PipeEndpoint incoming;
|
||||||
PipeEndpoint outgoing;
|
PipeEndpoint outgoing;
|
||||||
StreamSocket* prev_socket;
|
StreamSocket* prev_socket;
|
||||||
|
@ -189,6 +188,7 @@ StreamSocket::StreamSocket(uid_t owner, gid_t group, mode_t mode,
|
||||||
this->is_connected = false;
|
this->is_connected = false;
|
||||||
this->is_refused = false;
|
this->is_refused = false;
|
||||||
this->manager = manager;
|
this->manager = manager;
|
||||||
|
this->root = CurrentProcess()->GetRoot();
|
||||||
this->socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
this->socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
this->pending_cond = KTHREAD_COND_INITIALIZER;
|
this->pending_cond = KTHREAD_COND_INITIALIZER;
|
||||||
this->accepted_cond = KTHREAD_COND_INITIALIZER;
|
this->accepted_cond = KTHREAD_COND_INITIALIZER;
|
||||||
|
@ -476,20 +476,19 @@ int StreamSocket::getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager::Manager(uid_t owner, gid_t group, mode_t mode)
|
Manager::Manager()
|
||||||
{
|
{
|
||||||
inode_type = INODE_TYPE_UNKNOWN;
|
|
||||||
dev = (dev_t) this;
|
|
||||||
ino = 0;
|
|
||||||
this->type = S_IFDIR;
|
|
||||||
this->stat_uid = owner;
|
|
||||||
this->stat_gid = group;
|
|
||||||
this->stat_mode = (mode & S_SETABLE) | this->type;
|
|
||||||
this->manager_lock = KTHREAD_MUTEX_INITIALIZER;
|
this->manager_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
this->first_server = NULL;
|
this->first_server = NULL;
|
||||||
this->last_server = NULL;
|
this->last_server = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Manager::~Manager()
|
||||||
|
{
|
||||||
|
assert(!first_server);
|
||||||
|
assert(!last_server);
|
||||||
|
}
|
||||||
|
|
||||||
static int CompareAddress(const struct sockaddr_un* a,
|
static int CompareAddress(const struct sockaddr_un* a,
|
||||||
const struct sockaddr_un* b)
|
const struct sockaddr_un* b)
|
||||||
{
|
{
|
||||||
|
@ -498,8 +497,10 @@ static int CompareAddress(const struct sockaddr_un* a,
|
||||||
|
|
||||||
StreamSocket* Manager::LookupServer(struct sockaddr_un* address)
|
StreamSocket* Manager::LookupServer(struct sockaddr_un* address)
|
||||||
{
|
{
|
||||||
|
Ref<Descriptor> root = CurrentProcess()->GetRoot();
|
||||||
for ( StreamSocket* iter = first_server; iter; iter = iter->next_socket )
|
for ( StreamSocket* iter = first_server; iter; iter = iter->next_socket )
|
||||||
if ( CompareAddress(iter->name, address) == 0 )
|
if ( CompareAddress(iter->name, address) == 0 &&
|
||||||
|
iter->root->dev == root->dev && iter->root->ino == root->ino )
|
||||||
return iter;
|
return iter;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -664,24 +665,11 @@ bool Manager::Connect(StreamSocket* socket,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support a poll method in Manager.
|
|
||||||
|
|
||||||
Ref<Inode> Manager::open(ioctx_t* /*ctx*/, const char* filename,
|
|
||||||
int /*flags*/, mode_t /*mode*/)
|
|
||||||
{
|
|
||||||
if ( !strcmp(filename, "stream") )
|
|
||||||
{
|
|
||||||
StreamSocket* socket = new StreamSocket(0, 0, 0666, Ref<Manager>(this));
|
|
||||||
return Ref<StreamSocket>(socket);
|
|
||||||
}
|
|
||||||
return errno = ENOENT, Ref<Inode>(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Ref<Manager> manager;
|
static Ref<Manager> manager;
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
manager = Ref<Manager>(new Manager(0, 0, 0600));
|
manager = Ref<Manager>(new Manager());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Inode> Socket(int type, int protocol)
|
Ref<Inode> Socket(int type, int protocol)
|
||||||
|
|
|
@ -202,10 +202,10 @@ PingSocket::PingSocket(int af)
|
||||||
dev = (dev_t) this;
|
dev = (dev_t) this;
|
||||||
ino = (ino_t) this;
|
ino = (ino_t) this;
|
||||||
type = S_IFSOCK;
|
type = S_IFSOCK;
|
||||||
kthread_mutex_lock(&process->idlock);
|
kthread_mutex_lock(&process->id_lock);
|
||||||
stat_uid = process->uid;
|
stat_uid = process->uid;
|
||||||
stat_gid = process->gid;
|
stat_gid = process->gid;
|
||||||
kthread_mutex_unlock(&process->idlock);
|
kthread_mutex_unlock(&process->id_lock);
|
||||||
stat_mode = 0600 | this->type;
|
stat_mode = 0600 | this->type;
|
||||||
supports_iovec = true;
|
supports_iovec = true;
|
||||||
socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
|
@ -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()
|
||||||
|
@ -2354,10 +2417,10 @@ TCPSocketNode::TCPSocketNode(TCPSocket* socket)
|
||||||
dev = (dev_t) this;
|
dev = (dev_t) this;
|
||||||
ino = (ino_t) this;
|
ino = (ino_t) this;
|
||||||
type = S_IFSOCK;
|
type = S_IFSOCK;
|
||||||
kthread_mutex_lock(&process->idlock);
|
kthread_mutex_lock(&process->id_lock);
|
||||||
stat_uid = process->uid;
|
stat_uid = process->uid;
|
||||||
stat_gid = process->gid;
|
stat_gid = process->gid;
|
||||||
kthread_mutex_unlock(&process->idlock);
|
kthread_mutex_unlock(&process->id_lock);
|
||||||
stat_mode = 0600 | this->type;
|
stat_mode = 0600 | this->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
|
@ -175,10 +175,10 @@ UDPSocket::UDPSocket(int af)
|
||||||
dev = (dev_t) this;
|
dev = (dev_t) this;
|
||||||
ino = (ino_t) this;
|
ino = (ino_t) this;
|
||||||
type = S_IFSOCK;
|
type = S_IFSOCK;
|
||||||
kthread_mutex_lock(&process->idlock);
|
kthread_mutex_lock(&process->id_lock);
|
||||||
stat_uid = process->uid;
|
stat_uid = process->uid;
|
||||||
stat_gid = process->gid;
|
stat_gid = process->gid;
|
||||||
kthread_mutex_unlock(&process->idlock);
|
kthread_mutex_unlock(&process->id_lock);
|
||||||
stat_mode = 0600 | this->type;
|
stat_mode = 0600 | this->type;
|
||||||
supports_iovec = true;
|
supports_iovec = true;
|
||||||
socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -309,6 +309,7 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds,
|
||||||
{
|
{
|
||||||
if ( !kthread_cond_wait_signal(&wakeup_cond, &wakeup_mutex) )
|
if ( !kthread_cond_wait_signal(&wakeup_cond, &wakeup_mutex) )
|
||||||
{
|
{
|
||||||
|
ret = -1;
|
||||||
errno = -EINTR;
|
errno = -EINTR;
|
||||||
self_woken = true;
|
self_woken = true;
|
||||||
deliver_signal = true;
|
deliver_signal = true;
|
||||||
|
@ -327,7 +328,7 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds,
|
||||||
timer.Detach();
|
timer.Detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !unexpected_error )
|
if ( !deliver_signal && !unexpected_error )
|
||||||
{
|
{
|
||||||
int num_events = 0;
|
int num_events = 0;
|
||||||
for ( size_t i = 0; i < reqs; i++ )
|
for ( size_t i = 0; i < reqs; i++ )
|
||||||
|
@ -346,6 +347,9 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds,
|
||||||
if ( 0 <= ret )
|
if ( 0 <= ret )
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
|
if ( Signal::IsPending() )
|
||||||
|
ret = -1, errno = EINTR, deliver_signal = true;
|
||||||
|
|
||||||
if ( user_sigmask )
|
if ( user_sigmask )
|
||||||
{
|
{
|
||||||
if ( !deliver_signal )
|
if ( !deliver_signal )
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2016, 2021-2022 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011-2016, 2021-2024 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
|
||||||
|
@ -73,23 +73,21 @@ kthread_mutex_t process_family_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
// The system is shutting down and creation of additional processes and threads
|
// The system is shutting down and creation of additional processes and threads
|
||||||
// should be prevented. Protected by process_family_lock.
|
// should be prevented. Protected by process_family_lock.
|
||||||
static bool is_init_exiting = false;
|
|
||||||
|
|
||||||
Process::Process()
|
Process::Process()
|
||||||
{
|
{
|
||||||
program_image_path = NULL;
|
program_image_path = NULL;
|
||||||
addrspace = 0;
|
addrspace = 0;
|
||||||
pid = 0;
|
pid = 0;
|
||||||
|
|
||||||
nicelock = KTHREAD_MUTEX_INITIALIZER;
|
nice_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
nice = 0;
|
nice = 0;
|
||||||
|
|
||||||
idlock = KTHREAD_MUTEX_INITIALIZER;
|
id_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
uid = euid = 0;
|
uid = euid = 0;
|
||||||
gid = egid = 0;
|
gid = egid = 0;
|
||||||
umask = 0022;
|
umask = 0022;
|
||||||
|
|
||||||
ptrlock = KTHREAD_MUTEX_INITIALIZER;
|
ptr_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
// tty set to null reference in the member constructor.
|
// tty set to null reference in the member constructor.
|
||||||
// root set to null reference in the member constructor.
|
// root set to null reference in the member constructor.
|
||||||
// cwd set to null reference in the member constructor.
|
// cwd set to null reference in the member constructor.
|
||||||
|
@ -118,26 +116,31 @@ Process::Process()
|
||||||
sigreturn = NULL;
|
sigreturn = NULL;
|
||||||
|
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
prevsibling = NULL;
|
prev_sibling = NULL;
|
||||||
nextsibling = NULL;
|
next_sibling = NULL;
|
||||||
firstchild = NULL;
|
first_child = NULL;
|
||||||
zombiechild = NULL;
|
zombie_child = NULL;
|
||||||
group = NULL;
|
group = NULL;
|
||||||
groupprev = NULL;
|
group_prev = NULL;
|
||||||
groupnext = NULL;
|
group_next = NULL;
|
||||||
groupfirst = NULL;
|
group_first = NULL;
|
||||||
session = NULL;
|
session = NULL;
|
||||||
sessionprev = NULL;
|
session_prev = NULL;
|
||||||
sessionnext = NULL;
|
session_next = NULL;
|
||||||
sessionfirst = NULL;
|
session_first = NULL;
|
||||||
zombiecond = KTHREAD_COND_INITIALIZER;
|
init = NULL;
|
||||||
iszombie = false;
|
init_prev = NULL;
|
||||||
nozombify = false;
|
init_next = NULL;
|
||||||
|
init_first = NULL;
|
||||||
|
zombie_cond = KTHREAD_COND_INITIALIZER;
|
||||||
|
is_zombie = false;
|
||||||
|
no_zombify = false;
|
||||||
limbo = false;
|
limbo = false;
|
||||||
|
is_init_exiting = false;
|
||||||
exit_code = -1;
|
exit_code = -1;
|
||||||
|
|
||||||
firstthread = NULL;
|
first_thread = NULL;
|
||||||
threadlock = KTHREAD_MUTEX_INITIALIZER;
|
thread_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
threads_not_exiting_count = 0;
|
threads_not_exiting_count = 0;
|
||||||
threads_exiting = false;
|
threads_exiting = false;
|
||||||
|
|
||||||
|
@ -167,13 +170,15 @@ Process::~Process() // process_family_lock taken
|
||||||
if ( alarm_timer.IsAttached() )
|
if ( alarm_timer.IsAttached() )
|
||||||
alarm_timer.Detach();
|
alarm_timer.Detach();
|
||||||
delete[] program_image_path;
|
delete[] program_image_path;
|
||||||
assert(!zombiechild);
|
assert(!zombie_child);
|
||||||
|
assert(!init);
|
||||||
assert(!session);
|
assert(!session);
|
||||||
assert(!group);
|
assert(!group);
|
||||||
assert(!parent);
|
assert(!parent);
|
||||||
assert(!sessionfirst);
|
assert(!init_first);
|
||||||
assert(!groupfirst);
|
assert(!session_first);
|
||||||
assert(!firstchild);
|
assert(!group_first);
|
||||||
|
assert(!first_child);
|
||||||
assert(!addrspace);
|
assert(!addrspace);
|
||||||
assert(!segments);
|
assert(!segments);
|
||||||
assert(!dtable);
|
assert(!dtable);
|
||||||
|
@ -190,7 +195,7 @@ Process::~Process() // process_family_lock taken
|
||||||
|
|
||||||
void Process::BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtable)
|
void Process::BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtable)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(!this->dtable);
|
assert(!this->dtable);
|
||||||
assert(!this->mtable);
|
assert(!this->mtable);
|
||||||
this->dtable = dtable;
|
this->dtable = dtable;
|
||||||
|
@ -199,7 +204,7 @@ void Process::BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtabl
|
||||||
|
|
||||||
void Process::BootstrapDirectories(Ref<Descriptor> root)
|
void Process::BootstrapDirectories(Ref<Descriptor> root)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(!this->root);
|
assert(!this->root);
|
||||||
assert(!this->cwd);
|
assert(!this->cwd);
|
||||||
this->root = root;
|
this->root = root;
|
||||||
|
@ -208,9 +213,6 @@ void Process::BootstrapDirectories(Ref<Descriptor> root)
|
||||||
|
|
||||||
void Process::OnLastThreadExit()
|
void Process::OnLastThreadExit()
|
||||||
{
|
{
|
||||||
Process* init = Scheduler::GetInitProcess();
|
|
||||||
assert(init);
|
|
||||||
|
|
||||||
// Child processes can't be reparented away if we're init. The system is
|
// Child processes can't be reparented away if we're init. The system is
|
||||||
// about to shut down, so broadcast SIGKILL every process and wait for every
|
// about to shut down, so broadcast SIGKILL every process and wait for every
|
||||||
// single process to exit. The operating system is finished when init has
|
// single process to exit. The operating system is finished when init has
|
||||||
|
@ -221,36 +223,43 @@ void Process::OnLastThreadExit()
|
||||||
// Forbid any more processes and threads from being created, so this
|
// Forbid any more processes and threads from being created, so this
|
||||||
// loop will always terminate.
|
// loop will always terminate.
|
||||||
is_init_exiting = true;
|
is_init_exiting = true;
|
||||||
kthread_mutex_lock(&ptrlock);
|
Process* process = first_child;
|
||||||
for ( pid_t pid = ptable->Next(0); 0 < pid; pid = ptable->Next(pid) )
|
while ( process )
|
||||||
{
|
{
|
||||||
Process* process = ptable->Get(pid);
|
if ( process->pid != 0 )
|
||||||
if ( process->pid != 0 && process != init )
|
|
||||||
process->DeliverSignal(SIGKILL);
|
process->DeliverSignal(SIGKILL);
|
||||||
|
if ( process->init == process )
|
||||||
|
process->is_init_exiting = true;
|
||||||
|
if ( process->first_child )
|
||||||
|
process = process->first_child;
|
||||||
|
while ( process && process != this && !process->next_sibling )
|
||||||
|
process = process->parent;
|
||||||
|
if ( process == this )
|
||||||
|
break;
|
||||||
|
process = process->next_sibling;
|
||||||
}
|
}
|
||||||
kthread_mutex_unlock(&ptrlock);
|
// NotifyChildExit always signals zombie_cond for init when
|
||||||
// NotifyChildExit always signals zombiecond for init when
|
|
||||||
// is_init_exiting is true.
|
// is_init_exiting is true.
|
||||||
while ( firstchild )
|
while ( first_child )
|
||||||
kthread_cond_wait(&zombiecond, &process_family_lock);
|
kthread_cond_wait(&zombie_cond, &process_family_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::OnThreadDestruction(Thread* thread)
|
void Process::OnThreadDestruction(Thread* thread)
|
||||||
{
|
{
|
||||||
assert(thread->process == this);
|
assert(thread->process == this);
|
||||||
kthread_mutex_lock(&threadlock);
|
kthread_mutex_lock(&thread_lock);
|
||||||
if ( thread->prevsibling )
|
if ( thread->prev_sibling )
|
||||||
thread->prevsibling->nextsibling = thread->nextsibling;
|
thread->prev_sibling->next_sibling = thread->next_sibling;
|
||||||
if ( thread->nextsibling )
|
if ( thread->next_sibling )
|
||||||
thread->nextsibling->prevsibling = thread->prevsibling;
|
thread->next_sibling->prev_sibling = thread->prev_sibling;
|
||||||
if ( thread == firstthread )
|
if ( thread == first_thread )
|
||||||
firstthread = thread->nextsibling;
|
first_thread = thread->next_sibling;
|
||||||
if ( firstthread )
|
if ( first_thread )
|
||||||
firstthread->prevsibling = NULL;
|
first_thread->prev_sibling = NULL;
|
||||||
thread->prevsibling = thread->nextsibling = NULL;
|
thread->prev_sibling = thread->next_sibling = NULL;
|
||||||
bool threadsleft = firstthread;
|
bool threadsleft = first_thread;
|
||||||
kthread_mutex_unlock(&threadlock);
|
kthread_mutex_unlock(&thread_lock);
|
||||||
|
|
||||||
// We are called from the threads destructor, let it finish before we
|
// We are called from the threads destructor, let it finish before we
|
||||||
// we handle the situation by killing ourselves.
|
// we handle the situation by killing ourselves.
|
||||||
|
@ -266,7 +275,7 @@ void Process__AfterLastThreadExit(void* user)
|
||||||
void Process::ScheduleDeath()
|
void Process::ScheduleDeath()
|
||||||
{
|
{
|
||||||
// All our threads must have exited at this point.
|
// All our threads must have exited at this point.
|
||||||
assert(!firstthread);
|
assert(!first_thread);
|
||||||
Worker::Schedule(Process__AfterLastThreadExit, this);
|
Worker::Schedule(Process__AfterLastThreadExit, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +284,7 @@ void Process::ScheduleDeath()
|
||||||
// process after this call as another thread may garbage collect it.
|
// process after this call as another thread may garbage collect it.
|
||||||
void Process::AbortConstruction()
|
void Process::AbortConstruction()
|
||||||
{
|
{
|
||||||
nozombify = true;
|
no_zombify = true;
|
||||||
ScheduleDeath();
|
ScheduleDeath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +311,7 @@ void Process::LastPrayer()
|
||||||
{
|
{
|
||||||
assert(this);
|
assert(this);
|
||||||
// This must never be called twice.
|
// This must never be called twice.
|
||||||
assert(!iszombie);
|
assert(!is_zombie);
|
||||||
|
|
||||||
// This must be called from a thread using another address space as the
|
// This must be called from a thread using another address space as the
|
||||||
// address space of this process is about to be destroyed.
|
// address space of this process is about to be destroyed.
|
||||||
|
@ -310,7 +319,7 @@ void Process::LastPrayer()
|
||||||
assert(curthread->process != this);
|
assert(curthread->process != this);
|
||||||
|
|
||||||
// This can't be called if the process is still alive.
|
// This can't be called if the process is still alive.
|
||||||
assert(!firstthread);
|
assert(!first_thread);
|
||||||
|
|
||||||
// Disarm and detach all the timers in the process.
|
// Disarm and detach all the timers in the process.
|
||||||
DeleteTimers();
|
DeleteTimers();
|
||||||
|
@ -339,45 +348,43 @@ void Process::LastPrayer()
|
||||||
|
|
||||||
ScopedLock family_lock(&process_family_lock);
|
ScopedLock family_lock(&process_family_lock);
|
||||||
|
|
||||||
iszombie = true;
|
is_zombie = true;
|
||||||
|
|
||||||
// Init is nice and will gladly raise our orphaned children and zombies.
|
// Init is nice and will gladly raise our orphaned children and zombies.
|
||||||
Process* init = Scheduler::GetInitProcess();
|
|
||||||
assert(init);
|
|
||||||
|
|
||||||
// Child processes can't be reparented away if we're init. OnLastThreadExit
|
// Child processes can't be reparented away if we're init. OnLastThreadExit
|
||||||
// must have already killed all the child processes and prevented more from
|
// must have already killed all the child processes and prevented more from
|
||||||
// being created.
|
// being created.
|
||||||
|
assert(init);
|
||||||
if ( init == this )
|
if ( init == this )
|
||||||
{
|
{
|
||||||
assert(is_init_exiting);
|
assert(is_init_exiting);
|
||||||
assert(!firstchild);
|
assert(!first_child);
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( firstchild )
|
while ( first_child )
|
||||||
{
|
{
|
||||||
Process* process = firstchild;
|
Process* process = first_child;
|
||||||
firstchild = process->nextsibling;
|
first_child = process->next_sibling;
|
||||||
process->parent = init;
|
process->parent = init;
|
||||||
process->prevsibling = NULL;
|
process->prev_sibling = NULL;
|
||||||
process->nextsibling = init->firstchild;
|
process->next_sibling = init->first_child;
|
||||||
if ( init->firstchild )
|
if ( init->first_child )
|
||||||
init->firstchild->prevsibling = process;
|
init->first_child->prev_sibling = process;
|
||||||
init->firstchild = process;
|
init->first_child = process;
|
||||||
process->nozombify = true;
|
process->no_zombify = true;
|
||||||
}
|
}
|
||||||
// Since we have no more children (they are with init now), we don't
|
// Since we have no more children (they are with init now), we don't
|
||||||
// have to worry about new zombie processes showing up, so just collect
|
// have to worry about new zombie processes showing up, so just collect
|
||||||
// those that are left. Then we satisfiy the invariant !zombiechild that
|
// those that are left. Then we satisfiy the invariant !zombie_child that
|
||||||
// applies on process termination.
|
// applies on process termination.
|
||||||
while ( zombiechild )
|
while ( zombie_child )
|
||||||
{
|
{
|
||||||
Process* zombie = zombiechild;
|
Process* zombie = zombie_child;
|
||||||
zombiechild = zombie->nextsibling;
|
zombie_child = zombie->next_sibling;
|
||||||
zombie->nextsibling = NULL;
|
zombie->next_sibling = NULL;
|
||||||
if ( zombiechild )
|
if ( zombie_child )
|
||||||
zombiechild->prevsibling = NULL;
|
zombie_child->prev_sibling = NULL;
|
||||||
zombie->nozombify = true;
|
zombie->no_zombify = true;
|
||||||
zombie->WaitedFor();
|
zombie->WaitedFor();
|
||||||
}
|
}
|
||||||
// Remove ourself from our process group.
|
// Remove ourself from our process group.
|
||||||
|
@ -386,8 +393,11 @@ void Process::LastPrayer()
|
||||||
// Remove ourself from our session.
|
// Remove ourself from our session.
|
||||||
if ( session )
|
if ( session )
|
||||||
session->SessionRemoveMember(this);
|
session->SessionRemoveMember(this);
|
||||||
|
// Remove ourself from our init.
|
||||||
|
if ( init )
|
||||||
|
init->InitRemoveMember(this);
|
||||||
|
|
||||||
bool zombify = !nozombify;
|
bool zombify = !no_zombify;
|
||||||
|
|
||||||
// This class instance will be destroyed by our parent process when it
|
// This class instance will be destroyed by our parent process when it
|
||||||
// has received and acknowledged our death.
|
// has received and acknowledged our death.
|
||||||
|
@ -403,7 +413,7 @@ void Process::WaitedFor() // process_family_lock taken
|
||||||
{
|
{
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
limbo = false;
|
limbo = false;
|
||||||
if ( groupfirst || sessionfirst )
|
if ( group_first || session_first || init_first )
|
||||||
limbo = true;
|
limbo = true;
|
||||||
if ( !limbo )
|
if ( !limbo )
|
||||||
delete this;
|
delete this;
|
||||||
|
@ -429,12 +439,12 @@ void Process::ResetAddressSpace()
|
||||||
void Process::GroupRemoveMember(Process* child) // process_family_lock taken
|
void Process::GroupRemoveMember(Process* child) // process_family_lock taken
|
||||||
{
|
{
|
||||||
assert(child->group == this);
|
assert(child->group == this);
|
||||||
if ( child->groupprev )
|
if ( child->group_prev )
|
||||||
child->groupprev->groupnext = child->groupnext;
|
child->group_prev->group_next = child->group_next;
|
||||||
else
|
else
|
||||||
groupfirst = child->groupnext;
|
group_first = child->group_next;
|
||||||
if ( child->groupnext )
|
if ( child->group_next )
|
||||||
child->groupnext->groupprev = child->groupprev;
|
child->group_next->group_prev = child->group_prev;
|
||||||
child->group = NULL;
|
child->group = NULL;
|
||||||
if ( IsLimboDone() )
|
if ( IsLimboDone() )
|
||||||
delete this;
|
delete this;
|
||||||
|
@ -443,47 +453,61 @@ void Process::GroupRemoveMember(Process* child) // process_family_lock taken
|
||||||
void Process::SessionRemoveMember(Process* child) // process_family_lock taken
|
void Process::SessionRemoveMember(Process* child) // process_family_lock taken
|
||||||
{
|
{
|
||||||
assert(child->session == this);
|
assert(child->session == this);
|
||||||
if ( child->sessionprev )
|
if ( child->session_prev )
|
||||||
child->sessionprev->sessionnext = child->sessionnext;
|
child->session_prev->session_next = child->session_next;
|
||||||
else
|
else
|
||||||
sessionfirst = child->sessionnext;
|
session_first = child->session_next;
|
||||||
if ( child->sessionnext )
|
if ( child->session_next )
|
||||||
child->sessionnext->sessionprev = child->sessionprev;
|
child->session_next->session_prev = child->session_prev;
|
||||||
child->session = NULL;
|
child->session = NULL;
|
||||||
if ( !sessionfirst )
|
if ( !session_first )
|
||||||
{
|
{
|
||||||
// Remove reference to tty when session is empty.
|
// Remove reference to tty when session is empty.
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
tty.Reset();
|
tty.Reset();
|
||||||
}
|
}
|
||||||
if ( IsLimboDone() )
|
if ( IsLimboDone() )
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Process::InitRemoveMember(Process* child) // process_family_lock taken
|
||||||
|
{
|
||||||
|
assert(child->init == this);
|
||||||
|
if ( child->init_prev )
|
||||||
|
child->init_prev->init_next = child->init_next;
|
||||||
|
else
|
||||||
|
init_first = child->init_next;
|
||||||
|
if ( child->init_next )
|
||||||
|
child->init_next->init_prev = child->init_prev;
|
||||||
|
child->init = NULL;
|
||||||
|
if ( IsLimboDone() )
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
bool Process::IsLimboDone() // process_family_lock taken
|
bool Process::IsLimboDone() // process_family_lock taken
|
||||||
{
|
{
|
||||||
return limbo && !groupfirst && !sessionfirst;
|
return limbo && !group_first && !session_first && !init_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
// process_family_lock taken
|
// process_family_lock taken
|
||||||
void Process::NotifyChildExit(Process* child, bool zombify)
|
void Process::NotifyChildExit(Process* child, bool zombify)
|
||||||
{
|
{
|
||||||
if ( child->prevsibling )
|
if ( child->prev_sibling )
|
||||||
child->prevsibling->nextsibling = child->nextsibling;
|
child->prev_sibling->next_sibling = child->next_sibling;
|
||||||
if ( child->nextsibling )
|
if ( child->next_sibling )
|
||||||
child->nextsibling->prevsibling = child->prevsibling;
|
child->next_sibling->prev_sibling = child->prev_sibling;
|
||||||
if ( firstchild == child )
|
if ( first_child == child )
|
||||||
firstchild = child->nextsibling;
|
first_child = child->next_sibling;
|
||||||
if ( firstchild )
|
if ( first_child )
|
||||||
firstchild->prevsibling = NULL;
|
first_child->prev_sibling = NULL;
|
||||||
|
|
||||||
if ( zombify )
|
if ( zombify )
|
||||||
{
|
{
|
||||||
if ( zombiechild )
|
if ( zombie_child )
|
||||||
zombiechild->prevsibling = child;
|
zombie_child->prev_sibling = child;
|
||||||
child->prevsibling = NULL;
|
child->prev_sibling = NULL;
|
||||||
child->nextsibling = zombiechild;
|
child->next_sibling = zombie_child;
|
||||||
zombiechild = child;
|
zombie_child = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify this parent process about the child exiting if it's meant to
|
// Notify this parent process about the child exiting if it's meant to
|
||||||
|
@ -491,8 +515,8 @@ void Process::NotifyChildExit(Process* child, bool zombify)
|
||||||
// when init is exiting, because OnLastThreadExit needs to be able to catch
|
// when init is exiting, because OnLastThreadExit needs to be able to catch
|
||||||
// every child exiting.
|
// every child exiting.
|
||||||
DeliverSignal(SIGCHLD);
|
DeliverSignal(SIGCHLD);
|
||||||
if ( zombify || (is_init_exiting && Scheduler::GetInitProcess() == this) )
|
if ( zombify || is_init_exiting )
|
||||||
kthread_cond_broadcast(&zombiecond);
|
kthread_cond_broadcast(&zombie_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
|
pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
|
||||||
|
@ -504,7 +528,7 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
|
||||||
ScopedLock lock(&process_family_lock);
|
ScopedLock lock(&process_family_lock);
|
||||||
|
|
||||||
// A process can only wait if it has children.
|
// A process can only wait if it has children.
|
||||||
if ( !firstchild && !zombiechild )
|
if ( !first_child && !zombie_child )
|
||||||
return errno = ECHILD, -1;
|
return errno = ECHILD, -1;
|
||||||
|
|
||||||
// Processes can only wait for their own children to exit.
|
// Processes can only wait for their own children to exit.
|
||||||
|
@ -513,11 +537,11 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
|
||||||
// TODO: This is a slow but multithread safe way to verify that the
|
// TODO: This is a slow but multithread safe way to verify that the
|
||||||
// target process has the correct parent.
|
// target process has the correct parent.
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for ( Process* p = firstchild; !found && p; p = p->nextsibling )
|
for ( Process* p = first_child; !found && p; p = p->next_sibling )
|
||||||
if ( p->pid == thepid && !p->nozombify )
|
if ( p->pid == thepid && !p->no_zombify )
|
||||||
found = true;
|
found = true;
|
||||||
for ( Process* p = zombiechild; !found && p; p = p->nextsibling )
|
for ( Process* p = zombie_child; !found && p; p = p->next_sibling )
|
||||||
if ( p->pid == thepid && !p->nozombify )
|
if ( p->pid == thepid && !p->no_zombify )
|
||||||
found = true;
|
found = true;
|
||||||
if ( !found )
|
if ( !found )
|
||||||
return errno = ECHILD, -1;
|
return errno = ECHILD, -1;
|
||||||
|
@ -526,26 +550,26 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
|
||||||
Process* zombie = NULL;
|
Process* zombie = NULL;
|
||||||
while ( !zombie )
|
while ( !zombie )
|
||||||
{
|
{
|
||||||
for ( zombie = zombiechild; zombie; zombie = zombie->nextsibling )
|
for ( zombie = zombie_child; zombie; zombie = zombie->next_sibling )
|
||||||
if ( (thepid == -1 || thepid == zombie->pid) && !zombie->nozombify )
|
if ( (thepid == -1 || thepid == zombie->pid) && !zombie->no_zombify )
|
||||||
break;
|
break;
|
||||||
if ( zombie )
|
if ( zombie )
|
||||||
break;
|
break;
|
||||||
if ( options & WNOHANG )
|
if ( options & WNOHANG )
|
||||||
return 0;
|
return 0;
|
||||||
if ( !kthread_cond_wait_signal(&zombiecond, &process_family_lock) )
|
if ( !kthread_cond_wait_signal(&zombie_cond, &process_family_lock) )
|
||||||
return errno = EINTR, -1;
|
return errno = EINTR, -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from the list of zombies.
|
// Remove from the list of zombies.
|
||||||
if ( zombie->prevsibling )
|
if ( zombie->prev_sibling )
|
||||||
zombie->prevsibling->nextsibling = zombie->nextsibling;
|
zombie->prev_sibling->next_sibling = zombie->next_sibling;
|
||||||
if ( zombie->nextsibling )
|
if ( zombie->next_sibling )
|
||||||
zombie->nextsibling->prevsibling = zombie->prevsibling;
|
zombie->next_sibling->prev_sibling = zombie->prev_sibling;
|
||||||
if ( zombiechild == zombie )
|
if ( zombie_child == zombie )
|
||||||
zombiechild = zombie->nextsibling;
|
zombie_child = zombie->next_sibling;
|
||||||
if ( zombiechild )
|
if ( zombie_child )
|
||||||
zombiechild->prevsibling = NULL;
|
zombie_child->prev_sibling = NULL;
|
||||||
|
|
||||||
thepid = zombie->pid;
|
thepid = zombie->pid;
|
||||||
|
|
||||||
|
@ -584,7 +608,7 @@ void Process::ExitThroughSignal(int signal)
|
||||||
|
|
||||||
void Process::ExitWithCode(int requested_exit_code)
|
void Process::ExitWithCode(int requested_exit_code)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&threadlock);
|
ScopedLock lock(&thread_lock);
|
||||||
if ( exit_code == -1 )
|
if ( exit_code == -1 )
|
||||||
exit_code = requested_exit_code;
|
exit_code = requested_exit_code;
|
||||||
|
|
||||||
|
@ -592,74 +616,74 @@ void Process::ExitWithCode(int requested_exit_code)
|
||||||
// of process termination. We simply can't stop the threads as they may
|
// of process termination. We simply can't stop the threads as they may
|
||||||
// be running in kernel mode doing dangerous stuff. This thread will be
|
// be running in kernel mode doing dangerous stuff. This thread will be
|
||||||
// destroyed by SIGKILL once the system call returns.
|
// destroyed by SIGKILL once the system call returns.
|
||||||
for ( Thread* t = firstthread; t; t = t->nextsibling )
|
for ( Thread* t = first_thread; t; t = t->next_sibling )
|
||||||
t->DeliverSignal(SIGKILL);
|
t->DeliverSignal(SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<MountTable> Process::GetMTable()
|
Ref<MountTable> Process::GetMTable()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(mtable);
|
assert(mtable);
|
||||||
return mtable;
|
return mtable;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<DescriptorTable> Process::GetDTable()
|
Ref<DescriptorTable> Process::GetDTable()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(dtable);
|
assert(dtable);
|
||||||
return dtable;
|
return dtable;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<ProcessTable> Process::GetPTable()
|
Ref<ProcessTable> Process::GetPTable()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(ptable);
|
assert(ptable);
|
||||||
return ptable;
|
return ptable;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Descriptor> Process::GetTTY()
|
Ref<Descriptor> Process::GetTTY()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
return tty;
|
return tty;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Descriptor> Process::GetRoot()
|
Ref<Descriptor> Process::GetRoot()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(root);
|
assert(root);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Descriptor> Process::GetCWD()
|
Ref<Descriptor> Process::GetCWD()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(cwd);
|
assert(cwd);
|
||||||
return cwd;
|
return cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::SetTTY(Ref<Descriptor> newtty)
|
void Process::SetTTY(Ref<Descriptor> newtty)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
tty = newtty;
|
tty = newtty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::SetRoot(Ref<Descriptor> newroot)
|
void Process::SetRoot(Ref<Descriptor> newroot)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(newroot);
|
assert(newroot);
|
||||||
root = newroot;
|
root = newroot;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::SetCWD(Ref<Descriptor> newcwd)
|
void Process::SetCWD(Ref<Descriptor> newcwd)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(newcwd);
|
assert(newcwd);
|
||||||
cwd = newcwd;
|
cwd = newcwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Descriptor> Process::GetDescriptor(int fd)
|
Ref<Descriptor> Process::GetDescriptor(int fd)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&ptrlock);
|
ScopedLock lock(&ptr_lock);
|
||||||
assert(dtable);
|
assert(dtable);
|
||||||
return dtable->Get(fd);
|
return dtable->Get(fd);
|
||||||
}
|
}
|
||||||
|
@ -704,7 +728,7 @@ Process* Process::Fork()
|
||||||
kthread_mutex_lock(&process_family_lock);
|
kthread_mutex_lock(&process_family_lock);
|
||||||
|
|
||||||
// Forbid the creation of new processes if init has exited.
|
// Forbid the creation of new processes if init has exited.
|
||||||
if ( is_init_exiting )
|
if ( init->is_init_exiting )
|
||||||
{
|
{
|
||||||
kthread_mutex_unlock(&process_family_lock);
|
kthread_mutex_unlock(&process_family_lock);
|
||||||
clone->AbortConstruction();
|
clone->AbortConstruction();
|
||||||
|
@ -721,25 +745,32 @@ Process* Process::Fork()
|
||||||
|
|
||||||
// Remember the relation to the child process.
|
// Remember the relation to the child process.
|
||||||
clone->parent = this;
|
clone->parent = this;
|
||||||
clone->nextsibling = firstchild;
|
clone->next_sibling = first_child;
|
||||||
clone->prevsibling = NULL;
|
clone->prev_sibling = NULL;
|
||||||
if ( firstchild )
|
if ( first_child )
|
||||||
firstchild->prevsibling = clone;
|
first_child->prev_sibling = clone;
|
||||||
firstchild = clone;
|
first_child = clone;
|
||||||
|
|
||||||
// Add the new process to the current process group.
|
// Add the new process to the current process group.
|
||||||
clone->group = group;
|
clone->group = group;
|
||||||
clone->groupprev = NULL;
|
clone->group_prev = NULL;
|
||||||
if ( (clone->groupnext = group->groupfirst) )
|
if ( (clone->group_next = group->group_first) )
|
||||||
group->groupfirst->groupprev = clone;
|
group->group_first->group_prev = clone;
|
||||||
group->groupfirst = clone;
|
group->group_first = clone;
|
||||||
|
|
||||||
// Add the new process to the current session.
|
// Add the new process to the current session.
|
||||||
clone->session = session;
|
clone->session = session;
|
||||||
clone->sessionprev = NULL;
|
clone->session_prev = NULL;
|
||||||
if ( (clone->sessionnext = session->sessionfirst) )
|
if ( (clone->session_next = session->session_first) )
|
||||||
session->sessionfirst->sessionprev = clone;
|
session->session_first->session_prev = clone;
|
||||||
session->sessionfirst = clone;
|
session->session_first = clone;
|
||||||
|
|
||||||
|
// Add the new process to the current init.
|
||||||
|
clone->init = init;
|
||||||
|
clone->init_prev = NULL;
|
||||||
|
if ( (clone->init_next = init->init_first) )
|
||||||
|
init->init_first->init_prev = clone;
|
||||||
|
init->init_first = clone;
|
||||||
|
|
||||||
kthread_mutex_unlock(&process_family_lock);
|
kthread_mutex_unlock(&process_family_lock);
|
||||||
|
|
||||||
|
@ -749,22 +780,22 @@ Process* Process::Fork()
|
||||||
clone->resource_limits[i] = resource_limits[i];
|
clone->resource_limits[i] = resource_limits[i];
|
||||||
kthread_mutex_unlock(&resource_limits_lock);
|
kthread_mutex_unlock(&resource_limits_lock);
|
||||||
|
|
||||||
kthread_mutex_lock(&nicelock);
|
kthread_mutex_lock(&nice_lock);
|
||||||
clone->nice = nice;
|
clone->nice = nice;
|
||||||
kthread_mutex_unlock(&nicelock);
|
kthread_mutex_unlock(&nice_lock);
|
||||||
|
|
||||||
kthread_mutex_lock(&ptrlock);
|
kthread_mutex_lock(&ptr_lock);
|
||||||
clone->root = root;
|
clone->root = root;
|
||||||
clone->cwd = cwd;
|
clone->cwd = cwd;
|
||||||
kthread_mutex_unlock(&ptrlock);
|
kthread_mutex_unlock(&ptr_lock);
|
||||||
|
|
||||||
kthread_mutex_lock(&idlock);
|
kthread_mutex_lock(&id_lock);
|
||||||
clone->uid = uid;
|
clone->uid = uid;
|
||||||
clone->gid = gid;
|
clone->gid = gid;
|
||||||
clone->euid = euid;
|
clone->euid = euid;
|
||||||
clone->egid = egid;
|
clone->egid = egid;
|
||||||
clone->umask = umask;
|
clone->umask = umask;
|
||||||
kthread_mutex_unlock(&idlock);
|
kthread_mutex_unlock(&id_lock);
|
||||||
|
|
||||||
kthread_mutex_lock(&signal_lock);
|
kthread_mutex_lock(&signal_lock);
|
||||||
memcpy(&clone->signal_actions, &signal_actions, sizeof(signal_actions));
|
memcpy(&clone->signal_actions, &signal_actions, sizeof(signal_actions));
|
||||||
|
@ -775,13 +806,13 @@ Process* Process::Fork()
|
||||||
// Initialize things that can fail and abort if needed.
|
// Initialize things that can fail and abort if needed.
|
||||||
bool failure = false;
|
bool failure = false;
|
||||||
|
|
||||||
kthread_mutex_lock(&ptrlock);
|
kthread_mutex_lock(&ptr_lock);
|
||||||
if ( !(clone->dtable = dtable->Fork()) )
|
if ( !(clone->dtable = dtable->Fork()) )
|
||||||
failure = true;
|
failure = true;
|
||||||
//if ( !(clone->mtable = mtable->Fork()) )
|
//if ( !(clone->mtable = mtable->Fork()) )
|
||||||
// failure = true;
|
// failure = true;
|
||||||
clone->mtable = mtable;
|
clone->mtable = mtable;
|
||||||
kthread_mutex_unlock(&ptrlock);
|
kthread_mutex_unlock(&ptr_lock);
|
||||||
|
|
||||||
if ( !(clone->program_image_path = String::Clone(program_image_path)) )
|
if ( !(clone->program_image_path = String::Clone(program_image_path)) )
|
||||||
failure = true;
|
failure = true;
|
||||||
|
@ -1475,26 +1506,24 @@ pid_t sys_tfork(int flags, struct tfork* user_regs)
|
||||||
|
|
||||||
// TODO: Is it a hack to create a new kernel stack here?
|
// TODO: Is it a hack to create a new kernel stack here?
|
||||||
Thread* curthread = CurrentThread();
|
Thread* curthread = CurrentThread();
|
||||||
size_t newkernelstacksize = curthread->kernelstacksize;
|
size_t newkernel_stack_size = curthread->kernel_stack_size;
|
||||||
uint8_t* newkernelstack = new uint8_t[newkernelstacksize + stack_alignment];
|
uint8_t* newkernelstack = new uint8_t[newkernel_stack_size + stack_alignment];
|
||||||
if ( !newkernelstack )
|
if ( !newkernelstack )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
uintptr_t stack_aligned = (uintptr_t) newkernelstack;
|
uintptr_t stack_aligned = (uintptr_t) newkernelstack;
|
||||||
size_t stack_aligned_size = newkernelstacksize;
|
size_t stack_aligned_size = newkernel_stack_size;
|
||||||
|
|
||||||
if ( ((uintptr_t) stack_aligned) & (stack_alignment-1) )
|
if ( ((uintptr_t) stack_aligned) & (stack_alignment-1) )
|
||||||
stack_aligned = (stack_aligned + 16) & ~(stack_alignment-1);
|
stack_aligned = (stack_aligned + 16) & ~(stack_alignment-1);
|
||||||
stack_aligned_size &= 0xFFFFFFF0;
|
stack_aligned_size &= 0xFFFFFFF0;
|
||||||
|
|
||||||
|
Process* parent_process = CurrentProcess();
|
||||||
Process* child_process;
|
Process* child_process;
|
||||||
if ( making_thread )
|
if ( making_thread )
|
||||||
child_process = CurrentProcess();
|
child_process = parent_process;
|
||||||
else if ( !(child_process = CurrentProcess()->Fork()) )
|
else if ( !(child_process = parent_process->Fork()) )
|
||||||
{
|
return delete[] newkernelstack, -1;
|
||||||
delete[] newkernelstack;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct thread_registers cpuregs;
|
struct thread_registers cpuregs;
|
||||||
memset(&cpuregs, 0, sizeof(cpuregs));
|
memset(&cpuregs, 0, sizeof(cpuregs));
|
||||||
|
@ -1550,7 +1579,7 @@ pid_t sys_tfork(int flags, struct tfork* user_regs)
|
||||||
|
|
||||||
// Forbid the creation of new threads if init has exited.
|
// Forbid the creation of new threads if init has exited.
|
||||||
ScopedLock process_family_lock_lock(&process_family_lock);
|
ScopedLock process_family_lock_lock(&process_family_lock);
|
||||||
if ( is_init_exiting )
|
if ( child_process->init->is_init_exiting )
|
||||||
return errno = EPERM, -1;
|
return errno = EPERM, -1;
|
||||||
|
|
||||||
// If the thread could not be created, make the process commit suicide
|
// If the thread could not be created, make the process commit suicide
|
||||||
|
@ -1565,9 +1594,9 @@ pid_t sys_tfork(int flags, struct tfork* user_regs)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->kernelstackpos = (addr_t) newkernelstack;
|
thread->kernel_stack_pos = (addr_t) newkernelstack;
|
||||||
thread->kernelstacksize = newkernelstacksize;
|
thread->kernel_stack_size = newkernel_stack_size;
|
||||||
thread->kernelstackmalloced = true;
|
thread->kernel_stack_malloced = true;
|
||||||
memcpy(&thread->signal_mask, ®s.sigmask, sizeof(sigset_t));
|
memcpy(&thread->signal_mask, ®s.sigmask, sizeof(sigset_t));
|
||||||
memcpy(&thread->signal_stack, ®s.altstack, sizeof(stack_t));
|
memcpy(&thread->signal_stack, ®s.altstack, sizeof(stack_t));
|
||||||
|
|
||||||
|
@ -1604,7 +1633,8 @@ pid_t sys_getpgid(pid_t pid)
|
||||||
pid_t sys_getsid(pid_t pid)
|
pid_t sys_getsid(pid_t pid)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&process_family_lock);
|
ScopedLock lock(&process_family_lock);
|
||||||
Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
|
Process* process =
|
||||||
|
!pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
|
||||||
if ( !process )
|
if ( !process )
|
||||||
return errno = ESRCH, -1;
|
return errno = ESRCH, -1;
|
||||||
if ( !process->session )
|
if ( !process->session )
|
||||||
|
@ -1612,6 +1642,16 @@ pid_t sys_getsid(pid_t pid)
|
||||||
return process->session->pid;
|
return process->session->pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pid_t sys_getinit(pid_t pid)
|
||||||
|
{
|
||||||
|
ScopedLock lock(&process_family_lock);
|
||||||
|
Process* process =
|
||||||
|
!pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
|
||||||
|
if ( !process->init )
|
||||||
|
return errno = ESRCH, -1;
|
||||||
|
return process->init->pid;
|
||||||
|
}
|
||||||
|
|
||||||
int sys_setpgid(pid_t pid, pid_t pgid)
|
int sys_setpgid(pid_t pid, pid_t pgid)
|
||||||
{
|
{
|
||||||
if ( pid < 0 || pgid < 0 )
|
if ( pid < 0 || pgid < 0 )
|
||||||
|
@ -1649,13 +1689,13 @@ int sys_setpgid(pid_t pid, pid_t pgid)
|
||||||
return errno = EPERM, -1;
|
return errno = EPERM, -1;
|
||||||
// The process must not be a process group leader.
|
// The process must not be a process group leader.
|
||||||
// TODO: Maybe POSIX actually allows this.
|
// TODO: Maybe POSIX actually allows this.
|
||||||
if ( process->groupfirst )
|
if ( process->group_first )
|
||||||
return errno = EPERM, -1;
|
return errno = EPERM, -1;
|
||||||
// The process must not be a session leader.
|
// The process must not be a session leader.
|
||||||
if ( process->sessionfirst )
|
if ( process->session_first )
|
||||||
return errno = EPERM, -1;
|
return errno = EPERM, -1;
|
||||||
// The group must either exist or be the process itself.
|
// The group must either exist or be the process itself.
|
||||||
if ( !group->groupfirst && group != process )
|
if ( !group->group_first && group != process )
|
||||||
return errno = EPERM, -1;
|
return errno = EPERM, -1;
|
||||||
|
|
||||||
// Exit early if this is a noop.
|
// Exit early if this is a noop.
|
||||||
|
@ -1667,11 +1707,11 @@ int sys_setpgid(pid_t pid, pid_t pgid)
|
||||||
process->group->GroupRemoveMember(process);
|
process->group->GroupRemoveMember(process);
|
||||||
|
|
||||||
// Insert the process into its new process group.
|
// Insert the process into its new process group.
|
||||||
process->groupprev = NULL;
|
process->group_prev = NULL;
|
||||||
process->groupnext = group->groupfirst;
|
process->group_next = group->group_first;
|
||||||
if ( group->groupfirst )
|
if ( group->group_first )
|
||||||
group->groupfirst->groupprev = process;
|
group->group_first->group_prev = process;
|
||||||
group->groupfirst = process;
|
group->group_first = process;
|
||||||
process->group = group;
|
process->group = group;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1696,15 +1736,58 @@ pid_t sys_setsid(void)
|
||||||
process->session->SessionRemoveMember(process);
|
process->session->SessionRemoveMember(process);
|
||||||
|
|
||||||
// Insert the process into its new session.
|
// Insert the process into its new session.
|
||||||
process->sessionprev = NULL;
|
process->session_prev = NULL;
|
||||||
process->sessionnext = NULL;
|
process->session_next = NULL;
|
||||||
process->sessionfirst = process;
|
process->session_first = process;
|
||||||
process->session = process;
|
process->session = process;
|
||||||
|
|
||||||
// Insert the process into its new process group.
|
// Insert the process into its new process group.
|
||||||
process->groupprev = NULL;
|
process->group_prev = NULL;
|
||||||
process->groupnext = NULL;
|
process->group_next = NULL;
|
||||||
process->groupfirst = process;
|
process->group_first = process;
|
||||||
|
process->group = process;
|
||||||
|
|
||||||
|
return process->pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sys_setinit(void)
|
||||||
|
{
|
||||||
|
Process* process = CurrentProcess();
|
||||||
|
|
||||||
|
ScopedLock lock(&process_family_lock);
|
||||||
|
|
||||||
|
// Test if already a process group leader.
|
||||||
|
if ( process->group == process )
|
||||||
|
return errno = EPERM, -1;
|
||||||
|
|
||||||
|
// Remove the process from its current process group.
|
||||||
|
if ( process->group )
|
||||||
|
process->group->GroupRemoveMember(process);
|
||||||
|
|
||||||
|
// Remove the process from its current session.
|
||||||
|
if ( process->session )
|
||||||
|
process->session->SessionRemoveMember(process);
|
||||||
|
|
||||||
|
// Remove the process from its current init.
|
||||||
|
if ( process->init )
|
||||||
|
process->init->InitRemoveMember(process);
|
||||||
|
|
||||||
|
// Insert the process into its new init.
|
||||||
|
process->init_prev = NULL;
|
||||||
|
process->init_next = NULL;
|
||||||
|
process->init_first = process;
|
||||||
|
process->init = process;
|
||||||
|
|
||||||
|
// Insert the process into its new session.
|
||||||
|
process->session_prev = NULL;
|
||||||
|
process->session_next = NULL;
|
||||||
|
process->session_first = process;
|
||||||
|
process->session = process;
|
||||||
|
|
||||||
|
// Insert the process into its new process group.
|
||||||
|
process->group_prev = NULL;
|
||||||
|
process->group_next = NULL;
|
||||||
|
process->group_first = process;
|
||||||
process->group = process;
|
process->group = process;
|
||||||
|
|
||||||
return process->pid;
|
return process->pid;
|
||||||
|
@ -1718,7 +1801,7 @@ size_t sys_getpagesize(void)
|
||||||
mode_t sys_umask(mode_t newmask)
|
mode_t sys_umask(mode_t newmask)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
mode_t oldmask = process->umask;
|
mode_t oldmask = process->umask;
|
||||||
process->umask = newmask & 0666;
|
process->umask = newmask & 0666;
|
||||||
return oldmask;
|
return oldmask;
|
||||||
|
@ -1727,7 +1810,7 @@ mode_t sys_umask(mode_t newmask)
|
||||||
mode_t sys_getumask(void)
|
mode_t sys_getumask(void)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ScopedLock lock(&process->idlock);
|
ScopedLock lock(&process->id_lock);
|
||||||
return process->umask;
|
return process->umask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2016, 2022 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2015, 2016, 2022, 2024 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
|
||||||
|
@ -72,8 +72,8 @@ int sys_psctl(pid_t pid, int request, void* ptr)
|
||||||
{
|
{
|
||||||
Process* parent = process->parent;
|
Process* parent = process->parent;
|
||||||
psst.ppid = parent->pid;
|
psst.ppid = parent->pid;
|
||||||
psst.ppid_prev = process->prevsibling ? process->prevsibling->pid : -1;
|
psst.ppid_prev = process->prev_sibling ? process->prev_sibling->pid : -1;
|
||||||
psst.ppid_next = process->nextsibling ? process->nextsibling->pid : -1;
|
psst.ppid_next = process->next_sibling ? process->next_sibling->pid : -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -81,13 +81,13 @@ int sys_psctl(pid_t pid, int request, void* ptr)
|
||||||
psst.ppid_prev = -1;
|
psst.ppid_prev = -1;
|
||||||
psst.ppid_next = -1;
|
psst.ppid_next = -1;
|
||||||
}
|
}
|
||||||
psst.ppid_first = process->firstchild ? process->firstchild->pid : -1;
|
psst.ppid_first = process->first_child ? process->first_child->pid : -1;
|
||||||
if ( process->group )
|
if ( process->group )
|
||||||
{
|
{
|
||||||
Process* group = process->group;
|
Process* group = process->group;
|
||||||
psst.pgid = group->pid;
|
psst.pgid = group->pid;
|
||||||
psst.pgid_prev = process->groupprev ? process->groupprev->pid : -1;
|
psst.pgid_prev = process->group_prev ? process->group_prev->pid : -1;
|
||||||
psst.pgid_next = process->groupnext ? process->groupnext->pid : -1;
|
psst.pgid_next = process->group_next ? process->group_next->pid : -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -95,13 +95,13 @@ int sys_psctl(pid_t pid, int request, void* ptr)
|
||||||
psst.pgid_prev = -1;
|
psst.pgid_prev = -1;
|
||||||
psst.pgid_next = -1;
|
psst.pgid_next = -1;
|
||||||
}
|
}
|
||||||
psst.pgid_first = process->groupfirst ? process->groupfirst->pid : -1;
|
psst.pgid_first = process->group_first ? process->group_first->pid : -1;
|
||||||
if ( process->session )
|
if ( process->session )
|
||||||
{
|
{
|
||||||
Process* session = process->session;
|
Process* session = process->session;
|
||||||
psst.sid = session->pid;
|
psst.sid = session->pid;
|
||||||
psst.sid_prev = process->sessionprev ? process->sessionprev->pid : -1;
|
psst.sid_prev = process->session_prev ? process->session_prev->pid : -1;
|
||||||
psst.sid_next = process->sessionnext ? process->sessionnext->pid : -1;
|
psst.sid_next = process->session_next ? process->session_next->pid : -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -109,24 +109,34 @@ int sys_psctl(pid_t pid, int request, void* ptr)
|
||||||
psst.sid_prev = -1;
|
psst.sid_prev = -1;
|
||||||
psst.sid_next = -1;
|
psst.sid_next = -1;
|
||||||
}
|
}
|
||||||
psst.sid_first = process->sessionfirst ? process->sessionfirst->pid : -1;
|
psst.sid_first = process->session_first ? process->session_first->pid : -1;
|
||||||
// TODO: Implement init groupings.
|
|
||||||
psst.init = 1;
|
if ( process->init )
|
||||||
psst.init_prev = ptable->Prev(pid);
|
{
|
||||||
psst.init_next = ptable->Next(pid);
|
Process* init = process->init;
|
||||||
psst.init_first = pid == 1 ? 1 : -1;
|
psst.init = init->pid;
|
||||||
kthread_mutex_lock(&process->idlock);
|
psst.init_prev = process->init_prev ? process->init_prev->pid : -1;
|
||||||
|
psst.init_next = process->init_next ? process->init_next->pid : -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
psst.init = -1;
|
||||||
|
psst.init_prev = -1;
|
||||||
|
psst.init_next = -1;
|
||||||
|
}
|
||||||
|
psst.init_first = process->init_first ? process->init_first->pid : -1;
|
||||||
|
kthread_mutex_lock(&process->id_lock);
|
||||||
psst.uid = process->uid;
|
psst.uid = process->uid;
|
||||||
psst.euid = process->euid;
|
psst.euid = process->euid;
|
||||||
psst.gid = process->gid;
|
psst.gid = process->gid;
|
||||||
psst.egid = process->egid;
|
psst.egid = process->egid;
|
||||||
kthread_mutex_unlock(&process->idlock);
|
kthread_mutex_unlock(&process->id_lock);
|
||||||
kthread_mutex_lock(&process->threadlock);
|
kthread_mutex_lock(&process->thread_lock);
|
||||||
psst.status = process->exit_code;
|
psst.status = process->exit_code;
|
||||||
kthread_mutex_unlock(&process->threadlock);
|
kthread_mutex_unlock(&process->thread_lock);
|
||||||
kthread_mutex_lock(&process->nicelock);
|
kthread_mutex_lock(&process->nice_lock);
|
||||||
psst.nice = process->nice;
|
psst.nice = process->nice;
|
||||||
kthread_mutex_unlock(&process->nicelock);
|
kthread_mutex_unlock(&process->nice_lock);
|
||||||
kthread_mutex_lock(&process->segment_lock);
|
kthread_mutex_lock(&process->segment_lock);
|
||||||
// TODO: Cache these.
|
// TODO: Cache these.
|
||||||
for ( size_t i = 0; i < process->segments_used; i++ )
|
for ( size_t i = 0; i < process->segments_used; i++ )
|
||||||
|
|
|
@ -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_possible_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;
|
||||||
}
|
}
|
||||||
|
@ -656,6 +672,7 @@ int PTY::master_ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||||
const struct winsize* user_ws = (const struct winsize*) arg;
|
const struct winsize* user_ws = (const struct winsize*) arg;
|
||||||
if ( !ctx->copy_from_src(&ws, user_ws, sizeof(ws)) )
|
if ( !ctx->copy_from_src(&ws, user_ws, sizeof(ws)) )
|
||||||
return -1;
|
return -1;
|
||||||
|
winch();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return ioctl(ctx, cmd, arg);
|
return ioctl(ctx, cmd, arg);
|
||||||
|
|
|
@ -39,7 +39,7 @@ static int GetProcessPriority(pid_t who)
|
||||||
Process* process = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcess();
|
Process* process = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcess();
|
||||||
if ( !process )
|
if ( !process )
|
||||||
return errno = ESRCH, -1;
|
return errno = ESRCH, -1;
|
||||||
ScopedLock lock(&process->nicelock);
|
ScopedLock lock(&process->nice_lock);
|
||||||
return process->nice;
|
return process->nice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ static int SetProcessPriority(pid_t who, int prio)
|
||||||
Process* process = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcess();
|
Process* process = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcess();
|
||||||
if ( !process )
|
if ( !process )
|
||||||
return errno = ESRCH, -1;
|
return errno = ESRCH, -1;
|
||||||
ScopedLock lock(&process->nicelock);
|
ScopedLock lock(&process->nice_lock);
|
||||||
process->nice = prio;
|
process->nice = prio;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,9 @@ static int GetProcessGroupPriority(pid_t who)
|
||||||
if ( !group )
|
if ( !group )
|
||||||
return errno = ESRCH, -1;
|
return errno = ESRCH, -1;
|
||||||
int lowest = INT_MAX;
|
int lowest = INT_MAX;
|
||||||
for ( Process* process = group->groupfirst; process; process = process->groupnext )
|
for ( Process* process = group->group_first; process; process = process->group_next )
|
||||||
{
|
{
|
||||||
ScopedLock lock(&process->nicelock);
|
ScopedLock lock(&process->nice_lock);
|
||||||
if ( process->nice < lowest )
|
if ( process->nice < lowest )
|
||||||
lowest = process->nice;
|
lowest = process->nice;
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,9 @@ static int SetProcessGroupPriority(pid_t who, int prio)
|
||||||
Process* group = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcessGroup();
|
Process* group = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcessGroup();
|
||||||
if ( !group )
|
if ( !group )
|
||||||
return errno = ESRCH, -1;
|
return errno = ESRCH, -1;
|
||||||
for ( Process* process = group->groupfirst; process; process = process->groupnext )
|
for ( Process* process = group->group_first; process; process = process->group_next )
|
||||||
{
|
{
|
||||||
ScopedLock lock(&process->nicelock);
|
ScopedLock lock(&process->nice_lock);
|
||||||
process->nice = prio;
|
process->nice = prio;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -181,8 +181,8 @@ extern "C" void fake_interrupt(void);
|
||||||
static void FakeInterruptedContext(struct interrupt_context* intctx, int int_no)
|
static void FakeInterruptedContext(struct interrupt_context* intctx, int int_no)
|
||||||
{
|
{
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
uintptr_t stack = current_thread->kernelstackpos +
|
uintptr_t stack = current_thread->kernel_stack_pos +
|
||||||
current_thread->kernelstacksize;
|
current_thread->kernel_stack_size;
|
||||||
stack -= sizeof(struct interrupt_context);
|
stack -= sizeof(struct interrupt_context);
|
||||||
struct interrupt_context* fakectx = (struct interrupt_context*) stack;
|
struct interrupt_context* fakectx = (struct interrupt_context*) stack;
|
||||||
memcpy(fakectx, intctx, sizeof(struct interrupt_context));
|
memcpy(fakectx, intctx, sizeof(struct interrupt_context));
|
||||||
|
@ -209,8 +209,8 @@ static void FakeInterruptedContext(struct interrupt_context* intctx, int int_no)
|
||||||
intctx->esp = stack;
|
intctx->esp = stack;
|
||||||
intctx->ss = KDS | KRPL;
|
intctx->ss = KDS | KRPL;
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
uintptr_t stack = current_thread->kernelstackpos +
|
uintptr_t stack = current_thread->kernel_stack_pos +
|
||||||
current_thread->kernelstacksize;
|
current_thread->kernel_stack_size;
|
||||||
stack -= sizeof(struct interrupt_context);
|
stack -= sizeof(struct interrupt_context);
|
||||||
struct interrupt_context* fakectx = (struct interrupt_context*) stack;
|
struct interrupt_context* fakectx = (struct interrupt_context*) stack;
|
||||||
memcpy(fakectx, intctx, sizeof(struct interrupt_context));
|
memcpy(fakectx, intctx, sizeof(struct interrupt_context));
|
||||||
|
@ -268,7 +268,6 @@ static void SwitchRegisters(struct interrupt_context* intctx,
|
||||||
static Thread* idle_thread;
|
static Thread* idle_thread;
|
||||||
static Thread* first_runnable_thread;
|
static Thread* first_runnable_thread;
|
||||||
static Thread* true_current_thread;
|
static Thread* true_current_thread;
|
||||||
static Process* init_process;
|
|
||||||
|
|
||||||
static void SwitchThread(struct interrupt_context* intctx,
|
static void SwitchThread(struct interrupt_context* intctx,
|
||||||
Thread* old_thread,
|
Thread* old_thread,
|
||||||
|
@ -407,16 +406,6 @@ void SetIdleThread(Thread* thread)
|
||||||
true_current_thread = thread;
|
true_current_thread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetInitProcess(Process* init)
|
|
||||||
{
|
|
||||||
init_process = init;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process* GetInitProcess()
|
|
||||||
{
|
|
||||||
return init_process;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process* GetKernelProcess()
|
Process* GetKernelProcess()
|
||||||
{
|
{
|
||||||
if ( !idle_thread )
|
if ( !idle_thread )
|
||||||
|
|
|
@ -144,8 +144,8 @@ int sys_sigaction(int signum,
|
||||||
memcpy(kact, &newact, sizeof(struct sigaction));
|
memcpy(kact, &newact, sizeof(struct sigaction));
|
||||||
|
|
||||||
// Signals may become discarded because of the new handler.
|
// Signals may become discarded because of the new handler.
|
||||||
ScopedLock threads_lock(&process->threadlock);
|
ScopedLock threads_lock(&process->thread_lock);
|
||||||
for ( Thread* t = process->firstthread; t; t = t->nextsibling )
|
for ( Thread* t = process->first_thread; t; t = t->next_sibling )
|
||||||
UpdatePendingSignals(t);
|
UpdatePendingSignals(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,9 +315,9 @@ int sys_kill(pid_t pid, int signum)
|
||||||
|
|
||||||
bool Process::DeliverGroupSignal(int signum) // process_family_lock held
|
bool Process::DeliverGroupSignal(int signum) // process_family_lock held
|
||||||
{
|
{
|
||||||
if ( !groupfirst )
|
if ( !group_first )
|
||||||
return errno = ESRCH, false;
|
return errno = ESRCH, false;
|
||||||
for ( Process* iter = groupfirst; iter; iter = iter->groupnext )
|
for ( Process* iter = group_first; iter; iter = iter->group_next )
|
||||||
{
|
{
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING )
|
if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING )
|
||||||
|
@ -331,9 +331,9 @@ bool Process::DeliverGroupSignal(int signum) // process_family_lock held
|
||||||
|
|
||||||
bool Process::DeliverSessionSignal(int signum) // process_family_lock held
|
bool Process::DeliverSessionSignal(int signum) // process_family_lock held
|
||||||
{
|
{
|
||||||
if ( !sessionfirst )
|
if ( !session_first )
|
||||||
return errno = ESRCH, false;
|
return errno = ESRCH, false;
|
||||||
for ( Process* iter = sessionfirst; iter; iter = iter->sessionnext )
|
for ( Process* iter = session_first; iter; iter = iter->session_next )
|
||||||
{
|
{
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING )
|
if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING )
|
||||||
|
@ -347,16 +347,16 @@ bool Process::DeliverSessionSignal(int signum) // process_family_lock held
|
||||||
|
|
||||||
bool Process::DeliverSignal(int signum)
|
bool Process::DeliverSignal(int signum)
|
||||||
{
|
{
|
||||||
ScopedLock lock(&threadlock);
|
ScopedLock lock(&thread_lock);
|
||||||
|
|
||||||
if ( !firstthread )
|
if ( !first_thread )
|
||||||
return errno = EINIT, false;
|
return errno = EINIT, false;
|
||||||
|
|
||||||
// Broadcast particular signals to all the threads in the process.
|
// Broadcast particular signals to all the threads in the process.
|
||||||
if ( signum == SIGCONT || signum == SIGSTOP || signum == SIGKILL )
|
if ( signum == SIGCONT || signum == SIGSTOP || signum == SIGKILL )
|
||||||
{
|
{
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
for ( Thread* t = firstthread; t; t = t->nextsibling )
|
for ( Thread* t = first_thread; t; t = t->next_sibling )
|
||||||
{
|
{
|
||||||
if ( !t->DeliverSignal(signum) && errno != ESIGPENDING )
|
if ( !t->DeliverSignal(signum) && errno != ESIGPENDING )
|
||||||
{
|
{
|
||||||
|
@ -371,7 +371,7 @@ bool Process::DeliverSignal(int signum)
|
||||||
// TODO: This isn't how signals should be routed to a particular thread.
|
// TODO: This isn't how signals should be routed to a particular thread.
|
||||||
if ( CurrentThread()->process == this )
|
if ( CurrentThread()->process == this )
|
||||||
return CurrentThread()->DeliverSignal(signum);
|
return CurrentThread()->DeliverSignal(signum);
|
||||||
return firstthread->DeliverSignal(signum);
|
return first_thread->DeliverSignal(signum);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sys_raise(int signum)
|
int sys_raise(int signum)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2016, 2021-2022 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011-2016, 2021-2024 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
|
||||||
|
@ -204,6 +204,8 @@ void* syscall_list[SYSCALL_MAX_NUM + 1] =
|
||||||
[SYSCALL_SETDNSCONFIG] = (void*) sys_setdnsconfig,
|
[SYSCALL_SETDNSCONFIG] = (void*) sys_setdnsconfig,
|
||||||
[SYSCALL_FUTEX] = (void*) sys_futex,
|
[SYSCALL_FUTEX] = (void*) sys_futex,
|
||||||
[SYSCALL_MEMUSAGE] = (void*) sys_memusage,
|
[SYSCALL_MEMUSAGE] = (void*) sys_memusage,
|
||||||
|
[SYSCALL_GETINIT] = (void*) sys_getinit,
|
||||||
|
[SYSCALL_SETINIT] = (void*) sys_setinit,
|
||||||
[SYSCALL_MAX_NUM] = (void*) sys_bad_syscall,
|
[SYSCALL_MAX_NUM] = (void*) sys_bad_syscall,
|
||||||
};
|
};
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -59,18 +59,18 @@ Thread::Thread()
|
||||||
yield_to_tid = 0;
|
yield_to_tid = 0;
|
||||||
id = 0; // TODO: Make a thread id.
|
id = 0; // TODO: Make a thread id.
|
||||||
process = NULL;
|
process = NULL;
|
||||||
prevsibling = NULL;
|
prev_sibling = NULL;
|
||||||
nextsibling = NULL;
|
next_sibling = NULL;
|
||||||
scheduler_list_prev = NULL;
|
scheduler_list_prev = NULL;
|
||||||
scheduler_list_next = NULL;
|
scheduler_list_next = NULL;
|
||||||
state = NONE;
|
state = NONE;
|
||||||
memset(®isters, 0, sizeof(registers));
|
memset(®isters, 0, sizeof(registers));
|
||||||
kernelstackpos = 0;
|
kernel_stack_pos = 0;
|
||||||
kernelstacksize = 0;
|
kernel_stack_size = 0;
|
||||||
signal_count = 0;
|
signal_count = 0;
|
||||||
signal_single_frame = 0;
|
signal_single_frame = 0;
|
||||||
signal_canary = 0;
|
signal_canary = 0;
|
||||||
kernelstackmalloced = false;
|
kernel_stack_malloced = false;
|
||||||
pledged_destruction = false;
|
pledged_destruction = false;
|
||||||
force_no_signals = false;
|
force_no_signals = false;
|
||||||
signal_single = false;
|
signal_single = false;
|
||||||
|
@ -99,8 +99,8 @@ Thread::~Thread()
|
||||||
if ( process )
|
if ( process )
|
||||||
process->OnThreadDestruction(this);
|
process->OnThreadDestruction(this);
|
||||||
assert(CurrentThread() != this);
|
assert(CurrentThread() != this);
|
||||||
if ( kernelstackmalloced )
|
if ( kernel_stack_malloced )
|
||||||
delete[] (uint8_t*) kernelstackpos;
|
delete[] (uint8_t*) kernel_stack_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread* CreateKernelThread(Process* process,
|
Thread* CreateKernelThread(Process* process,
|
||||||
|
@ -116,7 +116,7 @@ Thread* CreateKernelThread(Process* process,
|
||||||
return errno = EINVAL, (Thread*) NULL;
|
return errno = EINVAL, (Thread*) NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
kthread_mutex_lock(&process->threadlock);
|
kthread_mutex_lock(&process->thread_lock);
|
||||||
|
|
||||||
// Note: Only allow the process itself to make threads, except the initial
|
// Note: Only allow the process itself to make threads, except the initial
|
||||||
// thread. This requirement is because kthread_exit() needs to know when
|
// thread. This requirement is because kthread_exit() needs to know when
|
||||||
|
@ -124,7 +124,7 @@ Thread* CreateKernelThread(Process* process,
|
||||||
// and that no more threads will appear, so it can run some final process
|
// and that no more threads will appear, so it can run some final process
|
||||||
// termination steps without any interference. It's always allowed to create
|
// termination steps without any interference. It's always allowed to create
|
||||||
// threads in the kernel process as it never exits.
|
// threads in the kernel process as it never exits.
|
||||||
assert(!process->firstthread ||
|
assert(!process->first_thread ||
|
||||||
process == CurrentProcess() ||
|
process == CurrentProcess() ||
|
||||||
process == Scheduler::GetKernelProcess());
|
process == Scheduler::GetKernelProcess());
|
||||||
|
|
||||||
|
@ -137,14 +137,14 @@ Thread* CreateKernelThread(Process* process,
|
||||||
|
|
||||||
// Create the family tree.
|
// Create the family tree.
|
||||||
thread->process = process;
|
thread->process = process;
|
||||||
Thread* firsty = process->firstthread;
|
Thread* firsty = process->first_thread;
|
||||||
if ( firsty )
|
if ( firsty )
|
||||||
firsty->prevsibling = thread;
|
firsty->prev_sibling = thread;
|
||||||
thread->nextsibling = firsty;
|
thread->next_sibling = firsty;
|
||||||
process->firstthread = thread;
|
process->first_thread = thread;
|
||||||
process->threads_not_exiting_count++;
|
process->threads_not_exiting_count++;
|
||||||
|
|
||||||
kthread_mutex_unlock(&process->threadlock);
|
kthread_mutex_unlock(&process->thread_lock);
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
@ -260,9 +260,9 @@ Thread* CreateKernelThread(Process* process, void (*entry)(void*), void* user,
|
||||||
Thread* thread = CreateKernelThread(process, ®s, name);
|
Thread* thread = CreateKernelThread(process, ®s, name);
|
||||||
if ( !thread ) { delete[] stack; return NULL; }
|
if ( !thread ) { delete[] stack; return NULL; }
|
||||||
|
|
||||||
thread->kernelstackpos = (uintptr_t) stack;
|
thread->kernel_stack_pos = (uintptr_t) stack;
|
||||||
thread->kernelstacksize = stacksize;
|
thread->kernel_stack_size = stacksize;
|
||||||
thread->kernelstackmalloced = true;
|
thread->kernel_stack_malloced = true;
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
@ -335,11 +335,11 @@ int sys_exit_thread(int requested_exit_code,
|
||||||
|
|
||||||
extended.unmap_size = Page::AlignUp(extended.unmap_size);
|
extended.unmap_size = Page::AlignUp(extended.unmap_size);
|
||||||
|
|
||||||
kthread_mutex_lock(&thread->process->threadlock);
|
kthread_mutex_lock(&thread->process->thread_lock);
|
||||||
bool is_others = false;
|
bool is_others = false;
|
||||||
for ( Thread* iter = thread->process->firstthread;
|
for ( Thread* iter = thread->process->first_thread;
|
||||||
!is_others && iter;
|
!is_others && iter;
|
||||||
iter = iter->nextsibling )
|
iter = iter->next_sibling )
|
||||||
{
|
{
|
||||||
if ( iter == thread )
|
if ( iter == thread )
|
||||||
continue;
|
continue;
|
||||||
|
@ -355,7 +355,7 @@ int sys_exit_thread(int requested_exit_code,
|
||||||
process->threads_exiting = true;
|
process->threads_exiting = true;
|
||||||
else if ( process->threads_exiting )
|
else if ( process->threads_exiting )
|
||||||
are_threads_exiting = true;
|
are_threads_exiting = true;
|
||||||
kthread_mutex_unlock(&thread->process->threadlock);
|
kthread_mutex_unlock(&thread->process->thread_lock);
|
||||||
|
|
||||||
// Self-destruct if another thread began exiting the process.
|
// Self-destruct if another thread began exiting the process.
|
||||||
if ( are_threads_exiting )
|
if ( are_threads_exiting )
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue