1. Overview1. 概述
Linux 5.2 introduced a brand-new filesystem mounting API, replacing the traditional mount(2) with a set of file-descriptor-based system calls. The new API addresses several core pain points of the old mount():
- Single-page limit: Old
mount()had to squeeze all options into a single 4 KB page; the new API passes one parameter at a time. - Error diagnostics: The context fd supports
read()to retrieve detailed error messages. - Secure isolation: You can create a "detached" mount object without attaching it to any path, and operate on it via file descriptor.
- Flexible reconfiguration:
fspick()+FSCONFIG_CMD_RECONFIGUREallows online modification of existing mount parameters.
Linux 5.2 引入了全新的文件系统挂载 API,用一组以文件描述符为基础的系统调用取代了传统的 mount(2)。新 API 解决了老 mount() 的几个核心痛点:
- 单页限制:老
mount()的选项必须全部塞进一个 4KB 的页面;新 API 每次只传一个参数。 - 错误诊断:新 API 的上下文 fd 上可以
read()读取详细的错误消息。 - 安全隔离:可以在不挂载到任何路径的情况下创建"游离的"挂载对象,然后用文件描述符直接操作。
- 灵活重配置:通过
fspick()+FSCONFIG_CMD_RECONFIGURE在线修改已有挂载的参数。
Workflow Comparison工作流对比
| Traditional mount(2)传统 mount(2) | New API (fsopen series)新 API(fsopen 系列) |
|---|---|
|
|
2. Quick Start: Your First Go Program2. 快速上手:你的第一个 Go 程序
package main
import (
"fmt"
"os"
"golang.org/x/sys/unix"
)
func main() {
// 1. Open tmpfs filesystem context
fsfd, err := unix.Fsopen("tmpfs", unix.FSOPEN_CLOEXEC)
if err != nil {
fmt.Fprintf(os.Stderr, "fsopen(tmpfs): %v\n", err)
os.Exit(1)
}
defer unix.Close(fsfd)
// 2. Set parameters
if err := unix.FsconfigSetString(fsfd, "size", "100M"); err != nil {
fmt.Fprintf(os.Stderr, "fsconfig size: %v\n", err)
os.Exit(1)
}
if err := unix.FsconfigSetString(fsfd, "mode", "1777"); err != nil {
fmt.Fprintf(os.Stderr, "fsconfig mode: %v\n", err)
os.Exit(1)
}
// 3. Create the filesystem instance
if err := unix.FsconfigCreate(fsfd); err != nil {
fmt.Fprintf(os.Stderr, "fsconfig create: %v\n", err)
os.Exit(1)
}
// 4. Create a mount object
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC, 0)
if err != nil {
fmt.Fprintf(os.Stderr, "fsmount: %v\n", err)
os.Exit(1)
}
defer unix.Close(mntfd)
// 5. Attach to target path
if err := unix.MoveMount(mntfd, "", unix.AT_FDCWD, "/mnt/mytmp",
unix.MOVE_MOUNT_F_EMPTY_PATH); err != nil {
fmt.Fprintf(os.Stderr, "move_mount: %v\n", err)
os.Exit(1)
}
fmt.Println("tmpfs mounted at /mnt/mytmp")
}
3.1 fsopen — Open a Filesystem Context3.1 fsopen — 打开文件系统上下文
Creates a blank filesystem configuration context for subsequent parameter setting and mounting.
创建一个空白的文件系统配置上下文,用于后续的参数设置和挂载。
func unix.Fsopen(fsName string, flags int) (fd int, err error)
| Parameter参数 | Description说明 | |
|---|---|---|
fsName | Filesystem type name, e.g. "proc", "tmpfs", "ext4", etc. | 文件系统类型名,如 "proc"、"tmpfs"、"ext4" 等 |
flags | 0 or或 unix.FSOPEN_CLOEXEC |
Requires CAP_SYS_ADMIN.
需要 CAP_SYS_ADMIN 权限。
3.2 fsconfig — Configure Parameters & Issue Commands3.2 fsconfig — 配置参数 & 执行命令
// Set a string parameter
func unix.FsconfigSetString(fd int, key, value string) error
// Set a boolean flag
func unix.FsconfigSetFlag(fd int, key string) error
// Set a file descriptor parameter
func unix.FsconfigSetFd(fd int, key string, fdValue int) error
// Set a binary blob parameter
func unix.FsconfigSetBinary(fd int, key string, value []byte) error
// Create the filesystem instance
func unix.FsconfigCreate(fd int) error
// Reconfigure an existing instance
func unix.FsconfigReconfigure(fd int) error
| Go Function函数 | Underlying cmd底层 cmd | Purpose用途 | |
|---|---|---|---|
FsconfigSetString(fd, k, v) | FSCONFIG_SET_STRING | Set a string parameter | 设置字符串参数 |
FsconfigSetFlag(fd, k) | FSCONFIG_SET_FLAG | Set a boolean flag | 设置布尔标志 |
FsconfigSetFd(fd, k, f) | FSCONFIG_SET_FD | Pass a file descriptor | 传入文件描述符 |
FsconfigSetBinary(fd, k, v) | FSCONFIG_SET_BINARY | Pass binary data | 传入二进制数据 |
FsconfigCreate(fd) | FSCONFIG_CMD_CREATE | Create the superblock | 创建超级块 |
FsconfigReconfigure(fd) | FSCONFIG_CMD_RECONFIGURE | Online reconfiguration | 在线重配置 |
3.3 fsmount — Create a Mount Object3.3 fsmount — 创建挂载对象
func unix.Fsmount(fsfd int, flags int, mountAttrs int) (mntfd int, err error)
| Parameter参数 | Description说明 | |
|---|---|---|
fsfd | Context fd returned by fsopen/fspick | fsopen/fspick 返回的上下文 fd |
flags | 0 or或 unix.FSMOUNT_CLOEXEC | |
mountAttrs | Mount attributes: MOUNT_ATTR_RDONLY, MOUNT_ATTR_NOSUID, MOUNT_ATTR_NODEV, MOUNT_ATTR_NOEXEC, MOUNT_ATTR_NOATIME, MOUNT_ATTR_RELATIME, etc. | 挂载属性:MOUNT_ATTR_RDONLY、MOUNT_ATTR_NOSUID、MOUNT_ATTR_NODEV、MOUNT_ATTR_NOEXEC、MOUNT_ATTR_NOATIME、MOUNT_ATTR_RELATIME 等 |
3.4 move_mount — Attach to Target3.4 move_mount — 挂载到目标
func unix.MoveMount(fromFd int, fromPath string, toFd int, toPath string, flags int) error
Most common usage (mount from mntfd):
最常见的用法(从 mntfd 挂载):
unix.MoveMount(mntfd, "", unix.AT_FDCWD, "/mnt/target",
unix.MOVE_MOUNT_F_EMPTY_PATH)
3.5 fspick — Pick an Existing Instance3.5 fspick — 选择已有实例
Used for online reconfiguration of an existing mount's parameters (no unmount needed). Available since Linux 5.8.
用于在线重配置一个已有挂载点的参数(无需卸载重挂)。从 Linux 5.8 开始可用。
func unix.Fspick(dirfd int, pathName string, flags int) (fd int, err error)
4.1 proc — Process Information Filesystem4.1 proc — 进程信息文件系统
| fstype namefstype 名 | "proc" |
|---|---|
| Typical mount常规挂载点 | /proc |
Parameters
参数表
| Parameter | Type | Go Call | Accepted Values / Notes接受值 / 说明 | |
|---|---|---|---|---|
hidepid | string | FsconfigSetString |
"off" / "0" — Everyone can see all PIDs (default)."noaccess" / "1" — Users can only access their own PID dirs."invisible" / "2" — Same as 1, plus other users' dirs are hidden entirely."ptraceable" / "4" — Only show processes the caller can ptrace (5.8+).
|
"off" / "0" — 所有人可见(默认)"noaccess" / "1" — 只能访问自己的 PID 目录"invisible" / "2" — 同上,且其他用户目录不可见"ptraceable" / "4" — 只显示可 ptrace 的进程 (5.8+)
|
subset | string | FsconfigSetString | "pid" — Show only process directories, hide kcore & other system files (5.8+). |
"pid" — 只显示进程目录,隐藏 kcore 等系统文件 (5.8+) |
gid | u32 | FsconfigSetString | Group ID exempt from hidepid restrictions (e.g. monitoring daemon's group). | 不受 hidepid 限制的组 ID(如监控 daemon 所在的组) |
pidns | string / fd | FsconfigSetString / FsconfigSetFd | PID namespace path, e.g. /proc/1234/ns/pid (newer kernels). |
PID 命名空间路径,如 /proc/1234/ns/pid(较新内核) |
func mountProc(target string) error {
fsfd, err := unix.Fsopen("proc", unix.FSOPEN_CLOEXEC)
if err != nil { return fmt.Errorf("fsopen(proc): %w", err) }
defer unix.Close(fsfd)
unix.FsconfigSetString(fsfd, "hidepid", "invisible")
unix.FsconfigSetString(fsfd, "subset", "pid")
unix.FsconfigSetString(fsfd, "gid", "1000") // proc group
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("fsconfig create: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_NOEXEC)
if err != nil { return fmt.Errorf("fsmount: %w", err) }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.2 tmpfs — In-Memory Filesystem4.2 tmpfs — 内存文件系统
| fstype namefstype 名 | "tmpfs" |
|---|
Parameters
参数表
| Parameter | Type | Go Call | Accepted Values / Notes接受值 / 说明 | |
|---|---|---|---|---|
size | string | FsconfigSetString | Max size, supports k/m/g/% (default 50% RAM) | 最大大小,支持 k/m/g/%(默认 50% RAM) |
nr_blocks | string | FsconfigSetString | Number of blocks (PAGE_SIZE units), supports k/m/g | 块数(PAGE_SIZE 单位),支持 k/m/g |
nr_inodes | string | FsconfigSetString | Max inodes, 0 = unlimited | 最大 inode 数,0 为不限制 |
mode | u32oct | FsconfigSetString | Octal permissions, e.g. "1777" | 八进制权限,如 "1777" |
uid | u32 | FsconfigSetString | Root directory UID | 根目录 UID |
gid | u32 | FsconfigSetString | Root directory GID | 根目录 GID |
inode32 | flag | FsconfigSetFlag | Use 32-bit inode numbers | 使用 32 位 inode 号 |
inode64 | flag | FsconfigSetFlag | Use 64-bit inode numbers | 使用 64 位 inode 号 |
noswap | flag | FsconfigSetFlag | Disable swap (6.4+) | 禁止 swap (6.4+) |
huge | enum | FsconfigSetString | "never" / "always" / "within_size" / "advise" | |
mpol | string | FsconfigSetString | NUMA memory policy (requires CONFIG_NUMA) | NUMA 内存策略(需 CONFIG_NUMA) |
quota | flag | FsconfigSetFlag | Enable quotas (CONFIG_TMPFS_QUOTA) | 启用配额(CONFIG_TMPFS_QUOTA) |
usrquota | flag | FsconfigSetFlag | Enable user quotas | 启用用户配额 |
grpquota | flag | FsconfigSetFlag | Enable group quotas | 启用组配额 |
func mountTmpfs(target, size, mode string) error {
fsfd, err := unix.Fsopen("tmpfs", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetString(fsfd, "size", size)
unix.FsconfigSetString(fsfd, "mode", mode)
unix.FsconfigSetString(fsfd, "nr_inodes", "100000")
unix.FsconfigSetFlag(fsfd, "noswap")
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create tmpfs: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC, unix.MOUNT_ATTR_NOSUID)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.3 devpts — Pseudo-Terminal Filesystem4.3 devpts — 伪终端文件系统
| fstype namefstype 名 | "devpts" |
|---|---|
| Typical mount常规挂载点 | /dev/pts |
Parameters
参数表
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
newinstance | flag | FsconfigSetFlag | Create independent devpts instance (essential for containers) | 创建独立的 devpts 实例(容器必备) |
gid | gid | FsconfigSetString | PTY slave device group | PTY 从设备所属组 |
uid | uid | FsconfigSetString | PTY slave device owner | PTY 从设备所属用户 |
mode | u32oct | FsconfigSetString | PTY slave permissions, e.g. "0620" | PTY 从设备权限(如 "0620") |
ptmxmode | u32oct | FsconfigSetString | ptmx node permissions | ptmx 节点权限 |
max | s32 | FsconfigSetString | Maximum number of PTYs | 最大 PTY 数量 |
func mountDevpts(target string) error {
fsfd, err := unix.Fsopen("devpts", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetFlag(fsfd, "newinstance")
unix.FsconfigSetString(fsfd, "gid", "5") // tty group
unix.FsconfigSetString(fsfd, "mode", "0620")
unix.FsconfigSetString(fsfd, "ptmxmode", "0000")
unix.FsconfigSetString(fsfd, "max", "1024")
if err := unix.FsconfigCreate(fsfd); err != nil { return err }
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.4 mqueue — POSIX Message Queues4.4 mqueue — POSIX 消息队列
| fstype namefstype 名 | "mqueue" |
|---|---|
| Typical mount常规挂载点 | /dev/mqueue |
func mountMqueue(target string) error {
fsfd, _ := unix.Fsopen("mqueue", unix.FSOPEN_CLOEXEC)
defer unix.Close(fsfd)
unix.FsconfigCreate(fsfd)
mntfd, _ := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC, unix.MOUNT_ATTR_NOSUID)
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.5 sysfs — Kernel Device Model4.5 sysfs — 内核设备模型
func mountSysfs(target string) error {
fsfd, _ := unix.Fsopen("sysfs", unix.FSOPEN_CLOEXEC)
defer unix.Close(fsfd)
unix.FsconfigCreate(fsfd)
mntfd, _ := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_NOEXEC)
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.6 cgroup2 — Unified Control Group v24.6 cgroup2 — 统一控制组 v2
| fstype namefstype 名 | "cgroup2" |
|---|---|
| Typical mount常规挂载点 | /sys/fs/cgroup |
Parameters — all flag type (FsconfigSetFlag)
参数表 — 全部为 flag 类型(FsconfigSetFlag)
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
nsdelegate | flag | FsconfigSetFlag | Allow delegation to namespaces | 允许委托给命名空间 |
favordynmods | flag | FsconfigSetFlag | Prefer dynamic cgroups | 优先动态 cgroup |
memory_localevents | flag | FsconfigSetFlag | Localize memory controller events | 内存事件本地化 |
memory_recursiveprot | flag | FsconfigSetFlag | Recursive memory.min protection | 递归保护内存 minimum |
memory_hugetlb_accounting | flag | FsconfigSetFlag | Enable hugetlb memory accounting | hugetlb 内存记账 |
pids_localevents | flag | FsconfigSetFlag | Localize PID controller events | PID 控制器事件本地化 |
func mountCgroup2(target string) error {
fsfd, err := unix.Fsopen("cgroup2", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetFlag(fsfd, "nsdelegate")
unix.FsconfigSetFlag(fsfd, "memory_recursiveprot")
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create cgroup2: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_NOEXEC)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.7 debugfs — Kernel Debug Filesystem4.7 debugfs — 内核调试文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
uid | uid | FsconfigSetString | Root directory UID | 根目录 UID |
gid | gid | FsconfigSetString | Root directory GID | 根目录 GID |
mode | u32oct | FsconfigSetString | Root directory permissions | 根目录权限 |
4.8 tracefs — Kernel Tracing Filesystem4.8 tracefs — 内核追踪文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
uid | uid | FsconfigSetString | Root directory UID | 根目录 UID |
gid | gid | FsconfigSetString | Root directory GID | 根目录 GID |
mode | u32oct | FsconfigSetString | Root directory permissions | 根目录权限 |
4.9 hugetlbfs — Huge Page Filesystem4.9 hugetlbfs — 大页文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
pagesize | string | FsconfigSetString | Page size, e.g. "2M", "1G" | 页面大小,如 "2M"、"1G" |
size | string | FsconfigSetString | Maximum size (supports k/m/g/%) | 最大大小(支持 k/m/g/%) |
min_size | string | FsconfigSetString | Minimum preallocated size | 预分配最小大小 |
nr_inodes | string | FsconfigSetString | Maximum inodes | 最大 inode 数 |
mode | u32oct | FsconfigSetString | Octal permissions | 八进制权限 |
uid | uid | FsconfigSetString | Root directory UID | 根目录 UID |
gid | gid | FsconfigSetString | Root directory GID | 根目录 GID |
4.10 overlay — Union Filesystem4.10 overlay — 联合文件系统
| fstype namefstype 名 | "overlay" |
|---|
Parameters
参数表
| Parameter | Type | Go Call | Values接受值 | |
|---|---|---|---|---|
lowerdir | string | FsconfigSetString | Lower directory (colon-separated or repeated) | 下层目录(冒号分隔或多参数追加) |
lowerdir+ | string / fd | FsconfigSetString / FsconfigSetFd | Append additional lower directory | 追加下层目录 |
upperdir | string / fd | FsconfigSetString / FsconfigSetFd | Upper directory | 上层目录 |
workdir | string / fd | FsconfigSetString / FsconfigSetFd | Work directory (must be on same fs as upperdir) | 工作目录(必须与 upperdir 同文件系统) |
redirect_dir | enum | FsconfigSetString | "off"/"follow"/"nofollow"/"on" | |
index | enum | FsconfigSetString | "on" / "off" | |
uuid | enum | FsconfigSetString | "off"/"null"/"auto"/"on" | |
nfs_export | enum | FsconfigSetString | "on" / "off" | |
userxattr | flag | FsconfigSetFlag | Use user xattr | 使用 user xattr |
xino | enum | FsconfigSetString | "off"/"auto"/"on" | |
metacopy | enum | FsconfigSetString | "on" / "off" | |
verity | enum | FsconfigSetString | "off"/"on"/"require" | |
volatile | flag | FsconfigSetFlag | Volatile mount (no data flush guarantee) | 易失性挂载(不保证数据落盘) |
override_creds | flag_no | FsconfigSetFlag | Enabled by default; pass "nooverride_creds" to disable | 默认启用;传递 "nooverride_creds" 禁用 |
default_permissions | flag | FsconfigSetFlag | Use kernel permission checks | 使用内核权限检查 |
fsync | enum | FsconfigSetString | "volatile"/"auto"/"strict" |
func mountOverlay(target, lower, upper, work string) error {
fsfd, err := unix.Fsopen("overlay", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetString(fsfd, "lowerdir+", lower)
unix.FsconfigSetString(fsfd, "upperdir", upper)
unix.FsconfigSetString(fsfd, "workdir", work)
unix.FsconfigSetString(fsfd, "redirect_dir", "on")
unix.FsconfigSetString(fsfd, "index", "on")
unix.FsconfigSetString(fsfd, "xino", "auto")
unix.FsconfigSetString(fsfd, "metacopy", "on")
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create overlay: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC, unix.MOUNT_ATTR_NOSUID)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
4.11 bpf — BPF Program Filesystem4.11 bpf — BPF 程序文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
uid | u32 | FsconfigSetString | Root directory UID | 根目录 UID |
gid | u32 | FsconfigSetString | Root directory GID | 根目录 GID |
mode | u32oct | FsconfigSetString | Root directory permissions | 根目录权限 |
delegate_cmds | string | FsconfigSetString | Delegated BPF commands | 委派的 BPF 命令 |
delegate_maps | string | FsconfigSetString | Delegated BPF map types | 委派的 BPF map 类型 |
delegate_progs | string | FsconfigSetString | Delegated BPF program types | 委派的 BPF prog 类型 |
delegate_attachs | string | FsconfigSetString | Delegated BPF attach types | 委派的 BPF attach 类型 |
4.12 ramfs — Non-swappable RAM Filesystem4.12 ramfs — 不可换出的内存文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
mode | u32oct | FsconfigSetString | Root permissions (octal, default "0755") | 根目录权限(八进制,默认 "0755") |
4.13 pstore — Persistent Storage Filesystem4.13 pstore — 持久存储文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
kmsg_bytes | u32 | FsconfigSetString | Per-CPU kernel log buffer size (bytes) | 每个 CPU 的内核日志缓冲区大小(字节) |
4.14 efivarfs — EFI Variable Filesystem4.14 efivarfs — EFI 变量文件系统
| Parameter | Type | Go Call | Notes说明 | |
|---|---|---|---|---|
uid | uid | FsconfigSetString | Root directory UID | 根目录 UID |
gid | gid | FsconfigSetString | Root directory GID | 根目录 GID |
4.15 devtmpfs — Device Node Population4.15 devtmpfs — 设备节点填充
init_fs_context, so it actually accepts all tmpfs parameters (size, mode, uid, etc.).
特殊:devtmpfs 委托给 tmpfs 或 ramfs 的 init_fs_context,因此实际可以使用 tmpfs 的全部参数(size、mode、uid 等)。
4.16 Filesystems Without Parameters4.16 无参数文件系统
The following filesystems accept no parameters in the new mount API. Simply fsopen → FsconfigCreate → fsmount:
以下文件系统在新挂载 API 中不接受任何参数,流程简化为 fsopen → FsconfigCreate → fsmount:
| fstype | Mount Point挂载点 | Description说明 | |
|---|---|---|---|
"mqueue" | /dev/mqueue | POSIX message queues | POSIX 消息队列 |
"sysfs" | /sys | Kernel device model | 内核设备模型 |
"securityfs" | /sys/kernel/security | Security module filesystem | 安全模块文件系统 |
"configfs" | /sys/kernel/config | Kernel configuration | 内核配置 |
5.1 Scenario: Secure /proc Mount5.1 场景:安全挂载 /proc
A complete, production-grade secure /proc mount for container sandboxes:
完整的、生产级别的安全 /proc 挂载,用于容器沙箱:
// MountProcSafe mounts /proc in the most restrictive mode.
// Equivalent to: mount -t proc proc /proc -o hidepid=invisible,gid=1000,subset=pid
func MountProcSafe(target string, exemptGid int) error {
fsfd, err := unix.Fsopen("proc", unix.FSOPEN_CLOEXEC)
if err != nil {
return fmt.Errorf("fsopen(proc): %w", err)
}
defer unix.Close(fsfd)
if err := unix.FsconfigSetString(fsfd, "hidepid", "invisible"); err != nil {
return err
}
if err := unix.FsconfigSetString(fsfd, "gid",
strconv.Itoa(exemptGid)); err != nil {
return err
}
if err := unix.FsconfigSetString(fsfd, "subset", "pid"); err != nil {
return err
}
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create proc: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_NOEXEC)
if err != nil {
return fmt.Errorf("fsmount: %w", err)
}
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
5.2 Scenario: tmpfs with Quotas5.2 场景:带配额的 tmpfs
func MountTmpfsWithQuota(target, size string, uid, gid int) error {
fsfd, err := unix.Fsopen("tmpfs", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetString(fsfd, "size", size)
unix.FsconfigSetString(fsfd, "mode", "1777")
unix.FsconfigSetString(fsfd, "uid", strconv.Itoa(uid))
unix.FsconfigSetString(fsfd, "gid", strconv.Itoa(gid))
unix.FsconfigSetString(fsfd, "nr_inodes", "50000")
unix.FsconfigSetFlag(fsfd, "noswap")
// Enable quotas (requires CONFIG_TMPFS_QUOTA)
unix.FsconfigSetFlag(fsfd, "usrquota")
unix.FsconfigSetFlag(fsfd, "grpquota")
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create tmpfs: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC, unix.MOUNT_ATTR_NOSUID)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
5.3 Scenario: cgroup2 Container Runtime5.3 场景:cgroup2 容器运行时
func MountCgroup2(target string) error {
fsfd, err := unix.Fsopen("cgroup2", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetFlag(fsfd, "nsdelegate")
unix.FsconfigSetFlag(fsfd, "memory_recursiveprot")
unix.FsconfigSetFlag(fsfd, "favordynmods")
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create cgroup2: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_NOEXEC)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
5.4 Scenario: Overlay Container rootfs5.4 场景:overlay 容器 rootfs
func MountOverlayRootFS(target, lower, upper, work string) error {
for _, dir := range []string{target, upper, work} {
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
}
fsfd, err := unix.Fsopen("overlay", unix.FSOPEN_CLOEXEC)
if err != nil { return err }
defer unix.Close(fsfd)
unix.FsconfigSetString(fsfd, "lowerdir", lower)
unix.FsconfigSetString(fsfd, "upperdir", upper)
unix.FsconfigSetString(fsfd, "workdir", work)
unix.FsconfigSetString(fsfd, "redirect_dir", "on")
unix.FsconfigSetString(fsfd, "index", "on")
unix.FsconfigSetString(fsfd, "xino", "auto")
unix.FsconfigSetString(fsfd, "metacopy", "on")
if err := unix.FsconfigCreate(fsfd); err != nil {
return fmt.Errorf("create overlay: %w", err)
}
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC, unix.MOUNT_ATTR_NOSUID)
if err != nil { return err }
defer unix.Close(mntfd)
return unix.MoveMount(mntfd, "", unix.AT_FDCWD, target,
unix.MOVE_MOUNT_F_EMPTY_PATH)
}
5.5 Scenario: Runtime Reconfiguration5.5 场景:运行时重配置 proc 参数
// ReconfigureProc modifies an existing /proc mount's hidepid setting at runtime.
// Equivalent to: mount -o remount,hidepid=invisible /proc
func ReconfigureProc(hidepid, subset string) error {
fsfd, err := unix.Fspick(unix.AT_FDCWD, "/proc", unix.FSPICK_CLOEXEC)
if err != nil {
return fmt.Errorf("fspick(/proc): %w", err)
}
defer unix.Close(fsfd)
if hidepid != "" {
if err := unix.FsconfigSetString(fsfd, "hidepid", hidepid); err != nil {
return err
}
}
if subset != "" {
if err := unix.FsconfigSetString(fsfd, "subset", subset); err != nil {
return err
}
}
return unix.FsconfigReconfigure(fsfd)
}
Appx A: fsparam Type Cheat Sheet附录 A:fsparam 类型速查
| Kernel Macro内核宏 | Meaning含义 | Go Call调用 | |
|---|---|---|---|
fsparam_string | String | 字符串 | FsconfigSetString |
fsparam_flag | Boolean flag | 布尔标志 | FsconfigSetFlag |
fsparam_flag_no | Enabled by default; pass "noXXX" to disable | 默认启用,传 "noXXX" 关闭 | FsconfigSetFlag("nooverride_creds") |
fsparam_u32 | 32-bit unsigned int | 32 位无符号整数 | FsconfigSetString(fd, k, "123") |
fsparam_s32 | 32-bit signed int | 32 位有符号整数 | FsconfigSetString(fd, k, "-1") |
fsparam_u32oct | Octal u32 | 八进制 u32 | FsconfigSetString(fd, k, "1777") |
fsparam_enum | Enumerated value | 枚举值 | FsconfigSetString(fd, k, "auto") |
fsparam_file_or_string | String or fd | 字符串或 fd | FsconfigSetString / FsconfigSetFd |
fsparam_uid | User ID | 用户 ID | FsconfigSetString(fd, k, "1000") |
fsparam_gid | Group ID | 组 ID | FsconfigSetString(fd, k, "1000") |
Appx B: mountAttr Flags附录 B:mountAttr 属性标志
| Constant常量 | Meaning含义 | |
|---|---|---|
unix.MOUNT_ATTR_RDONLY | Read-only | 只读 |
unix.MOUNT_ATTR_NOSUID | Disallow setuid/setgid | 禁止 setuid / setgid |
unix.MOUNT_ATTR_NODEV | Disallow device files | 禁止设备文件 |
unix.MOUNT_ATTR_NOEXEC | Disallow execution | 禁止执行 |
unix.MOUNT_ATTR_NOATIME | Do not update access times | 不更新访问时间 |
unix.MOUNT_ATTR_RELATIME | Relative atime updates | 相对访问时间更新 |
unix.MOUNT_ATTR_NOSYMFOLLOW | Disallow symlink following (5.10+) | 禁止跟随符号链接 (5.10+) |
unix.MOUNT_ATTR_NODIRATIME | Do not update directory atime | 不更新目录访问时间 |
Appx C: Error Diagnostics附录 C:错误诊断
A key advantage of the new API: read detailed error messages from the context fd.
新 API 的一大优势:可以从上下文 fd 读取详细的错误消息。
func readFsconfigError(fsfd int) string {
buf := make([]byte, 4096)
n, err := unix.Read(fsfd, buf)
if err != nil {
return "(no diagnostic message)"
}
return string(buf[:n])
}
if err := unix.FsconfigCreate(fsfd); err != nil {
diag := readFsconfigError(fsfd)
return fmt.Errorf("create failed: %w [diag: %s]", err, diag)
}
Message format:
消息格式:
"e <subsystem>:<message>" // error
"w <subsystem>:<message>" // warning
"i <subsystem>:<message>" // info
Appx D: Dependencies & Imports附录 D:依赖与导入
import (
"golang.org/x/sys/unix"
)
unix.Fsopen and friends require Linux 5.2+. Check kernel version at runtime with unix.KernelVersion(). Some parameters (e.g. hidepid=ptraceable, subset=pid) require 5.8+.
注意:unix.Fsopen 等函数要求 Linux 5.2+。运行时可通过 unix.KernelVersion() 检查内核版本。部分参数(如 hidepid=ptraceable、subset=pid)需要 5.8+。
Appx E: Real-World Best Practices & References附录 E:落地最佳实践与参考项目
The new mount API is not just theoretical — it has been adopted in production by major container runtimes and security libraries. Below are key real-world references that demonstrate battle-tested usage patterns.
新 mount API 并非纸上谈兵——它已被主流容器运行时和安全库在生产环境中采用。以下是经过实战检验的关键参考项目。
E.1 brauner/man-pages-md — Authoritative API DocumentationE.1 brauner/man-pages-md — 权威 API 文档
github.com/brauner/man-pages-md is the definitive reference for the new mount API man pages, maintained by Christian Brauner (Linux kernel maintainer for the VFS, mount, and namespace subsystems — essentially the person who designed this API). The repository provides:
github.com/brauner/man-pages-md 是新 mount API 手册页的权威参考,由 Christian Brauner(Linux 内核 VFS、mount 和 namespace 子系统的维护者——本质上就是这套 API 的设计者)维护。该仓库提供:
| Man Page手册页 | Description说明 | |
|---|---|---|
fsopen(2) | Open a filesystem configuration context | 打开文件系统配置上下文 |
fsconfig(2) | Set parameters and issue commands on a context | 在上下文上设置参数和执行命令 |
fsmount(2) | Create a mount object from a context | 从上下文创建挂载对象 |
fspick(2) | Pick an existing filesystem for reconfiguration | 选择已有文件系统进行重配置 |
move_mount(2) | Move a mount to a new location | 将挂载移动到新位置 |
open_tree(2) | Open a mount tree by path or fd | 通过路径或 fd 打开挂载树 |
mount_setattr(2) | Change properties of a mount | 修改挂载属性 |
mount_attr(2type) | Mount attribute structure reference | 挂载属性结构体参考 |
read() from the context fd. The diagnostic messages (prefixed with e / w / i ) are documented in fsconfig(2) from this repository and are far more informative than raw errno values.
实用技巧:遇到 fsconfig 错误时,务必从上下文 fd 中 read() 诊断信息。此仓库中的 fsconfig(2) 手册记录了这些诊断消息格式(以 e / w / i 为前缀),比原始 errno 值有用得多。
E.2 runc PR #5378 — libct: Switch to New Mount APIE.2 runc PR #5378 — libct:切换到新 Mount API
opencontainers/runc#5378 is the landmark pull request where runc — the reference OCI container runtime used by Docker, Kubernetes, and containerd — migrated its libct (container library) mount infrastructure from the legacy mount(2) syscall to the new mount API. This PR demonstrates production-grade usage at scale.
opencontainers/runc#5378 是标志性的 PR——runc(Docker、Kubernetes 和 containerd 使用的 OCI 参考容器运行时)将其 libct(容器库)挂载基础设施从传统 mount(2) 系统调用迁移到新 mount API。此 PR 展示了大规模生产环境的用法。
Key Takeaways
核心要点
- Graceful fallback: runc probes kernel support at runtime and falls back to legacy
mount(2)on older kernels — essential for portability across Linux distributions. - 优雅降级:runc 在运行时探测内核支持,并在旧内核上回退到传统
mount(2)——这对跨 Linux 发行版的可移植性至关重要。 - Detailed error diagnostics: The PR leverages
read()on the fs_context fd to extract human-readable error messages, significantly improving debuggability compared to legacymount(2)which only returnsEINVAL. - 详细的错误诊断:该 PR 利用 fs_context fd 上的
read()提取人类可读的错误消息,相比传统mount(2)仅返回EINVAL,显著提升了可调试性。 - Unified mount helper: The PR introduces a common mount wrapper that handles the full fsopen → fsconfig → fsmount → move_mount sequence, ensuring consistent error handling across all filesystem types (proc, tmpfs, devpts, cgroup2, sysfs, etc.).
- 统一挂载辅助函数:该 PR 引入了通用挂载包装器,处理完整的 fsopen → fsconfig → fsmount → move_mount 序列,确保所有文件系统类型(proc、tmpfs、devpts、cgroup2、sysfs 等)的错误处理一致。
- Idempotent reconfiguration: Uses
fspick()+FSCONFIG_CMD_RECONFIGUREfor online parameter changes without unmounting — critical for long-running container processes. - 幂等重配置:使用
fspick()+FSCONFIG_CMD_RECONFIGURE实现在线参数修改而不卸载——这对长时间运行的容器进程至关重要。
// Probe whether the new mount API is available on this kernel.
// If not, fall back to unix.Mount (legacy mount(2)).
var useNewMountAPI bool
func init() {
// Try fsopen on a trivial filesystem
fsfd, err := unix.Fsopen("tmpfs", unix.FSOPEN_CLOEXEC)
if err != nil {
useNewMountAPI = false
return
}
unix.Close(fsfd)
useNewMountAPI = true
}
func mountSafe(source, target, fstype string, flags uintptr, data string) error {
if useNewMountAPI {
return mountNew(source, target, fstype, flags, data)
}
return unix.Mount(source, target, fstype, flags, data)
}
E.3 runc PR #4448 — Overlayfs Write-Protect for /proc/self/exeE.3 runc PR #4448 — 用 overlayfs 写保护 /proc/self/exe
opencontainers/runc#4448 by Aleksa Sarai (cyphar) demonstrates an elegant security hardening technique: using the new mount API to create an overlayfs mount that write-protects a container runtime's own /proc/self/exe, preventing attackers from modifying the runtime binary even if they gain write access. This is a beautiful example of the new mount API's ability to create complex, detached mount trees without polluting the global namespace.
opencontainers/runc#4448 由 Aleksa Sarai(cyphar)开发,展示了一种精巧的安全加固技术:使用新 mount API 创建 overlayfs 挂载来写保护容器运行时自身的 /proc/self/exe,防止攻击者即使获得写权限后修改运行时二进制文件。这是新 mount API 在不污染全局命名空间的情况下创建复杂、游离挂载树的绝佳范例。
Key Takeaways
核心要点
- Detached mounts for security: Creates an overlay mount object without ever attaching it to the global VFS, then uses it purely through file descriptors — a pattern impossible with legacy
mount(2). - 安全游离挂载:创建 overlay 挂载对象而从不附加到全局 VFS,然后纯粹通过文件描述符使用它——这是传统
mount(2)无法实现的模式。 - Opportunistic use: Gracefully falls back to a memfd-based approach when overlayfs isn't available or when the new mount API isn't supported — never breaks existing functionality.
- 机会主义使用:当 overlayfs 不可用或新 mount API 不支持时优雅降级到基于 memfd 的方式——从不破坏现有功能。
- Minimal privilege surface: Uses overlayfs read-only lowerdir to create a tamper-proof view of the runtime binary, reducing the attack surface even if the attacker has root in the container.
- 最小化权限面:使用 overlayfs 只读 lowerdir 创建运行时二进制的防篡改视图,即使攻击者在容器中拥有 root 权限也能减小攻击面。
func sealExecutable(exePath string) (*os.File, error) {
fsfd, err := unix.Fsopen("overlay", unix.FSOPEN_CLOEXEC)
if err != nil {
return nil, err // Caller should fall back to memfd
}
defer unix.Close(fsfd)
// lowerdir = the original executable (read-only)
unix.FsconfigSetString(fsfd, "lowerdir", filepath.Dir(exePath))
// workdir = ephemeral, unused since we're read-only
unix.FsconfigSetString(fsfd, "workdir", workDir)
if err := unix.FsconfigCreate(fsfd); err != nil {
return nil, err
}
// Get a detached mount — never attached to VFS!
mntfd, err := unix.Fsmount(fsfd, unix.FSMOUNT_CLOEXEC,
unix.MOUNT_ATTR_RDONLY)
if err != nil {
return nil, err
}
// Open the sealed binary from the detached mount
fd, err := unix.Openat(mntfd, filepath.Base(exePath),
unix.O_RDONLY|unix.O_CLOEXEC, 0)
unix.Close(mntfd)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), "sealed-exe"), nil
}
E.4 cyphar/filepath-securejoin — Secure Path Operations via Mount APIE.4 cyphar/filepath-securejoin — 基于 Mount API 的安全路径操作
github.com/cyphar/filepath-securejoin by Aleksa Sarai is the gold standard for how the new mount API should be used in security-critical Go code. Originally a safer alternative to filepath.Join, the library has evolved to deeply integrate with the new mount API (openat2, fsopen, open_tree) to eliminate entire classes of TOCTOU (Time-of-Check-Time-of-Use) vulnerabilities that plague path-based APIs.
github.com/cyphar/filepath-securejoin 由 Aleksa Sarai 开发,是新 mount API 在安全关键 Go 代码中使用的标杆。最初作为 filepath.Join 的安全替代品,该库已深度集成了新 mount API(openat2、fsopen、open_tree),以消除困扰基于路径 API 的各类 TOCTOU(检查时间-使用时间)漏洞。
Architecture & Best Practices
架构与最佳实践
| Principle原则 | Implementation实现 | ||
|---|---|---|---|
| Fd over paths | 文件描述符优于路径 | New API (OpenInRoot, MkdirAll) returns *os.File (O_PATH fd) instead of string paths, eliminating TOCTOU races between path resolution and use. |
新 API(OpenInRoot、MkdirAll)返回 *os.File(O_PATH fd)而非字符串路径,消除了路径解析与使用之间的 TOCTOU 竞态。 |
| Progressive enhancement | 渐进增强 | Uses openat2(RESOLVE_IN_ROOT) when available (Linux 5.6+), falls back to userspace resolution. Privileged callers additionally use fsopen/open_tree for /proc attack resistance. |
有 openat2(RESOLVE_IN_ROOT) 时(Linux 5.6+)使用它,否则回退到用户空间解析。特权调用者还额外使用 fsopen/open_tree 来防御 /proc 攻击。 |
| O_PATH separation | O_PATH 分离 | Separates O_PATH fd acquisition (OpenInRoot) from actual file opening (Reopen), preventing accidental access to "bad" inodes (fifos, device nodes) that could cause DoS. |
将 O_PATH fd 获取(OpenInRoot)与实际文件打开(Reopen)分离,防止意外访问可能导致 DoS 的"坏"inode(fifo、设备节点)。 |
| Magic-link defense | Magic-link 防御 | Uses fsopen + open_tree to create a private mount of the rootfs, immunizing path resolution against manipulated /proc magic-links (e.g. /proc/self/fd/N). |
使用 fsopen + open_tree 创建 rootfs 的私有挂载,使路径解析免疫于被篡改的 /proc magic-link(如 /proc/self/fd/N)。 |
| Licensing hygiene | 许可证卫生 | Per-file SPDX headers: BSD-3-Clause for Docker-derived code, MPL-2.0 for libpathrs-derived new API. A model for multi-source open-source projects. | 每个文件 SPDX 许可证标识头:Docker 派生代码使用 BSD-3-Clause,libpathrs 派生新 API 使用 MPL-2.0。多来源开源项目的典范。 |
// OpenInRoot safely opens a path within a rootfs.
// This is a simplified illustration of filepath-securejoin's approach.
func OpenInRoot(root, unsafePath string) (*os.File, error) {
// Phase 1: Open the root directory
rootFd, err := unix.Open(root, unix.O_PATH|unix.O_CLOEXEC, 0)
if err != nil {
return nil, fmt.Errorf("open root %s: %w", root, err)
}
defer unix.Close(rootFd)
// Phase 2: If we have privileges, create a private mount
// of root to protect against /proc magic-link attacks.
// This is what filepath-securejoin does opportunistically:
// fsfd = fsopen(rootFd)
// open_tree to get a private copy
// use the private fd for the rest of the resolution
// Phase 3: Resolve the path using openat2(RESOLVE_IN_ROOT)
// on Linux 5.6+, or manual component-wise resolution otherwise
how := &unix.OpenHow{
Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW,
Mode: 0,
Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
}
fd, err := unix.Openat2(rootFd, unsafePath, how)
if err != nil {
return nil, fmt.Errorf("openat2: %w", err)
}
return os.NewFile(uintptr(fd), unsafePath), nil
}
E.5 How These Projects Fit TogetherE.5 这些项目如何协同
These four references form a complete picture of the new mount API ecosystem:
这四个参考形成了新 mount API 生态的完整图景:
| Project项目 | Role角色 | Author作者 | ||
|---|---|---|---|---|
| brauner/man-pages-md | Reference documentation — canonical man pages for every syscall | 参考文档 — 每个系统调用的权威手册 | Christian Brauner (kernel maintainer) | Christian Brauner(内核维护者) |
| runc#5378 | Production adoption — how a major runtime migrates its mount infrastructure | 生产落地 — 主流运行时如何迁移挂载基础设施 | lifubang (runc maintainer) | lifubang(runc 维护者) |
| runc#4448 | Security hardening — detached overlay mounts without polluting VFS | 安全加固 — 不污染 VFS 的游离 overlay 挂载 | Aleksa Sarai (cyphar, runc maintainer) | Aleksa Sarai(cyphar,runc 维护者) |
| filepath-securejoin | Security library — fd-based path ops eliminating TOCTOU | 安全库 — 基于 fd 的路径操作,消除 TOCTOU | Aleksa Sarai (cyphar, runc maintainer) | Aleksa Sarai(cyphar,runc 维护者) |
Recommended reading order: Start with brauner/man-pages-md for authoritative API reference → study runc#5378 for real-world migration patterns → explore runc#4448 for advanced detached-mount techniques → dive into filepath-securejoin for security-first API design principles.
推荐阅读顺序:从 brauner/man-pages-md 入手了解权威 API 参考 → 研究 runc#5378 学习真实迁移模式 → 探索 runc#4448 掌握高级游离挂载技巧 → 深入 filepath-securejoin 理解安全优先的 API 设计原则。
Go New Mount API — Complete Guide · Based on Linux 6.x kernel source · References: brauner/man-pages-md, runc, filepath-securejoin · 2026-07 Go 语言 New Mount API 完全指南 · 基于 Linux 6.x 内核源码 · 参考资料:brauner/man-pages-md、runc、filepath-securejoin · 2026-07