ci: harden the fusermount3 repair (#10485)

* ci: move the fusermount3 repair into a composite action

Three copies of the same block were already drifting apart, and the
target comes from PATH: only ever add setuid root to a root-owned,
non-symlink binary under the system bin paths, and say why otherwise.

* test: say that the process exited in the wait errors

"process exit status 1 before ... accepted connections" is missing its
verb. Also mark the SIGTERM return discarded - it fails with
os.ErrProcessDone exactly when the select below already handles it.

* ci: prefer the distro fusermount3 over escalating a shadow copy

The shadowing /usr/local/bin/fusermount3 is not root-owned either, so
setting its setuid bit would have handed root to a binary the runner
user owns - the repair now symlinks the distro one earlier in PATH and
touches nothing, keeping the in-place chmod for a root-owned binary with
no distro alternative. A setuid bit only grants root when root owns the
file, so accept an existing one only then.

* ci: run the FUSE workflows when the shared action changes

Their paths filters listed each workflow file but not the composite
action all three now call.
This commit is contained in:
Chris Lu
2026-07-29 14:02:33 -07:00
committed by GitHub
parent c4798979d8
commit a4692005e9
5 changed files with 87 additions and 40 deletions
+6 -4
View File
@@ -219,7 +219,7 @@ func (p *managedProcess) exited() error {
if p.err != nil {
return p.err
}
return fmt.Errorf("exited with status 0")
return fmt.Errorf("exit status 0")
default:
return nil
}
@@ -227,7 +227,9 @@ func (p *managedProcess) exited() error {
// stop asks the process to terminate and waits for it to go away.
func (p *managedProcess) stop() {
p.cmd.Process.Signal(syscall.SIGTERM)
// Signal fails with os.ErrProcessDone when the child is already gone, which
// is exactly the case the select below handles.
_ = p.cmd.Process.Signal(syscall.SIGTERM)
select {
case <-p.done:
case <-time.After(10 * time.Second):
@@ -379,7 +381,7 @@ func (f *FuseTestFramework) waitForService(proc *managedProcess, addr string, ti
return nil
}
if exitErr := proc.exited(); exitErr != nil {
return fmt.Errorf("process %v before %s accepted connections", exitErr, addr)
return fmt.Errorf("process exited (%v) before %s accepted connections", exitErr, addr)
}
time.Sleep(100 * time.Millisecond)
}
@@ -406,7 +408,7 @@ func (f *FuseTestFramework) waitForMount(timeout time.Duration) error {
// A mount that cannot mount at all (no /dev/fuse, fusermount not setuid)
// dies within a second; reporting that beats waiting out the timeout.
if exitErr := f.mountProcess.exited(); exitErr != nil {
return fmt.Errorf("mount process %v", exitErr)
return fmt.Errorf("mount process exited (%v)", exitErr)
}
time.Sleep(100 * time.Millisecond)
}