$RodHat_
Syscall reference · Filesystem

fsopen(2)

Open a filesystem context: step one of the fd-based mount API (fsconfig, fsmount, move_mount).

Since
Linux 5.2check your kernel →
glibc
glibc 2.36
System
Linux
Man page
fsopen(2)

Synopsis

#include <sys/mount.h>

int fsopen(const char *fsname, unsigned int flags);
int fsconfig(int fd, unsigned int cmd, const char *key,
             const void *value, int aux);
int fsmount(int fsfd, unsigned int flags, unsigned int attr_flags);
int move_mount(int from_dirfd, const char *from_path,
               int to_dirfd, const char *to_path, unsigned int flags);

Rod's take

mount(2) is one void pointer full of comma-separated wishes and a single undifferentiated EINVAL when one of them is wrong. The fd-based API that landed in Linux 5.2 builds a mount like any other kernel object: open a context, feed it options one fsconfig call at a time, commit, get a detached mount fd, attach it with move_mount. Per-option errors. A mount you can hand to another process over a Unix socket before deciding where it lives. No half-built rootfs flashing through the host namespace. More lines of C, sure. Worth every one of them.

Flags and arguments that matter

NameWhat it does
FSOPEN_CLOEXECfsopen(): set close-on-exec on the new filesystem context fd.
FSCONFIG_SET_FLAGfsconfig(): set a boolean option with no value, e.g. "ro".
FSCONFIG_SET_STRINGfsconfig(): set a string option such as "source" or "errors".
FSCONFIG_SET_FDfsconfig(): pass an open file descriptor as the option value.
FSCONFIG_CMD_CREATEfsconfig(): commit the configuration and create (or reuse) the superblock.
FSCONFIG_CMD_CREATE_EXCLLinux 6.6Like FSCONFIG_CMD_CREATE but fail instead of silently reusing an existing matching superblock.
FSCONFIG_CMD_RECONFIGUREfsconfig(): apply changes to an existing superblock (context from fspick(2) or a used fsmount context).
FSMOUNT_CLOEXECfsmount(): set close-on-exec on the returned detached mount fd. attr_flags takes MOUNT_ATTR_* values.
MOVE_MOUNT_F_EMPTY_PATHmove_mount(): from_path is empty, so the source is from_dirfd itself (e.g. the fd fsmount returned).
MOVE_MOUNT_BENEATHLinux 6.5move_mount(): mount beneath the top mount at the target instead of on top of it.

Errors you will actually hit

errnoMeaning
EPERMfsopen(): caller lacks CAP_SYS_ADMIN.
ENODEVfsopen(): the filesystem named by fsname isn't supported by this kernel.
EINVALInvalid flags; for fsmount(), also an invalid MOUNT_ATTR_* value in attr_flags.
EMFILEThe calling process has too many open files to create another fd.
EFAULTfsopen(): fsname is NULL or points outside the accessible address space.

Example

int fs = fsopen("ext4", FSOPEN_CLOEXEC);
if (fs < 0) err(1, "fsopen");
if (fsconfig(fs, FSCONFIG_SET_STRING, "source", "/dev/sdb1", 0) < 0 ||
    fsconfig(fs, FSCONFIG_SET_FLAG, "ro", NULL, 0) < 0 ||
    fsconfig(fs, FSCONFIG_SET_STRING, "errors", "remount-ro", 0) < 0 ||
    fsconfig(fs, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
    err(1, "fsconfig");

int mnt = fsmount(fs, FSMOUNT_CLOEXEC, MOUNT_ATTR_NODEV);
if (mnt < 0) err(1, "fsmount");
close(fs);

if (move_mount(mnt, "", AT_FDCWD, "/mnt/data", MOVE_MOUNT_F_EMPTY_PATH) < 0)
    err(1, "move_mount");
close(mnt);

Trimmed from the full walkthrough in The new mount API: fsopen(2), fsmount(2), and doing it right.

BSD equivalent

FreeBSD: nmount(2) takes structured name/value option iovecs (FreeBSD 5.0), but it's still a one-shot call. OpenBSD: none; plain mount(2).

The long version, from the console

Sources

  1. fsopen(2), man7.org
  2. fsconfig(2), man7.org
  3. fsmount(2), man7.org
  4. move_mount(2), man7.org
  5. open_tree(2), man7.org
  6. nmount(2), FreeBSD

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.