SysadminNews

Linux 7.3 Merges FailFS and Drops Two 1990s File Systems

On this page
  1. A file system that refuses everything
  2. fchroot(), and why chroot() was not enough
  3. The opposite of nullfs, by the same author
  4. What breaks, and what it costs you
  5. Two file systems retired
  6. What to do about it
  7. Sources and further reading

Linux 7.3 gained a file system that refuses to do anything, and lost two that nobody had used in twenty years. In the early pull requests Linus Torvalds merged on August seventeenth, 2026, the EFS driver from SGI IRIX and the FreeVxFS driver for Veritas VxFS were deleted, roughly three thousand seven hundred lines between them. In their place came FailFS, nine hundred and thirty one lines including documentation, where every operation returns EOPNOTSUPP and the root cannot be opened at all. It arrives with a new fchroot() system call and a FD_FAILFS_ROOT sentinel, aimed squarely at processes that want to stop having a file system.

The short answer

The early Linux 7.3 pull requests merged on August seventeenth, 2026 added FailFS, a pseudo file system where every operation returns EOPNOTSUPP and the root cannot be opened, together with a new fchroot() system call and an FD_FAILFS_ROOT sentinel. The point is to let a process drop its ambient file system entirely, so the only paths that still resolve are the ones anchored at a descriptor it already holds. The same merge deleted EFS, the pre XFS format from SGI IRIX, and FreeVxFS, the read only Veritas driver.

EOPNOTSUPPreturned by every FailFS operation, statfs() included
931 linesthe whole of FailFS, documentation included
~3,700 linesdeleted with the EFS and FreeVxFS drivers
Answer card: Linux 7.3 merged FailFS, 931 lines including documentation, where every operation returns EOPNOTSUPP and the root cannot be opened even with O_PATH, alongside a new fchroot system call and the FD_FAILFS_ROOT sentinel, while the EFS and FreeVxFS drivers were deleted for about 3700 lines removed.
One file system arrived that does nothing, two left that nobody was using. PNG

Most kernel file system news is about going faster or storing more. This one is about a driver whose entire job is to say no, and it is more interesting than that description makes it sound.

A file system that refuses everything

FailFS returns EOPNOTSUPP for every operation that reaches it. That is already unusual. What makes it a real isolation primitive is how thoroughly it removes itself from view. Per the RFC, statfs() fails too, so you cannot identify the file system through a descriptor. The root cannot be opened even with O_PATH, and it declines the weak revalidate callback the VFS uses for jumped terminals, which, in Christian Brauner's words in the posting, closes every remaining way to reference it.

Then there is the ownership model. One instance is mounted during early boot through kern_mount(), which makes it logically distinct from every mount namespace on the system. It cannot be cloned, user space cannot mount it, and nothing can be mounted on top of it. There is exactly one, it belongs to nobody, and you can only ever be inside it.

The payoff is in what happens to path resolution afterwards. With your root or your working directory set to FailFS, absolute paths fail, absolute symlinks fail, and AT_FDCWD relative lookups fail. The only thing that still works is a lookup anchored at an explicit file descriptor. That flips file system access from ambient to explicit: instead of a process having the whole tree and being held back by permissions, it has nothing and holds exactly the descriptors it was given.

Checklist comparing what still works and what fails once a process has its root or working directory in FailFS: openat with a descriptor it already holds works, while absolute paths, absolute symlinks, AT_FDCWD relative lookups, opening the FailFS root and running dynamically linked binaries all fail.
Once you are in FailFS, a descriptor you already hold is the only way out. PNG

fchroot(), and why chroot() was not enough

The mechanism needs a way in that does not depend on paths, because the whole point is that paths stop working. That is fchroot(), a new system call that changes the root using a file descriptor instead of a path string. Alongside it the kernel exposes FD_FAILFS_ROOT, a sentinel descriptor value understood by both fchdir() and fchroot(), so a task can move its root and its working directory into the failing file system without ever naming it.

The pull request notes that an unprivileged task can chroot into FailFS provided it has the no new privileges flag set. That is the standard condition for handing an unprivileged process a confinement primitive, the same bargain that makes unprivileged seccomp filters and user namespaces workable, and it is what keeps this from being a root only feature that only container runtimes would ever touch.

It is worth being precise about what this is and is not. A classic chroot moves the starting point of absolute path resolution, and the process still has a tree, still resolves a path like /etc/passwd to something. FailFS removes the tree. Those are different guarantees, and the second one is much easier to reason about when you are reviewing what a helper process can reach.

The opposite of nullfs, by the same author

FailFS only makes sense next to NULLFS, which Christian Brauner landed in Linux 7.0 and which sits under the boot path of the kernel released as 7.2 on August sixteenth. NULLFS is a permanently empty, immutable directory whose lookups fail with ENOENT. Nothing is here, but the directory itself is a normal object: openable, readable, mountable upon. It exists mostly to make early boot less awkward, since pivot_root() can then place the permanent root below the temporary one instead of forcing manual cleanup of the initial ramfs, and to give kernel threads a root with no file system access at all. Brauner has been blunt about the motivation there, describing the offloading of file system work to kernel threads as a broken concept.

