Compare commits

...

49 Commits

Author SHA1 Message Date
Juhani Krekelä 60a9c312ff fixup! Add tix-upgrade(8). 2023-08-21 22:00:23 +00:00
Jonas 'Sortie' Termansen 5e0fadf131 fixup! Support system upgrades and configuration in GRUB. 2023-08-21 23:54:33 +02:00
Jonas 'Sortie' Termansen 49f643fe90 Fix tix3g migration hook installing the wrong PLATFORM in collection.conf. 2023-08-21 23:53:25 +02:00
Jonas 'Sortie' Termansen 7e8b7f15d3 Support system upgrades and configuration in GRUB.
Move /etc/default/grub to /etc/grub as it's owned by the sysadmin.

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

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

Remove the old /etc/grub.d/10_sortix.cache with a compatibility hook as it
has moved to /etc/default/grub.d/10_sortix.cache.
2023-08-16 00:53:02 +02:00
Jonas 'Sortie' Termansen c9126b4f90 Fix the sortix-1.1-tix-3g hook marker not existing. 2023-08-15 23:57:28 +02:00
Jonas 'Sortie' Termansen 871c057b31 fixup! Add tix-upgrade(8). 2023-07-16 21:26:49 +02:00
Jonas 'Sortie' Termansen 14b389dfbe fixup! Add tix-upgrade(8). 2023-07-16 21:19:46 +02:00
Jonas 'Sortie' Termansen 2402ef8361 fixup! Add tix-upgrade(8). 2023-07-16 19:46:09 +02:00
Jonas 'Sortie' Termansen 7f8cb8aecc fixup! Add tix-upgrade(8). 2023-07-16 17:44:04 +02:00
Jonas 'Sortie' Termansen 401ec64e3a Add getty(8). 2023-07-16 16:03:13 +02:00
Jonas 'Sortie' Termansen 9fa88f0018 Add terminal and interrupt support to com(4). 2023-07-16 16:03:13 +02:00
Jonas 'Sortie' Termansen 7d581f920a Add nyan(1). 2023-07-16 16:03:12 +02:00
Jonas 'Sortie' Termansen 2288785566 Draft video-player. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen e71d6242a0 Work around pty deadlock. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen b1bfa673af Add cdrom mounting live environment. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen d02a5e646d Revert "Parallelize driver initialization."
This reverts commit 0fef08bbc4.
2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 2866794520 Parallelize driver initialization. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 1272d27522 Speed up ata(4) 400 ns waits.
Waiting for any non-zero duration currently waits for at least one timer
cycle (10 ms), which is especially expensive during early boot.

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

Try to determine the initial selected drive to save one drive selection.
2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 9c81cad140 Decrease PS/2 timeouts. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 14140ee18c Add uptime(1) -pr options. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen f5a892e054 Add iso9660 filesystem implementation. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 1ca5712115 Add kernel virtual address space usage debug information. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 29f27fc7a5 Revert "Update to bison-3.8.2."
This reverts commit b82fae810b42c5426d21c4dc153b32f086dd7fde.
2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen 1b52b0f6f9 Update to bison-3.8.2. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen b61d3d3a03 Debug TCP socket state listing. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen e528fc24e6 Add kernel heap allocation tracing debug facility. 2023-07-16 16:03:01 +02:00
Jonas 'Sortie' Termansen b2a22f1ad6 Add m4, perl, and texinfo to the basic ports set. 2023-07-16 16:03:00 +02:00
Jonas 'Sortie' Termansen 0818e5d78c Trianglix 4. 2023-07-16 16:03:00 +02:00
Jonas 'Sortie' Termansen 405d5c8d26 Add tix-check(8). 2023-07-16 16:03:00 +02:00
Jonas 'Sortie' Termansen d43bb712ee Volatile release. 2023-07-16 16:03:00 +02:00
Jonas 'Sortie' Termansen 1adf011044 Add tix-upgrade(8). 2023-07-16 16:03:00 +02:00
Jonas 'Sortie' Termansen 910ea14e18 Add signify port. 2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen 8c6080dfee Add pty(1). 2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen 68a5677c7a Add irc(1).
Co-authored-by: Juhani Krekelä <juhani@krekelä.fi>
2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen 2149b054ad Add getaddrinfo(1). 2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen 95eedfe1f1 Add host(1). 2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen e27e246cc6 Enable stack smash protection by default. 2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen 613e94a4a6 Enable undefined behavior sanitization by default. 2023-07-16 16:00:49 +02:00
Jonas 'Sortie' Termansen 563973d6b3 Remove mkinitrd(8). 2023-07-16 16:00:48 +02:00
Jonas 'Sortie' Termansen ccf98df93c Handle SIGTERM in chroot(8). 2023-07-16 16:00:23 +02:00
Jonas 'Sortie' Termansen 77e777423c Add sysmerge(8) --target option. 2023-07-16 16:00:22 +02:00
Jonas 'Sortie' Termansen 33c1e98f0e Fix tar race condition when tix-port(8) strips programs. 2023-07-16 12:58:45 +02:00
Jonas 'Sortie' Termansen d189183900 Third generation Tix.
The .tix.tar.xz binary package format now stores the contents in the root
rather than the data/ subdirectory and the tix metadata now has the same
layout as the loose files in /tix, such that a .tix.tar.xz package can
simply be directly extracted into the filesystem. The /tix/manifest/ is now
included in the binary package rather than being generated on installation.

The /tix/collection.conf and /tix/tixinfo metadata files are now in the
tix-vars(1) format in the style of port(5).

The /tix/installed.list file has been removed since it isn't loose file
compatible and one can list the /tix/tixinfo directory instead.

The /tix/repository.list file has been removed since the feature is unused
and doesn't match the future direction of tix.

The kernel support for tix binary packages has been removed since it will
simply install by extracting the tar archive into the root filesystem.

Add the post-install sha256sum to the port version stamp.
2023-07-15 16:43:27 +02:00
Jonas 'Sortie' Termansen b819428bd2 Remove outdated statement that ports can't be built natively. 2023-07-12 23:10:39 +02:00
Jonas 'Sortie' Termansen 4990cef43c Reposition windows when the display resolution changes. 2023-07-12 23:10:39 +02:00
Jonas 'Sortie' Termansen ab9f2353e5 Add sysinstall(8) --system and --ports options. 2023-07-12 21:55:32 +02:00
Jonas 'Sortie' Termansen edd8566155 Modernize extfs(8) error handling. 2023-07-12 21:54:57 +02:00
Jonas 'Sortie' Termansen ffc1b02b94 Remove workaround for qemu 1.4.x and 1.5.x.
These releases are now 10 years old and are no longer a concern.
2023-07-12 21:54:57 +02:00
Jonas 'Sortie' Termansen e933eb5a1c Replace mkinitrd(1) with tar(1).
The custom initrd format was originally useful when it was mounted,
however it has been extracted into the ramfs for a very long time and
has no advantages over the standard tar format which can be readily
created and modified using standard tools. The kernel initrd(7) support
already supports tar, so this change simply switches the format.
2023-07-12 21:45:11 +02:00
163 changed files with 12492 additions and 3359 deletions

137
Makefile
View File

