[go: up one dir, main page]

|
|
Log in / Subscribe / Register

The curious case of O_DIRECTORY|O_CREAT

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 28, 2023 16:19 UTC (Tue) by NYKevin (subscriber, #129325)
In reply to: The curious case of O_DIRECTORY|O_CREAT by josh
Parent article: The curious case of O_DIRECTORY|O_CREAT

You don't, strictly speaking, need it. mkdirat(2) and openat(2) should be able to resolve any race condition that might otherwise be possible. Here's a possible sequence of events:

0. Thread A opens the parent directory. This may or may not race with something, but let's call it out of scope for now and just assume that it happens.
1. Thread A calls mkdirat and creates /path/to/foo/
2. Thread B renames it to /path/to/bar/ (or removes it or whatever)
3. Now if thread A tries to openat /path/to/foo/, it gets ENOENT, so it would know to start over and try again.

Alternatively:

2. Thread B renames it to /path/to/bar/
3. Thread B creates a new /path/to/foo/
4. Thread A successfully openats /path/to/foo/. It's a different directory than the one that it created, but from thread A's perspective, this makes no practical difference. One newly-created directory is just as good as another, right?

Alternatively alternatively:

3. Thread B creates /path/to/foo as a symlink to something else.
4. Thread A tries to openat /path/to/foo/, but it fails because of O_NOFOLLOW. A knows that something is wrong and aborts the operation.


to post comments

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 28, 2023 18:35 UTC (Tue) by zev (subscriber, #88455) [Link] (5 responses)

> One newly-created directory is just as good as another, right?

Not if they have different metadata (permissions or ownership, which could arise with multiple processes accessing /tmp, say). Also, is there any guarantee B even left it empty and didn't also start populating it with things that would conflict with A's plans for it?

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 29, 2023 0:46 UTC (Wed) by NYKevin (subscriber, #129325) [Link]

> Not if they have different metadata (permissions or ownership, which could arise with multiple processes accessing /tmp, say).

A can fstat it after opening it, if A cares.

> Also, is there any guarantee B even left it empty and didn't also start populating it with things that would conflict with A's plans for it?

If the permissions on the original dir allow it, then B can do this anyway. If not, then see previous reply.

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 29, 2023 2:12 UTC (Wed) by interalia (subscriber, #26615) [Link] (3 responses)

I could be wrong but I imagine that even if A managed to create the directory atomically first, process B could create files in there before A gets control again. So there's guarantee the newly created directory is empty by the time that A reads it. Most programs probably just don't care, of course, as long as the directory serves their purposes.

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 29, 2023 5:37 UTC (Wed) by NYKevin (subscriber, #129325) [Link] (2 responses)

It is possible for A to use unlinkat(2) to remove the directory immediately after creating it (with AT_REMOVEDIR). In principle, I imagine that you might be able to continue using other fooat syscalls on the unlinked directory until you close it, thus creating a "true" temporary directory, but I have not tried it.

That still doesn't work because you race against someone else opening the directory before you can unlink it, but it's a neat party trick (if it works).

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 30, 2023 2:20 UTC (Thu) by josh (subscriber, #17465) [Link]

Sadly, that doesn't work. Once you've unlinked a directory, attempting to create something in that directory produces ENOENT.

The curious case of O_DIRECTORY|O_CREAT

Posted Mar 30, 2023 6:50 UTC (Thu) by donald.buczek (subscriber, #112892) [Link]

Yes, anonymous directories would be nice to have. Of course, with `dfd = mkdirat(parentfd, optional_name, mode, O_TMPDIR)` ínstead of a non-atomic `mkdir()`, `unlinkat()` combination. But I assume, that would be far from trivial to implement, because filesystems are not prepared to handle trees of unlinked directories.

If you want a directory tree and its contents to go away by kernel cleanup when the last access is gone and have privilege, you can use a lazily unmounted mount on top of a lazily detached loop device on top of an unlinked file like this:

root@theinternet:~# fallocate -l 10G /tmp/x.dat
root@theinternet:~# losetup --find --show /tmp/x.dat
/dev/loop0
root@theinternet:~# mkfs.ext4 -q /dev/loop0
root@theinternet:~# mount /dev/loop0 /mnt
root@theinternet:~# cd /mnt
root@theinternet:/mnt# umount -l /mnt
root@theinternet:/mnt# losetup -d /dev/loop0
root@theinternet:/mnt# rm /tmp/x.dat
root@theinternet:/mnt# ls -l
total 16
drwx------ 2 root root 16384 Mar 30 08:43 lost+found
root@theinternet:/mnt# df /tmp
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 268304384 44856232 223448152 17% /
root@theinternet:/mnt# cd
root@theinternet:~# df /tmp
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 268304384 44786364 223518020 17% /

But there is no way to create this stack atomically.

Another ugliness here is, that the system will unnecessarily flush modified data during (auto-)umount.


Copyright © 2026, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds