fsopen(2)

πŸ’– fsopen(2) β€” the system call that starts a whole filesystem situationship πŸ’–

okay bestie, gather round, because this one is literally the glow-up of mounting filesystems. this page lives in section 2 of the System Calls Manual, and it's the story of how you build a filesystem in the kernel like it's a little art project. 🎨✨


🌸 NAME 🌸

fsopen β€” create a new filesystem context, aka birth a brand-new filesystem configuration vibe into the world 🐣


πŸŽ€ LIBRARY πŸŽ€

Standard C library (libc, -lc). yes, the classic. she's been here the whole time. πŸ’…


✨ SYNOPSIS ✨

#include <sys/mount.h>

int fsopen(const char *fsname, unsigned int flags);

πŸ¦‹ DESCRIPTION πŸ¦‹

so. fsopen() is part of the suite of file-descriptor-based mount facilities in Linux. that whole little family. the mount girlies. 🫢

fsopen() creates a blank filesystem configuration context within the kernel for the filesystem named by fsname, and places it into creation mode. a new file descriptor associated with that filesystem configuration context is then returned. πŸ’«

heads up bestie: the calling process must have the CAP_SYS_ADMIN capability in order to create a new filesystem configuration context. no cap, literally no cap. πŸ”

a filesystem configuration context is an in-kernel representation of a pending transaction β€” basically a little to-do list of configuration parameters that are going to be applied when you create a new instance of a filesystem (or when you modify the configuration of an existing filesystem instance, like when you're using fspick(2)). it's giving "everything's already planned out, nothing's committed yet." πŸ’­

after you get a filesystem configuration context with fsopen(), the general workflow for operating on it looks like this:

  1. pass the filesystem context file descriptor to fsconfig(2) to specify any desired filesystem parameters. you can do this as many times as you need β€” go off, queen. πŸ”
  2. pass the same filesystem context file descriptor to fsconfig(2) with FSCONFIG_CMD_CREATE to create an instance of the configured filesystem. ✨ boom, she exists ✨
  3. pass the same filesystem context file descriptor to fsmount(2) to create a new detached mount object for the root of the filesystem instance, which is then attached to a new file descriptor. (this also places the filesystem context file descriptor into reconfiguration mode, similar to the mode produced by fspick(2).) once a mount object has been created with fsmount(2), the filesystem context file descriptor can be safely closed. πŸšͺ
  4. now that a mount object has been created, you may:
    • use the detached mount object file descriptor as a dirfd argument to "*at()" system calls; and/or
    • attach the mount object to a mount point by passing the mount object file descriptor to move_mount(2). this will also prevent the mount object from being unmounted and destroyed when the mount object file descriptor is closed. πŸ”’

the mount object file descriptor will remain associated with the mount object even after doing the above operations, so you may repeatedly use the mount object file descriptor with move_mount(2) and/or "*at()" system calls as many times as necessary. infinite reuse, it's giving sustainability πŸ’š

a filesystem context will move between different modes throughout its lifecycle β€” such as the creation phase when created with fsopen(), the reconfiguration phase when an existing filesystem instance is selected with fspick(2), and the intermediate "awaiting-mount" phase between FSCONFIG_CMD_CREATE and fsmount(2) β€” which has an impact on what operations are permitted on the filesystem context. she's a shapeshifter, and her rules change with the era she's in. πŸ¦‹

the file descriptor returned by fsopen() also acts as a channel for filesystem drivers to provide more comprehensive diagnostic information than is normally provided through the standard errno(3) interface for system calls. so if an error occurs at any time during the workflow mentioned above, calling read(2) on the filesystem context file descriptor will retrieve any ancillary information about the encountered errors. (see the "Message retrieval interface" section for more details on the message format.) πŸ“¬

flags can be used to control aspects of the creation of the filesystem configuration context file descriptor. a value for flags is constructed by bitwise ORing zero or more of the following constants:

  • FSOPEN_CLOEXEC β€” sets the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. see the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful. (spoiler: it's about not leaking fds into your exec'd children 🧼)

a list of filesystems supported by the running kernel (and thus a list of valid values for fsname) can be obtained from /proc/filesystems. (see also proc_filesystems(5).) πŸ“š

πŸ’Œ Message retrieval interface πŸ’Œ

when doing operations on a filesystem configuration context, the filesystem driver may choose to provide ancillary information to userspace in the form of message strings. little notes! from the kernel! to you! πŸ’Œ

the filesystem context file descriptors returned by fsopen() and fspick(2) may be queried for message strings at any time by calling read(2) on the file descriptor. each call to read(2) will return a single message, prefixed to indicate its class:

  • e message β€” an error message was logged. this is usually associated with an error being returned from the corresponding system call which triggered this message. 😭
  • w message β€” a warning message was logged. ⚠️
  • i message β€” an informational message was logged. πŸ’¬

messages are removed from the queue as they are read. note that the message queue has limited depth, so it is possible for messages to get lost. πŸ’€ if there are no messages in the message queue, read(2) will return -1 and errno will be set to ENODATA. if the buf argument to read(2) is not large enough to contain the entire message, read(2) will return -1 and errno will be set to EMSGSIZE. (see BUGS.) πŸ›

if there are multiple filesystem contexts referencing the same filesystem instance (such as if you call fspick(2) multiple times for the same mount), each one gets its own independent message queue. this does not apply to multiple file descriptors that are tied to the same underlying open file description (such as those created with dup(2)). everybody gets their own diary, unless you're literally the same person πŸ‘―

message strings will usually be prefixed by the name of the filesystem or kernel subsystem that logged the message, though this may not always be the case. see the Linux kernel source code for details. iykyk πŸ”


🌈 RETURN VALUE 🌈

on success, a new file descriptor is returned. πŸŽ‰ on error, -1 is returned, and errno is set to indicate the error. πŸ˜”


πŸ’€ ERRORS πŸ’€

  • EFAULT β€” fsname is NULL or a pointer to a location outside the calling process's accessible address space. (she's reaching for something that isn't there πŸ™ˆ)
  • EINVAL β€” flags had an invalid flag set. 🚫
  • EMFILE β€” the calling process has too many open files to create more. πŸ—‚οΈ
  • ENFILE β€” the system has too many open files to create more. πŸ—„οΈ
  • ENODEV β€” the filesystem named by fsname is not supported by the kernel. πŸ‘»
  • ENOMEM β€” the kernel could not allocate sufficient memory to complete the operation. 😡
  • EPERM β€” the calling process does not have the required CAP_SYS_ADMIN capability. πŸ”

⭐ STANDARDS ⭐

Linux. that's it, that's the standard. 🐧


πŸ•°οΈ HISTORY πŸ•°οΈ

Linux 5.2. glibc 2.36. she's not old, she's established. πŸ’…


πŸ› BUGS πŸ›

Message retrieval interface and EMSGSIZE

as described in the "Message retrieval interface" subsection above, calling read(2) with too small a buffer to contain the next pending message in the message queue for the filesystem configuration context will cause read(2) to return -1 and set errno(3) to EMSGSIZE.

however β€” and this is the cursed part β€” this failed operation still consumes the message from the message queue. this effectively discards the message silently, as no data is copied into the read(2) buffer. gone. poof. vanished into the void. πŸ«₯πŸ’¨

programs should take care to ensure that their buffers are sufficiently large to contain any reasonable message string, in order to avoid silently losing valuable diagnostic information. big buffer = big brain 🧠✨


🧚 EXAMPLES 🧚

to illustrate the workflow for creating a new mount, the following is an example of how to mount an ext4(5) filesystem stored on /dev/sdb1 onto /mnt.

int fsfd, mntfd;

fsfd = fsopen("ext4", FSOPEN_CLOEXEC);
fsconfig(fsfd, FSCONFIG_SET_FLAG, "ro", NULL, 0);
fsconfig(fsfd, FSCONFIG_SET_PATH, "source", "/dev/sdb1", AT_FDCWD);
fsconfig(fsfd, FSCONFIG_SET_FLAG, "noatime", NULL, 0);
fsconfig(fsfd, FSCONFIG_SET_FLAG, "acl", NULL, 0);
fsconfig(fsfd, FSCONFIG_SET_FLAG, "user_xattr", NULL, 0);
fsconfig(fsfd, FSCONFIG_SET_FLAG, "iversion", NULL, 0)
fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, MOUNT_ATTR_RELATIME);
move_mount(mntfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);

first, an ext4 configuration context is created and attached to the file descriptor fsfd. then, a series of parameters (such as the source of the filesystem) are provided using fsconfig(2), followed by the filesystem instance being created with FSCONFIG_CMD_CREATE. fsmount(2) is then used to create a new mount object attached to the file descriptor mntfd, which is then attached to the intended mount point using move_mount(2). πŸ—οΈβœ¨

the above procedure is functionally equivalent to the following mount operation using mount(2):

mount("/dev/sdb1", "/mnt", "ext4", MS_RELATIME,
      "ro,noatime,acl,user_xattr,iversion");

and here's an example of creating a mount object of an NFS server share and setting a Smack security module label. however, instead of attaching it to a mount point, the program uses the mount object directly to open a file from the NFS share. (main character energy: she doesn't need a mount point to be valid πŸ’β€β™€οΈ)

int fsfd, mntfd, fd;

fsfd = fsopen("nfs", 0);
fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "example.com/pub", 0);
fsconfig(fsfd, FSCONFIG_SET_STRING, "nfsvers", "3", 0);
fsconfig(fsfd, FSCONFIG_SET_STRING, "rsize", "65536", 0);
fsconfig(fsfd, FSCONFIG_SET_STRING, "wsize", "65536", 0);
fsconfig(fsfd, FSCONFIG_SET_STRING, "smackfsdef", "foolabel", 0);
fsconfig(fsfd, FSCONFIG_SET_FLAG, "rdma", NULL, 0);
fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
mntfd = fsmount(fsfd, 0, MOUNT_ATTR_NODEV);
fd = openat(mntfd, "src/linux-5.2.tar.xz", O_RDONLY);

unlike the previous example, this operation has no trivial equivalent with mount(2), as it was not previously possible to create a mount object that is not attached to any mount point. so this is genuinely new territory and she ATE. πŸ”₯


πŸ‘‘ SEE ALSO πŸ‘‘

fsconfig(2), fsmount(2), fspick(2), mount(2), mount_setattr(2), move_mount(2), open_tree(2), mount_namespaces(7)


okay that's the whole page bestie, you're officially a filesystem-context girlie now πŸ’…πŸ¦‹

this was Linux man-pages 6.20, dated 2025-12-25 β€” yes, christmas day, she said "merry crisis, here's a syscall" πŸŽ„βœ¨ love that for us. go forth and fsopen() responsibly, and remember: big buffers, no leaks, CAP_SYS_ADMIN only when you mean it. periodt. πŸ’–

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ“œ the page's license: Linux-man-pages-copyleft
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms