Why does open() create my file with the wrong permissions? - c

I am trying to read some text from a file and write it to another using open(), read() and write().
This is my open() for the file-to-write-to (I want to create a new file and write into it):
fOut = open ("test-1", O_RDWR | O_CREAT | O_SYNC);
This is setting file-permissions to something I don't understand at all. This is the output of ls -l:
---------T 1 chaitanya chaitanya 0 2010-02-11 09:38 test-1
Even the read permission is locked. I tried searching for this, but could not find ANYTHING.
Strangely, write() still successfully writes data to the file.
Also, if I do a 'chmod 777 test-1', things start working properly again.
Could someone please let me know where I am going wrong in my open call?
Thanks!
For your reference, I have pasted the complete program below:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main () {
char buffer[512], ch;
int fIn, fOut, i;
ssize_t bytes;
FILE *fp = NULL;
//open a file
fIn = open ("test", O_RDONLY);
if (fIn == -1) {
printf("\nfailed to open file.");
return 1;
}
//read from file
bytes = read (fIn, buffer, sizeof(buffer));
//and close it
close (fIn);
printf("\nSuccessfully read %d bytes.\n", bytes);
//Create a new file
fOut = open ("test-1", O_RDWR | O_CREAT | O_SYNC);
printf("\nThese are the permissions for test-1\n");
fflush(stdout);
system("ls -l test-1");
//write to it and close it.
write (fOut, buffer, bytes);
close (fOut);
//write is somehow locking even the read permission to the file. Change it.
system("chmod 777 test-1");
fp = fopen ("test-1", "r");
if (fp == NULL) {
printf("\nCan't open test-1");
return 1;
}
while (1)
{
ch = fgetc(fp);
if (ch == EOF)
break;
printf("\n%c", ch);
}
fclose (fp);
return 0;
}

open() takes a third argument which is the set of permissions, i.e.
open(filename, O_RDWR|O_CREAT, 0666)
0666 is an octal number, i.e. every one of the 6's corresponds to three permission bits
6 = rw
7 = rwx
first three bits for owner permission, next three bits for group permission and next is for the world
the first digit - represents that is file or directory. (0 - file, d - directory)
here we used 0 means file
It's a typical pitfall. The compiler allows you to leave the permission argument away because when you open an existing file the permission bits don't make sense. But when you forget the argument when you create a file, you get a random set of permissions, e.g. 0000 in your case (---).

Reading http://linux.die.net/man/2/open it seems you missed the mode parameter for open:
mode must be specified when O_CREAT is in the flags, and is ignored otherwise.
The argument mode specifies the permissions to use in case a new file is created.

This question recently helped me out, so I wanted to do my part to add a bit more depth as to what's going on. Like it was stated before, you were missing the third argument to open(). However, the permissions you see aren't random; they're coming from the stack. Look at the following code snippet:
asm("push $0");
asm("push $0");
asm("push $0");
fd = open("base", O_RDWR|O_CREAT);
Note the following result:
----------. 1 user user 4 Feb 26 08:21 base
Let's change the first push to 1, i.e. execute permission:
asm("push $1;push $0;push $0");
fd = open("base", O_RDWR|O_CREAT);
and we get:
---------x. 1 user user 4 Feb 26 08:25 base
Change the push to 4, i.e. read permission, and mess with the other two values:
asm("push $4;push $5;push $6");
fd = open("base", O_RDWR|O_CREAT);
and we get:
-------r--. 1 user user 4 Feb 26 08:27 base
Thus we can see the third value popped off the stack (first pushed) is what really matters. Finally for fun we can try 5 and then 50, which respectively result in:
-------r-x. 1 user user 4 Feb 26 08:27 base
----rw----. 1 user user 4 Feb 26 08:28 base
Hope this adds some clarity!

Actually umask() only filters permissions and does not set them. The typical umask() value is 0002 ("don't give away write permission to the world") and if your mode value in the open( "file", O_CREAT, 0777) gave all permissions, the resulting file would have 775 as its permssions.

Not strictly relevant to the question, but the accepted answer could use this clarifying point:
There is a relationship between rwx and its numerical representation that can be seen by treating the presence of a letter as a binary 1, and its absence as a binary 0.
e.g.
rwx <--> 111 (binary) <--> 7 (octal)
r-- <--> 100 (binary) <--> 4 (octal)
-wx <--> 011 (binary) <--> 3 (octal)
As a further addendum, you may now consider the chmod command:
chmod 777 filename.extension --> rwxrwxrwx permissions
777 <--> 111 111 111 <--> rwx rwx rwx
or: chmod 654 filename.extension --> rw-r-x-r--
654 <--> 110 101 100 <--> rw- r-x r--
Hope this is informative!

you can call umask(0); system call before using open(); system call to set your choices permissions to file correctly.

