|
|
Log in / Subscribe / Register

Reconsidering O_CREAT|O_DIRECTORY

By Jonathan Corbet
July 30, 2026
Linux provides a system call (mkdir()) to create a directory, and a few variants of open() that can open a directory. There is, however, no system call in Linux that can create and open a directory in a single, race-free call. Jori Koolstra has been working on remedying that situation, most recently by repurposing a set of open() flags that currently return an error. There are, however, concerns that show just how hard it can be to create user-space interfaces that do not present traps for application developers.

Creating and opening a directory in a single system call is simpler and more efficient than using two, of course. It also can guard against the possibility that some other process will, between the creation and open steps, replace a directory with something else. Detecting that case is possible on Linux now, but it requires some defensive programming of the type that application developers are not always good at. Thus the desire for a more straightforward way to accomplish that pair of operations.

In March, Koolstra attempted to address this problem with a patch series adding a new system call, mkdirat_fd(), that would return a file descriptor for the newly created directory. That system call was changed to mkdirat2() in a subsequent patch. There were a number of concerns about the implementation, but also about creating a new system call in the first place. Christian Brauner, in particular, thought that this problem was better solved with a modification to how open() handles a couple of existing flags.

Specifically, all of the open() variants support the O_DIRECTORY flag, which is necessary if the application is trying to open a directory rather than a normal file. Also supported is O_CREAT, which instructs open() to create the named file if it does not already exist. It would make sense to interpret the combination of those two flags as a request to create a directory and open it at the same time. Adding this capability to open(), Brauner said, would also make many of the other features of the system call, such as the ability to place restrictions on how the name is resolved, available for free. So, he concluded: "I think here it is pretty clear that O_DIRECTORY|O_CREAT is the right thing to do".

Koolstra duly implemented the new API as an RFC patch set, followed by three more RFC and three non-RFC versions; it was only after the last of those was posted that other developers started to take a serious look at the proposed API, and not all of them were convinced that it was the right approach. Pedro Falcato, in particular, argued that this API would be nearly impossible for applications to use in a portable way, given the number of different ways that O_CREAT|O_DIRECTORY has been interpreted in the past.

That story is, indeed, a bit complicated; LWN covered it in 2023. The ways in which Linux has handled an open() call with that flag combination include:

  • Older kernels would fail if the named file existed, returning ENOTDIR if it is a regular file and EISDIR if it is a directory. If, instead, nothing existed by the given name, open() would create a regular file, which seems unlikely to be what the caller wanted.
  • As of the 5.7 release in 2020, the kernel started returning an error in the last case, while still creating the file, which seemed even less likely to be what the developer was hoping for.
  • Brauner added a patch to 6.4 causing the kernel to fail with EINVAL for that flag combination in all cases.

Add in the fact that other systems supporting POSIX system calls have their own interpretation of that flag combination, Falcato said, and the result is going to be difficult to use properly:

If you're writing something that wants to be portable, or that intends to standardize on something, you have some 5 different behaviors all across the FOSS UNIX landscape, not considering everything else. It's also something that will, FWIW, probably never be included in POSIX because no one can agree on the semantics here.

TL;DR I don't see, given the reasons above, how users are supposed to use this without it being a total minefield.

He suggested that limiting the change to openat2() might be a better approach.

Brauner, though, dismissed the concern, saying: "I don't think any of this is really an argument worth considering". The behavior of that flag combination has been consistent on Linux for years, he said, so it makes sense to make better use of it now. Falcato pointed out that the older LTS kernels never received the 6.4 change and, as a result, do not have consistent behavior, and that user space would still have to perform "some pretty gnarly tests" to use the feature safely. Brauner suggested backporting the 6.4 fix, and added: "Feature testing has always been a pain that we forced onto userspace. I think we should continue with that proud tradition".

Christoph Hellwig disagreed:

We need an interface that is self discoverable on Linux. Any combination of flags that was accepted by previous kernels and gave different results than the new interface do not qualify for that.

He added that it was not possible to rely on a fix being backported to all of the old kernels in use.

