logo

bootstrap-initrd

Linux initrd to bootstrap from a small binary seed git clone https://hacktivis.me/git/make-initrd.git
commit: 03fa9407aebeb8c60b6b50a1c4334a510961f536
parent d0282804baecc8898a0313cf9e228e231d095b83
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Fri,  3 May 2024 02:17:27 +0200

cp-stub.c: Make much closer to POSIX

Diffstat:

Mcp-stub.c190++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 149 insertions(+), 41 deletions(-)

diff --git a/cp-stub.c b/cp-stub.c @@ -15,86 +15,177 @@ #include <sys/stat.h> #include <unistd.h> // getopt, copy_file_range +bool preserve_metadata = false, opt_R = false; +bool follow_src_symlinks = false; + +mode_t mode = 00755; +char *argv0 = "cp"; + +// Copied from utils-std/lib/path.c remove on de-stubbing +static char * +static_basename(char *path) +{ + char *sep = strrchr(path, '/'); + + return (sep == NULL) ? path : sep + 1; +} + static int -do_copy(char *src, char *dest, bool is_dir) +do_copy(char *src, char *dest, bool multi_src) { assert(errno == 0); - int src_fd = open(src, O_RDONLY); - if(src_fd < 0) + struct stat src_stat; + if(fstatat(AT_FDCWD, src, &src_stat, follow_src_symlinks ? 0 : AT_SYMLINK_NOFOLLOW) < 0) { - fprintf(stderr, "cp: Failed opening source '%s' for reading: %s\n", src, strerror(errno)); + fprintf( + stderr, "%s: Failed getting status for source '%s': %s\n", argv0, dest, strerror(errno)); return -1; } - struct stat src_stat; - if(fstat(src_fd, &src_stat) < 0) + int src_open_flags = O_RDONLY; + + if(!follow_src_symlinks) src_open_flags |= O_NOFOLLOW; + + int src_fd = open(src, src_open_flags); + if(src_fd < 0) { - fprintf(stderr, "cp: Failed getting status for source '%s': %s\n", dest, strerror(errno)); + fprintf(stderr, "%s: Failed opening file '%s' for reading: %s\n", argv0, src, strerror(errno)); return -1; } - if(!is_dir) + struct stat dest_stat; + if(stat(dest, &dest_stat) < 0) { - struct stat dest_stat; - if(stat(dest, &dest_stat) < 0) + if(errno == ENOENT) + { + errno = 0; + } + else { - fprintf(stderr, "cp: Failed getting status for target '%s': %s\n", dest, strerror(errno)); + fprintf(stderr, + "%s: Failed getting status for destination '%s': %s\n", + argv0, + dest, + strerror(errno)); return -1; } - if(S_ISDIR(dest_stat.st_mode)) is_dir = true; } char *dest_path = dest; - if(is_dir) + if(multi_src || S_ISDIR(dest_stat.st_mode)) { char target[PATH_MAX] = ""; - - char *src_sep = strrchr(src, '/'); - char *src_basename = src_sep != NULL ? src_sep + 1 : src; + char *src_basename = static_basename(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; + fprintf(stderr, + "%s: Failed joining destination '%s' and source '%s'\n", + argv0, + dest, + src_basename); + return -1; } dest_path = target; } - int dest_fd = open(dest_path, O_CREAT | O_WRONLY | O_TRUNC, src_stat.st_mode & 0777); - if(dest_fd < 0) + if(S_ISDIR(src_stat.st_mode)) { - fprintf(stderr, "cp: Failed create-opening destination '%s' for writing: %s\n", dest_path, strerror(errno)); + if(!opt_R) + { + fprintf(stderr, "%s: Option -R not specified and source file '%s' is a directory\n", argv0, src); + return -1; + } + + if(mkdir(dest, mode|S_IRWXU) != 0) + { + fprintf(stderr, "%s: Failed creating target directory '%s': %s\n", argv0, dest, strerror(errno)); + return -1; + } + + fprintf(stderr, "%s: FIXME: Copy the files inside '%s/'\n", argv0, dest); return -1; } - - off_t len = src_stat.st_size; - off_t ret = -1; - do + else if(S_ISREG(src_stat.st_mode)) { - ret = copy_file_range(src_fd, NULL, dest_fd, NULL, len, 0); - if(ret == -1) + // FIXME: Handle writing into block devices + + // FIXME: Handle -i (consent) + int dest_fd = open(dest_path, O_CREAT | O_WRONLY | O_TRUNC, mode); + if(dest_fd < 0) { + // FIXME: Handle -f (unlink on creation-opening fail) fprintf(stderr, - "cp: Error: Failed copying data from '%s' to '%s': %s\n", - src, + "%s: Failed create-opening file '%s' for writing: %s\n", + argv0, dest_path, 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; - } + 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, + "%s: Error: Failed copying data from '%s' to '%s': %s\n", + argv0, + src, + dest_path, + strerror(errno)); + return -1; + } + len -= ret; + } while(len > 0 && ret > 0); + + assert(errno == 0); + + if(preserve_metadata) + { + struct timespec src_times[2] = {src_stat.st_atim, src_stat.st_mtim}; + if(futimens(dest_fd, src_times) != 0) + { + fprintf(stderr, + "%s: Error while setting access/modification times on '%s': %s\n", + argv0, + dest_path, + strerror(errno)); + return -1; + } + + if(fchown(dest_fd, src_stat.st_uid, src_stat.st_gid) < 0) + { + fprintf(stderr, + "%s: Error: Failed setting ownership on file '%s': %s\n", + argv0, + dest_path, + strerror(errno)); + return -1; + } + } + + assert(errno == 0); + + if(close(src_fd) < 0) + { + fprintf(stderr, "%s: Error closing '%s'\n", argv0, src); + return -1; + } - if(close(dest_fd) < 0) + if(close(dest_fd) < 0) + { + fprintf(stderr, "%s: Error closing '%s'\n", argv0, dest_path); + return -1; + } + } + else { - fprintf(stderr, "cp: Error closing '%s'\n", dest); + fprintf(stderr, "%s: Unhandled file '%s' of type-mode %o\n", argv0, dest, src_stat.st_mode); return -1; } @@ -106,19 +197,36 @@ do_copy(char *src, char *dest, bool is_dir) static void usage() { - fprintf(stderr, "Usage: cp source... destination\n"); + fprintf(stderr, "Usage: cp [-p] [-rR [-H|-L|-P]] source... destination\n"); } int main(int argc, char *argv[]) { int c = -1; - while((c = getopt(argc, argv, ":")) != -1) + while((c = getopt(argc, argv, ":pRHLP")) != -1) { switch(c) { + case 'p': + preserve_metadata = true; + break; + case 'R': + case 'r': + opt_R = true; + break; + case 'H': + // FIXME: Only first level aka arguments, not traversal + follow_src_symlinks = true; + break; + case 'L': + follow_src_symlinks = true; + break; + case 'P': + follow_src_symlinks = false; + break; case '?': - fprintf(stderr, "cp: Unknown option '-%c'\n", optopt); + fprintf(stderr, "install: Unknown option '-%c'\n", optopt); usage(); break; }