fix(vim.fs): fs.dir() may return nil "type" on some filesystems #39749

Problem:
Currently, only some filesystems (Btrfs, ext2, ext3, ext4) have full
support of accessing the `dirent` entry-type. On other filesystems,
`uv.fs_scandir_next` may return `nil` for an existing but unsupported
entry-type.

This means consumers (such as `fs.dir()`), cannot know if `nil` means
"non-existent" or "unsupported".

Solution:
Fall back to `uv.fs_lstat` when `etype` is `nil`; return "unknown" if it
fails.
This commit is contained in:
abdulahmoda
2026-05-29 14:24:22 -04:00
committed by GitHub
parent ac352a6df6
commit 4b5f026ac9
2 changed files with 60 additions and 2 deletions

View File

@@ -147,6 +147,29 @@ function M.joinpath(...)
return (path:gsub(iswin and '[/\\][/\\]*' or '//+', '/'))
end
--- Wrapper around `uv.fs_scandir_next()` that ensures a file type is returned.
---
--- @param fs uv.uv_fs_t
--- @param path string
--- @return string?
--- @return string?
local function fs_scandir_next(fs, path)
-- use uv.fs_lstat instead of uv.fs_stat to avoid descending into a symlink entry as a directory/file
local name, etype = uv.fs_scandir_next(fs)
if not name then
return
end
if etype == nil then
local stat = vim.uv.fs_lstat(M.joinpath(path, name))
-- Workaround #39612 https://github.com/luvit/luv/issues/660
etype = stat and stat.type or 'unknown'
end
return name, etype
end
--- @class vim.fs.dir.Opts
--- @inlinedoc
---
@@ -190,7 +213,7 @@ function M.dir(path, opts)
if not fs then
return
end
return uv.fs_scandir_next(fs)
return fs_scandir_next(fs, path)
end
end
@@ -203,7 +226,7 @@ function M.dir(path, opts)
local dir = level == 1 and dir0 or M.joinpath(path, dir0)
local fs = uv.fs_scandir(dir)
while fs do
local name, t = uv.fs_scandir_next(fs)
local name, t = fs_scandir_next(fs, dir)
if not name then
break
end