DEBUG.WIKI
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
晚安云;香港 CN2 ¥15/月
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

Go 1.26.5 的 Root.MkdirAll 创建带尾斜杠路径报 no such file

Root.MkdirAll 在 Go 1.26.5、1.25.12 中可能因尾斜杠错误返回 ENOENT。保留 Root 的访问边界,采用含修正的补丁,或仅修正可控的目录参数。

os.Root 已经打开成功,调用 root.MkdirAll("some/path/", 0755) 却返回:

mkdirat some/path/: no such file or directory

目录不存在正是 MkdirAll 要处理的情况。如果同样的相对路径去掉最后一个 / 就成功,而故障出现在 Go 1.26.5 或 1.25.12,应检查这两个补丁版本的尾斜杠回归,不能仅凭 ENOENT 就认定根目录丢了。

这里针对 os.Root.MkdirAll,不是普通的 os.MkdirAll。两者的调用入口和访问边界不同,不能为了消掉一个错误就交换使用。

把打开 Root 与创建子目录分开检查

先保留实际构建工具链版本、目标系统和完整错误,区分失败发生在 os.OpenRoot,还是后面的 Root.MkdirAll。前者失败时,尚未进入本题的子目录创建路径。

下面展示故障调用形态。./sandbox 必须已经是一个存在的目录,示例只在它下面创建子目录:

package main

import (
    "log"
    "os"
)

func main() {
    root, err := os.OpenRoot("./sandbox")
    if err != nil {
        log.Fatal(err)
    }
    defer root.Close()

    if err := root.MkdirAll("some/path/", 0755); err != nil {
        log.Fatal(err)
    }
}

已确认报告覆盖 Go 1.26.5 和 1.25.12,另有 Linux/amd64 的 1.26.5 对照。不要把它扩大成任何平台、任何 ENOENT 都是同一原因。父目录权限、已经存在的普通文件、失效的符号链接或 Root 生命周期错误仍需按各自分支处理。

优先更新到保留安全修正的兼容补丁

这次回归出现在先前的安全修正之后。恢复到更早版本虽然可能让目录创建成功,也会撤掉对应安全变化,不适合作为默认处理。

Go 1.26.6 的正式 rootMkdirAll 实现已带入创建目录与尾分隔符处理标志;相比 1.26.5,调用不再沿用没有这些标志的旧路径。采用项目仍受支持、包含修正的正式工具链,并在干净构建中重新产出程序,保留平台与依赖兼容性检查。

这不是部署机器装一个新 go 命令就会自动生效的修复。Go 标准库通常编进可执行文件;真正运行的旧产物不会因此改变。应核对构建记录和最终二进制版本,按原发布流程替换整份产物。

若临时无法重建工具链,而目录参数由应用自己构造,可评估把固定的 "some/path/" 改为 "some/path",继续调用同一个 Root。这个受控改动只移除该目录参数末尾的分隔符,不把路径拼到 Root 外部,也不改变目录权限。

外部输入不能机械套用同样处理。尤其不要先对整条路径做随意清洗、折叠 .. 或转换绝对路径,再把“能创建”当成等价行为;符号链接和平台分隔符会影响解析语义。继续让 Root 执行原有的边界检查,不用拼接完整路径后的普通 MkdirAll 绕开它。

用同一程序验证尾分隔符与失败边界

在隔离目录中覆盖新建多层目录、有尾斜杠和无尾斜杠、目标目录已经存在,以及某个中间项是普通文件。前两种合法目录输入应能按预期创建;普通文件阻挡路径仍应明确失败,不能把错误统一吞掉。

再按项目既有安全用例核对 Root 外部路径与符号链接边界,确认修复没有被替换成更宽松的文件访问。不要只看主路径成功,就删除所有路径验证。

目录权限还受平台与 umask 等条件影响,0755 是示例参数,不是对最终权限的通用保证。保存原构建版本和参数变化;未来撤掉临时的尾斜杠处理时,用同一套创建与拒绝用例确认正式工具链已经覆盖原故障。