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
63 changed files with 2094 additions and 3577 deletions

View File

@ -30,7 +30,6 @@ iso9660 \
kblayout \
kblayout-compiler \
login \
mkinitrd \
nyan \
ping \
regress \
@ -79,11 +78,11 @@ include build-aux/dirs.mak
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
CHAIN_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).chain.initrd
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
@ -108,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
@ -116,7 +114,6 @@ clean-build-tools:
build-tools:
$(MAKE) -C carray
$(MAKE) -C kblayout-compiler
$(MAKE) -C mkinitrd
$(MAKE) -C sf
$(MAKE) -C tix
@ -124,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
@ -469,7 +465,8 @@ $(CHAIN_INITRD): $(CHAIN_INITRD).uuid sysroot
mkdir -p $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/init" $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/iso9660fs" $(CHAIN_INITRD).d/sbin
mkinitrd --format=sortix-initrd-2 $(CHAIN_INITRD).d -o $(CHAIN_INITRD)
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
@ -486,7 +483,6 @@ $(LIVE_INITRD): sysroot
(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/home
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:" && \
@ -496,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
@ -537,7 +537,6 @@ $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(CHAIN_INITRD) $(CHAIN_INITRD).
(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/home
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:" && \
@ -547,6 +546,7 @@ $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(CHAIN_INITRD) $(CHAIN_INITRD).
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`
@ -566,29 +566,29 @@ $(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
@ -624,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

@ -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
@ -117,10 +117,10 @@ if $mount; then
initrd=$(maybe_compressed boot/sortix.initrd)
initrds=$initrd
else
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)
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
@ -392,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

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

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

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>
@ -635,14 +635,14 @@ int fsmarshall_main(const char* argv0,
struct stat root_inode_st;
Inode* root_inode = fs->GetInode(fs->root_ino);
if ( !root_inode )
error(1, errno, "GetInode");
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 )
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);
@ -654,7 +654,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);
@ -672,7 +672,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;
}

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
@ -25,7 +25,6 @@
#include <endian.h>
#include <err.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
@ -216,7 +215,7 @@ int main(int argc, char* argv[])
int fd = open(device_path, O_RDONLY);
if ( fd < 0 )
error(1, errno, "`%s'", device_path);
err(1, "%s", device_path);
// Search for the Primary Volume Descriptor.
off_t block_size = 2048;
@ -249,16 +248,16 @@ int main(int argc, char* argv[])
Device* dev = new Device(fd, device_path, block_size);
if ( !dev )
error(1, errno, "malloc");
err(1, "malloc");
Filesystem* fs = new Filesystem(dev, pretend_mount_path, pvd_offset);
if ( !fs )
error(1, errno, "malloc");
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 )
error(1, errno, "GetInode");
err(1, "%s: GetInode", device_path);
fs->root_ino = 0;
uint32_t root_lba;
uint32_t root_size;

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) 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,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) 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

