Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions lua/sshfs/lib/mount_point.lua
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,14 @@ function MountPoint.cleanup_stale()
if vim.fn.isdirectory(file) == 1 then
-- Only remove if directory is empty AND not actively mounted
if Directory.is_empty(file) and not MountPoint.is_active(file) then
MountPoint.unmount(file)
local success = pcall(vim.fn.delete, file, "d")
if success then removed_count = removed_count + 1 end
-- Plain rmdir first; only a mount the table did not report needs unmounting.
local ok, result = pcall(vim.fn.delete, file, "d")
if not (ok and result == 0) then
MountPoint.unmount(file)
pcall(vim.fn.delete, file, "d")
end
-- unmount() removes the directory itself, so count what is gone, not what returned 0.
if vim.fn.isdirectory(file) == 0 then removed_count = removed_count + 1 end
end
end
end
Expand Down
67 changes: 67 additions & 0 deletions tests/cleanup_stale_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
-- tests/cleanup_stale_spec.lua
-- Startup sweep of leftover mount directories
--
-- setup() runs this, so it must not shell out through the unmount escalation
-- for a directory that is merely empty.

--- Build a base dir holding one empty directory, and load MountPoint against it
--- @return table MountPoint, string base_dir, table unmount_attempts
local function sweep_with(opts)
stub.reload()
local base_dir = vim.fn.tempname()
vim.fn.mkdir(base_dir .. "/leftover", "p")
require("sshfs.config").setup({ mounts = { base_dir = base_dir } })

stub.executable({ fusermount = true, umount = true })

local attempts = {}
stub.set("system", function(cmd)
table.insert(attempts, table.concat(cmd, " "))
return {
wait = function()
return { code = 0, stdout = "", stderr = "" }
end,
}
end)
-- An empty mount table means nothing is active.
stub.set("fn.system", function()
return ""
end)

if opts and opts.delete_fails then
local real_delete = vim.fn.delete
local calls = 0
stub.set("fn.delete", function(path, flags)
calls = calls + 1
if calls == 1 then return -1 end
return real_delete(path, flags)
end)
end

return require("sshfs.lib.mount_point"), base_dir, attempts
end

describe("MountPoint.cleanup_stale", function()
it("removes an empty leftover without spawning an unmount", function()
local MountPoint, base_dir, attempts = sweep_with()

local removed = MountPoint.cleanup_stale()
stub.restore_all()

expect.eq(removed, 1)
expect.eq(#attempts, 0, "an empty directory was never mounted")
expect.eq(vim.fn.isdirectory(base_dir .. "/leftover"), 0)
vim.fn.delete(base_dir, "rf")
end)

it("falls back to unmounting when the directory will not go away", function()
local MountPoint, base_dir, attempts = sweep_with({ delete_fails = true })

local removed = MountPoint.cleanup_stale()
stub.restore_all()

expect.eq(removed, 1, "the retry after unmounting must count")
expect.truthy(#attempts > 0, "a directory that resists rmdir may still be mounted")
vim.fn.delete(base_dir, "rf")
end)
end)
Loading