@ -21,13 +21,16 @@ dnsconfig \
editor \
ext \
games \
host \
hostname \
ifconfig \
init \
irc \
iso9660 \
kblayout \
kblayout-compiler \
login \
mkinitrd \
nyan \
ping \
regress \
rw \
@ -69,14 +72,17 @@ else
SORTIX_INCLUDE_SOURCE?=yes
endif
ISO_MOUNT?=no
include build-aux/dirs.mak
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.initrd
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.initrd
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.initrd
SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.initrd
CHAIN_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).chain.tar
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.tar
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.tar
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.tar
SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.tar
.PHONY: all
all: sysroot
@ -101,7 +107,6 @@ sysmerge-wait: sysroot
clean-build-tools:
$(MAKE) -C carray clean
$(MAKE) -C kblayout-compiler clean
$(MAKE) -C mkinitrd clean
$(MAKE) -C sf clean
$(MAKE) -C tix clean
@ -109,7 +114,6 @@ clean-build-tools:
build-tools:
$(MAKE) -C carray
$(MAKE) -C kblayout-compiler
$(MAKE) -C mkinitrd
$(MAKE) -C sf
$(MAKE) -C tix
@ -117,7 +121,6 @@ build-tools:
install-build-tools:
$(MAKE) -C carray install
$(MAKE) -C kblayout-compiler install
$(MAKE) -C mkinitrd install
$(MAKE) -C sf install
$(MAKE) -C tix install
@ -235,6 +238,8 @@ sysroot-system: sysroot-fsh sysroot-base-headers
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
echo /etc/os-release >> "$(SYSROOT)/tix/manifest/system"
find etc | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
cp -RT etc "$(SYSROOT)/etc"
find share | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
cp -RT share "$(SYSROOT)/share"
export SYSROOT="$(SYSROOT)" && \
@ -272,6 +277,7 @@ else ifneq ($(SORTIX_INCLUDE_SOURCE),no)
cp Makefile -t "$(SYSROOT)/src"
cp README -t "$(SYSROOT)/src"
cp -RT build-aux "$(SYSROOT)/src/build-aux"
cp -RT etc "$(SYSROOT)/src/etc"
cp -RT share "$(SYSROOT)/src/share"
(for D in $(MODULES); do (cp -R $$D -t "$(SYSROOT)/src" && $(MAKE) -C "$(SYSROOT)/src/$$D" clean) || exit $$?; done)
endif
@ -444,6 +450,25 @@ release-all-archs:
# Initial ramdisk
$(CHAIN_INITRD).uuid:
mkdir -p `dirname $@`
uuidgen > $@
$(CHAIN_INITRD): $(CHAIN_INITRD).uuid sysroot
mkdir -p `dirname $(CHAIN_INITRD)`
rm -rf $(CHAIN_INITRD).d
mkdir -p $(CHAIN_INITRD).d
mkdir -p $(CHAIN_INITRD).d/etc
echo "UUID=`cat $(CHAIN_INITRD).uuid` / iso9660 ro 0 1" > $(CHAIN_INITRD).d/etc/fstab
mkdir -p $(CHAIN_INITRD).d/etc/init
echo require chain exit-code > $(CHAIN_INITRD).d/etc/init/default
mkdir -p $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/init" $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/iso9660fs" $(CHAIN_INITRD).d/sbin
LC_ALL=C ls -A $(CHAIN_INITRD).d | \
tar -cf $(CHAIN_INITRD) -C $(CHAIN_INITRD).d --numeric-owner --owner=0 --group=0 -T -
rm -rf $(CHAIN_INITRD).d
$(LIVE_INITRD): sysroot
mkdir -p `dirname $(LIVE_INITRD)`
rm -rf $(LIVE_INITRD).d
@ -455,7 +480,9 @@ $(LIVE_INITRD): sysroot
echo "include /etc/default/passwd.d/*" >> $(LIVE_INITRD).d/etc/passwd
echo "root::0:root" > $(LIVE_INITRD).d/etc/group
echo "include /etc/default/group.d/*" >> $(LIVE_INITRD).d/etc/group
mkdir -p $(LIVE_INITRD).d/home
(echo 'channel = $(CHANNEL)' && \
echo 'release_key = $(RELEASE_KEY)' && \
echo 'release_sig_url = $(RELEASE_SIG_URL)') > $(LIVE_INITRD).d/etc/upgrade.conf
mkdir -p $(LIVE_INITRD).d/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(LIVE_INITRD).d/root
(echo "You can view the documentation for new users by typing:" && \
@ -465,20 +492,24 @@ $(LIVE_INITRD): sysroot
echo "You can view the installation instructions by typing:" && \
echo && \
echo " man installation") > $(LIVE_INITRD).d/root/welcome
tix-collection $(LIVE_INITRD).d create --platform=$(HOST) --prefix= --generation=2
mkinitrd --format=sortix-initrd-2 $(LIVE_INITRD).d -o $(LIVE_INITRD)
tix-collection $(LIVE_INITRD).d create --platform=$(HOST) --prefix= --generation=3
LC_ALL=C ls -A $(LIVE_INITRD).d | \
tar -cf $(LIVE_INITRD) -C $(LIVE_INITRD).d --numeric-owner --owner=0 --group=0 -T -
rm -rf $(LIVE_INITRD).d
.PHONY: $(OVERLAY_INITRD)
$(OVERLAY_INITRD): sysroot
test ! -d "$(SYSROOT_OVERLAY)" || \
mkinitrd --format=sortix-initrd-2 "$(SYSROOT_OVERLAY)" -o $(OVERLAY_INITRD)
LC_ALL=C ls -A "$(SYSROOT_OVERLAY)" | \
tar -cf $(OVERLAY_INITRD) -C "$(SYSROOT_OVERLAY)" --numeric-owner --owner=0 --group=0 -T -
$(SRC_INITRD): sysroot
mkinitrd --format=sortix-initrd-2 --manifest="$(SYSROOT)/tix/manifest/src" "$(SYSROOT)" -o $(SRC_INITRD)
sed -E 's,^/,,' "$(SYSROOT)/tix/manifest/src" | \
tar -cf $(SRC_INITRD) -C "$(SYSROOT)" --numeric-owner --owner=0 --group=0 --no-recursion -T - tix/manifest/src
$(SYSTEM_INITRD): sysroot
mkinitrd --format=sortix-initrd-2 --manifest="$(SYSROOT)/tix/manifest/system" "$(SYSROOT)" -o $(SYSTEM_INITRD)
sed -E 's,^/,,' "$(SYSROOT)/tix/manifest/system" | \
tar -cf $(SYSTEM_INITRD) -C "$(SYSROOT)" --numeric-owner --owner=0 --group=0 --no-recursion -T - tix/manifest/system
# Packaging
@ -487,6 +518,42 @@ $(SORTIX_BUILDS_DIR):
# Bootable images
ifeq ($(ISO_MOUNT),yes)
$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(CHAIN_INITRD) $(CHAIN_INITRD).uuid $(SORTIX_BUILDS_DIR)
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot
cp "$(SYSROOT)/boot/sortix.bin" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.bin
cp $(CHAIN_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.initrd
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc
echo "UUID=`cat $(CHAIN_INITRD).uuid` / iso9660 ro 0 1" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/fstab
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/init
echo require single-user exit-code > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/init/default
echo "root::0:0:root:/root:sh" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/passwd
echo "include /etc/default/passwd.d/*" >> $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/passwd
echo "root::0:root" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/group
echo "include /etc/default/group.d/*" >> $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/group
(echo 'channel = $(CHANNEL)' && \
echo 'release_key = $(RELEASE_KEY)' && \
echo 'release_sig_url = $(RELEASE_SIG_URL)') > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/upgrade.conf
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root
(echo "You can view the documentation for new users by typing:" && \
echo && \
echo " man user-guide" && \
echo && \
echo "You can view the installation instructions by typing:" && \
echo && \
echo " man installation") > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root/welcome
if [ -d "$(SYSROOT_OVERLAY)" ]; then cp -RT "$(SYSROOT_OVERLAY)" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso; fi
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/grub
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) --mount $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso sysroot $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso -- -volid Sortix -volset_id `cat $(CHAIN_INITRD).uuid`
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else
$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(LIVE_INITRD) $(OVERLAY_INITRD) $(SRC_INITRD) $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
@ -499,34 +566,36 @@ $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(LIVE_INITRD) $(OVERLAY_INITRD)
build-aux/iso-repository.sh $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/repository
ifeq ($(SORTIX_ISO_COMPRESSION),xz)
xz -c "$(SYSROOT)/boot/sortix.bin" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.bin.xz
xz -c $(LIVE_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/live.initrd.xz
xz -c $(LIVE_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/live.tar.xz
test ! -e "$(OVERLAY_INITRD)" || \
xz -c $(OVERLAY_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.initrd.xz
xz -c $(SRC_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.initrd.xz
xz -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.initrd.xz
xz -c $(OVERLAY_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.tar.xz
xz -c $(SRC_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.tar.xz
xz -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.tar.xz
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue --compress=xz -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else ifeq ($(SORTIX_ISO_COMPRESSION),gzip)
gzip -c "$(SYSROOT)/boot/sortix.bin" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.bin.gz
gzip -c $(LIVE_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/live.initrd.gz
gzip -c $(LIVE_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/live.tar.gz
test ! -e "$(OVERLAY_INITRD)" || \
gzip -c $(OVERLAY_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.initrd.gz
gzip -c $(SRC_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.initrd.gz
gzip -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.initrd.gz
gzip -c $(OVERLAY_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.tar.gz
gzip -c $(SRC_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.tar.gz
gzip -c $(SYSTEM_INITRD) > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.tar.gz
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue --compress=gz -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else # none
cp "$(SYSROOT)/boot/sortix.bin" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.bin
cp $(LIVE_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/live.initrd
cp $(LIVE_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/live.tar
test ! -e "$(OVERLAY_INITRD)" || \
cp $(OVERLAY_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.initrd
cp $(SRC_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.initrd
cp $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.initrd
cp $(OVERLAY_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/overlay.tar
cp $(SRC_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/src.tar
cp $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/system.tar
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
endif
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
endif
.PHONY: iso
iso: $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso
@ -555,25 +624,25 @@ $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot: $(SORTIX_RELEASE_DIR)/$(RELEAS
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/sortix.bin.xz: sysroot $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
xz -c "$(SYSROOT)/boot/sortix.bin" > $@
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/live.initrd.xz: $(LIVE_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/live.tar.xz: $(LIVE_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
xz -c $< > $@
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/overlay.initrd.xz: $(OVERLAY_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/overlay.tar.xz: $(OVERLAY_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
test ! -e $< || xz -c $< > $@
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/src.initrd.xz: $(SRC_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/src.tar.xz: $(SRC_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
xz -c $< > $@
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/system.initrd.xz: $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/system.tar.xz: $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot
xz -c $< > $@
.PHONY: release-boot
release-boot: \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/sortix.bin.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/live.initrd.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/overlay.initrd.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/src.initrd.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/system.initrd.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/live.tar.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/overlay.tar.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/src.tar.xz \
$(SORTIX_RELEASE_DIR)/$(RELEASE)/$(MACHINE)/boot/system.tar.xz \
.PHONY: release-iso
release-iso: $(SORTIX_RELEASE_DIR)/$(RELEASE)/builds/$(BUILD_NAME).iso

View File

@ -82,7 +82,7 @@ export CXXFLAGS
# Initialize Tix package management in the system root if absent.
if [ "$OPERATION" = build ]; then
if [ ! -e "$SYSROOT/tix/collection.conf" ]; then
tix-collection "$SYSROOT" create --platform=$HOST --prefix= --generation=2
tix-collection "$SYSROOT" create --platform=$HOST --prefix= --generation=3
fi
fi
@ -138,7 +138,7 @@ for PACKAGE in $PACKAGES; do
--collection="$SYSROOT" \
--destination="$SORTIX_REPOSITORY_DIR" \
${END:+--end="$END"} \
--generation=2 \
--generation=3 \
--host=$HOST \
${SORTIX_PORTS_MIRROR:+--mirror="$SORTIX_PORTS_MIRROR"} \
--mirror-directory="$SORTIX_MIRROR_DIR" \

View File

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

21
build-aux/config.mak Normal file
View File

@ -0,0 +1,21 @@
.PHONY: clean-tests
clean-tests:
rm -rf tests
rm -f config.h
.PHONY: clean
clean: clean-tests
config.h: $(addprefix tests/,$(addsuffix .h,$(TESTS)))
cat tests/*.h > config.h
tests/%.h: ../build-aux/tests/%.c
@if [ ! -d tests ]; then mkdir -p tests; fi
@ln -sf ../../build-aux/tests/$*.c tests/$*.c
@if $(CC) $(CFLAGS) $(CPPFLAGS) -Werror=incompatible-pointer-types -c tests/$*.c -o /dev/null 2>tests/$*.log; then \
echo "# tests/$*: Yes" && tail -n 1 $< > $@; \
else \
echo "# tests/$*: No" && true > $@; \
fi
-include ../build-aux/tests/*.d

View File

@ -1,5 +1,5 @@
#!/bin/sh
# Copyright (c) 2018, 2022 Jonas 'Sortie' Termansen.
# Copyright (c) 2018, 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
@ -23,8 +23,9 @@ set -e
this=$(which -- "$0")
thisdir=$(dirname -- "$this")
platform=
directory=
mount=false
platform=
version=
dashdash=
@ -44,8 +45,9 @@ for argument do
case $dashdash$argument in
--) dashdash=yes ;;
--platform=*) platform=$parameter ;;
--platform) previous_option=platform ;;
--platform=*) platform=$parameter ;;
--mount) mount=true ;;
--version=*) version=$parameter ;;
--version) previous_option=version ;;
-*) echo "$0: unrecognized option $argument" >&2
@ -111,13 +113,23 @@ isinset() {
cd "$directory"
kernel=$(maybe_compressed boot/sortix.bin)
live_initrd=$(maybe_compressed boot/live.initrd)
overlay_initrd=$(maybe_compressed boot/overlay.initrd)
src_initrd=$(maybe_compressed boot/src.initrd)
system_initrd=$(maybe_compressed boot/system.initrd)
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//')
if $mount; then
initrd=$(maybe_compressed boot/sortix.initrd)
initrds=$initrd
else
live_initrd=$(maybe_compressed boot/live.tar)
overlay_initrd=$(maybe_compressed boot/overlay.tar)
src_initrd=$(maybe_compressed boot/src.tar)
system_initrd=$(maybe_compressed boot/system.tar)
initrds="$system_initrd $src_initrd $live_initrd $overlay_initrd"
fi
if $mount; then
ports=
else
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//')
fi
mkdir -p boot/grub
mkdir -p boot/grub/init
@ -183,6 +195,12 @@ fi
set version="$version"
set machine="$machine"
set mount=$mount
if \$mount; then
chain='-- /sbin/init --target=chain'
else
chain=
fi
set base_menu_title="Sortix \$version for \$machine"
set menu_title="\$base_menu_title"
set title_single_user='live environment'
@ -204,6 +222,8 @@ set enable_sshd=false
export version
export machine
export mount
export chain
export base_menu_title
export menu_title
export title_single_user
@ -304,9 +324,10 @@ esac
cat << EOF
hook_kernel_pre
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
multiboot /$kernel \$no_random_seed \$enable_network_drivers "\$@"
multiboot /$kernel \$no_random_seed \$enable_network_drivers \$chain "\$@"
echo done
hook_kernel_post
# TODO: Injecting configuration doesn't work for mounted cdroms.
if ! \$enable_dhclient; then
echo -n "Disabling dhclient ... "
module /boot/grub/init/furthermore --create-to /etc/init/network
@ -331,7 +352,7 @@ cat << EOF
echo done
fi
EOF
for initrd in $system_initrd $src_initrd $live_initrd $overlay_initrd; do
for initrd in $initrds; do
if [ "$initrd" = "$src_initrd" ]; then
cat << EOF
if \$enable_src; then
@ -371,7 +392,7 @@ for port in $ports; do
fi
if \$port_$(portvar "$port"); then
echo -n "Loading /$tix ($(human_size $tix)) ... "
module /$tix --tix
module /$tix
echo done
fi
EOF
@ -427,9 +448,11 @@ menuentry "\$title_sysupgrade" '-- /sbin/init --target=sysupgrade'
cat << EOF
if ! $mount; then
menuentry "Select ports..." {
configfile /boot/grub/ports.cfg
}
fi
menuentry "Advanced..." {
configfile /boot/grub/advanced.cfg
@ -461,6 +484,7 @@ else
}
fi
if ! $mount; then
if "\$enable_src"; then
menuentry "Disable loading source code" {
enable_src=false
@ -472,6 +496,7 @@ else
configfile /boot/grub/advanced.cfg
}
fi
fi
if [ "\$enable_network_drivers" = --disable-network-drivers ]; then
menuentry "Enable networking drivers" {
@ -485,6 +510,7 @@ else
}
fi
if ! $mount; then
if \$enable_dhclient; then
menuentry "Disable DHCP client" {
enable_dhclient=false
@ -524,6 +550,7 @@ fi
menuentry "Select binary packages..." {
configfile /boot/grub/tix.cfg
}
fi
hook_advanced_menu_post
EOF

View File

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

View File

@ -0,0 +1,8 @@
#include <string.h>
int main(void)
{
void (*ptr)(void*, size_t) = explicit_bzero;
return ptr ? 0 : 1;
}
#define HAVE_EXPLICIT_BZERO 1

View File

@ -0,0 +1,8 @@
#include <stdlib.h>
int main(void)
{
void* (*ptr)(void*, size_t, size_t) = reallocarray;
return ptr ? 0 : 1;
}
#define HAVE_REALLOCARRAY 1

View File

@ -0,0 +1,8 @@
#include <string.h>
int main(void)
{
size_t (*ptr)(char*, const char*, size_t) = strlcat;
return ptr ? 0 : 1;
}
#define HAVE_STRLCAT 1

View File

@ -0,0 +1,8 @@
#include <string.h>
int main(void)
{
size_t (*ptr)(char*, const char*, size_t) = strlcpy;
return ptr ? 0 : 1;
}
#define HAVE_STRLCPY 1

View File

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

View File

@ -323,18 +323,39 @@ void window_destroy(struct window* window)
void window_on_display_resolution_change(struct window* window,
struct display* display)
{
// TODO: Move window back inside screen.
if ( window->window_state == WINDOW_STATE_MAXIMIZED )
switch ( window->window_state )
{
// TODO: Change size of maximized window.
(void) display;
case WINDOW_STATE_REGULAR:
{
ssize_t left = window->left, top = window->top;
if ( (ssize_t) display->screen_width <= left )
left = 0;
if ( (ssize_t) display->screen_height <= top )
top = 0;
window_move(window, left, top);
break;
}
case WINDOW_STATE_MAXIMIZED: window_maximize(window); break;
case WINDOW_STATE_MINIMIZED: break;
case WINDOW_STATE_TILE_LEFT: window_tile_left(window); break;
case WINDOW_STATE_TILE_RIGHT: window_tile_right(window); break;
case WINDOW_STATE_TILE_TOP: window_tile_top(window); break;
case WINDOW_STATE_TILE_TOP_LEFT: window_tile_top_left(window); break;
case WINDOW_STATE_TILE_TOP_RIGHT: window_tile_top_right(window); break;
case WINDOW_STATE_TILE_BOTTOM: window_tile_bottom(window); break;
case WINDOW_STATE_TILE_BOTTOM_LEFT: window_tile_bottom_left(window); break;
case WINDOW_STATE_TILE_BOTTOM_RIGHT:
window_tile_bottom_right(window);
break;
}
}
void window_tile(struct window* window, enum window_state state, size_t left,
size_t top, size_t width, size_t height)
{
if ( window->window_state == state )
if ( window->window_state == state &&
window->left == (ssize_t) left && window->top == (ssize_t) top &&
window->width == width && window->height == height )
return;
if ( window->window_state == WINDOW_STATE_REGULAR )

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* 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
@ -22,8 +22,8 @@
#include <assert.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
@ -212,7 +212,7 @@ int main(int argc, char* argv[])
read = read || write;
// Default to read and write filesystem access.
bool default_access = !read && !write ? read = write = true : false;
bool default_access = !read && !write ? (read = write = true) : false;
if ( argc == 1 )
{
@ -236,74 +236,71 @@ int main(int argc, char* argv[])
int fd = open(device_path, write ? O_RDWR : O_RDONLY);
if ( fd < 0 )
error(1, errno, "`%s'", device_path);
err(1, "%s", device_path);
// Read the super block from the filesystem so we can verify it.
struct ext_superblock sb;
if ( preadall(fd, &sb, sizeof(sb), 1024) != sizeof(sb) )
{
if ( errno == EEOF )
error(1, 0, "`%s' isn't a valid extended filesystem", device_path);
errx(1, "%s: Isn't a valid extended filesystem", device_path);
else
error(1, errno, "read: `%s'", device_path);
err(1, "read: %s", device_path);
}
// Verify the magic value to detect a compatible filesystem.
if ( sb.s_magic != EXT2_SUPER_MAGIC )
error(1, 0, "`%s' isn't a valid extended filesystem", device_path);
errx(1, "%s: Isn't a valid extended filesystem", device_path);
// Test whether this revision of the extended filesystem is supported.
if ( sb.s_rev_level == EXT2_GOOD_OLD_REV )
error(1, 0, "`%s' is formatted with an obsolete filesystem revision",
device_path);
errx(1, "%s: Is formatted with an obsolete filesystem revision",
device_path);
// Verify that no incompatible features are in use.
if ( sb.s_feature_compat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED )
error(1, 0, "`%s' uses unsupported and incompatible features",
device_path);
errx(1, "%s: Uses unsupported and incompatible features", device_path);
// Verify that no incompatible features are in use if opening for write.
if ( !default_access && write &&
sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED )
error(1, 0, "`%s' uses unsupported and incompatible features, "
"read-only access is possible, but write-access was "
"requested", device_path);
errx(1, "%s: Uses unsupported and incompatible features, "
"read-only access is possible, but write-access was requested",
device_path);
if ( write && sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED )
{
fprintf(stderr, "Warning: `%s' uses unsupported and incompatible "
"features, falling back to read-only access\n",
device_path);
warn("warning: %s: Uses unsupported and incompatible features, "
"falling back to read-only access\n", device_path);
// TODO: Modify the file descriptor such that writing fails!
write = false;
}
// Check whether any features are in use that we can safely disregard.
if ( sb.s_feature_compat & ~EXT2_FEATURE_COMPAT_SUPPORTED )
fprintf(stderr, "Note: filesystem uses unsupported but compatible "
"features\n");
warn("%s: Filesystem uses unsupported but compatible features\n",
device_path);
// Check the block size is sane. 64 KiB may have issues, 32 KiB then.
if ( sb.s_log_block_size > (15-10) /* 32 KiB blocks */ )
error(1, 0, "`%s': excess block size", device_path);
errx(1, "%s: Filesystem has excess block size", device_path);
// Check whether the filesystem was unmounted cleanly.
if ( sb.s_state != EXT2_VALID_FS )
fprintf(stderr, "Warning: `%s' wasn't unmounted cleanly\n",
device_path);
warn("warning: %s: Filesystem wasn't unmounted cleanly\n", device_path);
uint32_t block_size = 1024U << sb.s_log_block_size;
Device* dev = new Device(fd, device_path, block_size, write);
if ( !dev ) // TODO: Use operator new nothrow!
error(1, errno, "malloc");
err(1, "malloc");
Filesystem* fs = new Filesystem(dev, pretend_mount_path);
if ( !fs ) // TODO: Use operator new nothrow!
error(1, errno, "malloc");
err(1, "malloc");
fs->block_groups = new BlockGroup*[fs->num_groups];
if ( !fs->block_groups ) // TODO: Use operator new nothrow!
error(1, errno, "malloc");
err(1, "malloc");
for ( size_t i = 0; i < fs->num_groups; i++ )
fs->block_groups[i] = NULL;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2022 Jonas 'Sortie' Termansen.
* 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
@ -24,8 +24,8 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <error.h>
#include <dirent.h>
#include <fcntl.h>
#include <ioleast.h>
@ -658,8 +658,8 @@ void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
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);
warn("id exceeded 16-bit: uid=%ju gid=%ju\n",
(uintmax_t) request_uid, (uintmax_t) request_gid);
RespondError(chl, EOVERFLOW);
return;
}
@ -690,7 +690,7 @@ void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
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);
warn("message type %zu not supported\n", hdr->msgtype);
RespondError(chl, ENOTSUP);
return;
}
@ -744,14 +744,14 @@ int fsmarshall_main(const char* argv0,
struct stat root_inode_st;
Inode* root_inode = fs->GetInode((uint32_t) EXT2_ROOT_INO);
if ( !root_inode )
error(1, errno, "GetInode(%u)", EXT2_ROOT_INO);
err(1, "GetInode(%u)", EXT2_ROOT_INO);
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 )
error(1, errno, "%s", mount_path);
err(1, "%s", mount_path);
// Make sure the server isn't unexpectedly killed and data is lost.
signal(SIGINT, TerminationHandler);
@ -763,7 +763,7 @@ int fsmarshall_main(const char* argv0,
{
pid_t child_pid = fork();
if ( child_pid < 0 )
error(1, errno, "fork");
err(1, "fork");
if ( child_pid )
exit(0);
setpgid(0, 0);
@ -785,7 +785,7 @@ int fsmarshall_main(const char* argv0,
size_t amount;
if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) )
{
//error(0, errno, "incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
//warn("incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
errno = 0;
continue;
}

1
host/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
host

25
host/Makefile Normal file
View File

@ -0,0 +1,25 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CFLAGS += -Wall -Wextra
BINARIES = host
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARIES) $(DESTDIR)$(BINDIR)
%: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
clean:
rm -f $(BINARIES)

511
host/host.c Normal file
View File

@ -0,0 +1,511 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* host.c
* Domain name system client.
*/
#include <sys/dnsconfig.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <endian.h>
#include <err.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DNS_SIZE 512
#define DNS_NAME_MAX 255
#define DNS_LABEL_MAX 64
struct dns_header
{
uint16_t id;
uint16_t flags;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
};
struct dns_question
{
uint16_t qtype;
uint16_t qclass;
};
struct dns_record
{
uint16_t type;
uint16_t class;
uint16_t ttl_high;
uint16_t ttl_low;
uint16_t rdlength;
};
#define DNS_HEADER_FLAGS_RCODE_MASK (0xF << 0)
#define DNS_HEADER_FLAGS_RCODE_NO (0 << 0)
#define DNS_HEADER_FLAGS_RCODE_FORMAT (1 << 0)
#define DNS_HEADER_FLAGS_RCODE_SERVER (2 << 0)
#define DNS_HEADER_FLAGS_RCODE_NAME (3 << 0)
#define DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED (4 << 0)
#define DNS_HEADER_FLAGS_RCODE_REFUSED (5 << 0)
#define DNS_HEADER_FLAGS_RA (1 << 7)
#define DNS_HEADER_FLAGS_RD (1 << 8)
#define DNS_HEADER_FLAGS_TC (1 << 9)
#define DNS_HEADER_FLAGS_AA (1 << 10)
#define DNS_HEADER_FLAGS_OPCODE_MASK (0xF << 11)
#define DNS_HEADER_FLAGS_OPCODE_QUERY (0 << 11)
#define DNS_HEADER_FLAGS_OPCODE_IQUERY (1 << 11)
#define DNS_HEADER_FLAGS_OPCODE_STATUS (2 << 11)
#define DNS_HEADER_FLAGS_QR (1 << 15)
#define DNS_TYPE_A 1
#define DNS_TYPE_NS 2
#define DNS_TYPE_MD 3
#define DNS_TYPE_MF 4
#define DNS_TYPE_CNAME 5
#define DNS_TYPE_SOA 6
#define DNS_TYPE_MB 7
#define DNS_TYPE_MG 8
#define DNS_TYPE_MR 9
#define DNS_TYPE_NULL 10
#define DNS_TYPE_WKS 11
#define DNS_TYPE_PTR 12
#define DNS_TYPE_HINFO 13
#define DNS_TYPE_MINFO 14
#define DNS_TYPE_MX 15
#define DNS_TYPE_TXT 16
#define DNS_TYPE_AAAA 28
#define DNS_QTYPE_AXFR 252
#define DNS_QTYPE_MAILB 253
#define DNS_QTYPE_MAILA 254
#define DNS_QTYPE_ANY 255
#define DNS_CLASS_IN 1
#define DNS_CLASS_CS 2
#define DNS_CLASS_CH 3
#define DNS_CLASS_HS 4
#define DNS_QCLASS_ANY 255
static size_t encode_dns_header(unsigned char* msg,
size_t offset,
const struct dns_header* hdrin)
{
struct dns_header hdr;
if ( DNS_SIZE - offset < sizeof(hdr) )
errx(1, "dns message too large");
hdr.id = htobe16(hdrin->id);
hdr.flags = htobe16(hdrin->flags);
hdr.qdcount = htobe16(hdrin->qdcount);
hdr.ancount = htobe16(hdrin->ancount);
hdr.nscount = htobe16(hdrin->nscount);
hdr.arcount = htobe16(hdrin->arcount);
memcpy(msg + offset, &hdr, sizeof(hdr));
return offset + sizeof(hdr);
}
static size_t encode_dns_byte(unsigned char* msg,
size_t offset,
unsigned char byte)
{
if ( DNS_SIZE - offset < 1 )
errx(1, "dns message too large");
msg[offset] = byte;
return offset + 1;
}
static size_t encode_dns_name(unsigned char* msg,
size_t offset,
const char* name)
{
size_t index = 0;
size_t namelen = 0;
if ( !name[0] )
errx(1, "'%s' is not a valid name (unexpected end of input)", name);
while ( name[index] )
{
if ( !strcmp(name + index, ".") )
break;
if ( name[index] == '.' )
errx(1, "'%s' is not a valid name (empty label)", name);
size_t length = strcspn(name + index, ".");
if ( DNS_LABEL_MAX <= length )
errx(1, "'%s' is not a valid name (label too long)", name);
if ( namelen++ == DNS_NAME_MAX )
errx(1, "'%s' is not a valid name (name is too long)", name);
offset = encode_dns_byte(msg, offset, length & 0xFF);
for ( size_t i = 0; i < length; i++ )
{
if ( namelen++ == DNS_NAME_MAX )
errx(1, "'%s' is not a valid name (name is too long)", name);
offset = encode_dns_byte(msg, offset, name[index + i]);
}
index += length;
if ( name[index] == '.' )
index++;
}
if ( namelen++ == DNS_NAME_MAX )
errx(1, "'%s' is not a valid name (name is too long)", name);
offset = encode_dns_byte(msg, offset, 0);
return offset;
}
static size_t encode_dns_question(unsigned char* msg,
size_t offset,
const char* name,
const struct dns_question* qsin)
{
offset = encode_dns_name(msg, offset, name);
struct dns_question qs;
if ( DNS_SIZE - offset < sizeof(qs) )
errx(1, "dns message too large");
qs.qtype = htobe16(qsin->qtype);
qs.qclass = htobe16(qsin->qclass);
memcpy(msg + offset, &qs, sizeof(qs));
return offset + sizeof(qs);
}
static size_t decode_dns_header(const unsigned char* msg,
size_t offset,
size_t msg_size,
struct dns_header* hdrout)
{
struct dns_header hdr;
if ( msg_size - offset < sizeof(hdr) )
errx(1, "dns message too small");
memcpy(&hdr, msg + offset, sizeof(hdr));
hdrout->id = be16toh(hdr.id);
hdrout->flags = be16toh(hdr.flags);
hdrout->qdcount = be16toh(hdr.qdcount);
hdrout->ancount = be16toh(hdr.ancount);
hdrout->nscount = be16toh(hdr.nscount);
hdrout->arcount = be16toh(hdr.arcount);
return offset + sizeof(hdr);
}
static size_t decode_dns_byte(const unsigned char* msg,
size_t offset,
size_t msg_size,
unsigned char* byte)
{
if ( msg_size <= offset || msg_size - offset < 1 )
errx(1, "dns message too small");
*byte = msg[offset];
return offset + 1;
}
static size_t decode_dns_name(const unsigned char* msg,
size_t offset,
size_t msg_size,
char* name)
{
bool real_offset_set = false;
size_t real_offset = 0;
size_t index = 0;
size_t namelen = 0;
uint8_t b;
while ( true )
{
if ( namelen++ == DNS_NAME_MAX )
errx(1, "name too long");
offset = decode_dns_byte(msg, offset, msg_size, &b);
if ( 0xC0 & b )
{
namelen--;
size_t ptr = (b & 0x3F) << 8;
offset = decode_dns_byte(msg, offset, msg_size, &b);
ptr |= b;
if ( !real_offset_set )
{
real_offset = offset;
real_offset_set = true;
}
offset = ptr;
continue;
}
size_t length = b;
if ( DNS_LABEL_MAX <= length )
errx(1, "label too long");
if ( !length )
break;
if ( index )
name[index++] = '.';
for ( size_t i = 0; i < length; i++ )
{
if ( namelen++ == DNS_NAME_MAX )
errx(1, "name too long");
offset = decode_dns_byte(msg, offset, msg_size, &b);
// TODO: Handle if b == '.'.
name[index++] = b;
}
}
name[index++] = '.';
name[index] = '\0';
if ( real_offset_set )
return real_offset;
return offset;
}
static size_t decode_dns_question(const unsigned char* msg,
size_t offset,
size_t msg_size,
char* name,
struct dns_question* qsout)
{
offset = decode_dns_name(msg, offset, msg_size, name);
struct dns_question qs;
if ( msg_size <= offset || msg_size - offset < sizeof(qs) )
errx(1, "dns message too small");
memcpy(&qs, msg + offset, sizeof(qs));
qsout->qtype = be16toh(qs.qtype);
qsout->qclass = be16toh(qs.qclass);
return offset + sizeof(qs);
}
static size_t decode_dns_record(const unsigned char* msg,
size_t offset,
size_t msg_size,
char* name,
struct dns_record* rrout)
{
offset = decode_dns_name(msg, offset, msg_size, name);
struct dns_record rr;
if ( msg_size <= offset || msg_size - offset < sizeof(rr) )
errx(1, "dns message too small");
memcpy(&rr, msg + offset, sizeof(rr));
rrout->type = be16toh(rr.type);
rrout->class = be16toh(rr.class);
rrout->ttl_high = be16toh(rr.ttl_high);
rrout->ttl_low = be16toh(rr.ttl_low);
rrout->rdlength = be16toh(rr.rdlength);
return offset + sizeof(rr);
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
int main(int argc, char* argv[])
{
int ipv = 4;
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
case '4': ipv = 4; break;
case '6': ipv = 6; break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
exit(1);
}
}
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
exit(1);
}
}
compact_arguments(&argc, &argv);
if ( argc < 2 )
errx(1, "No host given");
const char* host = argv[1];
char nsipstr[INET_ADDRSTRLEN];
const char* nameserver;
if ( argc < 3 )
{
struct dnsconfig dnscfg;
if ( getdnsconfig(&dnscfg) < 0 )
err(1, "dnsconfig");
bool found = false;
for ( size_t i = 0; !found && i < dnscfg.servers_count; i++ )
{
if ( dnscfg.servers[i].family != AF_INET )
continue;
inet_ntop(AF_INET, &dnscfg.servers[i].addr, nsipstr, sizeof(nsipstr));
found = true;
}
if ( !found )
errx(1, "No nameserver given and no default configured");
nameserver = nsipstr;
}
else
nameserver = argv[2];
int port = 4 <= argc ? atoi(argv[3]) : 53;
if ( 5 <= argc )
errx(1, "Unexpected extra operand");
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if ( fd < 0 )
err(1, "socket");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htobe16(port);
if ( inet_pton(AF_INET, nameserver, &addr.sin_addr) < 1 )
errx(1, "invalid ip address: %s", nameserver);
if ( connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0 )
err(1, "connect");
unsigned char req[DNS_SIZE];
size_t req_size = 0;
struct dns_header hdr;
hdr.id = 0;
hdr.flags = DNS_HEADER_FLAGS_RD;
hdr.qdcount = 1;
hdr.ancount = 0;
hdr.nscount = 0;
hdr.arcount = 0;
req_size = encode_dns_header(req, req_size, &hdr);
struct dns_question qs;
qs.qtype = ipv == 4 ? DNS_TYPE_A : DNS_TYPE_AAAA;
qs.qclass = DNS_CLASS_IN;
req_size = encode_dns_question(req, req_size, host, &qs);
ssize_t amount = send(fd, req, req_size, 0);
if ( amount < 0 )
err(1, "send");
// TODO: Use recvfrom to get the server ip.
unsigned char resp[DNS_SIZE];
ssize_t resp_size = recv(fd, resp, sizeof(resp), 0);
if ( resp_size < 0 )
err(1, "recv");
// TODO: Verify the response came from the correct ip.
size_t offset = 0;
offset = decode_dns_header(resp, offset, resp_size, &hdr);
// TODO: Verify the response has the correct id.
#if 0
printf("id = %u\n", hdr.id);
printf("flags = 0x%X\n", hdr.flags);
printf("qdcount = %u\n", hdr.qdcount);
printf("ancount = %u\n", hdr.ancount);
printf("nscount = %u\n", hdr.nscount);
printf("arcount = %u\n", hdr.arcount);
#endif
uint16_t rcode = hdr.flags & DNS_HEADER_FLAGS_RCODE_MASK;
if ( rcode == DNS_HEADER_FLAGS_RCODE_FORMAT )
errx(1, "format error");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_SERVER )
errx(1, "server error");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NAME )
errx(1, "no such name");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED )
errx(1, "not implemented error");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_REFUSED )
errx(1, "refused");
else if ( rcode != DNS_HEADER_FLAGS_RCODE_NO )
errx(1, "unknown error (rcode=0x%X)", rcode);
if ( hdr.flags & DNS_HEADER_FLAGS_TC )
errx(1, "truncated");
// TODO: Check query bit.
for ( uint16_t i = 0; i < hdr.qdcount; i++ )
{
char name[DNS_NAME_MAX + 1];
offset = decode_dns_question(resp, offset, resp_size, name, &qs);
//printf("%s type=%u class=%u\n", name, qs.qtype, qs.qclass);
}
for ( uint16_t i = 0; i < hdr.ancount; i++ )
{
char name[DNS_NAME_MAX + 1];
struct dns_record rr;
offset = decode_dns_record(resp, offset, resp_size, name, &rr);
uint32_t ttl = (uint32_t) rr.ttl_high << 16 | rr.ttl_low;
printf("%s type=%u class=%u ttl=%u ", name, rr.type, rr.class, ttl);
if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_A )
{
unsigned char ip[4];
for ( size_t i = 0; i < 4; i++ )
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
printf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
}
else if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_AAAA )
{
unsigned char ip[16];
for ( size_t i = 0; i < 16; i++ )
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
for ( size_t i = 0; i < 16; i++ )
{
if ( i && !(i & 1) )
putchar(':');
printf("%02x", ip[i]);
}
}
else if ( rr.type == DNS_TYPE_CNAME )
{
char cname[DNS_NAME_MAX + 1];
offset = decode_dns_name(resp, offset, resp_size, cname);
printf("CNAME %s", cname);
}
else
{
printf("0x");
fflush(stdout);
for ( size_t i = 0; i < rr.rdlength; i++ )
{
unsigned char b;
offset = decode_dns_byte(resp, offset, resp_size, &b);
if ( isprint(b) && b != '\'' )
printf("'%c'", b);
else
printf("%02X", b);
fflush(stdout);
}
}
printf("\n");
}
return 0;
}

4
irc/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
irc
*.o
config.h
tests

56
irc/Makefile Normal file
View File

@ -0,0 +1,56 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CFLAGS += -Wall -Wextra
CPPFLAGS += -DVERSIONSTR=\"$(VERSION)\"
ifeq ($(HOST_IS_SORTIX),0)
CPPFLAGS+=-D_GNU_SOURCE
endif
BINARY = irc
#MANPAGES1 = irc.1
OBJS=\
compat.o \
connection.o \
database.o \
irc.o \
scrollback.o \
string.o \
ui.o \
all: $(BINARY)
.PHONY: all install clean
$(OBJS): config.h
%.o: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
$(BINARY): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(BINARY) $(LIBS)
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARY) $(DESTDIR)$(BINDIR)
#mkdir -p $(DESTDIR)$(MANDIR)/man1
#install $(MANPAGES1) $(DESTDIR)$(MANDIR)/man1
clean:
rm -f $(BINARY)
rm -f $(OBJS) *.o
TESTS=\
have-explicit_bzero \
have-reallocarray \
have-strlcat \
have-strlcpy \
include ../build-aux/config.mak

65
irc/compat.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* compat.c
* Compatibility.
*/
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "compat.h"
#ifndef HAVE_EXPLICIT_BZERO
void explicit_bzero(void* buffer, size_t size)
{
memset(buffer, 0, size);
}
#endif
#ifndef HAVE_REALLOCARRAY
void* reallocarray(void* ptr, size_t nmemb, size_t size)
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
return realloc(ptr, nmemb * size);
}
#endif
#ifndef HAVE_STRLCAT
size_t strlcat(char* restrict dest, const char* restrict src, size_t size)
{
size_t dest_len = strnlen(dest, size);
if ( size <= dest_len )
return dest_len + strlen(src);
return dest_len + strlcpy(dest + dest_len, src, size - dest_len);
}
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict dest, const char* restrict src, size_t size)
{
if ( !size )
return strlen(src);
size_t result;
for ( result = 0; result < size-1 && src[result]; result++ )
dest[result] = src[result];
dest[result] = '\0';
return result + strlen(src + result);
}
#endif

40
irc/compat.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* combat.h
* Compatibility.
*/
#ifndef COMPAT_H
#define COMPAT_H
#include <stddef.h>
#include "config.h"
#ifndef HAVE_EXPLICIT_BZERO
void explicit_bzero(void*, size_t);
#endif
#ifndef HAVE_REALLOCARRAY
void* reallocarray(void*, size_t, size_t);
#endif
#ifndef HAVE_STRLCAT
size_t strlcat(char* restrict, const char* restrict, size_t);
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict, const char* restrict, size_t);
#endif
#endif

549
irc/connection.c Normal file
View File

@ -0,0 +1,549 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* connection.c
* IRC protocol.
*/
#include <sys/socket.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "compat.h"
#include "connection.h"
void dump_error(const char* message,
size_t message_size,
const struct timespec* when)
{
// TODO: Send the error somewhere appropriate in the UI.
fprintf(stderr, "\e[91m");
struct tm tm;
gmtime_r(&when->tv_sec, &tm);
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
when->tv_nsec);
for ( size_t i = 0; i < message_size; i++ )
{
if ( message[i] == '\r' )
continue;
else if ( message[i] == '\n' )
continue;
else if ( (unsigned char) message[i] < 32 )
{
fprintf(stderr, "\e[31m");
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
fprintf(stderr, "\e[91m");
}
fputc((unsigned char) message[i], stderr);
}
fprintf(stderr, "\e[m\n");
}
void dump_outgoing(const char* message,
size_t message_size,
const struct timespec* when)
{
return; // TODO: Remove this, or adopt it for a logging mechanism.
fprintf(stderr, "\e[92m");
struct tm tm;
gmtime_r(&when->tv_sec, &tm);
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
when->tv_nsec);
for ( size_t i = 0; i < message_size; i++ )
{
if ( message[i] == '\r' )
continue;
else if ( message[i] == '\n' )
continue;
else if ( (unsigned char) message[i] < 32 )
{
fprintf(stderr, "\e[91m");
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
fprintf(stderr, "\e[92m");
continue;
}
fputc((unsigned char) message[i], stderr);
}
fprintf(stderr, "\e[m\n");
}
void dump_incoming(const char* message,
size_t message_size,
const struct timespec* when)
{
return; // TODO: Remove this, or adopt it for a logging mechanism.
fprintf(stderr, "\e[93m");
struct tm tm;
gmtime_r(&when->tv_sec, &tm);
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
when->tv_nsec);
for ( size_t i = 0; i < message_size; i++ )
{
if ( message[i] == '\r' )
continue;
else if ( message[i] == '\n' )
continue;
else if ( (unsigned char) message[i] < 32 )
{
fprintf(stderr, "\e[91m");
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
fprintf(stderr, "\e[93m");
continue;
}
fputc((unsigned char) message[i], stderr);
}
fprintf(stderr, "\e[m\n");
}
void irc_error_vlinef(const char* format, va_list ap_orig)
{
va_list ap;
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
va_copy(ap, ap_orig);
char* string;
if ( 0 <= vasprintf(&string, format, ap) )
{
va_end(ap);
dump_error(string, strlen(string), &now);
free(string);
return;
}
va_end(ap);
char buffer[512];
va_copy(ap, ap_orig);
if ( 0 <= vsnprintf(buffer, sizeof(buffer), format, ap) )
{
va_end(ap);
dump_error(buffer, strlen(buffer), &now);
dump_error("(vasprintf failed printing that line)",
strlen("(vasprintf failed printing that line)"), &now);
return;
}
va_end(ap);
dump_error(format, strlen(format), &now);
dump_error("(vsnprintf failed printing format string)",
strlen("(vsnprintf failed printing that format string)"), &now);
}
void irc_error_linef(const char* format, ...)
{
va_list ap;
va_start(ap, format);
irc_error_vlinef(format, ap),
va_end(ap);
}
void irc_transmit(struct irc_connection* irc_connection,
const char* message,
size_t message_size)
{
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
if ( irc_connection->connectivity_error )
return;
dump_outgoing(message, message_size, &now);
int fd = irc_connection->fd;
while ( message_size )
{
ssize_t amount = 0;
amount = send(fd, message, message_size, MSG_NOSIGNAL);
if ( amount < 0 || amount == 0 )
{
warn("send");
irc_connection->connectivity_error = true;
return;
}
message += amount;
message_size -= amount;
}
}
void irc_transmit_message(struct irc_connection* irc_connection,
const char* message,
size_t message_size)
{
assert(2 <= message_size);
assert(message[message_size - 2] == '\r');
assert(message[message_size - 1] == '\n');
char buffer[512];
if ( 512 < message_size )
{
memcpy(buffer, message, 510);
buffer[510] = '\r';
buffer[511] = '\n';
message = buffer;
message_size = 512;
}
irc_transmit(irc_connection, message, message_size);
explicit_bzero(buffer, sizeof(buffer));
}
void irc_receive_more_bytes(struct irc_connection* irc_connection)
{
if ( irc_connection->connectivity_error )
return;
int fd = irc_connection->fd;
char* buffer = irc_connection->incoming_buffer;
size_t buffer_size = sizeof(irc_connection->incoming_buffer);
size_t buffer_used = irc_connection->incoming_amount;
size_t buffer_free = buffer_size - buffer_used;
if ( buffer_free == 0 )
return;
// TODO: Use non-blocking IO for transmitting as well so O_NONBLOCK can
// always be used.
// TODO: Use MSG_DONTWAIT when supported in Sortix.
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
ssize_t amount = recv(fd, buffer + buffer_used, buffer_free, 0);
fcntl(fd, F_SETFL, flags);
if ( amount < 0 )
{
if ( errno == EAGAIN || errno == EWOULDBLOCK )
return;
warn("recv");
irc_connection->connectivity_error = true;
}
else if ( amount == 0 )
{
// TODO: Gracefully close the connection.
irc_connection->connectivity_error = true;
}
else
{
irc_connection->incoming_amount += amount;
}
}
void irc_receive_pop_bytes(struct irc_connection* irc_connection,
char* buffer,
size_t count)
{
assert(count <= irc_connection->incoming_amount);
memcpy(buffer, irc_connection->incoming_buffer, count);
explicit_bzero(irc_connection->incoming_buffer, count);
memmove(irc_connection->incoming_buffer,
irc_connection->incoming_buffer + count,
irc_connection->incoming_amount - count);
irc_connection->incoming_amount -= count;
explicit_bzero(irc_connection->incoming_buffer + irc_connection->incoming_amount,
count);
}
bool irc_receive_message(struct irc_connection* irc_connection,
char message[512],
struct timespec* when)
{
if ( irc_connection->connectivity_error )
return false;
size_t message_usable = 0;
while ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] != '\r' &&
irc_connection->incoming_buffer[message_usable] != '\n' )
message_usable++;
if ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] == '\r')
{
message_usable++;
if ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] == '\n' )
{
message_usable++;
irc_receive_pop_bytes(irc_connection, message, message_usable);
assert(message[message_usable-2] == '\r');
assert(message[message_usable-1] == '\n');
message[message_usable-2] = '\0';
message[message_usable-1] = '\0';
// TODO: This is not always when the message did arrive.
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
dump_incoming(message, message_usable-2, &now);
*when = now;
return true;
}
else if ( message_usable < irc_connection->incoming_amount )
{
// TODO: Handle bad newline sequence.
warnx("recv: bad IRC newline");
irc_connection->connectivity_error = true;
return false;
}
}
else if ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] == '\n' )
{
// TODO: Handle bad newline sequence.
warnx("recv: bad IRC newline");
irc_connection->connectivity_error = true;
return false;
}
if ( message_usable == 512 )
{
// TODO: Handle untruncated lines from the server.
warnx("recv: overlong IRC line from server");
irc_connection->connectivity_error = true;
return false;
}
return false;
}
void irc_transmit_string(struct irc_connection* irc_connection,
const char* string)
{
char message[512];
strncpy(message, string, 510);
message[510] = '\0';
message[511] = '\0';
size_t string_truncated_length = strlen(message);
for ( size_t i = 0; i < string_truncated_length; i++ )
{
if ( message[i] == '\r' )
message[i] = ' ';
if ( message[i] == '\n' )
message[i] = ' ';
}
message[string_truncated_length + 0] = '\r';
message[string_truncated_length + 1] = '\n';
size_t message_length = strnlen(message, 512);
irc_transmit_message(irc_connection, message, message_length);
explicit_bzero(message, sizeof(message));
}
__attribute__((format(printf, 2, 0)))
void irc_transmit_vformat(struct irc_connection* irc_connection,
const char* format,
va_list ap)
{
char* string = NULL;
if ( vasprintf(&string, format, ap) < 0 )
{
// TODO: Hmm, what do we do here.
warn("vasprintf");
// TODO: Should the error condition be set?
return;
}
irc_transmit_string(irc_connection, string);
explicit_bzero(string, strlen(string));
free(string);
}
__attribute__((format(printf, 2, 3)))
void irc_transmit_format(struct irc_connection* irc_connection,
const char* format,
...)
{
va_list ap;
va_start(ap, format);
irc_transmit_vformat(irc_connection, format, ap);
va_end(ap);
}
void irc_command_pass(struct irc_connection* irc_connection,
const char* password)
{
irc_transmit_format(irc_connection, "PASS :%s", password);
}
void irc_command_nick(struct irc_connection* irc_connection,
const char* nick)
{
irc_transmit_format(irc_connection, "NICK :%s", nick);
}
void irc_command_user(struct irc_connection* irc_connection,
const char* nick,
const char* local_hostname,
const char* server_hostname,
const char* real_name)
{
// TODO: What if there are spaces in some of these fields?
irc_transmit_format(irc_connection, "USER %s %s %s :%s",
nick, local_hostname, server_hostname, real_name);
}
void irc_command_join(struct irc_connection* irc_connection,
const char* channel)
{
irc_transmit_format(irc_connection, "JOIN :%s", channel);
}
void irc_command_part(struct irc_connection* irc_connection,
const char* channel)
{
irc_transmit_format(irc_connection, "PART :%s", channel);
}
void irc_command_privmsg(struct irc_connection* irc_connection,
const char* where,
const char* what)
{
// TODO: Ensure where is valid.
irc_transmit_format(irc_connection, "PRIVMSG %s :%s", where, what);
}
void irc_command_privmsgf(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...)
{
va_list ap;
va_start(ap, what_format);
char msg[512];
vsnprintf(msg, sizeof(msg), what_format, ap);
irc_command_privmsg(irc_connection, where, msg);
va_end(ap);
}
void irc_command_notice(struct irc_connection* irc_connection,
const char* where,
const char* what)
{
// TODO: Ensure where is valid.
irc_transmit_format(irc_connection, "NOTICE %s :%s", where, what);
}
void irc_command_noticef(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...)
{
va_list ap;
va_start(ap, what_format);
char msg[512];
vsnprintf(msg, sizeof(msg), what_format, ap);
irc_command_notice(irc_connection, where, msg);
va_end(ap);
}
void irc_command_kick(struct irc_connection* irc_connection,
const char* where,
const char* who,
const char* why)
{
// TODO: Ensure where and who are valid.
if ( why )
irc_transmit_format(irc_connection, "KICK %s %s :%s", where, who, why);
else
irc_transmit_format(irc_connection, "KICK %s %s", where, who);
}
void irc_command_quit(struct irc_connection* irc_connection,
const char* message)
{
if ( message )
irc_transmit_format(irc_connection, "QUIT :%s", message);
else
irc_transmit_string(irc_connection, "QUIT");
shutdown(irc_connection->fd, SHUT_WR);
}
void irc_command_quit_malfunction(struct irc_connection* irc_connection,
const char* message)
{
if ( message )
irc_transmit_format(irc_connection, "QUIT :%s", message);
else
irc_transmit_string(irc_connection, "QUIT");
shutdown(irc_connection->fd, SHUT_RDWR);
}
void irc_parse_message_parameter(char* message,
char* parameters[16],
size_t* num_parameters_ptr)
{
size_t num_parameters = 0;
while ( message[0] != '\0' )
{
if ( message[0] == ':' || num_parameters == (16-1) -1 )
{
message++;
parameters[num_parameters++] = message;
break;
}
parameters[num_parameters++] = message;
size_t usable = 0;
while ( message[usable] != '\0' && message[usable] != ' ' )
usable++;
char lc = message[usable];
message[usable] = '\0';
if ( lc != '\0' )
message += usable + 1;
else
message += usable;
}
*num_parameters_ptr = num_parameters;
}
void irc_parse_who(char* full, const char** who, const char** whomask)
{
size_t bangpos = strcspn(full, "!");
if ( full[bangpos] == '!' )
{
full[bangpos] = '\0';
*who = full;
*whomask = full + bangpos + 1;
}
else
{
*who = full;
*whomask = "";
}
}

102
irc/connection.h Normal file
View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* connection.h
* IRC protocol.
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <time.h>
struct irc_connection
{
int fd;
bool connectivity_error;
char incoming_buffer[512];
size_t incoming_amount;
};
__attribute__((format(printf, 1, 0)))
void irc_error_vlinef(const char* format, va_list ap);
__attribute__((format(printf, 1, 2)))
void irc_error_linef(const char* format, ...);
void irc_transmit(struct irc_connection* irc_connection,
const char* message,
size_t message_size);
void irc_transmit_message(struct irc_connection* irc_connection,
const char* message,
size_t message_size);
void irc_transmit_string(struct irc_connection* irc_connection,
const char* string);
__attribute__((format(printf, 2, 0)))
void irc_transmit_vformat(struct irc_connection* irc_connection,
const char* format,
va_list ap);
__attribute__((format(printf, 2, 3)))
void irc_transmit_format(struct irc_connection* irc_connection,
const char* format,
...);
void irc_receive_more_bytes(struct irc_connection* irc_connection);
bool irc_receive_message(struct irc_connection* irc_connection,
char message[512],
struct timespec* when);
void irc_command_pass(struct irc_connection* irc_connection,
const char* password);
void irc_command_nick(struct irc_connection* irc_connection,
const char* nick);
void irc_command_user(struct irc_connection* irc_connection,
const char* nick,
const char* local_hostname,
const char* server_hostname,
const char* real_name);
void irc_command_join(struct irc_connection* irc_connection,
const char* channel);
void irc_command_part(struct irc_connection* irc_connection,
const char* channel);
void irc_command_privmsg(struct irc_connection* irc_connection,
const char* where,
const char* what);
__attribute__((format(printf, 3, 4)))
void irc_command_privmsgf(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...);
void irc_command_notice(struct irc_connection* irc_connection,
const char* where,
const char* what);
__attribute__((format(printf, 3, 4)))
void irc_command_noticef(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...);
void irc_command_kick(struct irc_connection* irc_connection,
const char* where,
const char* who,
const char* why);
void irc_command_quit(struct irc_connection* irc_connection,
const char* message);
void irc_command_quit_malfunction(struct irc_connection* irc_connection,
const char* message);
void irc_parse_message_parameter(char* message,
char* parameters[16],
size_t* num_parameters_ptr);
void irc_parse_who(char* full, const char** who, const char** whomask);
#endif

237
irc/database.c Normal file
View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* database.c
* Data structure for keeping track of channels and people.
*/
#include <sys/types.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "database.h"
#include "network.h"
#include "string.h"
struct channel* find_channel(const struct network* state, const char* channel_name)
{
assert(channel_name);
for ( struct channel* channel = state->channels; channel; channel = channel->next_channel )
if ( strchannelcmp(channel->name, channel_name) == 0 )
return channel;
return NULL;
}
struct channel* add_channel(struct network* state, const char* channel_name)
{
assert(channel_name);
assert(!find_channel(state, channel_name));
struct channel* channel = (struct channel*) calloc(sizeof(struct channel), 1);
if ( !channel )
return NULL;
channel->name = strdup(channel_name);
if ( !channel->name )
return free(channel), (struct channel*) NULL;
channel->people = NULL;
channel->prev_channel = NULL;
channel->next_channel = state->channels;
if ( state->channels )
state->channels->prev_channel = channel;
state->channels = channel;
return channel;
}
struct channel* get_channel(struct network* state, const char* channel_name)
{
assert(channel_name);
for ( struct channel* result = find_channel(state, channel_name); result; result = NULL )
return result;
return add_channel(state, channel_name);
}
void remove_channel(struct network* state, struct channel* channel)
{
while ( channel->people )
remove_person_from_channel(state, channel->people);
if ( channel->prev_channel )
channel->prev_channel->next_channel = channel->next_channel;
else
state->channels = channel->next_channel;
if ( channel->next_channel )
channel->next_channel->prev_channel = channel->prev_channel;
free(channel->name);
free(channel);
}
struct person* find_person(const struct network* state, const char* nick)
{
assert(nick);
for ( struct person* person = state->people; person; person = person->next_person )
if ( strnickcmp(person->nick, nick) == 0 )
return person;
return NULL;
}
struct person* add_person(struct network* state, const char* nick)
{
assert(nick);
assert(!find_person(state, nick));
struct person* person = (struct person*) calloc(sizeof(struct person), 1);
if ( !person )
return NULL;
person->nick = strdup(nick);
if ( !person->nick )
return free(person), (struct person*) NULL;
person->prev_person = NULL;
person->next_person = state->people;
if ( state->people )
state->people->prev_person = person;
state->people = person;
return person;
}
struct person* get_person(struct network* state, const char* nick)
{
assert(nick);
for ( struct person* result = find_person(state, nick); result; result = NULL )
return result;
return add_person(state, nick);
}
void remove_person(struct network* state, struct person* person)
{
while ( person->channels )
remove_person_from_channel(state, person->channels);
if ( person->prev_person )
person->prev_person->next_person = person->next_person;
else
state->people = person->next_person;
if ( person->next_person )
person->next_person->prev_person = person->prev_person;
free(person->nick);
free(person);
}
struct channel_person* find_person_in_channel(const struct network* state, const char* nick, const char* channel_name)
{
assert(nick);
assert(channel_name);
struct channel* channel = find_channel(state, channel_name);
if ( !channel )
return NULL;
for ( struct channel_person* channel_person = channel->people; channel_person; channel_person = channel_person->next_person_in_channel )
{
assert(channel_person->person);
assert(channel_person->person->nick);
if ( strnickcmp(channel_person->person->nick, nick) == 0 )
return channel_person;
}
return NULL;
}
struct channel_person* add_person_to_channel(struct network* state, struct person* person, struct channel* channel)
{
assert(person);
assert(channel);
assert(person->nick);
assert(channel->name);
assert(!find_person_in_channel(state, person->nick, channel->name));
struct channel_person* channel_person = (struct channel_person*)
calloc(sizeof(struct channel_person), 1);
if ( !channel_person )
return NULL;
channel_person->channel = channel;
channel_person->person = person;
channel_person->prev_channel_person = NULL;
channel_person->next_channel_person = state->channel_people;
if ( state->channel_people )
state->channel_people->prev_channel_person = channel_person;
state->channel_people = channel_person;
channel_person->prev_person_in_channel = NULL;
channel_person->next_person_in_channel = channel->people;
if ( channel->people )
channel->people->prev_person_in_channel = channel_person;
channel->people = channel_person;
channel_person->prev_channel_for_person = NULL;
channel_person->next_channel_for_person = person->channels;
if ( person->channels )
person->channels->prev_channel_for_person = channel_person;
person->channels = channel_person;
return channel_person;
}
struct channel_person* get_person_in_channel(struct network* state, struct person* person, struct channel* channel)
{
for ( struct channel_person* result =
find_person_in_channel(state, person->nick, channel->name); result; result = NULL )
return result;
return add_person_to_channel(state, person, channel);
}
void remove_person_from_channel(struct network* state, struct channel_person* channel_person)
{
if ( state->channel_people != channel_person )
channel_person->prev_channel_person->next_channel_person = channel_person->next_channel_person;
else
state->channel_people = channel_person->next_channel_person;
if ( channel_person->next_channel_person )
channel_person->next_channel_person->prev_channel_person = channel_person->prev_channel_person;
if ( channel_person->channel->people != channel_person )
channel_person->prev_person_in_channel->next_person_in_channel = channel_person->next_person_in_channel;
else
channel_person->channel->people = channel_person->next_person_in_channel;
if ( channel_person->next_person_in_channel )
channel_person->next_person_in_channel->prev_person_in_channel = channel_person->prev_person_in_channel;
if ( channel_person->person->channels != channel_person )
channel_person->prev_channel_for_person->next_channel_for_person = channel_person->next_channel_for_person;
else
channel_person->person->channels = channel_person->next_channel_for_person;
if ( channel_person->next_channel_for_person )
channel_person->next_channel_for_person->prev_channel_for_person = channel_person->prev_channel_for_person;
free(channel_person);
}

73
irc/database.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* database.h
* Data structure for keeping track of channels and people.
*/
#ifndef DATABASE_H
#define DATABASE_H
struct channel;
struct channel_person;
struct network;
struct person;
struct person
{
struct person* prev_person;
struct person* next_person;
char* nick;
struct channel_person* channels;
bool always_observable; // myself or having private messaged me
};
struct channel_person
{
struct channel_person* prev_channel_person;
struct channel_person* next_channel_person;
struct channel_person* prev_person_in_channel;
struct channel_person* next_person_in_channel;
struct channel_person* prev_channel_for_person;
struct channel_person* next_channel_for_person;
struct channel* channel;
struct person* person;
bool is_operator;
bool is_voiced;
};
struct channel
{
struct channel* prev_channel;
struct channel* next_channel;
char* name;
char* topic;
struct channel_person* people;
};
struct channel* find_channel(const struct network* state, const char* channel_name);
struct channel* add_channel(struct network* state, const char* channel_name);
struct channel* get_channel(struct network* state, const char* channel_name);
void remove_channel(struct network* state, struct channel* channel);
struct person* find_person(const struct network* state, const char* nick);
struct person* add_person(struct network* state, const char* nick);
struct person* get_person(struct network* state, const char* nick);
void remove_person(struct network* state, struct person* person);
struct channel_person* find_person_in_channel(const struct network* state, const char* nick, const char* channel_name);
struct channel_person* add_person_to_channel(struct network* state, struct person* person, struct channel* channel);
struct channel_person* get_person_in_channel(struct network* state, struct person* person, struct channel* channel);
void remove_person_from_channel(struct network* state, struct channel_person* channel_person);
#endif

1007
irc/irc.c Normal file

File diff suppressed because it is too large Load Diff

24
irc/network.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef NETWORK_H
#define NETWORK_H
struct account;
struct channel;
struct channel_person;
struct person;
struct network
{
struct irc_connection* irc_connection;
struct account* accounts;
struct channel* channels;
struct person* people;
struct channel_person* channel_people;
struct scrollback* scrollbacks;
char* nick;
char* real_name;
char* password;
char* server_hostname;
const char* autojoin;
};
#endif

205
irc/scrollback.c Normal file
View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* scrollback.c
* Ordered messages for display.
*/
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "compat.h"
#include "network.h"
#include "scrollback.h"
#include "string.h"
void message_free(struct message* msg)
{
free(msg->who);
free(msg->what);
}
void scrollback_free(struct scrollback* sb)
{
if ( sb->network )
{
if ( sb->scrollback_prev )
sb->scrollback_prev->scrollback_next = sb->scrollback_next;
else
sb->network->scrollbacks = sb->scrollback_next;
if ( sb->scrollback_next )
sb->scrollback_next->scrollback_prev = sb->scrollback_prev;
sb->scrollback_prev = NULL;
sb->scrollback_next = NULL;
sb->network = NULL;
}
for ( size_t i = 0; i < sb->messages_count; i++ )
message_free(&sb->messages[i]);
free(sb->messages);
free(sb->name);
free(sb);
}
struct scrollback* find_scrollback_network(const struct network* network)
{
// TODO: The server hostname can be a valid nick, for instance if the
// hostname doesn't contain any dot characters.
for ( struct scrollback* sb = network->scrollbacks;
sb;
sb = sb->scrollback_next )
{
if ( sb->name[0] == '#' )
continue;
if ( !strnickcmp(network->server_hostname, sb->name) )
return sb;
}
return NULL;
}
struct scrollback* find_scrollback(const struct network* network,
const char* name)
{
assert(name);
for ( struct scrollback* sb = network->scrollbacks;
sb;
sb = sb->scrollback_next )
{
if ( name[0] == '#' && sb->name[0] == '#' )
{
if ( strchannelcmp(name + 1, sb->name + 1) == 0 )
return sb;
}
else if ( name[0] != '#' && sb->name[0] != '#' )
{
if ( strnickcmp(name + 1, sb->name + 1) == 0 )
return sb;
}
}
return NULL;
}
struct scrollback* add_scrollback(struct network* network, const char* name)
{
struct scrollback* sb =
(struct scrollback*) calloc(1, sizeof(struct scrollback));
if ( !sb )
return NULL;
if ( !(sb->name = strdup(name)) )
return scrollback_free(sb), (struct scrollback*) NULL;
sb->network = network;
sb->scrollback_next = sb->network->scrollbacks;
if ( sb->scrollback_next )
sb->scrollback_next->scrollback_prev = sb;
sb->network->scrollbacks = sb;
return sb;
}
struct scrollback* get_scrollback(struct network* network, const char* name)
{
struct scrollback* result = find_scrollback(network, name);
if ( result )
return result;
return add_scrollback(network, name);
}
bool scrollback_add_message(struct scrollback* sb,
enum activity activity,
const struct message* msg)
{
if ( sb->messages_count == sb->messages_allocated )
{
size_t new_allocated = 2 * sb->messages_allocated;
if ( new_allocated == 0 )
new_allocated = 64;
struct message* new_messages = (struct message*)
reallocarray(sb->messages, new_allocated, sizeof(struct message));
if ( !new_messages )
return false;
sb->messages = new_messages;
sb->messages_allocated = new_allocated;
}
sb->messages[sb->messages_count++] = *msg;
size_t who_width = strlen(msg->who); // TODO: Unicode?
if ( sb->who_width < who_width )
sb->who_width = who_width;
if ( sb->activity < activity )
sb->activity = activity;
return true;
}
static void message_timestamp(struct message* msg)
{
struct tm tm;
time_t now = time(NULL);
localtime_r(&now, &tm);
msg->sec = tm.tm_sec;
msg->min = tm.tm_min;
msg->hour = tm.tm_hour;
}
bool scrollback_print(struct scrollback* sb,
enum activity activity,
const char* who,
const char* what)
{
struct message msg;
memset(&msg, 0, sizeof(msg));
message_timestamp(&msg);
if ( (msg.who = strdup(who)) &&
(msg.what = strdup(what)) &&
scrollback_add_message(sb, activity, &msg) )
return true;
message_free(&msg);
return false;
}
bool scrollback_printf(struct scrollback* sb,
enum activity activity,
const char* who,
const char* whatf,
...)
{
struct message msg;
memset(&msg, 0, sizeof(msg));
message_timestamp(&msg);
va_list ap;
va_start(ap, whatf);
int len = vasprintf(&msg.what, whatf, ap);
va_end(ap);
if ( (msg.who = strdup(who)) &&
0 <= len &&
scrollback_add_message(sb, activity, &msg) )
return true;
message_free(&msg);
return false;
}
void scrollback_clear(struct scrollback* sb)
{
for ( size_t i = 0; i < sb->messages_count; i++ )
{
free(sb->messages[i].who);
free(sb->messages[i].what);
}
sb->messages_count = 0;
sb->messages_allocated = 0;
free(sb->messages);
sb->messages = NULL;
}

82
irc/scrollback.h Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* scrollback.c
* Ordered messages for display.
*/
#ifndef SCROLLBACK_H
#define SCROLLBACK_H
#include <stdbool.h>
#include <stddef.h>
struct network;
enum activity
{
ACTIVITY_NONE,
ACTIVITY_NONTALK,
ACTIVITY_TALK,
ACTIVITY_HIGHLIGHT,
};
struct message
{
int hour;
int min;
int sec;
char* who;
char* what;
};
struct scrollback
{
struct network* network;
struct scrollback* scrollback_prev;
struct scrollback* scrollback_next;
char* name;
struct message* messages;
size_t messages_count;
size_t messages_allocated;
size_t who_width;
enum activity activity;
};
void message_free(struct message* msg);
void scrollback_free(struct scrollback* sb);
struct scrollback* find_scrollback_network(const struct network* network);
struct scrollback* find_scrollback(const struct network* network,
const char* name);
struct scrollback* add_scrollback(struct network* network,
const char* name);
struct scrollback* get_scrollback(struct network* network,
const char* name);
bool scrollback_add_message(struct scrollback* sb,
enum activity activity,
const struct message* msg);
bool scrollback_print(struct scrollback* sb,
enum activity activity,
const char* who,
const char* what);
__attribute__((format(printf, 4, 5)))
bool scrollback_printf(struct scrollback* sb,
enum activity activity,
const char* who,
const char* whatf,
...);
void scrollback_clear(struct scrollback* sb);
#endif

34
irc/string.c Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* string.c
* String utility functions and compatibility.
*/
#include <string.h>
#include "string.h"
// TODO: Implement this properly in accordance with IRC RFC rules.
int strchannelcmp(const char* a, const char* b)
{
return strcasecmp(a, b);
}
// TODO: Implement this properly in accordance with IRC RFC rules.
int strnickcmp(const char* a, const char* b)
{
return strcasecmp(a, b);
}

28
irc/string.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* string.h
* String utility functions and compatibility.
*/
#ifndef STRING_H
#define STRING_H
#include <stddef.h>
int strchannelcmp(const char* a, const char* b);
int strnickcmp(const char* a, const char* b);
#endif

545
irc/ui.c Normal file
View File