@ -202,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 )

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,150 +0,0 @@
/*
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ioleast.h
* Versions of {,p}{read,write} that don't return until it has returned as much
* data as requested, end of file, or an error occurs. This is sometimes needed
* as read(2) and write(2) is not always guaranteed to fill up the entire
* buffer or write it all.
*/
#ifndef SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
#define SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
#if defined(__sortix__) || defined(__sortix_libc__)
#include_next <ioleast.h>
#else
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#if !defined(EEOF) && defined(EIO)
#define EEOF EIO
#endif
__attribute__((unused)) static inline
size_t readleast(int fd, void* buf_ptr, size_t least, size_t max)
{
assert(least <= max);
unsigned char* buf = (unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = read(fd, buf + done, max - done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t writeleast(int fd, const void* buf_ptr, size_t least, size_t max)
{
assert(least <= max);
const unsigned char* buf = (const unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = write(fd, buf + done, max - done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t preadleast(int fd, void* buf_ptr, size_t least, size_t max, off_t off)
{
assert(least <= max);
unsigned char* buf = (unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = pread(fd, buf + done, max - done, off + done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t pwriteleast(int fd, const void* buf_ptr, size_t least, size_t max, off_t off)
{
assert(least <= max);
const unsigned char* buf = (const unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = pwrite(fd, buf + done, max - done, off + done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t readall(int fd, void* buf, size_t count)
{
return readleast(fd, buf, count, count);
}
__attribute__((unused)) static inline
size_t writeall(int fd, const void* buf, size_t count)
{
return writeleast(fd, buf, count, count);
}
__attribute__((unused)) static inline
size_t preadall(int fd, void* buf, size_t count, off_t off)
{
return preadleast(fd, buf, count, count, off);
}
__attribute__((unused)) static inline
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
{
return pwriteleast(fd, buf, count, count, off);
}
#endif
#endif

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

View File

@ -1,42 +0,0 @@
/*
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* rules.h
* Determines whether a given path is included in the filesystem.
*/
#ifndef RULES_H
#define RULES_H
enum InclusionRuleType
{
RULE_INCLUDE,
RULE_EXCLUDE,
};
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);
#endif

View File

@ -1,84 +0,0 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* serialize.c
* Import and export binary data structures
*/
#include <endian.h>
#include <sortix/initrd.h>
#include "serialize.h"
void import_initrd_superblock(struct initrd_superblock* obj)
{
obj->fssize = le32toh(obj->fssize);
obj->revision = le32toh(obj->revision);
obj->inodesize = le32toh(obj->inodesize);
obj->inodecount = le32toh(obj->inodecount);
obj->inodeoffset = le32toh(obj->inodeoffset);
obj->root = le32toh(obj->root);
obj->sumalgorithm = le32toh(obj->sumalgorithm);
obj->sumsize = le32toh(obj->sumsize);
}
void export_initrd_superblock(struct initrd_superblock* obj)
{
obj->fssize = htole32(obj->fssize);
obj->revision = htole32(obj->revision);
obj->inodesize = htole32(obj->inodesize);
obj->inodecount = htole32(obj->inodecount);
obj->inodeoffset = htole32(obj->inodeoffset);
obj->root = htole32(obj->root);
obj->sumalgorithm = htole32(obj->sumalgorithm);
obj->sumsize = htole32(obj->sumsize);
}
void import_initrd_inode(struct initrd_inode* obj)
{
obj->mode = le32toh(obj->mode);
obj->uid = le32toh(obj->uid);
obj->nlink = le32toh(obj->nlink);
obj->ctime = le64toh(obj->ctime);
obj->mtime = le64toh(obj->mtime);
obj->dataoffset = le32toh(obj->dataoffset);
obj->size = le32toh(obj->size);
}
void export_initrd_inode(struct initrd_inode* obj)
{
obj->mode = htole32(obj->mode);
obj->uid = htole32(obj->uid);
obj->nlink = htole32(obj->nlink);
obj->ctime = htole64(obj->ctime);
obj->mtime = htole64(obj->mtime);
obj->dataoffset = htole32(obj->dataoffset);
obj->size = htole32(obj->size);
}
void import_initrd_dirent(struct initrd_dirent* obj)
{
obj->inode = le32toh(obj->inode);
obj->reclen = le16toh(obj->reclen);
obj->namelen = le16toh(obj->namelen);
}
void export_initrd_dirent(struct initrd_dirent* obj)
{
obj->inode = htole32(obj->inode);
obj->reclen = htole16(obj->reclen);
obj->namelen = htole16(obj->namelen);
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* serialize.h
* Import and export binary data structures
*/
#ifndef SERIALIZE_H
#define SERIALIZE_H
#include <sortix/initrd.h>
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);
#endif

View File

@ -1,2 +1,2 @@
chmod +x -- '10_sortix'
chmod +x -- 'update-grub'
chmod +x -- 'util/grub.d/10_sortix.in'

View File

@ -1,77 +1,58 @@
diff -Paur --no-dereference -- grub.upstream/10_sortix grub/10_sortix
--- grub.upstream/10_sortix
+++ grub/10_sortix
@@ -0,0 +1,70 @@
+#!/bin/sh -e
+! [ -e /etc/sortix-release ] && exit 0
+. /etc/sortix-release
+find_mountpoint() {(
+ FILE="$1"
+ DEVICE=$(grub-probe -t device -- "$FILE")
+ while [ "$FILE" != "/" ]; do
+ PARENT="$(dirname -- "$FILE")"
+ if [ x"$(grub-probe -t device -- "$PARENT")" != x"$DEVICE" ]; then
+ echo "$FILE"
+ exit 0
+ fi
+ FILE="$PARENT"
+ done
+ echo "$FILE"
+ exit 0
+)}
+mountpoint_relative() {(
+ REL=""
+ FILE="$1"
+ DEVICE=$(grub-probe -t device -- "$FILE")
+ while [ "$FILE" != "/" ]; do
+ PARENT="$(dirname -- "$FILE")"
+ if [ x"$(grub-probe -t device -- "$PARENT")" != x"$DEVICE" ]; then
+ echo "$REL"
+ exit 0
+ fi
+ REL="/$(basename -- "$FILE")$REL"
+ FILE="$PARENT"
+ done
+ echo "$REL"
+ exit 0
+)}
+BOOT_MNT=$(find_mountpoint /boot)
+BOOT_REL=$(mountpoint_relative /boot)
+DEVICE=$(grub-probe -t device -- "$BOOT_MNT")
+FS_UUID=$(grub-probe -t fs_uuid -- "$BOOT_MNT")
+HINTS_STRING=$(grub-probe -t hints_string -- "$BOOT_MNT")
+PARTMAP=$(grub-probe -t partmap -- "$BOOT_MNT")
+FS=$(grub-probe -t fs -- "$BOOT_MNT")
+echo "Found $PRETTY_NAME on $DEVICE" >&2
+cat > "$0.cache" << EOF
+menuentry "$PRETTY_NAME (on $DEVICE)" --unrestricted {
+ insmod part_$PARTMAP
+ insmod $FS
+ search --no-floppy --fs-uuid --set=root $HINTS_STRING $FS_UUID
+ if [ -e $BOOT_REL/sortix.bin.xz ]; then
+ insmod xzio
+ multiboot $BOOT_REL/sortix.bin.xz
+ elif [ -e $BOOT_REL/sortix.bin.gz ]; then
+ insmod gzio
+ multiboot $BOOT_REL/sortix.bin.gz
+ else
+ multiboot $BOOT_REL/sortix.bin
+ fi
+ if [ -e $BOOT_REL/random.seed ]; then
+ module $BOOT_REL/random.seed --random-seed
+ fi
+ if [ -e $BOOT_REL/sortix.initrd.xz ]; then
+ insmod xzio
+ module $BOOT_REL/sortix.initrd.xz
+ elif [ -e $BOOT_REL/sortix.initrd.gz ]; then
+ insmod gzio
+ module $BOOT_REL/sortix.initrd.gz
+ else
+ module $BOOT_REL/sortix.initrd
+ fi
+}
+EOF
+cat "$0.cache"
diff -Paur --no-dereference -- grub.upstream/Makefile.in grub/Makefile.in
--- grub.upstream/Makefile.in
+++ grub/Makefile.in
@@ -1212,6 +1212,7 @@
util/grub.d/10_kfreebsd.in util/grub.d/10_illumos.in \
util/grub.d/10_netbsd.in util/grub.d/10_linux.in \
util/grub.d/10_xnu.in util/grub.d/20_linux_xen.in \
+ util/grub.d/10_sortix.in \
util/grub.d/30_os-prober.in util/grub.d/40_custom.in \
util/grub.d/41_custom.in util/grub-mkconfig.in \
util/grub-set-default.in util/grub-reboot.in \
@@ -2372,7 +2373,7 @@
CCASFLAGS_LIBRARY =
# Other variables
-grubconfdir = $(sysconfdir)/grub.d
+grubconfdir = $(sysconfdir)/default/grub.d
platformdir = $(pkglibdir)/$(target_cpu)-$(platform)
starfielddir = $(pkgdatadir)/themes/starfield
CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion
@@ -2440,6 +2441,7 @@
grubconf_SCRIPTS = 00_header $(am__append_57) $(am__append_61) \
$(am__append_65) $(am__append_69) $(am__append_73) \
$(am__append_77) $(am__append_81) $(am__append_85) \
+ 10_sortix \
30_os-prober 40_custom 41_custom
noinst_LIBRARIES = libgrubkern.a libgrubmods.a libgrubgcry.a
dist_noinst_DATA = grub-core/kern/disk_common.c \
@@ -2460,6 +2462,7 @@
util/grub.d/00_header.in $(am__append_60) $(am__append_64) \
$(am__append_68) $(am__append_72) $(am__append_76) \
$(am__append_80) $(am__append_84) $(am__append_88) \
+ util/grub.d/10_sortix.in \
util/grub.d/30_os-prober.in util/grub.d/40_custom.in \
util/grub.d/41_custom.in util/grub-mkconfig.in \
util/grub-set-default.in util/grub-reboot.in \
@@ -2640,6 +2643,7 @@
$(am__append_62) $(am__append_66) $(am__append_70) \
$(am__append_74) $(am__append_78) $(am__append_82) \
$(am__append_86) 30_os-prober 40_custom 41_custom \
+ 10_sortix \
$(am__append_89) grub-mkconfig $(am__append_90) \
grub-set-default $(am__append_91) grub-reboot \
grub-mkconfig_lib $(am__append_92) grub-kbdcomp grub-shell \
@@ -12086,6 +12090,10 @@
@COND_HOST_XNU_TRUE@ (for x in util/grub.d/10_xnu.in ; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
@COND_HOST_XNU_TRUE@ chmod a+x 10_xnu
+10_sortix: $(top_builddir)/config.status util/grub.d/10_sortix.in
+ (for x in util/grub.d/10_sortix.in ; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
+ chmod a+x 10_sortix
+
@COND_HOST_LINUX_TRUE@20_linux_xen: $(top_builddir)/config.status util/grub.d/20_linux_xen.in
@COND_HOST_LINUX_TRUE@ (for x in util/grub.d/20_linux_xen.in ; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
@COND_HOST_LINUX_TRUE@ chmod a+x 20_linux_xen
diff -Paur --no-dereference -- grub.upstream/build-aux/config.sub grub/build-aux/config.sub
--- grub.upstream/build-aux/config.sub
+++ grub/build-aux/config.sub
@ -226,6 +207,18 @@ diff -Paur --no-dereference -- grub.upstream/configure grub/configure
FREETYPE=$ac_cv_prog_FREETYPE
if test -n "$FREETYPE"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $FREETYPE" >&5
diff -Paur --no-dereference -- grub.upstream/grub-core/Makefile.in grub/grub-core/Makefile.in
--- grub.upstream/grub-core/Makefile.in
+++ grub/grub-core/Makefile.in
@@ -13658,7 +13658,7 @@
CCASFLAGS_LIBRARY = $(CCASFLAGS_PLATFORM)
# Other variables
-grubconfdir = $(sysconfdir)/grub.d
+grubconfdir = $(sysconfdir)/default/grub.d
platformdir = $(pkglibdir)/$(target_cpu)-$(platform)
starfielddir = $(pkgdatadir)/themes/starfield
CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion
diff -Paur --no-dereference -- grub.upstream/grub-core/lib/libgcrypt-grub/cipher/rijndael.c grub/grub-core/lib/libgcrypt-grub/cipher/rijndael.c
--- grub.upstream/grub-core/lib/libgcrypt-grub/cipher/rijndael.c
+++ grub/grub-core/lib/libgcrypt-grub/cipher/rijndael.c
@ -665,6 +658,18 @@ diff -Paur --no-dereference -- grub.upstream/update-grub grub/update-grub
@@ -0,0 +1,2 @@
+#!/bin/sh -e
+exec grub-mkconfig -o /boot/grub/grub.cfg "$@"
diff -Paur --no-dereference -- grub.upstream/util/bash-completion.d/Makefile.in grub/util/bash-completion.d/Makefile.in
--- grub.upstream/util/bash-completion.d/Makefile.in
+++ grub/util/bash-completion.d/Makefile.in
@@ -1022,7 +1022,7 @@
bash_completion_script = grub
EXTRA_DIST = $(bash_completion_source)
CLEANFILES = $(bash_completion_script) config.log
-bashcompletiondir = $(sysconfdir)/bash_completion.d
+bashcompletiondir = $(sysconfdir)/default/bash_completion.d
bashcompletion_DATA = $(bash_completion_script)
all: all-am
diff -Paur --no-dereference -- grub.upstream/util/getroot.c grub/util/getroot.c
--- grub.upstream/util/getroot.c
+++ grub/util/getroot.c
@ -707,23 +712,6 @@ diff -Paur --no-dereference -- grub.upstream/util/getroot.c grub/util/getroot.c
#else
/* Linux counts partitions uniformly, whether a BSD partition or a DOS
diff -Paur --no-dereference -- grub.upstream/util/grub.d/00_header.in grub/util/grub.d/00_header.in
--- grub.upstream/util/grub.d/00_header.in
+++ grub/util/grub.d/00_header.in
@@ -27,6 +27,13 @@
. "$pkgdatadir/grub-mkconfig_lib"
+if [ -f "/etc/grubpw" ]; then
+ echo 'insmod password_pbkdf2'
+ echo 'set superusers="root"'
+ echo "password_pbkdf2 root $(cat /etc/grubpw)"
+ echo
+fi
+
# Do this as early as possible, since other commands might depend on it.
# (e.g. the `loadfont' command might need lvm or raid modules)
for i in ${GRUB_PRELOAD_MODULES} ; do
diff -Paur --no-dereference -- grub.upstream/util/grub-fstest.c grub/util/grub-fstest.c
--- grub.upstream/util/grub-fstest.c
+++ grub/util/grub-fstest.c
@ -739,7 +727,15 @@ diff -Paur --no-dereference -- grub.upstream/util/grub-fstest.c grub/util/grub-f
diff -Paur --no-dereference -- grub.upstream/util/grub-mkconfig.in grub/util/grub-mkconfig.in
--- grub.upstream/util/grub-mkconfig.in
+++ grub/util/grub-mkconfig.in
@@ -102,27 +102,6 @@
@@ -38,6 +38,7 @@
grub_cfg=""
grub_mkconfig_dir="${sysconfdir}"/grub.d
+grub_mkconfig_default_dir="${sysconfdir}"/default/grub.d
self=`basename $0`
@@ -102,27 +103,6 @@
esac
done
@ -767,6 +763,196 @@ diff -Paur --no-dereference -- grub.upstream/util/grub-mkconfig.in grub/util/gru
set $grub_probe dummy
if test -f "$1"; then
:
@@ -147,8 +127,8 @@
GRUB_FS="$(stat -f --printf=%T / || echo unknown)"
fi
-if test -f ${sysconfdir}/default/grub ; then
- . ${sysconfdir}/default/grub
+if test -f ${sysconfdir}/grub ; then
+ . ${sysconfdir}/grub
fi
# XXX: should this be deprecated at some point?
@@ -211,6 +191,7 @@
GRUB_CMDLINE_NETBSD \
GRUB_CMDLINE_NETBSD_DEFAULT \
GRUB_CMDLINE_GNUMACH \
+ GRUB_CMDLINE_SORTIX \
GRUB_TERMINAL_INPUT \
GRUB_TERMINAL_OUTPUT \
GRUB_SERIAL_COMMAND \
@@ -243,12 +224,23 @@
# DO NOT EDIT THIS FILE
#
# It is automatically generated by $self using templates
-# from ${grub_mkconfig_dir} and settings from ${sysconfdir}/default/grub
+# from ${grub_mkconfig_dir} and ${grub_mkconfig_default_dir}
+# and settings from ${sysconfdir}/grub
#
EOF
-
-for i in "${grub_mkconfig_dir}"/* ; do
+# PATCH: /etc/grub.d overrides /etc/default/grub.d, although it's unsupported to
+# override the 10_sortix file and /etc/grub should be used if possible.
+for n in $({ if [ -d "${grub_mkconfig_dir}" ]; then
+ ls -- "${grub_mkconfig_dir}"
+ fi
+ if [ -d "${grub_mkconfig_default_dir}" ]; then
+ ls -- "${grub_mkconfig_default_dir}"
+ fi; } | LC_ALL=C sort -u); do
+ i="${grub_mkconfig_dir}/$n"
+ if [ ! -f "$i" -a -f "${grub_mkconfig_default_dir}/$n" ]; then
+ i="${grub_mkconfig_default_dir}/$n"
+ fi
case "$i" in
# emacsen backup files. FIXME: support other editors
*~) ;;
@@ -269,7 +261,7 @@
if ! ${grub_script_check} ${grub_cfg}.new; then
# TRANSLATORS: %s is replaced by filename
gettext_printf "Syntax errors are detected in generated GRUB config file.
-Ensure that there are no errors in /etc/default/grub
+Ensure that there are no errors in /etc/grub
and /etc/grub.d/* files or please file a bug report with
%s file attached." "${grub_cfg}.new" >&2
echo >&2
diff -Paur --no-dereference -- grub.upstream/util/grub.d/00_header.in grub/util/grub.d/00_header.in
--- grub.upstream/util/grub.d/00_header.in
+++ grub/util/grub.d/00_header.in
@@ -27,6 +27,13 @@
. "$pkgdatadir/grub-mkconfig_lib"
+if [ -f "/etc/grubpw" ]; then
+ echo 'insmod password_pbkdf2'
+ echo 'set superusers="root"'
+ echo "password_pbkdf2 root $(cat /etc/grubpw)"
+ echo
+fi
+
# Do this as early as possible, since other commands might depend on it.
# (e.g. the `loadfont' command might need lvm or raid modules)
for i in ${GRUB_PRELOAD_MODULES} ; do
diff -Paur --no-dereference -- grub.upstream/util/grub.d/10_sortix.in grub/util/grub.d/10_sortix.in
--- grub.upstream/util/grub.d/10_sortix.in
+++ grub/util/grub.d/10_sortix.in
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+set -e
+
+rm -f "$0.cache.new"
+
+# This script will run on the previous stable release during a sysmerge(8) with
+# a potentially old version of GRUB installed.
+
+# Detect if a system upgrade is ready and emit a bootloader entry using the new
+# script if we're not already it. This script provides a stable interface where
+# the new release helps the old system figure out how to boot the new system.
+PREFIX=
+BOOT_DIR=/boot
+UPGRADE=false
+if [ "$1" = --sysmerge ]; then
+ PREFIX=/sysmerge
+ BOOT_DIR=/boot/sysmerge
+ UPGRADE=true
+elif [ -e /sysmerge/tix/sysmerge.ready ]; then
+ # Write the upgrade menu entry before the old menu entry.
+ /sysmerge/libexec/sysmerge/grub --sysmerge > "$0.cache.new"
+fi
+
+# Allow this script to be run standalone outside of grub-mkconfig.
+if [ -z "$pkgdatadir" -a -f /etc/grub ]; then
+ . /etc/grub
+fi
+
+if [ ! -e "$PREFIX/etc/sortix-release" ]; then
+ exit 0
+fi
+. "$PREFIX/etc/sortix-release"
+
+if $UPGRADE; then
+ PRETTY_NAME="Upgrade to $PRETTY_NAME"
+fi
+
+find_mountpoint() {(
+ FILE="$1"
+ DEVICE=$(grub-probe -t device -- "$FILE")
+ while [ "$FILE" != "/" ]; do
+ PARENT="$(dirname -- "$FILE")"
+ if [ x"$(grub-probe -t device -- "$PARENT")" != x"$DEVICE" ]; then
+ echo "$FILE"
+ exit 0
+ fi
+ FILE="$PARENT"
+ done
+ echo "$FILE"
+ exit 0
+)}
+
+mountpoint_relative() {(
+ REL=""
+ FILE="$1"
+ DEVICE=$(grub-probe -t device -- "$FILE")
+ while [ "$FILE" != "/" ]; do
+ PARENT="$(dirname -- "$FILE")"
+ if [ x"$(grub-probe -t device -- "$PARENT")" != x"$DEVICE" ]; then
+ echo "$REL"
+ exit 0
+ fi
+ REL="/$(basename -- "$FILE")$REL"
+ FILE="$PARENT"
+ done
+ echo "$REL"
+ exit 0
+)}
+
+BOOT_MNT=$(find_mountpoint /boot)
+BOOT_REL=$(mountpoint_relative "$BOOT_DIR")
+OLD_BOOT_REL=$(mountpoint_relative /boot)
+DEVICE=$(grub-probe -t device -- "$BOOT_MNT")
+FS_UUID=$(grub-probe -t fs_uuid -- "$BOOT_MNT")
+HINTS_STRING=$(grub-probe -t hints_string -- "$BOOT_MNT")
+PARTMAP=$(grub-probe -t partmap -- "$BOOT_MNT")
+FS=$(grub-probe -t fs -- "$BOOT_MNT")
+
+echo "Found $PRETTY_NAME on $DEVICE" >&2
+cat >> "$0.cache.new" << EOF
+menuentry "$PRETTY_NAME (on $DEVICE)" --unrestricted {
+ insmod part_$PARTMAP
+ insmod $FS
+ search --no-floppy --fs-uuid --set=root $HINTS_STRING $FS_UUID
+ multiboot $BOOT_REL/sortix.bin $GRUB_CMDLINE_SORTIX
+ module $OLD_BOOT_REL/random.seed --random-seed
+ module $BOOT_REL/sortix.initrd
+}
+EOF
+mv "$0.cache.new" "$0.cache"
+cat "$0.cache"
diff -Paur --no-dereference -- grub.upstream/util/grub.d/README grub/util/grub.d/README
--- grub.upstream/util/grub.d/README
+++ grub/util/grub.d/README
@@ -1,11 +1,15 @@
-
-All executable files in this directory are processed in shell expansion order.
+All executable files in /etc/grub.d and /etc/grub.d/default are processed in
+shell expansion order.
00_*: Reserved for 00_header.
10_*: Native boot entries.
20_*: Third party apps (e.g. memtest86+).
+Files in /etc/grub.d override files in /etc/grub.d/default if they exist. It is
+unsupported to override the native bootloader file 10_sortix since the
+configuration will be updated across releases
+
The number namespace in-between is configurable by system installer and/or
administrator. For example, you can add an entry to boot another OS as
01_otheros, 11_otheros, etc, depending on the position you want it to occupy in
-the menu; and then adjust the default setting via /etc/default/grub.
+the menu; and then adjust the default setting via /etc/grub.
diff -Paur --no-dereference -- grub.upstream/util/misc.c grub/util/misc.c
--- grub.upstream/util/misc.c
+++ grub/util/misc.c

View File

@ -1,6 +1,5 @@
#!/bin/sh -e
cp update-grub "$TIX_INSTALL_DIR$EXEC_PREFIX/sbin/update-grub"
cp 10_sortix "$TIX_INSTALL_DIR$PREFIX/etc/grub.d/10_sortix"
if [ ! -e "$TIX_INSTALL_DIR$PREFIX/share/grub/unicode.pf2" ]; then
# Cheat as I'm not sure how to get this when cross-building.
[ -e /share/grub/unicode.pf2 ] &&

View File

@ -152,7 +152,7 @@ and if
is set to
.Sy no ,
then regenerate
.Pa /etc/grub.d/10_sortix.cache .
.Pa /etc/default/grub.d/10_sortix.cache .
.El
.Pp
The defaults will be used if

View File

@ -344,8 +344,6 @@ carray
.It
kblayout-compiler
.It
mkinitrd
.It
sf
.It
tix

View File

@ -69,6 +69,94 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
.Xr grep 1
for it after a release.
.Sh CHANGES
.Ss Support system upgrades and configuration in GRUB
The GRUB configuration now implements the
.Pa /etc
and
.Pa /etc/default
configuration split per
.Xr hier 7
and
.Xr sysmerge 8
now collaborates with system upgrades to boot them correctly.
Installations with manually configured bootloaders may need updating.
.Pp
Notably
.Pa /etc/default/grub
has been moved to
.Pa /etc/grub
since it is owned by the system administrator and there is no counterpart with
operating system provided defaults.
.Pp
Additionally
.Pa /etc/grub.d
has been moved into
.Pa /etc/default/grub.d
since it provides operating system defaults.
The system administrator can now create the
.Pa /etc/grub.d
directory manually to override files in
.Pa /etc/default/grub.d ,
although overriding
.Pa 10_sortix
is unsupported.
.Pp
.Pa 10_sortix
now supports the
.Pa GRUB_CMDLINE_SORTIX
variable in
.Pa /etc/grub
to specify kernel command line options.
.Pp
.Xr sysmerge 8
now colloborates with the
.Xr update-initrd 8
and
.Pa 10_sortix
scripts in the new system to prepare the upgrade system's kernel files in
.Pa /boot/sysmerge/ .
If the GRUB bootloader is accepted, then
.Pa /boot/grub/grub.cfg
is regenerated with a menu entry to perform the upgrade.
The
.Pa /boot/sortix.bin
and
.Pa /boot/sortix.initrd
files are no longer replaced with the new versions and continue to boot the old
system without upgrading.
Any manual bootloader configuration must be configured to instead load the
kernel and initrd from
.Pa /boot/sysmerge/
(if they exist).
The
.Pa /etc/default/grub.d/10_sortix.cache
file will demonstrate how to boot the new system using GRUB after starting a
.Xr sysmerge 8
(if the grub port is installed).
.Pp
An upgrade hook will delete the obsolete stale
.Pa /etc/grub.d/10_sortix.cache
file.
.Ss Third generation Tix
The tix binary package format has upgraded from generation 2 to 3 and has a new
internal layout that can be directly extracted into the filesystem.
The metatdata in
.Pa /tix
is now using the
.Xr tix-vars 1
format in the style of
.Xr port 5 .
.Pp
Tix must be upgraded to build the new ports:
.Bd -literal
cd /src/tix &&
make clean &&
make install
.Ed
.Pp
An upgrade hook will migrate the
.Pa /tix
metadata to tix generation 3.
.Ss Add include and comment support to passwd(5) and group(5)
The
.Xr passwd 5
@ -295,7 +383,7 @@ The kernel will issue a security warning if it was booted without a random seed,
unless the kernel command line contains
.Fl \-no-random-seed .
The GRUB port has been updated with an improved
.Pa /etc/grub.d/10_sortix
.Pa /etc/default/grub.d/10_sortix
script that will automatically emit the appropriate GRUB commands.
.Pp
Users using the included GRUB will need to update to the latest GRUB port
@ -321,9 +409,9 @@ If the GRUB port is installed, but not used, then if that port is updated with
or
.Xr sysmerge 8
or manually, the
.Pa /etc/grub.d/10_sortix
.Pa /etc/default/grub.d/10_sortix
script can be invoked, which will generate a
.Pa /etc/grub.d/10_sortix.cache
.Pa /etc/default/grub.d/10_sortix.cache
fragment that can be spliced into the configuration of another GRUB
installation.
.Pp

View File

@ -6,7 +6,7 @@
.Nd layout of filesystems
.Sh DESCRIPTION
The filesystem hierarchy is as follows:
.Bl -tag -width "1234567891012"
.Bl -tag -width "/boot/sysmerge"
.It Pa /
Root directory.
.It Pa /bin
@ -16,6 +16,10 @@ Boot programs,
.Xr kernel 7 ,
.Xr initrd 7 ,
bootloader.
.It Pa /boot/sysmerge
Temporary area for
.Xr sysmerge 8
delayed upgrades.
.It Pa /dev
Devices.
.It Pa /etc

View File

@ -8,7 +8,9 @@
.Pa /boot/sortix.initrd
.Sh DESCRIPTION
.Pa /boot/sortix.initrd
is an archive of a minimal userland loaded by the bootloader and passed to the
is a
.Xr tar 1
archive of a minimal userland loaded by the bootloader and passed to the
.Xr kernel 7
that extracts it into the initial kernel memory root filesystem.
The kernel invokes the
@ -16,20 +18,13 @@ The kernel invokes the
extracted from the initrd as
.Pa /sbin/init .
.Pp
The initrd is in the custom format made by
.Xr mkinitrd 8
and can be viewed with
.Xr initrdfs 8 .
.Pp
The
.Xr update-initrd 8
program creates a minimal initrd that locates the root filesystem and chain
boots it as described in
.Xr init 8 .
.Sh SEE ALSO
.Xr initrd 7 ,
.Xr tar 1 ,
.Xr kernel 7 ,
.Xr init 8 ,
.Xr initrdfs 8 ,
.Xr mkinitrd 8 ,
.Xr update-initrd 8

View File

@ -529,7 +529,7 @@ If the included GRUB bootloader is used, after making the above edit, run
.Xr update-grub 8
within the new installation to regenerate the bootloader configuration.
Note that
.Pa /etc/grub.d/10_sortix
.Pa /etc/default/grub.d/10_sortix
is part of the GRUB package and local changes will be undone when the GRUB
package is updated or reinstalled, in which case you must make this change again
and run
@ -537,12 +537,12 @@ and run
again.
.Pp
If the included GRUB bootloader is not used, but instead the
.Pa /etc/grub.d/10_sortix.cache
.Pa /etc/default/grub.d/10_sortix.cache
fragment is spliced into another GRUB installation, make the above change and
then run the
.Pa /etc/grub.d/10_sortix
.Pa /etc/default/grub.d/10_sortix
command and use the freshly regenerated
.Pa /etc/grub.d/10_sortix.cache
.Pa /etc/default/grub.d/10_sortix.cache
fragment instead.
.Sh SEE ALSO
.Xr chkblayout 1 ,

View File

@ -80,10 +80,6 @@ The format of each multiboot module is automatically detected by default:
.Pp
.Bl -bullet -compact
.It
Initialization ramdisks produced by
.Xr mkinitrd 8
are extracted in the root directory.
.It
.Xr tar 7
archives in the ustar format are extracted into the root directory.
The bootloader must already have decompressed the archive.

View File

@ -384,13 +384,13 @@ as a multiboot module with the
command line.
.Pp
Load
.Pa /boot/system.initrd.xz ,
.Pa /boot/src.initrd.xz
.Pa /boot/system.tar.xz ,
.Pa /boot/src.tar.xz
(if
.Sy $enable_src ) ,
.Pa /boot/live.initrd.xz ,
.Pa /boot/live.tar.xz ,
and
.Pa /boot/overlay.initrd.xz
.Pa /boot/overlay.tar.xz
(only if a
.Pa sysroot-overlay
directory existed when making the release .iso)

View File

@ -95,8 +95,8 @@ command shows the current memory usage.
.Ss Third Party Software
Releases come with useful third party software installed.
The
.Pa /tix/installed.list
file lists all currently installed ports.
.Pa /tix/tixinfo
directory lists all currently installed ports.
.Ss Source Code
Releases come full with the system source code in
.Pa /src
@ -151,9 +151,6 @@ It is possible to transfer files over serial devices as described in
.Ss Development
The system is self-hosting and is capable of building itself as described in
.Xr development 7 .
Ports are cross-compiled as described in
.Xr cross-development 7 ,
but it is becoming feasible to build a large number of them natively.
.Sh SEE ALSO
.Xr cross-development 7 ,
.Xr development 7 ,

View File

View File

@ -46,6 +46,9 @@ install: all
install sysinstall $(DESTDIR)$(SBINDIR)
install sysmerge $(DESTDIR)$(SBINDIR)
install sysupgrade $(DESTDIR)$(SBINDIR)
mkdir -p $(DESTDIR)$(LIBEXECDIR)/sysmerge
install prepare $(DESTDIR)$(LIBEXECDIR)/sysmerge
install grub $(DESTDIR)$(LIBEXECDIR)/sysmerge
mkdir -p $(DESTDIR)$(MANDIR)/man8
cp sysinstall.8 $(DESTDIR)$(MANDIR)/man8/sysinstall.8
cp sysmerge.8 $(DESTDIR)$(MANDIR)/man8/sysmerge.8
@ -58,6 +61,8 @@ install: all
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-init
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-passwd
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-group
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-tix3g
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-grub-cache
sysinstall: $(SYSINSTALL_OBJS)
$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount -ldisplay

View File

@ -32,6 +32,7 @@
int execute(const char* const* argv, const char* flags, ...)
{
const char* chroot = NULL;
bool _exit_instead = false;
bool exit_on_failure = false;
bool foreground = false;
@ -51,6 +52,7 @@ int execute(const char* const* argv, const char* flags, ...)
switch ( flags[i] )
{
case '_': _exit_instead = true; break;
case 'c': chroot = va_arg(ap, const char*); break;
case 'e': exit_on_failure = true; break;
case 'f': foreground = true; break;
case 'g': gid_set = true; gid = va_arg(ap, gid_t); break;
@ -63,6 +65,8 @@ int execute(const char* const* argv, const char* flags, ...)
}
}
va_end(ap);
if ( chroot && !strcmp(chroot, "/") )
chroot = NULL;
sigset_t oldset, sigttou;
if ( foreground )
{
@ -88,6 +92,25 @@ int execute(const char* const* argv, const char* flags, ...)
}
if ( child_pid == 0 )
{
if ( chroot )
{
int argc = 0;
while ( argv[argc] )
argc++;
const char** new_argv = calloc(argc + 4, sizeof(const char*));
if ( !new_argv )
{
if ( !quiet_stderr )
warn("malloc");
_exit(2);
}
new_argv[0] = "chroot";
new_argv[1] = "-d";
new_argv[2] = chroot;
for ( int i = 0; i < argc; i++ )
new_argv[3 + i] = argv[i];
argv = new_argv;
}
if ( gid_set )
{
setegid(gid);

29
sysinstall/grub Executable file
View File

@ -0,0 +1,29 @@
#!/bin/sh
# Copyright (c) 2023 Jonas 'Sortie' Termansen.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# grub
# Output the GRUB configuration needed to boot the sysmerge(8) --wait upgrade.
# This script will run on the previous stable release during a sysmerge(8) as a
# high-level entry point to help the previous system generate the grub config to
# boot the new system. This script minimizes the interface between the old and
# new system to allow change. This script is ordinarily invoked by 10_sortix
# from the grub port.
set -e
# The old system has copied the new kernel and initrd files from /sysmerge/boot
# to /boot/sysmerge and we need to configure grub to boot them.
/sysmerge/etc/default/grub.d/10_sortix --sysmerge

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017, 2018, 2020, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2016, 2017, 2018, 2020, 2021, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -19,9 +19,11 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -111,6 +113,74 @@ static bool hook_needs_to_be_run(const char* source_prefix,
return result;
}
// If a hook needs to run a finalization step after the upgrade, it can leave
// behind a .running file in the hooks directory, which is deleted once the
// hook has run.
__attribute__((used))
static char* hook_finalization_file(const char* target_prefix, const char* hook)
{
char* target_path;
if ( asprintf(&target_path, "%s/share/sysinstall/hooks/%s.running",
target_prefix, hook) < 0 )
{
warn("malloc");
_exit(2);
}
return target_path;
}
__attribute__((used))
static void hook_want_finalization(const char* target_prefix, const char* hook)
{
// TODO: After releasing Sortix 1.1, remove compatibility for Sortix 1.0
// not having the /share/sysinstall/hooks directory.
char* path;
if ( asprintf(&path, "%s/share/sysinstall", target_prefix) < 0 )
{
warn("malloc");
_exit(2);
}
mkdir(path, 0755);
free(path);
if ( asprintf(&path, "%s/share/sysinstall/hooks", target_prefix) < 0 )
{
warn("malloc");
_exit(2);
}
mkdir(path, 0755);
free(path);
char* target_path = hook_finalization_file(target_prefix, hook);
int fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if ( fd < 0 )
{
warn("%s", target_path);
_exit(2);
}
close(fd);
free(target_path);
}
__attribute__((used))
static bool hook_needs_finalization(const char* target_prefix, const char* hook)
{
char* target_path = hook_finalization_file(target_prefix, hook);
bool result = !access_or_die(target_path, F_OK);
free(target_path);
return result;
}
__attribute__((used))
static void hook_did_finalization(const char* target_prefix, const char* hook)
{
char* target_path = hook_finalization_file(target_prefix, hook);
if ( unlink(target_path) && errno != ENOENT )
{
warn("%s", target_path);
_exit(2);
}
free(target_path);
}
void upgrade_prepare(const struct release* old_release,
const struct release* new_release,
const char* source_prefix,
@ -354,6 +424,68 @@ void upgrade_prepare(const struct release* old_release,
}
free(group_path);
}
// TODO: After releasing Sortix 1.1, remove this compatibility.
if ( hook_needs_to_be_run(source_prefix, target_prefix,
"sortix-1.1-tix3g") )
{
char* path = join_paths(target_prefix, "/tix/collection.conf");
if ( !path )
{
warn("malloc");
_exit(2);
}
FILE* fp = fopen(path, "w");
if ( fp )
{
printf(" - Migrating to tix version 3...\n");
struct utsname uts;
uname(&uts);
for ( size_t i = 0; uts.sysname[i]; i++ )
uts.sysname[i] = tolower((unsigned char) uts.sysname[i]);
if ( fprintf(fp, "TIX_COLLECTION_VERSION=3\n") < 0 ||
fprintf(fp, "PREFIX=\n") < 0 ||
fprintf(fp, "PLATFORM=%s-%s\n",
uts.machine, uts.sysname) < 0 ||
fclose(fp) == EOF )
{
warn("%s", path);
_exit(2);
}
}
else
{
warn("%s", path);
_exit(2);
}
free(path);
// Delay deleting installed.list since it's needed for the upgrade.
hook_want_finalization(target_prefix, "sortix-1.1-tix3g");
}
// TODO: After releasing Sortix 1.1, remove this compatibility.
if ( hook_needs_to_be_run(source_prefix, target_prefix,
"sortix-1.1-grub-cache") )
{
char* path = join_paths(target_prefix, "/etc/grub.d/10_sortix.cache");
if ( !path )
{
warn("malloc");
_exit(2);
}
if ( !access_or_die(path, F_OK) )
{
printf(" - Removing /etc/grub.d/10_sortix.cache...\n");
if ( unlink(path) < 0 )
{
warn("unlink: %s", path);
_exit(2);
}
}
free(path);
}
// TODO: Add upstream mirror to /etc/upgrade.conf.
}
void upgrade_finalize(const struct release* old_release,
@ -365,6 +497,36 @@ void upgrade_finalize(const struct release* old_release,
(void) new_release;
(void) source_prefix;
(void) target_prefix;
if ( hook_needs_finalization(target_prefix, "sortix-1.1-tix3g") )
{
printf(" - Finishing migration to tix version 3...\n");
char* path = join_paths(target_prefix, "/tix/installed.list");
if ( !path )
{
warn("malloc");
_exit(2);
}
if ( unlink(path) < 0 && errno != ENOENT )
{
warn("%s", path);
_exit(2);
}
free(path);
path = join_paths(target_prefix, "/tix/repository.list");
if ( !path )
{
warn("malloc");
_exit(2);
}
if ( unlink(path) < 0 && errno != ENOENT )
{
warn("%s", path);
_exit(2);
}
free(path);
hook_did_finalization(target_prefix, "sortix-1.1-tix3g");
}
}
// TODO: After releasing Sortix 1.1, remove this compatibility. These manifests

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, 2020, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2018, 2020, 2021, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,6 +20,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@ -479,8 +480,8 @@ void install_manifest(const char* manifest,
_exit(2);
}
}
// Write out the new manifests atomically afterwards to ensure no paths are
// leaked if the operation is aborted part way.
// Write out the new tixinfo afterwards to ensure no paths are leaked if the
// operation is aborted part way.
char* in_tixinfo;
char* out_tixinfo;
if ( asprintf(&in_tixinfo, "%s/tix/tixinfo/%s", from_prefix,
@ -537,81 +538,6 @@ void install_manifest(const char* manifest,
}
free(in_tixinfo);
free(out_tixinfo);
// Likewise write out the new installation list atomically afterwards to
// ensure no manifests are leaked if the operation is aborted part way.
char* installed_path;
char* installed_path_new;
if ( asprintf(&installed_path, "%s/tix/installed.list", to_prefix) < 0 ||
asprintf(&installed_path_new, "%s/tix/installed.list.new",
to_prefix) < 0 )
{
warn("malloc");
_exit(2);
}
size_t installed_count;
char** installed = read_lines_file(installed_path, &installed_count);
if ( !installed )
{
warn("%s", installed_path);
_exit(2);
}
size_t installed_length = installed_count;
if ( is_tix )
{
bool found = false;
for ( size_t i = 0; !found && i < installed_count; i++ )
found = !strcmp(installed[i], manifest);
if ( !found && !string_array_append(&installed, &installed_count,
&installed_length, manifest) )
{
warn("malloc");
_exit(2);
}
}
else
{
size_t o = 0;
for ( size_t i = 0; i < installed_count; i++ )
{
if ( !strcmp(installed[i], manifest) )
free(installed[i]);
else
installed[o++] = installed[i];
}
installed_count = o;
}
string_array_sort_strcmp(installed, installed_count);
mode_t temp_umask = umask(0022);
FILE* installed_fp = fopen(installed_path_new, "w");
if ( !installed_fp )
{
warn("%s", installed_path_new);
_exit(2);
}
umask(temp_umask);
for ( size_t i = 0; i < installed_count; i++ )
{
const char* name = installed[i];
if ( fputs(name, installed_fp) == EOF ||
fputc('\n', installed_fp) == EOF )
{
warn("%s", installed_path_new);
_exit(2);
}
}
if ( fclose(installed_fp) == EOF )
{
warn("%s", installed_path_new);
_exit(2);
}
if ( rename(installed_path_new, installed_path) < 0 )
{
warn("rename: %s -> %s", installed_path_new, installed_path);
_exit(2);
}
string_array_free(&installed, &installed_count, &installed_length);
free(installed_path);
free(installed_path_new);
if ( in_files != empty )
{
for ( size_t i = 0; i < in_files_count; i++ )
@ -701,36 +627,47 @@ void install_manifests(const char* const* manifests,
char** read_installed_list(const char* prefix, size_t* out_count)
{
char* path;
if ( asprintf(&path, "%s/tix/installed.list", prefix) < 0 )
char* tixinfo;
if ( asprintf(&tixinfo, "%s/tix/tixinfo", prefix) < 0 )
{
warn("malloc");
_exit(2);
}
size_t count;
size_t length;
char** installed;
size_t installed_count;
if ( !access_or_die(path, F_OK) )
if ( !string_array_init(&installed, &count, &length) )
{
if ( !(installed = read_lines_file(path, &installed_count)) )
{
warn("%s", path);
_exit(2);
}
string_array_sort_strcmp(installed, installed_count);
warn("malloc");
_exit(2);
}
else
DIR* dir = opendir(tixinfo);
if ( !dir )
{
installed = malloc(1);
if ( !installed )
if ( errno == ENOENT )
return *out_count = count, installed;
warn("opendir: %s", tixinfo);
_exit(2);
}
struct dirent* entry;
while ( (errno = 0, entry = readdir(dir)) )
{
if ( entry->d_name[0] == '.' )
continue;
if ( !string_array_append(&installed, &count, &length, entry->d_name) )
{
warn("malloc");
_exit(2);
}
installed_count = 0;
}
free(path);
*out_count = installed_count;
return installed;
if ( errno )
{
warn("readdir: %s", tixinfo);
_exit(2);
}
free(tixinfo);
string_array_sort_strcmp(installed, count);
return *out_count = count, installed;
}
void install_manifests_detect(const char* from_prefix,

27
sysinstall/prepare Executable file
View File

@ -0,0 +1,27 @@
#!/bin/sh
# Copyright (c) 2023 Jonas 'Sortie' Termansen.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# prepare
# Prepare a sysmerge(8) --wait upgrade on the next boot.
# This script will run on the previous stable release during a sysmerge(8) as a
# high-level entry point to help the previous system generate the kernel and
# initrd files for the new system. This script minimizes the interface between
# the old and new system to allow change. The working directory is the directory
# the sysmerge upgrade is pending inside, usually /sysmerge.
set -e
/sysmerge/sbin/update-initrd --sysmerge

View File

@ -156,6 +156,12 @@ static bool should_install_bootloader_path(const char* mnt,
return result;
}
static bool should_ignore_bootloader_on_filesystem(struct blockdevice* bdev)
{
return bdev->fs && bdev->fs->driver &&
!strcmp(bdev->fs->driver, "iso9660fs");
}
static bool should_install_bootloader_bdev(struct blockdevice* bdev)
{
if ( !bdev->fs )
@ -195,13 +201,16 @@ static bool should_install_bootloader(void)
{
for ( size_t n = 0; n < hd->bdev.pt->partitions_count; n++ )
{
any_systems = true;
struct partition* p = hd->bdev.pt->partitions[n];
if ( should_ignore_bootloader_on_filesystem(&p->bdev) )
continue;
any_systems = true;
if ( should_install_bootloader_bdev(&p->bdev) )
return true;
}
}
else if ( hd->bdev.fs )
else if ( hd->bdev.fs &&
!should_ignore_bootloader_on_filesystem(&hd->bdev) )
{
any_systems = true;
if ( should_install_bootloader_bdev(&hd->bdev) )
@ -1006,12 +1015,13 @@ int main(void)
execute((const char*[]) { "chroot", "-d", ".", "update-grub", NULL },
"_eqQ");
}
else if ( access_or_die("/etc/grub.d/10_sortix", F_OK) == 0 )
else if ( access_or_die("/etc/default/grub.d/10_sortix", F_OK) == 0 )
{
// Help dual booters by making /etc/grub.d/10_sortix.cache.
// Help dual booters by making /etc/grub.d/default/10_sortix.cache.
printf(" - Creating bootloader fragment...\n");
execute((const char*[]) { "chroot", "-d", ".",
"/etc/grub.d/10_sortix", NULL }, "_eq");
"/etc/grub.d/default/10_sortix",
NULL }, "_eq");
}
printf(" - Finishing installation...\n");
_exit(0);
@ -1545,8 +1555,9 @@ int main(void)
if ( strcasecmp(accept_grub, "no") == 0 )
text("You did not accept a bootloader and need to set up bootloading "
"yourself. /etc/grub.d/10_sortix.cache is a GRUB configuration "
"fragment that boots the newly installed system.\n\n");
"yourself. /etc/default/grub.d/10_sortix.cache is a GRUB "
"configuration fragment that boots the newly installed system."
"\n\n");
text("Upon boot, you'll be greeted with a login screen. Enter your "
"credentials to get a command line. Login as user 'poweroff' as "

View File

@ -6,7 +6,8 @@
.Nd upgrade current operating system from a sysroot
.Sh SYNOPSIS
.Nm sysmerge
.Op Fl cfw
.Op Fl cfpsw
.Op Fl t Ar target
.Op Fl \-booting
.Op Fl \-hook-finalize
.Op Fl \-hook-prepare
@ -87,54 +88,86 @@ This is meant to be used by the old
when it invokes the new
.Nm
during a non-waiting upgrade.
.It Fl s , Fl \-system
Upgrade the system.
This option is implied unless
.Fl \-ports
is passed.
.It Fl t , Fl \-target Ns = Ns Ar target
Upgrade the installation in the
.Ar target
directory rather than the root filesystem.
This option is supported if the
.Pa target
is the
.Pa /
directory or only has ports installed and no system.
This option is unsupported if
.Ar target
has the system installed and is not the
.Pa / directory .
Instead one should
.Xr chroot 8
into the
.Pa target
and run the old
.Nm
inside rather than the new
.Nm .
However, this option may be useful for repairing installations and should work
in practice, however it is untested and might not properly handle incompatible
changes across releases.
.It Fl p , Fl \-ports
Upgrade the ports.
This option is implied unless
.Fl \-system
is passed.
.It Fl w , Fl \-wait
Wait until the next boot to complete the upgrade, rather than finishing it now.
This installs into the
The upgrade is installed into the
.Pa /sysmerge
directory instead and replaces the
directory instead, with the new kernel and initrd files in
.Pa /boot/sysmerge .
The new
.Xr kernel 7
with the new kernel
and
has a new
.Xr initrd 7
with an initrd that runs
that runs
.Sy /sysmerge/sbin/sysmerge --booting
on boot through the
.Sy chain-merge
.Xr init 8
boot target.
Backups are made of the
.Xr kernel 7
and
.Xr initrd 7
such that the operation can be rolled back.
boot target, which performs the operating system upgrade.
The bootloader configuration is regenerated with a menu option to perform
the upgrade if grub is enabled in
.Xr upgrade.conf 5 ,
otherwise the
.Pa /etc/default/grub.d/10_sortix.cache
bootloader fragment can be used to manually bootload the upgrade.
.El
.Sh FILES
.Bl -tag -width "/boot/sortix.initrd.sysmerge.orig" -compact
.Bl -tag -width "/etc/default/grub.d/10_sortix.cache" -compact
.It Pa /boot/sortix.bin
system
.Xr kernel 7
.It Pa /boot/sortix.bin.sysmerge.orig
system
.Xr kernel 7
(backup)
Old system
.Xr kernel 7 .
.It Pa /boot/sortix.initrd
system
.Xr initrd 7
.It Pa /boot/sortix.initrd.sysmerge.orig
system
.Xr initrd 7
(backup)
Old system
.Xr initrd 7 .
.It Pa /boot/sysmerge/
New system kernel and initrd files.
.It Pa /etc/default/grub.d/10_sortix.cache
GRUB configuration fragment that boots the new system.
.It Pa /etc/machine
processor platform of this installation
Processor platform of this installation.
.It Pa /etc/sortix-release
the current system release
The current system release.
.It Pa /etc/upgrade.conf
controls the bootloader upgrade behavior (see
.Xr upgrade.conf 5 )
.It Pa /share/sysinstall/hooks
Controls the bootloader upgrade behavior (see
.Xr upgrade.conf 5 ) .
.It Pa /share/sysinstall/hooks/
A file per upgrade hook indicating that it doesn't need to be run.
.It Pa /sysmerge
pending upgrade is stored here
.It Pa /sysmerge/
The pending systen upgrade is stored here.
.El
.Sh SEE ALSO
.Xr development 7 ,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2018, 2020, 2021, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2016, 2018, 2020, 2021, 2022, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -24,6 +24,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
@ -83,24 +84,32 @@ static bool is_partition_name(const char* path)
return *name == '\0';
}
static void compact_arguments(int* argc, char*** argv)
static bool has_pending_upgrade(const char* target)
{
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)--;
}
}
char* sysmerge = join_paths(target, "sysmerge");
char* boot_sysmerge = join_paths(target, "boot/sysmerge");
if ( !sysmerge || !boot_sysmerge )
err(2, "malloc");
bool result = access_or_die(sysmerge, F_OK) == 0 ||
access_or_die(boot_sysmerge, F_OK) == 0;
free(sysmerge);
free(boot_sysmerge);
return result;
}
static bool has_pending_upgrade(void)
static void update_grub(struct conf* conf, const char* target)
{
return access_or_die("/boot/sortix.bin.sysmerge.orig", F_OK) == 0 ||
access_or_die("/boot/sortix.initrd.sysmerge.orig", F_OK) == 0 ||
access_or_die("/sysmerge", F_OK) == 0;
if ( conf->grub )
{
printf(" - Configuring bootloader...\n");
execute((const char*[]) { "update-grub", NULL }, "ceqQ", target);
}
else if ( access_or_die("/etc/default/grub.d/10_sortix", F_OK) == 0 )
{
printf(" - Creating bootloader fragment...\n");
execute((const char*[]) { "/etc/default/grub.d/10_sortix", NULL },
"ceq", target);
}
}
int main(int argc, char* argv[])
@ -112,46 +121,48 @@ int main(int argc, char* argv[])
bool full = false;
bool hook_finalize = false;
bool hook_prepare = false;
bool ports = false;
bool system = false;
const char* target = "/";
bool wait = false;
for ( int i = 1; i < argc; i++ )
enum longopt
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
OPT_BOOTING = 128,
OPT_HOOK_FINALIZE,
OPT_HOOK_PREPARE,
};
const struct option longopts[] =
{
{"booting", no_argument, NULL, OPT_BOOTING},
{"cancel", no_argument, NULL, 'c'},
{"full", no_argument, NULL, 'f'},
{"hook-finalize", no_argument, NULL, OPT_HOOK_FINALIZE},
{"hook-prepare", no_argument, NULL, OPT_HOOK_PREPARE},
{"ports", no_argument, NULL, 'p'},
{"system", no_argument, NULL, 's'},
{"target", required_argument, NULL, 't'},
{"wait", no_argument, NULL, 'w'},
{0, 0, 0, 0}
};
const char* opts = "cfpst:w";
int opt;
while ( (opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1 )
{
switch (opt)
{
char c;
while ( (c = *++arg) ) switch ( c )
{
case 'c': cancel = true; break;
case 'f': full = true; break;
case 'w': wait = true; break;
default:
errx(1, "unknown option -- '%c'", c);
}
case OPT_BOOTING: booting = true; break;
case 'c': cancel = true; break;
case 'f': full = true; break;
case OPT_HOOK_FINALIZE: hook_finalize = true; break;
case OPT_HOOK_PREPARE: hook_prepare = true; break;
case 'p': ports = true; break;
case 's': system = true; break;
case 't': target = optarg; break;
case 'w': wait = true; break;
default: return 2;
}
else if ( !strcmp(arg, "--booting") )
booting = true;
else if ( !strcmp(arg, "--cancel") )
cancel = true;
else if ( !strcmp(arg, "--full") )
full = true;
else if ( !strcmp(arg, "--hook-finalize") )
hook_finalize = true;
else if ( !strcmp(arg, "--hook-prepare") )
hook_prepare = true;
else if ( !strcmp(arg, "--wait") )
wait = true;
else
errx(1, "unknown option: %s", arg);
}
compact_arguments(&argc, &argv);
if ( 1 < booting + cancel + hook_finalize + hook_prepare + wait )
errx(2, "Mutually incompatible options were passed");
@ -163,32 +174,69 @@ int main(int argc, char* argv[])
if ( no_source )
{
source = "";
if ( 1 < argc )
errx(2, "Unexpected extra operand `%s'", argv[1]);
if ( optind < argc )
errx(2, "Unexpected extra operand: %s", argv[optind]);
}
else if ( booting )
{
source = "/sysmerge";
if ( 1 < argc )
errx(2, "Unexpected extra operand `%s'", argv[1]);
full = access_or_die("/sysmerge/tix/sysmerge.full", F_OK) == 0;
source = join_paths(target, "sysmerge");
if ( !source )
err(2, "malloc");
if ( optind < argc )
errx(2, "Unexpected extra operand: %s", argv[optind]);
char* system_path = join_paths(target, "sysmerge/tix/sysmerge.system");
char* ports_path = join_paths(target, "sysmerge/tix/sysmerge.ports");
char* full_path = join_paths(target, "sysmerge/tix/sysmerge.full");
if ( !system_path || !ports_path || !full_path )
err(2, "malloc");
system = access_or_die(system_path, F_OK) == 0;
ports = access_or_die(ports_path, F_OK) == 0;
full = access_or_die(full_path, F_OK) == 0;
free(system_path);
free(ports_path);
free(full_path);
}
else
{
if ( argc < 2 )
if ( argc - optind < 1 )
errx(2, "No source operand was given");
source = argv[1];
if ( 2 < argc )
errx(2, "Unexpected extra operand `%s'", argv[2]);
source = argv[optind];
if ( 1 < argc - optind )
errx(2, "Unexpected extra operand: %s", argv[optind + 1]);
}
if ( !system && !ports )
system = ports = true;
if ( !ports )
full = false;
char* system_manifest = join_paths(target, "tix/manifest/system");
if ( !system_manifest )
err(2, "malloc");
bool has_system = !access_or_die(system_manifest, F_OK);
free(system_manifest);
if ( !has_system )
system = false;
struct conf conf;
conf_init(&conf);
char* conf_path = join_paths(target, "etc/upgrade.conf");
if ( !conf_path )
err(2, "malloc");
if ( !conf_load(&conf, conf_path) && errno != ENOENT )
err(2, conf_path);
bool did_cancel = false;
if ( !no_cancel && has_pending_upgrade() )
if ( !no_cancel && has_pending_upgrade(target) )
{
rename("/boot/sortix.bin.sysmerge.orig", "/boot/sortix.bin");
rename("/boot/sortix.initrd.sysmerge.orig", "/boot/sortix.initrd");
execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
execute((const char*[]) { "update-initrd", NULL }, "e");
char* sysmerge = join_paths(target, "sysmerge");
char* boot_sysmerge = join_paths(target, "boot/sysmerge");
if ( !sysmerge || !boot_sysmerge )
err(2, "malloc");
execute((const char*[]) { "rm", "-rf", "--", sysmerge, NULL }, "");
execute((const char*[]) { "rm", "-rf", "--", boot_sysmerge, NULL }, "");
update_grub(&conf, target);
printf("Cancelled pending system upgrade.\n");
did_cancel = true;
}
@ -200,51 +248,64 @@ int main(int argc, char* argv[])
return 0;
}
const char* old_release_path = "/etc/sortix-release";
char* old_release_path = join_paths(target, "etc/sortix-release");
if ( !old_release_path )
err(2, "malloc");
struct release old_release;
if ( !os_release_load(&old_release, old_release_path, old_release_path) )
{
if ( errno == ENOENT )
warn("%s", old_release_path);
exit(2);
if ( has_system || errno != ENOENT )
{
if ( errno == ENOENT )
warn("%s", old_release_path);
exit(2);
}
}
free(old_release_path);
char* new_release_path;
if ( asprintf(&new_release_path, "%s/etc/sortix-release", source) < 0 )
err(2, "asprintf");
char* new_release_path = join_paths(source, "etc/sortix-release");
if ( !new_release_path )
err(2, "malloc");
struct release new_release;
if ( !os_release_load(&new_release, new_release_path, new_release_path) )
{
if ( errno == ENOENT )
warn("%s", new_release_path);
exit(2);
if ( !system )
new_release = old_release;
else
{
if ( errno == ENOENT )
warn("%s", new_release_path);
exit(2);
}
}
free(new_release_path);
const char* old_machine_path = "/etc/machine";
char* old_machine = read_string_file(old_machine_path);
if ( !old_machine )
err(2, "%s", old_machine_path);
char* new_machine_path;
if ( asprintf(&new_machine_path, "%s/etc/machine", source) < 0 )
err(2, "asprintf");
char* new_machine = read_string_file(new_machine_path);
if ( !new_machine )
err(2, "%s", new_machine_path);
if ( strcmp(old_machine, new_machine) != 0 )
errx(2, "%s (%s) does not match %s (%s)", new_machine_path,
new_machine, old_machine_path, old_machine);
free(old_machine);
free(new_machine_path);
free(new_machine);
if ( has_system )
{
char* old_machine_path = join_paths(target, "etc/machine");
if ( !old_machine_path )
err(2, "malloc");
char* old_machine = read_string_file(old_machine_path);
if ( !old_machine )
err(2, "%s", old_machine_path);
free(old_machine_path);
char* new_machine_path = join_paths(source, "etc/machine");
if ( !new_machine_path )
err(2, "malloc");
char* new_machine = !system ? strdup(old_machine) :
read_string_file(new_machine_path);
if ( !new_machine )
err(2, "%s", new_machine_path);
if ( strcmp(old_machine, new_machine) != 0 )
errx(2, "%s (%s) does not match %s (%s)", new_machine_path,
new_machine, old_machine_path, old_machine);
free(old_machine);
free(new_machine_path);
free(new_machine);
}
// TODO: Check for version (skipping, downgrading).
struct conf conf;
conf_init(&conf);
if ( !conf_load(&conf, "/etc/upgrade.conf") && errno == ENOENT )
err(2, "/etc/upgrade.conf");
bool can_run_new_abi =
abi_compatible(new_release.abi_major, new_release.abi_minor,
old_release.abi_major, old_release.abi_minor);
@ -300,21 +361,33 @@ int main(int argc, char* argv[])
my_finalize = false;
}
if ( !system )
{
run_prepare = false;
run_finalize = false;
}
if ( wait && !has_system )
err(2, "--wait requires a system is installed in: %s", target);
if ( header )
{
if ( wait )
if ( wait && new_release.pretty_name )
printf("Scheduling upgrade to %s on next boot using %s:\n",
new_release.pretty_name, source);
else
else if ( new_release.pretty_name )
printf("Upgrading to %s using %s:\n",
new_release.pretty_name, source);
else
printf("Upgrading %s using %s:\n", target, source);
}
// Upgrade hooks that runs before the old system is replaced.
if ( run_prepare )
if ( has_system && run_prepare )
{
const char* prefix = !strcmp(target, "/") ? "" : target;
if ( my_prepare )
upgrade_prepare(&old_release, &new_release, source, "");
upgrade_prepare(&old_release, &new_release, source, prefix);
else
{
char* new_sysmerge = join_paths(source, "sbin/sysmerge");
@ -334,37 +407,74 @@ int main(int argc, char* argv[])
// TODO: What about native upgrades using make sysmerge? Should those
// values be updated then? Should there be an option to control
// this behavior?
const char* target = "";
const char* sysmerge = target;
if ( wait )
{
target = "/sysmerge";
if ( mkdir(target, 0755) < 0 )
err(2, "%s", target);
execute((const char*[]) { "tix-collection", "/sysmerge", "create",
sysmerge = join_paths(target, "sysmerge");
if ( !sysmerge )
err(2, "malloc");
if ( mkdir(sysmerge, 0755) < 0 )
err(2, "%s", sysmerge);
execute((const char*[]) { "tix-collection", sysmerge, "create",
NULL }, "e");
}
install_manifests_detect(source, target, true, true, full);
const char* prefix = !strcmp(sysmerge, "/") ? "" : sysmerge;
install_manifests_detect(source, prefix, system, ports, full);
}
if ( wait )
{
printf(" - Scheduling upgrade on next boot...\n");
char* system_path = join_paths(target, "sysmerge/tix/sysmerge.system");
char* ports_path = join_paths(target, "sysmerge/tix/sysmerge.ports");
char* full_path = join_paths(target, "sysmerge/tix/sysmerge.full");
char* ready_path = join_paths(target, "sysmerge/tix/sysmerge.ready");
char* sysmerge_boot = join_paths(target, "sysmerge/boot");
char* boot_sysmerge = join_paths(target, "boot/sysmerge");
if ( !system_path || !ports_path || !full_path || !ready_path ||
!sysmerge_boot || !boot_sysmerge )
err(2, "malloc");
if ( full )
{
int fd = open("/sysmerge/tix/sysmerge.full", O_WRONLY | O_CREAT);
int fd = open(full_path, O_WRONLY | O_CREAT);
if ( fd < 0 )
err(1, "/sysmerge/tix/sysmerge.full");
err(2, "%s", full_path);
close(fd);
}
execute((const char*[]) { "cp", "/boot/sortix.bin",
"/boot/sortix.bin.sysmerge.orig",
NULL }, "e");
execute((const char*[]) { "cp", "/boot/sortix.initrd",
"/boot/sortix.initrd.sysmerge.orig",
NULL }, "e");
execute((const char*[]) { "cp", "/sysmerge/boot/sortix.bin",
"/boot/sortix.bin", NULL }, "e");
execute((const char*[]) { "/sysmerge/sbin/update-initrd", NULL }, "e");
if ( system && !ports )
{
int fd = open(system_path, O_WRONLY | O_CREAT);
if ( fd < 0 )
err(2, "%s", system_path);
close(fd);
}
if ( ports && !system )
{
int fd = open(ports_path, O_WRONLY | O_CREAT);
if ( fd < 0 )
err(2, "%s", ports_path);
close(fd);
}
// Generate the new initrd in /sysmerge/boot.
execute((const char*[]) { "/sysmerge/libexec/sysmerge/prepare",
NULL }, "ce", target);
// Move the kernel and initrd files to the boot partition where the
// bootloader is guaranteed to be able to read them.
execute((const char*[]) { "rm", "-rf", "--", boot_sysmerge,
NULL }, "e");
execute((const char*[]) { "cp", "-RT", "--", sysmerge_boot,
boot_sysmerge, NULL }, "e");
// Signal the sysmerge upgrade is ready and isn't partial.
int fd = open(ready_path, O_WRONLY | O_CREAT);
if ( fd < 0 )
err(2, "%s", ready_path);
close(fd);
update_grub(&conf, target);
printf("The system will be upgraded to %s on the next boot.\n",
new_release.pretty_name);
@ -374,15 +484,16 @@ int main(int argc, char* argv[])
}
// Upgrade hooks that run after the new system is installed.
if ( run_finalize )
if ( has_system && run_finalize )
{
const char* prefix = !strcmp(target, "/") ? "" : target;
if ( my_finalize )
upgrade_finalize(&old_release, &new_release, source, "");
upgrade_finalize(&old_release, &new_release, source, prefix);
else
{
char* new_sysmerge = join_paths(source, "sbin/sysmerge");
if ( !new_sysmerge )
err(2, "asprintf");
err(2, "malloc");
execute((const char*[]) { new_sysmerge, "--hook-finalize", source,
NULL }, "e");
free(new_sysmerge);
@ -391,46 +502,78 @@ int main(int argc, char* argv[])
return 0;
}
if ( booting )
// Remove the upgrade readiness marker now that the upgrade has gone through
// such that the bootloader configuration and initrds don't try to do the
// upgrade again.
if ( has_system && booting )
{
unlink("/boot/sortix.bin.sysmerge.orig");
unlink("/boot/sortix.initrd.sysmerge.orig");
execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
char* ready_path = join_paths(target, "sysmerge/tix/sysmerge.ready");
if ( !ready_path )
err(2, "malloc");
unlink(ready_path);
free(ready_path);
}
if ( access_or_die("/etc/fstab", F_OK) == 0 )
// Update the initrd and bootloader. The new bootloader config won't refer
// to the upgrade as it's complete and the marker is gone.
if ( has_system && access_or_die("/etc/fstab", F_OK) == 0 )
{
printf(" - Creating initrd...\n");
execute((const char*[]) { "update-initrd", NULL }, "e");
execute((const char*[]) { "update-initrd", NULL }, "ce", target);
if ( conf.grub )
{
int boot_fd = open("/boot", O_RDONLY);
char* boot_path = join_paths(target, "boot");
if ( !boot_path )
err(2, "malloc");
int boot_fd = open(boot_path, O_RDONLY);
if ( boot_fd < 0 )
err(2, "/boot");
err(2, boot_path);
char* boot_device = atcgetblob(boot_fd, "device-path", NULL);
if ( !boot_device )
err(2, "Failed to find device of filesystem: /boot");
err(2, "Failed to find device of filesystem: %s", boot_path);
close(boot_fd);
// TODO: A better design for finding the parent block device of a
// partition without scanning every block device.
free(boot_path);
// TODO: A better design for finding the parent block device.
if ( is_partition_name(boot_device) )
*strrchr(boot_device, 'p') = '\0';
printf(" - Installing bootloader...\n");
execute((const char*[]) { "grub-install", boot_device,
NULL },"eqQ");
NULL }, "ceqQ", target);
free(boot_device);
printf(" - Configuring bootloader...\n");
execute((const char*[]) { "update-grub", NULL }, "eqQ");
}
else if ( access_or_die("/etc/grub.d/10_sortix", F_OK) == 0 )
{
printf(" - Creating bootloader fragment...\n");
execute((const char*[]) { "/etc/grub.d/10_sortix", NULL }, "eq");
}
update_grub(&conf, target);
}
printf("Successfully upgraded to %s.\n", new_release.pretty_name);
// Finally clean up /sysmerge and /boot/sysmerge. They were left alone so
// the system remained bootable with the idempotent upgrade if it failed
// midway. Okay there's a bit of race conditions in grub-install, though the
// replacement of grub.cfg is atomic. Everything now points into the new
// system and nothing refers to the sysmerge directories.
if ( has_system && booting )
{
// TODO: After releasing Sortix 1.1, remove sysmerge.orig compatibility.
char* kernel = join_paths(target, "boot/sortix.bin.sysmerge.orig");
char* initrd = join_paths(target, "boot/sortix.initrd.sysmerge.orig");
char* sysmerge = join_paths(target, "sysmerge");
char* boot_sysmerge = join_paths(target, "boot/sysmerge");
if ( !kernel || !initrd || !sysmerge || !boot_sysmerge )
err(2, "malloc");
unlink(kernel);
unlink(initrd);
execute((const char*[]) { "rm", "-rf", "--", sysmerge, NULL }, "");
execute((const char*[]) { "rm", "-rf", "--", boot_sysmerge, NULL }, "");
free(kernel);
free(initrd);
free(sysmerge);
free(boot_sysmerge);
}
if ( new_release.pretty_name )
printf("Successfully upgraded to %s.\n", new_release.pretty_name);
else
printf("Successfully upgraded %s.\n", target);
return 0;
}

View File

@ -921,12 +921,13 @@ int main(void)
"_eqQ");
}
else if ( conf.system &&
access_or_die("etc/grub.d/10_sortix", F_OK) == 0 )
access_or_die("etc/default/grub.d/10_sortix", F_OK) == 0 )
{
// Help dual booters by making /etc/grub.d/10_sortix.cache.
// Help dual booters by making /etc/default/grub.d/10_sortix.cache.
printf(" - Creating bootloader fragment...\n");
execute((const char*[]) { "chroot", "-d", ".",
"/etc/grub.d/10_sortix", NULL }, "_eq");
"/etc/grub.d/default/10_sortix", NULL },
"_eq");
}
printf(" - Finishing upgrade...\n");
_exit(0);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2020, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2013-2016, 2020, 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
@ -27,6 +27,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
@ -667,22 +668,32 @@ static void Build(struct metainfo* minfo)
Make(minfo, build_target, NULL, true, subdir);
}
static void CreateDestination(void)
static void CreateDestination(struct metainfo* minfo)
{
char* tardir_rel = join_paths(tmp_root, "tix");
char* destdir_rel = join_paths(tardir_rel, "data");
char* tixdir_rel = join_paths(tardir_rel, "tix");
if ( mkdir(tardir_rel, 0777) < 0 )
err(1, "mkdir: %s", tardir_rel);
if ( mkdir(destdir_rel, 0755) != 0 )
err(1, "mkdir: `%s'", destdir_rel);
if ( mkdir(tixdir_rel, 0755) != 0 )
err(1, "mkdir: `%s'", tixdir_rel);
if ( !tardir_rel )
err(1, "malloc");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
const char* prefix = 3 <= minfo->generation ? minfo->prefix : "";
char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix);
if ( !prefixdir_rel )
err(1, "malloc");
if ( mkdir_p(prefixdir_rel, 0755) < 0 )
err(1, "mkdir: %s", prefixdir_rel);
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( minfo->generation == 2 )
{
char* destdir_rel = join_paths(prefixdir_rel, "data");
char* tixdir_rel = join_paths(prefixdir_rel, "tix");
if ( mkdir(destdir_rel, 0755) != 0 )
err(1, "mkdir: `%s'", destdir_rel);
if ( mkdir(tixdir_rel, 0755) != 0 )
err(1, "mkdir: `%s'", tixdir_rel);
free(tixdir_rel);
free(destdir_rel);
}
free(prefixdir_rel);
free(tardir_rel);
free(destdir_rel);
free(tixdir_rel);
}
static void Install(struct metainfo* minfo)
@ -692,10 +703,12 @@ static void Install(struct metainfo* minfo)
metainfo_get_def(minfo, "MAKE_INSTALL_TARGET",
"pkg.make.install-target", "install");
char* tardir_rel = join_paths(tmp_root, "tix");
char* destdir_rel = join_paths(tardir_rel, "data");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
char* destdir_rel = minfo->generation == 3 ? strdup(tardir_rel) :
join_paths(tardir_rel, "data");
char* destdir = realpath(destdir_rel, NULL);
if ( !destdir )
err(1, "realpath: %s", destdir_rel);
err(1, "realpath: %s", tardir_rel);
Make(minfo, install_target, destdir, true, subdir);
@ -716,7 +729,9 @@ static void PostInstall(struct metainfo* minfo)
const char* subdir = metainfo_get(minfo, "SUBDIR", "pkg.subdir");
char* tardir_rel = join_paths(tmp_root, "tix");
char* destdir_rel = join_paths(tardir_rel, "data");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
char* destdir_rel = minfo->generation == 3 ? strdup(tardir_rel) :
join_paths(tardir_rel, "data");
char* destdir = realpath(destdir_rel, NULL);
if ( !destdir )
err(1, "realpath: %s", destdir_rel);
@ -758,9 +773,28 @@ static void TixInfo(struct metainfo* minfo)
char* tardir_rel = join_paths(tmp_root, "tix");
if ( !tardir_rel )
err(1, "malloc");
char* tixinfo_rel = join_paths(tardir_rel, "tix/tixinfo");
if ( !tixinfo_rel )
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
const char* prefix = 3 <= minfo->generation ? minfo->prefix : "";
char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix);
if ( !prefixdir_rel )
err(1, "malloc");
char* tixdir_rel = join_paths(prefixdir_rel, "tix");
if ( !tixdir_rel )
err(1, "malloc");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( 3 <= minfo->generation && mkdir(tixdir_rel, 0755) && errno != EEXIST )
err(1, "%s", tixdir_rel);
char* tixinfodir_rel = join_paths(tixdir_rel, "tixinfo");
if ( !tixinfodir_rel )
err(1, "malloc");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( 3 <= minfo->generation &&
mkdir(tixinfodir_rel, 0755) && errno != EEXIST )
err(1, "%s", tixdir_rel);
char* tixinfo_rel = 3 <= minfo->generation ?
join_paths(tixinfodir_rel, minfo->package_name) :
strdup(tixinfodir_rel);
const char* alias = metainfo_get(minfo, "ALIAS_OF", "pkg.alias-of");
const char* runtime_deps =
metainfo_get(minfo, "RUNTIME_DEPS", "pkg.runtime-deps");
@ -772,20 +806,48 @@ static void TixInfo(struct metainfo* minfo)
if ( !tixinfo_fp )
err(1, "`%s'", tixinfo_rel);
fprintf(tixinfo_fp, "tix.version=1\n");
fprintf(tixinfo_fp, "tix.class=tix\n");
fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host);
fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name);
if ( alias )
fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias);
if ( 3 <= minfo->generation )
{
// TODO: Shell escape the values if needed.
fwrite_variable(tixinfo_fp, "TIX_VERSION", "3");
fwrite_variable(tixinfo_fp, "NAME", minfo->package_name);
const char* version = metainfo_get(minfo, "VERSION", "VERSION");
if ( version )
fwrite_variable(tixinfo_fp, "VERSION", version);
const char* version_2 = metainfo_get(minfo, "VERSION_2", "VERSION_2");
if ( version_2 )
fwrite_variable(tixinfo_fp, "VERSION_2", version_2);
fwrite_variable(tixinfo_fp, "PLATFORM", minfo->host);
if ( alias )
fwrite_variable(tixinfo_fp, "ALIAS_OF", alias);
else
{
if ( runtime_deps )
fwrite_variable(tixinfo_fp, "RUNTIME_DEPS", runtime_deps);
if ( location_independent )
fwrite_variable(tixinfo_fp, "LOCATION_INDEPENDENT", "true");
else
fwrite_variable(tixinfo_fp, "PREFIX", minfo->prefix);
}
}
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
else
{
if ( runtime_deps )
fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps);
if ( location_independent )
fprintf(tixinfo_fp, "pkg.location-independent=true\n");
fprintf(tixinfo_fp, "tix.version=1\n");
fprintf(tixinfo_fp, "tix.class=tix\n");
fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host);
fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name);
if ( alias )
fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias);
else
fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix);
{
if ( runtime_deps )
fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps);
if ( location_independent )
fprintf(tixinfo_fp, "pkg.location-independent=true\n");
else
fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix);
}
}
if ( ferror(tixinfo_fp) || fflush(tixinfo_fp) == EOF )
@ -793,27 +855,64 @@ static void TixInfo(struct metainfo* minfo)
fclose(tixinfo_fp);
free(tardir_rel);
free(prefixdir_rel);
free(tixdir_rel);
free(tixinfodir_rel);
free(tixinfo_rel);
}
static void TixManifest(struct metainfo* minfo)
{
if ( !fork_and_wait_or_recovery() )
return;
char* tardir_rel = join_paths(tmp_root, "tix");
if ( !tardir_rel )
err(1, "malloc");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
const char* prefix = 3 <= minfo->generation ? minfo->prefix : "";
char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix);
if ( !prefixdir_rel )
err(1, "malloc");
if ( chdir(prefixdir_rel) < 0 )
err(1, "%s", prefixdir_rel);
if ( mkdir("tix", 0755) && errno != EEXIST )
err(1, "%s", "tix");
if ( mkdir("tix/manifest", 0755) && errno != EEXIST )
err(1, "%s", "tix/manifest");
char* command;
if ( asprintf(&command,
"find . -name tix -prune -o -print | "
"sed -E -e 's,^\\.$,/,' -e 's,^\\./,/,' | "
"LC_ALL=C sort > tix/manifest/%s",
minfo->package_name) < 0 )
err(1, "malloc");
const char* cmd_argv[] = { "sh", "-c", command, NULL };
recovery_execvp(cmd_argv[0], (char* const*) cmd_argv);
err(127, "%s", cmd_argv[0]);
}
static void Package(struct metainfo* minfo)
{
if ( !fork_and_wait_or_recovery() )
return;
char* tardir_rel = join_paths(tmp_root, "tix");
if ( !tardir_rel )
err(1, "malloc");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
const char* prefix = 3 <= minfo->generation ? minfo->prefix : "";
char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix);
if ( !prefixdir_rel )
err(1, "malloc");
char* package_tix = print_string("%s/%s.tix.tar.xz",
minfo->destination, minfo->package_name);
if ( !package_tix )
err(1, "malloc");
printf("Creating `%s'...\n", package_tix);
fflush(stdout);
const char* cmd_argv[] =
{
minfo->tar,
"-C", tardir_rel,
"-C", prefixdir_rel,
"--remove-files",
"--create",
"--xz",
@ -821,12 +920,30 @@ static void Package(struct metainfo* minfo)
"--owner=0",
"--group=0",
"--file", package_tix,
"--",
"tix",
"data",
NULL
};
recovery_execvp(cmd_argv[0], (char* const*) cmd_argv);
err(127, "%s", cmd_argv[0]);
string_array_t cmd = string_array_make();
for ( size_t i = 0; cmd_argv[i]; i++ )
if ( !string_array_append(&cmd, cmd_argv[i]) )
err(1, "malloc");
struct dirent** entries;
int count = scandir(prefixdir_rel, &entries, NULL, alphasort);
if ( count < 0 )
err(1, "scandir: %s", prefixdir_rel);
for ( int i = 0; i < count; i++ )
{
const char* name = entries[i]->d_name;
if ( !strcmp(name, ".") || !strcmp(name, "..") || !strcmp(name, "tix") )
continue;
if ( !string_array_append(&cmd, name) )
err(1, "malloc");
}
if ( !string_array_append(&cmd, NULL) )
err(1, "malloc");
recovery_execvp(cmd.strings[0], (char* const*) cmd.strings);
err(127, "%s", cmd.strings[0]);
}
static void Compile(struct metainfo* minfo)
@ -970,7 +1087,7 @@ static void BuildPackage(struct metainfo* minfo)
"specify the intended destination prefix using --prefix",
minfo->package_name);
CreateDestination();
CreateDestination(minfo);
// Possibly build a native version of the package to aid cross-compilation.
// This is an anti-feature needed for broken packages that don't properly
@ -991,6 +1108,9 @@ static void BuildPackage(struct metainfo* minfo)
if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PACKAGE, minfo) )
{
TixInfo(minfo);
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( 3 <= minfo->generation )
TixManifest(minfo);
Package(minfo);
}
}
@ -1144,6 +1264,9 @@ int main(int argc, char* argv[])
minfo.generation = atoi(generation_string);
free(generation_string);
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( minfo.generation != 2 && minfo.generation != 3 )
errx(1, "Unsupported generation: %i", minfo.generation);
if ( !(minfo.start_step = step_of_step_name(start_step_string)) )
{
@ -1220,8 +1343,8 @@ int main(int argc, char* argv[])
minfo.tixbuildinfo = true;
minfo.package_info = string_array_make();
string_array_t* package_info = &minfo.package_info;
if ( !dictionary_append_file_path(package_info,
minfo.package_info_path) )
if ( variables_append_file_path(package_info,
minfo.package_info_path) < 0 )
err(1, "`%s'", minfo.package_info_path);
}
else

View File

@ -27,6 +27,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
@ -107,13 +108,6 @@ int main(int argc, char* argv[])
compact_arguments(&argc, &argv);
ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv);
VerifyCommandLineCollection(&collection);
int generation = atoi(generation_string);
free(generation_string);
if ( !prefix )
prefix = strdup(collection);
if ( argc == 1 )
{
@ -121,12 +115,30 @@ int main(int argc, char* argv[])
exit(1);
}
// The collection directory might not exist yet.
if ( strcmp(argv[1], "create") != 0 )
VerifyCommandLineCollection(&collection);
int generation = atoi(generation_string);
free(generation_string);
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( generation != 2 && generation != 3 )
errx(1, "Unsupported generation: %i", generation);
if ( !prefix )
prefix = strdup(collection);
const char* cmd = argv[1];
if ( !strcmp(cmd, "create") )
{
if ( !platform && !(platform = GetBuildTriplet()) )
err(1, "unable to determine platform, use --platform");
if ( mkdir_p(collection, 0755) != 0 )
err(1, "mkdir: `%s'", collection);
VerifyCommandLineCollection(&collection);
char* tix_path = join_paths(collection, "tix");
if ( mkdir_p(tix_path, 0755) != 0 )
err(1, "mkdir: `%s'", tix_path);
@ -149,26 +161,41 @@ int main(int argc, char* argv[])
errx(1, "error: `%s' already exists, a tix collection is "
"already installed at `%s'.", collection_conf_path,
collection);
fprintf(conf_fp, "tix.version=1\n");
fprintf(conf_fp, "tix.class=collection\n");
fprintf(conf_fp, "collection.generation=%i\n", generation);
fprintf(conf_fp, "collection.prefix=%s\n", !strcmp(prefix, "/") ? "" :
prefix);
fprintf(conf_fp, "collection.platform=%s\n", platform);
if ( 3 <= generation )
{
fwrite_variable(conf_fp, "TIX_COLLECTION_VERSION", "3");
fwrite_variable(conf_fp, "PREFIX",
!strcmp(prefix, "/") ? "" : prefix);
fwrite_variable(conf_fp, "PLATFORM", platform);
}
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
else
{
fprintf(conf_fp, "tix.version=1\n");
fprintf(conf_fp, "tix.class=collection\n");
fprintf(conf_fp, "collection.generation=%i\n", generation);
fprintf(conf_fp, "collection.prefix=%s\n",
!strcmp(prefix, "/") ? "" : prefix);
fprintf(conf_fp, "collection.platform=%s\n", platform);
}
fclose(conf_fp);
free(collection_conf_path);
const char* repo_list_path = join_paths(tixdb_path, "repository.list");
FILE* repo_list_fp = fopen(repo_list_path, "w");
if ( !repo_list_fp )
err(1, "`%s'", repo_list_path);
fclose(repo_list_fp);
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
if ( generation < 3 )
{
const char* repo_list_path = join_paths(tixdb_path, "repository.list");
FILE* repo_list_fp = fopen(repo_list_path, "w");
if ( !repo_list_fp )
err(1, "`%s'", repo_list_path);
fclose(repo_list_fp);
const char* inst_list_path = join_paths(tixdb_path, "installed.list");
FILE* inst_list_fp = fopen(inst_list_path, "w");
if ( !inst_list_fp )
err(1, "`%s'", inst_list_path);
fclose(inst_list_fp);
const char* inst_list_path = join_paths(tixdb_path, "installed.list");
FILE* inst_list_fp = fopen(inst_list_path, "w");
if ( !inst_list_fp )
err(1, "`%s'", inst_list_path);
fclose(inst_list_fp);
}
return 0;
}

View File

@ -1,5 +1,5 @@
#!/bin/sh
# Copyright (c) 2017, 2021 Jonas 'Sortie' Termansen.
# Copyright (c) 2017, 2021, 2023 Jonas 'Sortie' Termansen.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@ -46,12 +46,13 @@ srctix=false
sysroot=""
toolchain=false
url=false
url_master=false
url_master_release=false
url_main=false
url_main_release=false
url_mirror=false
url_mirror_release=false
url_release_sig=false
url_sha256sum=false
upgrade=false
# TODO: Option to select this default:
# TODO: This hides errors. Fix wget so it has a quiet, but errors, mode.
wget_options="-q --show-progress"
@ -86,10 +87,10 @@ for argument do
--collection=*) collection=$parameter ;;
--collection) previous_option=collection ;;
--continue) continue="--continue" ;;
--download-non-verbose) previous_option="-nv" ;;
--download-non-verbose) previous_option="-v" ;;
--download-quiet) previous_option="-q" ;;
--download-verbose) previous_option="-v" ;;
--download-non-verbose) wget_options="-nv" ;;
--download-non-verbose) wget_options="-v" ;;
--download-quiet) wget_options="-q" ;;
--download-verbose) wget_options="-v" ;;
--execpatch) execpatch=true ;;
--initrd) initrd=true ;;
--input-release-file=*) input_release_file=$parameter ;;
@ -112,6 +113,8 @@ for argument do
--output-release-sig-file) previous_option=output_release_sig_file ;;
--output-sha256sum=*) output_sha256sum=$parameter ;;
--output-sha256sum) previous_option=output_sha256sum ;;
--output-upgrade-file=*) output_upgrade_file=$parameter ;;
--output-upgrade-file) previous_option=output_upgrade_file ;;
--patch) patch=true ;;
--port) port=true ;;
--porttix) porttix=true ;;
@ -124,10 +127,11 @@ for argument do
--sysroot) previous_option=sysroot ;;
--sysroot=*) sysroot=$parameter ;;
--toolchain) toolchain=true ;;
--upgrade) upgrade=true ;;
--url) url=true ;;
--url-master) url_master=true ;;
--url-main) url_main=true ;;
--url-mirror) url_mirror=true ;;
--url-master-release) url_master_release=true ;;
--url-main-release) url_main_release=true ;;
--url-mirror-release) url_mirror_release=true ;;
--url-release-sig) url_release_sig=true ;;
--url-sha256sum) url_sha256sum=true ;;
@ -149,18 +153,20 @@ fi
# TODO: Mutually incompatible options.
conf() {
grep -E "^$1[[:space:]]*=" -- "$collection/etc/upgrade.conf" |
tail -n 1 |
sed -E 's/^[^=]*=[[:space:]]*(|.*[^[:space:]])[[:space:]]*/\1/'
sed -E -e 's/([a-zA-Z0-9_]+) *? *= */\U\1=/' \
-e 's/=yes$/=true/' -e 's/no$/=false/' "$3" | \
tix-vars -d "$2" - "$4"
}
tmpdir=$(mktemp -dt tix-fetch-port.XXXXXX)
trap 'rm -rf -- "$tmpdir"' EXIT HUP INT QUIT TERM
RELEASE_KEY=$(conf release_key)
RELEASE_SIG_URL=$(conf release_sig_url)
PREFERRED_MIRROR=$(conf mirror)
FORCE_MIRROR=$(conf force_mirror)
upgrade_conf="${collection%/}/etc/upgrade.conf"
CHANNEL=$(conf -d '' "$upgrade_conf" CHANNEL)
RELEASE_KEY=$(conf -d '' "$upgrade_conf" RELEASE_KEY)
RELEASE_SIG_URL=$(conf -d '' "$upgrade_conf" RELEASE_SIG_URL)
PREFERRED_MIRROR=$(conf -d '' "$upgrade_conf" PREFERRED_MIRROR)
FORCE_MIRROR=$(conf -d '' "$upgrade_conf" FORCE_MIRROR)
USER_AGENT="$(uname -s)/$(uname -r) ($(uname -m); $(uname -v))"
if $insecure_no_check_certificate; then
@ -202,17 +208,33 @@ do_wget() {
}
# Fetch signed release description.
if [ -z "$input_release_file" ]; then
if [ -z "$input_release_sig_file" ]; then
(cd "$tmpdir" &&
do_wget -U "$USER_AGENT" $wget_options -O release.sh.sig \
-- "$RELEASE_SIG_URL")
else
cp -T -- "$input_release_sig_file" "$tmpdir/release.sh.sig"
fi
download_release_sh() {
(cd "$tmpdir" &&
do_wget -U "$USER_AGENT" $wget_options -O release.sh.sig \
-- "$RELEASE_SIG_URL")
signify -Vq -p "$RELEASE_KEY" -em "$tmpdir/release.sh"
else
}
true > "$tmpdir/upgrade.sh"
if [ -z "$input_release_file" -a -z "$input_release_sig_file" ]; then
download_release_sh
# TODO: tix-vars's output is not quoted so it can be input again.
tix-vars "$tmpdir/release.sh" | \
grep -E '^UPGRADE_=' | \
cat > "$tmpdir/upgrade.sh"
UPGRADE_SIG_URL=$(tix-vars -d '' "$tmpdir/upgrade.sh" UPGRADE_SIG_URL)
if $upgrade && [ -n "$UPGRADE_SIG_URL" ]; then
RELEASE_SIG_URL="$UPGRADE_SIG_URL"
RELEASE_KEY=$(tix-vars "$tmpdir/upgrade.sh" UPGRADE_KEY)
download_release_sh
fi
fi
if [ -n "$input_release_file" ]; then
cp -T -- "$input_release_file" "$tmpdir/release.sh"
elif [ -n "$input_release_sig_file" ]; then
signify -Vq -p "$RELEASE_KEY" -em "$tmpdir/release.sh"
fi
# Store the signed release file if requested.
@ -225,35 +247,52 @@ if [ -n "$output_release_file" ]; then
cp -T -- "$tmpdir/release.sh" "$output_release_file"
fi
# Load the release description.
# TODO: SECURITY: REMOTE CODE EXECUTION OF SIGNED REMOTE CODE.
# TODO: SECURITY: Protect against responding with older release.sh.
. "`realpath -- "$tmpdir/release.sh"`" # Avoid PATH search with absolute path.
# Store the upgrade file if requested.
if [ -n "$output_upgrade_file" ]; then
cp -T -- "$tmpdir/upgrade.sh" "$output_upgrade_file"
fi
if $url_master; then
printf "%s\n" "$MASTER"
# Load the release description.
# TODO: SECURITY: Protect against responding with older release.sh.
# TODO: DO NOT SUBMIT: Temporary compatibility.
MAIN=$(tix-vars -d '' "$tmpdir/release.sh" MAIN)
MASTER=$(tix-vars -d '' "$tmpdir/release.sh" MASTER)
if [ -z "$MAIN" ]; then
MAIN="$MASTER"
fi
RELEASE=$(tix-vars "$tmpdir/release.sh" RELEASE)
MACHINE=$(tix-vars "$tmpdir/release.sh" MACHINE)
SHA256SUM_FILE=$(tix-vars -d sha256sum "$tmpdir/release.sh" SHA256SUM_FILE)
SHA256SUM_SHA256SUM=$(tix-vars "$tmpdir/release.sh" SHA256SUM_SHA256SUM)
MIRRORS=$(tix-vars -d '' "$tmpdir/release.sh" MIRRORS)
if $url_main; then
printf "%s\n" "$MAIN"
exit
elif $url_master_release; then
printf "%s\n" "$MASTER/$RELEASE"
elif $url_main_release; then
printf "%s\n" "$MAIN/$RELEASE"
exit
fi
# Default to the master mirror but switch to the preferred mirror if the release
# Default to the main mirror but switch to the preferred mirror if the release
# description knows about the mirror and believes it to be trustworthy.
MIRROR="$MASTER"
MIRROR="$MAIN"
for POTENTIAL_MIRROR in $MIRRORS; do
if [ "$POTENTIAL_MIRROR" = "$PREFERRED_MIRROR" ]; then
MIRROR="$PREFERRED_MIRROR"
fi
done
if [ -n "$PREFERRED_MIRROR" ] && [ "$MIRROR" != "$PREFERRED_MIRROR" ]; then
if [ "$FORCE_MIRROR" = yes ]; then
if [ "$FORCE_MIRROR" = true ]; then
MIRROR="$PREFERRED_MIRROR"
else
echo "$0: warning: ignoring unsupported mirror $PREFERRED_MIRROR" >&2
fi
fi
# TODO: Make sure the distant future http downgrade is supported.
if $insecure_downgrade_to_http; then
MIRROR="$(echo "$MIRROR" | sed -E 's,^https:,http:,')"
fi
@ -274,7 +313,7 @@ if $url_sha256sum; then
exit
fi
if [ -z "$input_sha256sum" ]; then
# TODO: If the mirror doesn't work, try the master.
# TODO: If the mirror doesn't work, try the main.
(cd "$tmpdir" &&
do_wget -U "$USER_AGENT" $wget_options -O sha256sum \
-- "$RELEASE_URL/$SHA256SUM_FILE")
@ -293,11 +332,6 @@ escape_extended_regex() {
printf "%s\n" "$1" | sed -E -e 's/[[$()*?\+.^{|}]/\\\0/g'
}
# TODO: Remove:
#escape_extended_regex_test_self() {
# printf "%s\n" "$1" | grep -E "^$(escape_extended_regex "$1")\$"
#}
request() {
REQUEST="$1"
REQUESTDIR="$2"
@ -351,7 +385,7 @@ request() {
fi
# Fetch the file.
# TODO: If the mirror doesn't work, try the master.
# TODO: If the mirror doesn't work, try the main.
(cd "$DOWNLOADDIR" &&
mkdir -p -- "$(dirname -- "$REQUEST")" &&
do_wget -U "$USER_AGENT" $wget_options $continue -O "$REQUEST" \
@ -385,6 +419,7 @@ request() {
}
if $release; then
MIRRORS=$(tix-vars "$tmpdir/release.sh" BUILD_FILE)
request "$BUILD_FILE" "" "$(basename -- "$BUILD_FILE")"
fi
@ -401,7 +436,7 @@ for REQUEST; do
REQUEST="$REQUEST"
REQUESTDIR="$MACHINE/boot/"
elif $initrd; then
REQUEST="$REQUEST.initrd.xz"
REQUEST="$REQUEST.tar.xz"
REQUESTDIR="$MACHINE/boot/"
elif $patch; then
REQUEST="$REQUEST.patch"

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, 2016, 2017, 2020, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2015-2017, 2020, 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
@ -27,6 +27,7 @@
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
@ -78,50 +79,24 @@ void VerifyTixDatabase(const char* prefix,
err(1, "error: tix collection information unavailable: `%s'",
info_path);
}
char* installed_list_path = join_paths(tixdb_path, "installed.list");
FILE* installed_list_fp = fopen(installed_list_path, "a");
if ( !installed_list_fp )
{
warn("error: unable to open `%s' for writing",
installed_list_path);
errx(1, "error: `%s': do you have sufficient permissions to "
"administer this tix collection?", prefix);
}
fclose(installed_list_fp);
free(installed_list_path);
free(info_path);
}
bool IsPackageInstalled(const char* tixdb_path, const char* package)
{
char* installed_list_path = join_paths(tixdb_path, "installed.list");
FILE* installed_list_fp = fopen(installed_list_path, "r");
if ( !installed_list_fp )
err(1, "`%s'", installed_list_path);
bool ret = false;
char* line = NULL;
size_t line_size = 0;
ssize_t line_len;
while ( 0 < (line_len = getline(&line, &line_size, installed_list_fp)) )
{
if ( line[line_len-1] == '\n' )
line[--line_len] = '\0';
if ( !strcmp(line, package) )
{
ret = true;
break;
}
}
free(line);
if ( ferror(installed_list_fp) )
err(1, "`%s'", installed_list_path);
fclose(installed_list_fp);
free(installed_list_path);
return ret;
char* tixinfo_dir = join_paths(tixdb_path, "tixinfo");
if ( !tixinfo_dir )
err(1, "malloc");
char* tixinfo = join_paths(tixinfo_dir, package);
if ( !tixinfo )
err(1, "malloc");
bool installed = !access(tixinfo, F_OK);
free(tixinfo);
free(tixinfo_dir);
return installed;
}
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
void MarkPackageAsInstalled(const char* tixdb_path, const char* package)
{
char* installed_list_path = join_paths(tixdb_path, "installed.list");
@ -228,18 +203,40 @@ int main(int argc, char* argv[])
char* coll_conf_path = join_paths(tix_directory_path, "collection.conf");
string_array_t coll_conf = string_array_make();
if ( !dictionary_append_file_path(&coll_conf, coll_conf_path) )
err(1, "`%s'", coll_conf_path);
VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path);
free(coll_conf_path);
switch ( variables_append_file_path(&coll_conf, coll_conf_path) )
{
case -1: err(1, "%s", coll_conf_path);
case -2: errx(2, "%s: Syntax error", coll_conf_path);
}
const char* coll_generation = dictionary_get(&coll_conf, "collection.generation");
assert(coll_generation);
VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path);
const char* coll_generation =
dictionary_get(&coll_conf, "TIX_COLLECTION_VERSION");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( !coll_generation )
coll_generation = dictionary_get(&coll_conf, "collection.generation");
if ( !coll_generation )
err(1, "%s: No TIX_COLLECTION_VERSION was set", coll_conf_path);
generation = atoi(coll_generation);
coll_prefix = dictionary_get(&coll_conf, "collection.prefix");
assert(coll_prefix);
coll_platform = dictionary_get(&coll_conf, "collection.platform");
assert(coll_platform);
if ( generation == 3 )
{
coll_prefix = dictionary_get(&coll_conf, "PREFIX");
coll_platform = dictionary_get(&coll_conf, "PLATFORM");
}
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
else if ( generation == 2 )
{
coll_prefix = dictionary_get(&coll_conf, "collection.prefix");
coll_platform = dictionary_get(&coll_conf, "collection.platform");
}
else
errx(1, "%s: Unsupported TIX_COLLECTION_VERSION: %i",
coll_conf_path, generation);
if ( !coll_prefix )
err(1, "%s: No PREFIX was set", coll_conf_path);
if ( !coll_platform )
err(1, "%s: No PLATFORM was set", coll_conf_path);
for ( int i = 1; i < argc; i++ )
InstallPackage(argv[i]);
@ -247,6 +244,7 @@ int main(int argc, char* argv[])
string_array_reset(&coll_conf);
free(tix_directory_path);
free(coll_conf_path);
return 0;
}
@ -263,25 +261,47 @@ void InstallPackage(const char* tix_path)
if ( !IsFile(tix_path) )
err(1, "`%s'", tix_path);
const char* tixinfo_path = "tix/tixinfo";
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
bool modern = true;
const char* tixinfo_path = "tix/tixinfo/";
if ( !TarContainsFile(tix_path, tixinfo_path) )
errx(1, "`%s' doesn't contain a `%s' file", tix_path, tixinfo_path);
{
const char* tixinfo_path_old = "tix/tixinfo";
if ( !TarContainsFile(tix_path, tixinfo_path_old) )
errx(1, "`%s' doesn't contain a `%s' directory", tix_path,
tixinfo_path);
tixinfo_path = tixinfo_path_old;
modern = false;
}
string_array_t tixinfo = string_array_make();
FILE* tixinfo_fp = TarOpenFile(tix_path, tixinfo_path);
dictionary_append_file(&tixinfo, tixinfo_fp);
switch ( variables_append_file(&tixinfo, tixinfo_fp) )
{
case -1: err(1, "%s: %s", tix_path, tixinfo_path);
case -2: errx(1, "%s: %s: Syntax error", tix_path, tixinfo_path);
}
fclose(tixinfo_fp);
const char* package_name = dictionary_get(&tixinfo, "pkg.name");
const char* version = dictionary_get(&tixinfo, "TIX_VERSION");
if ( modern && (!version || strcmp(version, "3") != 0) )
errx(1, "%s: unsupported TIX_VERSION: %s", tix_path, version);
const char* package_name =
dictionary_get(&tixinfo, modern ? "NAME" : "pkg.name");
assert(package_name);
const char* package_prefix = dictionary_get(&tixinfo, "pkg.prefix");
const char* package_prefix =
dictionary_get(&tixinfo, modern ? "PREFIX" : "pkg.prefix");
assert(package_prefix || !package_prefix);
const char* package_platform = dictionary_get(&tixinfo, "tix.platform");
const char* package_platform =
dictionary_get(&tixinfo, modern ? "PLATFORM" : "tix.platform");
assert(package_platform || !package_platform);
bool already_installed = IsPackageInstalled(tix_directory_path, package_name);
bool already_installed =
IsPackageInstalled(tix_directory_path, package_name);
if ( already_installed && !reinstall )
errx(1, "error: package `%s' is already installed. Use --reinstall "
"to force reinstallation.", package_name);
@ -313,44 +333,51 @@ void InstallPackage(const char* tix_path)
fflush(stdout);
}
const char* data = modern ? "" : "data";
char* data_and_prefix = package_prefix && package_prefix[0] ?
print_string("data%s", package_prefix) :
strdup("data");
print_string("%s%s", data, package_prefix) :
strdup(data);
char* tixinfo_out_path = print_string("%s/tixinfo/%s", tix_directory_path, package_name);
int tixinfo_fd = open(tixinfo_out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if ( tixinfo_fd < 0 )
err(1, "%s", tixinfo_out_path);
TarExtractFileToFD(tix_path, "tix/tixinfo", tixinfo_fd);
close(tixinfo_fd);
FILE* index_fp = TarOpenIndex(tix_path);
string_array_t files = string_array_make();
string_array_append_file(&files, index_fp);
qsort(files.strings, files.length, sizeof(char*), strcmp_indirect);
char* manifest_path = print_string("%s/manifest/%s", tix_directory_path, package_name);
FILE* manifest_fp = fopen(manifest_path, "w");
if ( !manifest_fp )
err(1, "%s", manifest_path);
for ( size_t i = 0; i < files.length; i++ )
if ( !modern )
{
char* str = files.strings[i];
if ( strncmp(str, "data", strlen("data")) != 0 )
continue;
str += strlen("data");
if ( str[0] && str[0] != '/' )
continue;
size_t len = strlen(str);
while ( 2 <= len && str[len-1] == '/' )
str[--len] = '\0';
if ( fprintf(manifest_fp, "%s\n", str) < 0 )
char* tixinfo_out_path =
print_string("%s/tixinfo/%s", tix_directory_path, package_name);
int tixinfo_fd =
open(tixinfo_out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if ( tixinfo_fd < 0 )
err(1, "%s", tixinfo_out_path);
TarExtractFileToFD(tix_path, "tix/tixinfo", tixinfo_fd);
close(tixinfo_fd);
FILE* index_fp = TarOpenIndex(tix_path);
string_array_t files = string_array_make();
string_array_append_file(&files, index_fp);
qsort(files.strings, files.length, sizeof(char*), strcmp_indirect);
char* manifest_path =
print_string("%s/manifest/%s", tix_directory_path, package_name);
FILE* manifest_fp = fopen(manifest_path, "w");
if ( !manifest_fp )
err(1, "%s", manifest_path);
for ( size_t i = 0; i < files.length; i++ )
{
char* str = files.strings[i];
if ( strncmp(str, "data", strlen("data")) != 0 )
continue;
str += strlen("data");
if ( str[0] && str[0] != '/' )
continue;
size_t len = strlen(str);
while ( 2 <= len && str[len-1] == '/' )
str[--len] = '\0';
if ( fprintf(manifest_fp, "%s\n", str) < 0 )
err(1, "%s", manifest_path);
}
if ( ferror(manifest_fp) || fflush(manifest_fp) == EOF )
err(1, "%s", manifest_path);
fclose(manifest_fp);
string_array_reset(&files);
fclose(index_fp);
}
if ( ferror(manifest_fp) || fflush(manifest_fp) == EOF )
err(1, "%s", manifest_path);
fclose(manifest_fp);
string_array_reset(&files);
fclose(index_fp);
if ( fork_and_wait_or_death() )
{
@ -358,14 +385,14 @@ void InstallPackage(const char* tix_path)
const char* cmd_argv[] =
{
"tar",
print_string("--strip-components=%zu", num_strips),
"-C", collection,
"--extract",
"--file", tix_path,
"--keep-directory-symlink",
"--same-permissions",
"--no-same-owner",
data_and_prefix,
modern ? NULL : print_string("--strip-components=%zu", num_strips),
modern ? NULL : data_and_prefix,
NULL
};
execvp(cmd_argv[0], (char* const*) cmd_argv);
@ -373,7 +400,8 @@ void InstallPackage(const char* tix_path)
}
free(data_and_prefix);
if ( !already_installed )
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
if ( generation <= 2 && !already_installed )
MarkPackageAsInstalled(tix_directory_path, package_name);
string_array_reset(&tixinfo);

View File

@ -1,5 +1,5 @@
#!/bin/sh
# Copyright (c) 2022 Jonas 'Sortie' Termansen.
# Copyright (c) 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
@ -304,6 +304,9 @@ desired_version() {(
if [ -f "$port.rmpatch" ]; then
stamp="$stamp.$(cat "$port.rmpatch" | sha256sum | grep -Eo '^[^ ]*')"
fi
if [ -f "$port.post-install" ]; then
stamp="$stamp.$(cat "$port.post-install" | sha256sum | grep -Eo '^[^ ]*')"
fi
DEVELOPMENT=$(tix-vars -d false "$port.port" DEVELOPMENT)
if [ "$DEVELOPMENT" = true ]; then
stamp="$stamp.development"
@ -428,13 +431,20 @@ strip_tix() {(
strip=${host+$host-}strip
dir=$(mktemp -d)
tar -C "$dir" -xf "$1"
$strip -d "$dir/data/bin/"* 2>/dev/null || true
$strip -d "$dir/data/lib/"* 2>/dev/null || true
$strip -d "$dir/data/libexec"* 2>/dev/null || true
$strip -d "$dir/data/libexec/git-core/"* 2>/dev/null || true
$strip -d "$dir/data/sbin/"* 2>/dev/null || true
(cd "$dir" && tar --numeric-owner --owner=0 --group=0 -cJf port.tar.tix.xz tix data)
cp "$dir/port.tar.tix.xz" "$1"
# TODO: After releasing Sortix 1.1, remove the data directory compatibility.
if [ -e "$dir/data" ]; then
data_dir="$dir/data"
else
data_dir="$dir"
fi
$strip -d "$data_dir/bin/"* 2>/dev/null || true
$strip -d "$data_dir/lib/"* 2>/dev/null || true
$strip -d "$data_dir/libexec"* 2>/dev/null || true
$strip -d "$data_dir/libexec/git-core/"* 2>/dev/null || true
$strip -d "$data_dir/sbin/"* 2>/dev/null || true
(cd "$dir" &&
LC_ALL=C ls -A | grep -Ev '^tix$' |
tar --numeric-owner --owner=0 --group=0 -cJf "$1" tix -T -)
rm -rf "$dir"
)}

View File

@ -1,5 +1,5 @@
#!/bin/sh
# Copyright (c) 2017, 2021 Jonas 'Sortie' Termansen.
# Copyright (c) 2017, 2021, 2023 Jonas 'Sortie' Termansen.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@ -19,12 +19,14 @@
set -e
cachedir=""
cancel=false
clean=false
collection=""
collection=/
download_only=false
fetch_options=
ports_only=false
sysroot=""
upgrade=--upgrade
upgrade_ports=false
upgrade_system=false
wait=""
@ -50,12 +52,16 @@ for argument do
-w) wait=-w ;;
--cachedir=*) cachedir=$parameter ;;
--cachedir) previous_option=cachedir ;;
--cancel) cancel=true ;;
--clean) clean=true ;;
--collection=*) collection=$parameter ;;
--collection) previous_option=collection ;;
--download-only) download_only=true ;;
--fetch-options=*) fetch_options="$parameter" ;;
--fetch-options) previous_option=fetch_options ;;
--insecure-downgrade-to-http) fetch_options="$fetch_options $argument" ;;
--insecure-no-check-certificate) fetch_options="$fetch_options $argument" ;;
--no-upgrade) upgrade= ;;
--ports) upgrade_ports=true ;;
--system) upgrade_system=true ;;
--sysroot) previous_option=sysroot ;;
@ -74,91 +80,131 @@ if test -n "$previous_option"; then
exit 1
fi
# TODO: Reject additional operands.
if [ -z "$collection" ]; then
collection="$sysroot"
if [ 0 -lt $# ]; then
echo "$0: Unexpected extra operand: $1" >&2
exit 1
fi
if [ -n "$collection" ]; then
collection=$(cd "$collection" && pwd)
fi
conf() {
sed -E -e 's/([a-zA-Z0-9_]+) *? *= */\U\1=/' \
-e 's/=yes$/=true/' -e 's/no$/=false/' "$3" | \
tix-vars -d "$2" - "$4"
}
collection=$(cd "$collection" && pwd)
if ! $upgrade_ports && ! $upgrade_system; then
upgrade_ports=true
upgrade_system=true
if [ -e "$collection/etc/upgrade.conf" ]; then
upgrade_ports=$(conf -d true "$collection/etc/upgrade.conf" PORTS)
upgrade_system=$(conf -d true "$collection/etc/upgrade.conf" SYSTEM)
fi
fi
# Update only ports if this isn't a whole system installation.
# If this isn't a system installation, only upgrade the ports.
if [ ! -e "$collection/tix/manifest/system" ]; then
ports_only=true
upgrade_system=false
fi
if [ -z "$cachedir" ]; then
cachedir="$collection/var/cache/tix"
cachedir="${collection%/}/var/cache/tix"
fi
if $clean; then
if $cancel || $clean; then
echo "Removing cache directory: $cachedir"
rm -rf -- "$cachedir"
fi
if $cancel; then
sysmerge -t "$collection" --cancel
exit
fi
mkdir -p -- "$cachedir"
mkdir -p -- "$cachedir/new"
# Fetch latest release.sig.sh from the master and its matching sha256sum file.
# Fetch the latest official signed release.sh and its matching sha256sum file.
tix-fetch $fetch_options \
--output-release-sig-file="$cachedir/new/release.sh.sig" \
--output-sha256sum="$cachedir/new/sha256sum"
--collection="$collection" \
--output-release-file="$cachedir/new/release.sh" \
--output-sha256sum="$cachedir/new/sha256sum" \
--output-upgrade-file="$cachedir/new/upgrade.sh" \
$upgrade
# If release.sig.sh or sha256sum changed, clean the cache directory of downloads
# If release.sh or sha256sum changed, clean the cache directory of downloads
# that were currently in progress as they might not have the right checksums.
# TODO: This requires diff(1) in the base system! Avoid that. Get a cmp(1)!
if ! diff -- "$cachedir/release.sh.sig" \
"$cachedir/new/release.sh.sig" 1>/dev/null 2>/dev/null ||
! diff -- "$cachedir/sha256sum" \
"$cachedir/new/sha256sum" 1>/dev/null 2>/dev/null; then
if [ ! -e "$cachedir/release.sh" ] ||
[ ! -e "$cachedir/sha256sum" ] ||
[ ! -e "$cachedir/upgrade.sh" ] ||
! (cd "$cachedir/new" && sha256sum release.sh sha256sum upgrade.sh) |
(cd "$cachedir" && sha256sum -cs); then
rm -rf -- "$cachedir/boot"
rm -rf -- "$cachedir/repository"
rm -rf -- "$cachedir/sysroot"
fi
# Store the new release.sig.sh and sha256sum files so we can resume the download
# Store the new release.sh and sha256sum files so we can resume the download
# if cancelled and these files still match.
mv -- "$cachedir/new/release.sh.sig" "$cachedir/release.sh.sig"
mv -- "$cachedir/new/release.sh" "$cachedir/release.sh"
mv -- "$cachedir/new/sha256sum" "$cachedir/sha256sum"
mv -- "$cachedir/new/upgrade.sh" "$cachedir/upgrade.sh"
rm -rf -- "$cachedir/new"
mkdir -p "$cachedir/boot"
mkdir -p "$cachedir/repository"
# Check if we're upgrading to a new release.
UPGRADE_SIG_URL=$(tix-vars -d '' "$cachedir/upgrade.sh" UPGRADE_SIG_URL)
if [ -n "$UPGRADE_SIG_URL" ]; then
UPGRADE_CHANNEL=$(tix-vars "$cachedir/upgrade.sh" UPGRADE_CHANNEL)
UPGRADE_KEY=$(tix-vars "$cachedir/upgrade.sh" UPGRADE_KEY)
UPGRADE_NAME=$(tix-vars "$cachedir/upgrade.sh" UPGRADE_NAME)
if [ -n "$upgrade" ]; then
echo "Upgrading to $UPGRADE_NAME."
else
echo "Ignoring available upgrade to $UPGRADE_NAME."
fi
fi
mkdir -p -- "$cachedir/boot"
mkdir -p -- "$cachedir/repository"
# TODO: DO NOT SUBMIT: Temporary -d system compatibility until builds roll.
SYSTEM_INITRDS=$(tix-vars -d system "$cachedir/release.sh" SYSTEM_INITRDS)
# TODO: What about the system source code in /src?
if $upgrade_system; then
# Fetch the base system initrds from the mirror.
# TODO: What about the system source code in /src?
# TODO: Get the overlay initrd if it exists.
tix-fetch $fetch_options \
--input-release-sig-file="$cachedir/release.sh.sig" \
--input-sha256sum="$cachedir/sha256sum" \
-c --initrd -O "$cachedir/boot" -- system
for initrd in $SYSTEM_INITRDS; do
tix-fetch $fetch_options \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
-c --initrd -O "$cachedir/boot" -- "$initrd"
done
fi
if $upgrade_ports; then
# Fetch each port from the mirror.
ports=$(LC_ALL=C sort -- "$collection/tix/installed.list")
# TODO: This fails if the port does not exist upstream.
# TODO: Handle new mandatory / recommended ports.
# TODO: Handle renamed ports.
mkdir -p "$cachedir/repository"
for port in $ports; do
ports=""
for port in $(LC_ALL=C ls -- "$collection/tix/tixinfo"); do
# The port has a hash if if it exists upstream.
sha256=$(tix-fetch $fetch_options \
--input-release-sig-file="$cachedir/release.sh.sig" \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
--sha256 --port -- $port)
# If the port exists upstream, get the latest version.
# If the port exists upstream, get the latest version and note its name down
# for the extraction step.
if [ -n "$sha256" ]; then
tix-fetch $fetch_options \
--input-release-sig-file="$cachedir/release.sh.sig" \
--collection="$collection" \
--input-release-file="$cachedir/release.sh" \
--input-sha256sum="$cachedir/sha256sum" \
-c --port -O "$cachedir/repository" -- $port
ports="$ports $port"
fi
done
fi
@ -171,60 +217,47 @@ fi
rm -rf -- "$cachedir/sysroot"
mkdir -p -- "$cachedir/sysroot"
# Forward the upgrade metadata.
UPGRADE_SIG_URL=$(tix-vars -d '' "$cachedir/upgrade.sh" UPGRADE_SIG_URL)
if [ -n $upgrade ] && [ -n "$UPGRADE_SIG_URL" ]; then
mkdir -p -- "$cachedir/etc"
# TODO: More flexible and simple model.
cat > "$cachedir/etc/upgrade.conf" << EOF
channel = $UPGRADE_CHANNEL
release_key = $UPGRADE_KEY
release_sig_url = $UPGRADE_SIG_URL
EOF
fi
# Extract the base system into the sysroot.
if $upgrade_system; then
# Extract the base system into the sysroot.
rm -rf -- "$cachedir/sysroot"
mkdir -p -- "$cachedir/sysroot"
for initrd in system overlay; do
initrd_path="$cachedir/boot/$initrd.initrd"
if [ -e "$initrd_path.xz" ]; then
echo "Extracting $initrd.initrd.xz..."
unxz -k "$initrd_path.xz"
initrdfs "$initrd_path" extract -C "$cachedir/sysroot" /
rm -f "$initrd_path.xz"
fi
for initrd in $SYSTEM_INITRDS; do
echo "Extracting $initrd.tar.xz..."
tar -C "$cachedir/sysroot" -xJf "$cachedir/boot/$initrd.tar.xz"
rm -f "$cachedir/boot/$initrd.tar.xz"
done
fi
# TODO: These install all the ports in "$cachedir/repository" but the directory
# might not have been cleared, it could contain unverified ports.
if $upgrade_system; then
# Do a full system upgrade using the sysroot and the new ports.
# TODO: If sysmerge -w, such as on ABI upgrades, the e2fsprogs package is not
# installed in the system root and the new initrd might not contain a
# /sbin/fsck.ext2 and the system can become bricked if it needs a fsck
# on boot.
# TODO: There's no support here for doing a sysmerge on a given sysroot.
if $upgrade_ports; then
# TODO: Get the architecture from somewhere else than /etc/machine?
tix-collection "$cachedir/sysroot" create \
--platform="$(cat -- "$sysroot/etc/machine")-sortix" \
--prefix= --generation=2
tix-install --collection="$cachedir/sysroot" -- "$cachedir/repository/"*
full=--full
else
# TODO: Is this case supported?
full=
fi
sysmerge $full $wait "$cachedir/sysroot"
elif $upgrade_ports; then
# Upgrade to each of the new ports.
# TODO: tix-install --reinstall is unsupported due to lack of support for
# files moving between manifests or being deleted.
tix-install --reinstall -- "$cachedir/repository/"*
# Extract the ports into the sysroot.
full=
if $upgrade_ports; then
full=--full
for port in $ports; do
echo "Extracting $port.tix.tar.xz..."
tar -C "$cachedir/sysroot" -xJf "$cachedir/repository/$port.tix.tar.xz"
rm -f "$cachedir/repository/$port.tix.tar.xz"
done
fi
case "$upgrade_system$upgrade_ports" in
truefalse) what_to_upgrade=--system;;
falsetrue) what_to_upgrade=--ports;;
*) what_to_upgrade=;;
esac
# Merge the new sysroot onto the installation.
sysmerge -t "$collection" $what_to_upgrade $full $wait "$cachedir/sysroot"
rm -rf -- "$cachedir/boot"
rm -rf -- "$cachedir/repository"
rm -rf -- "$cachedir/sysroot"
#tmp=
#trap '[ -n "$tmp" ] && rm -rf "$tmp"' EXIT HUP INT QUIT TERM
#tmp=$(mktemp -dt upgrade.XXXXXX)
#mkdir -p "$tmp/tmp"
#export TMPDIR="$tmp/tmp"
#(cd "$tmp" && tix-fetch --release -o sortix.iso)
#mkdir -p "$tmp/sortix.iso.d"
#echo "Extracting sortix.iso..."
#xorriso -osirrox on -indev "$tmp/sortix.iso" -extract / "$tmp/sortix.iso.d" > /dev/null 2> /dev/null
#rm -f "$tmp/sortix.iso"

