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 是示例参数,不是对最终权限的通用保证。保存原构建版本和参数变化;未来撤掉临时的尾斜杠处理时,用同一套创建与拒绝用例确认正式工具链已经覆盖原故障。


