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
This commit is contained in:
parent
d8dc46f6e9
commit
1ea95ba231
@ -74,6 +74,26 @@ __FBSDID("$FreeBSD$");
|
|||||||
*/
|
*/
|
||||||
#define BUFSIZE_SMALL (MAXPHYS)
|
#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
|
int
|
||||||
copy_file(const FTSENT *entp, int dne)
|
copy_file(const FTSENT *entp, int dne)
|
||||||
{
|
{
|
||||||
@ -88,6 +108,7 @@ copy_file(const FTSENT *entp, int dne)
|
|||||||
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
|
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
|
||||||
char *p;
|
char *p;
|
||||||
#endif
|
#endif
|
||||||
|
int use_copy_file_range = 1;
|
||||||
|
|
||||||
from_fd = to_fd = -1;
|
from_fd = to_fd = -1;
|
||||||
if (!lflag && !sflag &&
|
if (!lflag && !sflag &&
|
||||||
@ -212,9 +233,19 @@ copy_file(const FTSENT *entp, int dne)
|
|||||||
err(1, "Not enough memory");
|
err(1, "Not enough memory");
|
||||||
}
|
}
|
||||||
wtotal = 0;
|
wtotal = 0;
|
||||||
while ((rcount = copy_file_range(from_fd, NULL,
|
do {
|
||||||
to_fd, NULL, bufsize, 0)) > 0)
|
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;
|
wtotal += rcount;
|
||||||
if (info) {
|
if (info) {
|
||||||
info = 0;
|
info = 0;
|
||||||
@ -223,7 +254,7 @@ copy_file(const FTSENT *entp, int dne)
|
|||||||
entp->fts_path, to.p_path,
|
entp->fts_path, to.p_path,
|
||||||
cp_pct(wtotal, fs->st_size));
|
cp_pct(wtotal, fs->st_size));
|
||||||
}
|
}
|
||||||
}
|
} while (rcount > 0);
|
||||||
if (rcount < 0) {
|
if (rcount < 0) {
|
||||||
warn("%s", entp->fts_path);
|
warn("%s", entp->fts_path);
|
||||||
rval = 1;
|
rval = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user