install: Expect EINTR while copying

Both copy_file_range() and read() / write() in our fallback loop can be
interrupted before copying anything at all, in which case it returns -1
and sets errno to EINTR.  If that happens, we should retry, not fail.

While here, drop the size argument from copy() (we always want to copy
the entire file anyway) and add test cases which exercise the metalog
and digest functionality.

PR:		293028
MFC after:	1 week
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D55168
This commit is contained in:
Dag-Erling Smørgrav
2026-02-11 17:24:50 +01:00
parent 7aa30669d6
commit 0fb940fd63
2 changed files with 60 additions and 22 deletions
+38
View File
@@ -512,6 +512,42 @@ set_optional_exec_body()
atf_check test ! -x testfile
}
atf_test_case metalog
metalog_head() {
atf_set "descr" "Test metalog generation"
}
metalog_body() {
atf_check install -M metalog -D dst -m 0755 -d dst
echo ". type=dir mode=0755" >expect
atf_check install -M metalog -D dst -m 0705 -d dst/dir
echo "./dir type=dir mode=0705" >>expect
atf_check -o save:file echo "Hello, world!"
atf_check install -M metalog -D dst -m 0604 file dst/dir
echo "./dir/file type=file mode=0604 size=14" >>expect
atf_check install -M metalog -D dst -lrs dir/file dst/"li nk"
echo "./li\040nk type=link mode=0755 link=dir/file" >>expect
atf_check mtree -f expect -p dst
atf_check -o file:expect cat metalog
}
atf_test_case digest
digest_head() {
atf_set "descr" "Compute digest while copying"
}
digest_body() {
atf_check mkdir src dst
atf_check -e ignore dd if=/dev/random of=src/file bs=1m count=1
for alg in md5 rmd160 sha1 sha256 sha512 ; do
rm -f dst/file digest metalog
atf_check -o save:digest $alg -q src/file
atf_check install -M metalog -D dst -h $alg -m 0644 src/file dst
echo -n "./file type=file mode=0644 size=1048576 $alg=" >expect
cat digest >>expect
atf_check cmp src/file dst/file
atf_check -o file:expect cat metalog
done
}
atf_init_test_cases() {
atf_add_test_case copy_to_empty
atf_add_test_case copy_to_nonexistent
@@ -557,4 +593,6 @@ atf_init_test_cases() {
atf_add_test_case set_owner_group_mode
atf_add_test_case set_owner_group_mode_unpriv
atf_add_test_case set_optional_exec
atf_add_test_case metalog
atf_add_test_case digest
}
+24 -24
View File
@@ -140,7 +140,7 @@ static char *destdir, *digest, *fflags, *metafile, *tags;
static int compare(int, const char *, size_t, int, const char *, size_t,
char **);
static char *copy(int, const char *, int, const char *, off_t);
static char *copy(int, const char *, int, const char *);
static int create_tempfile(const char *, char *, size_t);
static char *quiet_mktemp(char *template);
static char *digest_file(const char *);
@@ -877,7 +877,7 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
}
if (!stripped) {
digestresult = copy(from_fd, from_name, to_fd,
tempfile, from_sb.st_size);
tempfile);
}
}
}
@@ -1175,8 +1175,7 @@ create_tempfile(const char *path, char *temp, size_t tsize)
* copy from one file to another
*/
static char *
copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
off_t size)
copy(int from_fd, const char *from_name, int to_fd, const char *to_name)
{
static char *buf = NULL;
static size_t bufsize;
@@ -1198,8 +1197,8 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
if (digesttype == DIGEST_NONE) {
do {
ret = copy_file_range(from_fd, NULL, to_fd, NULL,
(size_t)size, 0);
} while (ret > 0);
SSIZE_MAX, 0);
} while (ret > 0 || (ret < 0 && errno == EINTR));
if (ret == 0)
goto done;
if (errno != EINVAL) {
@@ -1227,29 +1226,30 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
if (buf == NULL)
err(1, "Not enough memory");
}
while ((nr = read(from_fd, buf, bufsize)) > 0) {
if ((nw = write(to_fd, buf, nr)) != nr) {
serrno = errno;
(void)unlink(to_name);
if (nw >= 0) {
errx(EX_OSERR,
"short write to %s: %jd bytes written, "
"%jd bytes asked to write",
to_name, (uintmax_t)nw,
(uintmax_t)size);
} else {
errno = serrno;
err(EX_OSERR, "%s", to_name);
}
}
digest_update(&ctx, buf, nr);
}
if (nr != 0) {
for (;;) {
if ((nr = read(from_fd, buf, bufsize)) < 0) {
if (errno == EINTR)
continue;
serrno = errno;
(void)unlink(to_name);
errno = serrno;
err(EX_OSERR, "%s", from_name);
}
if (nr <= 0)
break;
digest_update(&ctx, buf, nr);
while (nr > 0) {
if ((nw = write(to_fd, buf, nr)) < 0) {
if (errno == EINTR)
continue;
serrno = errno;
(void)unlink(to_name);
errno = serrno;
err(EX_OSERR, "%s", to_name);
}
nr -= nw;
}
}
#ifndef BOOTSTRAP_XINSTALL
done:
#endif