@ -0,0 +1,545 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ui.c
* User Interface.
*/
#include <sys/ioctl.h>
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <wchar.h>
#include "connection.h"
#include "network.h"
#include "scrollback.h"
#include "ui.h"
struct cell
{
wchar_t c;
int fgcolor;
int bgcolor;
};
static struct termios saved_termios;
void tty_show(struct cell* cells, size_t cols, size_t rows)
{
printf("\e[H");
int fgcolor = -1;
int bgcolor = -1;
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
for ( size_t r = 0; r < rows; r++ )
{
for ( size_t c = 0; c < cols; c++ )
{
struct cell* cell = &cells[r * cols + c];
if ( fgcolor != cell->fgcolor )
{
printf("\e[%im", cell->fgcolor);
fgcolor = cell->fgcolor;
}
if ( bgcolor != cell->bgcolor )
{
printf("\e[%im", cell->bgcolor);
bgcolor = cell->bgcolor;
}
char mb[MB_CUR_MAX];
size_t amount = wcrtomb(mb, cell->c, &ps);
if ( amount == (size_t) -1 )
continue;
fwrite(mb, 1, amount, stdout);
}
if ( r + 1 != rows )
printf("\n");
}
fflush(stdout);
}
void on_sigquit(int sig)
{
// TODO: This is not async signal safe.
ui_destroy(NULL);
// TODO: Use sigaction so the handler only runs once.
//raise(sig);
(void) sig;
raise(SIGKILL);
}
void ui_initialize(struct ui* ui, struct network* network)
{
memset(ui, 0, sizeof(*ui));
ui->network = network;
ui->current = find_scrollback_network(network);
struct winsize ws;
if ( ioctl(1, TIOCGWINSZ, &ws) < 0 )
err(1, "stdout: ioctl: TIOCGWINSZ");
if ( tcgetattr(0, &saved_termios) < 0 )
err(1, "stdin: tcgetattr");
struct termios tcattr;
memcpy(&tcattr, &saved_termios, sizeof(struct termios));
tcattr.c_lflag &= ~(ECHO | ICANON | IEXTEN);
tcattr.c_iflag |= ICRNL | ISIG;
tcattr.c_cc[VMIN] = 1;
tcattr.c_cc[VTIME] = 0;
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, on_sigquit);
tcsetattr(0, TCSADRAIN, &tcattr);
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049h");
fflush(stdout);
}
}
void ui_destroy(struct ui* ui)
{
// TODO.
(void) ui;
// TODO: This should be done in an atexit handler as well.
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049l");
fflush(stdout);
}
tcsetattr(0, TCSADRAIN, &saved_termios);
}
void increment_offset(size_t* o_ptr, size_t* line_ptr, size_t cols)
{
if ( (*o_ptr)++ == cols )
{
*o_ptr = 0;
(*line_ptr)++;
}
}
void ui_render(struct ui* ui)
{
mbstate_t ps;
struct winsize ws;
if ( ioctl(1, TIOCGWINSZ, &ws) < 0 )
err(1, "stdout: ioctl: TIOCGWINSZ");
size_t cols = ws.ws_col;
size_t rows = ws.ws_row;
struct cell* cells = calloc(sizeof(struct cell) * cols, rows);
if ( !cells )
err(1, "calloc");
for ( size_t r = 0; r < rows; r++ )
{
for ( size_t c = 0; c < cols; c++ )
{
struct cell* cell = &cells[r * cols + c];
cell->c = L' ';
cell->fgcolor = 0;
cell->bgcolor = 0;
}
}
// TODO: What if the terminal isn't large enough?
struct scrollback* sb = ui->current;
sb->activity = ACTIVITY_NONE;
size_t title_from = 0;
size_t when_offset = 0;
size_t when_width = 2 + 1 + 2 + 1 + 2;
size_t who_offset = when_offset + when_width + 1;
size_t who_width = sb->who_width;
size_t div_offset = who_offset + who_width + 1;
size_t what_offset = div_offset + 2;
size_t what_width = cols - what_offset;
size_t input_width = cols;
size_t input_num_lines = 1;
for ( size_t i = 0, o = 0; i < ui->input_used; i++ )
{
wchar_t wc = ui->input[i];
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( input_width <= o )
{
input_num_lines++;
o = 0;
}
o += w;
}
char* title;
if ( asprintf(&title, "%s @ %s / %s", ui->network->nick,
ui->network->server_hostname, ui->current->name) < 0 )
err(1, "asprintf");
size_t title_len = strlen(title);
size_t title_how_many = cols < title_len ? cols : title_len;
size_t title_offset = (cols - title_how_many) / 2;
for ( size_t i = 0; i < title_how_many; i++ )
{
char c = title[i];
size_t cell_r = title_from;
size_t cell_c = title_offset + i;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = btowc((unsigned char) c);
}
free(title);
size_t scrollbacks_from = title_from + 1;
size_t scrollbacks_lines = 1;
size_t scrollbacks_o = 0;
for ( struct scrollback* iter = ui->network->scrollbacks;
iter;
iter = iter->scrollback_next )
{
if ( iter->scrollback_prev )
{
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
}
for ( size_t i = 0; iter->name[i]; i++ )
{
char c = iter->name[i];
size_t cell_r = scrollbacks_from + (scrollbacks_lines - 1);
size_t cell_c = scrollbacks_o;
struct cell* cell = &cells[cell_r * cols + cell_c];
int fgcolor = 0;
if ( iter == sb )
fgcolor = 1; // TODO: Boldness should be its own property.
else if ( iter->activity == ACTIVITY_NONTALK )
fgcolor = 31;
else if ( iter->activity == ACTIVITY_TALK )
fgcolor = 91;
else if ( iter->activity == ACTIVITY_HIGHLIGHT )
fgcolor = 94;
cell->c = btowc((unsigned char) c);
cell->fgcolor = fgcolor;
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
}
}
size_t horhigh_from = scrollbacks_from + scrollbacks_lines;
for ( size_t c = 0; c < cols; c++ )
{
size_t cell_r = horhigh_from;
size_t cell_c = c;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = c == div_offset ? L'' : L'';
}
size_t sb_from = horhigh_from + 1;
// TODO: What if the input is too big?
size_t input_bottom = rows - input_num_lines;
size_t input_offset = 0;
for ( size_t i = 0, o = 0, line = 0; i < ui->input_used; i++ )
{
wchar_t wc = ui->input[i];
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( input_width <= o )
{
line++;
o = 0;
}
// TODO: If 1 < w.
size_t cell_r = input_bottom + line;
size_t cell_c = input_offset + o;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = wc;
o += w;
}
size_t horlow_from = input_bottom - 1;
for ( size_t c = 0; c < cols; c++ )
{
size_t cell_r = horlow_from;
size_t cell_c = c;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = c == div_offset ? L'' : L'';
}
size_t sb_to = horlow_from;
for ( size_t r = sb_to - 1; r != sb_from - 1; r-- )
{
size_t cell_r = r;
size_t cell_c = div_offset;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = L'';
}
for ( size_t r = sb_to - 1, m = sb->messages_count - 1;
r != (sb_from - 1) && m != SIZE_MAX;
r--, m-- )
{
struct message* msg = &sb->messages[m];
size_t num_lines = 1;
size_t max_lines = sb_from - r + 1;
memset(&ps, 0, sizeof(ps));
for ( size_t i = 0, o = 0; msg->what[i]; )
{
wchar_t wc;
size_t amount = mbrtowc(&wc, msg->what + i, SIZE_MAX, &ps);
if ( amount == (size_t) -1 || amount == (size_t) -2 )
{
// TODO.
memset(&ps, 0, sizeof(ps));
continue;
}
i += amount;
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( what_width <= o )
{
num_lines++;
o = 0;
}
o += w;
}
size_t how_many_lines = max_lines < num_lines ? max_lines : num_lines;
size_t first_line = num_lines - how_many_lines;
if ( 1 < how_many_lines )
r -= how_many_lines - 1;
if ( first_line == 0 )
{
char when[2 + 1 + 2 + 1 + 2 + 1 + 1];
snprintf(when, sizeof(when), "%02i:%02i:%02i ",
msg->hour, msg->min, msg->sec);
for ( size_t i = 0; when[i]; i++ )
{
size_t cell_r = r;
size_t cell_c = when_offset + i;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = btowc((unsigned char) when[i]);
}
memset(&ps, 0, sizeof(ps));
size_t msg_who_width = strlen(msg->who);
size_t msg_who_how_many = who_width < msg_who_width ? who_width : msg_who_width;
size_t msg_who_first = msg_who_width - msg_who_how_many;
size_t msg_who_offset = who_width - msg_who_how_many;
for ( size_t i = 0; i < msg_who_how_many; i++ )
{
char c = msg->who[msg_who_first + i];
size_t cell_r = r;
size_t cell_c = who_offset + msg_who_offset + i;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = btowc((unsigned char) c);
}
}
for ( size_t i = 0, o = 0, line = 0; msg->what[i]; )
{
wchar_t wc;
size_t amount = mbrtowc(&wc, msg->what + i, SIZE_MAX, &ps);
if ( amount == (size_t) -1 || amount == (size_t) -2 )
{
// TODO.
memset(&ps, 0, sizeof(ps));
continue;
}
i += amount;
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( what_width <= o )
{
line++;
o = 0;
}
// TODO: If 1 < w.
if ( first_line <= line )
{
size_t cell_r = r + line - first_line;
size_t cell_c = what_offset + o;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = wc;
}
o += w;
}
}
(void) ui;
tty_show(cells, cols, rows);
free(cells);
}
static bool is_command(const char* input,
const char* cmd,
const char** param)
{
size_t cmdlen = strlen(cmd);
if ( strncmp(input, cmd, cmdlen) != 0 )
return false;
if ( !input[cmdlen] )
{
if ( param )
*param = NULL;
return true;
}
if ( input[cmdlen] != ' ' )
return false;
if ( !param )
return false;
*param = input + cmdlen + 1;
return true;
}
static bool is_command_param(const char* input,
const char* cmd,
const char** param)
{
if ( !is_command(input, cmd, param) )
return false;
if ( !*param )
return false; // TODO: Help message in scrollback.
return true;
}
void ui_input_char(struct ui* ui, char c)
{
wchar_t wc;
size_t amount = mbrtowc(&wc, &c, 1, &ui->input_ps);
if ( amount == (size_t) -2 )
return;
if ( amount == (size_t) -1 )
{
// TODO.
memset(&ui->input_ps, 0, sizeof(ui->input_ps));
return;
}
if ( wc == L'\b' || wc == 127 )
{
if ( 0 < ui->input_used )
ui->input_used--;
}
else if ( wc == L'\f' /* ^L */ )
{
scrollback_clear(ui->current);
// TODO: Schedule full redraw?
}
else if ( wc == L'\n' )
{
char input[4 * sizeof(ui->input) / sizeof(ui->input[0])];
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
const wchar_t* wcs = ui->input;
size_t amount = wcsnrtombs(input, &wcs, ui->input_used, sizeof(input), &ps);
ui->input_used = 0;
if ( amount == (size_t) -1 )
return;
input[amount < sizeof(input) ? amount : amount - 1] = '\0';
struct irc_connection* conn = ui->network->irc_connection;
const char* who = ui->network->nick;
const char* where = ui->current->name;
const char* param;
if ( input[0] == '/' && input[1] != '/' )
{
if ( !input[1] )
return;
if ( is_command_param(input, "/w", &param) ||
is_command_param(input, "/window", &param) )
{
struct scrollback* sb = find_scrollback(ui->network, param);
if ( sb )
ui->current = sb;
}
else if ( is_command_param(input, "/query", &param) )
{
if ( param[0] == '#' )
return; // TODO: Help in scrollback.
struct scrollback* sb = get_scrollback(ui->network, param);
if ( sb )
ui->current = sb;
}
else if ( is_command_param(input, "/join", &param) )
{
irc_command_join(conn, param);
struct scrollback* sb = get_scrollback(ui->network, param);
if ( sb )
ui->current = sb;
}
// TODO: Make it default to the current channel if any.
else if ( is_command_param(input, "/part", &param) )
{
irc_command_part(conn, param);
}
else if ( is_command(input, "/quit", &param) )
{
irc_command_quit(conn, param ? param : "Quiting");
}
else if ( is_command_param(input, "/nick", &param) )
{
irc_command_nick(conn, param);
}
else if ( is_command_param(input, "/raw", &param) )
{
irc_transmit_string(conn, param);
}
else if ( is_command_param(input, "/me", &param) )
{
scrollback_printf(ui->current, ACTIVITY_NONE, "*", "%s %s",
who, param);
irc_command_privmsgf(conn, where, "\x01""ACTION %s""\x01",
param);
}
else if ( is_command(input, "/clear", &param) )
{
scrollback_clear(ui->current);
}
// TODO: /ban
// TODO: /ctcp
// TODO: /deop
// TODO: /devoice
// TODO: /kick
// TODO: /mode
// TODO: /op
// TODO: /quiet
// TODO: /topic
// TODO: /voice
else
{
scrollback_printf(ui->current, ACTIVITY_NONE, "*",
"%s :Unknown command", input + 1);
}
}
else
{
const char* what = input;
if ( what[0] == '/' )
what++;
scrollback_print(ui->current, ACTIVITY_NONE, who, what);
irc_command_privmsg(conn, where, what);
}
}
else
{
if ( ui->input_used < sizeof(ui->input) / sizeof(ui->input[0]) )
ui->input[ui->input_used++] = wc;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -13,30 +13,30 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* rules.h
* Determines whether a given path is included in the filesystem.
* ui.h
* User Interface.
*/
#ifndef RULES_H
#define RULES_H
#ifndef UI_H
#define UI_H
enum InclusionRuleType
#include <wchar.h>
struct network;
struct scrollback;
struct ui
{
RULE_INCLUDE,
RULE_EXCLUDE,
struct network* network;
struct scrollback* current;
wchar_t input[512];
size_t input_used;
mbstate_t input_ps;
};
struct InclusionRule
{
char* pattern;
enum InclusionRuleType rule;
};
bool IncludesPath(const char* path);
bool AddRule(struct InclusionRule* rule);
bool AddRulesFromFile(FILE* fp, const char* fpname);
bool AddManifestFromFile(FILE* fp, const char* fpname);
bool AddManifestPath(const char* path);
bool ChangeRulesAmount(size_t newnum);
void ui_initialize(struct ui* ui, struct network* network);
void ui_render(struct ui* ui);
void ui_input_char(struct ui* ui, char c);
void ui_destroy(struct ui* ui);
#endif

2
iso9660/.gitignore vendored Normal file
View File

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

33
iso9660/Makefile Normal file
View File

@ -0,0 +1,33 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CXXFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -fcheck-new
LIBS:=$(LIBS)
ifeq ($(HOST_IS_SORTIX),0)
LIBS:=$(LIBS) -lfuse
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
endif
BINARIES:=iso9660fs
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
iso9660fs: *.cpp *.h
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
clean:
rm -f $(BINARIES) *.o

108
iso9660/block.cpp Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* block.cpp
* Blocks in the filesystem.
*/
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Block::Block()
{
this->block_data = NULL;
}
Block::Block(Device* device, uint32_t block_id)
{
Construct(device, block_id);
}
void Block::Construct(Device* device, uint32_t block_id)
{
this->prev_block = NULL;
this->next_block = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->device = device;
this->reference_count = 1;
this->block_id = block_id;
}
Block::~Block()
{
Destruct();
delete[] block_data;
}
void Block::Destruct()
{
Unlink();
}
void Block::Refer()
{
reference_count++;
}
void Block::Unref()
{
if ( !--reference_count )
{
#if 0
device->block_count--;
delete this;
#endif
}
}
void Block::Use()
{
Unlink();
Prelink();
}
void Block::Unlink()
{
(prev_block ? prev_block->next_block : device->mru_block) = next_block;
(next_block ? next_block->prev_block : device->lru_block) = prev_block;
size_t bin = block_id % DEVICE_HASH_LENGTH;
(prev_hashed ? prev_hashed->next_hashed : device->hash_blocks[bin]) = next_hashed;
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
}
void Block::Prelink()
{
prev_block = NULL;
next_block = device->mru_block;
if ( device->mru_block )
device->mru_block->prev_block = this;
device->mru_block = this;
if ( !device->lru_block )
device->lru_block = this;
size_t bin = block_id % DEVICE_HASH_LENGTH;
prev_hashed = NULL;
next_hashed = device->hash_blocks[bin];
device->hash_blocks[bin] = this;
if ( next_hashed )
next_hashed->prev_hashed = this;
}

53
iso9660/block.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* block.h
* Blocks in the filesystem.
*/
#ifndef BLOCK_H
#define BLOCK_H
class Device;
class Block
{
public:
Block();
Block(Device* device, uint32_t block_id);
~Block();
void Construct(Device* device, uint32_t block_id);
void Destruct();
public:
Block* prev_block;
Block* next_block;
Block* prev_hashed;
Block* next_hashed;
Device* device;
size_t reference_count;
uint32_t block_id;
uint8_t* block_data;
public:
void Refer();
void Unref();
void Use();
void Unlink();
void Prelink();
};
#endif

107
iso9660/device.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* device.cpp
* Block device.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Device::Device(int fd, const char* path, uint32_t block_size)
{
this->mru_block = NULL;
this->lru_block = NULL;
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
hash_blocks[i] = NULL;
struct stat st;
fstat(fd, &st);
this->device_size = st.st_size;
this->path = path;
this->block_size = block_size;
this->fd = fd;
this->block_count = 0;
#ifdef __sortix__
// TODO: This isn't scaleable if there's multiple filesystems mounted.
size_t memory;
memstat(NULL, &memory);
this->block_limit = (memory / 10) / block_size;
#else
this->block_limit = 32768;
#endif
}
Device::~Device()
{
while ( mru_block )
delete mru_block;
close(fd);
}
Block* Device::AllocateBlock()
{
if ( block_limit <= block_count )
{
for ( Block* block = lru_block; block; block = block->prev_block )
{
if ( block->reference_count )
continue;
block->Destruct(); // Syncs.
return block;
}
}
uint8_t* data = new uint8_t[block_size];
if ( !data ) // TODO: Use operator new nothrow!
return NULL;
Block* block = new Block();
if ( !block ) // TODO: Use operator new nothrow!
return delete[] data, (Block*) NULL;
block->block_data = data;
block_count++;
return block;
}
Block* Device::GetBlock(uint32_t block_id)
{
if ( Block* block = GetCachedBlock(block_id) )
return block;
Block* block = AllocateBlock();
if ( !block )
return NULL;
block->Construct(this, block_id);
off_t file_offset = (off_t) block_size * (off_t) block_id;
preadall(fd, block->block_data, block_size, file_offset);
block->Prelink();
return block;
}
Block* Device::GetCachedBlock(uint32_t block_id)
{
size_t bin = block_id % DEVICE_HASH_LENGTH;
for ( Block* iter = hash_blocks[bin]; iter; iter = iter->next_hashed )
if ( iter->block_id == block_id )
return iter->Refer(), iter;
return NULL;
}

51
iso9660/device.h Normal file
View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* device.h
* Block device.
*/
#ifndef DEVICE_H
#define DEVICE_H
class Block;
static const size_t DEVICE_HASH_LENGTH = 1 << 16;
class Device
{
public:
Device(int fd, const char* path, uint32_t block_size);
~Device();
public:
Block* mru_block;
Block* lru_block;
Block* hash_blocks[DEVICE_HASH_LENGTH];
off_t device_size;
const char* path;
uint32_t block_size;
int fd;
size_t block_count;
size_t block_limit;
public:
Block* AllocateBlock();
Block* GetBlock(uint32_t block_id);
Block* GetCachedBlock(uint32_t block_id);
};
#endif

87
iso9660/filesystem.cpp Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* filesystem.cpp
* ISO 9660 filesystem implementation.
*/
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <stdio.h> // DEBUG
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "util.h"
Filesystem::Filesystem(Device* device, const char* mount_path,
uint64_t pvd_offset)
{
// TODO: This should be replaced by the . entry within the root directory
// so the Rock Ridge extensions are available.
uint32_t pvd_block_id = pvd_offset / device->block_size;
this->pvd_block = device->GetBlock(pvd_block_id);
assert(pvd_block); // TODO: This can fail.
this->pvd = (struct iso9660_pvd*)
(pvd_block->block_data + pvd_offset % device->block_size);
this->root_ino = pvd_offset + offsetof(struct iso9660_pvd, root_dirent);
this->device = device;
this->mount_path = mount_path;
this->block_size = device->block_size;
this->mru_inode = NULL;
this->lru_inode = NULL;
for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ )
this->hash_inodes[i] = NULL;
}
Filesystem::~Filesystem()
{
while ( mru_inode )
delete mru_inode;
pvd_block->Unref();
}
Inode* Filesystem::GetInode(iso9660_ino_t inode_id)
{
size_t bin = inode_id % INODE_HASH_LENGTH;
for ( Inode* iter = hash_inodes[bin]; iter; iter = iter->next_hashed )
if ( iter->inode_id == inode_id )
return iter->Refer(), iter;
uint32_t block_id = inode_id / block_size;
uint32_t offset = inode_id % block_size;
Block* block = device->GetBlock(block_id);
if ( !block )
return (Inode*) NULL;
Inode* inode = new Inode(this, inode_id);
if ( !inode )
return block->Unref(), (Inode*) NULL;
inode->data_block = block;
uint8_t* buf = inode->data_block->block_data + offset;
inode->data = (struct iso9660_dirent*) buf;
inode->Prelink();
inode->Parse();
return inode;
}

52
iso9660/filesystem.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* filesystem.h
* ISO 9660 filesystem implementation.
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include "iso9660.h"
class Device;
class Inode;
static const size_t INODE_HASH_LENGTH = 1 << 16;
class Filesystem
{
public:
Filesystem(Device* device, const char* mount_path, uint64_t pvd_offset);
~Filesystem();
public:
Block* pvd_block;
struct iso9660_pvd* pvd;
Device* device;
const char* mount_path;
iso9660_ino_t root_ino;
uint32_t block_size;
Inode* mru_inode;
Inode* lru_inode;
Inode* hash_inodes[INODE_HASH_LENGTH];
public:
Inode* GetInode(iso9660_ino_t inode_id);
};
#endif

701
iso9660/fsmarshall.cpp Normal file
View File

@ -0,0 +1,701 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fsmarshall.cpp
* Sortix fsmarshall frontend.
*/
#if defined(__sortix__)
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <ioleast.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#include <sortix/dirent.h>
#include <fsmarshall.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "fsmarshall.h"
#include "fuse.h"
#include "inode.h"
#include "iso9660fs.h"
bool RespondData(int chl, const void* ptr, size_t count)
{
return writeall(chl, ptr, count) == count;
}
bool RespondHeader(int chl, size_t type, size_t size)
{
struct fsm_msg_header hdr;
hdr.msgtype = type;
hdr.msgsize = size;
return RespondData(chl, &hdr, sizeof(hdr));
}
bool RespondMessage(int chl, size_t type, const void* ptr, size_t count)
{
return RespondHeader(chl, type, count) &&
RespondData(chl, ptr, count);
}
bool RespondError(int chl, int errnum)
{
struct fsm_resp_error body;
body.errnum = errnum;
return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body));
}
bool RespondSuccess(int chl)
{
struct fsm_resp_success body;
return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body));
}
bool RespondStat(int chl, struct stat* st)
{
struct fsm_resp_stat body;
body.st = *st;
return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body));
}
bool RespondStatVFS(int chl, struct statvfs* stvfs)
{
struct fsm_resp_statvfs body;
body.stvfs = *stvfs;
return RespondMessage(chl, FSM_RESP_STATVFS, &body, sizeof(body));
}
bool RespondSeek(int chl, off_t offset)
{
struct fsm_resp_lseek body;
body.offset = offset;
return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body));
}
bool RespondRead(int chl, const uint8_t* buf, size_t count)
{
struct fsm_resp_read body;
body.count = count;
return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) &&
RespondData(chl, buf, count);
}
bool RespondReadlink(int chl, const uint8_t* buf, size_t count)
{
struct fsm_resp_readlink body;
body.targetlen = count;
return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) &&
RespondData(chl, buf, count);
}
bool RespondWrite(int chl, size_t count)
{
struct fsm_resp_write body;
body.count = count;
return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body));
}
bool RespondOpen(int chl, ino_t ino, mode_t type)
{
struct fsm_resp_open body;
body.ino = ino;
body.type = type;
return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body));
}
bool RespondMakeDir(int chl, ino_t ino)
{
struct fsm_resp_mkdir body;
body.ino = ino;
return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body));
}
bool RespondReadDir(int chl, struct dirent* dirent)
{
struct fsm_resp_readdirents body;
body.ino = dirent->d_ino;
body.type = dirent->d_type;
body.namelen = dirent->d_namlen;
return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) &&
RespondData(chl, dirent->d_name, dirent->d_namlen);
}
bool RespondTCGetBlob(int chl, const void* data, size_t data_size)
{
struct fsm_resp_tcgetblob body;
body.count = data_size;
return RespondMessage(chl, FSM_RESP_TCGETBLOB, &body, sizeof(body)) &&
RespondData(chl, data, data_size);
}
Inode* SafeGetInode(Filesystem* fs, ino_t ino)
{
if ( (iso9660_ino_t) ino != ino )
return errno = EBADF, (Inode*) ino;
return fs->GetInode((iso9660_ino_t) ino);
}
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
{
(void) chl;
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
{
inode->RemoteRefer();
inode->Unref();
}
}
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
{
(void) chl;
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
{
inode->RemoteUnref();
inode->Unref();
}
}
void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondSuccess(chl);
}
void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
struct stat st;
StatInode(inode, &st);
inode->Unref();
RespondStat(chl, &st);
}
void HandleChangeMode(int chl, struct fsm_req_chmod* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleChangeOwner(int chl, struct fsm_req_chown* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleUTimens(int chl, struct fsm_req_utimens* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleTruncate(int chl, struct fsm_req_truncate* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( msg->whence == SEEK_SET )
RespondSeek(chl, msg->offset);
else if ( msg->whence == SEEK_END )
{
off_t inode_size = inode->Size();
if ( (msg->offset < 0 && inode_size + msg->offset < 0) ||
(0 <= msg->offset && OFF_MAX - inode_size < msg->offset) )
RespondError(chl, EOVERFLOW);
else
RespondSeek(chl, msg->offset + inode_size);
}
else
RespondError(chl, EINVAL);
inode->Unref();
}
void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
uint8_t* buf = (uint8_t*) malloc(msg->count);
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset);
inode->Unref();
if ( amount < 0 ) { free(buf); RespondError(chl, errno); return; }
RespondRead(chl, buf, amount);
free(buf);
}
void HandleWriteAt(int chl, struct fsm_req_pwrite* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
Inode* result = inode->Open(path, msg->flags, FsModeFromHostMode(msg->mode));
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
result->Unref();
}
void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondError(chl, EROFS);
}
void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !S_ISDIR(inode->Mode()) )
{
inode->Unref();
RespondError(chl, ENOTDIR);
return;
}
union
{
struct dirent kernel_entry;
uint8_t padding[sizeof(struct dirent) + 256];
};
memset(&kernel_entry, 0, sizeof(kernel_entry));
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( inode->ReadDirectory(&offset, &block, &block_id,
msg->rec_num ? NULL : name, &file_type,
&inode_id) )
{
if ( !(msg->rec_num--) )
{
size_t name_len = strlen(name);
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
kernel_entry.d_ino = inode_id;
kernel_entry.d_dev = 0;
kernel_entry.d_type = HostDTFromFsDT(file_type);
kernel_entry.d_namlen = name_len;
memcpy(kernel_entry.d_name, name, name_len);
size_t dname_offset = offsetof(struct dirent, d_name);
padding[dname_offset + kernel_entry.d_namlen] = '\0';
block->Unref();
inode->Unref();
RespondReadDir(chl, &kernel_entry);
return;
}
}
int errnum = errno;
if ( block )
block->Unref();
inode->Unref();
if ( errnum )
{
RespondError(chl, errnum);
return;
}
kernel_entry.d_reclen = sizeof(kernel_entry);
RespondReadDir(chl, &kernel_entry);
}
void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
RespondError(chl, ENOTTY);
inode->Unref();
}
void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
bool result = inode->Unlink(path, false);
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
bool result = inode->RemoveDirectory(path);
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
Inode* dest = SafeGetInode(fs, msg->linkino);
if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
bool result = inode->Link(path, dest);
free(path);
dest->Unref();
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondError(chl, EROFS);
}
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !ISO9660_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; }
size_t count = inode->Size();
uint8_t* buf = (uint8_t*) malloc(count);
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
ssize_t amount = inode->ReadLink(buf, count);
inode->Unref();
if ( amount < 0 ) { RespondError(chl, errno); return; }
RespondReadlink(chl, buf, amount);
free(buf);
}
void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs)
{
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1);
if ( !path ) { RespondError(chl, errno); return; }
memcpy(path, pathraw, msg->oldnamelen);
path[msg->oldnamelen] = '\0';
memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen);
path[msg->oldnamelen + 1 + msg->newnamelen] = '\0';
const char* oldname = path;
const char* newname = path + msg->oldnamelen + 1;
Inode* olddir = SafeGetInode(fs, msg->olddirino);
if ( !olddir ) { free(path); RespondError(chl, errno); return; }
Inode* newdir = SafeGetInode(fs, msg->newdirino);
if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; }
bool result = newdir->Rename(olddir, oldname, newname);
newdir->Unref();
olddir->Unref();
free(path);
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs)
{
(void) msg;
struct statvfs stvfs;
stvfs.f_bsize = fs->block_size;
stvfs.f_frsize = fs->block_size;
stvfs.f_blocks = fs->device->device_size / fs->block_size;
stvfs.f_bfree = 0;
stvfs.f_bavail = 0;
stvfs.f_files = 0;
stvfs.f_ffree = 0;
stvfs.f_favail = 0;
stvfs.f_ffree = 0;
stvfs.f_fsid = 0;
stvfs.f_flag = ST_RDONLY;
stvfs.f_namemax = 255;
RespondStatVFS(chl, &stvfs);
}
void HandleTCGetBlob(int chl, struct fsm_req_tcgetblob* msg, Filesystem* fs)
{
char* nameraw = (char*) &(msg[1]);
char* name = (char*) malloc(msg->namelen + 1);
if ( !name )
return (void) RespondError(chl, errno);
memcpy(name, nameraw, msg->namelen);
name[msg->namelen] = '\0';
//static const char index[] = "device-path\0filesystem-type\0filesystem-uuid\0mount-path\0";
static const char index[] = "device-path\0filesystem-type\0mount-path\0";
if ( !strcmp(name, "") )
RespondTCGetBlob(chl, index, sizeof(index) - 1);
else if ( !strcmp(name, "device-path") )
RespondTCGetBlob(chl, fs->device->path, strlen(fs->device->path));
else if ( !strcmp(name, "filesystem-type") )
RespondTCGetBlob(chl, "iso9660", strlen("iso9660"));
// TODO: Some kind of unique id.
//else if ( !strcmp(name, "filesystem-uuid") )
// RespondTCGetBlob(chl, fs->sb->s_uuid, sizeof(fs->sb->s_uuid));
else if ( !strcmp(name, "mount-path") )
RespondTCGetBlob(chl, fs->mount_path, strlen(fs->mount_path));
else
RespondError(chl, ENOENT);
free(name);
}
void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
{
request_uid = hdr->uid;
request_gid = hdr->gid;
if ( (uint16_t) request_uid != request_uid ||
(uint16_t) request_gid != request_gid )
{
fprintf(stderr, "extfs: id exceeded 16-bit: uid=%ju gid=%ju\n",
(uintmax_t) request_uid, (uintmax_t) request_gid);
RespondError(chl, EOVERFLOW);
return;
}
typedef void (*handler_t)(int, void*, Filesystem*);
handler_t handlers[FSM_MSG_NUM] = { NULL };
handlers[FSM_REQ_SYNC] = (handler_t) HandleSync;
handlers[FSM_REQ_STAT] = (handler_t) HandleStat;
handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode;
handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner;
handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate;
handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek;
handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt;
handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen;
handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir;
handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt;
handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY;
handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens;
handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir;
handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir;
handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink;
handlers[FSM_REQ_LINK] = (handler_t) HandleLink;
handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink;
handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink;
handlers[FSM_REQ_RENAME] = (handler_t) HandleRename;
handlers[FSM_REQ_REFER] = (handler_t) HandleRefer;
handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref;
handlers[FSM_REQ_STATVFS] = (handler_t) HandleStatVFS;
handlers[FSM_REQ_TCGETBLOB] = (handler_t) HandleTCGetBlob;
if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] )
{
fprintf(stderr, "extfs: message type %zu not supported\n", hdr->msgtype);
RespondError(chl, ENOTSUP);
return;
}
uint8_t body_buffer[65536];
uint8_t* body = body_buffer;
if ( sizeof(body_buffer) < hdr->msgsize )
{
body = (uint8_t*) mmap(NULL, hdr->msgsize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if ( (void*) body == MAP_FAILED )
{
RespondError(chl, errno);
return;
}
}
if ( readall(chl, body, hdr->msgsize) == hdr->msgsize )
handlers[hdr->msgtype](chl, body, fs);
else
RespondError(chl, errno);
if ( sizeof(body_buffer) < hdr->msgsize )
munmap(body, hdr->msgsize);
}
static volatile bool should_terminate = false;
void TerminationHandler(int)
{
should_terminate = true;
}
static void ready(void)
{
const char* readyfd_env = getenv("READYFD");
if ( !readyfd_env )
return;
int readyfd = atoi(readyfd_env);
char c = '\n';
write(readyfd, &c, 1);
close(readyfd);
unsetenv("READYFD");
}
int fsmarshall_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev)
{
(void) argv0;
// Stat the root inode.
struct stat root_inode_st;
Inode* root_inode = fs->GetInode(fs->root_ino);
if ( !root_inode )
err(1, "GetInode");
StatInode(root_inode, &root_inode_st);
root_inode->Unref();
// Create a filesystem server connected to the kernel that we'll listen on.
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_inode_st, 0);
if ( serverfd < 0 )
err(1, "%s", mount_path);
// Make sure the server isn't unexpectedly killed and data is lost.
signal(SIGINT, TerminationHandler);
signal(SIGTERM, TerminationHandler);
signal(SIGQUIT, TerminationHandler);
// Become a background process in its own process group by default.
if ( !foreground )
{
pid_t child_pid = fork();
if ( child_pid < 0 )
err(1, "fork");
if ( child_pid )
exit(0);
setpgid(0, 0);
}
else
ready();
// Listen for filesystem messages.
int channel;
while ( 0 <= (channel = accept(serverfd, NULL, NULL)) )
{
if ( should_terminate )
break;
struct fsm_msg_header hdr;
size_t amount;
if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) )
{
//warn("incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
errno = 0;
continue;
}
HandleIncomingMessage(channel, &hdr, fs);
close(channel);
}
// Garbage collect all open inode references.
while ( fs->mru_inode )
{
Inode* inode = fs->mru_inode;
if ( inode->remote_reference_count )
inode->RemoteUnref();
else if ( inode->reference_count )
inode->Unref();
}
close(serverfd);
delete fs;
delete dev;
return 0;
}
#endif

31
iso9660/fsmarshall.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fsmarshall.h
* Sortix fsmarshall frontend.
*/
#ifndef FSMARSHALL_H
#define FSMARSHALL_H
class Device;
class Filesystem;
int fsmarshall_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev);
#endif

572
iso9660/fuse.cpp Normal file
View File

@ -0,0 +1,572 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fuse.cpp
* FUSE frontend.
*/
#if !defined(__sortix__)
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define FUSE_USE_VERSION 26
#include <fuse.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "fuse.h"
#include "inode.h"
#include "iso9660fs.h"
#include <stdio.h> // DEBUG
struct iso9660_fuse_ctx
{
Device* dev;
Filesystem* fs;
};
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
#define FUSE_FS (((struct iso9660_fuse_ctx*) (fuse_get_context()->private_data))->fs)
void* iso9660_fuse_init(struct fuse_conn_info* /*conn*/)
{
return fuse_get_context()->private_data;
}
void iso9660_fuse_destroy(void* fs_private)
{
struct iso9660_fuse_ctx* iso9660_fuse_ctx =
(struct iso9660_fuse_ctx*) fs_private;
while ( iso9660_fuse_ctx->fs->mru_inode )
{
Inode* inode = iso9660_fuse_ctx->fs->mru_inode;
if ( inode->remote_reference_count )
inode->RemoteUnref();
else if ( inode->reference_count )
inode->Unref();
}
delete iso9660_fuse_ctx->fs; iso9660_fuse_ctx->fs = NULL;
delete iso9660_fuse_ctx->dev; iso9660_fuse_ctx->dev = NULL;
}
Inode* iso9660_fuse_resolve_path(const char* path)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode(fs->root_ino);
if ( !inode )
return (Inode*) NULL;
while ( path[0] )
{
if ( *path == '/' )
{
if ( !ISO9660_S_ISDIR(inode->Mode()) )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path++;
continue;
}
size_t elem_len = strcspn(path, "/");
char* elem = strndup(path, elem_len);
if ( !elem )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path += elem_len;
Inode* next = inode->Open(elem, O_RDONLY, 0);
free(elem);
inode->Unref();
if ( !next )
return NULL;
inode = next;
}
return inode;
}
// Assumes that the path doesn't end with / unless it's the root directory.
Inode* iso9660_fuse_parent_dir(const char** path_ptr)
{
const char* path = *path_ptr;
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode(fs->root_ino);
if ( !inode )
return (Inode*) NULL;
while ( strchr(path, '/') )
{
if ( *path == '/' )
{
if ( !ISO9660_S_ISDIR(inode->Mode()) )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path++;
continue;
}
size_t elem_len = strcspn(path, "/");
char* elem = strndup(path, elem_len);
if ( !elem )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path += elem_len;
Inode* next = inode->Open(elem, O_RDONLY, 0);
free(elem);
inode->Unref();
if ( !next )
return (Inode*) NULL;
inode = next;
}
*path_ptr = *path ? path : ".";
assert(!strchr(*path_ptr, '/'));
return inode;
}
int iso9660_fuse_getattr(const char* path, struct stat* st)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
StatInode(inode, st);
inode->Unref();
return 0;
}
int iso9660_fuse_fgetattr(const char* /*path*/, struct stat* st,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
StatInode(inode, st);
inode->Unref();
return 0;
}
int iso9660_fuse_readlink(const char* path, char* buf, size_t bufsize)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
if ( !ISO9660_S_ISLNK(inode->Mode()) )
return inode->Unref(), -(errno = EINVAL);
if ( !bufsize )
return inode->Unref(), -(errno = EINVAL);
ssize_t amount = inode->ReadLink((uint8_t*) buf, bufsize);
if ( amount < 0 )
return inode->Unref(), -errno;
buf[(size_t) amount < bufsize ? (size_t) amount : bufsize - 1] = '\0';
inode->Unref();
return 0;
}
int iso9660_fuse_mknod(const char* path, mode_t mode, dev_t dev)
{
(void) path;
(void) mode;
(void) dev;
return -(errno = ENOSYS);
}
int iso9660_fuse_mkdir(const char* path, mode_t /*mode*/)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
inode->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_unlink(const char* path)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
bool success = inode->Unlink(path, false);
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_rmdir(const char* path)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
bool success = inode->RemoveDirectory(path);
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_symlink(const char* /*oldname*/, const char* newname)
{
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return -errno;
newdir->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_rename(const char* oldname, const char* newname)
{
Inode* olddir = iso9660_fuse_parent_dir(&oldname);
if ( !olddir )
return -errno;
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return olddir->Unref(), -errno;
bool success = newdir->Rename(olddir, oldname, newname);
newdir->Unref();
olddir->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_link(const char* oldname, const char* newname)
{
Inode* inode = iso9660_fuse_resolve_path(oldname);
if ( !inode )
return -errno;
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return inode->Unref(), -errno;
bool success = inode->Link(newname, inode);
newdir->Unref();
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_chmod(const char* path, mode_t mode)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) mode;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_chown(const char* path, uid_t owner, gid_t group)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) owner;
(void) group;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_truncate(const char* path, off_t size)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) size;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_ftruncate(const char* /*path*/, off_t size,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
(void) size;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_open(const char* path, struct fuse_file_info* fi)
{
int flags = fi->flags;
Inode* dir = iso9660_fuse_parent_dir(&path);
if ( !dir )
return -errno;
Inode* result = dir->Open(path, flags, 0);
dir->Unref();
if ( !result )
return -errno;
fi->fh = (uint64_t) result->inode_id;
fi->keep_cache = 1;
result->RemoteRefer();
result->Unref();
return 0;
}
int iso9660_fuse_access(const char* path, int mode)
{
Inode* dir = iso9660_fuse_parent_dir(&path);
if ( !dir )
return -errno;
Inode* result = dir->Open(path, O_RDONLY, 0);
dir->Unref();
if ( !result )
return -errno;
(void) mode;
result->Unref();
return 0;
}
int iso9660_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi)
{
int flags = fi->flags | O_CREAT;
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
Inode* result = inode->Open(path, flags, FsModeFromHostMode(mode));
inode->Unref();
if ( !result )
return -errno;
fi->fh = (uint64_t) result->inode_id;
fi->keep_cache = 1;
result->RemoteRefer();
result->Unref();
return 0;
}
int iso9660_fuse_opendir(const char* path, struct fuse_file_info* fi)
{
return iso9660_fuse_open(path, fi);
}
int iso9660_fuse_read(const char* /*path*/, char* buf, size_t count,
off_t offset, struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
if ( INT_MAX < count )
count = INT_MAX;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset);
inode->Unref();
return 0 <= result ? (int) result : -errno;
}
int iso9660_fuse_write(const char* /*path*/, const char* /*buf*/,
size_t /*count*/, off_t /*offset*/,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_statfs(const char* /*path*/, struct statvfs* stvfs)
{
memset(stvfs, 0, sizeof(*stvfs));
Filesystem* fs = FUSE_FS;
stvfs->f_bsize = fs->block_size;
stvfs->f_frsize = fs->block_size;
stvfs->f_blocks = fs->device->device_size / fs->block_size;
stvfs->f_bfree = 0;
stvfs->f_bavail = 0;
stvfs->f_files = 0;
stvfs->f_ffree = 0;
stvfs->f_favail = 0;
stvfs->f_ffree = 0;
stvfs->f_fsid = 0;
stvfs->f_flag = ST_RDONLY;
stvfs->f_namemax = 255;
return 0;
}
int iso9660_fuse_flush(const char* /*path*/, struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return 0;
}
int iso9660_fuse_release(const char* /*path*/, struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->RemoteUnref();
inode->Unref();
return 0;
}
int iso9660_fuse_releasedir(const char* path, struct fuse_file_info* fi)
{
return iso9660_fuse_release(path, fi);
}
int iso9660_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi)
{
(void) data;
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return 0;
}
/*int iso9660_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi)
{
return iso9660_fuse_sync(path, data, fi);
}*/
/*int iso9660_fuse_setxattr(const char *, const char *, const char *, size_t, int)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_getxattr(const char *, const char *, char *, size_t)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_listxattr(const char *, char *, size_t)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_removexattr(const char *, const char *)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_readdir(const char* /*path*/, void* buf,
fuse_fill_dir_t filler, off_t rec_num,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((iso9660_ino_t) fi->fh);
if ( !inode )
return -errno;
if ( !S_ISDIR(inode->Mode()) )
return inode->Unref(), -(errno = ENOTDIR);
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( inode->ReadDirectory(&offset, &block, &block_id,
rec_num ? NULL : name, &file_type, &inode_id) )
{
if ( !rec_num || !rec_num-- )
{
if ( filler(buf, name, NULL, 0) )
{
block->Unref();
inode->Unref();
return 0;
}
}
}
int errnum = errno;
if ( block )
block->Unref();
inode->Unref();
if ( errnum )
return -errnum;
return 0;
}
/*int iso9660_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_utimens(const char* path, const struct timespec tv[2])
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) tv;
return inode->Unref(), -(errno = EROFS);
}
/*int iso9660_fuse_bmap(const char*, size_t blocksize, uint64_t* idx)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev)
{
struct fuse_operations operations;
memset(&operations, 0, sizeof(operations));
operations.access = iso9660_fuse_access;
operations.chmod = iso9660_fuse_chmod;
operations.chown = iso9660_fuse_chown;
operations.create = iso9660_fuse_create;
operations.destroy = iso9660_fuse_destroy;
operations.fgetattr = iso9660_fuse_fgetattr;
operations.flush = iso9660_fuse_flush;
operations.fsync = iso9660_fuse_fsync;
operations.ftruncate = iso9660_fuse_ftruncate;
operations.getattr = iso9660_fuse_getattr;
operations.init = iso9660_fuse_init;
operations.link = iso9660_fuse_link;
operations.mkdir = iso9660_fuse_mkdir;
operations.mknod = iso9660_fuse_mknod;
operations.opendir = iso9660_fuse_opendir;
operations.open = iso9660_fuse_open;
operations.readdir = iso9660_fuse_readdir;
operations.read = iso9660_fuse_read;
operations.readlink = iso9660_fuse_readlink;
operations.releasedir = iso9660_fuse_releasedir;
operations.release = iso9660_fuse_release;
operations.rename = iso9660_fuse_rename;
operations.rmdir = iso9660_fuse_rmdir;
operations.statfs = iso9660_fuse_statfs;
operations.symlink = iso9660_fuse_symlink;
operations.truncate = iso9660_fuse_truncate;
operations.unlink = iso9660_fuse_unlink;
operations.utimens = iso9660_fuse_utimens;
operations.write = iso9660_fuse_write;
operations.flag_nullpath_ok = 1;
operations.flag_nopath = 1;
char* argv_fuse[] =
{
(char*) argv0,
(char*) "-s",
(char*) (foreground ? "-f" : mount_path),
(char*) (foreground ? mount_path : NULL),
(char*) NULL,
};
int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1;
struct iso9660_fuse_ctx iso9660_fuse_ctx;
iso9660_fuse_ctx.fs = fs;
iso9660_fuse_ctx.dev = dev;
return fuse_main(argc_fuse, argv_fuse, &operations, &iso9660_fuse_ctx);
}
#endif

View File

@ -13,20 +13,20 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* serialize.h
* Import and export binary data structures
* fuse.h
* FUSE frontend.
*/
#ifndef SERIALIZE_H
#define SERIALIZE_H
#ifndef FUSE_H
#define FUSE_H
#include <sortix/initrd.h>
class Device;
class Filesystem;
void import_initrd_superblock(struct initrd_superblock* obj);
void export_initrd_superblock(struct initrd_superblock* obj);
void import_initrd_inode(struct initrd_inode* obj);
void export_initrd_inode(struct initrd_inode* obj);
void import_initrd_dirent(struct initrd_dirent* obj);
void export_initrd_dirent(struct initrd_dirent* obj);
int iso9660_fuse_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev);
#endif

