Compare commits
13 Commits
917722cf70
...
4533a2ade2
Author | SHA1 | Date |
---|---|---|
Jonas 'Sortie' Termansen | 4533a2ade2 | |
Jonas 'Sortie' Termansen | 6ac0061380 | |
Jonas 'Sortie' Termansen | 6303a59439 | |
Jonas 'Sortie' Termansen | e5465e64a1 | |
Nathan Fisher | 93dd46192d | |
Jonas 'Sortie' Termansen | 33c1e98f0e | |
Jonas 'Sortie' Termansen | d189183900 | |
Jonas 'Sortie' Termansen | b819428bd2 | |
Jonas 'Sortie' Termansen | 4990cef43c | |
Jonas 'Sortie' Termansen | ab9f2353e5 | |
Jonas 'Sortie' Termansen | edd8566155 | |
Jonas 'Sortie' Termansen | ffc1b02b94 | |
Jonas 'Sortie' Termansen | e933eb5a1c |
67
Makefile
67
Makefile
|
@ -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
|
||||
|
|
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*.iso
|
||||
*.so
|
||||
*.a
|
||||
*.initrd
|
||||
*.out
|
||||
kb/default-kblayout
|
||||
kb/default-kblayout.h
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2016 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* sortix/initrd.h
|
||||
* The Sortix init ramdisk filesystem format.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SORTIX_INITRD_H
|
||||
#define _INCLUDE_SORTIX_INITRD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define INITRD_ALGO_CRC32 0
|
||||
#define INITRD_ALGO_NONE 1
|
||||
|
||||
#define INITRD_S_IXOTH 01
|
||||
#define INITRD_S_IWOTH 02
|
||||
#define INITRD_S_IROTH 03
|
||||
#define INITRD_S_IRWXO 07
|
||||
#define INITRD_S_IXGRP 010
|
||||
#define INITRD_S_IWGRP 020
|
||||
#define INITRD_S_IRGRP 040
|
||||
#define INITRD_S_IRWXG 070
|
||||
#define INITRD_S_IXUSR 0100
|
||||
#define INITRD_S_IWUSR 0200
|
||||
#define INITRD_S_IRUSR 0400
|
||||
#define INITRD_S_IRWXU 0700
|
||||
#define INITRD_S_IFMT 0xF000
|
||||
#define INITRD_S_IFSOCK 0xC000
|
||||
#define INITRD_S_IFLNK 0xA000
|
||||
#define INITRD_S_IFREG 0x8000
|
||||
#define INITRD_S_IFBLK 0x6000
|
||||
#define INITRD_S_IFDIR 0x4000
|
||||
#define INITRD_S_IFCHR 0x2000
|
||||
#define INITRD_S_IFIFO 0x1000
|
||||
#define INITRD_S_ISUID 0x0800
|
||||
#define INITRD_S_ISGID 0x0400
|
||||
#define INITRD_S_ISVTX 0x0200
|
||||
#define INITRD_S_ISSOCK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFSOCK)
|
||||
#define INITRD_S_ISLNK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFLNK)
|
||||
#define INITRD_S_ISREG(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFREG)
|
||||
#define INITRD_S_ISBLK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFBLK)
|
||||
#define INITRD_S_ISDIR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFDIR)
|
||||
#define INITRD_S_ISCHR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFCHR)
|
||||
#define INITRD_S_ISFIFO(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFIFO)
|
||||
|
||||
typedef struct initrd_superblock
|
||||
{
|
||||
char magic[16]; /* "sortix-initrd-2" */
|
||||
uint32_t fssize;
|
||||
uint32_t revision;
|
||||
uint32_t inodesize;
|
||||
uint32_t inodecount;
|
||||
uint32_t inodeoffset;
|
||||
uint32_t root;
|
||||
uint32_t sumalgorithm;
|
||||
uint32_t sumsize;
|
||||
} initrd_superblock_t;
|
||||
|
||||
typedef struct initrd_inode
|
||||
{
|
||||
uint32_t mode;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t nlink;
|
||||
uint64_t ctime;
|
||||
uint64_t mtime;
|
||||
uint32_t dataoffset;
|
||||
uint32_t size;
|
||||
} initrd_inode_t;
|
||||
|
||||
typedef struct initrd_dirent
|
||||
{
|
||||
uint32_t inode;
|
||||
uint16_t reclen;
|
||||
uint16_t namelen;
|
||||
char name[0];
|
||||
} initrd_dirent_t;
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 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);
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
mkinitrd
|
||||
initrdfs
|
|
@ -1,35 +0,0 @@
|
|||
include ../build-aux/platform.mak
|
||||
include ../build-aux/compiler.mak
|
||||
include ../build-aux/version.mak
|
||||
include ../build-aux/dirs.mak
|
||||
|
||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||
CFLAGS?=$(OPTLEVEL)
|
||||
|
||||
SORTIXKERNEL=../kernel
|
||||
|
||||
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\" -I$(SORTIXKERNEL)/include -I.
|
||||
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
||||
|
||||
ifeq ($(HOST_IS_SORTIX),0)
|
||||
CPPFLAGS+=-D_GNU_SOURCE
|
||||
endif
|
||||
|
||||
BINARIES=mkinitrd initrdfs
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
%: %.c rules.c serialize.c
|
||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< rules.c serialize.c -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(BINARIES)
|
||||
|
||||
install: all
|
||||
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||
install $(BINARIES) $(DESTDIR)$(SBINDIR)
|
||||
mkdir -p $(DESTDIR)$(MANDIR)/man8
|
||||
cp initrdfs.8 $(DESTDIR)$(MANDIR)/man8/initrdfs.8
|
||||
cp mkinitrd.8 $(DESTDIR)$(MANDIR)/man8/mkinitrd.8
|
|
@ -1,67 +0,0 @@
|
|||
.Dd October 7, 2015
|
||||
.Dt INITRDFS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm initrdfs
|
||||
.Nd view initialization ramdisk
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Ar initrd
|
||||
cat
|
||||
.Ar path ...
|
||||
.Nm
|
||||
.Ar initrd
|
||||
extract
|
||||
.Op Fl C Ar destination
|
||||
.Ar path ...
|
||||
.Nm
|
||||
.Ar initrd
|
||||
ls
|
||||
.Op Fl a
|
||||
.Ar path ...
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
opens a
|
||||
.Xr initrd 7
|
||||
made with
|
||||
.Xr mkinitrd 8
|
||||
and lets you view the stored directories and files.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "12345678"
|
||||
.It Fl a
|
||||
.Sy ( ls )
|
||||
Include directory entries whose names begin with a
|
||||
dot
|
||||
.Pq Sq \&. .
|
||||
.It Fl C Ar destination
|
||||
.Sy ( extract )
|
||||
Extract to the specified
|
||||
.Ar destination
|
||||
rather than the default current directory.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
supports these commands:
|
||||
.Bl -tag -width "12345678"
|
||||
.It Sy cat
|
||||
Show the contents of each
|
||||
.Ar path .
|
||||
.It Sy extract
|
||||
Extract each
|
||||
.Ar path
|
||||
recursively to the current directory.
|
||||
.It Sy ls
|
||||
List each directory
|
||||
.Ar path .
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Nm
|
||||
will exit 0 on success and non-zero otherwise.
|
||||
.Sh SEE ALSO
|
||||
.Xr initrd 7 ,
|
||||
.Xr mkinitrd 8
|
||||
.Sh BUGS
|
||||
.Nm
|
||||
is feature limited and doesn't let you view all the contained meta information.
|
||||
It's also not a filesystem driver.
|
|
@ -1,395 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* initrdfs.c
|
||||
* Provides access to filesystems in the Sortix kernel initrd format.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <ioleast.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sortix/initrd.h>
|
||||
|
||||
#include "serialize.h"
|
||||
|
||||
char* Substring(const char* str, size_t start, size_t length)
|
||||
{
|
||||
char* result = (char*) malloc(length+1);
|
||||
strncpy(result, str + start, length);
|
||||
result[length] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ReadSuperBlock(int fd, initrd_superblock_t* dest)
|
||||
{
|
||||
if ( preadall(fd, dest, sizeof(*dest), 0) != sizeof(*dest) )
|
||||
return false;
|
||||
import_initrd_superblock(dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
initrd_superblock_t* GetSuperBlock(int fd)
|
||||
{
|
||||
size_t sbsize = sizeof(initrd_superblock_t);
|
||||
initrd_superblock_t* sb = (initrd_superblock_t*) malloc(sbsize);
|
||||
if ( !sb ) { return NULL; }
|
||||
if ( !ReadSuperBlock(fd, sb) ) { free(sb); return NULL; }
|
||||
return sb;
|
||||
}
|
||||
|
||||
bool ReadInode(int fd, initrd_superblock_t* sb, uint32_t ino,
|
||||
initrd_inode_t* dest)
|
||||
{
|
||||
uint32_t inodepos = sb->inodeoffset + sb->inodesize * ino;
|
||||
if ( preadall(fd, dest, sizeof(*dest), inodepos) != sizeof(*dest) )
|
||||
return false;
|
||||
import_initrd_inode(dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
initrd_inode_t* GetInode(int fd, initrd_superblock_t* sb, uint32_t ino)
|
||||
{
|
||||
initrd_inode_t* inode = (initrd_inode_t*) malloc(sizeof(initrd_inode_t));
|
||||
if ( !inode ) { return NULL; }
|
||||
if ( !ReadInode(fd, sb, ino, inode) ) { free(inode); return NULL; }
|
||||
return inode;
|
||||
}
|
||||
|
||||
initrd_inode_t* CloneInode(const initrd_inode_t* src)
|
||||
{
|
||||
initrd_inode_t* result = (initrd_inode_t*) malloc(sizeof(*src));
|
||||
if ( !result ) { return NULL; }
|
||||
memcpy(result, src, sizeof(*src));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ReadInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode,
|
||||
uint8_t* dest, size_t size, off_t offset)
|
||||
{
|
||||
(void) sb;
|
||||
if ( offset < 0 )
|
||||
return errno = EINVAL, false;
|
||||
if ( inode->size < (uintmax_t) offset )
|
||||
return errno = EINVAL, false;
|
||||
size_t available = inode->size - offset;
|
||||
if ( inode->size < available )
|
||||
return errno = EINVAL, false;
|
||||
return preadall(fd, dest, size, inode->dataoffset + offset) == size;
|
||||
}
|
||||
|
||||
uint8_t* GetInodeDataSize(int fd, initrd_superblock_t* sb,
|
||||
initrd_inode_t* inode, size_t size)
|
||||
{
|
||||
uint8_t* buf = (uint8_t*) malloc(size);
|
||||
if ( !buf )
|
||||
return NULL;
|
||||
if ( !ReadInodeData(fd, sb, inode, buf, size, 0) )
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
uint8_t* GetInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode)
|
||||
{
|
||||
return GetInodeDataSize(fd, sb, inode, inode->size);
|
||||
}
|
||||
|
||||
uint32_t Traverse(int fd, initrd_superblock_t* sb, initrd_inode_t* inode,
|
||||
const char* name)
|
||||
{
|
||||
if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; }
|
||||
uint8_t* direntries = GetInodeData(fd, sb, inode);
|
||||
if ( !direntries ) { return 0; }
|
||||
uint32_t result = 0;
|
||||
uint32_t offset = 0;
|
||||
while ( offset < inode->size )
|
||||
{
|
||||
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
|
||||
import_initrd_dirent(dirent);
|
||||
if ( dirent->namelen && !strcmp(dirent->name, name) )
|
||||
{
|
||||
result = dirent->inode;
|
||||
export_initrd_dirent(dirent);
|
||||
break;
|
||||
}
|
||||
offset += dirent->reclen;
|
||||
export_initrd_dirent(dirent);
|
||||
}
|
||||
free(direntries);
|
||||
if ( !result ) { errno = ENOENT; }
|
||||
return result;
|
||||
}
|
||||
|
||||
initrd_inode_t* ResolvePath(int fd, initrd_superblock_t* sb,
|
||||
initrd_inode_t* inode, const char* path)
|
||||
{
|
||||
if ( !path[0] ) { return CloneInode(inode); }
|
||||
if ( path[0] == '/' )
|
||||
{
|
||||
if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return NULL; }
|
||||
return ResolvePath(fd, sb, inode, path+1);
|
||||
}
|
||||
size_t elemlen = strcspn(path, "/");
|
||||
char* elem = Substring(path, 0, elemlen);
|
||||
uint32_t ino = Traverse(fd, sb, inode, elem);
|
||||
free(elem);
|
||||
if ( !ino ) { return NULL; }
|
||||
initrd_inode_t* child = GetInode(fd, sb, ino);
|
||||
if ( !child ) { return NULL; }
|
||||
if ( !path[elemlen] ) { return child; }
|
||||
initrd_inode_t* result = ResolvePath(fd, sb, child, path + elemlen);
|
||||
free(child);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ListDirectory(int fd, initrd_superblock_t* sb, initrd_inode_t* dir,
|
||||
bool all)
|
||||
{
|
||||
if ( !INITRD_S_ISDIR(dir->mode) ) { errno = ENOTDIR; return false; }
|
||||
uint8_t* direntries = GetInodeData(fd, sb, dir);
|
||||
if ( !direntries ) { return false; }
|
||||
uint32_t offset = 0;
|
||||
while ( offset < dir->size )
|
||||
{
|
||||
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
|
||||
if ( dirent->namelen && (all || dirent->name[0] != '.'))
|
||||
{
|
||||
printf("%s\n", dirent->name);
|
||||
}
|
||||
offset += dirent->reclen;
|
||||
}
|
||||
free(direntries);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrintFile(int fd, initrd_superblock_t* sb, initrd_inode_t* inode)
|
||||
{
|
||||
if ( INITRD_S_ISDIR(inode->mode ) ) { errno = EISDIR; return false; }
|
||||
uint32_t sofar = 0;
|
||||
while ( sofar < inode->size )
|
||||
{
|
||||
const size_t BUFFER_SIZE = 16UL * 1024UL;
|
||||
uint8_t buffer[BUFFER_SIZE];
|
||||
uint32_t available = inode->size - sofar;
|
||||
uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE;
|
||||
if ( !ReadInodeData(fd, sb, inode, buffer, count, 0) ) { return false; }
|
||||
if ( writeall(1, buffer, count) != count ) { return false; }
|
||||
sofar += count;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Extract(int fd,
|
||||
const char* fd_path,
|
||||
initrd_superblock_t* sb,
|
||||
initrd_inode_t* inode,
|
||||
const char* out_path,
|
||||
bool verbose)
|
||||
{
|
||||
if ( verbose )
|
||||
printf("%s\n", out_path);
|
||||
if ( INITRD_S_ISLNK(inode->mode) )
|
||||
{
|
||||
char* buffer = (char*) malloc(inode->size + 1);
|
||||
if ( !buffer )
|
||||
err(1, "malloc");
|
||||
if ( !ReadInodeData(fd, sb, inode, (uint8_t*) buffer, inode->size, 0) )
|
||||
err(1, "%s", fd_path);
|
||||
buffer[inode->size] = '\0';
|
||||
// TODO: What if it already exists.
|
||||
if ( symlink(buffer, out_path) < 0 )
|
||||
err(1, "%s", out_path);
|
||||
return;
|
||||
}
|
||||
else if ( INITRD_S_ISREG(inode->mode) )
|
||||
{
|
||||
int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0200);
|
||||
if ( out_fd < 0 )
|
||||
err(1, "%s", out_path);
|
||||
uint32_t sofar = 0;
|
||||
while ( sofar < inode->size )
|
||||
{
|
||||
const size_t BUFFER_SIZE = 16UL * 1024UL;
|
||||
uint8_t buffer[BUFFER_SIZE];
|
||||
uint32_t available = inode->size - sofar;
|
||||
uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE;
|
||||
if ( !ReadInodeData(fd, sb, inode, buffer, count, sofar) )
|
||||
err(1, "%s", fd_path);
|
||||
if ( writeall(out_fd, buffer, count) != count )
|
||||
err(1, "%s", out_path);
|
||||
sofar += count;
|
||||
}
|
||||
if ( fchmod(out_fd, inode->mode & 07777) < 0 )
|
||||
err(1, "%s", out_path);
|
||||
close(out_fd);
|
||||
return;
|
||||
}
|
||||
else if ( !INITRD_S_ISDIR(inode->mode) )
|
||||
errx(1, "%s: Unsupported kind of file", out_path);
|
||||
bool made = true;
|
||||
if ( mkdir(out_path, 0700) < 0 )
|
||||
{
|
||||
if ( errno != EEXIST )
|
||||
err(1, "%s", out_path);
|
||||
made = false;
|
||||
}
|
||||
uint8_t* direntries = GetInodeData(fd, sb, inode);
|
||||
if ( !direntries )
|
||||
err(1, "%s", out_path);
|
||||
uint32_t offset = 0;
|
||||
while ( offset < inode->size )
|
||||
{
|
||||
initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset);
|
||||
offset += dirent->reclen;
|
||||
if ( !dirent->namelen ) // TODO: Possible?
|
||||
continue;
|
||||
if ( !strcmp(dirent->name, ".") || !strcmp(dirent->name, "..") )
|
||||
continue;
|
||||
char* child_path;
|
||||
if ( asprintf(&child_path, "%s/%s", out_path, dirent->name) < 0 )
|
||||
err(1, "asprintf");
|
||||
initrd_inode_t* child_inode = ResolvePath(fd, sb, inode, dirent->name);
|
||||
if ( !child_inode )
|
||||
err(1, "%s: %s", fd_path, out_path);
|
||||
Extract(fd, fd_path, sb, child_inode, child_path, verbose);
|
||||
free(child_path);
|
||||
}
|
||||
free(direntries);
|
||||
// TODO: Time of check to time of use race condition, a concurrent rename
|
||||
// and we may assign the permissions to the wrong file, potentially
|
||||
// exploitable.
|
||||
if ( made && chmod(out_path, inode->mode & 07777) < 0 )
|
||||
err(1, " %s", out_path);
|
||||
}
|
||||
|
||||
static void compact_arguments(int* argc, char*** argv)
|
||||
{
|
||||
for ( int i = 0; i < *argc; i++ )
|
||||
{
|
||||
while ( i < *argc && !(*argv)[i] )
|
||||
{
|
||||
for ( int n = i; n < *argc; n++ )
|
||||
(*argv)[n] = (*argv)[n+1];
|
||||
(*argc)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
bool all = false;
|
||||
const char* destination = ".";
|
||||
bool verbose = false;
|
||||
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' || !arg[1] )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
char c;
|
||||
while ( (c = *++arg) ) switch ( c )
|
||||
{
|
||||
case 'a': all = true; break;
|
||||
case 'C':
|
||||
if ( !*(destination = arg + 1) )
|
||||
{
|
||||
if ( i + 1 == argc )
|
||||
errx(1, "option requires an argument -- 'C'");
|
||||
destination = argv[i+1];
|
||||
argv[++i] = NULL;
|
||||
}
|
||||
arg = "C";
|
||||
break;
|
||||
case 'v': verbose = true; break;
|
||||
default:
|
||||
errx(1, "unknown option -- '%c'", c);
|
||||
}
|
||||
}
|
||||
else
|
||||
errx(1, "unknown option: %s", arg);
|
||||
}
|
||||
|
||||
compact_arguments(&argc, &argv);
|
||||
|
||||
if ( argc == 1 )
|
||||
errx(1, "No initrd specified");
|
||||
const char* initrd = argv[1];
|
||||
if ( argc == 2 )
|
||||
errx(1, "No command specified");
|
||||
const char* cmd = argv[2];
|
||||
|
||||
int fd = open(initrd, O_RDONLY);
|
||||
if ( fd < 0 )
|
||||
err(1, "open: %s", initrd);
|
||||
|
||||
initrd_superblock_t* sb = GetSuperBlock(fd);
|
||||
if ( !sb )
|
||||
err(1, "read: %s", initrd);
|
||||
|
||||
initrd_inode_t* root = GetInode(fd, sb, sb->root);
|
||||
if ( !root )
|
||||
err(1, "read: %s", initrd);
|
||||
|
||||
for ( int i = 3; i < argc; i++ )
|
||||
{
|
||||
const char* path = argv[i];
|
||||
if ( path[0] != '/' )
|
||||
{
|
||||
errno = ENOENT;
|
||||
errx(1, "%s", path);
|
||||
}
|
||||
|
||||
initrd_inode_t* inode = ResolvePath(fd, sb, root, path+1);
|
||||
if ( !inode )
|
||||
err(1, "%s", path);
|
||||
|
||||
if ( !strcmp(cmd, "cat") )
|
||||
{
|
||||
if ( !PrintFile(fd, sb, inode) )
|
||||
err(1, "%s", path);
|
||||
}
|
||||
else if ( !strcmp(cmd, "ls") )
|
||||
{
|
||||
if ( !ListDirectory(fd, sb, inode, all) )
|
||||
err(1, "%s", path);
|
||||
}
|
||||
else if ( !strcmp(cmd, "extract") )
|
||||
Extract(fd, initrd, sb, inode, destination, verbose);
|
||||
else
|
||||
errx(1, "unrecognized command: %s", cmd);
|
||||
|
||||
free(inode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,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
|
|
@ -1,126 +0,0 @@
|
|||
.Dd October 7, 2015
|
||||
.Dt MKINITRD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mkinitrd
|
||||
.Nd make initialization ramdisk
|
||||
.Sh SYNOPSIS
|
||||
.Nm mkinitrd
|
||||
.Op Fl \-filter Ns "=" Ns Ar rules-file
|
||||
.Op Fl \-format Ns "=" Ns Ar format
|
||||
.Op Fl \-manifest Ns "=" Ns Ar manifest-file
|
||||
.Fl o Ar destination
|
||||
.Ar directory ...
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
produces a
|
||||
.Xr initrd 7
|
||||
for the Sortix
|
||||
.Xr kernel 7
|
||||
at the
|
||||
.Ar destination .
|
||||
It is an archive in the
|
||||
.In sortix/initrd.h
|
||||
format of files and directories.
|
||||
.Pp
|
||||
Every specified
|
||||
.Ar directory
|
||||
is used as a root directory and is recursively searched for files and
|
||||
directories matching the filter.
|
||||
If multiple directories are specified, the directories are merged together.
|
||||
In case two files with the same path conflict, precedence is given to the file
|
||||
in the root directory specified first.
|
||||
.Pp
|
||||
Hardlinks are detected and preserved to avoid data duplication.
|
||||
Inode times are truncated to second precision due to format limitations.
|
||||
Inodes are stored with uid 0 and gid 0 of the root user.
|
||||
The format is not compressed but can be compressed externally if it is
|
||||
decompressed during bootloading.
|
||||
.Pp
|
||||
.Xr initrdfs 8
|
||||
can be used to view the files produced by
|
||||
.Nm .
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "12345678"
|
||||
.It Fl \-filter Ns "=" Ns Ar rule-file
|
||||
Include only files and directories during the recursive search that matches
|
||||
rules in the
|
||||
.Ar rule-file
|
||||
in the format specified under
|
||||
.Sx FILTER RULES .
|
||||
.It Fl \-format Ns "=" Ns Ar format
|
||||
Produce the archive in the specified format.
|
||||
This is for forward compatibility and only
|
||||
.Sy sortix-initrd-2
|
||||
is supported.
|
||||
.Sy default
|
||||
is an alias for the newest format
|
||||
.Sy sortix-initrd-2 .
|
||||
.Nm
|
||||
will default to a newer format when one is introduced and this allows
|
||||
.Nm
|
||||
to support old callers during the transitional period.
|
||||
.It Fl \-manifest Ns "=" Ns Ar manifest-file
|
||||
Include only files and directories during the recursive search whose path
|
||||
exactly matches a line in the
|
||||
.Ar manifest-file .
|
||||
.It Fl o , Fl \-output Ns "=" Ns Ar destination
|
||||
Store the produced
|
||||
.Xr initrd 7
|
||||
at the specified
|
||||
.Ar destination .
|
||||
.El
|
||||
.Sh FILTER RULES
|
||||
The rule format is line based and leading whitespace is skipped.
|
||||
Lines starting with a
|
||||
.Li #
|
||||
character are ignored as comments.
|
||||
The first word on a line must be one of the following commands and the rest of
|
||||
the line is its parameter.
|
||||
Trailing whitespace is not ignored.
|
||||
.Bl -tag -width "12345678"
|
||||
.It Sy default Ar boolean
|
||||
The
|
||||
.Ar boolean
|
||||
parameter is either
|
||||
.Sy true
|
||||
or
|
||||
.Sy false
|
||||
and determines whether a file or directory is included if no other rules match
|
||||
it.
|
||||
This defaults to
|
||||
.Sy true .
|
||||
.It Sy include Ar path
|
||||
Include the file or directory if it matches
|
||||
.Ar path .
|
||||
.It Sy exclude Ar path
|
||||
Exclude the file or directory if it matches
|
||||
.Ar path .
|
||||
.El
|
||||
.Pp
|
||||
The rules are checked on the paths relative to the root directories during the
|
||||
recursive descent.
|
||||
The last rule to match a path decides whether it is included or not.
|
||||
Directory are not descended into if they are excluded.
|
||||
The pattern patch is simple and matches paths exactly.
|
||||
.Sh EXIT STATUS
|
||||
.Nm
|
||||
will exit 0 on success and non-zero otherwise.
|
||||
.Sh EXAMPLES
|
||||
.Bd -literal
|
||||
# By default include everything except these directories:
|
||||
exclude /dev
|
||||
exclude /src/sysroot
|
||||
exclude /tmp
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr initrd 7 ,
|
||||
.Xr kernel 7 ,
|
||||
.Xr initrdfs 8 ,
|
||||
.Xr update-initrd 8
|
||||
.Sh BUGS
|
||||
The path pattern matching should be upgraded to use
|
||||
.Xr fnmatch 3 .
|
||||
The initrd format does not losslessly represent the Sortix
|
||||
.Li struct stat .
|
|
@ -1,776 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* mkinitrd.c
|
||||
* Produces a simple ramdisk filesystem readable by the Sortix kernel.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <endian.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <ioleast.h>
|
||||
#include <stdalign.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sortix/initrd.h>
|
||||
|
||||
#define DEFAULT_FORMAT "sortix-initrd-2"
|
||||
|
||||
#include "rules.h"
|
||||
#include "serialize.h"
|
||||
|
||||
uint32_t HostModeToInitRD(mode_t mode)
|
||||
{
|
||||
uint32_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
|
||||
if ( S_ISVTX & mode ) { result |= INITRD_S_ISVTX; }
|
||||
if ( S_ISSOCK(mode) ) { result |= INITRD_S_IFSOCK; }
|
||||
if ( S_ISLNK(mode) ) { result |= INITRD_S_IFLNK; }
|
||||
if ( S_ISREG(mode) ) { result |= INITRD_S_IFREG; }
|
||||
if ( S_ISBLK(mode) ) { result |= INITRD_S_IFBLK; }
|
||||
if ( S_ISDIR(mode) ) { result |= INITRD_S_IFDIR; }
|
||||
if ( S_ISCHR(mode) ) { result |= INITRD_S_IFCHR; }
|
||||
if ( S_ISFIFO(mode) ) { result |= INITRD_S_IFIFO; }
|
||||
return result;
|
||||
}
|
||||
|
||||
mode_t InitRDModeToHost(uint32_t mode)
|
||||
{
|
||||
mode_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
|
||||
if ( INITRD_S_ISVTX & mode ) { result |= S_ISVTX; }
|
||||
if ( INITRD_S_ISSOCK(mode) ) { result |= S_IFSOCK; }
|
||||
if ( INITRD_S_ISLNK(mode) ) { result |= S_IFLNK; }
|
||||
if ( INITRD_S_ISREG(mode) ) { result |= S_IFREG; }
|
||||
if ( INITRD_S_ISBLK(mode) ) { result |= S_IFBLK; }
|
||||
if ( INITRD_S_ISDIR(mode) ) { result |= S_IFDIR; }
|
||||
if ( INITRD_S_ISCHR(mode) ) { result |= S_IFCHR; }
|
||||
if ( INITRD_S_ISFIFO(mode) ) { result |= S_IFIFO; }
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Node;
|
||||
struct DirEntry;
|
||||
|
||||
struct DirEntry
|
||||
{
|
||||
char* name;
|
||||
struct Node* node;
|
||||
};
|
||||
|
||||
int DirEntryCompare(const struct DirEntry* a, const struct DirEntry* b)
|
||||
{
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
int DirEntryCompareIndirect(const void* a_ptr, const void* b_ptr)
|
||||
{
|
||||
const struct DirEntry* a = (const struct DirEntry*) a_ptr;
|
||||
const struct DirEntry* b = (const struct DirEntry*) b_ptr;
|
||||
return DirEntryCompare(a, b);
|
||||
}
|
||||
|
||||
struct Node
|
||||
{
|
||||
char* path;
|
||||
uint32_t ino;
|
||||
uint32_t nlink;
|
||||
size_t direntsused;
|
||||
size_t direntslength;
|
||||
struct DirEntry* dirents;
|
||||
mode_t mode;
|
||||
time_t ctime;
|
||||
time_t mtime;
|
||||
bool written;
|
||||
size_t refcount;
|
||||
};
|
||||
|
||||
void FreeNode(struct Node* node)
|
||||
{
|
||||
if ( !node )
|
||||
return;
|
||||
if ( 1 < node->nlink ) { node->nlink--; return; }
|
||||
for ( size_t i = 0; i < node->direntsused; i++ )
|
||||
{
|
||||
struct DirEntry* entry = node->dirents + i;
|
||||
if ( !entry->name )
|
||||
continue;
|
||||
if ( strcmp(entry->name, ".") != 0 && strcmp(entry->name, "..") != 0 )
|
||||
{
|
||||
if ( --entry->node->refcount == 0 )
|
||||
FreeNode(entry->node);
|
||||
}
|
||||
free(entry->name);
|
||||
}
|
||||
free(node->dirents);
|
||||
free(node->path);
|
||||
free(node);
|
||||
}
|
||||
|
||||
struct CacheEntry
|
||||
{
|
||||
ino_t ino;
|
||||
dev_t dev;
|
||||
struct Node* node;
|
||||
};
|
||||
|
||||
static size_t cacheused = 0;
|
||||
static size_t cachelen = 0;
|
||||
static struct CacheEntry* cache = NULL;
|
||||
|
||||
struct Node* LookupCache(dev_t dev, ino_t ino)
|
||||
{
|
||||
for ( size_t i = 0; i < cacheused; i++ )
|
||||
if ( cache[i].dev == dev && cache[i].ino == ino )
|
||||
return cache[i].node;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AddToCache(struct Node* node, dev_t dev, ino_t ino)
|
||||
{
|
||||
if ( cacheused == cachelen )
|
||||
{
|
||||
size_t newcachelen = cachelen ? 2 * cachelen : 256;
|
||||
size_t newcachesize = newcachelen * sizeof(struct CacheEntry);
|
||||
struct CacheEntry* newcache =
|
||||
(struct CacheEntry*) realloc(cache, newcachesize);
|
||||
if ( !newcache )
|
||||
return false;
|
||||
cache = newcache;
|
||||
cachelen = newcachelen;
|
||||
}
|
||||
size_t index = cacheused++;
|
||||
cache[index].ino = ino;
|
||||
cache[index].dev = dev;
|
||||
cache[index].node = node;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Node* RecursiveSearch(const char* real_path, const char* virt_path,
|
||||
uint32_t* ino, struct Node* parent)
|
||||
{
|
||||
printf("%s\n", virt_path);
|
||||
fflush(stdout);
|
||||
|
||||
if ( virt_path[0] == '/' && !virt_path[1] )
|
||||
virt_path = "";
|
||||
|
||||
struct stat st;
|
||||
if ( lstat(real_path, &st) != 0 )
|
||||
{
|
||||
warn("stat: %s", real_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !S_ISDIR(st.st_mode) && 2 <= st.st_nlink )
|
||||
{
|
||||
struct Node* cached = LookupCache(st.st_dev, st.st_ino);
|
||||
if ( cached )
|
||||
return cached->nlink++, cached->refcount++, cached;
|
||||
}
|
||||
|
||||
struct Node* node = (struct Node*) calloc(1, sizeof(struct Node));
|
||||
if ( !node )
|
||||
return NULL;
|
||||
|
||||
node->nlink = 1;
|
||||
node->refcount = 1;
|
||||
node->mode = st.st_mode;
|
||||
node->ino = (*ino)++;
|
||||
node->ctime = st.st_ctim.tv_sec;
|
||||
node->mtime = st.st_mtim.tv_sec;
|
||||
|
||||
char* real_path_clone = strdup(real_path);
|
||||
if ( !real_path_clone )
|
||||
{
|
||||
warn("strdup");
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->path = real_path_clone;
|
||||
|
||||
if ( !S_ISDIR(st.st_mode))
|
||||
{
|
||||
if ( 2 <= st.st_nlink && !AddToCache(node, st.st_dev, st.st_ino) )
|
||||
{
|
||||
free(real_path_clone);
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
DIR* dir = opendir(real_path);
|
||||
if ( !dir )
|
||||
{
|
||||
warn("opendir: %s", real_path);
|
||||
FreeNode(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t real_path_len = strlen(real_path);
|
||||
size_t virt_path_len = strlen(virt_path);
|
||||
|
||||
bool successful = true;
|
||||
struct dirent* entry;
|
||||
while ( (entry = readdir(dir)) )
|
||||
{
|
||||
size_t namelen = strlen(entry->d_name);
|
||||
|
||||
size_t virt_subpath_len = virt_path_len + 1 + namelen;
|
||||
char* virt_subpath = (char*) malloc(virt_subpath_len+1);
|
||||
if ( !virt_subpath )
|
||||
{
|
||||
warn("malloc");
|
||||
successful = false;
|
||||
break;
|
||||
}
|
||||
stpcpy(stpcpy(stpcpy(virt_subpath, virt_path), "/"), entry->d_name);
|
||||
|
||||
if ( strcmp(entry->d_name, ".") != 0 &&
|
||||
strcmp(entry->d_name, "..") != 0 &&
|
||||
!IncludesPath(virt_subpath) )
|
||||
{
|
||||
free(virt_subpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t real_subpath_len = real_path_len + 1 + namelen;
|
||||
char* real_subpath = (char*) malloc(real_subpath_len+1);
|
||||
if ( !real_subpath )
|
||||
{
|
||||
free(virt_subpath);
|
||||
warn("malloc");
|
||||
successful = false;
|
||||
break;
|
||||
}
|
||||
stpcpy(stpcpy(stpcpy(real_subpath, real_path), "/"), entry->d_name);
|
||||
|
||||
struct Node* child = NULL;
|
||||
if ( !strcmp(entry->d_name, ".") )
|
||||
child = node;
|
||||
if ( !strcmp(entry->d_name, "..") )
|
||||
child = parent ? parent : node;
|
||||
if ( !child )
|
||||
child = RecursiveSearch(real_subpath, virt_subpath, ino, node);
|
||||
free(real_subpath);
|
||||
free(virt_subpath);
|
||||
if ( !child )
|
||||
{
|
||||
successful = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( node->direntsused == node->direntslength )
|
||||
{
|
||||
size_t oldlength = node->direntslength;
|
||||
size_t newlength = oldlength ? 2 * oldlength : 8;
|
||||
size_t newsize = sizeof(struct DirEntry) * newlength;
|
||||
struct DirEntry* newdirents = (struct DirEntry*) realloc(node->dirents, newsize);
|
||||
if ( !newdirents )
|
||||
{
|
||||
warn("realloc");
|
||||
successful = false;
|
||||
break;
|
||||
}
|
||||
node->dirents = newdirents;
|
||||
node->direntslength = newlength;
|
||||
}
|
||||
|
||||
char* nameclone = strdup(entry->d_name);
|
||||
if ( !nameclone )
|
||||
{
|
||||
warn("strdup");
|
||||
successful = false;
|
||||
break;
|
||||
}
|
||||
|
||||
struct DirEntry* entry = node->dirents + node->direntsused++;
|
||||
|
||||
entry->name = nameclone;
|
||||
entry->node = child;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
if ( !successful )
|
||||
{
|
||||
FreeNode(node);
|
||||
return NULL;
|
||||
}
|
||||
qsort(node->dirents, node->direntsused, sizeof(struct DirEntry),
|
||||
DirEntryCompareIndirect);
|
||||
return node;
|
||||
}
|
||||
|
||||
struct Node* MergeNodes(struct Node* a, struct Node* b)
|
||||
{
|
||||
if ( !S_ISDIR(a->mode) || !S_ISDIR(b->mode) )
|
||||
{
|
||||
FreeNode(b);
|
||||
return a;
|
||||
}
|
||||
size_t dirents_used = 0;
|
||||
size_t dirents_length = a->direntsused + b->direntsused;
|
||||
struct DirEntry* dirents = (struct DirEntry*)
|
||||
malloc(sizeof(struct DirEntry) * dirents_length);
|
||||
if ( !dirents )
|
||||
{
|
||||
warn("malloc");
|
||||
FreeNode(a);
|
||||
FreeNode(b);
|
||||
return NULL;
|
||||
}
|
||||
bool failure = false;
|
||||
size_t ai = 0;
|
||||
size_t bi = 0;
|
||||
while ( ai != a->direntsused || bi != b->direntsused )
|
||||
{
|
||||
if ( bi == b->direntsused ||
|
||||
(ai != a->direntsused &&
|
||||
bi != b->direntsused &&
|
||||
DirEntryCompare(&a->dirents[ai], &b->dirents[bi]) < 0) )
|
||||
{
|
||||
dirents[dirents_used++] = a->dirents[ai];
|
||||
a->dirents[ai].name = NULL;
|
||||
a->dirents[ai].node = NULL;
|
||||
ai++;
|
||||
continue;
|
||||
}
|
||||
if ( ai == a->direntsused ||
|
||||
(ai != a->direntsused &&
|
||||
bi != b->direntsused &&
|
||||
DirEntryCompare(&a->dirents[ai], &b->dirents[bi]) > 0) )
|
||||
{
|
||||
dirents[dirents_used++] = b->dirents[bi];
|
||||
for ( size_t i = 0; i < b->dirents[bi].node->direntsused; i++ )
|
||||
{
|
||||
if ( strcmp(b->dirents[bi].node->dirents[i].name, "..") != 0 )
|
||||
continue;
|
||||
b->dirents[bi].node->dirents[i].node = a;
|
||||
}
|
||||
b->dirents[bi].name = NULL;
|
||||
b->dirents[bi].node = NULL;
|
||||
bi++;
|
||||
continue;
|
||||
}
|
||||
const char* name = a->dirents[ai].name;
|
||||
dirents[dirents_used].name = a->dirents[ai].name;
|
||||
if ( !strcmp(name, ".") || !strcmp(name, "..") )
|
||||
dirents[dirents_used].node = a->dirents[ai].node;
|
||||
else
|
||||
{
|
||||
dirents[dirents_used].node =
|
||||
MergeNodes(a->dirents[ai].node, b->dirents[bi].node);
|
||||
if ( !dirents[dirents_used].node )
|
||||
failure = true;
|
||||
}
|
||||
dirents_used++;
|
||||
a->dirents[ai].name = NULL;
|
||||
a->dirents[ai].node = NULL;
|
||||
ai++;
|
||||
free(b->dirents[bi].name);
|
||||
b->dirents[bi].name = NULL;
|
||||
b->dirents[bi].node = NULL;
|
||||
bi++;
|
||||
}
|
||||
free(a->dirents);
|
||||
a->dirents = dirents;
|
||||
a->direntsused = dirents_used;
|
||||
a->direntslength = dirents_length;
|
||||
b->direntsused = 0;
|
||||
FreeNode(b);
|
||||
if ( failure )
|
||||
return FreeNode(b), (struct Node*) NULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool WriteNode(struct initrd_superblock* sb, int fd, const char* outputname,
|
||||
struct Node* node)
|
||||
{
|
||||
if ( node->written )
|
||||
return true;
|
||||
|
||||
uint32_t filesize = 0;
|
||||
uint32_t origfssize = sb->fssize;
|
||||
uint32_t dataoff = origfssize;
|
||||
uint32_t filestart = dataoff;
|
||||
|
||||
if ( S_ISLNK(node->mode) ) // Symbolic link
|
||||
{
|
||||
const size_t NAME_SIZE = 1024UL;
|
||||
char name[NAME_SIZE];
|
||||
ssize_t namelen = readlink(node->path, name, NAME_SIZE);
|
||||
if ( namelen < 0 )
|
||||
return warn("readlink: %s", node->path), false;
|
||||
filesize = (uint32_t) namelen;
|
||||
if ( pwriteall(fd, name, filesize, dataoff) < filesize )
|
||||
return warn("read: %s", node->path), false;
|
||||
dataoff += filesize;
|
||||
}
|
||||
else if ( S_ISREG(node->mode) ) // Regular file
|
||||
{
|
||||
int nodefd = open(node->path, O_RDONLY);
|
||||
if ( nodefd < 0 )
|
||||
return warn("open: %s", node->path), false;
|
||||
const size_t BUFFER_SIZE = 16UL * 1024UL;
|
||||
uint8_t buffer[BUFFER_SIZE];
|
||||
ssize_t amount;
|
||||
while ( 0 < (amount = read(nodefd, buffer, BUFFER_SIZE)) )
|
||||
{
|
||||
if ( pwriteall(fd, buffer, amount, dataoff) < (size_t) amount )
|
||||
{
|
||||
close(nodefd);
|
||||
return warn("write: %s", outputname), false;
|
||||
}
|
||||
dataoff += amount;
|
||||
filesize += amount;
|
||||
}
|
||||
close(nodefd);
|
||||
if ( amount < 0 )
|
||||
return warn("read: %s", node->path), false;
|
||||
}
|
||||
else if ( S_ISDIR(node->mode) ) // Directory
|
||||
{
|
||||
for ( size_t i = 0; i < node->direntsused; i++ )
|
||||
{
|
||||
struct DirEntry* entry = node->dirents + i;
|
||||
const char* name = entry->name;
|
||||
size_t namelen = strlen(entry->name);
|
||||
struct initrd_dirent dirent;
|
||||
dirent.inode = entry->node->ino;
|
||||
dirent.namelen = (uint16_t) namelen;
|
||||
dirent.reclen = sizeof(dirent) + dirent.namelen + 1;
|
||||
dirent.reclen = (dirent.reclen+3)/4*4; // Align entries.
|
||||
size_t entsize = sizeof(dirent);
|
||||
export_initrd_dirent(&dirent);
|
||||
assert((dataoff & (alignof(dirent)-1)) == 0 );
|
||||
ssize_t hdramt = pwriteall(fd, &dirent, entsize, dataoff);
|
||||
import_initrd_dirent(&dirent);
|
||||
ssize_t nameamt = pwriteall(fd, name, namelen+1, dataoff + entsize);
|
||||
if ( hdramt < (ssize_t) entsize || nameamt < (ssize_t) (namelen+1) )
|
||||
return warn("write: %s", outputname), false;
|
||||
size_t padding = dirent.reclen - (entsize + (namelen+1));
|
||||
for ( size_t n = 0; n < padding; n++ )
|
||||
{
|
||||
uint8_t nul = 0;
|
||||
if ( pwrite(fd, &nul, 1, dataoff+entsize+namelen+1+n) != 1 )
|
||||
return warn("write: %s", outputname), false;
|
||||
}
|
||||
filesize += dirent.reclen;
|
||||
dataoff += dirent.reclen;
|
||||
}
|
||||
}
|
||||
|
||||
struct initrd_inode inode;
|
||||
inode.mode = HostModeToInitRD(node->mode);
|
||||
inode.uid = 0;
|
||||
inode.gid = 0;
|
||||
inode.nlink = node->nlink;
|
||||
inode.ctime = (uint64_t) node->ctime;
|
||||
inode.mtime = (uint64_t) node->mtime;
|
||||
inode.dataoffset = filestart;
|
||||
inode.size = filesize;
|
||||
|
||||
uint32_t inodepos = sb->inodeoffset + node->ino * sb->inodesize;
|
||||
uint32_t inodesize = sizeof(inode);
|
||||
export_initrd_inode(&inode);
|
||||
assert((inodepos & (alignof(inode)-1)) == 0 );
|
||||
if ( pwriteall(fd, &inode, inodesize, inodepos) < inodesize )
|
||||
return warn("write: %s", outputname), false;
|
||||
import_initrd_inode(&inode);
|
||||
|
||||
uint32_t increment = dataoff - origfssize;
|
||||
sb->fssize += increment;
|
||||
sb->fssize = (sb->fssize+7)/8*8; // Align upwards.
|
||||
|
||||
return node->written = true;
|
||||
}
|
||||
|
||||
bool WriteNodeRecursive(struct initrd_superblock* sb, int fd,
|
||||
const char* outputname, struct Node* node)
|
||||
{
|
||||
if ( !WriteNode(sb, fd, outputname, node) )
|
||||
return false;
|
||||
|
||||
if ( !S_ISDIR(node->mode) )
|
||||
return true;
|
||||
|
||||
for ( size_t i = 0; i < node->direntsused; i++ )
|
||||
{
|
||||
struct DirEntry* entry = node->dirents + i;
|
||||
const char* name = entry->name;
|
||||
struct Node* child = entry->node;
|
||||
if ( !strcmp(name, ".") || !strcmp(name, ".." ) )
|
||||
continue;
|
||||
if ( !WriteNodeRecursive(sb, fd, outputname, child) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FormatFD(const char* outputname, int fd, uint32_t inodecount,
|
||||
struct Node* root)
|
||||
{
|
||||
struct initrd_superblock sb;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
strncpy(sb.magic, "sortix-initrd-2", sizeof(sb.magic));
|
||||
sb.revision = 0;
|
||||
sb.fssize = sizeof(sb);
|
||||
sb.inodesize = sizeof(struct initrd_inode);
|
||||
sb.inodeoffset = sizeof(sb);
|
||||
sb.inodecount = inodecount;
|
||||
sb.root = root->ino;
|
||||
|
||||
uint32_t inodebytecount = sb.inodesize * sb.inodecount;
|
||||
sb.fssize += inodebytecount;
|
||||
sb.fssize = (sb.fssize+7)/8*8; // Align upwards.
|
||||
|
||||
if ( !WriteNodeRecursive(&sb, fd, outputname, root) )
|
||||
return false;
|
||||
|
||||
sb.sumalgorithm = INITRD_ALGO_NONE;
|
||||
sb.sumsize = 0;
|
||||
sb.fssize = (sb.fssize+3)/4*4; // Align upwards.
|
||||
sb.fssize += sb.sumsize;
|
||||
|
||||
export_initrd_superblock(&sb);
|
||||
if ( pwriteall(fd, &sb, sizeof(sb), 0) < sizeof(sb) )
|
||||
{
|
||||
warn("write: %s", outputname);
|
||||
return false;
|
||||
}
|
||||
import_initrd_superblock(&sb);
|
||||
|
||||
if ( ftruncate(fd, sb.fssize) < 0 )
|
||||
{
|
||||
warn("truncate: %s", outputname);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Format(const char* pathname, uint32_t inodecount, struct Node* root)
|
||||
{
|
||||
int fd = open(pathname, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
bool result = FormatFD(pathname, fd, inodecount, root);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void compact_arguments(int* argc, char*** argv)
|
||||
{
|
||||
for ( int i = 0; i < *argc; i++ )
|
||||
{
|
||||
while ( i < *argc && !(*argv)[i] )
|
||||
{
|
||||
for ( int n = i; n < *argc; n++ )
|
||||
(*argv)[n] = (*argv)[n+1];
|
||||
(*argc)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get_option_variable(const char* option, char** varptr,
|
||||
const char* arg, int argc, char** argv, int* ip,
|
||||
const char* argv0)
|
||||
{
|
||||
size_t option_len = strlen(option);
|
||||
if ( strncmp(option, arg, option_len) != 0 )
|
||||
return false;
|
||||
if ( arg[option_len] == '=' )
|
||||
{
|
||||
*varptr = strdup(arg + option_len + 1);
|
||||
return true;
|
||||
}
|
||||
if ( arg[option_len] != '\0' )
|
||||
return false;
|
||||
if ( *ip + 1 == argc )
|
||||
{
|
||||
fprintf(stderr, "%s: expected operand after `%s'\n", argv0, option);
|
||||
exit(1);
|
||||
}
|
||||
*varptr = strdup(argv[++*ip]), argv[*ip] = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GET_OPTION_VARIABLE(str, varptr) \
|
||||
get_option_variable(str, varptr, arg, argc, argv, &i, argv0)
|
||||
|
||||
static void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... ROOT... -o OUTPUT\n", argv0);
|
||||
fprintf(fp, "Creates a init ramdisk for the Sortix kernel.\n");
|
||||
fprintf(fp, "\n");
|
||||
fprintf(fp, "Mandatory arguments to long options are mandatory for short options too.\n");
|
||||
fprintf(fp, " --filter=FILE import filter rules from FILE\n");
|
||||
fprintf(fp, " --format=FORMAT format version [%s]\n", DEFAULT_FORMAT);
|
||||
fprintf(fp, " -o, --output=FILE write result to FILE\n");
|
||||
fprintf(fp, " --help display this help and exit\n");
|
||||
fprintf(fp, " --version output version information and exit\n");
|
||||
}
|
||||
|
||||
static void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char* arg_filter = NULL;
|
||||
char* arg_format = strdup(DEFAULT_FORMAT);
|
||||
char* arg_manifest = NULL;
|
||||
char* arg_output = NULL;
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' || !arg[1] )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
char c;
|
||||
while ( (c = *++arg) ) switch ( c )
|
||||
{
|
||||
case 'o':
|
||||
free(arg_output);
|
||||
if ( *(arg+1) )
|
||||
arg_output = strdup(arg + 1);
|
||||
else
|
||||
{
|
||||
if ( i + 1 == argc )
|
||||
{
|
||||
warnx("option requires an argument -- 'o'");
|
||||
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
||||
exit(125);
|
||||
}
|
||||
arg_output = strdup(argv[i+1]);
|
||||
argv[++i] = NULL;
|
||||
}
|
||||
arg = "o";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") )
|
||||
help(stdout, argv0), exit(0);
|
||||
else if ( !strcmp(arg, "--version") )
|
||||
version(stdout, argv0), exit(0);
|
||||
else if ( GET_OPTION_VARIABLE("--filter", &arg_filter) )
|
||||
{
|
||||
FILE* fp = fopen(arg_filter, "r");
|
||||
if ( !fp )
|
||||
err(1, "%s", arg_filter);
|
||||
if ( !AddRulesFromFile(fp, arg_filter) )
|
||||
exit(1);
|
||||
fclose(fp);
|
||||
free(arg_filter);
|
||||
arg_filter = NULL;
|
||||
}
|
||||
else if ( GET_OPTION_VARIABLE("--manifest", &arg_manifest) )
|
||||
{
|
||||
FILE* fp = fopen(arg_manifest, "r");
|
||||
if ( !fp )
|
||||
err(1, "%s", arg_manifest);
|
||||
if ( !AddManifestFromFile(fp, arg_manifest) )
|
||||
exit(1);
|
||||
fclose(fp);
|
||||
free(arg_manifest);
|
||||
arg_manifest = NULL;
|
||||
}
|
||||
else if ( GET_OPTION_VARIABLE("--format", &arg_format) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--output", &arg_output) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc < 2 )
|
||||
{
|
||||
help(stdout, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
compact_arguments(&argc, &argv);
|
||||
|
||||
if ( argc < 2 )
|
||||
{
|
||||
fprintf(stderr, "%s: No root specified\n", argv0),
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !arg_output )
|
||||
{
|
||||
fprintf(stderr, "%s: No output file specified\n", argv0),
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* format = arg_format;
|
||||
|
||||
if ( !strcmp(format, "default") )
|
||||
format = DEFAULT_FORMAT;
|
||||
|
||||
if ( strcmp(format, "sortix-initrd-2") != 0 )
|
||||
{
|
||||
fprintf(stderr, "%s: Unsupported format `%s'\n", argv0, format);
|
||||
fprintf(stderr, "Try `%s --help' for more information.\n", argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uint32_t inodecount = 1;
|
||||
struct Node* root = NULL;
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
struct Node* node = RecursiveSearch(argv[i], "/", &inodecount, NULL);
|
||||
if ( !node )
|
||||
exit(1);
|
||||
if ( root )
|
||||
root = MergeNodes(root, node);
|
||||
else
|
||||
root = node;
|
||||
if ( !root )
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !Format(arg_output, inodecount, root) )
|
||||
exit(1);
|
||||
|
||||
FreeNode(root);
|
||||
|
||||
return 0;
|
||||
}
|
317
mkinitrd/rules.c
317
mkinitrd/rules.c
|
@ -1,317 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* rules.c
|
||||
* Determines whether a given path is included in the filesystem.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rules.h"
|
||||
|
||||
static struct InclusionRule** rules;
|
||||
static size_t num_rules;
|
||||
static size_t num_rules_allocated;
|
||||
static bool default_inclusion = true;
|
||||
static bool default_inclusion_determined;
|
||||
static char** manifest;
|
||||
static size_t manifest_used;
|
||||
static size_t manifest_length;
|
||||
|
||||
static const char* SkipCharacters(const char* str, char c)
|
||||
{
|
||||
while ( *str == c)
|
||||
str++;
|
||||
return str;
|
||||
}
|
||||
|
||||
// /usr/bin/foobar match /usr = true
|
||||
// /usr/bin/foobar match usr = false
|
||||
// ///usr////bin//foobar match //usr// = true
|
||||
// ///usr////bin//foobar match //usr//./evince = false
|
||||
// TODO: Should this support . and .. too?
|
||||
static bool PathMatchesPattern(const char* path, const char* pattern)
|
||||
{
|
||||
bool last_was_slash = false;
|
||||
while ( true )
|
||||
{
|
||||
if ( !*pattern )
|
||||
return !*path || last_was_slash;
|
||||
if ( (last_was_slash = *pattern == '/') )
|
||||
{
|
||||
if ( *path == '/' )
|
||||
{
|
||||
path = SkipCharacters(path, '/');
|
||||
pattern = SkipCharacters(pattern, '/');
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ( *pattern++ != *path++ )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int search_path(const void* a_ptr, const void* b_ptr)
|
||||
{
|
||||
const char* key = (const char*) a_ptr;
|
||||
char* path = *(char**) b_ptr;
|
||||
return strcmp(key, path);
|
||||
}
|
||||
|
||||
bool IncludesPath(const char* path)
|
||||
{
|
||||
bool determined = false;
|
||||
bool included = false;
|
||||
for ( size_t i = 0; i < num_rules; i++ )
|
||||
{
|
||||
struct InclusionRule* rule = rules[i];
|
||||
if ( !PathMatchesPattern(path, rule->pattern) )
|
||||
continue;
|
||||
switch ( rules[i]->rule )
|
||||
{
|
||||
case RULE_INCLUDE:
|
||||
included = true;
|
||||
determined = true;
|
||||
break;
|
||||
case RULE_EXCLUDE:
|
||||
included = false;
|
||||
determined = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !determined )
|
||||
included = default_inclusion;
|
||||
if ( !included )
|
||||
return false;
|
||||
if ( manifest_used &&
|
||||
!bsearch(path, manifest, manifest_used, sizeof(char*), search_path) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChangeRulesAmount(size_t new_length)
|
||||
{
|
||||
size_t new_num_rules = new_length < num_rules ? new_length : num_rules;
|
||||
for ( size_t i = new_num_rules; i < num_rules; i++ )
|
||||
{
|
||||
free(rules[i]->pattern);
|
||||
free(rules[i]);
|
||||
}
|
||||
num_rules = new_num_rules;
|
||||
struct InclusionRule** new_rules = (struct InclusionRule**)
|
||||
malloc(sizeof(struct InclusionRule*) * new_length);
|
||||
for ( size_t i = 0; i < new_length && i < num_rules; i++ )
|
||||
new_rules[i] = rules[i];
|
||||
free(rules); rules = new_rules;
|
||||
num_rules_allocated = new_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddRule(struct InclusionRule* rule)
|
||||
{
|
||||
if ( num_rules == num_rules_allocated )
|
||||
{
|
||||
size_t new_length = num_rules_allocated ? 2 * num_rules_allocated : 32;
|
||||
if ( !ChangeRulesAmount(new_length) )
|
||||
return false;
|
||||
}
|
||||
rules[num_rules++] = rule;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* SkipWhitespace(const char* line)
|
||||
{
|
||||
while ( *line && isspace((unsigned char) *line) )
|
||||
line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
static bool IsLineComment(const char* line)
|
||||
{
|
||||
return !*line || *line == '#';
|
||||
}
|
||||
|
||||
static const char* IsLineCommand(const char* line, const char* command)
|
||||
{
|
||||
while ( *line && isspace((unsigned char) *line) )
|
||||
line++;
|
||||
size_t cmdlen = strlen(command);
|
||||
if ( strncmp(line, command, cmdlen) != 0 )
|
||||
return NULL;
|
||||
if ( line[cmdlen] && !isspace((unsigned char) line[cmdlen]) )
|
||||
return NULL;
|
||||
while ( line[cmdlen] && isspace((unsigned char) line[cmdlen]) )
|
||||
cmdlen++;
|
||||
return line + cmdlen;
|
||||
}
|
||||
|
||||
bool AddRulesFromFile(FILE* fp, const char* fpname)
|
||||
{
|
||||
size_t rules_at_start = num_rules;
|
||||
size_t line_size;
|
||||
size_t line_num = 0;
|
||||
char* mem = NULL;
|
||||
ssize_t line_len;
|
||||
while ( 0 < (line_len = getline(&mem, &line_size, fp)) )
|
||||
{
|
||||
char* line = mem;
|
||||
line_num++;
|
||||
if ( line[line_len-1] == '\n' )
|
||||
line[--line_len] = '\0';
|
||||
line = (char*) SkipWhitespace((char*) line);
|
||||
if ( IsLineComment(line) )
|
||||
continue;
|
||||
const char* parameter;
|
||||
if ( (parameter = IsLineCommand(line, "default")) )
|
||||
{
|
||||
bool value;
|
||||
if ( !strcmp(parameter, "true") )
|
||||
value = true;
|
||||
else if ( !strcmp(parameter, "false") )
|
||||
value = false;
|
||||
else
|
||||
{
|
||||
warnx("%s:%zu: not a boolean '%s'", fpname, line_num, parameter);
|
||||
goto error_out;
|
||||
}
|
||||
if ( !default_inclusion_determined )
|
||||
default_inclusion = value,
|
||||
default_inclusion_determined = true;
|
||||
else
|
||||
default_inclusion = default_inclusion || value;
|
||||
}
|
||||
else if ( (parameter = IsLineCommand(line, "exclude")) ||
|
||||
(parameter = IsLineCommand(line, "include")) )
|
||||
{
|
||||
if ( !*parameter )
|
||||
{
|
||||
warnx("%s:%zu: no parameter given", fpname, line_num);
|
||||
goto error_out;
|
||||
}
|
||||
const char* pattern = parameter;
|
||||
enum InclusionRuleType type = line[0] == 'e' ? RULE_EXCLUDE : RULE_INCLUDE;
|
||||
struct InclusionRule* rule =
|
||||
(struct InclusionRule*) malloc(sizeof(struct InclusionRule));
|
||||
rule->pattern = strdup(pattern);
|
||||
rule->rule = type;
|
||||
if ( !AddRule(rule) )
|
||||
goto error_out_errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
warnx("%s:%zu: line not understood: '%s'", fpname, line_num, line);
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
if ( ferror(fp) )
|
||||
{
|
||||
error_out_errno:
|
||||
warn("%s", fpname);
|
||||
error_out:
|
||||
free(mem);
|
||||
ChangeRulesAmount(rules_at_start);
|
||||
return false;
|
||||
}
|
||||
free(mem);
|
||||
return true;
|
||||
}
|
||||
|
||||
int compare_path(const void* a_ptr, const void* b_ptr)
|
||||
{
|
||||
const char* a = *(const char* const*) a_ptr;
|
||||
const char* b = *(const char* const*) b_ptr;
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
bool AddManifestPath(const char* path)
|
||||
{
|
||||
if ( manifest_used == manifest_length )
|
||||
{
|
||||
size_t new_length = 2 * manifest_length;
|
||||
if ( new_length == 0 )
|
||||
new_length = 64;
|
||||
size_t new_size = new_length * sizeof(char*);
|
||||
char** new_manifest = (char**) realloc(manifest, new_size);
|
||||
if ( !new_manifest )
|
||||
{
|
||||
warn("malloc");
|
||||
return false;
|
||||
}
|
||||
manifest = new_manifest;
|
||||
manifest_length = new_length;
|
||||
}
|
||||
char* copy = strdup(path);
|
||||
if ( !copy )
|
||||
{
|
||||
warn("malloc");
|
||||
return false;
|
||||
}
|
||||
manifest[manifest_used++] = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddManifestFromFile(FILE* fp, const char* fpname)
|
||||
{
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_len;
|
||||
while ( 0 < (line_len = getline(&line, &line_size, fp)) )
|
||||
{
|
||||
if ( line[line_len-1] == '\n' )
|
||||
line[--line_len] = '\0';
|
||||
if ( !AddManifestPath(line) )
|
||||
return false;
|
||||
}
|
||||
free(line);
|
||||
if ( ferror(fp) )
|
||||
{
|
||||
warn("%s", fpname);
|
||||
return false;
|
||||
}
|
||||
if ( !AddManifestPath("/") ||
|
||||
!AddManifestPath("/tix") ||
|
||||
!AddManifestPath("/tix/manifest") )
|
||||
return false;
|
||||
char* fpname_copy = strdup(fpname);
|
||||
if ( !fpname_copy )
|
||||
{
|
||||
warn("malloc");
|
||||
return false;
|
||||
}
|
||||
const char* fpname_basename = basename(fpname_copy);
|
||||
char* manifest_path;
|
||||
if ( asprintf(&manifest_path, "/tix/manifest/%s", fpname_basename) < 0 )
|
||||
{
|
||||
free(fpname_copy);
|
||||
warn("malloc");
|
||||
return false;
|
||||
}
|
||||
free(fpname_copy);
|
||||
if ( !AddManifestPath(manifest_path) )
|
||||
return free(manifest_path), false;
|
||||
free(manifest_path);
|
||||
qsort(manifest, manifest_used, sizeof(char*), compare_path);
|
||||
return true;
|
||||
}
|
|
@ -1,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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -344,8 +344,6 @@ carray
|
|||
.It
|
||||
kblayout-compiler
|
||||
.It
|
||||
mkinitrd
|
||||
.It
|
||||
sf
|
||||
.It
|
||||
tix
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
201
tix/tix-build.c
201
tix/tix-build.c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
26
tix/tix-port
26
tix/tix-port
|
@ -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"
|
||||
)}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
56
tix/tix.c
56
tix/tix.c
|
@ -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(¶ms.coll_conf, coll_conf_path) )
|
||||
err(1, "`%s'", coll_conf_path);
|
||||
switch ( variables_append_file_path(¶ms.coll_conf, coll_conf_path) )
|
||||
{
|
||||
case -1: err(1, "%s", coll_conf_path);
|
||||
case -2: errx(2, "%s: Syntax error", coll_conf_path);
|
||||
}
|
||||
VerifyTixCollectionConfiguration(¶ms.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(¶ms.repo_list, repo_list_path) )
|
||||
if ( !string_array_append_file_path(¶ms.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(¶ms.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(¶ms.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(¶ms, &work, pkg_name);
|
||||
}
|
||||
free(tixinfo_dir);
|
||||
|
||||
for ( size_t i = 0; i < work.length; i++ )
|
||||
InstallPackageOfName(¶ms, work.strings[i]);
|
||||
|
|
134
tix/util.h
134
tix/util.h
|
@ -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);
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue