why permission of file created by open() is different with by touch? - c

#include<apue.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main(int argc , char *argv[])
{
int fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC);
int pid,status;
const char *str_for_parent = "str_for_parent\n";
const char *str_for_child = "str_for_child\n";
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0)
{
write(fd,str_for_child,strlen(str_for_child)+1);
_exit(0);
}
wait(&status);
write(fd,str_for_parent,strlen(str_for_parent)+1);
return 0;
}
The test.txt is created by open().But it's permission(---------x) is different with those files(-rw-rw-r--) created by touch or any other softwares in my system.my umask is 0002

open actually takes an (optional) third parameter:
int open(const char *pathname, int flags, mode_t mode);
The mode of the new file is set to the AND of mode and the inverse of your umask (ie, mode & ~umask). Since you're not passing mode, it's getting initialized to some garbage, and you're getting the wrong mode as a result.
Generally if you're passing O_CREAT, you should pass a mode of 0666, unless you have some specific reason to use some other mode (eg, if you want to make sure the file is not readable by other users regardless of what the umask is set to).

If you supply the O_CREAT flag to open(), you must also supply a third mode parameter, which is combined with the current umask to set the created file's permission bits.
Unless you are specifically creating an executable file, you should almost always use 0666 as this parameter:
int fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
This should give you the same result that you see from touch. Note that the leading zero in C indicates an octal constant.

Related

Setting permissions for open() system call in C [duplicate]

This question already has answers here:
Why are the file permissions changed when creating a file with the open system call on Linux?
(3 answers)
Closed 3 years ago.
I am trying to create a file which will have read, write, execute permissions for user group and others, but this is not happening.
I tried the following command
fd = open("file1",O_CREAT| O_WRONLY, 0777);
the permissions that this gives is
rwx-xr-r-x
I want
rwxrwxrwx
I tried tinkering with other modes like
fd2 = open ("file2",O_WRONLY | O_CREAT | O_TRUNC, 0644 );
and they gave the right permissions
rw-r--r--
where am I going wrong ?
My Code
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd;
fd = open("file1",O_CREAT| O_WRONLY, 0777);
int fd2;
fd2 = open ("file2",O_WRONLY | O_CREAT | O_TRUNC, 0644 );
}
Processess have an inheritable mode_t-typed property called umask which you can set with the umask call.
When you create a filesystem item with specified permissions P, the resulting permissions are P &~ the_umask.
Example:
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
mode_t getumask(void)
{
mode_t m = umask(0);
umask(m);
return m;
}
int main(void){
unlink("file1");
unlink("file2");
int fd;
mode_t cur_umask = getumask();
printf("cur_umask=%#o\n", cur_umask);
mode_t given_mode=0777;
if(0>(fd = open("file1",O_CREAT| O_WRONLY, given_mode))) return perror("open"),1;
struct stat sb;
if(0>fstat(fd,&sb)) return perror("fstat"),1;
close(fd);
printf("given_mode=%#o mode=%#o expected_mode=%#o\n", given_mode, 0777&sb.st_mode, given_mode&~cur_umask);
//Without cur_umask
int r=0;
mode_t oldumask = umask(0);
given_mode=0777;
if(0>(fd = open("file2",O_CREAT| O_WRONLY, given_mode))) return perror("open"),1;
if(0>fstat(fd,&sb)) return perror("fstat"),1;
printf("given_mode=%#o mode=%#o expected_mode=%#o\n", given_mode, 0777&sb.st_mode, given_mode);
out:umask(oldumask);
return r;
}
Possible example output:
cur_umask=027
given_mode=0777 mode=0750 expected_mode=0750
given_mode=0777 mode=0777 expected_mode=0777
Usually, umask is set with the umask shell builtin in one of your loggin-session startup scripts (e.g., /etc/profile) and is then inherited to descendant processes.
If you want your C process to create filesystem items with permissions exactly as specified, you need to zero your umask before creating your items. Keep in mind that if you intend to exec after the umask change you might want to restore the old value before you exec.

error when writing to file after opening it with O_APPEND | O_CREATE

I have opened a text file named "pranav" in O_APPEND | O_CREAT mode as shown below:
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
main()
{
//FILE Descriptor fdes
/*Open file pranav.txt in write-only mode,
O_CREAT creates file if it does not exist*/
int fdes = open("pranav.txt",O_APPEND | O_CREAT );
//Error returns -1
if(fdes!=-1)
{
//To write on file
if((write(fdes,"Pranav",6))== -1)
write(2,"File_Writing_Error",18);
//To print on screen
else
write(1,"Done",4);
}
else
{
//Print "error" on screen
write(2,"File_Opening_Error",18);
}
close(fdes);
}
In O_APPEND mode it executes the write(2,"File_Writing_Error",18); statement, thus not able to write "Pranav" on file, but this error does not occur and program successfully runs if I use O_WRONLY mode
Documentation for open says, that you must give exactly one of the flags O_RDONLY, O_WRONLY and O_RDWR and that you can use any combination of the other flags like O_APPEND and O_CREAT.
You did not provide O_WRONLY in addition to O_APPEND and O_CREAT. My guess is that O_RDONLY is 0, so when not giving one of the access flags you end up with O_RDONLY and thus cannot write to the file.
So the correct code should be:
mode_t mode = S_IRWXU | SIRWXG; // or any other mode
int fdes = open("pranav.txt", O_APPEND | O_CREAT | O_WRONLY, mode);
Please note the additional parameter mode which is required if the flags included O_CREAT or O_TMPFILE.

