open with O_CREAT - was it opened or created?
Solution 1
Based roughly on your comments, you want something along the lines of this function:
/* return the fd or negative on error (check errno);
how is 1 if created, or 0 if opened */
int create_or_open (const char *path, int create_flags, int open_flags,
int *how) {
int fd;
create_flags |= (O_CREAT|O_EXCL);
open_flags &= ~(O_CREAT|O_EXCL);
for (;;) {
*how = 1;
fd = open(path, create_flags);
if (fd >= 0) break;
if (errno != EEXIST) break;
*how = 0;
fd = open(path, open_flags);
if (fd >= 0) break;
if (errno != ENOENT) break;
}
return fd;
}
This solution is not bullet proof. There may be cases (symbolic links maybe?) that would cause it to loop forever. Also, it may live-lock in certain concurrency scenarios. I'll leave resolving such issues as an exercise. :-)
In your edited question, you pose:
I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it.
A hack-ish, but more bullet proof, solution would be to give each process a different user ID. Then, just use the regular open(path, O_CREAT|...)
call. You can then query the file with fstat()
on the file descriptor, and check the st_uid
field of the stat
structure. If the field equals the processes' user ID, then it was the creator. Otherwise, it was an opener. This works since each process deletes the file after opening.
Solution 2
Use O_EXCL
flag with O_CREAT
. This will fail if the file exists and errno will be set to EEXIST
. If it does fail
then attempt open again without O_CREAT
and without O_EXCL
modes.
e.g.
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if ((fd == -1) && (EEXIST == errno))
{
/* open the existing file with write flag */
fd = open(path, O_WRONLY);
}
Sergey
Updated on February 01, 2020Comments
-
Sergey over 4 years
I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it. Is there any robust way to find out which process actually did create the file and which did open already create file, for instance, if I want to accurately count how many times that file was opened in such scenario.
I guess I could put a global mutex on file open operation, and do a sequence of open() calls using O_CREAT and O_EXCL flags, but that doesn't fit my definition of "robust".
-
Sergey about 11 yearscan you please clarify "attempt to open again without O_CREAT". If I omit O_CREAT from your code, I'll be left with O_EXCL only, and accorting to this "In general, the behavior of O_EXCL is undefined if it is used without O_CREAT".
-
Jonathan Leffler about 11 yearsIf the O_EXCL create fails because EEXISTS, try again without the
O_EXCL
and without theO_CREAT
! Note that when you have O_CREAT,open()
takes 3 arguments; the third is the file permissions mode (0644 or similar). You also need one of O_RDONLY, O_WRONLY or O_RDWR (though omitting them is equivalent to O_RDONLY, but creating a file in read-only mode is modestly pointless). -
suspectus about 11 yearsI've amended the post which hopefully will clarify.
-
Sergey about 11 yearsok, but while the processor is executing the comments right after the if-check, and someone else opens my file, what do I do then ?
-
jxh about 11 years@Sergey: Then your problem description is not complete, so we can't answer it.
-
Jonathan Leffler about 11 years@Sergey? What's your goal? Multiple processes can have the same file open. If you use O_APPEND, you can get atomic writes at the end. Why do you care whether it existed or not that isn't handled by the options to
open()
? -
Sergey about 11 yearsOk, please help me create a complete description. I've marked the important part with bold, what else is missing ?
-
jxh about 11 years@Sergey: We don't know what problem you actually encountered. We don't know what problem you are trying to solve. We don't know what your code is trying to do. Only you can tell us that.
-
suspectus about 11 years@Sergey you will need to check that the second
open()
is successful and handle open failure appropriately. -
Sergey about 11 years@JonathanLeffler, I have 10 processes trying to open the file, which might no exist yes. I want to know, if it is the case, which one process did create the new file, and which other 9 did open existing file.
-
jxh about 11 years@Sergey: My suggestion is that you attempt to solve that problem with the answer suspectus provided. The process that succeeds with the first open knows who it is. The other nine will fail on the first open. You make code to do the rest. If there is an issue you cannot resolve on your own, bring it back in a separate question, showing the code, describe what it was supposed to do, and what it does instead.
-
Sergey about 11 yearssolution posted by @suspectus kinda works, but my concern is that it is not 100% safe. File can be deleted in-between 2 lines and then 2nd open will fail. I guess I need to mention that in original question. To make this solution be bulletproof, a retry loop needs to be introduced to guard the code against deletions.
-
Sergey about 11 yearsTo fight live-lock I'd fallback to some sort of global shared mutex after a few iterations. But then the solution becomes something I was hoping to avoid.
-
Sergey about 11 yearsI accept this answer since this is the closest to what I wanted to achieve.
-
Admin almost 5 yearsIn this case, but also in general, consider compiling your programs with
_FORTIFY_SOURCE
set to 1 which adds some checks at compile-time only.