Compare commits

...

13 Commits

Author SHA1 Message Date
Jonas 'Sortie' Termansen 4533a2ade2 Remove mkinitrd(8). 2023-08-26 14:15:12 +02:00
Jonas 'Sortie' Termansen 6ac0061380 Add sysmerge(8) --target option. 2023-08-26 14:15:12 +02:00
Jonas 'Sortie' Termansen 6303a59439 Handle SIGTERM in chroot(8). 2023-08-26 14:15:12 +02:00
Jonas 'Sortie' Termansen e5465e64a1 Fix tix3g migration hook installing the wrong PLATFORM in collection.conf.
Fix the hook marker not existing and change its name to allow upgrading from
broken experimental builds.
2023-08-26 14:04:22 +02:00
Nathan Fisher 93dd46192d Fix gettext port build by un-confusing the build system about whether c#
is available
2023-08-05 22:36:39 -04: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
45 changed files with 1087 additions and 3163 deletions

View File

@ -27,7 +27,6 @@ init \
kblayout \
kblayout-compiler \
login \
mkinitrd \
ping \
regress \
rw \
@ -73,10 +72,10 @@ include build-aux/dirs.mak
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.initrd
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.initrd
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.initrd
SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.initrd
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.tar
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.tar
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.tar
SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.tar
.PHONY: all
all: sysroot
@ -101,7 +100,6 @@ sysmerge-wait: sysroot
clean-build-tools:
$(MAKE) -C carray clean
$(MAKE) -C kblayout-compiler clean
$(MAKE) -C mkinitrd clean
$(MAKE) -C sf clean
$(MAKE) -C tix clean
@ -109,7 +107,6 @@ clean-build-tools:
build-tools:
$(MAKE) -C carray
$(MAKE) -C kblayout-compiler
$(MAKE) -C mkinitrd
$(MAKE) -C sf
$(MAKE) -C tix
@ -117,7 +114,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
@ -455,7 +451,6 @@ $(LIVE_INITRD): sysroot
echo "include /etc/default/passwd.d/*" >> $(LIVE_INITRD).d/etc/passwd
echo "root::0:root" > $(LIVE_INITRD).d/etc/group
echo "include /etc/default/group.d/*" >> $(LIVE_INITRD).d/etc/group
mkdir -p $(LIVE_INITRD).d/home
mkdir -p $(LIVE_INITRD).d/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(LIVE_INITRD).d/root
(echo "You can view the documentation for new users by typing:" && \
@ -465,20 +460,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
@ -499,29 +498,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
@ -555,25 +554,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
@ -111,10 +111,10 @@ isinset() {
cd "$directory"
kernel=$(maybe_compressed boot/sortix.bin)
live_initrd=$(maybe_compressed boot/live.initrd)
overlay_initrd=$(maybe_compressed boot/overlay.initrd)
src_initrd=$(maybe_compressed boot/src.initrd)
system_initrd=$(maybe_compressed boot/system.initrd)
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)
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//')
@ -371,7 +371,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;
}

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

@ -200,33 +200,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

@ -13,5 +13,5 @@ LICENSE=GPL-3.0-or-later
# them as optional.
SUBDIR=gettext-tools
BUILD_SYSTEM=configure
CONFIGURE_ARGS='--with-installed-libtextstyle --with-installed-csharp-dll'
CONFIGURE_ARGS='--with-installed-libtextstyle --with-installed-csharp-dll --disable-csharp'
POST_INSTALL=tix-eradicate-libtool-la

View File

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

View File

@ -69,6 +69,26 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
.Xr grep 1
for it after a release.
.Sh CHANGES
.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

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

@ -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
@ -148,9 +148,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

