Why does fopen() fail to open /dev/null but open() succeeds?
Asked Answered
K

2

5

On Godbolt, opening /dev/null for writing fails when you use fopen but succeeds when you use open, and I have no idea why.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    if (fopen("/dev/null", "w") == NULL) {
        perror("fopen");
    }
    if (open("/dev/null", O_WRONLY) == -1) {
        perror("open");
    }
}

https://godbolt.org/z/1Y6x58Tv6

Edit: I've since opened a bug report.

Kv answered 30/5 at 7:52 Comment(4)
Not an answer, but this post lists some differences between fopen and open (one of them being the issue of buffered I/O). Maybe it will give a hint towards an answer.Eulogium
"w" does not directly correspond to O_WRONLY, it corresponds to O_WRONLY|O_CREAT|O_TRUNC.Mcmillen
@n.m.couldbeanAI And if you update the Godbolt code to open("/dev/null", O_WRONLY | O_TRUNC | O_CREAT, 0666 ); that also fails with "Permission denied".Springfield
@AndrewHenle actually the O_CREAT flag triggers "Permission denied"Stonebroke
D
7

I mostly agree with the existing answer:

fopen( "/dev/null", "w") means "open for writing, create if it doesn't exist, then truncate to zero bytes"

By trial and error, I discovered that the problematic flag is O_CREAT — you can't create /dev/null if it doesn't exist. Go figure.

Also by trial and error: the following works:

fopen("/dev/null", "r+")

Here, "r+" means "open for reading and writing", which is a clumsy way to say "the file should exist". You don't need read permissions, so it's less than ideal.


Another way to make this work:

int fd = open("/dev/null", O_WRONLY);
FILE *f = fdopen(fd, "w");

The manual page for fdopen says

The mode of the stream ... must be compatible with the mode of the file descriptor

which is vague, but of course this has no chance to fail because of O_CREATfdopen has no access to file name so it can't try to create it.


This looks like a configuration problem on this specific system; fopen with plain "w" mode should work. So you shouldn't change the way you usually write code because of this.

Dominicdominica answered 30/5 at 8:37 Comment(4)
Nice work, but clearly something is amiss if you cannot fopen /dev/null for writing on a linux system. That's what it's there for.Kv
I agree. Added a note on this.Dominicdominica
I opened a bug report on compiler-explorers github page.Kv
Is it really strange to not have permission to write in /dev in a system that is not even yours?Keikokeil
S
6

fopen( "/dev/null", "w" ) means "open for writing, create if it doesn't exist, then truncate to zero bytes", which is not the same as open("/dev/null", O_WRONLY), which means "open for writing if it exists".

So fopen( "/dev/null", "w" ) uses something like open("/dev/null", O_WRONLY | O_TRUNC | O_CREAT, 0666 ); to either create or truncate the file, which also fails with "Permission denied" because the caller doesn't have permission on the Godbolt to truncate /dev/null.

Springfield answered 30/5 at 8:9 Comment(2)
So I take it fopen does not support the equivalent of just O_WRONLY... That seems odd... that or godbolt's permissions are overly restrictive.Kv
It's the latter; our sandboxing only lets us bindmount in on a file-by-file basis, and apparently a bind-mounted /dev/null works differently that a real one.Atomic

© 2022 - 2024 — McMap. All rights reserved.