614
iso9660/inode.cpp Normal file
View File

@ -0,0 +1,614 @@
/*
* Copyright (c) 2013, 2014, 2015, 2018, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* inode.cpp
* Filesystem inode.
*/
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <endian.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "iso9660fs.h"
#include "util.h"
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
#ifndef O_WRITE
#define O_WRITE (O_WRONLY | O_RDWR)
#endif
Inode::Inode(Filesystem* filesystem, iso9660_ino_t inode_id)
{
this->prev_inode = NULL;
this->next_inode = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->data_block = NULL;
this->data = NULL;
this->filesystem = filesystem;
this->reference_count = 1;
this->remote_reference_count = 0;
this->inode_id = inode_id;
this->parent_inode_id = 0;
}
Inode::~Inode()
{
if ( data_block )
data_block->Unref();
Unlink();
}
void Inode::Parse()
{
const uint8_t* block_data = (const uint8_t*) data;
uid = 0;
gid = 0;
time = 0;
uint8_t file_flags = block_data[25];
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
mode = 0555 | (is_directory ? ISO9660_S_IFDIR : ISO9660_S_IFREG);
uint32_t u32;
memcpy(&u32, block_data + 10, sizeof(u32));
size = le32toh(u32);
nlink = 1;
const uint8_t* time_bytes = block_data + 18;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = time_bytes[0];
tm.tm_mon = time_bytes[1] - 1;
tm.tm_mday = time_bytes[2];
tm.tm_hour = time_bytes[3];
tm.tm_min = time_bytes[4];
tm.tm_sec = time_bytes[5];
// TODO: Is this accurate?
time_t tz_offset = (-48 + time_bytes[6]) * 15 * 60;
// TODO: The timezone offset should've been mixed in with mktime, somehow.
time = mktime(&tm) + tz_offset;
uint8_t dirent_len = block_data[0];
uint8_t name_len = block_data[32];
size_t extended_off = 33 + name_len + !(name_len & 1);
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( len == 36 && field[0] == 'P' && field[1] == 'X' && field[3] == 1 )
{
uint32_t bits;
memcpy(&bits, field + 4, sizeof(bits));
mode = le32toh(bits) & 0xffff;
memcpy(&bits, field + 12, sizeof(bits));
nlink = le32toh(bits);
memcpy(&bits, field + 20, sizeof(bits));
uid = le32toh(bits);
memcpy(&bits, field + 28, sizeof(bits));
gid = le32toh(bits);
}
}
if ( ISO9660_S_ISLNK(mode) )
{
uint8_t buf[256];
size = ReadLink(buf, sizeof(buf));
}
}
uint32_t Inode::Mode()
{
return mode;
}
uint32_t Inode::UserId()
{
return uid;
}
uint32_t Inode::GroupId()
{
return gid;
}
uint64_t Inode::Size()
{
return size;
}
uint64_t Inode::Time()
{
return time;
}
Block* Inode::GetBlock(uint32_t offset)
{
uint32_t lba;
memcpy(&lba, (uint8_t*) data + 2, sizeof(lba));
lba = le32toh(lba);
uint32_t block_id = lba + offset;
return filesystem->device->GetBlock(block_id);
}
bool Inode::FindParentInode(uint64_t parent_lba, uint64_t parent_size)
{
if ( inode_id == filesystem->root_ino )
return parent_inode_id = inode_id, true;
Block* block = NULL;
uint32_t block_size = filesystem->block_size;
uint64_t parent_offset = parent_lba * block_size;
uint64_t block_id = 0;
uint64_t offset = 0;
while ( offset < parent_size )
{
uint64_t entry_block_id = (parent_offset + offset) / block_size;
uint64_t entry_block_offset = (parent_offset + offset) % block_size;
if ( block && block_id != entry_block_id )
{
block->Unref();
block = NULL;
}
if ( !block && !(block = filesystem->device->GetBlock(entry_block_id)) )
return false;
const uint8_t* block_data = block->block_data + entry_block_offset;
uint8_t dirent_len = block_data[0];
if ( !dirent_len )
{
offset = (entry_block_id + 1) * block_size;
continue;
}
uint8_t name_len = block_data[32];
const uint8_t* name_data = block_data + 33;
if ( name_len == 0 || !name_data[0] )
{
parent_inode_id = parent_offset + offset;
return true;
}
// TODO: Can dirent_len be misaligned?
uint64_t reclen = dirent_len + (dirent_len & 1);
if ( !reclen )
return errno = EINVAL, false;
offset += reclen;
}
return errno = EINVAL, false;
}
bool Inode::ReadDirectory(uint64_t* offset_inout,
Block** block_inout,
uint64_t* block_id_inout,
char* name,
uint8_t* file_type_out,
iso9660_ino_t* inode_id_out)
{
uint64_t offset = *offset_inout;
next_block:
uint64_t filesize = Size();
if ( filesize <= offset )
return errno = 0, false;
uint64_t entry_block_id = offset / filesystem->block_size;
uint64_t entry_block_offset = offset % filesystem->block_size;
if ( *block_inout && *block_id_inout != entry_block_id )
{
(*block_inout)->Unref();
(*block_inout) = NULL;
}
if ( !*block_inout &&
!(*block_inout = GetBlock(*block_id_inout = entry_block_id)) )
return false;
const uint8_t* block_data = (*block_inout)->block_data + entry_block_offset;
uint8_t dirent_len = block_data[0];
if ( !dirent_len )
{
offset = (entry_block_id + 1) * filesystem->block_size;
goto next_block;
}
if ( name )
{
uint8_t name_len = block_data[32];
const uint8_t* name_data = block_data + 33;
size_t extended_off = 33 + name_len + !(name_len & 1);
iso9660_ino_t entry_inode_id =
((*block_inout)->block_id * filesystem->block_size) +
entry_block_offset;
// TODO: The root directory inode should be that of its . entry.
if ( name_len == 0 || !name_data[0] )
{
name[0] = '.';
name[1] = '\0';
name_len = 1;
entry_inode_id = inode_id;
}
else if ( name_len == 1 && name_data[0] == 1 )
{
name[0] = '.';
name[1] = '.';
name[2] = '\0';
name_len = 2;
if ( !parent_inode_id )
{
uint32_t parent_lba;
memcpy(&parent_lba, block_data + 2, sizeof(parent_lba));
parent_lba = le32toh(parent_lba);
uint32_t parent_size;
memcpy(&parent_size, block_data + 10, sizeof(parent_size));
parent_size = le32toh(parent_size);
if ( !FindParentInode(parent_lba, parent_size) )
return false;
}
entry_inode_id = parent_inode_id;
}
else
{
for ( size_t i = 0; i < name_len; i++ )
{
if ( name_data[i] == ';' )
{
name_len = i;
break;
}
name[i] = tolower(name_data[i]);
}
name[name_len] = '\0';
}
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( 5 <= len && field[0] == 'N' && field[1] == 'M' &&
field[3] == 1 )
{
uint8_t nm_flags = field[4];
if ( nm_flags & (1 << 0) ) // TODO: Continued names.
break;
name_len = len - 5;
memcpy(name, field + 5, name_len);
name[name_len] = '\0';
}
// TODO: Other extensions.
}
uint8_t file_flags = block_data[25];
// TODO: Rock Ridge.
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
uint8_t file_type = is_directory ? ISO9660_FT_DIR : ISO9660_FT_REG_FILE;
*file_type_out = file_type;
*inode_id_out = entry_inode_id;
}
// TODO: Can dirent_len be misaligned?
uint64_t reclen = dirent_len + (dirent_len & 1);
if ( !reclen )
return errno = EINVAL, false;
offset += reclen;
*offset_inout = offset;
return true;
}
Inode* Inode::Open(const char* elem, int flags, mode_t mode)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, (Inode*) NULL;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
block->Unref();
if ( (flags & O_CREAT) && (flags & O_EXCL) )
return errno = EEXIST, (Inode*) NULL;
if ( (flags & O_DIRECTORY) &&
file_type != ISO9660_FT_UNKNOWN &&
file_type != ISO9660_FT_DIR &&
file_type != ISO9660_FT_SYMLINK )
return errno = ENOTDIR, (Inode*) NULL;
Inode* inode = filesystem->GetInode(inode_id);
if ( !inode )
return (Inode*) NULL;
if ( flags & O_DIRECTORY &&
!ISO9660_S_ISDIR(inode->Mode()) &&
!ISO9660_S_ISLNK(inode->Mode()) )
{
inode->Unref();
return errno = ENOTDIR, (Inode*) NULL;
}
if ( flags & O_WRITE )
{
inode->Unref();
return errno = EROFS, (Inode*) NULL;
}
return inode;
}
}
if ( block )
block->Unref();
if ( flags & O_CREAT )
{
(void) mode;
return errno = EROFS, (Inode*) NULL;
}
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Link(const char* elem, Inode* dest)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, false;
if ( ISO9660_S_ISDIR(dest->Mode()) )
return errno = EISDIR, false;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, false;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
block->Unref();
return errno = EEXIST, false;
}
}
if ( block )
block->Unref();
return errno = EROFS, false;
}
Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, (Inode*) NULL;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
(void) directories;
(void) force;
block->Unref();
return errno = EROFS, (Inode*) NULL;
}
}
if ( block )
block->Unref();
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Unlink(const char* elem, bool directories, bool force)
{
Inode* result = UnlinkKeep(elem, directories, force);
if ( !result )
return false;
result->Unref();
return true;
}
ssize_t Inode::ReadLink(uint8_t* buf, size_t bufsize)
{
size_t result = 0;
const uint8_t* block_data = (const uint8_t*) data;
uint8_t dirent_len = block_data[0];
uint8_t name_len = block_data[32];
size_t extended_off = 33 + name_len + !(name_len & 1);
bool continued = true;
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( 5 <= len && field[0] == 'S' && field[1] == 'L' && field[3] == 1 )
{
for ( size_t n = 5; n < len && 2 <= len - n; )
{
uint8_t comp_flags = field[n + 0];
uint8_t comp_len = field[n + 1];
if ( len - (n + 2) < comp_len )
break;
const char* data = (const char*) (field + n + 2);
size_t datalen = comp_len;
// TODO: How is a trailing slash encoded?
if ( !continued || (comp_flags & (1 << 3) /* root */) )
{
buf[result++] = '/';
if ( result == bufsize )
return (ssize_t) result;
}
if ( comp_flags & (1 << 1) )
{
data = ".";
datalen = 1;
}
else if ( comp_flags & (1 << 2) )
{
data = "..";
datalen = 2;
}
size_t possible = bufsize - result;
size_t count = datalen < possible ? datalen : possible;
memcpy(buf + result, data, count);
result += count;
if ( result == bufsize )
return (ssize_t) result;
continued = comp_flags & (1 << 0);
n += 2 + comp_len;
}
}
}
return (ssize_t) result;
}
ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !ISO9660_S_ISREG(Mode()) && !ISO9660_S_ISLNK(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
if ( SSIZE_MAX < s_count )
s_count = SSIZE_MAX;
uint64_t sofar = 0;
uint64_t count = (uint64_t) s_count;
uint64_t offset = (uint64_t) o_offset;
uint64_t file_size = Size();
if ( file_size <= offset )
return 0;
if ( file_size - offset < count )
count = file_size - offset;
while ( sofar < count )
{
uint64_t block_id = offset / filesystem->block_size;
uint32_t block_offset = offset % filesystem->block_size;
uint32_t block_left = filesystem->block_size - block_offset;
Block* block = GetBlock(block_id);
if ( !block )
return sofar ? sofar : -1;
size_t amount = count - sofar < block_left ? count - sofar : block_left;
memcpy(buf + sofar, block->block_data + block_offset, amount);
sofar += amount;
offset += amount;
block->Unref();
}
return (ssize_t) sofar;
}
bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname)
{
if ( !strcmp(oldname, ".") || !strcmp(oldname, "..") ||
!strcmp(newname, ".") || !strcmp(newname, "..") )
return errno = EPERM, false;
Inode* src_inode = olddir->Open(oldname, O_RDONLY, 0);
if ( !src_inode )
return false;
if ( Inode* dst_inode = Open(newname, O_RDONLY, 0) )
{
bool same_inode = src_inode->inode_id == dst_inode->inode_id;
dst_inode->Unref();
if ( same_inode )
return src_inode->Unref(), true;
}
src_inode->Unref();
return errno = EROFS, false;
}
bool Inode::RemoveDirectory(const char* path)
{
return UnlinkKeep(path, true);
}
void Inode::Refer()
{
reference_count++;
}
void Inode::Unref()
{
assert(0 < reference_count);
reference_count--;
if ( !reference_count && !remote_reference_count )
delete this;
}
void Inode::RemoteRefer()
{
remote_reference_count++;
}
void Inode::RemoteUnref()
{
assert(0 < remote_reference_count);
remote_reference_count--;
if ( !reference_count && !remote_reference_count )
delete this;
}
void Inode::Use()
{
data_block->Use();
Unlink();
Prelink();
}
void Inode::Unlink()
{
(prev_inode ? prev_inode->next_inode : filesystem->mru_inode) = next_inode;
(next_inode ? next_inode->prev_inode : filesystem->lru_inode) = prev_inode;
size_t bin = inode_id % INODE_HASH_LENGTH;
(prev_hashed ? prev_hashed->next_hashed : filesystem->hash_inodes[bin]) = next_hashed;
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
}
void Inode::Prelink()
{
prev_inode = NULL;
next_inode = filesystem->mru_inode;
if ( filesystem->mru_inode )
filesystem->mru_inode->prev_inode = this;
filesystem->mru_inode = this;
if ( !filesystem->lru_inode )
filesystem->lru_inode = this;
size_t bin = inode_id % INODE_HASH_LENGTH;
prev_hashed = NULL;
next_hashed = filesystem->hash_inodes[bin];
filesystem->hash_inodes[bin] = this;
if ( next_hashed )
next_hashed->prev_hashed = this;
}

83
iso9660/inode.h Normal file
View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* inode.h
* Filesystem inode.
*/
#ifndef INODE_H
#define INODE_H
#include "iso9660.h"
class Block;
class Filesystem;
class Inode
{
public:
Inode(Filesystem* filesystem, iso9660_ino_t inode_id);
~Inode();
public:
Inode* prev_inode;
Inode* next_inode;
Inode* prev_hashed;
Inode* next_hashed;
Block* data_block;
struct iso9660_dirent* data;
Filesystem* filesystem;
size_t reference_count;
size_t remote_reference_count;
iso9660_ino_t inode_id;
iso9660_ino_t parent_inode_id;
uint32_t uid;
uint32_t gid;
uint64_t size;
uint64_t time;
uint32_t mode;
uint32_t nlink;
public:
void Parse();
uint32_t Mode();
uint32_t UserId();
uint32_t GroupId();
uint64_t Size();
uint64_t Time();
Block* GetBlock(uint32_t offset);
bool FindParentInode(uint64_t parent_lba, uint64_t parent_size);
bool ReadDirectory(uint64_t* offset_inout, Block** block_inout,
uint64_t* block_id_inout, char* name,
uint8_t* file_type_out, iso9660_ino_t* inode_id_out);
Inode* Open(const char* elem, int flags, mode_t mode);
bool Link(const char* elem, Inode* dest);
bool Unlink(const char* elem, bool directories, bool force=false);
Inode* UnlinkKeep(const char* elem, bool directories, bool force=false);
ssize_t ReadLink(uint8_t* buf, size_t bufsize);
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
bool Rename(Inode* olddir, const char* oldname, const char* newname);
bool RemoveDirectory(const char* path);
void Refer();
void Unref();
void RemoteRefer();
void RemoteUnref();
void Use();
void Unlink();
void Prelink();
};
#endif

128
iso9660/iso9660.h Normal file
View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660.h
* Data structures for the ISO 9660 filesystem.
*/
#ifndef ISO9660_H
#define ISO9660_H
#include <stdint.h>
const uint8_t TYPE_BOOT_RECORD = 0x00;
const uint8_t TYPE_PRIMARY_VOLUME_DESCRIPTOR = 0x01;
const uint8_t TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR = 0xFF;
struct iso9660_pvd /* primary volume descriptor */
{
uint8_t type;
char standard_identifier[5];
uint8_t version;
uint8_t unused1;
char system_identifier[32];
char volume_identifier[32];
uint8_t unused2[8];
uint32_t volume_space_size_le;
uint32_t volume_space_size_be;
uint8_t unused3[32];
uint16_t volume_set_size_le;
uint16_t volume_set_size_be;
uint16_t volume_sequence_number_le;
uint16_t volume_sequence_number_be;
uint16_t logical_block_size_le;
uint16_t logical_block_size_be;
uint32_t path_table_size_le;
uint32_t path_table_size_be;
uint32_t path_table_lba_le;
uint32_t path_table_opt_lba_le;
uint32_t path_table_lba_be;
uint32_t path_table_opt_lba_be;
uint8_t root_dirent[34];
char volume_set_identifier[128];
char publisher_identifier[128];
char data_preparer_identifier[128];
char application_identifier[128];
char copyright_file_identifier[37];
char abstract_file_identifier[37];
char bibliographic_file_identifier[37];
char creation_datetime[17];
char modification_datetime[17];
char expiration_datetime[17];
char effective_datetime[17];
uint8_t file_structure_version;
uint8_t unused4;
uint8_t application_use[512];
uint8_t reserved[653];
};
static_assert(sizeof(struct iso9660_pvd) == 2048,
"sizeof(struct iso9660_pvd) == 2048");
typedef uint64_t iso9660_ino_t;
struct iso9660_dirent
{
uint8_t unused;
};
#define ISO9660_DIRENT_FLAG_DIR (1 << 1)
#define ISO9660_S_IFMT (0170000)
#define ISO9660_S_IFIFO (0010000)
#define ISO9660_S_IFCHR (0020000)
#define ISO9660_S_IFDIR (0040000)
#define ISO9660_S_IFBLK (0060000)
#define ISO9660_S_IFREG (0100000)
#define ISO9660_S_IFLNK (0120000)
#define ISO9660_S_IFSOCK (0140000)
#define ISO9660_S_ISSOCK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFSOCK)
#define ISO9660_S_ISLNK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFLNK)
#define ISO9660_S_ISREG(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFREG)
#define ISO9660_S_ISBLK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFBLK)
#define ISO9660_S_ISDIR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFDIR)
#define ISO9660_S_ISCHR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFCHR)
#define ISO9660_S_ISFIFO(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFIFO)
static const uint8_t ISO9660_FT_UNKNOWN = 0;
static const uint8_t ISO9660_FT_REG_FILE = 1;
static const uint8_t ISO9660_FT_DIR = 2;
static const uint8_t ISO9660_FT_CHRDEV = 3;
static const uint8_t ISO9660_FT_BLKDEV = 4;
static const uint8_t ISO9660_FT_FIFO = 5;
static const uint8_t ISO9660_FT_SOCK = 6;
static const uint8_t ISO9660_FT_SYMLINK = 7;
static inline uint8_t ISO9660_FT_OF_MODE(mode_t mode)
{
if ( ISO9660_S_ISREG(mode) )
return ISO9660_FT_REG_FILE;
if ( ISO9660_S_ISDIR(mode) )
return ISO9660_FT_DIR;
if ( ISO9660_S_ISCHR(mode) )
return ISO9660_FT_CHRDEV;
if ( ISO9660_S_ISBLK(mode) )
return ISO9660_FT_BLKDEV;
if ( ISO9660_S_ISFIFO(mode) )
return ISO9660_FT_FIFO;
if ( ISO9660_S_ISSOCK(mode) )
return ISO9660_FT_SOCK;
if ( ISO9660_S_ISLNK(mode) )
return ISO9660_FT_SYMLINK;
return ISO9660_FT_UNKNOWN;
}
#endif

278
iso9660/iso9660fs.cpp Normal file
View File

@ -0,0 +1,278 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660fs.cpp
* Implementation of the ISO 9660 filesystem.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <dirent.h>
#include <endian.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__sortix__)
#include "fsmarshall.h"
#else
#include "fuse.h"
#endif
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "ioleast.h"
#include "iso9660.h"
#include "iso9660fs.h"
#include "util.h"
uid_t request_uid;
uid_t request_gid;
mode_t HostModeFromFsMode(uint32_t mode)
{
mode_t hostmode = mode & 07777;
if ( ISO9660_S_ISSOCK(mode) ) hostmode |= S_IFSOCK;
if ( ISO9660_S_ISLNK(mode) ) hostmode |= S_IFLNK;
if ( ISO9660_S_ISREG(mode) ) hostmode |= S_IFREG;
if ( ISO9660_S_ISBLK(mode) ) hostmode |= S_IFBLK;
if ( ISO9660_S_ISDIR(mode) ) hostmode |= S_IFDIR;
if ( ISO9660_S_ISCHR(mode) ) hostmode |= S_IFCHR;
if ( ISO9660_S_ISFIFO(mode) ) hostmode |= S_IFIFO;
return hostmode;
}
uint32_t FsModeFromHostMode(mode_t hostmode)
{
uint32_t mode = hostmode & 07777;
if ( S_ISSOCK(hostmode) ) mode |= ISO9660_S_IFSOCK;
if ( S_ISLNK(hostmode) ) mode |= ISO9660_S_IFLNK;
if ( S_ISREG(hostmode) ) mode |= ISO9660_S_IFREG;
if ( S_ISBLK(hostmode) ) mode |= ISO9660_S_IFBLK;
if ( S_ISDIR(hostmode) ) mode |= ISO9660_S_IFDIR;
if ( S_ISCHR(hostmode) ) mode |= ISO9660_S_IFCHR;
if ( S_ISFIFO(hostmode) ) mode |= ISO9660_S_IFIFO;
return mode;
}
uint8_t HostDTFromFsDT(uint8_t fsdt)
{
switch ( fsdt )
{
case ISO9660_FT_UNKNOWN: return DT_UNKNOWN;
case ISO9660_FT_REG_FILE: return DT_REG;
case ISO9660_FT_DIR: return DT_DIR;
case ISO9660_FT_CHRDEV: return DT_CHR;
case ISO9660_FT_BLKDEV: return DT_BLK;
case ISO9660_FT_FIFO: return DT_FIFO;
case ISO9660_FT_SOCK: return DT_SOCK;
case ISO9660_FT_SYMLINK: return DT_LNK;
}
return DT_UNKNOWN;
}
void StatInode(Inode* inode, struct stat* st)
{
memset(st, 0, sizeof(*st));
st->st_ino = inode->inode_id;
st->st_mode = HostModeFromFsMode(inode->Mode());
st->st_nlink = inode->nlink;
st->st_uid = inode->uid;
st->st_gid = inode->gid;
st->st_size = inode->size;
// TODO: Rock Ridge.
st->st_atim.tv_sec = inode->Time();
st->st_atim.tv_nsec = 0;
// TODO: Rock Ridge.
st->st_ctim.tv_sec = inode->Time();
st->st_ctim.tv_nsec = 0;
// TODO: Rock Ridge.
st->st_mtim.tv_sec = inode->Time();
st->st_mtim.tv_nsec = 0;
st->st_blksize = inode->filesystem->block_size;
st->st_blocks = divup(st->st_size, (off_t) 512);
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
static void help(FILE* fp, const char* argv0)
{
fprintf(fp, "Usage: %s [OPTION]... DEVICE [MOUNT-POINT]\n", argv0);
}
static void version(FILE* fp, const char* argv0)
{
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
}
int main(int argc, char* argv[])
{
const char* argv0 = argv[0];
const char* pretend_mount_path = NULL;
bool foreground = false;
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
while ( char c = *++arg ) switch ( c )
{
case 'r': break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
help(stderr, argv0);
exit(1);
}
}
else if ( !strcmp(arg, "--help") )
help(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--version") )
version(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--background") )
foreground = false;
else if ( !strcmp(arg, "--foreground") )
foreground = true;
else if ( !strcmp(arg, "--read") )
{
}
else if ( !strncmp(arg, "--pretend-mount-path=", strlen("--pretend-mount-path=")) )
pretend_mount_path = arg + strlen("--pretend-mount-path=");
else if ( !strcmp(arg, "--pretend-mount-path") )
{
if ( i+1 == argc )
{
fprintf(stderr, "%s: --pretend-mount-path: Missing operand\n", argv0);
exit(1);
}
pretend_mount_path = argv[++i];
argv[i] = NULL;
}
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
help(stderr, argv0);
exit(1);
}
}
if ( argc == 1 )
{
help(stdout, argv0);
exit(0);
}
compact_arguments(&argc, &argv);
const char* device_path = 2 <= argc ? argv[1] : NULL;
const char* mount_path = 3 <= argc ? argv[2] : NULL;
if ( !device_path )
{
help(stderr, argv0);
exit(1);
}
if ( !pretend_mount_path )
pretend_mount_path = mount_path;
int fd = open(device_path, O_RDONLY);
if ( fd < 0 )
err(1, "%s", device_path);
// Search for the Primary Volume Descriptor.
off_t block_size = 2048;
struct iso9660_pvd pvd;
off_t pvd_offset;
for ( uint32_t i = 16; true; i++ )
{
pvd_offset = block_size * i;
if ( preadall(fd, &pvd, sizeof(pvd), pvd_offset) != sizeof(pvd) )
{
if ( errno == EEOF )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
else
err(1, "read: %s", device_path);
}
if ( memcmp(pvd.standard_identifier, "CD001", 5) != 0 )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
if ( pvd.type == TYPE_PRIMARY_VOLUME_DESCRIPTOR )
break;
if ( pvd.type == TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
}
if ( pvd.version != 1 || pvd.file_structure_version != 1 )
errx(1, "Unsupported ISO 9660 filesystem version: %s", device_path);
// TODO: Test if appropriate power of two and allow.
if ( le32toh(pvd.logical_block_size_le) != block_size )
errx(1, "Unsupported ISO 9660 block size: %s", device_path);
block_size = le32toh(pvd.logical_block_size_le);
Device* dev = new Device(fd, device_path, block_size);
if ( !dev )
err(1, "malloc");
Filesystem* fs = new Filesystem(dev, pretend_mount_path, pvd_offset);
if ( !fs )
err(1, "%s", device_path);
// Change the root inode to be its own . entry instead of the pvd record, so
// parent directories correctly .. to it, and so Rock Ridge extensions work.
Inode* root = fs->GetInode(fs->root_ino);
if ( !root )
err(1, "%s: GetInode", device_path);
fs->root_ino = 0;
uint32_t root_lba;
uint32_t root_size;
memcpy(&root_lba, pvd.root_dirent + 2, sizeof(root_lba));
memcpy(&root_size, pvd.root_dirent + 10, sizeof(root_size));
root->FindParentInode(root_lba, root_size);
fs->root_ino = root->parent_inode_id;
root->Unref();
if ( !mount_path )
return 0;
#if defined(__sortix__)
return fsmarshall_main(argv0, mount_path, foreground, fs, dev);
#else
return iso9660_fuse_main(argv0, mount_path, foreground, fs, dev);
#endif
}

33
iso9660/iso9660fs.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660fs.h
* Implementation of the ISO 9660 filesystem.
*/
#ifndef ISO9660FS_H
#define ISO9660FS_H
extern uid_t request_uid;
extern gid_t request_gid;
class Inode;
mode_t HostModeFromFsMode(uint32_t fsmode);
uint32_t FsModeFromHostMode(mode_t hostmode);
uint8_t HostDTFromFsDT(uint8_t fsdt);
void StatInode(Inode* inode, struct stat* st);
#endif

49
iso9660/util.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* util.h
* Utility functions for the filesystem implementation.
*/
#ifndef UTIL_H
#define UTIL_H
template <class T> T divup(T a, T b)
{
return a/b + (a % b ? 1 : 0);
}
template <class T> T roundup(T a, T b)
{
return a % b ? a + b - a % b : a;
}
static inline bool checkbit(const uint8_t* bitmap, size_t bit)
{
uint8_t bits = bitmap[bit / 8UL];
return bits & (1U << (bit % 8UL));
}
static inline void setbit(uint8_t* bitmap, size_t bit)
{
bitmap[bit / 8UL] |= 1U << (bit % 8UL);
}
static inline void clearbit(uint8_t* bitmap, size_t bit)
{
bitmap[bit / 8UL] &= ~(1U << (bit % 8UL));
}
#endif

1
kernel/.gitignore vendored
View File

@ -4,7 +4,6 @@
*.iso
*.so
*.a
*.initrd
*.out
kb/default-kblayout
kb/default-kblayout.h

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -29,6 +29,7 @@
namespace Sortix {
size_t aux_allocated = 0;
size_t aux_leaked = 0;
size_t heap_allocated = 0;
kthread_mutex_t alloc_lock = KTHREAD_MUTEX_INITIALIZER;
@ -69,6 +70,8 @@ void FreeKernelAddress(addralloc_t* alloc)
addr_t aux_reached = kmem_from + kmem_size - aux_allocated;
if ( alloc->from == aux_reached )
aux_allocated -= alloc->size;
else
aux_leaked += alloc->size;
alloc->from = 0;
alloc->size = 0;
@ -98,6 +101,17 @@ void ShrinkHeap(size_t decrease)
heap_allocated -= decrease;
}
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked,
size_t* total)
{
ScopedLock lock(&alloc_lock);
addr_t kmem_from;
Memory::GetKernelVirtualArea(&kmem_from, total);
*heap = heap_allocated;
*aux = aux_allocated - aux_leaked;
*leaked = aux_leaked;
}
// No need for locks in these three functions, since only the heap calls these
// and it already uses an internal lock, and heap_allocated will not change
// unless the heap calls ExpandHeap.

