add RISC-V 32-bit env to CI (#6109)

This commit is contained in:
مصطفي محمود كمال الدين 2026-04-14 09:39:39 +02:00 committed by GitHub
parent 4e5a8e4826
commit cf39746a03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 932 additions and 0 deletions

63
.github/riscv32-qemu-setup-all.sh vendored Normal file
View file

@ -0,0 +1,63 @@
#!/bin/sh
set -eux
MINSTACK_VERSION=v12
# Download the linux kernel image, opensbi firmware, and the busybox userspace from the minstack repo
wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-Image"
wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-fw_dynamic.bin"
wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-busybox"
#---------------------------------------------------------------------------------------------------------------------
cd rizin
# Setup cross file
sed -i "s|<<path-to-bootlin-installation>>|${BOOTLIN_INSTALLATION}|g" doc/examples/cross_builds/meson-linux-riscv32.ini
export PATH="${BOOTLIN_TOOLS}:${PATH}"
# Build Rizin
meson setup build --cross-file doc/examples/cross_builds/meson-linux-riscv32.ini
meson compile -C build || true # Expected to fail due to Zydis bug on RISC-V 32-bit
# Quick and dirty fix for Zydis compilation error, remove when upstream merges a fix and we bump
patch -p1 < patches/fix_zydis_amalgamated_riscv32_build
# Build again
meson compile -C build
file build/binrz/rizin/rizin
cd ..
#---------------------------------------------------------------------------------------------------------------------
# Create a init config for busybox
cat << EOF > inittab
::sysinit:/mount -t proc proc /proc
::sysinit:/mount -t sysfs sysfs /sys
::sysinit:/mount -t devtmpfs devtmpfs /dev
::sysinit:/mount -t tmpfs -o size=128M,nodev,nosuid tmpfs /tmp
::wait:$@
::once:/poweroff -f
EOF
# Describe the boot filesystem
cat << EOF > initramfs
dir /bin 0755 0 0
dir /etc 0755 0 0
dir /lib 0755 0 0
dir /tmp 1777 0 0
dir /proc 0755 0 0
dir /sys 0755 0 0
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
file /bin/busybox rv32-busybox 0755 0 0
slink /init /bin/busybox 0777 0 0
slink /mount /bin/busybox 0777 0 0
slink /poweroff /bin/busybox 0777 0 0
file /etc/inittab inittab 0644 0 0
file /lib/ld-musl-riscv32.so.1 ${BOOTLIN_INSTALLATION}/riscv32-buildroot-linux-musl/sysroot/lib/ld-musl-riscv32.so.1 0755 0 0
file /bin/rizin rizin/build/binrz/rizin/rizin 0755 0 0
EOF
# Generate the boot filesystem
gcc rizin/sys/gen_cpio_script.c -O3 -o gen_cpio
./gen_cpio initramfs > initrd_rv32.cpio

View file

@ -914,6 +914,7 @@ jobs:
.\rizin_cmake_test
test-riscv-inside-qemu:
name: Test Rizin on RISC-V 64-bit inside QEMU
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
@ -943,6 +944,56 @@ jobs:
-append "console=ttyS0,115200 earlycon" \
-nographic
test-riscv32-in-qemu:
name: Test Rizin on RISC-V 32-bit inside QEMU
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: |
sudo apt-get update -q
sudo apt-get install -y qemu-system-misc qemu-user-static
pip3 install meson ninja
- name: Bootlin env definitions
run: |
set -a
BOOTLIN_VERSION=stable-2025.08-1
BOOTLIN_LIBC=musl
BOOTLIN_RV32_TOOLCHAIN=riscv32-ilp32d--${BOOTLIN_LIBC}--${BOOTLIN_VERSION}.tar.xz
BOOTLIN_DOMAIN=https://toolchains.bootlin.com
BOOTLIN_DOWNLOAD_URL=${BOOTLIN_DOMAIN}/downloads/releases/toolchains/riscv32-ilp32d/tarballs/${BOOTLIN_RV32_TOOLCHAIN}
BOOTLIN_INSTALLATION="${GITHUB_WORKSPACE}/toolchain/${BOOTLIN_RV32_TOOLCHAIN%.tar.xz}"
BOOTLIN_TOOLS="${BOOTLIN_INSTALLATION}/bin"
env | grep "^BOOTLIN_" >> $GITHUB_ENV
- name: Try getting the Bootlin toolchain from cache
id: cache-toolchain
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/toolchain
key: ${{ env.BOOTLIN_RV32_TOOLCHAIN }}
- name: Download Bootlin on cache miss
if: steps.cache-toolchain.outputs.cache-hit != 'true'
run: |
mkdir -p toolchain && cd toolchain
wget -q ${{ env.BOOTLIN_DOWNLOAD_URL }}
tar -xf ${{ env.BOOTLIN_RV32_TOOLCHAIN }}
rm ${{ env.BOOTLIN_RV32_TOOLCHAIN }}
- name: Setup the QEMU image
run: cd .. && sh ./rizin/.github/riscv32-qemu-setup-all.sh /bin/rizin -hh
- name: Run Rizin on RISCV32 inside QEMU
run: |
cd ..
qemu-system-riscv32 -machine virt -m 512M \
-bios rv32-fw_dynamic.bin \
-kernel rv32-Image \
-initrd initrd_rv32.cpio \
-append "earlycon console=hvc0" \
-nographic
create-release:
name: Create draft release and upload artifacts

View file

@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2024-2026 moste00 <ubermenchun@gmail.com>
# SPDX-License-Identifier: BSD-3-Clause
# Replace (manually or with sed -i) <<path-to-bootlin-installation>> with the actual path to your Bootlin directory
# (The Bootlin directory is the parent of bin/riscv32-buildroot-linux-musl-*)
[binaries]
c = 'riscv32-buildroot-linux-musl-gcc'
cpp = 'riscv32-buildroot-linux-musl-g++'
ar = 'riscv32-buildroot-linux-musl-ar'
strip = 'riscv32-buildroot-linux-musl-strip'
objcopy = 'riscv32-buildroot-linux-musl-objcopy'
objdump = 'riscv32-buildroot-linux-musl-objdump'
ranlib = 'riscv32-buildroot-linux-musl-ranlib'
pkgconfig = 'pkg-config'
[built-in options]
c_args = ['-march=rv32gc', '-mabi=ilp32d', '-static', '-fPIC']
cpp_args = ['-march=rv32gc', '-mabi=ilp32d', '-static', '-fPIC']
c_link_args = ['-march=rv32gc', '-mabi=ilp32d', '-fPIC']
cpp_link_args = ['-march=rv32gc', '-mabi=ilp32d', '-fPIC']
default_library = 'static'
[properties]
sys_root = '<<path-to-bootlin-installation>>/riscv32-buildroot-linux-musl/sysroot'
pkg_config_libdir = '<<path-to-bootlin-installation>>/riscv32-buildroot-linux-musl/sysroot/usr/lib/pkgconfig'
[host_machine]
system = 'linux'
cpu_family = 'riscv32'
cpu = 'riscv32'
endian = 'little'

View file

@ -0,0 +1,22 @@
diff --git a/home/mostafa/BEFORE_ZYDIS.c b/subprojects/zydis/amalgamated-dist/Zydis.c
index 66dcb1ed3d..297dfd050a 100644
--- a/subprojects/zydis/amalgamated-dist/Zydis.c
+++ b/subprojects/zydis/amalgamated-dist/Zydis.c
@@ -54437,7 +54437,7 @@ static const char* const DECIMAL_LOOKUP =
/* Decimal */
/* ---------------------------------------------------------------------------------------------- */
-#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) || defined(ZYAN_S390)
+#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) || defined(ZYAN_S390) || defined(ZYAN_RISCV32)
static ZyanStatus ZydisStringAppendDecU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length)
{
ZYAN_ASSERT(string);
@@ -54529,7 +54529,7 @@ static ZyanStatus ZydisStringAppendDecU64(ZyanString* string, ZyanU64 value, Zya
/* Hexadecimal */
/* ---------------------------------------------------------------------------------------------- */
-#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC)
+#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) || defined(ZYAN_RISCV32)
static ZyanStatus ZydisStringAppendHexU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length,
ZyanBool force_leading_number, ZyanBool uppercase)
{

766
sys/gen_cpio_script.c Normal file
View file

@ -0,0 +1,766 @@
// VENDORED FROM: Linux Kernel sources 6.19.0
// PLEASE DO NOT EDIT BY HAND UNLESS YOU KNOW WHAT YOU'RE DOING
// (But can copy again from newer kernel sources when/if necessary,
// but please keep this text and the FileCopyrightText line)
// ----------------------------------------------------------------------------
// SPDX-FileCopyrightText: 2005 Jeff Garzik <none@none.com>
// SPDX-License-Identifier: GPL-2.0-only
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
/*
* Original work by Jeff Garzik
*
* External file lists, symlink, pipe and fifo support by Thayne Harbaugh
* Hard link support by Luciano Rocha
*/
#define xstr(s) #s
#define str(s) xstr(s)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define CPIO_HDR_LEN 110
#define CPIO_TRAILER "TRAILER!!!"
#define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align))
/* zero-padding the filename field for data alignment is limited by PATH_MAX */
static char padding[PATH_MAX];
static unsigned int offset;
static unsigned int ino = 721;
static time_t default_mtime;
static bool do_file_mtime;
static bool do_csum = false;
static int outfd = STDOUT_FILENO;
static unsigned int dalign;
struct file_handler {
const char *type;
int (*handler)(const char *line);
};
static int push_buf(const char *name, size_t name_len) {
ssize_t len;
len = write(outfd, name, name_len);
if (len != name_len)
return -1;
offset += name_len;
return 0;
}
static int push_pad(size_t padlen) {
ssize_t len = 0;
if (!padlen)
return 0;
if (padlen < sizeof(padding))
len = write(outfd, padding, padlen);
if (len != padlen)
return -1;
offset += padlen;
return 0;
}
static int push_rest(const char *name, size_t name_len) {
ssize_t len;
len = write(outfd, name, name_len);
if (len != name_len)
return -1;
offset += name_len;
return push_pad(padlen(name_len + CPIO_HDR_LEN, 4));
}
static int cpio_trailer(void) {
int len;
unsigned int namesize = sizeof(CPIO_TRAILER);
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
"%08X%08X%08X%08X%08X%08X%08X",
do_csum ? "070702" : "070701", /* magic */
0, /* ino */
0, /* mode */
(long)0, /* uid */
(long)0, /* gid */
1, /* nlink */
(long)0, /* mtime */
0, /* filesize */
0, /* major */
0, /* minor */
0, /* rmajor */
0, /* rminor */
namesize, /* namesize */
0); /* chksum */
offset += len;
if (len != CPIO_HDR_LEN ||
push_rest(CPIO_TRAILER, namesize) < 0 ||
push_pad(padlen(offset, 512)) < 0)
return -1;
if (fsync(outfd) < 0 && errno != EINVAL)
return -1;
return 0;
}
static int cpio_mkslink(const char *name, const char *target,
unsigned int mode, uid_t uid, gid_t gid) {
int len;
unsigned int namesize, targetsize = strlen(target) + 1;
if (name[0] == '/')
name++;
namesize = strlen(name) + 1;
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
"%08X%08X%08X%08X%08X%08X%08X",
do_csum ? "070702" : "070701", /* magic */
ino++, /* ino */
S_IFLNK | mode, /* mode */
(long)uid, /* uid */
(long)gid, /* gid */
1, /* nlink */
(long)default_mtime, /* mtime */
targetsize, /* filesize */
3, /* major */
1, /* minor */
0, /* rmajor */
0, /* rminor */
namesize, /* namesize */
0); /* chksum */
offset += len;
if (len != CPIO_HDR_LEN ||
push_buf(name, namesize) < 0 ||
push_pad(padlen(offset, 4)) < 0 ||
push_buf(target, targetsize) < 0 ||
push_pad(padlen(offset, 4)) < 0)
return -1;
return 0;
}
static int cpio_mkslink_line(const char *line) {
char name[PATH_MAX + 1];
char target[PATH_MAX + 1];
unsigned int mode;
int uid;
int gid;
int rc = -1;
if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
fprintf(stderr, "Unrecognized dir format '%s'", line);
goto fail;
}
rc = cpio_mkslink(name, target, mode, uid, gid);
fail:
return rc;
}
static int cpio_mkgeneric(const char *name, unsigned int mode,
uid_t uid, gid_t gid) {
int len;
unsigned int namesize;
if (name[0] == '/')
name++;
namesize = strlen(name) + 1;
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
"%08X%08X%08X%08X%08X%08X%08X",
do_csum ? "070702" : "070701", /* magic */
ino++, /* ino */
mode, /* mode */
(long)uid, /* uid */
(long)gid, /* gid */
2, /* nlink */
(long)default_mtime, /* mtime */
0, /* filesize */
3, /* major */
1, /* minor */
0, /* rmajor */
0, /* rminor */
namesize, /* namesize */
0); /* chksum */
offset += len;
if (len != CPIO_HDR_LEN ||
push_rest(name, namesize) < 0)
return -1;
return 0;
}
enum generic_types {
GT_DIR,
GT_PIPE,
GT_SOCK
};
struct generic_type {
const char *type;
mode_t mode;
};
static const struct generic_type generic_type_table[] = {
[GT_DIR] = {
.type = "dir",
.mode = S_IFDIR },
[GT_PIPE] = { .type = "pipe", .mode = S_IFIFO },
[GT_SOCK] = { .type = "sock", .mode = S_IFSOCK }
};
static int cpio_mkgeneric_line(const char *line, enum generic_types gt) {
char name[PATH_MAX + 1];
unsigned int mode;
int uid;
int gid;
int rc = -1;
if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
fprintf(stderr, "Unrecognized %s format '%s'",
line, generic_type_table[gt].type);
goto fail;
}
mode |= generic_type_table[gt].mode;
rc = cpio_mkgeneric(name, mode, uid, gid);
fail:
return rc;
}
static int cpio_mkdir_line(const char *line) {
return cpio_mkgeneric_line(line, GT_DIR);
}
static int cpio_mkpipe_line(const char *line) {
return cpio_mkgeneric_line(line, GT_PIPE);
}
static int cpio_mksock_line(const char *line) {
return cpio_mkgeneric_line(line, GT_SOCK);
}
static int cpio_mknod(const char *name, unsigned int mode,
uid_t uid, gid_t gid, char dev_type,
unsigned int maj, unsigned int min) {
int len;
unsigned int namesize;
if (dev_type == 'b')
mode |= S_IFBLK;
else
mode |= S_IFCHR;
if (name[0] == '/')
name++;
namesize = strlen(name) + 1;
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
"%08X%08X%08X%08X%08X%08X%08X",
do_csum ? "070702" : "070701", /* magic */
ino++, /* ino */
mode, /* mode */
(long)uid, /* uid */
(long)gid, /* gid */
1, /* nlink */
(long)default_mtime, /* mtime */
0, /* filesize */
3, /* major */
1, /* minor */
maj, /* rmajor */
min, /* rminor */
namesize, /* namesize */
0); /* chksum */
offset += len;
if (len != CPIO_HDR_LEN ||
push_rest(name, namesize) < 0)
return -1;
return 0;
}
static int cpio_mknod_line(const char *line) {
char name[PATH_MAX + 1];
unsigned int mode;
int uid;
int gid;
char dev_type;
unsigned int maj;
unsigned int min;
int rc = -1;
if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u", name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
fprintf(stderr, "Unrecognized nod format '%s'", line);
goto fail;
}
rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
fail:
return rc;
}
static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum) {
while (size) {
unsigned char filebuf[65536];
ssize_t this_read;
size_t i, this_size = MIN(size, sizeof(filebuf));
this_read = read(fd, filebuf, this_size);
if (this_read <= 0 || this_read > this_size)
return -1;
for (i = 0; i < this_read; i++)
*csum += filebuf[i];
size -= this_read;
}
/* seek back to the start for data segment I/O */
if (lseek(fd, 0, SEEK_SET) < 0)
return -1;
return 0;
}
static int cpio_mkfile(const char *name, const char *location,
unsigned int mode, uid_t uid, gid_t gid,
unsigned int nlinks) {
struct stat buf;
unsigned long size;
int file, retval, len;
int rc = -1;
time_t mtime;
int namesize, namepadlen;
unsigned int i;
uint32_t csum = 0;
ssize_t this_read;
mode |= S_IFREG;
file = open(location, O_RDONLY);
if (file < 0) {
fprintf(stderr, "File %s could not be opened for reading\n", location);
goto error;
}
retval = fstat(file, &buf);
if (retval) {
fprintf(stderr, "File %s could not be stat()'ed\n", location);
goto error;
}
if (do_file_mtime) {
mtime = default_mtime;
} else {
mtime = buf.st_mtime;
if (mtime > 0xffffffff) {
fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
location);
mtime = 0xffffffff;
}
if (mtime < 0) {
fprintf(stderr, "%s: Timestamp negative, clipping.\n",
location);
mtime = 0;
}
}
if (buf.st_size > 0xffffffff) {
fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
location);
goto error;
}
if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
fprintf(stderr, "Failed to checksum file %s\n", location);
goto error;
}
size = 0;
namepadlen = 0;
for (i = 1; i <= nlinks; i++) {
if (name[0] == '/')
name++;
namesize = strlen(name) + 1;
/* data goes on last link, after any alignment padding */
if (i == nlinks)
size = buf.st_size;
if (dalign && size > dalign) {
namepadlen = padlen(offset + CPIO_HDR_LEN + namesize,
dalign);
if (namesize + namepadlen > PATH_MAX) {
fprintf(stderr,
"%s: best-effort alignment %u missed\n",
name, dalign);
namepadlen = 0;
}
}
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
"%08lX%08X%08X%08X%08X%08X%08X",
do_csum ? "070702" : "070701", /* magic */
ino, /* ino */
mode, /* mode */
(long)uid, /* uid */
(long)gid, /* gid */
nlinks, /* nlink */
(long)mtime, /* mtime */
size, /* filesize */
3, /* major */
1, /* minor */
0, /* rmajor */
0, /* rminor */
namesize + namepadlen, /* namesize */
size ? csum : 0); /* chksum */
offset += len;
if (len != CPIO_HDR_LEN ||
push_buf(name, namesize) < 0 ||
push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0)
goto error;
if (size) {
this_read = copy_file_range(file, NULL, outfd, NULL, size, 0);
if (this_read > 0) {
if (this_read > size)
goto error;
offset += this_read;
size -= this_read;
}
/* short or failed copy falls back to read/write... */
}
while (size) {
unsigned char filebuf[65536];
size_t this_size = MIN(size, sizeof(filebuf));
this_read = read(file, filebuf, this_size);
if (this_read <= 0 || this_read > this_size) {
fprintf(stderr, "Can not read %s file\n", location);
goto error;
}
if (write(outfd, filebuf, this_read) != this_read) {
fprintf(stderr, "writing filebuf failed\n");
goto error;
}
offset += this_read;
size -= this_read;
}
if (push_pad(padlen(offset, 4)) < 0)
goto error;
name += namesize;
}
ino++;
rc = 0;
error:
if (file >= 0)
close(file);
return rc;
}
static char *cpio_replace_env(char *new_location) {
char expanded[PATH_MAX + 1];
char *start, *end, *var;
while ((start = strstr(new_location, "${")) &&
(end = strchr(start + 2, '}'))) {
*start = *end = 0;
var = getenv(start + 2);
snprintf(expanded, sizeof expanded, "%s%s%s",
new_location, var ? var : "", end + 1);
strcpy(new_location, expanded);
}
return new_location;
}
static int cpio_mkfile_line(const char *line) {
char name[PATH_MAX + 1];
char *dname = NULL; /* malloc'ed buffer for hard links */
char location[PATH_MAX + 1];
unsigned int mode;
int uid;
int gid;
int nlinks = 1;
int end = 0, dname_len = 0;
int rc = -1;
if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d %n",
name, location, &mode, &uid, &gid, &end)) {
fprintf(stderr, "Unrecognized file format '%s'", line);
goto fail;
}
if (end && isgraph(line[end])) {
int len;
int nend;
dname = malloc(strlen(line));
if (!dname) {
fprintf(stderr, "out of memory (%d)\n", dname_len);
goto fail;
}
dname_len = strlen(name) + 1;
memcpy(dname, name, dname_len);
do {
nend = 0;
if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
name, &nend) < 1)
break;
len = strlen(name) + 1;
memcpy(dname + dname_len, name, len);
dname_len += len;
nlinks++;
end += nend;
} while (isgraph(line[end]));
} else {
dname = name;
}
rc = cpio_mkfile(dname, cpio_replace_env(location),
mode, uid, gid, nlinks);
fail:
if (dname_len)
free(dname);
return rc;
}
static void usage(const char *prog) {
fprintf(stderr, "Usage:\n"
"\t%s [-t <timestamp>] [-c] [-o <output_file>] [-a <data_align>] <cpio_list>\n"
"\n"
"<cpio_list> is a file containing newline separated entries that\n"
"describe the files to be included in the initramfs archive:\n"
"\n"
"# a comment\n"
"file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
"dir <name> <mode> <uid> <gid>\n"
"nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
"slink <name> <target> <mode> <uid> <gid>\n"
"pipe <name> <mode> <uid> <gid>\n"
"sock <name> <mode> <uid> <gid>\n"
"\n"
"<name> name of the file/dir/nod/etc in the archive\n"
"<location> location of the file in the current filesystem\n"
" expands shell variables quoted with ${}\n"
"<target> link target\n"
"<mode> mode/permissions of the file\n"
"<uid> user id (0=root)\n"
"<gid> group id (0=root)\n"
"<dev_type> device type (b=block, c=character)\n"
"<maj> major number of nod\n"
"<min> minor number of nod\n"
"<hard links> space separated list of other links to file\n"
"\n"
"example:\n"
"# A simple initramfs\n"
"dir /dev 0755 0 0\n"
"nod /dev/console 0600 0 0 c 5 1\n"
"dir /root 0700 0 0\n"
"dir /sbin 0755 0 0\n"
"file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
"\n"
"<timestamp> is time in seconds since Epoch that will be used\n"
"as mtime for symlinks, directories, regular and special files.\n"
"The default is to use the current time for all files, but\n"
"preserve modification time for regular files.\n"
"-c: calculate and store 32-bit checksums for file data.\n"
"<output_file>: write cpio to this file instead of stdout\n"
"<data_align>: attempt to align file data by zero-padding the\n"
"filename field up to data_align. Must be a multiple of 4.\n"
"Alignment is best-effort; PATH_MAX limits filename padding.\n",
prog);
}
static const struct file_handler file_handler_table[] = {
{
.type = "file",
.handler = cpio_mkfile_line,
},
{
.type = "nod",
.handler = cpio_mknod_line,
},
{
.type = "dir",
.handler = cpio_mkdir_line,
},
{
.type = "slink",
.handler = cpio_mkslink_line,
},
{
.type = "pipe",
.handler = cpio_mkpipe_line,
},
{
.type = "sock",
.handler = cpio_mksock_line,
},
{
.type = NULL,
.handler = NULL,
}
};
#define LINE_SIZE (2 * PATH_MAX + 50)
int main(int argc, char *argv[]) {
FILE *cpio_list;
char line[LINE_SIZE];
char *args, *type;
int ec = 0;
int line_nr = 0;
const char *filename;
default_mtime = time(NULL);
while (1) {
int opt = getopt(argc, argv, "t:cho:a:");
char *invalid;
if (opt == -1)
break;
switch (opt) {
case 't':
default_mtime = strtol(optarg, &invalid, 10);
if (!*optarg || *invalid) {
fprintf(stderr, "Invalid timestamp: %s\n",
optarg);
usage(argv[0]);
exit(1);
}
do_file_mtime = true;
break;
case 'c':
do_csum = true;
break;
case 'o':
outfd = open(optarg,
O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC,
0600);
if (outfd < 0) {
fprintf(stderr, "failed to open %s\n", optarg);
usage(argv[0]);
exit(1);
}
break;
case 'a':
dalign = strtoul(optarg, &invalid, 10);
if (!*optarg || *invalid || (dalign & 3)) {
fprintf(stderr, "Invalid data_align: %s\n",
optarg);
usage(argv[0]);
exit(1);
}
break;
case 'h':
case '?':
usage(argv[0]);
exit(opt == 'h' ? 0 : 1);
}
}
/*
* Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
* representation that exceeds 8 chars and breaks the cpio header
* specification. Negative timestamps similarly exceed 8 chars.
*/
if (default_mtime > 0xffffffff || default_mtime < 0) {
fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n");
exit(1);
}
if (argc - optind != 1) {
usage(argv[0]);
exit(1);
}
filename = argv[optind];
if (!strcmp(filename, "-"))
cpio_list = stdin;
else if (!(cpio_list = fopen(filename, "r"))) {
fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
filename, strerror(errno));
usage(argv[0]);
exit(1);
}
while (fgets(line, LINE_SIZE, cpio_list)) {
int type_idx;
size_t slen = strlen(line);
line_nr++;
if ('#' == *line) {
/* comment - skip to next line */
continue;
}
if (!(type = strtok(line, " \t"))) {
fprintf(stderr,
"ERROR: incorrect format, could not locate file type line %d: '%s'\n",
line_nr, line);
ec = -1;
break;
}
if ('\n' == *type) {
/* a blank line */
continue;
}
if (slen == strlen(type)) {
/* must be an empty line */
continue;
}
if (!(args = strtok(NULL, "\n"))) {
fprintf(stderr,
"ERROR: incorrect format, newline required line %d: '%s'\n",
line_nr, line);
ec = -1;
}
for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
int rc;
if (!strcmp(line, file_handler_table[type_idx].type)) {
if ((rc = file_handler_table[type_idx].handler(args))) {
ec = rc;
fprintf(stderr, " line %d\n", line_nr);
}
break;
}
}
if (NULL == file_handler_table[type_idx].type) {
fprintf(stderr, "unknown file type line %d: '%s'\n",
line_nr, line);
}
}
if (ec == 0)
ec = cpio_trailer();
exit(ec);
}