mirror of
https://github.com/fosslinux/live-bootstrap.git
synced 2026-03-23 11:36:32 +01:00
fix(kernel-bootstrap): restore kexec-fiwix baseline and move post-fiwix distfiles into raw payload import path
The instability was not caused by kexec-fiwix logic itself but by oversized early-stage payload pressure in kernel-bootstrap mode. When too many distfiles are injected before the Fiwix handoff, the early memory/file-table assumptions become fragile and KVM can fail during transition. This change restores kexec-fiwix.c to the known baseline (matching commit 984b8322...) and fixes the actual failure mode by moving non-early distfiles out of the initial image. What changed: - Keep only bootstrap-required distfiles in early init image. - Generate a separate raw payload image (LBPAYLD1 format) for the remaining distfiles. - Attach payload image as an extra disk in QEMU/bare-metal kernel-bootstrap flow. - Add a dedicated C89/tcc-compatible importer (payload-import) that scans payload disks and copies files into /external/distfiles after jump: fiwix. - Insert improve: import_payload immediately after jump: fiwix so the full distfile set is restored before heavy build steps continue. - Add PAYLOAD_REQUIRED config gating so this behavior is active only in kernel-bootstrap paths that need it. Why this design: - Preserves minimal early environment assumptions (no dependency on full shell utilities for the copy operation itself). - Avoids adding filesystem-construction toolchain dependencies for the payload container by using a simple length-prefixed raw format. - Keeps bare-metal and QEMU behavior aligned: both can carry full build artifacts without overloading the early handoff stage. - Leaves kexec-fiwix behavior deterministic and auditable by reverting to a known-good baseline implementation.
This commit is contained in:
parent
11c4dd8c01
commit
f30c20b7be
8 changed files with 441 additions and 52 deletions
|
|
@ -12,6 +12,7 @@ import hashlib
|
|||
import os
|
||||
import random
|
||||
import shutil
|
||||
import struct
|
||||
import tarfile
|
||||
import traceback
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ class Generator():
|
|||
|
||||
git_dir = os.path.join(os.path.dirname(os.path.join(__file__)), '..')
|
||||
distfiles_dir = os.path.join(git_dir, 'distfiles')
|
||||
payload_magic = b'LBPAYLD1'
|
||||
|
||||
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
def __init__(self, arch, external_sources, early_preseed, repo_path, mirrors,
|
||||
|
|
@ -39,6 +41,9 @@ class Generator():
|
|||
build_guix_also=self.build_guix_also)
|
||||
self.early_source_manifest = self.get_source_manifest(True,
|
||||
build_guix_also=self.build_guix_also)
|
||||
self.bootstrap_source_manifest = self.source_manifest
|
||||
self.payload_source_manifest = []
|
||||
self.payload_image = None
|
||||
self.target_dir = None
|
||||
self.external_dir = None
|
||||
|
||||
|
|
@ -50,6 +55,69 @@ class Generator():
|
|||
self.external_dir = os.path.join(self.target_dir, 'external')
|
||||
self.distfiles()
|
||||
|
||||
def _prepare_kernel_bootstrap_payload_manifests(self):
|
||||
"""
|
||||
Split early source payload from full offline payload.
|
||||
"""
|
||||
# Keep the early builder payload small enough to avoid overrunning
|
||||
# builder-hex0 memory file allocation before we can jump into Fiwix.
|
||||
self.bootstrap_source_manifest = self.get_source_manifest(True, build_guix_also=False)
|
||||
|
||||
full_manifest = self.get_source_manifest(False, build_guix_also=self.build_guix_also)
|
||||
bootstrap_set = set(self.bootstrap_source_manifest)
|
||||
self.payload_source_manifest = [entry for entry in full_manifest if entry not in bootstrap_set]
|
||||
|
||||
def _copy_manifest_distfiles(self, out_dir, manifest):
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
for entry in manifest:
|
||||
file_name = entry[3].strip()
|
||||
shutil.copy2(os.path.join(self.distfiles_dir, file_name),
|
||||
os.path.join(out_dir, file_name))
|
||||
|
||||
def _ensure_manifest_distfiles(self, manifest):
|
||||
for entry in manifest:
|
||||
checksum, directory, url, file_name = entry
|
||||
distfile_path = os.path.join(directory, file_name)
|
||||
if not os.path.isfile(distfile_path):
|
||||
self.download_file(url, directory, file_name)
|
||||
self.check_file(distfile_path, checksum)
|
||||
|
||||
def _create_raw_payload_image(self, target_path, manifest):
|
||||
if not manifest:
|
||||
return None
|
||||
|
||||
# Guarantee all payload distfiles exist and match checksums.
|
||||
self._ensure_manifest_distfiles(manifest)
|
||||
|
||||
files_by_name = {}
|
||||
for checksum, _, _, file_name in manifest:
|
||||
if file_name in files_by_name and files_by_name[file_name] != checksum:
|
||||
raise ValueError(f"Conflicting payload file with same name but different hash: {file_name}")
|
||||
files_by_name[file_name] = checksum
|
||||
|
||||
payload_path = os.path.join(target_path, "payload.img")
|
||||
ordered_names = sorted(files_by_name.keys())
|
||||
with open(payload_path, "wb") as payload:
|
||||
payload.write(self.payload_magic)
|
||||
payload.write(struct.pack("<I", len(ordered_names)))
|
||||
for file_name in ordered_names:
|
||||
file_name_bytes = file_name.encode("utf_8")
|
||||
if len(file_name_bytes) > 0xFFFFFFFF:
|
||||
raise ValueError(f"Payload file name too long: {file_name}")
|
||||
|
||||
src_path = os.path.join(self.distfiles_dir, file_name)
|
||||
file_size = os.path.getsize(src_path)
|
||||
if file_size > 0xFFFFFFFF:
|
||||
raise ValueError(f"Payload file too large for raw container format: {file_name}")
|
||||
|
||||
payload.write(struct.pack("<II", len(file_name_bytes), file_size))
|
||||
payload.write(file_name_bytes)
|
||||
|
||||
with open(src_path, "rb") as src_file:
|
||||
shutil.copyfileobj(src_file, payload, 1024 * 1024)
|
||||
|
||||
return payload_path
|
||||
|
||||
def prepare(self, target, using_kernel=False, kernel_bootstrap=False, target_size=0):
|
||||
"""
|
||||
Prepare basic media of live-bootstrap.
|
||||
|
|
@ -59,6 +127,9 @@ class Generator():
|
|||
"""
|
||||
self.target_dir = target.path
|
||||
self.external_dir = os.path.join(self.target_dir, 'external')
|
||||
self.payload_image = None
|
||||
self.payload_source_manifest = []
|
||||
self.bootstrap_source_manifest = self.source_manifest
|
||||
|
||||
# We use ext3 here; ext4 actually has a variety of extensions that
|
||||
# have been added with varying levels of recency
|
||||
|
|
@ -74,6 +145,7 @@ class Generator():
|
|||
|
||||
if not self.repo_path and not self.external_sources:
|
||||
self.external_dir = os.path.join(self.target_dir, 'external')
|
||||
self._prepare_kernel_bootstrap_payload_manifests()
|
||||
elif using_kernel:
|
||||
self.target_dir = os.path.join(self.target_dir, 'disk')
|
||||
self.external_dir = os.path.join(self.target_dir, 'external')
|
||||
|
|
@ -108,6 +180,9 @@ class Generator():
|
|||
if self.repo_path or self.external_sources:
|
||||
mkfs_args = ['-d', os.path.join(target.path, 'external')]
|
||||
target.add_disk("external", filesystem="ext3", mkfs_args=mkfs_args)
|
||||
elif self.payload_source_manifest:
|
||||
self.payload_image = self._create_raw_payload_image(target.path, self.payload_source_manifest)
|
||||
target.add_existing_disk("payload", self.payload_image)
|
||||
elif using_kernel:
|
||||
mkfs_args = ['-F', '-d', os.path.join(target.path, 'disk')]
|
||||
target.add_disk("disk",
|
||||
|
|
@ -158,26 +233,16 @@ class Generator():
|
|||
|
||||
def distfiles(self):
|
||||
"""Copy in distfiles"""
|
||||
def copy_no_network_distfiles(out, early):
|
||||
# Note that "no disk" implies "no network" for kernel bootstrap mode
|
||||
manifest = self.early_source_manifest if early else self.source_manifest
|
||||
for file in manifest:
|
||||
file = file[3].strip()
|
||||
shutil.copy2(os.path.join(self.distfiles_dir, file),
|
||||
os.path.join(out, file))
|
||||
|
||||
early_distfile_dir = os.path.join(self.target_dir, 'external', 'distfiles')
|
||||
main_distfile_dir = os.path.join(self.external_dir, 'distfiles')
|
||||
|
||||
if early_distfile_dir != main_distfile_dir:
|
||||
os.makedirs(early_distfile_dir, exist_ok=True)
|
||||
copy_no_network_distfiles(early_distfile_dir, True)
|
||||
self._copy_manifest_distfiles(early_distfile_dir, self.early_source_manifest)
|
||||
|
||||
if self.external_sources:
|
||||
shutil.copytree(self.distfiles_dir, main_distfile_dir, dirs_exist_ok=True)
|
||||
else:
|
||||
os.mkdir(main_distfile_dir)
|
||||
copy_no_network_distfiles(main_distfile_dir, False)
|
||||
self._copy_manifest_distfiles(main_distfile_dir, self.bootstrap_source_manifest)
|
||||
|
||||
@staticmethod
|
||||
def output_dir(srcfs_file, dirpath):
|
||||
|
|
|
|||
|
|
@ -59,3 +59,7 @@ class Target:
|
|||
def get_disk(self, name):
|
||||
"""Get the path to a device of a disk"""
|
||||
return self._disks.get(name)
|
||||
|
||||
def add_existing_disk(self, name, path):
|
||||
"""Register an existing disk image path."""
|
||||
self._disks[name] = os.path.abspath(path)
|
||||
|
|
|
|||
14
rootfs.py
14
rootfs.py
|
|
@ -127,6 +127,10 @@ def create_configuration_file(args):
|
|||
"""
|
||||
config_path = os.path.join('steps', 'bootstrap.cfg')
|
||||
with open(config_path, "w", encoding="utf_8") as config:
|
||||
payload_required = ((args.bare_metal or args.qemu)
|
||||
and not args.kernel
|
||||
and not args.repo
|
||||
and not args.external_sources)
|
||||
config.write(f"ARCH={args.arch}\n")
|
||||
config.write(f"ARCH_DIR={stage0_arch_map.get(args.arch, args.arch)}\n")
|
||||
config.write(f"FORCE_TIMESTAMPS={args.force_timestamps}\n")
|
||||
|
|
@ -137,6 +141,7 @@ def create_configuration_file(args):
|
|||
config.write(f"FINAL_JOBS={args.cores}\n")
|
||||
config.write(f"INTERNAL_CI={args.internal_ci or False}\n")
|
||||
config.write(f"INTERACTIVE={args.interactive}\n")
|
||||
config.write(f"PAYLOAD_REQUIRED={payload_required}\n")
|
||||
config.write(f"QEMU={args.qemu}\n")
|
||||
config.write(f"BARE_METAL={args.bare_metal or (args.qemu and args.interactive)}\n")
|
||||
config.write(f"BUILD_GUIX_ALSO={args.build_guix_also}\n")
|
||||
|
|
@ -407,6 +412,11 @@ print(shutil.which('chroot'))
|
|||
path = os.path.join(args.target, os.path.relpath(generator.target_dir, args.target))
|
||||
print("Please:")
|
||||
print(f" 1. Take {path}.img and write it to a boot drive and then boot it.")
|
||||
payload_disk = target.get_disk("payload")
|
||||
if payload_disk is not None:
|
||||
payload_path = os.path.join(args.target, os.path.relpath(payload_disk, args.target))
|
||||
print(" 2. Take " +
|
||||
f"{payload_path} and attach it as a second raw disk (/dev/sdb preferred).")
|
||||
|
||||
else:
|
||||
if args.stage0_image:
|
||||
|
|
@ -460,6 +470,10 @@ print(shutil.which('chroot'))
|
|||
arg_list += [
|
||||
'-drive', 'file=' + target.get_disk("external") + ',format=raw',
|
||||
]
|
||||
if target.get_disk("payload") is not None:
|
||||
arg_list += [
|
||||
'-drive', 'file=' + target.get_disk("payload") + ',format=raw',
|
||||
]
|
||||
arg_list += [
|
||||
'-machine', 'kernel-irqchip=split',
|
||||
'-nic', 'user,ipv6=off,model=e1000'
|
||||
|
|
|
|||
22
steps/improve/import_payload.sh
Normal file
22
steps/improve/import_payload.sh
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 live-bootstrap contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -ex
|
||||
|
||||
if match x${PAYLOAD_REQUIRED} xTrue; then
|
||||
mkdir -p /dev
|
||||
test -b /dev/sda || mknod -m 600 /dev/sda b 8 0
|
||||
test -b /dev/sdb || mknod -m 600 /dev/sdb b 8 16
|
||||
test -b /dev/sdc || mknod -m 600 /dev/sdc b 8 32
|
||||
test -b /dev/sdd || mknod -m 600 /dev/sdd b 8 48
|
||||
|
||||
mkdir -p /external/distfiles
|
||||
if test -f /external/distfiles/.payload_imported; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
payload-import /external/distfiles
|
||||
touch /external/distfiles/.payload_imported
|
||||
fi
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
#include "multiboot1.h"
|
||||
|
||||
#define MULTIBOOT_MAGIC 0x2BADB002
|
||||
#define ELF_PT_LOAD 1
|
||||
|
||||
multiboot_uint32_t get_memmap(char *filename, void *memmap_addr) {
|
||||
|
||||
|
|
@ -100,43 +99,24 @@ int main(int argc, char **argv) {
|
|||
unsigned int e_phentsize = *((unsigned int *) (&fiwix_mem[0x2A]));
|
||||
e_phentsize &= 0xFFFF;
|
||||
printf("ELF size of program headers : %d\n", e_phentsize);
|
||||
if ((unsigned long) e_phoff + ((unsigned long) e_phnum * (unsigned long) e_phentsize) > (unsigned long) fiwix_len) {
|
||||
printf("kexec-fiwix: invalid ELF header table (offset=0x%x num=%u entsize=%u)\n",
|
||||
e_phoff, e_phnum, e_phentsize);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
/* Load the kernel */
|
||||
puts("kexec-fiwix: Placing kernel in memory...");
|
||||
|
||||
int header_num;
|
||||
int adjusted_entry = 0;
|
||||
unsigned int e_entry_phys = e_entry;
|
||||
for (header_num = 0; header_num < e_phnum; header_num++) {
|
||||
char * fiwix_prog_header = &fiwix_mem[e_phoff + header_num * e_phentsize];
|
||||
|
||||
unsigned int p_type = *((unsigned int *) (&fiwix_prog_header[0x00]));
|
||||
unsigned int p_offset = *((unsigned int *) (&fiwix_prog_header[0x04]));
|
||||
unsigned int p_vaddr = *((unsigned int *) (&fiwix_prog_header[0x08]));
|
||||
unsigned int p_paddr = *((unsigned int *) (&fiwix_prog_header[0x0C]));
|
||||
unsigned int p_filesz = *((unsigned int *) (&fiwix_prog_header[0x10]));
|
||||
unsigned int p_memsz = *((unsigned int *) (&fiwix_prog_header[0x14]));
|
||||
|
||||
if (p_type != ELF_PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
if (p_filesz > p_memsz) {
|
||||
printf("kexec-fiwix: invalid segment %d, p_filesz > p_memsz\n", header_num);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if ((unsigned long) p_offset + (unsigned long) p_filesz > (unsigned long) fiwix_len) {
|
||||
printf("kexec-fiwix: invalid segment %d, out-of-bounds file range\n", header_num);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!adjusted_entry) {
|
||||
e_entry_phys -= (p_vaddr - p_paddr);
|
||||
adjusted_entry = 1;
|
||||
if (header_num == 0) {
|
||||
e_entry -= (p_vaddr - p_paddr);
|
||||
printf("ELF physical entry point : 0x%x\n", e_entry);
|
||||
}
|
||||
|
||||
printf("header %d:\n", header_num);
|
||||
|
|
@ -148,11 +128,6 @@ int main(int argc, char **argv) {
|
|||
memset((void *)p_paddr, 0, p_memsz + 0x10000);
|
||||
memcpy((void *)p_paddr, &fiwix_mem[p_offset], p_filesz);
|
||||
}
|
||||
if (!adjusted_entry) {
|
||||
printf("kexec-fiwix: no PT_LOAD segments found in kernel ELF\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("ELF physical entry point : 0x%x\n", e_entry_phys);
|
||||
|
||||
puts("Preparing multiboot info for kernel...");
|
||||
|
||||
|
|
@ -181,26 +156,16 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
int filenum;
|
||||
int found_image = 0;
|
||||
unsigned int filename_addr;
|
||||
for (filenum = 4, filename_addr = 0x201000; filenum <= 14335; filenum++, filename_addr += 1024) {
|
||||
if (!strcmp((char *) filename_addr, initrd_filename)) {
|
||||
printf("Found image at filenum %d\n", filenum);
|
||||
found_image = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_image) {
|
||||
printf("kexec-fiwix: initrd image not found in file table: %s\n", initrd_filename);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
unsigned int initrd_src = *((unsigned int *) (0x01000000 + (16 * filenum) + 4));
|
||||
unsigned int initrd_len = *((unsigned int *) (0x01000000 + (16 * filenum) + 8));
|
||||
if (initrd_src == 0 || initrd_len == 0) {
|
||||
printf("kexec-fiwix: invalid initrd metadata src=0x%08x len=0x%08x\n", initrd_src, initrd_len);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("initrd_src: 0x%08x\n", initrd_src);
|
||||
printf("initrd_len: 0x%08x\n", initrd_len);
|
||||
|
||||
|
|
@ -276,6 +241,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
/* Jump to kernel entry point */
|
||||
unsigned int magic = MULTIBOOT_BOOTLOADER_MAGIC;
|
||||
unsigned int dummy = 0;
|
||||
unsigned int multiboot_info_num = (unsigned int) pmultiboot_info;
|
||||
|
||||
printf("Preparing trampoline...\n");
|
||||
|
|
@ -294,7 +260,7 @@ int main(int argc, char **argv) {
|
|||
0xF3, 0xA4, /* rep movsb */
|
||||
0xB8, 0x00, 0x00, 0x00, 0x00, /* mov eax, 0x00000000 */
|
||||
0xBB, 0x00, 0x00, 0x00, 0x00, /* mov ebx, 0x00000000 */
|
||||
0xFA, /* cli */
|
||||
0xFB, /* sti */
|
||||
0xEA, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 /* jmp far 0x0008:0x00000000 */
|
||||
};
|
||||
|
||||
|
|
@ -304,7 +270,7 @@ int main(int argc, char **argv) {
|
|||
*((unsigned int *) &trampoline[11]) = initrd_len;
|
||||
*((unsigned int *) &trampoline[19]) = magic;
|
||||
*((unsigned int *) &trampoline[24]) = multiboot_info_num;
|
||||
*((unsigned int *) &trampoline[30]) = e_entry_phys;
|
||||
*((unsigned int *) &trampoline[30]) = e_entry;
|
||||
|
||||
memcpy((void *)0x4000, trampoline, sizeof(trampoline));
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ define: BUILD_FIWIX = ( KERNEL_BOOTSTRAP == True || BUILD_KERNELS == True )
|
|||
build: fiwix-1.5.0-lb1 ( BUILD_FIWIX == True )
|
||||
build: lwext4-1.0.0-lb1 ( BUILD_FIWIX == True )
|
||||
build: kexec-fiwix-1.0 ( BUILD_FIWIX == True )
|
||||
build: payload-import-1.0 ( KERNEL_BOOTSTRAP == True )
|
||||
jump: fiwix ( KERNEL_BOOTSTRAP == True )
|
||||
improve: import_payload ( KERNEL_BOOTSTRAP == True )
|
||||
improve: reconfigure ( CONFIGURATOR != True )
|
||||
define: JOBS = 1 ( KERNEL_BOOTSTRAP == True )
|
||||
build: make-3.82
|
||||
|
|
|
|||
19
steps/payload-import-1.0/pass1.kaem
Normal file
19
steps/payload-import-1.0/pass1.kaem
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 live-bootstrap contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -ex
|
||||
|
||||
cd src
|
||||
tcc -m32 -march=i386 -std=c89 -I../../tcc/tcc-0.9.27/include -o ${BINDIR}/payload-import payload-import.c
|
||||
cd ..
|
||||
|
||||
if match x${UPDATE_CHECKSUMS} xTrue; then
|
||||
sha256sum -o ${pkg}.checksums \
|
||||
/usr/bin/payload-import
|
||||
|
||||
cp ${pkg}.checksums ${SRCDIR}
|
||||
elif test -f ${pkg}.checksums; then
|
||||
sha256sum -c ${pkg}.checksums
|
||||
fi
|
||||
297
steps/payload-import-1.0/src/payload-import.c
Normal file
297
steps/payload-import-1.0/src/payload-import.c
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* SPDX-FileCopyrightText: 2026 live-bootstrap contributors */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAGIC "LBPAYLD1"
|
||||
#define MAGIC_LEN 8
|
||||
#define MAX_NAME_LEN 1024
|
||||
#define COPY_BUFSZ 65536
|
||||
|
||||
static unsigned int read_u32le(const unsigned char *buf)
|
||||
{
|
||||
return (unsigned int)buf[0]
|
||||
| ((unsigned int)buf[1] << 8)
|
||||
| ((unsigned int)buf[2] << 16)
|
||||
| ((unsigned int)buf[3] << 24);
|
||||
}
|
||||
|
||||
static int read_exact(FILE *in, void *buf, unsigned int len)
|
||||
{
|
||||
unsigned int got = 0;
|
||||
unsigned char *out = (unsigned char *)buf;
|
||||
|
||||
while (got < len) {
|
||||
size_t n = fread(out + got, 1, len - got, in);
|
||||
if (n == 0) {
|
||||
return -1;
|
||||
}
|
||||
got += (unsigned int)n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_exact(FILE *in, FILE *out, unsigned int len)
|
||||
{
|
||||
unsigned char *buf;
|
||||
unsigned int remaining = len;
|
||||
|
||||
buf = (unsigned char *)malloc(COPY_BUFSZ);
|
||||
if (buf == NULL) {
|
||||
fputs("payload-import: out of memory\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (remaining > 0) {
|
||||
unsigned int chunk = remaining;
|
||||
size_t written;
|
||||
if (chunk > COPY_BUFSZ) {
|
||||
chunk = COPY_BUFSZ;
|
||||
}
|
||||
if (read_exact(in, buf, chunk) != 0) {
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
written = fwrite(buf, 1, chunk, out);
|
||||
if (written != chunk) {
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
remaining -= chunk;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_valid_name(const char *name)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char *)name;
|
||||
|
||||
if (*s == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*s != 0) {
|
||||
if (*s == '/' || *s == '\\') {
|
||||
return 0;
|
||||
}
|
||||
s += 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int has_payload_magic(const char *path)
|
||||
{
|
||||
FILE *in;
|
||||
char magic[MAGIC_LEN];
|
||||
|
||||
in = fopen(path, "rb");
|
||||
if (in == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if (read_exact(in, magic, MAGIC_LEN) != 0) {
|
||||
fclose(in);
|
||||
return 1;
|
||||
}
|
||||
fclose(in);
|
||||
if (memcmp(magic, MAGIC, MAGIC_LEN) != 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extract_payload(const char *device, const char *dest_dir)
|
||||
{
|
||||
FILE *in;
|
||||
char magic[MAGIC_LEN];
|
||||
unsigned char u32buf[4];
|
||||
unsigned int file_count;
|
||||
unsigned int i;
|
||||
|
||||
in = fopen(device, "rb");
|
||||
if (in == NULL) {
|
||||
fprintf(stderr, "payload-import: cannot open %s: %s\n", device, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read_exact(in, magic, MAGIC_LEN) != 0 || memcmp(magic, MAGIC, MAGIC_LEN) != 0) {
|
||||
fclose(in);
|
||||
fprintf(stderr, "payload-import: %s is not a payload image\n", device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read_exact(in, u32buf, 4) != 0) {
|
||||
fclose(in);
|
||||
fputs("payload-import: malformed payload header\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
file_count = read_u32le(u32buf);
|
||||
if (file_count > 200000U) {
|
||||
fclose(in);
|
||||
fprintf(stderr, "payload-import: unreasonable file count: %u\n", file_count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mkdir(dest_dir, 0755) != 0 && errno != EEXIST) {
|
||||
fclose(in);
|
||||
fprintf(stderr, "payload-import: cannot create %s: %s\n", dest_dir, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("payload-import: reading %u files from %s\n", file_count, device);
|
||||
for (i = 0; i < file_count; ++i) {
|
||||
unsigned int name_len;
|
||||
unsigned int data_len;
|
||||
char *name;
|
||||
char out_path[4096];
|
||||
FILE *out;
|
||||
|
||||
if (read_exact(in, u32buf, 4) != 0) {
|
||||
fclose(in);
|
||||
fputs("payload-import: truncated entry header\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
name_len = read_u32le(u32buf);
|
||||
if (read_exact(in, u32buf, 4) != 0) {
|
||||
fclose(in);
|
||||
fputs("payload-import: truncated entry size\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
data_len = read_u32le(u32buf);
|
||||
|
||||
if (name_len == 0 || name_len > MAX_NAME_LEN) {
|
||||
fclose(in);
|
||||
fprintf(stderr, "payload-import: invalid name length %u\n", name_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
name = (char *)malloc(name_len + 1);
|
||||
if (name == NULL) {
|
||||
fclose(in);
|
||||
fputs("payload-import: out of memory\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read_exact(in, name, name_len) != 0) {
|
||||
free(name);
|
||||
fclose(in);
|
||||
fputs("payload-import: truncated file name\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
name[name_len] = 0;
|
||||
|
||||
if (!is_valid_name(name)) {
|
||||
fclose(in);
|
||||
fprintf(stderr, "payload-import: invalid payload file name: %s\n", name);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (snprintf(out_path, sizeof(out_path), "%s/%s", dest_dir, name) >= (int)sizeof(out_path)) {
|
||||
free(name);
|
||||
fclose(in);
|
||||
fputs("payload-import: output path too long\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
out = fopen(out_path, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "payload-import: cannot write %s: %s\n", out_path, strerror(errno));
|
||||
free(name);
|
||||
fclose(in);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (copy_exact(in, out, data_len) != 0) {
|
||||
fprintf(stderr, "payload-import: failed while copying %s\n", name);
|
||||
free(name);
|
||||
fclose(out);
|
||||
fclose(in);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
printf("payload-import: %s\n", name);
|
||||
free(name);
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_from_first_payload(const char *dest_dir)
|
||||
{
|
||||
const char *prefixes[] = {"/dev/sd", "/dev/vd", "/dev/hd"};
|
||||
int p;
|
||||
|
||||
for (p = 0; p < 3; ++p) {
|
||||
char letter;
|
||||
for (letter = 'b'; letter <= 'z'; ++letter) {
|
||||
char device[16];
|
||||
if (snprintf(device, sizeof(device), "%s%c", prefixes[p], letter) >= (int)sizeof(device)) {
|
||||
continue;
|
||||
}
|
||||
if (access(device, R_OK) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (has_payload_magic(device) == 0) {
|
||||
return extract_payload(device, dest_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs("payload-import: no payload disk found\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage:\n"
|
||||
" %s --probe <device>\n"
|
||||
" %s [--device <device>] <dest-dir>\n",
|
||||
name, name);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *device = NULL;
|
||||
const char *dest_dir = NULL;
|
||||
int i;
|
||||
|
||||
if (argc == 3 && strcmp(argv[1], "--probe") == 0) {
|
||||
return has_payload_magic(argv[2]);
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--device") == 0) {
|
||||
i += 1;
|
||||
if (i >= argc) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
device = argv[i];
|
||||
} else if (dest_dir == NULL) {
|
||||
dest_dir = argv[i];
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_dir == NULL) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (device != NULL) {
|
||||
return extract_payload(device, dest_dir);
|
||||
}
|
||||
return import_from_first_payload(dest_dir);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue