logo

bootstrap-initrd

Linux initrd to bootstrap from a small binary seed git clone https://hacktivis.me/git/make-initrd.git
commit: e71d9b8795f325678cec28b0c29afe0aa7a63adc
parent d8e0f335ee317f6793305c1d175319f4976708b3
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Tue, 30 Apr 2024 01:40:04 +0200

cp-stub.c: new

Diffstat:

Acp-stub.c130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minit.sh4+---
Mmake-root.sh2+-
3 files changed, 132 insertions(+), 4 deletions(-)

diff --git a/cp-stub.c b/cp-stub.c @@ -0,0 +1,130 @@ +// utils-std: Collection of commonly available Unix tools +// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#define _GNU_SOURCE // copy_file_range + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> // linkat, AT_SYMLINK_FOLLOW +#include <limits.h> // PATH_MAX +#include <stdbool.h> +#include <stdio.h> // fprintf +#include <string.h> // strerror +#include <sys/stat.h> +#include <unistd.h> // getopt, copy_file_range + +static int +do_copy(char *src, char *dest) +{ + assert(errno == 0); + int src_fd = open(src, O_RDONLY); + if(src_fd < 0) + { + fprintf(stderr, "cp: Failed opening file '%s' for reading: %s\n", src, strerror(errno)); + return -1; + } + + struct stat src_stat; + if(fstat(src_fd, &src_stat) < 0) + { + fprintf(stderr, "cp: Failed getting status for target '%s': %s\n", dest, strerror(errno)); + return -1; + } + + int dest_fd = open(dest, O_CREAT | O_WRONLY | O_TRUNC, src_stat.st_mode & 0777); + if(dest_fd < 0) + { + fprintf(stderr, "cp: Failed create-opening file '%s' for writing: %s\n", dest, strerror(errno)); + return -1; + } + + off_t len = src_stat.st_size; + off_t ret = -1; + do + { + ret = copy_file_range(src_fd, NULL, dest_fd, NULL, len, 0); + if(ret == -1) + { + fprintf(stderr, + "cp: Error: Failed copying data from '%s' to '%s': %s\n", + src, + dest, + strerror(errno)); + return -1; + } + len -= ret; + } while(len > 0 && ret > 0); + + if(close(src_fd) < 0) + { + fprintf(stderr, "cp: Error closing '%s'\n", src); + return -1; + } + + if(close(dest_fd) < 0) + { + fprintf(stderr, "cp: Error closing '%s'\n", dest); + return -1; + } + + assert(errno == 0); + + return 0; +} + +static void +usage() +{ + fprintf(stderr, "Usage: cp source... destination\n"); +} + +int +main(int argc, char *argv[]) +{ + int c = -1; + while((c = getopt(argc, argv, ":")) != -1) + { + switch(c) + { + case '?': + fprintf(stderr, "cp: Unknown option '-%c'\n", optopt); + usage(); + break; + } + } + + assert(errno == 0); + + argc -= optind; + argv += optind; + + if(argc == 2) + { + if(do_copy(argv[0], argv[1]) < 0) return 1; + } + else + { + char *dest = argv[argc - 1]; + char target[PATH_MAX] = ""; + + for(int i = 0; i < argc - 1; i++) + { + char *src = argv[i]; + + char *src_sep = strrchr(src, '/'); + char *src_basename = src_sep != NULL ? src_sep + 1 : src; + + if(snprintf(target, PATH_MAX, "%s/%s", dest, src_basename) < 0) + { + fprintf(stderr, "cp: Failed joining destination '%s' and target '%s'\n", dest, src); + return 1; + } + + if(do_copy(src, target) < 0) return 1; + } + } + + return 0; +} diff --git a/init.sh b/init.sh @@ -26,15 +26,13 @@ build_awk() { } build_stubs() { - for i in ls mv grep + for i in ls mv grep cp do $CC $CFLAGS -o "/bin/$i" "/${i}-stub.c" || die "Failed compiling $i stub" done ln -s grep /bin/egrep || die ln -s grep /bin/fgrep || die ln -s /utils-std-*/cmd/cat /bin/sort || die - # Yeah, I know, copying != linking - ln -s /utils-std-*/cmd/ln /bin/cp || die } build_bmake() { diff --git a/make-root.sh b/make-root.sh @@ -69,7 +69,7 @@ cp "${WORKDIR}/init.c" ./init || die "copying init" sed -i '1i#!/bin/tcc -run' ./init || die "failed adding tcc shebang to init" chmod 755 init || die "init chmod" -for i in init.sh ls-stub.c mv-stub.c grep-stub.c +for i in init.sh ls-stub.c mv-stub.c grep-stub.c cp-stub.c do cp -p "${WORKDIR}/$i" ./ || die "failed copying $i" done