FailFS is the other half. NULLFS says nothing is here. FailFS says nothing is supported here. Between them you can describe almost any degree of file system deprivation a process might want, which is a genuinely useful vocabulary for anyone building sandboxes, and a natural companion to the container tooling work we covered in Podman 6.1.

What breaks, and what it costs you

One limitation is load bearing: dynamically linked binaries cannot be executed once you are in FailFS, because the dynamic loader resolves absolute paths and every absolute path now fails. If you want to use this, either your program is statically linked, or you do your execution before dropping into FailFS and treat the transition as a one way door. That is not a new discipline. It is the same shape of constraint that tight seccomp filters and the no new privileges flag already impose, and it tends to push designs toward doing all the delicate setup up front.

The cost side is small. Nine hundred and thirty one lines including documentation, with the driver itself under two hundred lines in the RFC, is close to free as kernel features go.

Two file systems retired

The same merge window deleted drivers with the opposite problem: plenty of history, no users.

FreeVxFS was the read only, open source driver for the Veritas VxFS format used by HP-UX and SCO UnixWare. The pull request removing it is direct about the reasoning, noting that providing compatibility with old school Unix systems from the 1990s was fun twenty five years ago and that today it mostly serves as fodder for automated bug checkers, with only one known user and contributor in the last fifteen years. That last part is the modern argument for removal, and it is a newer argument than it looks: unmaintained drivers now generate a steady stream of reports from automated tools and model driven bug hunters, and somebody has to triage each one. It is the same maintainer time question the Debian project has been voting on for its own tooling, arrived at from the code side.

EFS went the same way. It was the read only driver for the on disk format SGI used on IRIX before XFS, unmaintained for more than twenty years. Together the two removals took out roughly three thousand seven hundred lines.

Also in the VFS pulls, NILFS2 switched its O_DIRECT reads to the iomap infrastructure, which is the same consolidation path most file systems have been walking for years.

What to do about it

If you run production systems, nothing this week. Linux 7.3 is in its merge window, and neither the removals nor FailFS will reach you before you deliberately move to that kernel, alongside the scheduler and cluster balancing changes already queued for the release.

If you maintain anything that spawns helper processes and hands them file descriptors, FailFS is worth reading about now rather than later. The design question it answers is one most sandboxes get wrong: not how do I restrict what this process can open, but how do I make sure it can only open what I gave it. And if you still have an IRIX tape or a VxFS volume you were planning to read one day, do it on a kernel older than 7.3.

Sources and further reading

Frequently asked questions

What does FailFS actually do?

It fails. Every operation that reaches FailFS returns EOPNOTSUPP, the error code Linux uses for operation not supported, and that includes statfs(), so the file system cannot even be identified through a file descriptor. The root directory cannot be opened, not even with O_PATH, and it refuses the weak revalidate callback the VFS uses for jumped terminals, which closes the last indirect way to get a reference to it. A single instance is mounted during early boot with kern_mount(), so it exists outside every mount namespace. It cannot be cloned, it cannot be mounted from user space, and nothing can be mounted on top of it.

Why would anyone want a file system that only returns errors?

To let a process shed its file system state completely. Once your root directory or working directory is FailFS, every absolute path, every absolute symlink and every AT_FDCWD relative lookup fails immediately. The only paths that resolve are the ones anchored at a file descriptor you already hold, with openat() and friends. That turns file system access from something ambient into something explicit: a process reaches exactly the descriptors it was handed, and nothing else. Compare that with a classic chroot, where the process still has a full tree under it and still resolves absolute paths, just from a different starting point.

What is fchroot() and how is it different from chroot()?

chroot() takes a path. fchroot() takes a file descriptor, which makes changing root a first class descriptor operation rather than a path operation, and that matters when your goal is to have no working paths left. The kernel also exposes a FD_FAILFS_ROOT sentinel understood by fchdir() and fchroot(), so a task can point both its root and its working directory at the failing file system without needing a path to it in the first place. Per the pull request, an unprivileged task can chroot into FailFS provided it has the no new privileges flag set, which is the usual condition for handing an unprivileged process a confinement primitive.

How does this relate to nullfs?

They are deliberate opposites, both from Christian Brauner. NULLFS, merged for Linux 7.0, is a permanently empty immutable directory: lookups fail with ENOENT, meaning nothing is here, but the directory itself can be opened, read, inspected with stat and mounted upon. FailFS says nothing is supported here, and refuses even to be looked at. NULLFS exists largely to make boot cleaner, letting pivot_root() place the permanent root below the temporary one, and to give kernel threads a root with no file system access. FailFS is the version you point a user space process at when you want it to have no ambient file system at all.

Can I use this today, and what breaks?

Not on a stable kernel yet. This is Linux 7.3 merge window material, so the first release you could run it on is 7.3 itself, and the sentinel plus fchroot() plumbing landed as follow up work on the original RFC that Christian Brauner posted to linux-fsdevel on July twenty third, 2026. The known limitation is worth planning around: dynamically linked binaries cannot be executed from inside FailFS, because the loader resolves absolute paths and every absolute path now fails. In practice that means static binaries, or doing your execution before you drop into FailFS, which is the same discipline you already need for tight seccomp sandboxes.