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:
M | cp-stub.c | 190 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
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;
}