@ -58,6 +58,7 @@ 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
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);

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,12 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -111,6 +114,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 +425,44 @@ 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");
}
}
void upgrade_finalize(const struct release* old_release,
@ -365,6 +474,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,

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,6 +88,40 @@ 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

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,20 @@ 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)--;
}
}
}
static bool has_pending_upgrade(void)
{
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;
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");
if ( !kernel || !initrd || !sysmerge )
err(2, "malloc");
bool result = access_or_die(kernel, F_OK) == 0 ||
access_or_die(initrd, F_OK) == 0 ||
access_or_die(sysmerge, F_OK) == 0;
free(kernel);
free(initrd);
free(sysmerge);
return result;
}
int main(int argc, char* argv[])
@ -112,46 +109,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 +162,65 @@ 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;
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* kernel = join_paths(target, "boot/sortix.bin.sysmerge.orig");
char* kernel_real = join_paths(target, "boot/sortix.bin");
char* initrd = join_paths(target, "boot/sortix.initrd.sysmerge.orig");
char* initrd_real = join_paths(target, "boot/sortix.initrd");
char* sysmerge = join_paths(target, "sysmerge");
if ( !kernel || !kernel_real || !initrd || !initrd_real || !sysmerge )
err(2, "malloc");
rename(kernel, kernel_real);
rename(initrd, initrd_real);
execute((const char*[]) { "rm", "-rf", "--", sysmerge, NULL }, "");
execute((const char*[]) { "update-initrd", NULL }, "ce", target);
printf("Cancelled pending system upgrade.\n");
did_cancel = true;
}
@ -200,50 +232,71 @@ 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");
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 can_run_new_abi =
abi_compatible(new_release.abi_major, new_release.abi_minor,
@ -300,21 +353,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");
@ -330,37 +395,61 @@ int main(int argc, char* argv[])
if ( copy_files )
{
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* kernel = join_paths(target, "boot/sortix.bin.sysmerge.orig");
char* kernel_real = join_paths(target, "boot/sortix.bin");
char* kernel_new = join_paths(target, "sysmerge/boot/sortix.bin");
char* initrd = join_paths(target, "boot/sortix.initrd.sysmerge.orig");
char* initrd_real = join_paths(target, "boot/sortix.initrd");
if ( !system_path || !ports_path || !full_path || !kernel ||
!kernel_real || !kernel_new || !initrd || !initrd_real )
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);
}
execute((const char*[]) { "cp", kernel_real, kernel, NULL }, "e");
execute((const char*[]) { "cp", initrd_real, initrd, NULL }, "e");
execute((const char*[]) { "cp", kernel_new, kernel_real, NULL }, "e");
execute((const char*[]) { "/sysmerge/sbin/update-initrd", NULL }, "ce",
target);
printf("The system will be upgraded to %s on the next boot.\n",
new_release.pretty_name);
@ -370,15 +459,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);
@ -387,46 +477,62 @@ int main(int argc, char* argv[])
return 0;
}
if ( booting )
if ( has_system && booting )
{
unlink("/boot/sortix.bin.sysmerge.orig");
unlink("/boot/sortix.initrd.sysmerge.orig");
execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
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");
if ( !kernel || !initrd || !sysmerge )
err(2, "malloc");
unlink(kernel);
unlink(initrd);
execute((const char*[]) { "rm", "-rf", "--", sysmerge, NULL }, "");
free(kernel);
free(initrd);
free(sysmerge);
}
if ( access_or_die("/etc/fstab", F_OK) == 0 )
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);
free(boot_path);
// TODO: A better design for finding the parent block device of a
// partition without scanning every 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");
execute((const char*[]) { "update-grub", NULL }, "ceqQ", target);
}
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");
execute((const char*[]) { "/etc/grub.d/10_sortix", NULL }, "ceq",
target);
}
}
printf("Successfully upgraded to %s.\n", new_release.pretty_name);
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

@ -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 @@
/*
* 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

@ -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 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 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
@ -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);
@ -224,23 +240,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.");
@ -252,10 +267,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 )
{
@ -268,6 +289,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,7 +14,7 @@
# 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.
set -e
@ -86,4 +86,6 @@ require chain exit-code
EOF
fi
mkdir -p "$sysroot/boot"
mkinitrd --format=sortix-initrd-2 "$tmp" -o "$sysroot/boot/sortix.initrd" > /dev/null
LC_AL=C ls -A "$tmp" |
tar -C "$tmp" -cf "$sysroot/boot/sortix.initrd" \
--numeric-owner --owner=0 --group=0 -T -

View File

@ -63,12 +63,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

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