This is kind of an old thread, but I think people should be aware of the "sys/stat.h" library. This includes a bunch of symbolic constants for setting permission bits.
For example: To open a file with Read/Write permissions enabled for the user
#include <fcntl.h>
#include <sys/stat.h>
open("Your/File/Path", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
where:
S_IWUSR // Sets the Users Write bit
S_IRUSR // Sets the Users Read bit
This library includes a bunch of others, I won't list them all here but you can read up on it all here.
Of course you can put in the octal values to set these bits, however some may argue that it is poor coding practice.

Related

writing files at a low level

I was reading the GNU C PROGRAMMING TUTORIAL online, and get some confusion on the code example for low level read & write.
The code is as below:
#include <stdio.h>
#include <fcntl.h>
int main()
{
char my_write_str[] = "1234567890";
char my_read_str[100];
char my_filename[] = "snazzyjazz.txt";
int my_file_descriptor, close_err;
/* Open the file. Clobber it if it exists. */
my_file_descriptor = open (my_filename, O_RDWR | O_CREAT | O_TRUNC);
/* Write 10 bytes of data and make sure it's written */
write (my_file_descriptor, (void *) my_write_str, 10);
fsync (my_file_descriptor);
/* Seek the beginning of the file */
lseek (my_file_descriptor, 0, SEEK_SET);
/* Read 10 bytes of data */
read (my_file_descriptor, (void *) my_read_str, 10);
/* Terminate the data we've read with a null character */
my_read_str[10] = '\0';
printf ("String read = %s.\n", my_read_str);
close (my_file_descriptor);
return 0;
}
I compiled the code with gcc without issue. And run the first time, it is also ok. Output as below:
$ ./lowLevelWrite
String read = 1234567890.
The problem comes when i run the program second time:
$ ./lowLevelWrite
String read = .
Seems the code fails to write the string "1234567890" to the file second time. As we know from the GNU C manual, O_RDWR | O_CREAT | O_TRUNC these flag should allow us to truncate the file to 0 every time and then write to the file. I am not sure why it fails from second time execution.
Can anybody help me out of this confusion?
When you're creating a file with open() you need to pass a third argument, the permission modes:
my_file_descriptor = open (my_filename, O_RDWR | O_CREAT | O_TRUNC, 0664);
0664 is the permissions rw-rw-r--: readable and writable by the owner and group, readable by everyone else. These permissions will be further masked by your umask.
Since you didn't pass this argument, open() used random stack garbage, and this probably didn't include write permission. So you couldn't open the file for writing when it already exists.

C: Executing and outputing shell commands in C

Aside from using popen() (as was discussed in this question) is this a valid way of doing it ?
Say we had a program who's name is hexdump_dup and wanted the program to output the exact output of the hexdump command.
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755); // (line 8)
write(fd, "/usr/bin/hexdump $#;", 20); // (line 9)
close(fd);
return (0);
}
Also could someone briefly explain what line 8 and 9 do, and how afterwards the command gets executed ? Like when, where does it say to execute the command or what makes the command execute ?
After this
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755); // (line 8)
write(fd, "/usr/bin/hexdump $#;", 20);
you need to execute hexdump_dup executable, for that you need to use either system() or exec() family function. For e.g
system("./hexdump_dup 1 2 3"); /* after creating binary file(hexdump_dup) & writing command into it, you need to run it, for that use system() or exec() */
This
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755);
will create the hexdump_dup binary if it doesn't exist before & if exists before it will truncate its content to 0. You can refer the man page of open() , it says
int open(const char *pathname, int flags, mode_t mode);
The argument flags must include one of the following access
modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening
the file read-only, write-only, or read/write, respectively.
O_CREAT
If the file does not exist it will be created. The
owner (user ID) of the file is set to the effective
user ID of the process.
O_TRUNC
If the file already exists and is a regular file and
the open mode allows writing (i.e., is O_RDWR or
O_WRONLY) it will be truncated to length 0. If the
file is a FIFO or terminal device file, the O_TRUNC
flag is ignored.
Lastly this
write(fd, "/usr/bin/hexdump $#;", 20);
writes 20 bytes containing array of characters /usr/bin/hexdump $#; in this case into a file where fd points i.e it will put this into hexdump_dup file.
Here $# means when you execute hexdump_dup like
./hexdump_dup 1 2 3
it will take all the parameters to be passed.

Why are the file permissions changed when creating a file with the open system call on Linux?

