fix: detect mounts via /proc/self/mountinfo so stale FUSE works
Update PKGBUILD version / update-pkgver (push) Successful in 5s
Update PKGBUILD version / update-pkgver (push) Successful in 5s
mountinfo.Mounted lstats the path. When the sshfs link dies, every stat on the mountpoint returns EIO, so -l filtered the dead mount out, mount failed in MkdirAll, and -u failed before fusermount. Switch detection to mountinfo.GetMounts (no stat) and add a fusermount -uz fallback so a stale mount can actually be torn down.
This commit is contained in:
@@ -167,9 +167,9 @@ func main() {
|
||||
fmt.Println("Mount: ", mount)
|
||||
}
|
||||
|
||||
chkmount, chkmount_err := mountinfo.Mounted(mount)
|
||||
chkmount, chkmount_err := is_mounted_at(mount)
|
||||
if chkmount_err != nil {
|
||||
fmt.Fprintf(os.Stderr, "mountinfo.Mounted() failed with %s\n", chkmount_err)
|
||||
fmt.Fprintf(os.Stderr, "is_mounted_at() failed with %s\n", chkmount_err)
|
||||
os.Exit(5)
|
||||
}
|
||||
if !chkmount {
|
||||
@@ -258,12 +258,28 @@ func verify_mount_dir(name string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// If the path is already a mountpoint (possibly stale), skip MkdirAll —
|
||||
// it would stat the target, which fails with EIO on stale FUSE mounts.
|
||||
if ok, _ := is_mounted_at(mount); ok {
|
||||
return mount, nil
|
||||
}
|
||||
if err := os.MkdirAll(mount, 0700); err != nil {
|
||||
return "", fmt.Errorf("create mount dir %q: %w", mount, err)
|
||||
}
|
||||
return mount, nil
|
||||
}
|
||||
|
||||
// is_mounted_at reports whether path is a mountpoint per /proc/self/mountinfo.
|
||||
// Unlike mountinfo.Mounted, this does not stat the path, so stale FUSE mounts
|
||||
// (whose targets return EIO on stat) are still detected.
|
||||
func is_mounted_at(path string) (bool, error) {
|
||||
mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(path))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(mounts) > 0, nil
|
||||
}
|
||||
|
||||
func list_mounts(out io.Writer) error {
|
||||
base, err := mount_base(false)
|
||||
if err != nil {
|
||||
@@ -272,24 +288,28 @@ func list_mounts(out io.Writer) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
entries, err := os.ReadDir(base)
|
||||
mounts, err := mountinfo.GetMounts(mountinfo.PrefixFilter(base))
|
||||
if err != nil {
|
||||
return fmt.Errorf("read base %q: %w", base, err)
|
||||
return fmt.Errorf("read mountinfo: %w", err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
prefix := base + string(os.PathSeparator)
|
||||
for _, m := range mounts {
|
||||
rel := strings.TrimPrefix(m.Mountpoint, prefix)
|
||||
if rel == "" || strings.Contains(rel, string(os.PathSeparator)) {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(base, entry.Name())
|
||||
ok, merr := mountinfo.Mounted(path)
|
||||
if merr != nil || !ok {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintln(out, entry.Name())
|
||||
fmt.Fprintln(out, rel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func run_fusermount(flag, mount string) error {
|
||||
cmd := exec.Command("fusermount", flag, mount)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func unmount_sshfs(alias string) error {
|
||||
if err := validate_ssh_field("alias", alias, rxHostUser); err != nil {
|
||||
return err
|
||||
@@ -305,18 +325,19 @@ func unmount_sshfs(alias string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := mountinfo.Mounted(mount)
|
||||
ok, err := is_mounted_at(mount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mountinfo.Mounted(%q): %w", mount, err)
|
||||
return fmt.Errorf("check mount %q: %w", mount, err)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("not mounted: %s", alias)
|
||||
}
|
||||
cmd := exec.Command("fusermount", "-u", mount)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("fusermount -u %q: %w", mount, err)
|
||||
if err := run_fusermount("-u", mount); err != nil {
|
||||
// stale FUSE mounts often need lazy unmount; -u fails with
|
||||
// "Transport endpoint is not connected" on those.
|
||||
if err2 := run_fusermount("-uz", mount); err2 != nil {
|
||||
return fmt.Errorf("fusermount -u %q: %w (lazy retry: %v)", mount, err, err2)
|
||||
}
|
||||
}
|
||||
if err := os.Remove(mount); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: remove empty mount dir %q: %v\n", mount, err)
|
||||
|
||||
Reference in New Issue
Block a user