View File

@ -25,6 +25,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2015, 2016, 2017, 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
@ -27,6 +27,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
@ -45,7 +46,6 @@ typedef struct
char* tixdb_path;
string_array_t coll_conf;
string_array_t repo_list;
string_array_t inst_list;
bool reinstall;
} params_t;
@ -92,20 +92,36 @@ string_array_t GetPackageDependencies(params_t* params, const char* pkg_name)
if ( !pkg_path )
err(1, "unable to locate package `%s'", pkg_name);
const char* tixinfo_path = "tix/tixinfo";
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
bool modern = false;
const char* tixinfo_path = "tix/tixinfo/";
if ( !TarContainsFile(pkg_path, tixinfo_path) )
errx(1, "`%s' doesn't contain a `%s' file", pkg_path, tixinfo_path);
{
const char* tixinfo_path_old = "tix/tixinfo";
if ( !TarContainsFile(pkg_path, tixinfo_path_old) )
errx(1, "`%s' doesn't contain a `%s' directory", pkg_path,
tixinfo_path);
tixinfo_path = tixinfo_path_old;
modern = false;
}
string_array_t tixinfo = string_array_make();
FILE* tixinfo_fp = TarOpenFile(pkg_path, tixinfo_path);
dictionary_append_file(&tixinfo, tixinfo_fp);
switch ( variables_append_file(&tixinfo, tixinfo_fp) )
{
case -1: err(1, "%s: %s", pkg_path, tixinfo_path);
case -2: errx(1, "%s: %s: Syntax error", pkg_path, tixinfo_path);
}
fclose(tixinfo_fp);
VerifyTixInformation(&tixinfo, pkg_path);
const char* deps = dictionary_get_def(&tixinfo, "pkg.runtime-deps", "");
const char* deps = dictionary_get_def(&tixinfo, modern ? "RUNTIME_DEPS" :
"pkg.runtime-deps", "");
string_array_append_token_string(&ret, deps);
const char* alias = dictionary_get_def(&tixinfo, "pkg.alias-of", "");
const char* alias = dictionary_get_def(&tixinfo, modern ? "ALIAS_OF" :
"pkg.alias-of", "");
string_array_append_token_string(&ret, alias);
string_array_reset(&tixinfo);
@ -226,23 +242,22 @@ int main(int argc, char* argv[])
char* coll_conf_path = join_paths(params.tixdb_path, "collection.conf");
params.coll_conf = string_array_make();
if ( !dictionary_append_file_path(&params.coll_conf, coll_conf_path) )
err(1, "`%s'", coll_conf_path);
switch ( variables_append_file_path(&params.coll_conf, coll_conf_path) )
{
case -1: err(1, "%s", coll_conf_path);
case -2: errx(2, "%s: Syntax error", coll_conf_path);
}
VerifyTixCollectionConfiguration(&params.coll_conf, coll_conf_path);
free(coll_conf_path);
// TODO: Decide the fate of repository.list.
char* repo_list_path = join_paths(params.tixdb_path, "repository.list");
params.repo_list = string_array_make();
if ( !string_array_append_file_path(&params.repo_list, repo_list_path) )
if ( !string_array_append_file_path(&params.repo_list, repo_list_path) &&
errno != ENOENT )
err(1, "`%s'", repo_list_path);
free(repo_list_path);
char* inst_list_path = join_paths(params.tixdb_path, "installed.list");
params.inst_list = string_array_make();
if ( !string_array_append_file_path(&params.inst_list, inst_list_path) )
err(1, "`%s'", inst_list_path);
free(inst_list_path);
if ( argc == 1 )
errx(1, "error: no command specified.");
@ -254,10 +269,16 @@ int main(int argc, char* argv[])
string_array_t work = string_array_make();
char* tixinfo_dir = join_paths(params.tixdb_path, "tixinfo");
if ( !tixinfo_dir )
err(1, "malloc");
for ( int i = 2; i < argc; i++ )
{
const char* pkg_name = argv[i];
if ( string_array_contains(&params.inst_list, pkg_name) )
char* tixinfo = join_paths(tixinfo_dir, pkg_name);
bool installed = !access(tixinfo, F_OK);
free(tixinfo);
if ( installed )
{
if ( params.reinstall )
{
@ -270,6 +291,7 @@ int main(int argc, char* argv[])
GetPackageRecursiveDependencies(&params, &work, pkg_name);
}
free(tixinfo_dir);
for ( size_t i = 0; i < work.length; i++ )
InstallPackageOfName(&params, work.strings[i]);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, 2016, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 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
@ -20,7 +20,7 @@
#ifndef UTIL_H
#define UTIL_H
#define DEFAULT_GENERATION "2"
#define DEFAULT_GENERATION "3"
extern char** environ;
@ -326,34 +326,6 @@ void dictionary_normalize_entry(char* entry)
entry[output_off] = '\0';
}
void dictionary_append_file(string_array_t* sa, FILE* fp)
{
char* entry = NULL;
size_t entry_size = 0;
ssize_t entry_length;
while ( 0 < (entry_length = getline(&entry, &entry_size, fp)) )
{
if ( entry[entry_length-1] == '\n' )
entry[--entry_length] = '\0';
dictionary_normalize_entry(entry);
if ( entry[0] == '#' )
continue;
string_array_append(sa, entry);
}
free(entry);
assert(!ferror(fp));
}
bool dictionary_append_file_path(string_array_t* sa, const char* path)
{
FILE* fp = fopen(path, "r");
if ( !fp )
return false;
dictionary_append_file(sa, fp);
fclose(fp);
return true;
}
bool is_identifier_char(char c)
{
return ('a' <= c && c <= 'z') ||
@ -595,10 +567,24 @@ const char* getenv_def(const char* var, const char* def)
int mkdir_p(const char* path, mode_t mode)
{
int saved_errno = errno;
if ( mkdir(path, mode) != 0 && errno != EEXIST )
return -1;
errno = saved_errno;
return 0;
if ( !mkdir(path, mode) )
return 0;
if ( errno == ENOENT )
{
char* prev = strdup(path);
if ( !prev )
return -1;
int status = mkdir_p(dirname(prev), mode | 0500);
free(prev);
if ( status < 0 )
return -1;
errno = saved_errno;
if ( !mkdir(path, mode) )
return 0;
}
if ( errno == EEXIST )
return errno = saved_errno, 0;
return -1;
}
static void compact_arguments(int* argc, char*** argv)
@ -925,23 +911,39 @@ void VerifyCommandLineCollection(char** collection)
void VerifyTixCollectionConfiguration(string_array_t* info, const char* path)
{
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
const char* tix_version = dictionary_get(info, "tix.version");
if ( !tix_version )
errx(1, "error: `%s': no `tix.version' variable declared", path);
if ( atoi(tix_version) != 1 )
errx(1, "error: `%s': tix version `%s' not supported", path,
tix_version);
const char* tix_class = dictionary_get(info, "tix.class");
if ( !tix_class )
errx(1, "error: `%s': no `tix.class' variable declared", path);
if ( strcmp(tix_class, "collection") != 0 )
errx(1, "error: `%s': error: unexpected tix class `%s'.", path,
tix_class);
if ( !(dictionary_get(info, "collection.prefix")) )
errx(1, "error: `%s': no `collection.prefix' variable declared", path);
if ( !(dictionary_get(info, "collection.platform")) )
errx(1, "error: `%s': no `collection.platform' variable declared",
path);
if ( tix_version )
{
if ( !tix_version )
errx(1, "error: `%s': no `tix.version' variable declared", path);
if ( atoi(tix_version) != 1 )
errx(1, "error: `%s': tix version `%s' not supported", path,
tix_version);
const char* tix_class = dictionary_get(info, "tix.class");
if ( !tix_class )
errx(1, "error: `%s': no `tix.class' variable declared", path);
if ( strcmp(tix_class, "collection") != 0 )
errx(1, "error: `%s': error: unexpected tix class `%s'.", path,
tix_class);
if ( !(dictionary_get(info, "collection.prefix")) )
errx(1, "error: `%s': no `collection.prefix' variable declared",
path);
if ( !(dictionary_get(info, "collection.platform")) )
errx(1, "error: `%s': no `collection.platform' variable declared",
path);
return;
}
const char* version = dictionary_get(info, "TIX_COLLECTION_VERSION");
if ( !version )
errx(1, "%s: Mandatory TIX_COLLECTION_VERSION was not set", path);
if ( atoi(version) != 3 )
errx(1, "%s: Unsupported: TIX_COLLECTION_VERSION: %s", path, version);
if ( !(dictionary_get(info, "PREFIX")) )
errx(1, "%s: Mandatory PREFIX was not set", path);
if ( !(dictionary_get(info, "PLATFORM")) )
errx(1, "%s: Mandatory PLATFORM was not set", path);
}
static pid_t original_pid;
@ -1032,6 +1034,37 @@ void fprint_shell_variable_assignment(FILE* fp, const char* variable, const char
}
}
bool needs_single_quote(const char* string)
{
for ( size_t i = 0; string[i]; i++ )
{
char c = string[i];
if ( !(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9') ||
c == '/' || c == '_' || c == '.' || c == '+' || c == ':' ||
c == '%' || c == '$' || c == '{' || c == '}' || c == '-') )
return true;
}
return false;
}
void fwrite_variable(FILE* fp, const char* key, const char* value)
{
fprintf(fp, "%s=", key);
if ( !needs_single_quote(value) )
fprintf(fp, "%s\n", value);
else
{
fputc('\'', fp);
for ( size_t i = 0; value[i]; i++ )
if ( value[i] == '\'' )
fprintf(fp, "'\\''");
else
fputc(value[i], fp);
fputs("'\n", fp);
}
}
bool is_success_exit_status(int status)
{
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
@ -1149,6 +1182,7 @@ int recovery_execvp(const char* path, char* const* argv)
}
printf("\n");
fflush(stdout);
if ( recovery_configure_state(false) == RECOVERY_STATE_PRINT_COMMAND )
_exit(0);

View File

@ -1,5 +1,5 @@
#!/bin/sh
# Copyright (c) 2015, 2016, 2017 Jonas 'Sortie' Termansen.
# Copyright (c) 2015, 2016, 2017, 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
@ -14,11 +14,14 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# update-initrd
# Generate a mkinitrd that locates and chain boots the real root filesystem.
# Generate an initrd(7) that locates and chain boots the real root filesystem.
# This script will run on the previous stable release during a sysmerge(8).
set -e
sysroot=
sysmerge=false
dashdash=
previous_option=
@ -35,6 +38,7 @@ for argument do
esac
case $dashdash$argument in
--) dashdash=yes ;;
--sysmerge) sysmerge=true ;;
--sysroot=*) sysroot=$parameter ;;
--sysroot) previous_option=sysroot ;;
-*) echo "$0: unrecognized option $argument" >&2
@ -49,17 +53,20 @@ if test -n "$previous_option"; then
exit 1
fi
sysmerge=false
exec_prefix="$sysroot"
if [ -d "$sysroot/sysmerge" ]; then
# If an upgrade is pending, invoke the update-initrd of the new system, if
# we're not already it.
if [ "$(realpath -- "$(which -- "$0")")" != \
"$(realpath -- "$sysroot/sysmerge/sbin/update-initrd")" ]; then
exec "$sysroot/sysmerge/sbin/update-initrd" "$@"
fi
root="$sysroot"
output="$sysroot"
if $sysmerge; then
root="$sysroot/sysmerge"
output="$root"
# TODO: After releasing Sortix 1.1, remove the check for /sysmerge as the old
# update-initrd will invoke the newer version and expect the new version
# to notice that it is the version in /sysmerge. However, write the new
# initrd to /boot rather than /sysmerge/boot since 1.0 doesn't implement
# the new scheme.
elif [ -e /sysmerge -a "$0" = /sysmerge/sbin/update-initrd ]; then
sysmerge=true
exec_prefix="$sysroot/sysmerge"
root="$sysroot/sysmerge"
output="$sysroot"
fi
if [ ! -e "$sysroot/etc/fstab" ]; then
echo "$0: $sysroot/etc/fstab: Need a filesystem table to make an initrd" >&2
@ -69,10 +76,10 @@ tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT TERM
mkdir "$tmp/bin"
mkdir "$tmp/sbin"
cp "$exec_prefix/sbin/init" "$tmp/sbin"
cp "$exec_prefix/sbin/extfs" "$tmp/sbin"
test -f "$exec_prefix/sbin/fsck.ext2" &&
cp "$exec_prefix/sbin/fsck.ext2" "$tmp/sbin"
cp "$root/sbin/init" "$tmp/sbin"
cp "$root/sbin/extfs" "$tmp/sbin"
test -f "$root/sbin/fsck.ext2" &&
cp "$root/sbin/fsck.ext2" "$tmp/sbin"
mkdir "$tmp/etc"
cp "$sysroot/etc/fstab" "$tmp/etc/fstab"
mkdir "$tmp/etc/init"
@ -85,5 +92,7 @@ else
require chain exit-code
EOF
fi
mkdir -p "$sysroot/boot"
mkinitrd --format=sortix-initrd-2 "$tmp" -o "$sysroot/boot/sortix.initrd" > /dev/null
mkdir -p "$output/boot"
LC_AL=C ls -A "$tmp" |
tar -C "$tmp" -cf "$output/boot/sortix.initrd" \
--numeric-owner --owner=0 --group=0 -T -

