nextboot: Smarter warning about deleting nextboot

Read only filesystems always error when trying to remove something
with EROFS. However, that's true even if the file isn't there. The
code assumed it would always get a ENOENT if the file wasn't there,
but produced a gross message on read only systems. This message was
harmless, but annoying. Instead, stat the file first and return if
it's already not there.

Some readings of POSIX require that the ENOENT error take precidence
over the EROFS error. Linux made this change years ago, and we should
too. POSIX.1-2024 isn't explicit, but does say for unlink() does say
"[EROFS] The directory entry to be unlinked is part of a read-only file
system" and "[ENOENT] A component of path does not name an existing
file" implying that EROFS should only be returned for an existing
file. FreeBSD doesn't implement this, so this workaround is
necessary. Ideally, we'd fix this in the kernel in the future.

Sponsored by: Netflix
Discussed with: jrtc23
Differential Revision:	https://reviews.freebsd.org/D48425
This commit is contained in:
Warner Losh 2025-01-14 10:32:01 -07:00
parent 618d1621c2
commit bec5a3c046

View file

@ -308,7 +308,22 @@ main(int argc, char *argv[])
errx(1, "-r and -k cannot be used together, there is no next kernel");
if (Dflag) {
if (unlink(PATH_NEXTBOOT) != 0 && errno != ENOENT)
struct stat sb;
/*
* Break the rule about stat then doing
* something. When we're booting, there's no
* race. When we're a read-only root, though, the
* read-only error takes priority over the file not
* there error in unlink. So stat it first and exit
* with success if it isn't there. Otherwise, let
* unlink sort error reporting. POSIX-1.2024 suggests
* ENOENT should be preferred to EROFS for unlink,
* but FreeBSD historically has preferred EROFS.
*/
if (stat(PATH_NEXTBOOT, &sb) != 0 && errno == ENOENT)
exit(0);
if (unlink(PATH_NEXTBOOT) != 0)
warn("unlink " PATH_NEXTBOOT);
exit(0);
}