fix: no sudo advice for a read-only mount

EROFS was advised like a permission refusal, and sudo fails the same way on a read-only mount. The advice now reads the mount flag and says to check and remount the card or drive.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 09:54:55 +02:00
1 parent 443a4e56c9
commit 10c94369df
5 files changed
+46 -3

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@
$ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "1eb1aed96cab57bf3bc4ef2918bf1ef40cafcb1de157af2e5fcd5a23ae69a443"
$defaultInstallSha256 = "86c4e8b2a67b5721020dd3629212c741a67a3b3180e8f62fb61bba333303a5b6"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+23 -1
View File
@@ -3186,6 +3186,20 @@ class Invocation:
_INVOCATION = Invocation()
def _on_read_only_mount(path: Path) -> bool:
"""Whether the file system holding path, or the folder it would go in,
is mounted read-only (a card Linux remounts read-only after an error)."""
if not hasattr(os, "statvfs"):
return False
probe = path
while not probe.exists() and probe.parent != probe:
probe = probe.parent
try:
return bool(os.statvfs(probe).f_flag & os.ST_RDONLY)
except OSError:
return False
def _write_advice(path: Path, assumed: bool, plat: str = "") -> str:
"""What to do about a folder this run could not write.
@@ -3193,8 +3207,16 @@ def _write_advice(path: Path, assumed: bool, plat: str = "") -> str:
machine it would create that folder on the wrong disk: a platform named
but not found here is a card or share mounted elsewhere, named by --dest.
Windows has no sudo, and Termux reaches shared storage once
termux-setup-storage has run.
termux-setup-storage has run. A read-only mount refuses root as well,
so sudo is never offered for it.
"""
if _on_read_only_mount(path):
again = f"\n {_INVOCATION.command(plat, path)}" if plat else ""
return (f" {path} is on a file system mounted read-only, which no user can "
"write, sudo included. Mount it read-write again (a card or drive "
"the system switched to read-only after an error needs a check "
"first: fsck, or chkdsk on Windows), then run the same command "
f"again.{again}")
if assumed:
return (f" {path} is where the platform keeps its BIOS on its own system, "
"and it was not found here. If this machine prepares its card or "
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER=""
TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="1eb1aed96cab57bf3bc4ef2918bf1ef40cafcb1de157af2e5fcd5a23ae69a443"
DEFAULT_INSTALL_SHA256="86c4e8b2a67b5721020dd3629212c741a67a3b3180e8f62fb61bba333303a5b6"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+17
View File
@@ -2425,6 +2425,23 @@ class SudoAdviceNamesTheFolder(unittest.TestCase):
source = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
self.assertIn("platform_of={path: plat for plat, path in platforms}", source)
@unittest.skipUnless(hasattr(os, "statvfs"), "statvfs reads mount flags")
def test_a_read_only_mount_is_not_sent_to_sudo(self):
"""A card Linux mounted read-only after a dirty unplug was answered
with 'run it again with sudo', which fails the same way."""
real = os.statvfs(".")
flags = SimpleNamespace(f_flag=real.f_flag | os.ST_RDONLY)
for plat in ("retroarch", ""):
with self.subTest(platform=plat), \
unittest.mock.patch.object(install.os, "statvfs", return_value=flags):
advice = install._write_advice(Path("/media/card/bios"), False, plat)
self.assertIn("mounted read-only", advice)
self.assertNotIn("Run it again with sudo", advice)
self.assertNotIn("sudo python3", advice)
writable = SimpleNamespace(f_flag=real.f_flag & ~os.ST_RDONLY)
with unittest.mock.patch.object(install.os, "statvfs", return_value=writable):
self.assertIn("sudo", install._write_advice(Path("/media/card/bios"), False, "retroarch"))
class CaseVariants(unittest.TestCase):
+4
View File
@@ -272,6 +272,10 @@ The installer needs write access to the target directory.
- On Linux/macOS: check directory ownership (`ls -la`) and run with appropriate
permissions. Avoid running as root unless the target directory requires it.
- On Windows: run PowerShell as Administrator if installing to a protected directory.
- A file system mounted read-only refuses root too, so the installer does not
suggest sudo for it. Linux remounts a card or drive read-only after an error
(an unclean removal, for one): check it (`fsck`, or `chkdsk` on Windows) and
mount it read-write again.
A file already in the BIOS folder with the read-only attribute is replaced
like any other: on Windows the installer clears the attribute before putting