I am creating a file with full permission (777) using the open system call, but when I do ls -l I can see only permission as (755). Could you please tell why file permission is not 777?
Code
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int fd;
/* Creates a file with full permission*/
fd = open("test", O_CREAT | O_RDWR | O_APPEND, 0777);
if (fd = -1)
{
return -1;
}
close(fd);
}
Output
$ ls -l
-rwxr-xr-x 1 ubuntu ubuntu 0 2012-09-19 11:55 test
There's a value maintained by the system called the umask; it is a property of the process, just like the PID (process ID) or EUID (effective user ID) is. It will be set to 022 (octal), which indicates that the system should remove the group and other write permission from files that are created.
You can call umask(0); before using open() so that the mode you specify in open() won't be altered. You should certainly do this to demonstrate that umask is the issue. However, it is generally best to let the user's choice of umask prevail — I for one get very stroppy if a program doesn't obey my umask setting; it tends not to be used again after I spot and verify the problem.
The shell also has a (built-in) command umask which you can use. The 022 value is a sensible default; most of the time, you do not want just anybody writing to your files.
The permissions of a created file are restricted by the process's current umask -- your current umask is 022, so group and world write are always disabled by default. (Which is a good thing, in most cases.) If you really want a group- and world-writable file, you will need to temporarily set umask(0) while creating this file (make sure to save the old value returned by the system call, and set it back afterwards!), or "manually" set the file's permissions using chmod().
umask will return the original value of the mask, so to reset it temporarily you just need to do
#include <sys/types.h>
#include <sys/stat.h>
mode_t old_mask = umask(0);
...
umask(old_mask);
though it would be perhaps preferable to use fchmod after opening the file - this will avoid the problem of changing process-global state:
fd = open("test", O_CREAT | O_RDWR | O_APPEND, 0777);
if (fd < 0) { ... }
int rv = fchmod(fd, 0777);
if (rv < 0) { /* fchmod errored */ }

Why won't this C code create a file with user, group and other permissions read and write?

Why won't the following code create a file with permissions read and write for user, group, and other?
char data[10] = "123456789";
int fh = open("test.txt", O_RDWR|O_CREAT, 0666);
write(fh, data, 10);
printf(strerror(errno));
close(fh);
Produces this file:
-rw-r--r-- 1 pc users 9 Nov 15 16:15 test.txt
What does that mean? I specifically asked for r+w in all categories.
Reset your user mask using the umask() system call before calling open().

C system calls open / read / write / close and O_CREAT|O_EXCL

Given the following code (it's supposed to write "helloworld" in a "helloworld" file, and then read the text):
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FNAME "helloworld"
int main(){
int filedes, nbytes;
char buf[128];
/* Creates a file */
if((filedes=open(FNAME, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
S_IRUSR | S_IWUSR)) == -1){
write(2, "Error1\n", 7);
}
/* Writes hello world to file */
if(write(filedes, FNAME, 10) != 10)
write(2, "Error2\n", 7);
/* Close file */
close(filedes);
if((filedes = open(FNAME, O_RDONLY))==-1)
write(2, "Error3\n", 7);
/* Prints file contents on screen */
if((nbytes=read(filedes, buf, 128)) == -1)
write(2, "Error4\n", 7);
if(write(1, buf, nbytes) != nbytes)
write(2, "Error5\n", 7);
/* Close file after read */
close(filedes);
return (0);
}
The first time I run the program, the output is:
helloworld
After that every time I to run the program, the output is:
Error1
Error2
helloworld
I don't understand why the text isn't appended, as I've specified the O_APPEND file.
Is it because I've included O_CREAT?
It the file is already created, shouldn't O_CREAT be ignored?
O_EXCL forces the file to be created. If the file already exists, the call fails.
It is used to ensure that the file has to be created, with the given permissions passed in the third parameter. In short, you have these options:
O_CREAT: Create the file with the given permissions if the file doesn't already exist. If the file exists, it is opened and permissions are ignored.
O_CREAT | O_EXCL: Create the file with the given permissions if the file doesn't already exist. If the file exists, it fails. This is useful in order to create lockfiles and guarantee exclusive access to the file (as long as all programs which use that file follow the same protocol).
O_CREAT | O_TRUNC: Create the file with the given permissions if the file doesn't already exist. Otherwise, truncate the file to zero bytes. This has more of the effect we expect when we think "create a new blank file". Still, it keeps the permissions already present in the existing file.
More information from the manual page:
O_EXCL
When used with O_CREAT, if the file
already exists it is an error and
the open() will fail. In this context,
a symbolic link exists, regardless of
where it points to. O_EXCL is broken
on NFS file systems; programs which
rely on it for performing locking
tasks will contain a race condition.
The solution for performing atomic
file locking using a lockfile is to
create a unique file on the same file
system (e.g., incorporating hostname
and pid), use link(2) to make a link
to the lockfile. If link() returns 0,
the lock is successful. Otherwise, use
stat(2) on the unique file to check if
its link count has increased to 2, in
which case the lock is also
successful.

Resources