os.Lstat是判断软链接的唯一可靠方式,因它只读取路径自身inode元数据而不解析链接;需配合os.Readlink获取目标路径并校验有效性,缺一不可。

os.Lstat 是唯一可靠方式
想确认一个路径是不是软链接,os.Stat 完全不可靠——它会直接穿透到目标,返回目标文件的 os.FileInfo,连“这是个链接”都看不出来。os.Lstat 才是正解,它只读取路径自身的 inode 元数据,不解析、不跟随。
典型错误现象:os.Stat("mylink") 返回 /etc/passwd 的大小和权限,你以为在查普通文件,其实已掉进软链接陷阱。
- 必须先调用
os.Lstat(path),再检查fi.Mode() & os.ModeSymlink != 0 - 如果
err != nil,要区分:是路径根本不存在(os.IsNotExist(err)),还是权限不足(os.IsPermission(err)),或是 Windows 上坏链导致的模糊失败 - 硬链接无法用此法识别——它和原文件共享 inode,
os.Lstat返回的Mode()完全一样
os.Readlink 获取目标路径字符串
os.Readlink 的作用很单纯:读出软链接 inode 里存的原始字节串。它不校验目标是否存在,也不做任何路径拼接或清理,传进去什么路径,就原样返回链接内容。
常见误用:os.Readlink("a.ln") 返回 "../bin/sh",你直接拿这个字符串去 os.Stat,结果失败——因为它是相对路径,得先相对于 a.ln 所在目录解析。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 正确做法:用
filepath.Dir("a.ln")拿到父目录,再filepath.Join(dir, linkTarget)构造下一级真实路径 - 别对
os.Readlink的返回值调filepath.Abs——它按当前工作目录算,而软链接的相对路径语义是相对于它自己所在目录 - 坏链(dangling symlink)下
os.Readlink仍成功,但后续os.Stat必然失败;这个中间态必须显式处理,不能跳过
如何安全判断“是软链接且目标有效”
单靠 os.Lstat 只能知道“它是个链接”,单靠 os.Stat 只能知道“目标存在与否”,二者必须组合使用才能闭环。
典型场景:备份工具需要跳过坏链,或 Web 服务需拒绝解析出站路径。这时候不能只查类型,也不能只查存在性。
- 第一步:
os.Lstat(path)成功且fi.Mode() & os.ModeSymlink != 0→ 确认是软链接 - 第二步:
target, err := os.Readlink(path)→ 拿到原始目标字符串 - 第三步:构造绝对路径后调
os.Stat(resolvedTarget),用os.IsNotExist(err)判断是否为坏链 - 注意:Windows 上
os.Readlink对 junction 或快捷方式可能返回空或错误,这类不是os.ModeSymlink,得走另一套逻辑
filepath.EvalSymlinks 不适合做判定依据
filepath.EvalSymlinks 是路径规范化函数,不是类型检测工具。它会递归展开所有软链接并返回最终物理路径,但过程中不告诉你“中间哪一环是软链接”,也无法区分硬链接或普通文件。
更危险的是:它不做安全校验。传入 ../../../etc/shadow,它就真给你返回 /etc/shadow——这在文件服务中等于直接交出 root 权限。
- 永远不要用
filepath.EvalSymlinks替代os.Lstat做软链接判定 - 它适合用在“已知路径可信”的场景,比如构建时路径标准化;若用于运行时用户输入路径,必须配合白名单前缀校验(如
strings.HasPrefix(realPath, "/var/www/")) - 遇到循环链接时它会 panic 或返回
too many levels of symbolic links,而os.Lstat + os.Readlink手动循环可精确控制层数和防环逻辑
最易被忽略的点:坏链既不是有效文件,也不是安全链接,但它确实存在——os.Lstat 能看见它,os.Stat 和 filepath.EvalSymlinks 都失败。这个状态必须作为独立分支显式处理,不能混同于“路径不存在”。

