open with O_CREAT - was it opened or created?

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".
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);
}
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.

O_RDWR permission denied

I created this file
char *output = "big";
creat(output, O_RDWR);
When I'm trying to read the file
cat big
I'm getting permission denied. Whats wrong with my code? How to create a file with read and write permission mode?
with ls -l, the permission of big looked like this
----------
what does this mean?
You have misinterpeted the mode argument. From the man page:
mode specifies the permissions to use in case a new file is cre‐
ated. This argument must be supplied when O_CREAT is specified
in flags; if O_CREAT is not specified, then mode is ignored.
The effective permissions are modified by the process's umask in
the usual way: The permissions of the created file are
(mode & ~umask). Note that this mode only applies to future
accesses of the newly created file; the open() call that creates
a read-only file may well return a read/write file descriptor.
and also
creat() is equivalent to open() with flags equal to
O_CREAT|O_WRONLY|O_TRUNC.
So, a more appropriate call might look like:
int fd = creat(output, 0644); /*-rw-r--r-- */
If you want to open it O_RDWR though, then just use open():
int fd = open(output, O_CREAT|O_RDWR|O_TRUNC, 0644);
This is obviously a permission issue, start trying to see if creat doesn't returns -1, if so, print the errno value, with perror(""), so that you could resolve the problem.
Imho, i'd rather use open() to do this, because as mentionned in the creat man page,
"Note that open() can open device special files, but creat() cannot create them; ..", and
"creat() is equivalent to open() with flags equals to O_CREAT | O_WRONLY | O_TRUNC", and this doesn't talks about the permissions..
it would be the exact same result if you did this:
char* output = "big";
int fd;
fd = open(output, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
// do whaterver you want to do in your file
close(fd);
For more information, "man 2 open"

When is umask() useful?

umask(0);
fd = open("/dev/null", O_RDWR);
Here's man 2 umask:
umask() sets the calling process’s file mode creation mask (umask) to mask & 0777.
But it doesn't make sense for me,as when we call open ,we will also provide a mode parameter.
So what's the point of umask?
The umask is applied to all modes used in file system operations. From the manual open(2):
The permissions of the created file are (mode & ~umask)
So with a single call to umask, you can influence the mode of all create files.
This is usually used when a program wants the user to allow to overrule the default grants for files/directories it creates. A paranoid user (or root) can set the umask to 0077 which means that even if you specify 0777 in open(2), only the current user will have access.
I know this is and old question but here is my two cents:
Permissions of shared memory object
I was trying to make a shared memory object, with:
int shm_open(const char *name, int oflag, mode_t mode);
The resulting shared memory did not have the permission set in mode argument, so I read the shm_open man page which led me to the open function man page and there it says:
mode specifies the permissions to use in case a new file is created. This argument must be supplied when O_CREAT is specified in flags; if O_CREAT is not specified, then mode is ignored. The effective permissions are modified by the process's umask in the usual way: The permissions of the created file are (mode & ~umask). Note that this mode only applies to future accesses of the newly created file
So I tried to modify the umask with:
mode_t umask(mode_t mask);
but it did not work either, so after more google I found this Setting Permission document in gnu.org
Which recommends:
When your program needs to create a file and bypass the umask for its access permissions, the easiest way to do this is to use fchmod after opening the file, rather than changing the umask. In fact, changing the umask is usually done only by shells. They use the umask function.
and with fchmod my function worked as I wanted :) her it is:
int open_signals_shmem(struct signal_shmem **shmem, int size)
{
int fd, ret;
void *ptr;
*shmem = NULL;
ret = 1;
fd = shm_open(SIGNALS_SHMEM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
if (fd == -1)
{
printf("error: signals shmem could not be allocated (%s, errno=%d)\n", SIGNALS_SHMEM_NAME, errno);
}
else
{
// Change permissions of shared memory, so every body can access it
fchmod(fd, S_IRWXU | S_IRWXG | S_IRWXO);
if (ftruncate(fd, size) == -1)
{
printf("error: signals shmem could not be truncated (%s, errno=%d)\n", SIGNALS_SHMEM_NAME, errno);
}
else
{
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
printf("error: signals shmem could not be mapped (%s, errno=%d)\n", SIGNALS_SHMEM_NAME, errno);
}
else
{
*shmem = ptr;
ret = 0;
}
}
}
return ret;
}
Citing this article:
The purpose of the umask is to allow
users to influence the permissions
given to newly created files and
directories. Daemons should not allow
themselves to be affected by this
setting, because what was appropriate
for the user will not necessarily be
suitable for the daemon.
In some cases it may be more
convenient for the umask to be set to
a non-zero value. This is equally
acceptable: the important point is
that the daemon has taken control of
the value, as opposed to merely
accepting what it was given.
Most Mac developers (and by extension most software testers), from the time they were babies, put this in their .cshrc
umask 002
However, most end users don't know about umask, so if they create a new user on the machine, and run your app, you are likely to create a bunch of log files and whatnot without group read/write permissions.
Then they switch users again and suddenly your app doesn't work.
For this reason, we're adding this to all our apps.
Our rule-of-thumb when it comes to security is that "we want users to be able to use our software".
#import <sys/types.h>
#import <sys/stat.h>
int main(int argc, char *argv[])
{
// set permissions for newly created files to ug+rwX,o+rX
umask(0002);

Resources