Neil Brown suggested adding a new flag, OPENAT2_NEW_COMBINATION, that would cause openat2() to reject any flag combination that is not recognized. Any older kernels will immediately fail a call with that flag; new kernels can use it to enable combinations that were not previously supported, including O_CREAT|O_DIRECTORY. Brauner, though, once again dismissed Hellwig's concern as "a strawman". The fact that he was able to make that flag combination return an error without generating a single regression report, he said, was evidence that there is no risk to adding meaning to it now. Hellwig disagreed, saying that, once the combination is supported, applications will start using it, and they will break on older systems. It is, he said, necessary to add "APIs that don't accidentally do the wrong thing on any old kernel".

Koolstra had a different concern about the openat2() suggestion: seemingly a lot of seccomp() configurations still block openat2(). That could make the new feature unavailable on a lot of systems, reducing its value.

Toward the end of the discussion (so far), Brauner asked Koolstra to send a version with support in openat() and openat2(). He did not say whether the change to open() should remain, but complained that "we're deliberately crippling a useful extension for userspace". The latest version of the series from Koolstra enables that flag combination for all open() variants without any additional checks. Brauner has not yet applied that series, so we do not yet know if it will find its way into the mainline in that form or not.

Maintaining compatibility over the long term is not an easy task. It is hard enough to ensure that new kernels do not break older applications, but it can often be trickier to avoid breaking newer applications on older kernels. One of the key ways to do that is to provide ways for applications to discover whether a given feature is supported by the kernel or not. The disagreement here is over whether the proposed feature provides that discovery mechanism, with developers like Falcato and Hellwig saying "no", while Brauner feels that it is discoverable on all kernels that actually matter. There is only one chance to make the correct decision; once the new feature is exposed in a kernel release, it will be difficult to change thereafter.

Index entries for this article
KernelDevelopment model/User-space ABI
KernelSystem calls/open()


to post comments

How about O_CREAT_DIRECTORY?

