summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2020-09-11 20:49:36 +0000
committerAlan Somers <asomers@FreeBSD.org>2020-09-11 20:49:36 +0000
commit1ea95ba2317b2fe482da0ea1a3d132643a0705b8 (patch)
treefcd9ec0a7e00bdb1dfa2183ec94133bd8787b61e /bin
parentd8dc46f6e9d32a811865b4c5986fc7146a4bd653 (diff)
downloadsrc-test2-1ea95ba2317b2fe482da0ea1a3d132643a0705b8.tar.gz
src-test2-1ea95ba2317b2fe482da0ea1a3d132643a0705b8.zip
cp: fall back to read/write if copy_file_range fails
Even though copy_file_range has a file-system agnostic version, it still fails on devfs (perhaps because the file descriptor is non-seekable?) In that case, fallback to old-fashioned read/write. Fixes "cp /dev/null /tmp/null" PR: 249248 Reported by: Michael Butler Reviewed by: mjg MFC-With: 365549 Differential Revision: https://reviews.freebsd.org/D26395
Notes
Notes: svn path=/head/; revision=365643
Diffstat (limited to 'bin')
-rw-r--r--bin/cp/utils.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/bin/cp/utils.c b/bin/cp/utils.c
index cca12202a70a..1a3b5502145a 100644
--- a/bin/cp/utils.c
+++ b/bin/cp/utils.c
@@ -74,6 +74,26 @@ __FBSDID("$FreeBSD$");
*/
#define BUFSIZE_SMALL (MAXPHYS)
+static int
+copy_fallback(int from_fd, int to_fd, char *buf, size_t bufsize)
+{
+ int rcount;
+ ssize_t wresid, wcount = 0;
+ char *bufp;
+
+ rcount = read(from_fd, buf, bufsize);
+ if (rcount <= 0)
+ return (rcount);
+ for (bufp = buf, wresid = rcount; ; bufp += wcount, wresid -= wcount) {
+ wcount = write(to_fd, bufp, wresid);
+ if (wcount <= 0)
+ break;
+ if (wcount >= (ssize_t)wresid)
+ break;
+ }
+ return (wcount < 0 ? wcount : rcount);
+}
+
int
copy_file(const FTSENT *entp, int dne)
{
@@ -88,6 +108,7 @@ copy_file(const FTSENT *entp, int dne)
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
char *p;
#endif
+ int use_copy_file_range = 1;
from_fd = to_fd = -1;
if (!lflag && !sflag &&
@@ -212,9 +233,19 @@ copy_file(const FTSENT *entp, int dne)
err(1, "Not enough memory");
}
wtotal = 0;
- while ((rcount = copy_file_range(from_fd, NULL,
- to_fd, NULL, bufsize, 0)) > 0)
- {
+ do {
+ if (use_copy_file_range) {
+ rcount = copy_file_range(from_fd, NULL,
+ to_fd, NULL, bufsize, 0);
+ if (rcount < 0 && errno == EINVAL) {
+ /* Prob a non-seekable FD */
+ use_copy_file_range = 0;
+ }
+ }
+ if (!use_copy_file_range) {
+ rcount = copy_fallback(from_fd, to_fd,
+ buf, bufsize);
+ }
wtotal += rcount;
if (info) {
info = 0;
@@ -223,7 +254,7 @@ copy_file(const FTSENT *entp, int dne)
entp->fts_path, to.p_path,
cp_pct(wtotal, fs->st_size));
}
- }
+ } while (rcount > 0);
if (rcount < 0) {
warn("%s", entp->fts_path);
rval = 1;