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:
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