View File

@ -7,6 +7,7 @@
.Sh SYNOPSIS
.Nm update-initrd
.Op Fl \-sysroot Ns "=" Ns Ar sysroot
TODO: --sysmerge
.Sh DESCRIPTION
.Nm update-initrd
generates the
@ -31,14 +32,6 @@ should be regenerated by invoking
.Nm .
.Pp
.Nm
is aware of
.Xr sysmerge 8
pending upgrades and will instead invoke the new
.Nm
in
.Pa /sysmerge
if an upgrade is pending.
.Nm
is written as a script so the initrd of the new system can produced even
across incompatible ABI changes.
.Pp
@ -51,6 +44,7 @@ for the root filesystem, locate files in the
.Ar sysroot
directory instead and store the result as
.Ar sysroot Ns Pa /boot/sortix.initrd .
TODO: --sysmerge
.El
.Sh FILES
.Bl -tag -width "/sbin/fsck.ext2" -compact
@ -63,12 +57,11 @@ directory instead and store the result as
.Nm
will exit 0 on success and non-zero otherwise.
.Sh SEE ALSO
.Xr tar 1 ,
.Xr fstab 5 ,
.Xr initrd 7 ,
.Xr disked 8 ,
.Xr extfs 8 ,
.Xr init 8 ,
.Xr initrdfs 8 ,
.Xr mkinitrd 8 ,
.Xr sysmerge 8 ,
.Xr update-grub 8

