util/nvmutil: partially mitigate fd offset race

our fallback pwrite/pread behaviour still does not
properly replicate the safety of real pwrite/pread

i intend to put this i/o code into a library for use
in other programs; nvmutil is single-threaded so
this change is largely redundant (but can't hurt)

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2026-03-14 22:11:32 +00:00
parent 291c8a9a8b
commit 6d338e1fa4
+42 -12
View File
@@ -1951,6 +1951,7 @@ prw(int fd, void *mem, size_t nrw,
int saved_errno; int saved_errno;
int positional_rw; int positional_rw;
struct stat st; struct stat st;
off_t verified;
if (mem == NULL) if (mem == NULL)
goto err_prw; goto err_prw;
@@ -2014,22 +2015,51 @@ real_pread_pwrite:
goto real_pread_pwrite; goto real_pread_pwrite;
#else #else
if ((off_orig = lseek_loop(fd, (off_t)0, SEEK_CUR, if ((off_orig = lseek_loop(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr)) == (off_t)-1) loop_eagain, loop_eintr)) == (off_t)-1) {
r = -1; r = -1;
else if (lseek_loop(fd, off, SEEK_SET, } else if (lseek_loop(fd, off, SEEK_SET,
loop_eagain, loop_eintr) == (off_t)-1) loop_eagain, loop_eintr) == (off_t)-1) {
r = -1; r = -1;
} else {
verified = lseek_loop(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr);
do { /*
if (rw_type == IO_PREAD) * Partial thread-safety: detect
r = read(fd, mem, nrw); * if the offset changed to what
else if (rw_type == IO_PWRITE) * we previously got. If it did,
r = write(fd, mem, nrw); * then another thread may have
* changed it.
*
* This is no substitute for real
* pread/pwrite, which would be
* fully atomic at kernel-level
* and do not use file offsets.
*
* TODO: Add a toggle to make it
* recover instead, reset
* to known offset, and
* carry on operations.
*
* Failure is the better option
* here, since recovery would
* mask hidden bugs in code.
*/
if (off != verified)
goto err_prw;
r = rw_over_nrw(r, nrw); do {
} while (r == -1 && if (rw_type == IO_PREAD)
(errno == try_err(loop_eintr, EINTR) r = read(fd, mem, nrw);
|| errno == try_err(loop_eagain, EAGAIN))); else if (rw_type == IO_PWRITE)
r = write(fd, mem, nrw);
r = rw_over_nrw(r, nrw);
} while (r == -1 &&
(errno == try_err(loop_eintr, EINTR)
|| errno == try_err(loop_eagain, EAGAIN)));
}
saved_errno = errno; saved_errno = errno;
off_last = lseek_loop(fd, off_orig, SEEK_SET, off_last = lseek_loop(fd, off_orig, SEEK_SET,