Posted Jul 30, 2026 14:10 UTC (Thu) by davecb (subscriber, #1574) [Link] (13 responses)

At the expense of suggesting something overly simple, what about O_CREAT_DIRECTORY as a new option, with a very specific interpretation?

How about O_CREAT_DIRECTORY?

Posted Jul 30, 2026 14:16 UTC (Thu) by corbet (editor, #1) [Link] (12 responses)

One problem, as always, is that open() does not check its flags for validity, so it is almost impossible to add new ones in a safe way.

Besides, we would want O_CREATE_DIRECTORY, it's not the 1970s anymore :)

How about O_CREAT_DIRECTORY?

Posted Jul 30, 2026 16:03 UTC (Thu) by mathstuf (subscriber, #69389) [Link] (1 responses)

> Besides, we would want O_CREATE_DIRECTORY, it's not the 1970s anymore :)

Not to mention that "spelling `creat` with an 'e'` was one thing that Ken Thompson would have changed about Unix given a redo.

https://en.wikiquote.org/wiki/Kenneth_Thompson

How about O_CREAT_DIRECTORY?

Posted Aug 2, 2026 9:57 UTC (Sun) by wjb (subscriber, #71810) [Link]

It's a feature of fixed length naming conventions (or strings) that the length being one character longer would have been better.

How about O_CREAT_DIRECTORY?

Posted Jul 30, 2026 16:05 UTC (Thu) by ejr (subscriber, #51652) [Link]

I still work in the BLAS/LAPACK community. Keeping the olde names is a point of pride to some. ;)

How about O_CREAT_DIRECTORY?

Posted Jul 30, 2026 20:28 UTC (Thu) by alx.manpages (subscriber, #145117) [Link] (1 responses)

> Besides, we would want O_CREATE_DIRECTORY, it's not the 1970s anymore :)

Yes, and no. I think consistency is more important.

While creat(2) is a well-known example of what we would have all done different with a time machine, now that we have O_CREAT, if we're adding a new flag that is a sibling of it, it should be O_CREAT_xxx. That way, users can immediately expect that it does essentially the same as O_CREAT, with the minor difference related to the trailing of the name. Otherwise, one could legitimately wonder why it's CREATE instead of CREAT, and be puzzled about whether it's be different in any other ways.

That said, I hope `O_DIRECTORY|O_CREAT` passes. I agree with Brauner that given that no regressions have appeared when changing to EINVAL means that there are no existing users, and that means EINVAL could be backported to all LTS kernels, thus resulting in either correct behavior or an error, but not incorrect behavior.

It'd be interesting to know what the BSDs and other FOSS Unix systems do with this combination.

How about O_CREAT_DIRECTORY?

Posted Jul 30, 2026 21:15 UTC (Thu) by alx.manpages (subscriber, #145117) [Link]

Quoting the previous LWN article (referenced in the article), these are the behavior of the BSDs:

> Falcato did some research on what other systems do in response to that combination of flags. NetBSD, it seems, will simply fail an open() call in that situation, returning EINVAL. FreeBSD, instead, will allow the call to succeed if the path exists and is a directory; otherwise it will fail. He also noted that all of the behaviors seen — Linux pre- and post-5.7, NetBSD, and FreeBSD — are allowed by POSIX: ""I would not call the old Linux behavior a *bug*, just really odd semantics"".

(And unrelated, but as a comment to the last sentence from Falcato above):

FWIW, "really odd semantics" are a bug, IMO. A bug that conforms to POSIX is still a bug.

How about O_CREAT_DIRECTORY?

Posted Jul 31, 2026 6:17 UTC (Fri) by lmartelli (subscriber, #11755) [Link] (6 responses)

It makes me wonder why did they choose O_DIRECTORY rather than O_DIR ?

How about O_CREAT_DIRECTORY?

Posted Jul 31, 2026 8:12 UTC (Fri) by pm215 (subscriber, #98099) [Link] (5 responses)

O_DIRECTORY is a much later addition, and presumably done when there was much less cultural pressure towards abbreviations in naming. In 7th edition unix the only valid mode flags were 0, 1 and 2 for read, write and readwrite, and the manpage documents them as magic numbers without any named symbols provided. By 2.10BSD the O_ names have been defined, but O_DIRECTORY is not yet among them, and it's not in 4.4BSD either.

How about O_CREAT_DIRECTORY?

Posted Jul 31, 2026 18:35 UTC (Fri) by huntermatthews (subscriber, #4490) [Link] (4 responses)

It wasn't _just_ cultural pressure - some early linkers had a 6 char limit. "O_CREAT" is 6 chars. My memory says the linker only cared that the first 6 chars were unique and you could have identifiers longer than that - but maybe that was a half way mark?

My understanding is that this was corrected or worked around by the late 70's.

HOWEVER, I was still dealing with scientific software in C in the mid 00's that had broken six char clean C pre-processor macros.
The answer was to turn them all off, but thats a different issue.

How about O_CREAT_DIRECTORY?

Posted Jul 31, 2026 19:48 UTC (Fri) by farnz (subscriber, #17727) [Link]

My understanding is that this all comes down to IBM SQUOZE and DEC RADIX 50 (and I suspect that DEC RADIX 50 was what influenced early UNIX, since it started on a PDP-7, an 18-bit DEC machine).

Those encodings get you 6 characters and a few flag bits in two 18-bit machine words, which is useful for both linkers and preprocessors alike; they were commonly used on the PDP-7 (and a slight variant on 16-bit minis like the PDP-11) for linkers, and almost certainly explain where the 6 significant character limit comes from - you squish the first 6 characters into a fixed 32 bit (on PDP-11) or 36-bit (on PDP-7) word, making lookup etc much simpler.

How about O_CREAT_DIRECTORY?

Posted Jul 31, 2026 23:45 UTC (Fri) by pm215 (subscriber, #98099) [Link] (1 responses)

O_CREAT is seven chars :) It also didn't arrive until 2.10BSD or so, which also added longer identifiers like O_APPEND and O_RDONLY. It's obviously named for the older creat() syscall.

For that matter, creat is also not six characters. Even back in the PDP-7 Unix that is as early as the tuhs website has, there are some six character syscall names, like rename. The linker being used on that PDP-7 seems to have had a "truncates to 8 chars" limit, as symbols like ttyrestart come out as ttyresta in the linker map; the convention seems to have been a leading '.' on syscall entrypoints, but that still leaves 7 chars. So it seems to me that while you're right that some older systems definitely did have very short limits, at the point when Thompson and Ritchie were defining unix syscall names, the choice of "creat" over "create" was not imposed by a technical limit but was rather "we like and are used to abbreviated names".

How about O_CREAT_DIRECTORY?

Posted Aug 3, 2026 7:45 UTC (Mon) by anselm (subscriber, #2796) [Link]

at the point when Thompson and Ritchie were defining unix syscall names, the choice of "creat" over "create" was not imposed by a technical limit but was rather "we like and are used to abbreviated names".

Remember that these are the folks who brought you ls, cp, and rm. If all you have to interact with your computer is an actual teletype (with a roll of paper rather than a CRT display), brevity becomes very enticing.

Also, people were presumably used to the idea of identifiers with at most six significant characters from Fortran.

How about O_CREAT_DIRECTORY?

Posted Aug 4, 2026 12:49 UTC (Tue) by dankamongmen (subscriber, #35141) [Link]

how is a linker implementation detail relevant for a macro? O_CREAT is not an object symbol.

O_DIRECTORY|O_TMPFILE

Posted Jul 31, 2026 5:57 UTC (Fri) by donald.buczek (subscriber, #112892) [Link] (20 responses)

I'd love to have O_DIRECTORY|O_TMPFILE but I know its impossible, because the filesystems can't support that easily. Transactions, ordering, isloations modes and ponies were nice, too. All that working over nfs of course. Santa?

O_DIRECTORY|O_TMPFILE

Posted Jul 31, 2026 11:32 UTC (Fri) by neilbrown (subscriber, #359) [Link] (19 responses)

"impossible" is a strong word.
I don't see why we couldn't come up with implementable semantics if we had a clear justification and no easy alternate approach.

Presumably you particularly want the dir tree to disappear when the root is no longer open. That would be an interesting challenge but I suspect a tmpfs implementation wouldn't be too intrusive.

What is your compelling use case?

O_DIRECTORY|O_TMPFILE

Posted Jul 31, 2026 19:23 UTC (Fri) by donald.buczek (subscriber, #112892) [Link] (18 responses)

Presumably you particularly want the dir tree to disappear when the root is no longer open.

Yes, the guaranteed cleanup by the system would be the main point of this wish. Additionally, it would be convenient if you were able to create a temporary directory without the need to address name clashes and races.

That would be an interesting challenge but I suspect a tmpfs implementation wouldn't be too intrusive.

That would be nice to have, although I also have use cases for persistent filesystems like the root filesystem or big, secondary filesystems.

What is your compelling use case?

I think about all the bash scripts which use mktemp and trap, which is unreliable. I imagine a wrapper to create an unlinked directory and call a command (script) maybe with cwd in it or with TMPDIR or another environment variable pointing to /proc/self/fd/N for the scripts usage. The script could just use that directory for all its scratch files including calling external tools (e.g. git clone) without the need to address cleanup or name collisions in any way. Yes, the wrapper could do the cleanup, too, but this would also not be reliable. For example, the wrapper could be killed by the user or the users session manager before completing the cleanup. Even if you make the wrapper privileged or a us a privileged service, you still would have the cleanup problem if the system goes down (not on tmpfs, though).

I think you don't necessarily need a script to be called from a wrapper, the script could also make use of the feature by its own code and a helper program: For example, bash could use coproc to run an external program which creates the directory, echos the fd-number to stdout and than waits forever or for a reply. bash would read the fdnum, open the directory via /proc/PID/fd/N itself and than kill the coproc or tell it to finish.

For the system disk usage: We distribute system software by sending tar archives to clients. These tar archives are extracted to a temporary directory on the system disk and than the files are moved into place.

For the usage on real on-disk filesystems: Just programs which need lot of scratch space that don't fit into a memory filesystem.

We have more interest in non-tmpfs solutions, because we operate multi-user systems. We don't want users to exhaust memory by using /tmp , so our /tmp is not tmpfs. Users should use "real" filesystems for temporary data and we hope that the filesystem caches partly compensates for the performance loss.

O_DIRECTORY|O_TMPFILE

Posted Aug 2, 2026 3:01 UTC (Sun) by koverstreet (subscriber, #4296) [Link] (9 responses)

Given how we've moved towards referencing directories by file handle, it's not a crazy ask, and it would solve real problems.

It'd create others, though: unlinked files are a problem for observability; if you forget to kill something, or a program is doing something unexpected, it makes "where'd my disk space go" into a real game.

Private mount namespaces can create similar problems.

So, as the bcachefs maintainer: I'd be happy to take patches or implement O_DIRECTORY|O_TMPFILE in bcachefs - after we have a solution to that one.

It'd need major cooperation between the VFS and the filesystem. The filesystem would have to implement rm -rf in .evict(), and that means on disk format changes because unlinked files have to be processed in recovery, and an unlinked file is not quite the same thing as an unlinked directory.

You'd need the VFS to track when the tree has gone away, and the dcache would have to be able to operate on disconnected subtrees. The VFS does this sort of tracking today only in the context of a mount, not a random disconnected directory, to my knowledge.

O_DIRECTORY|O_TMPFILE

Posted Aug 2, 2026 5:37 UTC (Sun) by donald.buczek (subscriber, #112892) [Link] (1 responses)

It'd create others, though: unlinked files are a problem for observability; if you forget to kill something, or a program is doing something unexpected, it makes "where'd my disk space go" into a real game.

Yes, but we already have that with unlinked regular files.

So, as the bcachefs maintainer: I'd be happy to take patches or implement O_DIRECTORY|O_TMPFILE in bcachefs - after we have a solution to that one.

Would anyone other than you be able to do that? There is no point even asking my employer to release me from work for the last few years leading up to retirement. :-)

You'd need the VFS to track when the tree has gone away, and the dcache would have to be able to operate on disconnected subtrees.

So I think an implementation for tmpfs, which already has its own independent value, would indeed be a good first target and an implementation on a persistent filesystem might come after that.

O_DIRECTORY|O_TMPFILE

Posted Aug 2, 2026 7:33 UTC (Sun) by koverstreet (subscriber, #4296) [Link]

> Would anyone other than you be able to do that? There is no point even asking my employer to release me from work for the last few years leading up to retirement. :-)

Your estimate is a couple years for figuring out an API for tracking and viewing unlinked-but-open files? Yeesh.

I get pretty scathing when people pile on complexity without thinking about how it'll be debugged. There's too many opaque black boxes already in the kernel.

My rule is, before you pile something on, you need to make sure what you already have is understandable.

O_DIRECTORY|O_TMPFILE

Posted Aug 3, 2026 6:48 UTC (Mon) by neilbrown (subscriber, #359) [Link] (6 responses)

The filesystem would have to implement rm -rf in .evict(), and that means on disk format changes because unlinked files have to be processed in recovery, and an unlinked file is not quite the same thing as an unlinked directory.

The "rm -rf' in .evict() would be lazy I think and could proceed breadth-first rather than the depth-first that "rm -r" does. It would do a "readdir" and attach each directory to the list of unattached directories, and schedule a truncate of each file however that normally happens. The unattached directories would be recorded however unattached files are (and orphan list?) and on recovery you wouldn't need to process them immediately, but simply add them to the list of unattached directories. Then let the lazy cleanup continue.

I think this might be sufficiently generic that the VFS could provide code to handle it.

Then we could add at AT_RECURSIVE flag to be used with AT_REMOVEDIR in unlinkat(). Wouldn't that be fun!

O_DIRECTORY|O_TMPFILE

Posted Aug 4, 2026 16:54 UTC (Tue) by koverstreet (subscriber, #4296) [Link] (5 responses)

Well, if you've got an unlinked directory with files in it, the filesystem has to handle that in recovery. But breadth first is a good point, that does simplify.

And I like your AT_RECURSIVE|AT_REMOVEDIR idea. I think there's actually no need for unlinked-but-open directories, it's the rm -rf on close that we want; that means we don't have open but invisible files and it might even be possible to use in bash scripts. Done that way, maybe it could be a purely VFS thing.

O_DIRECTORY|O_TMPFILE

Posted Aug 4, 2026 23:16 UTC (Tue) by neilbrown (subscriber, #359) [Link] (4 responses)

I think there's actually no need for unlinked-but-open directories, it's the rm -rf on close that we want

"man 2 open" identifies 4 important aspects of O_TMPFILE:

  1. are automatically deleted when closed;
  2. can never be reached via any pathname;
  3. are not subject to symlink attacks; and
  4. do not require the caller to devise unique names.

I think these are all important. However this is the VFS view, not the internal FS view. For example, I imagine a filesystem could implement these just like regular directories which are subdirs of the root, but with names that contain a "/'. .iterate_shared() would skip over these. This would answer 2 and 3. The fs would choose a unique name (sprintf(s, "/%d", i->i_ino) which would answer 4. Then you just need the "rm -rf" on close which the VFS could handle most of.

Maybe the fs would provide:

  • .tmpdir() which does .mkdir() in the root directory with a synthesized name
  • .mvtmp() which take a dentry and moves it to the root with a sythesized name
  • .iterate_tmp() which is like iterate_shared() but returns dentries for the hidden objects, which can be passed to .unlink() or .rmdir()

The VFS could have code that uses iterate_tmp() and everything that it finds which isn't open is unlink()ed if a file, rmdir()ed if an empty directory, or iterate_shared() with everything passed to .mvtmp() (or maybe unlink()). This code would be called at mount time and whenever a tmp file/dir is closed for the last time.

So yes, the FS doesn't need to have a concept of unlinked-but-open, but the VFS must have access to "open, but no link that is visible/accessable".

O_DIRECTORY|O_TMPFILE

Posted Aug 5, 2026 2:34 UTC (Wed) by koverstreet (subscriber, #4296) [Link] (3 responses)

Why is "can not be reached via any pathname" important, though?

It breaks the recursively enumerable property of filesystems; i.e. I can recursively enumerate to find all the consumed resources. I'm not a fan of private mount namespaces for the same reason :)

And considering that the implementation you're proposing has them having dirents anyways - to be that's another reason to consider just skipping the "unlinked directories idea".

> For example, I imagine a filesystem could implement these just like regular directories which are subdirs of the root

Not of the root, they'd need to be subdirectories of the directory that userspace specified. Project quota in practice already inherits from the parent directory on other filesystems, and on bcachefs IO path options inherit from the parent directory.

O_DIRECTORY|O_TMPFILE

Posted Aug 5, 2026 3:34 UTC (Wed) by neilbrown (subscriber, #359) [Link] (2 responses)

Why is "can not be reached via any pathname" important, though?

I suspect that is primarily about supporting "are not subject to symlink attacks", though that ends up meaning a bit more with directories.

I think the key idea is that no other process has any write capability to anything in the "TMP" file or tree, so that it is easier to write safe code that cannot be subverted. I suspect read access is not so important - normal permissions should be enough. Not giving a name to something is a simple way to avoid anyone getting any capability at all.

If we were to add an FMODE flag which denies write access and is inherited through openat() and friends, then I think it would be safe to allow these TMP dirs to have a name as long as the new FMODE flag was imposed whenever a path lookup crossed into a TMP dir via the name. It isn't a obviously-secure as not having a name, but maybe it is close enough.

Of course unlinked things have names via /proc/$PID/fd/$FD so maybe having a name in the fs doesn't reduce access.

Not of the root, they'd need to be subdirectories of the directory that userspace specified.

Sure, put them wherever you like as long as they can be found with .iterate_tmp(). I was just presenting a proof-of-concept approach.

Project quota in practice already inherits from the parent directory on other filesystems, and on bcachefs IO path options inherit from the parent directory.

When you create the tmp directory inode it would certainly make sense to inherit from the given parent. Whether it then needs to be linked from that parent or not would depend on internal implementation details of the FS.

O_DIRECTORY|O_TMPFILE

Posted Aug 5, 2026 18:18 UTC (Wed) by koverstreet (subscriber, #4296) [Link] (1 responses)

> I think the key idea is that no other process has any write capability to anything in the "TMP" file or tree, so that it is easier to write safe code that cannot be subverted. I suspect read access is not so important - normal permissions should be enough. Not giving a name to something is a simple way to avoid anyone getting any capability at all.

Well, I think you described the real issue right there, a shared world writeable /tmp was never a good idea :) We've been going through contortions and pointing at symlinks, but really that was the biggest footgun. It boggles my mind that a world writeable /tmp is still the default for most code, that one is plenty fixable.

Similarly with programs invoked by sudo and unprepared for all the ways that they can be maliciously pointed at something they shouldn't write to. There's a better solution to that one, but we've been stuck with the Unix security model for too long and it's become too ingrained.

The question is if it's even possible to bring a better security model to Linux, and I'm not sure about that one. The biggest thing that needs fixing is that users and groups are integer namespaces when they really should be heirarchical; Plan 9 representing users and groups as strings everywhere was a step in the right direction, and extending that and making users paths gets you something much saner than Linux namespaces. And there's been a lot of development on capability based systems, but it's hard to see doing that well in a system where APIs are still C.

Just consider this article, where a lot of bits and brain cycles are being devoted to "can we extend bitflags to represent a new mode without screwing something else up". That sort of thing is a complete non issue when you've got algebraic data types - this clearly wants to be a Rust style enum.

We're in an awkward moment in the history of systems engineering... things should be getting a whole lot better, cleaner, easier to reason about over the next decade, if we can put in the work to clean stuff up and make the Rust transition happen (among other things). But it's going to be a lot of tedious grindy work and messy transitions before we get there and can start doing the genuinely fun and interesting stuff.

This thread is of real interest to me because Valve keeps nagging me about "can we get better filesystem interfaces to solve stuff that's been a giant pain in the backside of userspace for decades?", and I see this thread and O_DIRECTORY|O_TMPFILE and my first reaction is "OF COURSE WE WANT THAT! WE'VE WANTED THAT FOR YEARS!".

But then by the time monthly meetings roll around I have to put on my hard nosed realist hat and tell them "yes, that's a really good idea and something we have all the pieces to give you, but right now I have to focus on getting in-kernel Rust out the door and getting that transition underway - what you want is going to be much cleaner and more straightforward once that's done".

Anyways, filing this away for who knows when :)

O_DIRECTORY|O_TMPFILE

Posted Aug 17, 2026 9:27 UTC (Mon) by taladar (subscriber, #68407) [Link]

I am not sure introducing yet another hierarchy is such a great idea. Hierarchies tend to be bad for composition. We have seen that with inheritance-based interface systems in languages like C++. Something like Rust's traits compose much better and in the security domain in particular we have use cases for that too.

One simple use case that hierarchies are bad at comes up when you need to look at the same objects from different angles, e.g. organisational structure in one case, physical limits in another. Say you want to give everyone who works under manager x certain permissions, everyone with a certain seniority certain permissions and everyone who works in a specific building certain permissions. Those could all include the same person but you can't have them in one hierarchy.

In practice I have encountered this e.g. with cgroups when trying to setup shared memory limits for systemd-oomd for systemd --user units owned by different users (the sum of memory used by the processes of a specific unit across all users shouldn't exceed a certain limit, e.g. PHP-FPM pools for all vhosts).

O_DIRECTORY|O_TMPFILE

Posted Aug 2, 2026 14:47 UTC (Sun) by ebiederm (subscriber, #35028) [Link] (7 responses)

There is already something similar in the VFS to handle the case where a mount point is unlinked under you, and processes are still using that directory.

I expect an implementation of O_DIRECTORY|O_TMPFILE could reuse that code to get the dcache details correct.

O_DIRECTORY|O_TMPFILE

Posted Aug 2, 2026 15:15 UTC (Sun) by jkoolstra (subscriber, #180288) [Link] (6 responses)

You cannot do this in this fashion since we have:

#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)

This is a terrible hack that we unfortunately have to live with :')

O_DIRECTORY|O_TMPFILE

Posted Aug 3, 2026 5:56 UTC (Mon) by donald.buczek (subscriber, #112892) [Link]

#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)

This is a terrible hack that we unfortunately have to live with :')

Ouch!

O_DIRECTORY|O_TMPFILE

Posted Aug 3, 2026 6:37 UTC (Mon) by neilbrown (subscriber, #359) [Link] (4 responses)

You cannot do this in this fashion since we have:

#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)

True, but opening with O_CREAT | O_TMPFILE | O_DIRECTORY could work. That combination is currently illegal and has been since O_DIRECTORY was added to O_TMPFILE.

That isn't exactly elegant, but isn't as inelegant as combining O_DIRECTORY with O_TMPFILE.

(I would have added O_EXCL to __O_TMPFILE as old kernels would return -EEXIST if O_TMPFILE we given but not understood).

O_DIRECTORY|O_TMPFILE - sample code

Posted Aug 10, 2026 2:48 UTC (Mon) by neilbrown (subscriber, #359) [Link] (3 responses)

I decide to play and wrote some code which implements this idea for tmpfs.
It is in the TMPDIR branch of my "linux" tree on github.

https://github.com/neilbrown/linux/tree/TMPDIR

There is a .c file in the root for testing.

No promises - it might eat your data.

O_DIRECTORY|O_TMPFILE - sample code

Posted Aug 10, 2026 9:07 UTC (Mon) by donald.buczek (subscriber, #112892) [Link] (2 responses)

I decide to play and wrote some code which implements this idea for tmpfs.

Oh, just wow. This alone has so much value that I might include it as an out-of-tree patch if it doesn't go upstream.

buczek@dose:/scratch/local2/linux (x)$ df -k /dev/shm
Filesystem     1K-blocks  Used Available Use% Mounted on
tmpfs            5096620     0   5096620   0% /dev/shm
buczek@dose:/scratch/local2/linux (x)$ ./tmpcd /dev/shm /usr/bin/bash
shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
buczek@dose:/scratch/local2/linux$ cp -a /scratch/local2/linux/Documentation .
buczek@dose:/scratch/local2/linux$ df -k /dev/shm
Filesystem     1K-blocks  Used Available Use% Mounted on
tmpfs            5096620 81904   5014716   2% /dev/shm
buczek@dose:/scratch/local2/linux$ exit
exit
buczek@dose:/scratch/local2/linux (x)$ df -k /dev/shm
Filesystem     1K-blocks  Used Available Use% Mounted on
tmpfs            5096620     0   5096620   0% /dev/shm

O_DIRECTORY|O_TMPFILE - sample code

Posted Aug 12, 2026 13:19 UTC (Wed) by donald.buczek (subscriber, #112892) [Link] (1 responses)

You can avoid the bash warnings by setting the PWD environment variable to "/proc/PID/fd/FD" in the wrapper. However git, for example, uses getcwd() from glibc (not the system call) which translates "(unreachable)" names to errors. So my hope, that a script could use any external tool, is not fulfilled. Still very useful, though.

buczek@dose:/scratch/local2/linux (x)$ ./tmpcd /dev/shm /bin/bash
buczek@dose:/proc/837/fd/3$ pwd
/proc/837/fd/3
buczek@dose:/proc/837/fd/3$ /bin/pwd
/bin/pwd: couldn't find directory entry in ‘..’ with matching i-node
buczek@dose:/proc/837/fd/3$ git status
fatal: Unable to read current working directory: No such file or directory
buczek@dose:/proc/837/fd/3$ strace git status 2>&1 | fgrep getcwd
getcwd("(unreachable)/", 129)           = 15
getcwd("(unreachable)/", 129)           = 15
buczek@dose:/proc/837/fd/3$ ltrace git status 2>&1 | fgrep getcwd
getcwd(0x7de8a0, 129 <unfinished ...>
<... getcwd resumed> )                           = nil
getcwd(0x7de8a0, 129 <unfinished ...>
<... getcwd resumed> )                           = nil

O_DIRECTORY|O_TMPFILE - sample code

Posted Aug 13, 2026 1:47 UTC (Thu) by neilbrown (subscriber, #359) [Link]

You can avoid the bash warnings by setting the PWD environment variable to ...

You can also avoid the bash warnings by actually mounting the directory. You would want to do that in a private namespace to keep the directory private. The namespace would be torn down on exit which would clean up the mount and the temp directory.

Where to mount it? I would designate a place which is otherwise unused where anyone can mount a private directory in a private namespace. Maybe /run/scratch ??

I've pushed out some changes to github which you can experiment with.

still on 6.1

Posted Aug 3, 2026 7:54 UTC (Mon) by grmnsftphr (subscriber, #178591) [Link]

Still on 6.1 and civil infrastructure even on 5.x. Not that I like the situation but it is the IRL situation.


Copyright © 2026, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds