This question already has answers here:
Why does open() create my file with the wrong permissions?
(7 answers)
Closed 5 years ago.
I made a little project some weeks ago, but it somehow didn't work anymore recently (or at least, not like it previously worked).
It had to create a file with open(), and fill it with some content.
[...]
int fd=open(filename, O_RDWR | O_CREAT);
/* write content */
close(fd);
[...]
The problem was just that it recently didn't create the file with the right permissions anymore (it was created with 0110 when the problem was occurring)
I now know that I just have to specify the permissions like this :
int fd=open(filename, O_RDWR | O_CREAT, 0700); /* (for instance) */
But the project still worked on some computers (Didn't work on OSX, but did work on a Linux, on which it was created with 0640, so it still worked because I still had reading permission).
So here is my question:
How are those default permissions defined for open() function at file creation?
(If I don't explicitly pass it to my open() call as a parameter.)
There is no default. You must specify them when you use O_CREAT.
According to the documentation on my system,
[The mode] argument must be supplied when O_CREAT is specified in flags
(Emphasis mine)
This makes it sound like it's undefined behaviour when you don't, and I do indeed get junk when I omit it (--wS--S--T).
You need to specify the mode arguments as an integer containing a set of bit flags. Something like:
int fd=open(filename, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG);
See the man page for chmod. The call also uses the process umask to limit things.
Related
I know that open(filepath, O_WRONLY|O_EXCL) will fail if filepath exists, but does it return anything?
Now say if I want to know if a file exists, and would like to print a message if it does, should I include the O_EXCL flag in the open() command above?
Edit: I guess I made a mistake, and I was supposed to use open(filepath, O_CREAT|O_EXCL)
open(filepath, O_WRONLY|O_EXCL) will not fail if filepath exists, it will fail if it does not exist or if you do not have write access.
O_EXCL should only be used with O_CREAT and an extra argument must then be passed to specify the mode bits for the file to create:
int hd = open(filepath, O_WRONLY | O_CREAT | O_EXCL, 0644);
hd will have a negative value in case of failure and errno will be set to indicate the error cause. Use perror() to report the failure with an explicit error message.
You can also test file existence and write access with access(), but it is not appropriate for your use case as the file could be created by a concurrent process between the test with access and the call to open (among other reasons).
An important note from the manual:
In general, the behavior of O_EXCL is undefined if it is used
without O_CREAT. There is one exception: on Linux 2.6 and
later, O_EXCL can be used without O_CREAT if pathname refers
to a block device.
So barring one use case, your command invokes undefined behavior.
If open fails for any reason, it will return -1 and set errno to indicate the cause. The details are in your open(2) manual page. Type man 2 open in any Unixy system.
from the MAN page for open():
O_EXCL Ensure that this call creates the file: if this flag is speci‐
fied in conjunction with O_CREAT, and pathname already exists,
then open() fails with the error EEXIST.
When these two flags are specified, symbolic links are not fol‐
lowed: if pathname is a symbolic link, then open() fails regard‐
less of where the symbolic link points.
In general, the behavior of O_EXCL is undefined if it is used
without O_CREAT. There is one exception: on Linux 2.6 and
later, O_EXCL can be used without O_CREAT if pathname refers to
a block device. If the block device is in use by the system
(e.g., mounted), open() fails with the error EBUSY.
Therefore, your premise that it fails if the file already exist is not quite correct.
However, when it fails: (per the MAN page)
open(), openat(), and creat() return the new file descriptor, or -1 if
an error occurred (in which case, errno is set appropriately).
I'm a beginner in C, have a question about the flags and mode paramaters in open file function in C
so C's open function is :
int open(char *filename, int flags, mode_t mode);
and some macros for the flags are:
O_RDONLY: Reading only
O_WRONLY: Writing only
O_RDWR: Reading and writing
and the mode bit is something like:
What I don't understand is,
let say we have a open function as:
fd = Open("foo.txt", O_RDONLY, S_IWOTH);
so O_RDONLY specifies that we can only read the file, but S_IWOTH specifies that anyone can write this file, isn't that they contradict to each other?
The flags decide the properties to be applied during opening of this file at this time (let's call this the "session") - this affects what you can do with the file while it's open (or, more correctly, what you can do with the file descriptor).
The mode decide the properties of the file should it be created as part of the opening process - this affects how anyone can open the file in future.
Your specific example (albeit with the correct open rather than Open):
fd = open("foo.txt", O_RDONLY, S_IWOTH);
is not really relevant since the file won't be created without the O_CREAT flag(a).
However, had you supplied O_CREAT, it's perfectly acceptable to create the file allowing anyone to write to it, but have it opened for this session in read-only mode.
(a) Some systems have other flags which may create the file under some circumstances. For example, Linux has the O_TMPFILE flag.
I wrote the following C code to open a non-existent file.
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
int main(){
int fd = open("test.c",O_WRONLY | O_CREAT);
printf("%d\n",fd);
close(fd);
}
Although the umask is set to 0002 when I run the ls -l command, I get the following output for the file I created.
-r--rws--T 1 urohit011 urohit011 0 Feb 14 22:35 test.c
The access mode changes when I run the code with a new file name. I have two questions here.
Shouldn't the default access mode of that file be 664 since the umask
is 0002 ?
Why the access mode changes when the code is run with a new file
name ?
The mode / permission bits are specified by the third argument of open call. You do not provide that argument and that is a silent programming error when O_CREAT is used:
This argument (mode) must be supplied when O_CREAT or O_TMPFILE is specified in flags; if neither O_CREAT nor O_TMPFILE is specified, then mode is ignored.
Shouldn't the default access mode of that file be 664 since the umask is 0002
Default mode must be explicitly provided:
open("test.c",O_WRONLY | O_CREAT, 0666)
^^
Why the access mode changes when the code is run with a new file name ?
open is a variadic function that accepts 2 or more arguments:
int open(const char* file, int flag, ...);
Hence, the third argument of the function gets initialized with an indeterminate value if no value is provided. There is no compiler error if only 2 arguments are provided. On the other hand, it is not an error to always provide the third argument to open call.
The access mode on a file created must be specified on the open(2) call as the third parameter to it. Because of the way C does things, that third parameter (allowed depending on flags used in the second parameter) makes the open(2) call variadic, and the compiler doesn't check if you have provided the right number of parameters.
Due to this, if you don't pass a third parameter, it will not be pushed onto the stack by the calling code, but the function will use whatever is there in the position for the third parameter. You got a Undefined Behaviour issue, with the best result being getting a new file with the wrong access mode. You can get a crash if the open(2) routine modifies the third parameter.
The access mode you provide will be finally modified by the umask bits to get the final mode of the created file.
I want:
file to be created if it does not exist, not overwritten if it does.
to read and write and fseek where ever I want
and I can not find out valid mode — "w+", "rw" "rwb+" "r+b" "w+b" "a+" or what?
The basic level 'open' that performs well is:
int fd =open("fname", O_RDWR | O_CREAT, 0666);
but I would like to know fopen alternative.
Every mode letters [w, r, a, +] combination I tried will either overwrite contents, or fseek-fwrite not write where it should. "a+" will always append no matter what fseek sets... "rw+" works ok, but does not create nonexistent file ...etc.
Update: to clarify why for example "a+" is NOT a solution:
#include <stdio.h>
int main()
{ FILE *fp =fopen("aaa.txt", "a+");
fwrite("aaaaaaaaaaaaaaaaaaa", 1, 10, fp);
fseek(fp, 5, SEEK_SET);
fwrite("AAA", 1, 3, fp);
fclose(fp);
return 0;
}
runned with: $ rm aaa.txt; gcc test.c && ./a.out && cat aaa.txt && echo .
produces wrong result: aaaaaaaaaaAAA.
result should be: aaaaaAAAaa
Update2: summary... minimal functions that i end up with:
FILE *fopenrwc(char*n) {FILE*f=fopen(n,"a");if(f)fclose(f),f=fopen(n,"r+");return f;}
or:
FILE *fopenrwc(char*n) {return fdopen(open(n,O_RDWR|O_CREAT,0666),"r+");}
If you review the manual page for fopen(), none of the standard open mode strings meets your requirements.
If you're on a sufficiently POSIX-like machine to be able to use open(), don't underestimate the benefits of fdopen() which would allow you to use open() with the options you showed and then create a file stream to use that file.
Note that "rw+" is not a valid mode. If you're (un)lucky, it gets treated as r+.
If you can't use fdopen() for some reason, you may be best off trying r+ and if that fails using w+; that opens a small window of vulnerability where someone might create a file that you then clobber with the w+ option — or creates a symlink so you end up creating a file where you didn't intend to do so.
This is the way it used to be necessary to work with open(); originally, you called open() — in the days before there was an O_CREAT — and if that failed, then you used creat() instead. That's a long time ago, though — see 'UNIX Programming' in 7th Edition UNIX Programmer's Manual Vol 2.
In general, testing with access() doesn't help. It leaves open a window of vulnerability because there is a TOCTOU — Time of Check, Time of Use — gap between the use of access() and open() (or fopen()). This is also the trouble with open() and creat(), or two calls to fopen().
If you want the finer controls, such as O_EXCL, or specialized properties such as O_DSYNC or O_NOCTTY, or even control over the permissions on the created file other than the default as modified by umask(), then open() plus fdopen() is practically the only way to go.
There is no direct way to meet your requirement with a simple fopen. IMHO, you best choice is to first use a low level open to create the file, and then use a fdopen (as suggested by Jonathan Leffler) to get a FILE * that can then be used with all the C library IO functions:
int fd =open("fname", O_RDWR | O_CREAT, 0666);
FILE *fp = fdopen(fd, "r+");
/* Ok, you can do what you want with fp */
I am writing an ANSI C cgi-bin server program.
Each instance of program can access to the same files simultaneously.
I do as follows:
handle = fopen(name,type);
fd = fileno(handle);
MyLockFile(fd) //I use fcntl
.....
The problem is that I open file with "fopen", not with "open".
Will "locking" work in such manner?
I can lock "fd", not "handle".
The reason is that I can't write workable "fd=open..." code.
My code below creates the executable file, write permissions were not set.
I don't know why
fd = open(name,O_CREAT|O_WRONLY|S_IREAD|S_IWRITE|S_IRGRP|
S_IWGRP|S_IROTH|S_IWOTH);
write(fd,data,strlen(data));
close(fd);
I can neither write nor append to this file.
Your open() function is wrong, when you specify O_CREAT, the permission bits needs to be the 3. argument, e.g.
open(name,O_CREAT|O_WRONLY,
S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);