1
utils/.gitignore vendored
View File

@ -21,6 +21,7 @@ expr
false
find
getaddrinfo
getty
halt
head
help

View File

@ -93,6 +93,7 @@ uname.1 \
SBINS=\
chroot \
getty \
unmount \
MANPAGES8=\

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2015, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -106,6 +106,7 @@ int main(int argc, char* argv[])
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
// Mount the current device directory inside the new root filesystem.
int old_dev_fd = open("/dev", O_DIRECTORY | O_RDONLY);
@ -119,6 +120,7 @@ int main(int argc, char* argv[])
sigaddset(&sigs, SIGHUP);
sigaddset(&sigs, SIGINT);
sigaddset(&sigs, SIGQUIT);
sigaddset(&sigs, SIGTERM);
sigprocmask(SIG_BLOCK, &sigs, &oldset);
pid_t child_pid = fork();
if ( child_pid < 0 )
@ -146,6 +148,7 @@ int main(int argc, char* argv[])
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
sigprocmask(SIG_SETMASK, &oldset, NULL);
}

114
utils/getty.c Normal file
View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF7
* 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.
*
* getty.c
* Initialize a terminal session.
*/
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <brand.h>
#include <err.h>
#include <fcntl.h>
#include <termios.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
bool logo = true;
struct winsize ws;
ws.ws_row = 24;
ws.ws_col = 80;
int opt;
while ( (opt = getopt(argc, argv, "h:lw:")) != -1 )
{
switch ( opt )
{
case 'l': logo = true; break;
case 'h': ws.ws_row = atoi(optarg); break;
case 'w': ws.ws_col = atoi(optarg); break;
default: return 1;
}
}
if ( argc - optind < 1 )
errx(1, "Expected terminal path");
if ( argc - optind < 2 )
errx(1, "Expected program");
const char* path = argv[optind + 0];
int tty = open(path, O_RDWR);
if ( tty < 0 )
err(1, "%s", path);
if ( !isatty(tty) )
err(1, "%s", path);
struct termios tio;
if ( tcgetattr(tty, &tio) < 0 )
err(1, "tcgetattr: %s", path);
pid_t child_pid = fork();
if ( child_pid < 0 )
err(1, "fork");
if ( child_pid )
{
int status;
waitpid(child_pid, &status, 0);
return WEXITSTATUS(status);
}
if ( setsid() < 0 )
err(1, "setsid");
if ( ioctl(tty, TIOCSCTTY) < 0 )
err(1, "ioctl: TIOCSCTTY");
if ( close(0) < 0 || close(1) < 0 || close(2) < 0 )
err(1, "close");
if ( dup2(tty, 0) != 0 ||
dup2(tty, 1) != 1 ||
dup2(tty, 2) != 2 )
err(1, "dup");
if ( closefrom(3) < 0 )
err(1, "closefrom");
tty = 0;
if ( ioctl(tty, TIOCSWINSZ, &ws) < 0 )
err(1, "TIOCSWINSZ");
tio.c_cflag |= CREAD;
if ( tcsetattr(tty, TCSANOW, &tio) < 0 )
err(1, "tcsetattr: %s", path);
if ( logo )
{
printf("\e[37;41m\e[J");
const char* string = BRAND_LOGO;
while ( *string )
{
size_t string_width = strcspn(string, "\n");
size_t leading = string_width <= ws.ws_col ?
(ws.ws_col - string_width) / 2 : 0;
for ( size_t i = 0; i < leading; i++ )
putchar(' ');
fwrite(string, string_width, 1, stdout);
string += string_width;
if ( *string == '\n' )
{
string++;
if ( !*string )
printf("\e[m");
putchar('\n');
}
}
}
execvp(argv[optind + 1], argv + optind + 1);
err(1, "%s", argv[optind + 1]);
}