commit: ee3a29fbf94556f9ea83d486578797d5b9cbabf7
parent 55ecff146288046783aba44af4f8dbe135ca633b
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Mon, 6 May 2024 03:04:58 +0200
cmd/install: Fallback to manual read-write on EXDEV
Diffstat:
2 files changed, 55 insertions(+), 44 deletions(-)
diff --git a/cmd/install.c b/cmd/install.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
// For copy_file_range
-#define _GNU_SOURCE // musl, glibc
+#define _GNU_SOURCE // musl, glibc
#define _DEFAULT_SOURCE // FreeBSD
#include "../lib/mkdir.h"
@@ -32,6 +32,50 @@ uid_t user = (uid_t)-1;
gid_t group = (gid_t)-1;
char *argv0 = "install";
+#undef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+static int
+manual_file_copy(int fd_in, int fd_out, off_t len, int flags)
+{
+ do
+ {
+ char buf[BUFSIZ];
+
+ ssize_t nread = read(fd_in, buf, MIN(BUFSIZ, len));
+ if(nread < 0) return nread;
+
+ ssize_t nwrite = write(fd_out, buf, (size_t)nread);
+ if(nwrite < 0) return nwrite;
+
+ len -= nwrite;
+ } while(len > 0);
+
+ return 0;
+}
+
+#ifdef HAS_COPY_FILE_RANGE
+static int
+auto_file_copy(int fd_in, int fd_out, off_t len, int flags)
+{
+ off_t ret = -1;
+ do
+ {
+ ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
+ if(ret < 0)
+ {
+ if(errno == EXDEV) return manual_file_copy(fd_in, fd_out, len, flags);
+
+ return ret;
+ }
+ len -= ret;
+ } while(len > 0 && ret > 0);
+
+ return 0;
+}
+#else
+#define auto_file_copy manual_file_copy
+#endif
+
static int
do_install(char *src, char *dest, bool is_dir)
{
@@ -125,49 +169,16 @@ do_install(char *src, char *dest, bool is_dir)
return -1;
}
-#ifdef HAS_COPY_FILE_RANGE
- off_t len = src_stat.st_size;
- off_t ret = -1;
- do
+ if(auto_file_copy(src_fd, dest_fd, src_stat.st_size, 0) < 0)
{
- 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);
-#else
-#undef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
- off_t len = src_stat.st_size;
- do
- {
- char buf[BUFSIZ];
- ssize_t nread = read(src_fd, buf, MIN(BUFSIZ, len));
- if(nread < 0)
- {
- fprintf(stderr, "install: Error reading from '%s': %s\n", src, strerror(errno));
- errno = 0;
- return -1;
- }
-
- ssize_t nwrite = write(dest_fd, buf, (size_t)nread);
- if(nwrite < 0)
- {
- fprintf(stderr, "install: Error writing to '%s': %s\n", dest_path, strerror(errno));
- errno = 0;
- return -1;
- }
- len -= nwrite;
- } while(len > 0);
-#endif
+ fprintf(stderr,
+ "%s: Error: Failed copying data from '%s' to '%s': %s\n",
+ argv0,
+ src,
+ dest_path,
+ strerror(errno));
+ return -1;
+ }
assert(errno == 0);
diff --git a/configure.d/copy_file_range.c b/configure.d/copy_file_range.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
// For copy_file_range
-#define _GNU_SOURCE // musl, glibc
+#define _GNU_SOURCE // musl, glibc
#define _DEFAULT_SOURCE // FreeBSD
#include <stdint.h> // SIZE_MAX