View File

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

View File

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

View File

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

View File

@ -1,92 +0,0 @@
/*
* Copyright (c) 2012, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* sortix/initrd.h
* The Sortix init ramdisk filesystem format.
*/
#ifndef _INCLUDE_SORTIX_INITRD_H
#define _INCLUDE_SORTIX_INITRD_H
#include <stdint.h>
#define INITRD_ALGO_CRC32 0
#define INITRD_ALGO_NONE 1
#define INITRD_S_IXOTH 01
#define INITRD_S_IWOTH 02
#define INITRD_S_IROTH 03
#define INITRD_S_IRWXO 07
#define INITRD_S_IXGRP 010
#define INITRD_S_IWGRP 020
#define INITRD_S_IRGRP 040
#define INITRD_S_IRWXG 070
#define INITRD_S_IXUSR 0100
#define INITRD_S_IWUSR 0200
#define INITRD_S_IRUSR 0400
#define INITRD_S_IRWXU 0700
#define INITRD_S_IFMT 0xF000
#define INITRD_S_IFSOCK 0xC000
#define INITRD_S_IFLNK 0xA000
#define INITRD_S_IFREG 0x8000
#define INITRD_S_IFBLK 0x6000
#define INITRD_S_IFDIR 0x4000
#define INITRD_S_IFCHR 0x2000
#define INITRD_S_IFIFO 0x1000
#define INITRD_S_ISUID 0x0800
#define INITRD_S_ISGID 0x0400
#define INITRD_S_ISVTX 0x0200
#define INITRD_S_ISSOCK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFSOCK)
#define INITRD_S_ISLNK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFLNK)
#define INITRD_S_ISREG(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFREG)
#define INITRD_S_ISBLK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFBLK)
#define INITRD_S_ISDIR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFDIR)
#define INITRD_S_ISCHR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFCHR)
#define INITRD_S_ISFIFO(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFIFO)
typedef struct initrd_superblock
{
char magic[16]; /* "sortix-initrd-2" */
uint32_t fssize;
uint32_t revision;
uint32_t inodesize;
uint32_t inodecount;
uint32_t inodeoffset;
uint32_t root;
uint32_t sumalgorithm;
uint32_t sumsize;
} initrd_superblock_t;
typedef struct initrd_inode
{
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint32_t nlink;
uint64_t ctime;
uint64_t mtime;
uint32_t dataoffset;
uint32_t size;
} initrd_inode_t;
typedef struct initrd_dirent
{
uint32_t inode;
uint16_t reclen;
uint16_t namelen;
char name[0];
} initrd_dirent_t;
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -39,6 +39,7 @@ void ShrinkHeap(size_t decrease);
addr_t GetHeapLower();
addr_t GetHeapUpper();
size_t GetHeapSize();
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked, size_t* total);
} // namespace Sortix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -36,4 +36,35 @@ typedef uintptr_t addr_t;
#define CPU X64
#endif
#ifdef __TRACE_ALLOCATION_SITES
struct kernel_allocation_site
{
struct __allocation_site allocation_site;
struct kernel_allocation_site* next;
bool registered;
};
extern struct kernel_allocation_site* first_kernel_allocation_site;
#undef ALLOCATION_SITE
#define ALLOCATION_SITE \
({ \
static struct kernel_allocation_site site = \
{ { __FILE__, __LINE__, __func__, 0, 0 }, NULL, false }; \
if ( !site.registered ) \
{ \
site.registered = true; \
site.next = first_kernel_allocation_site; \
first_kernel_allocation_site = &site; \
} \
&site.allocation_site; \
})
#include <stddef.h>
void* operator new(size_t size, struct __allocation_site* allocation_site);
void* operator new[](size_t size, struct __allocation_site* allocation_site);
#define new new (ALLOCATION_SITE)
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-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
@ -31,7 +31,6 @@
#include <sortix/dirent.h>
#include <sortix/fcntl.h>
#include <sortix/initrd.h>
#include <sortix/mman.h>
#include <sortix/stat.h>
#include <sortix/tar.h>
@ -51,17 +50,12 @@
namespace Sortix {
// TODO: The initrd is not being properly verified.
// TODO: The initrd is not handled in an endian-neutral manner.
struct initrd_context
{
uint8_t* initrd;
size_t initrd_size;
addr_t initrd_unmap_start;
addr_t initrd_unmap_end;
struct initrd_superblock* sb;
Ref<Descriptor> links;
ioctx_t ioctx;
};
@ -81,262 +75,6 @@ static void UnmapInitrdPage(struct initrd_context* ctx, addr_t vaddr)
Page::Put(addr, PAGE_USAGE_WASNT_ALLOCATED);
}
static mode_t initrd_mode_to_host_mode(uint32_t mode)
{
mode_t result = mode & 0777;
if ( INITRD_S_ISVTX & mode ) result |= S_ISVTX;
if ( INITRD_S_ISSOCK(mode) ) result |= S_IFSOCK;
if ( INITRD_S_ISLNK(mode) ) result |= S_IFLNK;
if ( INITRD_S_ISREG(mode) ) result |= S_IFREG;
if ( INITRD_S_ISBLK(mode) ) result |= S_IFBLK;
if ( INITRD_S_ISDIR(mode) ) result |= S_IFDIR;
if ( INITRD_S_ISCHR(mode) ) result |= S_IFCHR;
if ( INITRD_S_ISFIFO(mode) ) result |= S_IFIFO;
return result;
}
static struct initrd_inode* initrd_get_inode(struct initrd_context* ctx,
uint32_t inode)
{
if ( ctx->sb->inodecount <= inode )
return errno = EINVAL, (struct initrd_inode*) NULL;
uint32_t pos = ctx->sb->inodeoffset + ctx->sb->inodesize * inode;
return (struct initrd_inode*) (ctx->initrd + pos);
}
static uint8_t* initrd_inode_get_data(struct initrd_context* ctx,
struct initrd_inode* inode,
size_t* size)
{
return *size = inode->size, ctx->initrd + inode->dataoffset;
}
static uint32_t initrd_directory_open(struct initrd_context* ctx,
struct initrd_inode* inode,
const char* name)
{
if ( !INITRD_S_ISDIR(inode->mode) )
return errno = ENOTDIR, 0;
uint32_t offset = 0;
while ( offset < inode->size )
{
uint32_t pos = inode->dataoffset + offset;
struct initrd_dirent* dirent =
(struct initrd_dirent*) (ctx->initrd + pos);
if ( dirent->namelen && !strcmp(dirent->name, name) )
return dirent->inode;
offset += dirent->reclen;
}
return errno = ENOENT, 0;
}
static const char* initrd_directory_get_filename(struct initrd_context* ctx,
struct initrd_inode* inode,
size_t index)
{
if ( !INITRD_S_ISDIR(inode->mode) )
return errno = ENOTDIR, (const char*) NULL;
uint32_t offset = 0;
while ( offset < inode->size )
{
uint32_t pos = inode->dataoffset + offset;
struct initrd_dirent* dirent =
(struct initrd_dirent*) (ctx->initrd + pos);
if ( index-- == 0 )
return dirent->name;
offset += dirent->reclen;
}
return errno = EINVAL, (const char*) NULL;
}
static size_t initrd_directory_get_num_files(struct initrd_context* ctx,
struct initrd_inode* inode)
{
if ( !INITRD_S_ISDIR(inode->mode) )
return errno = ENOTDIR, 0;
uint32_t offset = 0;
size_t numentries = 0;
while ( offset < inode->size )
{
uint32_t pos = inode->dataoffset + offset;
const struct initrd_dirent* dirent =
(const struct initrd_dirent*) (ctx->initrd + pos);
numentries++;
offset += dirent->reclen;
}
return numentries;
}
static void ExtractNode(struct initrd_context* ctx,
struct initrd_inode* inode,
Ref<Descriptor> node);
static void ExtractFile(struct initrd_context* ctx,
struct initrd_inode* inode,
Ref<Descriptor> file)
{
size_t filesize;
uint8_t* data = initrd_inode_get_data(ctx, inode, &filesize);
if ( file->truncate(&ctx->ioctx, filesize) != 0 )
PanicF("initrd: truncate: %m");
size_t sofar = 0;
while ( sofar < filesize )
{
size_t left = filesize - sofar;
size_t chunk = 1024 * 1024;
size_t count = left < chunk ? left : chunk;
ssize_t numbytes = file->write(&ctx->ioctx, data + sofar, count);
if ( numbytes <= 0 )
PanicF("initrd: write: %m");
sofar += numbytes;
}
}
static void ExtractDir(struct initrd_context* ctx,
struct initrd_inode* inode,
Ref<Descriptor> dir)
{
size_t numfiles = initrd_directory_get_num_files(ctx, inode);
for ( size_t i = 0; i < numfiles; i++ )
{
const char* name = initrd_directory_get_filename(ctx, inode, i);
if ( !name )
PanicF("initrd_directory_get_filename: %m");
if ( IsDotOrDotDot(name) )
continue;
uint32_t childino = initrd_directory_open(ctx, inode, name);
if ( !childino )
PanicF("initrd_directory_open: %s: %m", name);
struct initrd_inode* child =
(struct initrd_inode*) initrd_get_inode(ctx, childino);
if ( !child )
PanicF("initrd_get_inode(%u): %s: %m", childino, name);
mode_t mode = initrd_mode_to_host_mode(child->mode);
if ( INITRD_S_ISDIR(child->mode) )
{
if ( dir->mkdir(&ctx->ioctx, name, mode) && errno != EEXIST )
PanicF("initrd: mkdir: %s: %m", name);
Ref<Descriptor> desc = dir->open(&ctx->ioctx, name,
O_SEARCH | O_DIRECTORY, 0);
if ( !desc )
PanicF("initrd: %s: %m", name);
ExtractNode(ctx, child, desc);
}
if ( INITRD_S_ISREG(child->mode) )
{
assert(child->nlink != 0);
char link_path[sizeof(childino) * 3];
snprintf(link_path, sizeof(link_path), "%ju", (uintmax_t) childino);
Ref<Descriptor> existing(ctx->links->open(&ctx->ioctx, link_path,
O_READ, 0));
if ( !existing || dir->link(&ctx->ioctx, name, existing) != 0 )
{
Ref<Descriptor> desc(dir->open(&ctx->ioctx, name,
O_WRITE | O_CREATE, mode));
if ( !desc )
PanicF("initrd: %s: %m", name);
ExtractNode(ctx, child, desc);
if ( 2 <= child->nlink )
ctx->links->link(&ctx->ioctx, link_path, desc);
}
if ( --child->nlink == 0 && INITRD_S_ISREG(child->mode) )
{
size_t filesize;
const uint8_t* data =
initrd_inode_get_data(ctx, child, &filesize);
uintptr_t from = (uintptr_t) data;
uintptr_t size = filesize;
uintptr_t from_aligned = Page::AlignUp(from);
uintptr_t from_distance = from_aligned - from;
if ( from_distance <= size )
{
uintptr_t size_aligned =
Page::AlignDown(size - from_distance);
for ( size_t i = 0; i < size_aligned; i += Page::Size() )
UnmapInitrdPage(ctx, from_aligned + i);
Memory::Flush();
}
}
}
if ( INITRD_S_ISLNK(child->mode) )
{
size_t filesize;
uint8_t* data = initrd_inode_get_data(ctx, child, &filesize);
char* oldname = new char[filesize + 1];
if ( !oldname )
PanicF("initrd: malloc: %m");
memcpy(oldname, data, filesize);
oldname[filesize] = '\0';
int ret = dir->symlink(&ctx->ioctx, oldname, name);
delete[] oldname;
if ( ret < 0 )
PanicF("initrd: symlink: %s", name);
Ref<Descriptor> desc = dir->open(&ctx->ioctx, name,
O_READ | O_SYMLINK_NOFOLLOW, 0);
if ( desc )
ExtractNode(ctx, child, desc);
}
}
}
static void ExtractNode(struct initrd_context* ctx,
struct initrd_inode* inode,
Ref<Descriptor> node)
{
if ( node->chmod(&ctx->ioctx, initrd_mode_to_host_mode(inode->mode)) < 0 )
PanicF("initrd: chmod: %m");
if ( node->chown(&ctx->ioctx, inode->uid, inode->gid) < 0 )
PanicF("initrd: chown: %m");
if ( INITRD_S_ISDIR(inode->mode) )
ExtractDir(ctx, inode, node);
if ( INITRD_S_ISREG(inode->mode) )
ExtractFile(ctx, inode, node);
struct timespec times[2];
times[0] = timespec_make((time_t) inode->mtime, 0);
times[1] = timespec_make((time_t) inode->mtime, 0);
if ( node->utimens(&ctx->ioctx, times) < 0 )
PanicF("initrd: utimens: %m");
}
static void ExtractInitrd(Ref<Descriptor> desc, struct initrd_context* ctx)
{
ctx->sb = (struct initrd_superblock*) ctx->initrd;
if ( ctx->initrd_size < ctx->sb->fssize )
Panic("Initrd header does not match its size");
if ( desc->mkdir(&ctx->ioctx, ".initrd-links", 0777) != 0 )
PanicF("initrd: .initrd-links: %m");
if ( !(ctx->links = desc->open(&ctx->ioctx, ".initrd-links",
O_READ | O_DIRECTORY, 0)) )
PanicF("initrd: .initrd-links: %m");
struct initrd_inode* root = initrd_get_inode(ctx, ctx->sb->root);
if ( !root )
PanicF("initrd: initrd_get_inode(%u): %m", ctx->sb->root);
ExtractNode(ctx, root, desc);
union
{
struct dirent dirent;
uint8_t dirent_data[sizeof(struct dirent) + sizeof(uintmax_t) * 3];
};
while ( 0 < ctx->links->readdirents(&ctx->ioctx, &dirent, sizeof(dirent_data)) &&
((const char*) dirent.d_name)[0] )
{
if ( ((const char*) dirent.d_name)[0] == '.' )
continue;
ctx->links->unlinkat(&ctx->ioctx, dirent.d_name, AT_REMOVEFILE);
ctx->links->lseek(&ctx->ioctx, 0, SEEK_SET);
}
ctx->links.Reset();
desc->unlinkat(&ctx->ioctx, ".initrd-links", AT_REMOVEDIR);
}
struct TAR
{
unsigned char* tar_file;
@ -447,18 +185,6 @@ static bool ReadTar(TAR* TAR)
}
}
static bool SearchTar(struct initrd_context* ctx, TAR* TAR, const char* path)
{
OpenTar(TAR, ctx->initrd, ctx->initrd_size);
while ( ReadTar(TAR) )
{
if ( !strcmp(TAR->name, path) )
return true;
}
CloseTar(TAR);
return false;
}
static void ExtractTarObject(Ref<Descriptor> desc,
struct initrd_context* ctx,
TAR* TAR)
@ -517,166 +243,6 @@ static void ExtractTar(Ref<Descriptor> desc, struct initrd_context* ctx)
CloseTar(&TAR);
}
static bool TarIsTix(struct initrd_context* ctx)
{
TAR TAR;
bool result = SearchTar(ctx, &TAR, "tix/tixinfo");
CloseTar(&TAR);
return result;
}
static char* tixinfo_lookup(const char* info,
size_t info_size,
const char* what)
{
size_t what_length = strlen(what);
while ( info_size )
{
size_t line_length = 0;
while ( line_length < info_size && info[line_length] != '\n' )
line_length++;
if ( what_length <= line_length &&
!strncmp(info, what, what_length) &&
info[what_length] == '=' )
{
char* result = strndup(info + what_length + 1,
line_length - (what_length + 1));
if ( !result )
Panic("initrd tar malloc failure");
return result;
}
info += line_length;
info_size -= line_length;
if ( info_size && info[0] == '\n' )
{
info++;
info_size--;
}
}
return NULL;
}
static void DescriptorWriteLine(Ref<Descriptor> desc,
ioctx_t* ioctx,
const char* str)
{
size_t len = strlen(str);
while ( str[0] )
{
ssize_t done = desc->write(ioctx, (unsigned char*) str, len);
if ( done <= 0 )
PanicF("initrd tix metadata write: %m");
str += done;
len -= done;
}
if ( desc->write(ioctx, (unsigned char*) "\n", 1) != 1 )
PanicF("initrd tix metadata write: %m");
}
static int manifest_sort(const void* a_ptr, const void* b_ptr)
{
const char* a = *(const char* const*) a_ptr;
const char* b = *(const char* const*) b_ptr;
return strcmp(a, b);
}
static void ExtractTix(Ref<Descriptor> desc, struct initrd_context* ctx)
{
TAR TAR;
if ( !SearchTar(ctx, &TAR, "tix/tixinfo") )
Panic("initrd was not tix");
char* pkg_name =
tixinfo_lookup((const char*) TAR.data, TAR.size, "pkg.name");
if ( !pkg_name )
Panic("initrd tixinfo lacked pkg.name");
if ( desc->mkdir(&ctx->ioctx, "/tix", 0755) < 0 && errno != EEXIST )
PanicF("/tix: %m");
if ( desc->mkdir(&ctx->ioctx, "/tix/tixinfo", 0755) < 0 && errno != EEXIST )
PanicF("/tix/tixinfo: %m");
if ( desc->mkdir(&ctx->ioctx, "/tix/manifest", 0755) < 0 && errno != EEXIST )
PanicF("/tix/manifest: %m");
char* tixinfo_path;
if ( asprintf(&tixinfo_path, "/tix/tixinfo/%s", pkg_name) < 0 )
Panic("initrd tar malloc failure");
char* TAR_oldname = TAR.name;
TAR.name = tixinfo_path;
ExtractTarObject(desc, ctx, &TAR);
TAR.name = TAR_oldname;
free(tixinfo_path);
CloseTar(&TAR);
Ref<Descriptor> installed_list =
desc->open(&ctx->ioctx, "/tix/installed.list",
O_CREATE | O_WRITE | O_APPEND, 0644);
if ( !installed_list )
PanicF("/tix/installed.list: %m");
DescriptorWriteLine(installed_list, &ctx->ioctx, pkg_name);
installed_list.Reset();
size_t manifest_list_size = 0;
OpenTar(&TAR, ctx->initrd, ctx->initrd_size);
while ( ReadTar(&TAR) )
{
if ( !strncmp(TAR.name, "data", 4) && TAR.name[4] == '/' )
manifest_list_size++;
}
CloseTar(&TAR);
char** manifest_list = new char*[manifest_list_size];
if ( !manifest_list )
Panic("initrd tar malloc failure");
OpenTar(&TAR, ctx->initrd, ctx->initrd_size);
size_t manifest_list_count = 0;
while ( ReadTar(&TAR) )
{
if ( strncmp(TAR.name, "data", 4) != 0 || TAR.name[4] != '/' )
continue;
char* path = strdup(TAR.name + 4);
if ( !path )
Panic("initrd tar malloc failure");
size_t length = strlen(path);
// Trim the trailing slash from directories.
if ( 2 <= length && path[length-1] == '/' )
path[length-1] = '\0';
manifest_list[manifest_list_count++] = path;
}
CloseTar(&TAR);
qsort(manifest_list, manifest_list_count, sizeof(char*), manifest_sort);
char* manifest_path;
if ( asprintf(&manifest_path, "/tix/manifest/%s", pkg_name) < 0 )
Panic("initrd tar malloc failure");
Ref<Descriptor> manifest =
desc->open(&ctx->ioctx, manifest_path, O_WRITE | O_CREATE | O_TRUNC, 0644);
if ( !manifest )
PanicF("%s: %m", manifest_path);
free(manifest_path);
for ( size_t i = 0; i < manifest_list_count; i++ )
DescriptorWriteLine(manifest, &ctx->ioctx, manifest_list[i]);
manifest.Reset();
for ( size_t i = 0; i < manifest_list_count; i++ )
free(manifest_list[i]);
delete[] manifest_list;
OpenTar(&TAR, ctx->initrd, ctx->initrd_size);
const char* subdir = "data/";
size_t subdir_length = strlen(subdir);
while ( ReadTar(&TAR) )
{
bool name_data = !strncmp(TAR.name, subdir, subdir_length) &&
TAR.name[subdir_length];
bool linkname_data = !strncmp(TAR.linkname, subdir, subdir_length) &&
TAR.linkname[subdir_length];
if ( name_data )
{
TAR.name += subdir_length;
if ( linkname_data )
TAR.linkname += subdir_length;
ExtractTarObject(desc, ctx, &TAR);
TAR.name -= subdir_length;
if ( linkname_data )
TAR.linkname -= subdir_length;
}
}
CloseTar(&TAR);
free(pkg_name);
}
static int ExtractTo_mkdir(Ref<Descriptor> desc, ioctx_t* ctx,
const char* path, mode_t mode)
{
@ -786,21 +352,13 @@ static void ExtractModule(struct multiboot_mod_list* module,
else if ( !strncmp(cmdline, "--create-to ", strlen("--create-to ")) ||
!strncmp(cmdline, "--create-to=", strlen("--create-to=")) )
ExtractTo(desc, ctx, cmdline + strlen("--create-to "), O_EXCL);
else if ( sizeof(struct initrd_superblock) <= ctx->initrd_size &&
// TODO: After releasing Sortix 1.1, remove this nice error message.
else if ( strlen("sortix-initrd-2") <= ctx->initrd_size &&
!memcmp(ctx->initrd, "sortix-initrd-2", strlen("sortix-initrd-2")) )
ExtractInitrd(desc, ctx);
Panic("The sortix-initrd-2 format is no longer supported");
else if ( sizeof(struct tar) <= ctx->initrd_size &&
!memcmp(ctx->initrd + offsetof(struct tar, magic), "ustar", 5) )
{
if ( !strcmp(cmdline, "--tar") )
ExtractTar(desc, ctx);
else if ( !strcmp(cmdline, "--tix") )
ExtractTix(desc, ctx);
else if ( TarIsTix(ctx) )
ExtractTix(desc, ctx);
else
ExtractTar(desc, ctx);
}
ExtractTar(desc, ctx);
else if ( sizeof(xz_magic) <= ctx->initrd_size &&
!memcmp(ctx->initrd, xz_magic, sizeof(xz_magic)) )
Panic("Bootloader failed to decompress an xz initrd, "
@ -821,7 +379,6 @@ static void ExtractModule(struct multiboot_mod_list* module,
UnmapInitrdPage(ctx, mapat + i);
Memory::Flush();
// Free the used virtual address space.
FreeKernelAddress(&initrd_addr_alloc);
}

View File

@ -102,6 +102,8 @@
#include "x86-family/vbox.h"
#endif
struct kernel_allocation_site* first_kernel_allocation_site = NULL;
// Keep the stack size aligned with $CPU/boot.s
const size_t STACK_SIZE = 64*1024;
extern "C" { __attribute__((aligned(16))) size_t stack[STACK_SIZE / sizeof(size_t)]; }
@ -200,33 +202,6 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
// Display the logo.
Log::PrintF("\e[37;41m\e[2J");
Log::Center(BRAND_LOGO);
#if defined(__x86_64__)
// TODO: Remove this hack when qemu 1.4.x and 1.5.0 are obsolete.
// Verify that we are not running under a buggy qemu where the instruction
// movl (%eax), %esi is misinterpreted (amongst others). In this case it
// will try to access the memory at [bx + si]. We'll make sure that eax
// points to a variable on the stack that has another value than at bx + si,
// and if the values compare equal using the buggy instruction, we panic.
uint32_t intended_variable; // rax will point to here.
uint32_t is_buggy_qemu;
asm ("movq $0x1000, %%rbx\n" /* access 32-bit value at 0x1000 */
"movl (%%rbx), %%esi\n"
"subl $1, %%esi\n" /* change the 32-bit value */
"movl %%esi, (%%rax)\n" /* store the new value in intended_variable */
"movq $0x0, %%rsi\n" /* make rsi zero, so bx + si points to 0x1000 */
"movl (%%eax), %%esi\n" /* do the perhaps-buggy memory access */
"movl (%%rax), %%ebx\n" /* do a working memory access */
"movl %%ebx, %0\n" /* load the desired value into is_buggy_qemu */
"subl %%esi, %0\n" /* subtract the possibly incorrect value. */
: "=r"(is_buggy_qemu)
: "a"(&intended_variable)
: "rsi", "rbx");
if ( is_buggy_qemu )
Panic("You are running a buggy version of qemu. The 1.4.x and 1.5.0 "
"releases are known to execute some instructions incorrectly on "
"x86_64 without KVM. You have three options: 1) Enable KVM 2) "
"Use a 32-bit OS 3) Use another version of qemu.");
#endif
char* cmdline = NULL;
if ( bootinfo->flags & MULTIBOOT_INFO_CMDLINE && bootinfo->cmdline )

View File

@ -24,6 +24,8 @@
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/syscall.h>
#include "net/tcp.h"
#ifndef VERSIONSTR
#define VERSIONSTR "unknown"
#endif
@ -50,6 +52,77 @@ ssize_t sys_kernelinfo(const char* user_req, char* user_resp, size_t resplen)
char* req = GetStringFromUser(user_req);
if ( !req )
return -1;
// DEBUG
if ( !strcmp(req, "virtual-usage") )
{
delete[] req;
size_t heap;
size_t aux;
size_t leaked;
size_t total;
KernelAddressStatistics(&heap, &aux, &leaked, &total);
char str[4 * (20 + 3 * sizeof(size_t) + 1 + 8)];
snprintf(str, sizeof(str),
"%20zu B heap\n%20zu B aux\n%20zu B leaked\n%20zu B total",
heap, aux, leaked, total);
size_t stringlen = strlen(str);
if ( resplen < stringlen + 1 )
return errno = ERANGE, (ssize_t) stringlen;
if ( !CopyToUser(user_resp, str, sizeof(char) * (stringlen + 1)) )
return -1;
return 0;
}
// DEBUG
if ( !strcmp(req, "tcp") )
{
delete[] req;
return TCP::Info(user_resp, resplen);
}
#ifdef __TRACE_ALLOCATION_SITES
if ( !strcmp(req, "allocations") )
{
delete[] req;
bool exhausted = false;
size_t total_needed = 0;
for ( struct kernel_allocation_site* site = first_kernel_allocation_site;
site;
site = site->next )
{
char str[256];
snprintf(str, sizeof(str), "%20zu B %zu allocations %s:%zu %s %c",
site->allocation_site.current_size,
site->allocation_site.allocations,
site->allocation_site.file,
site->allocation_site.line,
site->allocation_site.function,
site->next ? '\n' : '\0');
size_t stringlen = strlen(str);
total_needed += stringlen;
if ( exhausted )
continue;
if ( resplen < stringlen )
{
exhausted = true;
continue;
}
if ( !CopyToUser(user_resp, str, sizeof(char) * stringlen) )
return -1;
user_resp += stringlen;
resplen -= stringlen;
}
if ( !exhausted && !resplen )
exhausted = true;
if ( !exhausted )
{
char zero = '\0';
if ( !CopyToUser(user_resp, &zero, 1) )
return -1;
}
if ( exhausted )
return errno = ERANGE, (ssize_t) total_needed;
return 0;
}
#endif
const char* str = KernelInfo(req);
delete[] req;
if ( !str )

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2016, 2017, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -34,6 +34,7 @@ void HandleIP(Ref<Packet> pkt,
const struct in_addr* dst,
bool dst_broadcast);
Ref<Inode> Socket(int af);
ssize_t Info(char* user_resp, size_t resplen);
} // namespace TCP
} // namespace Sortix

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -242,8 +242,14 @@ int vdprintf(int fildes, const char* __restrict format, __gnuc_va_list ap)
/* Functions copied from elsewhere. */
#if __USE_SORTIX
#ifdef __TRACE_ALLOCATION_SITES
int asprintf_trace(struct __allocation_site*, char** __restrict, const char* __restrict, ...)
__attribute__((__format__ (printf, 3, 4)));
#define asprintf(a, ...) asprintf_trace(ALLOCATION_SITE, (a), __VA_ARGS__)
#else
int asprintf(char** __restrict, const char* __restrict, ...)
__attribute__((__format__ (printf, 2, 3)));
#endif
void clearerr_unlocked(FILE* stream);
int feof_unlocked(FILE* stream);
int ferror_unlocked(FILE* stream);
@ -255,9 +261,15 @@ int fputc_unlocked(int c, FILE* stream);
int fputs_unlocked(const char* __restrict, FILE* __restrict stream);
size_t fread_unlocked(void* __restrict ptr, size_t size, size_t nitems, FILE* __restrict stream);
size_t fwrite_unlocked(const void* __restrict ptr, size_t size, size_t nitems, FILE* __restrict stream);
#ifdef __TRACE_ALLOCATION_SITES
int vasprintf_trace(struct __allocation_site*, char** __restrict, const char* __restrict, __gnuc_va_list)
__attribute__((__format__ (printf, 3, 0)));
#define vasprintf(a, ...) vasprintf_trace(ALLOCATION_SITE, (a), __VA_ARGS__)
#else
int vasprintf(char** __restrict, const char* __restrict, __gnuc_va_list)
__attribute__((__format__ (printf, 2, 0)));
#endif
#endif
/* Functions that are Sortix extensions. */
#if __USE_SORTIX

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -88,7 +88,12 @@ double atof(const char* value);
int atoi(const char*);
long atol(const char*);
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
#ifdef __TRACE_ALLOCATION_SITES
void* calloc_trace(struct __allocation_site*, size_t, size_t);
#define calloc(a, b) calloc_trace(ALLOCATION_SITE, (a), (b))
#else
void* calloc(size_t, size_t);
#endif
char* canonicalize_file_name(const char* path);
char* canonicalize_file_name_at(int dirfd, const char* path);
int clearenv(void);
@ -99,7 +104,12 @@ void free(void*);
char* getenv(const char*);
long labs(long);
ldiv_t ldiv(long, long);
#ifdef __TRACE_ALLOCATION_SITES
void* malloc_trace(struct __allocation_site*, size_t);
#define malloc(a) malloc_trace(ALLOCATION_SITE, (a))
#else
void* malloc(size_t);
#endif
int mblen(const char*, size_t);
size_t mbstowcs(wchar_t* __restrict, const char* __restrict, size_t);
int mbtowc(wchar_t *__restrict, const char* __restrict, size_t);
@ -116,7 +126,12 @@ void qsort_r(void*, size_t, size_t, int (*)(const void*, const void*, void*), vo
__attribute__((__warning__("rand() isn't random, use arc4random()")))
#endif
int rand(void);
#ifdef __TRACE_ALLOCATION_SITES
void* realloc_trace(struct __allocation_site*, void*, size_t);
#define realloc(a, b) realloc_trace(ALLOCATION_SITE, (a), (b))
#else
void* realloc(void*, size_t);
#endif
char* realpath(const char* __restrict, char* __restrict);
int setenv(const char*, const char*, int);
#if !defined(__is_sortix_libc) /* not a warning inside libc */
@ -189,7 +204,12 @@ int posix_openpt(int);
uint32_t arc4random(void);
void arc4random_buf(void*, size_t);
uint32_t arc4random_uniform(uint32_t);
#ifdef __TRACE_ALLOCATION_SITES
void* reallocarray_trace(struct __allocation_site*, void*, size_t, size_t);
#define reallocarray(a, b, c) reallocarray_trace(ALLOCATION_SITE, (a), (b), (c))
#else
void* reallocarray(void*, size_t, size_t);
#endif
int ptsname_r(int, char*, size_t);
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2017, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -91,8 +91,13 @@ void* memccpy(void* __restrict, const void* __restrict, int, size_t);
/* Functions from XOPEN 420 gone into POSIX 2008. */
#if __USE_SORTIX || 420 <= __USE_XOPEN || 200809L <= __USE_POSIX
#ifdef __TRACE_ALLOCATION_SITES
char* strdup_trace(struct __allocation_site*, const char*);
#define strdup(a) strdup_trace(ALLOCATION_SITE, (a))
#else
char* strdup(const char*);
#endif
#endif
/* Functions from POSIX 2001. */
#if __USE_SORTIX || 200112L <= __USE_POSIX
@ -111,7 +116,12 @@ char* strtok_r(char* __restrict, const char* __restrict, char** __restrict);
char* stpcpy(char* __restrict, const char* __restrict);
char* stpncpy(char* __restrict, const char* __restrict, size_t);
int strcoll_l(const char*, const char*, locale_t);
#ifdef __TRACE_ALLOCATION_SITES
char* strndup_trace(struct __allocation_site*, const char*, size_t);
#define strndup(a, b) strndup_trace(ALLOCATION_SITE, (a), (b))
#else
char* strndup(const char*, size_t);
#endif
size_t strnlen(const char*, size_t);
#if __USE_SORTIX && __SORTIX_STDLIB_REDIRECTS
const char* strsignal(int signum) __asm__ ("sortix_strsignal");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -128,4 +128,24 @@
is updated to not rely on this macro. */
#undef __SORTIX_HAS_GETSERVBYNAME__
#if (defined(__is_sortix_libk) || defined(__is_sortix_kernel)) && \
defined(__TRACE_KMALLOC)
#undef __TRACE_ALLOCATION_SITES
#define __TRACE_ALLOCATION_SITES
#endif
#ifdef __TRACE_ALLOCATION_SITES
#include <stddef.h>
struct __allocation_site
{
const char* file;
size_t line;
const char* function;
size_t current_size;
size_t allocations;
};
#define __TRACE_ALLOCATION_SITES
#define ALLOCATION_SITE 0
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,13 +20,24 @@
#include <stdarg.h>
#include <stdio.h>
#ifdef __TRACE_ALLOCATION_SITES
int asprintf_trace(struct __allocation_site* allocation_site,
char** restrict result_ptr,
const char* restrict format,
...)
#else
int asprintf(char** restrict result_ptr,
const char* restrict format,
...)
#endif
{
va_list list;
va_start(list, format);
#ifdef __TRACE_ALLOCATION_SITES
int result = vasprintf_trace(allocation_site, result_ptr, format, list);
#else
int result = vasprintf(result_ptr, format, list);
#endif
va_end(list);
return result;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -27,6 +27,9 @@ struct vasprintf
char* buffer;
size_t used;
size_t size;
#ifdef __TRACE_ALLOCATION_SITES
struct __allocation_site* allocation_site;
#endif
};
static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
@ -39,7 +42,12 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
size_t new_size = 2 * state->size;
if ( new_size < needed_size )
new_size = needed_size;
#ifdef __TRACE_ALLOCATION_SITES
char* new_buffer = (char*) realloc_trace(state->allocation_site,
state->buffer, new_size);
#else
char* new_buffer = (char*) realloc(state->buffer, new_size);
#endif
if ( !new_buffer )
{
free(state->buffer);
@ -54,14 +62,26 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
return length;
}
#ifdef __TRACE_ALLOCATION_SITES
int vasprintf_trace(struct __allocation_site* allocation_site,
char** restrict result_ptr,
const char* restrict format,
va_list list)
#else
int vasprintf(char** restrict result_ptr,
const char* restrict format,
va_list list)
#endif
{
struct vasprintf state;
state.used = 0;
state.size = 32;
#ifdef __TRACE_ALLOCATION_SITES
state.allocation_site = allocation_site;
if ( !(state.buffer = (char*) malloc_trace(allocation_site, state.size)) )
#else
if ( !(state.buffer = (char*) malloc(state.size)) )
#endif
return -1;
int result = vcbprintf(&state, vasprintf_callback, format, list);
if ( !state.buffer )

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -22,12 +22,21 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
void* calloc_trace(struct __allocation_site* allocation_site,
size_t nmemb, size_t size)
#else
void* calloc(size_t nmemb, size_t size)
#endif
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
size_t total = nmemb * size;
#ifdef __TRACE_ALLOCATION_SITES
void* result = malloc_trace(allocation_site, total);
#else
void* result = malloc(total);
#endif
if ( !result )
return NULL;
memset(result, 0, total);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -39,6 +39,16 @@ void free(void* addr)
// Retrieve the chunk that contains this allocation.
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) addr);
#ifdef __TRACE_ALLOCATION_SITES
if ( MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic) )
{
struct __allocation_site* allocation_site =
ALLOCATION_SITE_OF_MAGIC(chunk->chunk_magic);
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
}
#endif
// Return the chunk to the heap.
heap_insert_chunk(chunk);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -31,7 +31,12 @@
#if !defined(HEAP_GUARD_DEBUG)
#ifdef __TRACE_ALLOCATION_SITES
void* malloc_trace(struct __allocation_site* allocation_site,
size_t original_size)
#else
void* malloc(size_t original_size)
#endif
{
if ( !heap_size_has_bin(original_size) )
return errno = ENOMEM, (void*) NULL;
@ -89,6 +94,12 @@ void* malloc(size_t original_size)
if ( heap_can_split_chunk(result_chunk, chunk_size) )
heap_split_chunk(result_chunk, chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += result_chunk->chunk_size;
allocation_site->allocations++;
result_chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -21,9 +21,18 @@
#include <stdint.h>
#include <stdlib.h>
#ifdef __TRACE_ALLOCATION_SITES
void* reallocarray_trace(struct __allocation_site* allocation_site,
void* ptr, size_t nmemb, size_t size)
#else
void* reallocarray(void* ptr, size_t nmemb, size_t size)
#endif
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
#ifdef __TRACE_ALLOCATION_SITES
return realloc_trace(allocation_site, ptr, nmemb * size);
#else
return realloc(ptr, nmemb * size);
#endif
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,10 +20,18 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
char* strdup_trace(struct __allocation_site* allocation_site, const char* input)
#else
char* strdup(const char* input)
#endif
{
size_t input_length = strlen(input);
#ifdef __TRACE_ALLOCATION_SITES
char* result = (char*) malloc_trace(allocation_site, input_length + 1);
#else
char* result = (char*) malloc(input_length + 1);
#endif
if ( !result )
return NULL;
memcpy(result, input, input_length + 1);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,10 +20,19 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
char* strndup_trace(struct __allocation_site* allocation_site,
const char* input, size_t n)
#else
char* strndup(const char* input, size_t n)
#endif
{
size_t input_size = strnlen(input, n);
#ifdef __TRACE_ALLOCATION_SITES
char* result = (char*) malloc_trace(allocation_site, input_size + 1);
#else
char* result = (char*) malloc(input_size + 1);
#endif
if ( !result )
return NULL;
memcpy(result, input, input_size);

View File

@ -20,6 +20,7 @@ extended.o \
filesystem.o \
gpt.o \
harddisk.o \
iso9660.o \
mbr.o \
partition.o \
uuid.o \

View File

@ -32,6 +32,7 @@
#include <mount/ext2.h>
#include <mount/extended.h>
#include <mount/filesystem.h>
#include <mount/iso9660.h>
#include <mount/partition.h>
const char* filesystem_error_string(enum filesystem_error error)
@ -55,6 +56,7 @@ static const struct filesystem_handler* filesystem_handlers[] =
&biosboot_handler,
&extended_handler,
&ext2_handler,
&iso9660_handler,
NULL,
};

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* mount/iso9660.h
* ISO 9660 filesystem.
*/
#ifndef INCLUDE_MOUNT_ISO9660_H
#define INCLUDE_MOUNT_ISO9660_H
#include <stdint.h>
#include <mount/filesystem.h>
#if defined(__cplusplus)
extern "C" {
#endif
extern const struct filesystem_handler iso9660_handler;
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

98
libmount/iso9660.c Normal file
View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660.c
* iso9660 filesystem.
*/
#include <endian.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <mount/blockdevice.h>
#include <mount/filesystem.h>
#include <mount/iso9660.h>
#include <mount/uuid.h>
#include "util.h"
static size_t iso9660_probe_amount(struct blockdevice* bdev)
{
(void) bdev;
size_t block_size = 2048 /* TODO: Actual block size? */;
return 17 * block_size;
}
static bool iso9660_probe(struct blockdevice* bdev,
const unsigned char* leading,
size_t amount)
{
(void) bdev;
size_t block_size = 2048 /* TODO: Actual block size? */;
if ( amount < 17 * block_size )
return false;
size_t offset = 16 * block_size;
return !memcmp(leading + offset + 1, "CD001", 5);
}
static void iso9660_release(struct filesystem* fs)
{
if ( !fs )
return;
free(fs);
}
static enum filesystem_error iso9660_inspect(struct filesystem** fs_ptr,
struct blockdevice* bdev)
{
*fs_ptr = NULL;
struct filesystem* fs = CALLOC_TYPE(struct filesystem);
if ( !fs )
return FILESYSTEM_ERROR_ERRNO;
fs->bdev = bdev;
fs->handler = &iso9660_handler;
fs->handler_private = NULL;
fs->fstype_name = "iso9660";
fs->driver = "iso9660fs";
size_t block_size = 2048 /* TODO: Actual block size? */;
size_t offset = 16 * block_size;
uint8_t pvd[2048];
if ( blockdevice_preadall(bdev, pvd, sizeof(pvd), offset) != sizeof(pvd) )
return iso9660_release(fs), FILESYSTEM_ERROR_ERRNO;
// TODO: LABEL support using the volume name.
char vol_set[128 + 1];
memcpy(vol_set, pvd + 190, 128);
vol_set[128] = '\0';
for ( size_t i = 128; i && vol_set[i-1] == ' '; i-- )
vol_set[i-1] = '\0';
if ( uuid_validate(vol_set) )
{
fs->flags |= FILESYSTEM_FLAG_UUID;
uuid_from_string(fs->uuid, vol_set);
}
return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
}
const struct filesystem_handler iso9660_handler =
{
.handler_name = "iso9660",
.probe_amount = iso9660_probe_amount,
.probe = iso9660_probe,
.inspect = iso9660_inspect,
.release = iso9660_release,
};

View File

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

2
mkinitrd/.gitignore vendored
View File

@ -1,2 +0,0 @@
mkinitrd
initrdfs

View File

@ -1,35 +0,0 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
SORTIXKERNEL=../kernel
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\" -I$(SORTIXKERNEL)/include -I.
CFLAGS:=$(CFLAGS) -Wall -Wextra
ifeq ($(HOST_IS_SORTIX),0)
CPPFLAGS+=-D_GNU_SOURCE
endif
BINARIES=mkinitrd initrdfs
all: $(BINARIES)
.PHONY: all install clean
%: %.c rules.c serialize.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< rules.c serialize.c -o $@
clean:
rm -f $(BINARIES)
install: all
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
mkdir -p $(DESTDIR)$(MANDIR)/man8
cp initrdfs.8 $(DESTDIR)$(MANDIR)/man8/initrdfs.8
cp mkinitrd.8 $(DESTDIR)$(MANDIR)/man8/mkinitrd.8

View File

@ -1,67 +0,0 @@
.Dd October 7, 2015
.Dt INITRDFS 8
.Os
.Sh NAME
.Nm initrdfs
.Nd view initialization ramdisk
.Sh SYNOPSIS
.Nm
.Ar initrd
cat
.Ar path ...
.Nm
.Ar initrd
extract
.Op Fl C Ar destination
.Ar path ...
.Nm
.Ar initrd
ls
.Op Fl a
.Ar path ...
.Sh DESCRIPTION
.Nm
opens a
.Xr initrd 7
made with
.Xr mkinitrd 8
and lets you view the stored directories and files.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl a
.Sy ( ls )
Include directory entries whose names begin with a
dot
.Pq Sq \&. .
.It Fl C Ar destination
.Sy ( extract )
Extract to the specified
.Ar destination
rather than the default current directory.
.El
.Pp
.Nm
supports these commands:
.Bl -tag -width "12345678"
.It Sy cat
Show the contents of each
.Ar path .
.It Sy extract
Extract each
.Ar path
recursively to the current directory.
.It Sy ls
List each directory
.Ar path .
.El
.Sh EXIT STATUS
.Nm
will exit 0 on success and non-zero otherwise.
.Sh SEE ALSO
.Xr initrd 7 ,
.Xr mkinitrd 8
.Sh BUGS
.Nm
is feature limited and doesn't let you view all the contained meta information.
It's also not a filesystem driver.

View File

@ -1,395 +0,0 @@
/*
* Copyright (c) 2012, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* initrdfs.c
* Provides access to filesystems in the Sortix kernel initrd format.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <ioleast.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sortix/initrd.h>
#include "serialize.h"
char* Substring(const char* str, size_t start, size_t length)
{
char* result = (char*) malloc(length+1);
strncpy(result, str + start, length);
result[length] = 0;
return result;
}
bool ReadSuperBlock(int fd, initrd_superblock_t* dest)
{
if ( preadall(fd, dest, sizeof(*dest), 0) != sizeof(*dest) )
return false;
import_initrd_superblock(dest);
return true;
}
initrd_superblock_t* GetSuperBlock(int fd)
{
size_t sbsize = sizeof(initrd_superblock_t);
initrd_superblock_t* sb = (initrd_superblock_t*) malloc(sbsize);
if ( !sb ) { return NULL; }
if ( !ReadSuperBlock(fd, sb) ) { free(sb); return NULL; }
return sb;
}
bool ReadInode(int fd, initrd_superblock_t* sb, uint32_t ino,
initrd_inode_t* dest)
{
uint32_t inodepos = sb->inodeoffset + sb->inodesize * ino;
if ( preadall(fd, dest, sizeof(*dest), inodepos) != sizeof(*dest) )
return false;
import_initrd_inode(dest);
return true;
}
initrd_inode_t* GetInode(int fd, initrd_superblock_t* sb, uint32_t ino)
{
initrd_inode_t* inode = (initrd_inode_t*) malloc(sizeof(initrd_inode_t));
if ( !inode ) { return NULL; }
if ( !ReadInode(fd, sb, ino, inode) ) { free(inode); return NULL; }
return inode;
}
initrd_inode_t* CloneInode(const initrd_inode_t* src)
{
initrd_inode_t* result = (initrd_inode_t*) malloc(sizeof(*src));
if ( !result ) { return NULL; }
memcpy(result, src, sizeof(*src));
return result;
}
bool ReadInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode,
uint8_t* dest, size_t size, off_t offset)
{
(void) sb;
if ( offset < 0 )
return errno = EINVAL, false;
if ( inode->size < (uintmax_t) offset )
return errno = EINVAL, false;
size_t available = inode->size - offset;
if ( inode->size < available )
return errno = EINVAL, false;
return preadall(fd, dest, size, inode->dataoffset + offset) == size;
}
uint8_t* GetInodeDataSize(int fd, initrd_superblock_t* sb,
initrd_inode_t* inode, size_t size)
{
uint8_t* buf = (uint8_t*) malloc(size);
if ( !buf )
return NULL;
if ( !ReadInodeData(fd, sb, inode, buf, size, 0) )
{
free(buf);
return NULL;
}
return buf;
}
uint8_t* GetInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode)
{
return GetInodeDataSize(fd, sb, inode, inode->size);
}
uint32_t Traverse(int fd, initrd_superblock_t* sb, initrd_inode_t* inode,
const char* name)
{
if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; }
uint8_t* direntries = GetInodeData(fd, sb, inode);
if ( !direntries ) { return 0; }
uint32_t result = 0;
uint32_t offset = 0;
while ( offset < inode->size )
{
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
import_initrd_dirent(dirent);
if ( dirent->namelen && !strcmp(dirent->name, name) )
{
result = dirent->inode;
export_initrd_dirent(dirent);
break;
}
offset += dirent->reclen;
export_initrd_dirent(dirent);
}
free(direntries);
if ( !result ) { errno = ENOENT; }
return result;
}
initrd_inode_t* ResolvePath(int fd, initrd_superblock_t* sb,
initrd_inode_t* inode, const char* path)
{
if ( !path[0] ) { return CloneInode(inode); }
if ( path[0] == '/' )
{
if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return NULL; }
return ResolvePath(fd, sb, inode, path+1);
}
size_t elemlen = strcspn(path, "/");
char* elem = Substring(path, 0, elemlen);
uint32_t ino = Traverse(fd, sb, inode, elem);
free(elem);
if ( !ino ) { return NULL; }
initrd_inode_t* child = GetInode(fd, sb, ino);
if ( !child ) { return NULL; }
if ( !path[elemlen] ) { return child; }
initrd_inode_t* result = ResolvePath(fd, sb, child, path + elemlen);
free(child);
return result;
}
bool ListDirectory(int fd, initrd_superblock_t* sb, initrd_inode_t* dir,
bool all)
{
if ( !INITRD_S_ISDIR(dir->mode) ) { errno = ENOTDIR; return false; }
uint8_t* direntries = GetInodeData(fd, sb, dir);
if ( !direntries ) { return false; }
uint32_t offset = 0;
while ( offset < dir->size )
{
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
if ( dirent->namelen && (all || dirent->name[0] != '.'))
{
printf("%s\n", dirent->name);
}
offset += dirent->reclen;
}
free(direntries);
return true;
}
bool PrintFile(int fd, initrd_superblock_t* sb, initrd_inode_t* inode)
{
if ( INITRD_S_ISDIR(inode->mode ) ) { errno = EISDIR; return false; }
uint32_t sofar = 0;
while ( sofar < inode->size )
{
const size_t BUFFER_SIZE = 16UL * 1024UL;
uint8_t buffer[BUFFER_SIZE];
uint32_t available = inode->size - sofar;
uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE;
if ( !ReadInodeData(fd, sb, inode, buffer, count, 0) ) { return false; }
if ( writeall(1, buffer, count) != count ) { return false; }
sofar += count;
}
return true;
}
void Extract(int fd,
const char* fd_path,
initrd_superblock_t* sb,
initrd_inode_t* inode,
const char* out_path,
bool verbose)
{
if ( verbose )
printf("%s\n", out_path);
if ( INITRD_S_ISLNK(inode->mode) )
{
char* buffer = (char*) malloc(inode->size + 1);
if ( !buffer )
err(1, "malloc");
if ( !ReadInodeData(fd, sb, inode, (uint8_t*) buffer, inode->size, 0) )
err(1, "%s", fd_path);
buffer[inode->size] = '\0';
// TODO: What if it already exists.
if ( symlink(buffer, out_path) < 0 )
err(1, "%s", out_path);
return;
}
else if ( INITRD_S_ISREG(inode->mode) )
{
int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0200);
if ( out_fd < 0 )
err(1, "%s", out_path);
uint32_t sofar = 0;
while ( sofar < inode->size )
{
const size_t BUFFER_SIZE = 16UL * 1024UL;
uint8_t buffer[BUFFER_SIZE];
uint32_t available = inode->size - sofar;
uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE;
if ( !ReadInodeData(fd, sb, inode, buffer, count, sofar) )
err(1, "%s", fd_path);
if ( writeall(out_fd, buffer, count) != count )
err(1, "%s", out_path);
sofar += count;
}
if ( fchmod(out_fd, inode->mode & 07777) < 0 )
err(1, "%s", out_path);
close(out_fd);
return;
}
else if ( !INITRD_S_ISDIR(inode->mode) )
errx(1, "%s: Unsupported kind of file", out_path);
bool made = true;
if ( mkdir(out_path, 0700) < 0 )
{
if ( errno != EEXIST )
err(1, "%s", out_path);
made = false;
}
uint8_t* direntries = GetInodeData(fd, sb, inode);
if ( !direntries )
err(1, "%s", out_path);
uint32_t offset = 0;
while ( offset < inode->size )
{
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
offset += dirent->reclen;
if ( !dirent->namelen ) // TODO: Possible?
continue;
if ( !strcmp(dirent->name, ".") || !strcmp(dirent->name, "..") )
continue;
char* child_path;
if ( asprintf(&child_path, "%s/%s", out_path, dirent->name) < 0 )
err(1, "asprintf");
initrd_inode_t* child_inode = ResolvePath(fd, sb, inode, dirent->name);
if ( !child_inode )
err(1, "%s: %s", fd_path, out_path);
Extract(fd, fd_path, sb, child_inode, child_path, verbose);
free(child_path);
}
free(direntries);
// TODO: Time of check to time of use race condition, a concurrent rename
// and we may assign the permissions to the wrong file, potentially
// exploitable.
if ( made && chmod(out_path, inode->mode & 07777) < 0 )
err(1, " %s", out_path);
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
int main(int argc, char* argv[])
{
bool all = false;
const char* destination = ".";
bool verbose = false;
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
case 'a': all = true; break;
case 'C':
if ( !*(destination = arg + 1) )
{
if ( i + 1 == argc )
errx(1, "option requires an argument -- 'C'");
destination = argv[i+1];
argv[++i] = NULL;
}
arg = "C";
break;
case 'v': verbose = true; break;
default:
errx(1, "unknown option -- '%c'", c);
}
}
else
errx(1, "unknown option: %s", arg);
}
compact_arguments(&argc, &argv);
if ( argc == 1 )
errx(1, "No initrd specified");
const char* initrd = argv[1];
if ( argc == 2 )
errx(1, "No command specified");
const char* cmd = argv[2];
int fd = open(initrd, O_RDONLY);
if ( fd < 0 )
err(1, "open: %s", initrd);
initrd_superblock_t* sb = GetSuperBlock(fd);
if ( !sb )
err(1, "read: %s", initrd);
initrd_inode_t* root = GetInode(fd, sb, sb->root);
if ( !root )
err(1, "read: %s", initrd);
for ( int i = 3; i < argc; i++ )
{
const char* path = argv[i];
if ( path[0] != '/' )
{
errno = ENOENT;
errx(1, "%s", path);
}
initrd_inode_t* inode = ResolvePath(fd, sb, root, path+1);
if ( !inode )
err(1, "%s", path);
if ( !strcmp(cmd, "cat") )
{
if ( !PrintFile(fd, sb, inode) )
err(1, "%s", path);
}
else if ( !strcmp(cmd, "ls") )
{
if ( !ListDirectory(fd, sb, inode, all) )
err(1, "%s", path);
}
else if ( !strcmp(cmd, "extract") )
Extract(fd, initrd, sb, inode, destination, verbose);
else
errx(1, "unrecognized command: %s", cmd);
free(inode);
}
return 0;
}

View File

@ -1,126 +0,0 @@
.Dd October 7, 2015
.Dt MKINITRD 8
.Os
.Sh NAME
.Nm mkinitrd
.Nd make initialization ramdisk
.Sh SYNOPSIS
.Nm mkinitrd
.Op Fl \-filter Ns "=" Ns Ar rules-file
.Op Fl \-format Ns "=" Ns Ar format
.Op Fl \-manifest Ns "=" Ns Ar manifest-file
.Fl o Ar destination
.Ar directory ...
.Sh DESCRIPTION
.Nm
produces a
.Xr initrd 7
for the Sortix
.Xr kernel 7
at the
.Ar destination .
It is an archive in the
.In sortix/initrd.h
format of files and directories.
.Pp
Every specified
.Ar directory
is used as a root directory and is recursively searched for files and
directories matching the filter.
If multiple directories are specified, the directories are merged together.
In case two files with the same path conflict, precedence is given to the file
in the root directory specified first.
.Pp
Hardlinks are detected and preserved to avoid data duplication.
Inode times are truncated to second precision due to format limitations.
Inodes are stored with uid 0 and gid 0 of the root user.
The format is not compressed but can be compressed externally if it is
decompressed during bootloading.
.Pp
.Xr initrdfs 8
can be used to view the files produced by
.Nm .
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl \-filter Ns "=" Ns Ar rule-file
Include only files and directories during the recursive search that matches
rules in the
.Ar rule-file
in the format specified under
.Sx FILTER RULES .
.It Fl \-format Ns "=" Ns Ar format
Produce the archive in the specified format.
This is for forward compatibility and only
.Sy sortix-initrd-2
is supported.
.Sy default
is an alias for the newest format
.Sy sortix-initrd-2 .
.Nm
will default to a newer format when one is introduced and this allows
.Nm
to support old callers during the transitional period.
.It Fl \-manifest Ns "=" Ns Ar manifest-file
Include only files and directories during the recursive search whose path
exactly matches a line in the
.Ar manifest-file .
.It Fl o , Fl \-output Ns "=" Ns Ar destination
Store the produced
.Xr initrd 7
at the specified
.Ar destination .
.El
.Sh FILTER RULES
The rule format is line based and leading whitespace is skipped.
Lines starting with a
.Li #
character are ignored as comments.
The first word on a line must be one of the following commands and the rest of
the line is its parameter.
Trailing whitespace is not ignored.
.Bl -tag -width "12345678"
.It Sy default Ar boolean
The
.Ar boolean
parameter is either
.Sy true
or
.Sy false
and determines whether a file or directory is included if no other rules match
it.
This defaults to
.Sy true .
.It Sy include Ar path
Include the file or directory if it matches
.Ar path .
.It Sy exclude Ar path
Exclude the file or directory if it matches
.Ar path .
.El
.Pp
The rules are checked on the paths relative to the root directories during the
recursive descent.
The last rule to match a path decides whether it is included or not.
Directory are not descended into if they are excluded.
The pattern patch is simple and matches paths exactly.
.Sh EXIT STATUS
.Nm
will exit 0 on success and non-zero otherwise.
.Sh EXAMPLES
.Bd -literal
# By default include everything except these directories:
exclude /dev
exclude /src/sysroot
exclude /tmp
.Ed
.Sh SEE ALSO
.Xr initrd 7 ,
.Xr kernel 7 ,
.Xr initrdfs 8 ,
.Xr update-initrd 8
.Sh BUGS
The path pattern matching should be upgraded to use
.Xr fnmatch 3 .
The initrd format does not losslessly represent the Sortix
.Li struct stat .

View File

@ -1,776 +0,0 @@
/*
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* mkinitrd.c
* Produces a simple ramdisk filesystem readable by the Sortix kernel.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <dirent.h>
#include <endian.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <ioleast.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sortix/initrd.h>
#define DEFAULT_FORMAT "sortix-initrd-2"
#include "rules.h"
#include "serialize.h"
uint32_t HostModeToInitRD(mode_t mode)
{
uint32_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
if ( S_ISVTX & mode ) { result |= INITRD_S_ISVTX; }
if ( S_ISSOCK(mode) ) { result |= INITRD_S_IFSOCK; }
if ( S_ISLNK(mode) ) { result |= INITRD_S_IFLNK; }
if ( S_ISREG(mode) ) { result |= INITRD_S_IFREG; }
if ( S_ISBLK(mode) ) { result |= INITRD_S_IFBLK; }
if ( S_ISDIR(mode) ) { result |= INITRD_S_IFDIR; }
if ( S_ISCHR(mode) ) { result |= INITRD_S_IFCHR; }
if ( S_ISFIFO(mode) ) { result |= INITRD_S_IFIFO; }
return result;
}
mode_t InitRDModeToHost(uint32_t mode)
{
mode_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
if ( INITRD_S_ISVTX & mode ) { result |= S_ISVTX; }
if ( INITRD_S_ISSOCK(mode) ) { result |= S_IFSOCK; }
if ( INITRD_S_ISLNK(mode) ) { result |= S_IFLNK; }
if ( INITRD_S_ISREG(mode) ) { result |= S_IFREG; }
if ( INITRD_S_ISBLK(mode) ) { result |= S_IFBLK; }
if ( INITRD_S_ISDIR(mode) ) { result |= S_IFDIR; }
if ( INITRD_S_ISCHR(mode) ) { result |= S_IFCHR; }
if ( INITRD_S_ISFIFO(mode) ) { result |= S_IFIFO; }
return result;
}
struct Node;
struct DirEntry;
struct DirEntry
{
char* name;
struct Node* node;
};
int DirEntryCompare(const struct DirEntry* a, const struct DirEntry* b)
{
return strcmp(a->name, b->name);
}
int DirEntryCompareIndirect(const void* a_ptr, const void* b_ptr)
{
const struct DirEntry* a = (const struct DirEntry*) a_ptr;
const struct DirEntry* b = (const struct DirEntry*) b_ptr;
return DirEntryCompare(a, b);
}
struct Node
{
char* path;
uint32_t ino;
uint32_t nlink;
size_t direntsused;
size_t direntslength;
struct DirEntry* dirents;
mode_t mode;
time_t ctime;
time_t mtime;
bool written;
size_t refcount;
};
void FreeNode(struct Node* node)
{
if ( !node )
return;
if ( 1 < node->nlink ) { node->nlink--; return; }
for ( size_t i = 0; i < node->direntsused; i++ )
{
struct DirEntry* entry = node->dirents + i;
if ( !entry->name )
continue;
if ( strcmp(entry->name, ".") != 0 && strcmp(entry->name, "..") != 0 )
{
if ( --entry->node->refcount == 0 )
FreeNode(entry->node);
}
free(entry->name);
}
free(node->dirents);
free(node->path);
free(node);
}
struct CacheEntry
{
ino_t ino;
dev_t dev;
struct Node* node;
};
static size_t cacheused = 0;
static size_t cachelen = 0;
static struct CacheEntry* cache = NULL;
struct Node* LookupCache(dev_t dev, ino_t ino)
{
for ( size_t i = 0; i < cacheused; i++ )
if ( cache[i].dev == dev && cache[i].ino == ino )
return cache[i].node;
return NULL;
}
bool AddToCache(struct Node* node, dev_t dev, ino_t ino)
{
if ( cacheused == cachelen )
{
size_t newcachelen = cachelen ? 2 * cachelen : 256;
size_t newcachesize = newcachelen * sizeof(struct CacheEntry);
struct CacheEntry* newcache =
(struct CacheEntry*) realloc(cache, newcachesize);
if ( !newcache )
return false;
cache = newcache;
cachelen = newcachelen;
}
size_t index = cacheused++;
cache[index].ino = ino;
cache[index].dev = dev;
cache[index].node = node;
return true;
}
struct Node* RecursiveSearch(const char* real_path, const char* virt_path,
uint32_t* ino, struct Node* parent)
{
printf("%s\n", virt_path);
fflush(stdout);
if ( virt_path[0] == '/' && !virt_path[1] )
virt_path = "";
struct stat st;
if ( lstat(real_path, &st) != 0 )
{
warn("stat: %s", real_path);
return NULL;
}
if ( !S_ISDIR(st.st_mode) && 2 <= st.st_nlink )
{
struct Node* cached = LookupCache(st.st_dev, st.st_ino);
if ( cached )
return cached->nlink++, cached->refcount++, cached;
}
struct Node* node = (struct Node*) calloc(1, sizeof(struct Node));
if ( !node )
return NULL;
node->nlink = 1;
node->refcount = 1;
node->mode = st.st_mode;
node->ino = (*ino)++;
node->ctime = st.st_ctim.tv_sec;
node->mtime = st.st_mtim.tv_sec;
char* real_path_clone = strdup(real_path);
if ( !real_path_clone )
{
warn("strdup");
free(node);
return NULL;
}
node->path = real_path_clone;
if ( !S_ISDIR(st.st_mode))
{
if ( 2 <= st.st_nlink && !AddToCache(node, st.st_dev, st.st_ino) )
{
free(real_path_clone);
free(node);
return NULL;
}
return node;
}
DIR* dir = opendir(real_path);
if ( !dir )
{
warn("opendir: %s", real_path);
FreeNode(node);
return NULL;
}
size_t real_path_len = strlen(real_path);
size_t virt_path_len = strlen(virt_path);
bool successful = true;
struct dirent* entry;
while ( (entry = readdir(dir)) )
{
size_t namelen = strlen(entry->d_name);
size_t virt_subpath_len = virt_path_len + 1 + namelen;
char* virt_subpath = (char*) malloc(virt_subpath_len+1);
if ( !virt_subpath )
{
warn("malloc");
successful = false;
break;
}
stpcpy(stpcpy(stpcpy(virt_subpath, virt_path), "/"), entry->d_name);
if ( strcmp(entry->d_name, ".") != 0 &&
strcmp(entry->d_name, "..") != 0 &&
!IncludesPath(virt_subpath) )
{
free(virt_subpath);
continue;
}
size_t real_subpath_len = real_path_len + 1 + namelen;
char* real_subpath = (char*) malloc(real_subpath_len+1);
if ( !real_subpath )
{
free(virt_subpath);
warn("malloc");
successful = false;
break;
}
stpcpy(stpcpy(stpcpy(real_subpath, real_path), "/"), entry->d_name);
struct Node* child = NULL;
if ( !strcmp(entry->d_name, ".") )
child = node;
if ( !strcmp(entry->d_name, "..") )
child = parent ? parent : node;
if ( !child )
child = RecursiveSearch(real_subpath, virt_subpath, ino, node);
free(real_subpath);
free(virt_subpath);
if ( !child )
{
successful = false;
break;
}
if ( node->direntsused == node->direntslength )
{
size_t oldlength = node->direntslength;
size_t newlength = oldlength ? 2 * oldlength : 8;
size_t newsize = sizeof(struct DirEntry) * newlength;
struct DirEntry* newdirents = (struct DirEntry*) realloc(node->dirents, newsize);
if ( !newdirents )
{
warn("realloc");
successful = false;
break;
}
node->dirents = newdirents;
node->direntslength = newlength;
}
char* nameclone = strdup(entry->d_name);
if ( !nameclone )
{
warn("strdup");
successful = false;
break;
}
struct DirEntry* entry = node->dirents + node->direntsused++;
entry->name = nameclone;
entry->node = child;
}
closedir(dir);
if ( !successful )
{
FreeNode(node);
return NULL;
}
qsort(node->dirents, node->direntsused, sizeof(struct DirEntry),
DirEntryCompareIndirect);
return node;
}
struct Node* MergeNodes(struct Node* a, struct Node* b)
{
if ( !S_ISDIR(a->mode) || !S_ISDIR(b->mode) )
{
FreeNode(b);
return a;
}
size_t dirents_used = 0;
size_t dirents_length = a->direntsused + b->direntsused;
struct DirEntry* dirents = (struct DirEntry*)
malloc(sizeof(struct DirEntry) * dirents_length);
if ( !dirents )
{
warn("malloc");
FreeNode(a);
FreeNode(b);
return NULL;
}
bool failure = false;
size_t ai = 0;
size_t bi = 0;
while ( ai != a->direntsused || bi != b->direntsused )
{
if ( bi == b->direntsused ||
(ai != a->direntsused &&
bi != b->direntsused &&
DirEntryCompare(&a->dirents[ai], &b->dirents[bi]) < 0) )
{
dirents[dirents_used++] = a->dirents[ai];
a->dirents[ai].name = NULL;
a->dirents[ai].node = NULL;
ai++;
continue;
}
if ( ai == a->direntsused ||
(ai != a->direntsused &&
bi != b->direntsused &&
DirEntryCompare(&a->dirents[ai], &b->dirents[bi]) > 0) )
{
dirents[dirents_used++] = b->dirents[bi];
for ( size_t i = 0; i < b->dirents[bi].node->direntsused; i++ )
{
if ( strcmp(b->dirents[bi].node->dirents[i].name, "..") != 0 )
continue;
b->dirents[bi].node->dirents[i].node = a;
}
b->dirents[bi].name = NULL;
b->dirents[bi].node = NULL;
bi++;
continue;
}
const char* name = a->dirents[ai].name;
dirents[dirents_used].name = a->dirents[ai].name;
if ( !strcmp(name, ".") || !strcmp(name, "..") )
dirents[dirents_used].node = a->dirents[ai].node;
else
{
dirents[dirents_used].node =
MergeNodes(a->dirents[ai].node, b->dirents[bi].node);
if ( !dirents[dirents_used].node )
failure = true;
}
dirents_used++;
a->dirents[ai].name = NULL;
a->dirents[ai].node = NULL;
ai++;
free(b->dirents[bi].name);
b->dirents[bi].name = NULL;
b->dirents[bi].node = NULL;
bi++;
}
free(a->dirents);
a->dirents = dirents;
a->direntsused = dirents_used;
a->direntslength = dirents_length;
b->direntsused = 0;
FreeNode(b);
if ( failure )
return FreeNode(b), (struct Node*) NULL;
return a;
}
bool WriteNode(struct initrd_superblock* sb, int fd, const char* outputname,
struct Node* node)
{
if ( node->written )
return true;
uint32_t filesize = 0;
uint32_t origfssize = sb->fssize;
uint32_t dataoff = origfssize;
uint32_t filestart = dataoff;
if ( S_ISLNK(node->mode) ) // Symbolic link
{
const size_t NAME_SIZE = 1024UL;
char name[NAME_SIZE];
ssize_t namelen = readlink(node->path, name, NAME_SIZE);
if ( namelen < 0 )
return warn("readlink: %s", node->path), false;
filesize = (uint32_t) namelen;
if ( pwriteall(fd, name, filesize, dataoff) < filesize )
return warn("read: %s", node->path), false;
dataoff += filesize;
}
else if ( S_ISREG(node->mode) ) // Regular file
{
int nodefd = open(node->path, O_RDONLY);
if ( nodefd < 0 )
return warn("open: %s", node->path), false;
const size_t BUFFER_SIZE = 16UL * 1024UL;
uint8_t buffer[BUFFER_SIZE];
ssize_t amount;
while ( 0 < (amount = read(nodefd, buffer, BUFFER_SIZE)) )
{
if ( pwriteall(fd, buffer, amount, dataoff) < (size_t) amount )
{
close(nodefd);
return warn("write: %s", outputname), false;
}
dataoff += amount;
filesize += amount;
}
close(nodefd);
if ( amount < 0 )
return warn("read: %s", node->path), false;
}
else if ( S_ISDIR(node->mode) ) // Directory
{
for ( size_t i = 0; i < node->direntsused; i++ )
{
struct DirEntry* entry = node->dirents + i;
const char* name = entry->name;
size_t namelen = strlen(entry->name);
struct initrd_dirent dirent;
dirent.inode = entry->node->ino;
dirent.namelen = (uint16_t) namelen;
dirent.reclen = sizeof(dirent) + dirent.namelen + 1;
dirent.reclen = (dirent.reclen+3)/4*4; // Align entries.
size_t entsize = sizeof(dirent);
export_initrd_dirent(&dirent);
assert((dataoff & (alignof(dirent)-1)) == 0 );
ssize_t hdramt = pwriteall(fd, &dirent, entsize, dataoff);
import_initrd_dirent(&dirent);
ssize_t nameamt = pwriteall(fd, name, namelen+1, dataoff + entsize);
if ( hdramt < (ssize_t) entsize || nameamt < (ssize_t) (namelen+1) )
return warn("write: %s", outputname), false;
size_t padding = dirent.reclen - (entsize + (namelen+1));
for ( size_t n = 0; n < padding; n++ )
{
uint8_t nul = 0;
if ( pwrite(fd, &nul, 1, dataoff+entsize+namelen+1+n) != 1 )
return warn("write: %s", outputname), false;
}
filesize += dirent.reclen;
dataoff += dirent.reclen;
}
}
struct initrd_inode inode;
inode.mode = HostModeToInitRD(node->mode);
inode.uid = 0;
inode.gid = 0;
inode.nlink = node->nlink;
inode.ctime = (uint64_t) node->ctime;
inode.mtime = (uint64_t) node->mtime;
inode.dataoffset = filestart;
inode.size = filesize;
uint32_t inodepos = sb->inodeoffset + node->ino * sb->inodesize;
uint32_t inodesize = sizeof(inode);
export_initrd_inode(&inode);
assert((inodepos & (alignof(inode)-1)) == 0 );
if ( pwriteall(fd, &inode, inodesize, inodepos) < inodesize )
return warn("write: %s", outputname), false;
import_initrd_inode(&inode);
uint32_t increment = dataoff - origfssize;
sb->fssize += increment;
sb->fssize = (sb->fssize+7)/8*8; // Align upwards.
return node->written = true;
}
bool WriteNodeRecursive(struct initrd_superblock* sb, int fd,
const char* outputname, struct Node* node)
{
if ( !WriteNode(sb, fd, outputname, node) )
return false;
if ( !S_ISDIR(node->mode) )
return true;
for ( size_t i = 0; i < node->direntsused; i++ )
{
struct DirEntry* entry = node->dirents + i;
const char* name = entry->name;
struct Node* child = entry->node;
if ( !strcmp(name, ".") || !strcmp(name, ".." ) )
continue;
if ( !WriteNodeRecursive(sb, fd, outputname, child) )
return false;
}
return true;
}
bool FormatFD(const char* outputname, int fd, uint32_t inodecount,
struct Node* root)
{
struct initrd_superblock sb;
memset(&sb, 0, sizeof(sb));
strncpy(sb.magic, "sortix-initrd-2", sizeof(sb.magic));
sb.revision = 0;
sb.fssize = sizeof(sb);
sb.inodesize = sizeof(struct initrd_inode);
sb.inodeoffset = sizeof(sb);
sb.inodecount = inodecount;
sb.root = root->ino;
uint32_t inodebytecount = sb.inodesize * sb.inodecount;
sb.fssize += inodebytecount;
sb.fssize = (sb.fssize+7)/8*8; // Align upwards.
if ( !WriteNodeRecursive(&sb, fd, outputname, root) )
return false;
sb.sumalgorithm = INITRD_ALGO_NONE;
sb.sumsize = 0;
sb.fssize = (sb.fssize+3)/4*4; // Align upwards.
sb.fssize += sb.sumsize;
export_initrd_superblock(&sb);
if ( pwriteall(fd, &sb, sizeof(sb), 0) < sizeof(sb) )
{
warn("write: %s", outputname);
return false;
}
import_initrd_superblock(&sb);
if ( ftruncate(fd, sb.fssize) < 0 )
{
warn("truncate: %s", outputname);
return false;
}
return true;
}
bool Format(const char* pathname, uint32_t inodecount, struct Node* root)
{
int fd = open(pathname, O_RDWR | O_CREAT | O_TRUNC, 0666);
bool result = FormatFD(pathname, fd, inodecount, root);
close(fd);
return result;
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
bool get_option_variable(const char* option, char** varptr,
const char* arg, int argc, char** argv, int* ip,
const char* argv0)
{
size_t option_len = strlen(option);
if ( strncmp(option, arg, option_len) != 0 )
return false;
if ( arg[option_len] == '=' )
{
*varptr = strdup(arg + option_len + 1);
return true;
}
if ( arg[option_len] != '\0' )
return false;
if ( *ip + 1 == argc )
{
fprintf(stderr, "%s: expected operand after `%s'\n", argv0, option);
exit(1);
}
*varptr = strdup(argv[++*ip]), argv[*ip] = NULL;
return true;
}
#define GET_OPTION_VARIABLE(str, varptr) \
get_option_variable(str, varptr, arg, argc, argv, &i, argv0)
static void help(FILE* fp, const char* argv0)
{
fprintf(fp, "Usage: %s [OPTION]... ROOT... -o OUTPUT\n", argv0);
fprintf(fp, "Creates a init ramdisk for the Sortix kernel.\n");
fprintf(fp, "\n");
fprintf(fp, "Mandatory arguments to long options are mandatory for short options too.\n");
fprintf(fp, " --filter=FILE import filter rules from FILE\n");
fprintf(fp, " --format=FORMAT format version [%s]\n", DEFAULT_FORMAT);
fprintf(fp, " -o, --output=FILE write result to FILE\n");
fprintf(fp, " --help display this help and exit\n");
fprintf(fp, " --version output version information and exit\n");
}
static void version(FILE* fp, const char* argv0)
{
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
}
int main(int argc, char* argv[])
{
char* arg_filter = NULL;
char* arg_format = strdup(DEFAULT_FORMAT);
char* arg_manifest = NULL;
char* arg_output = NULL;
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
case 'o':
free(arg_output);
if ( *(arg+1) )
arg_output = strdup(arg + 1);
else
{
if ( i + 1 == argc )
{
warnx("option requires an argument -- 'o'");
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
exit(125);
}
arg_output = strdup(argv[i+1]);
argv[++i] = NULL;
}
arg = "o";
break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
help(stderr, argv0);
exit(1);
}
}
else if ( !strcmp(arg, "--help") )
help(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--version") )
version(stdout, argv0), exit(0);
else if ( GET_OPTION_VARIABLE("--filter", &arg_filter) )
{
FILE* fp = fopen(arg_filter, "r");
if ( !fp )
err(1, "%s", arg_filter);
if ( !AddRulesFromFile(fp, arg_filter) )
exit(1);
fclose(fp);
free(arg_filter);
arg_filter = NULL;
}
else if ( GET_OPTION_VARIABLE("--manifest", &arg_manifest) )
{
FILE* fp = fopen(arg_manifest, "r");
if ( !fp )
err(1, "%s", arg_manifest);
if ( !AddManifestFromFile(fp, arg_manifest) )
exit(1);
fclose(fp);
free(arg_manifest);
arg_manifest = NULL;
}
else if ( GET_OPTION_VARIABLE("--format", &arg_format) ) { }
else if ( GET_OPTION_VARIABLE("--output", &arg_output) ) { }
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
help(stderr, argv0);
exit(1);
}
}
if ( argc < 2 )
{
help(stdout, argv0);
exit(1);
}
compact_arguments(&argc, &argv);
if ( argc < 2 )
{
fprintf(stderr, "%s: No root specified\n", argv0),
help(stderr, argv0);
exit(1);
}
if ( !arg_output )
{
fprintf(stderr, "%s: No output file specified\n", argv0),
help(stderr, argv0);
exit(1);
}
const char* format = arg_format;
if ( !strcmp(format, "default") )
format = DEFAULT_FORMAT;
if ( strcmp(format, "sortix-initrd-2") != 0 )
{
fprintf(stderr, "%s: Unsupported format `%s'\n", argv0, format);
fprintf(stderr, "Try `%s --help' for more information.\n", argv0);
exit(1);
}
uint32_t inodecount = 1;
struct Node* root = NULL;
for ( int i = 1; i < argc; i++ )
{
struct Node* node = RecursiveSearch(argv[i], "/", &inodecount, NULL);
if ( !node )
exit(1);
if ( root )
root = MergeNodes(root, node);
else
root = node;
if ( !root )
exit(1);
}
if ( !Format(arg_output, inodecount, root) )
exit(1);
FreeNode(root);
return 0;
}

View File

@ -1,317 +0,0 @@
/*
* Copyright (c) 2013, 2015, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* rules.c
* Determines whether a given path is included in the filesystem.
*/
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "rules.h"
static struct InclusionRule** rules;
static size_t num_rules;
static size_t num_rules_allocated;
static bool default_inclusion = true;
static bool default_inclusion_determined;
static char** manifest;
static size_t manifest_used;
static size_t manifest_length;
static const char* SkipCharacters(const char* str, char c)
{
while ( *str == c)
str++;
return str;
}
// /usr/bin/foobar match /usr = true
// /usr/bin/foobar match usr = false
// ///usr////bin//foobar match //usr// = true
// ///usr////bin//foobar match //usr//./evince = false
// TODO: Should this support . and .. too?
static bool PathMatchesPattern(const char* path, const char* pattern)
{
bool last_was_slash = false;
while ( true )
{
if ( !*pattern )
return !*path || last_was_slash;
if ( (last_was_slash = *pattern == '/') )
{
if ( *path == '/' )
{
path = SkipCharacters(path, '/');
pattern = SkipCharacters(pattern, '/');
continue;
}
return false;
}
if ( *pattern++ != *path++ )
return false;
}
}
static int search_path(const void* a_ptr, const void* b_ptr)
{
const char* key = (const char*) a_ptr;
char* path = *(char**) b_ptr;
return strcmp(key, path);
}
bool IncludesPath(const char* path)
{
bool determined = false;
bool included = false;
for ( size_t i = 0; i < num_rules; i++ )
{
struct InclusionRule* rule = rules[i];
if ( !PathMatchesPattern(path, rule->pattern) )
continue;
switch ( rules[i]->rule )
{
case RULE_INCLUDE:
included = true;
determined = true;
break;
case RULE_EXCLUDE:
included = false;
determined = true;
break;
}
}
if ( !determined )
included = default_inclusion;
if ( !included )
return false;
if ( manifest_used &&
!bsearch(path, manifest, manifest_used, sizeof(char*), search_path) )
return false;
return true;
}
bool ChangeRulesAmount(size_t new_length)
{
size_t new_num_rules = new_length < num_rules ? new_length : num_rules;
for ( size_t i = new_num_rules; i < num_rules; i++ )
{
free(rules[i]->pattern);
free(rules[i]);
}
num_rules = new_num_rules;
struct InclusionRule** new_rules = (struct InclusionRule**)
malloc(sizeof(struct InclusionRule*) * new_length);
for ( size_t i = 0; i < new_length && i < num_rules; i++ )
new_rules[i] = rules[i];
free(rules); rules = new_rules;
num_rules_allocated = new_length;
return true;
}
bool AddRule(struct InclusionRule* rule)
{
if ( num_rules == num_rules_allocated )
{
size_t new_length = num_rules_allocated ? 2 * num_rules_allocated : 32;
if ( !ChangeRulesAmount(new_length) )
return false;
}
rules[num_rules++] = rule;
return true;
}
static const char* SkipWhitespace(const char* line)
{
while ( *line && isspace((unsigned char) *line) )
line++;
return line;
}
static bool IsLineComment(const char* line)
{
return !*line || *line == '#';
}
static const char* IsLineCommand(const char* line, const char* command)
{
while ( *line && isspace((unsigned char) *line) )
line++;
size_t cmdlen = strlen(command);
if ( strncmp(line, command, cmdlen) != 0 )
return NULL;
if ( line[cmdlen] && !isspace((unsigned char) line[cmdlen]) )
return NULL;
while ( line[cmdlen] && isspace((unsigned char) line[cmdlen]) )
cmdlen++;
return line + cmdlen;
}
bool AddRulesFromFile(FILE* fp, const char* fpname)
{
size_t rules_at_start = num_rules;
size_t line_size;
size_t line_num = 0;
char* mem = NULL;
ssize_t line_len;
while ( 0 < (line_len = getline(&mem, &line_size, fp)) )
{
char* line = mem;
line_num++;
if ( line[line_len-1] == '\n' )
line[--line_len] = '\0';
line = (char*) SkipWhitespace((char*) line);
if ( IsLineComment(line) )
continue;
const char* parameter;
if ( (parameter = IsLineCommand(line, "default")) )
{
bool value;
if ( !strcmp(parameter, "true") )
value = true;
else if ( !strcmp(parameter, "false") )
value = false;
else
{
warnx("%s:%zu: not a boolean '%s'", fpname, line_num, parameter);
goto error_out;
}
if ( !default_inclusion_determined )
default_inclusion = value,
default_inclusion_determined = true;
else
default_inclusion = default_inclusion || value;
}
else if ( (parameter = IsLineCommand(line, "exclude")) ||
(parameter = IsLineCommand(line, "include")) )
{
if ( !*parameter )
{
warnx("%s:%zu: no parameter given", fpname, line_num);
goto error_out;
}
const char* pattern = parameter;
enum InclusionRuleType type = line[0] == 'e' ? RULE_EXCLUDE : RULE_INCLUDE;
struct InclusionRule* rule =
(struct InclusionRule*) malloc(sizeof(struct InclusionRule));
rule->pattern = strdup(pattern);
rule->rule = type;
if ( !AddRule(rule) )
goto error_out_errno;
}
else
{
warnx("%s:%zu: line not understood: '%s'", fpname, line_num, line);
goto error_out;
}
}
if ( ferror(fp) )
{
error_out_errno:
warn("%s", fpname);
error_out:
free(mem);
ChangeRulesAmount(rules_at_start);
return false;
}
free(mem);
return true;
}
int compare_path(const void* a_ptr, const void* b_ptr)
{
const char* a = *(const char* const*) a_ptr;
const char* b = *(const char* const*) b_ptr;
return strcmp(a, b);
}
bool AddManifestPath(const char* path)
{
if ( manifest_used == manifest_length )
{
size_t new_length = 2 * manifest_length;
if ( new_length == 0 )
new_length = 64;
size_t new_size = new_length * sizeof(char*);
char** new_manifest = (char**) realloc(manifest, new_size);
if ( !new_manifest )
{
warn("malloc");
return false;
}
manifest = new_manifest;
manifest_length = new_length;
}
char* copy = strdup(path);
if ( !copy )
{
warn("malloc");
return false;
}
manifest[manifest_used++] = copy;
return true;
}
bool AddManifestFromFile(FILE* fp, const char* fpname)
{
char* line = NULL;
size_t line_size = 0;
ssize_t line_len;
while ( 0 < (line_len = getline(&line, &line_size, fp)) )
{
if ( line[line_len-1] == '\n' )
line[--line_len] = '\0';
if ( !AddManifestPath(line) )
return false;
}
free(line);
if ( ferror(fp) )
{
warn("%s", fpname);
return false;
}
if ( !AddManifestPath("/") ||
!AddManifestPath("/tix") ||
!AddManifestPath("/tix/manifest") )
return false;
char* fpname_copy = strdup(fpname);
if ( !fpname_copy )
{
warn("malloc");
return false;
}
const char* fpname_basename = basename(fpname_copy);
char* manifest_path;
if ( asprintf(&manifest_path, "/tix/manifest/%s", fpname_basename) < 0 )
{
free(fpname_copy);
warn("malloc");
return false;
}
free(fpname_copy);
if ( !AddManifestPath(manifest_path) )
return free(manifest_path), false;
free(manifest_path);
qsort(manifest, manifest_used, sizeof(char*), compare_path);
return true;
}

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