73

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;
}
JB.
  • 40,344
  • 12
  • 79
  • 106
Chaitanya
  • 751
  • 1
  • 6
  • 4
  • 1
    You probably don't need 777 permission; you probably only need 666 at most, and usually you don't want public write permission either. You don't want people executing your data files. – Jonathan Leffler Feb 11 '10 at 15:12

7 Answers7

111

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 (---).

Community
  • 1
  • 1
Antti Huima
  • 25,136
  • 3
  • 52
  • 71
  • 3
    For completeness, octal 4 = r – Sam Post Feb 11 '10 at 14:55
  • 2
    @Adam Liss: hex 4 = octal 4. Octal is convenient for permissions because each group of 3 bits makes up a unit of permissions and an octal digit. 0644 = 0x1A4, but it is a lot easier to see the permissions in the octal notation than it is in hexadecimal. (Partly experience, but mainly the appropriate grouping of bits). – Jonathan Leffler Feb 11 '10 at 15:10
  • 2
    0666 is usually a good setting, even if you don't intend for "other" to be able to write to it. umask is usually set to 0022, which removes write permissions for everyone except the owner. – Jay Conrod Feb 11 '10 at 16:09
  • 6
    I don't understand how the last argument is obtained, this is not on the man pages? – r4bb1t Apr 12 '20 at 01:16
  • Is it random or is it guaranteed to be all 0s? – user129393192 Jun 26 '23 at 01:51
  • @r4bb1t See my answer for a demonstration of where the values actually come from (tldr: wherever the 3rd argument would be, whether on the stack or in a register based on your architecture). – Billy Jun 30 '23 at 06:03
  • @user129393192 Same for you: it's definitely not guaranteed to be all 0s. – Billy Jun 30 '23 at 06:06
12

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ZeissS
  • 11,867
  • 4
  • 35
  • 50
10

This question 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 story doesn't end there: the permissions you see aren't random!

If you're on an x86 system, you'll see they're coming from the stack. A debugger is always helpful, but we can also look at the following code snippet:

    #include<stdlib.h>
    #include<fcntl.h>
    
    int main(){

        asm("push $0");
        asm("push $0");
        asm("push $0");
        fd = open("base", O_RDWR|O_CREAT);
    }

You can see we're using inline assembly to manually set up parts of the stack. 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

Note that if you're on an x86-64 system, arguments are first stored in registers and not the stack. In particular, on a Linux system the first six arguments are passed in RDI, RSI, RDX, RCX, R8, R9. You're still targeting what would be the third argument, so in particular:

    asm("mov $1, %rdx");
    fd = open("base", O_RDWR|O_CREAT);

Hope this adds some clarity!

Billy
  • 607
  • 2
  • 8
  • 20
  • 1
    Sorry what do you mean by making C use that version of open? From the man page: "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)." Arguments are passed on the stack; by using inline assembly to manually set up the stack, we can demonstrate where the seemingly "random" permissions are coming from. – Billy Jun 07 '18 at 06:16
  • Is this with x86-64? I thought the first 6 arguments to a function are always stored in register. – user129393192 Jun 26 '23 at 01:52
  • You're right that my original post assumed x86 and I should edit that to fix it. However, the underlying principle as to what's going on here is still the same: the "randomness" is still completely understandable. I think if you read my answer again, and your comment, you already know enough to answer it yourself. If not, fire up a 64-bit Linux system, try my code snippet, see it fail, and then finally change the inline assembly to "mov $1, %rdx". – Billy Jun 30 '23 at 05:41
5

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.

ByteHamster
  • 4,884
  • 9
  • 38
  • 53
rgt
  • 51
  • 1
  • 1
4

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!

Evan
  • 429
  • 4
  • 7
2

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

Abdo
  • 600
  • 6
  • 9
  • it’s worth pointing that umask out have has effect on the whole process and thus poses a risk for race conditions in a multithreaded program – Alexey Mar 25 '18 at 09:37
2

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.

Meevs
  • 31
  • 2
  • I think what you are saying is general subjective stuff and the question is that even after setting the permission we cannot see permission bits set. This holds true for both stdlb api or system apis. – Bhupesh Pant Mar 26 '18 at 11:49
  • 2
    @BhupeshPant The question asks why the permission bits of the file are being set to "---------T 1 chaitanya chaitanya 0 2010-02-11 09:38 test-1" That is, why does the file have no permission bits set. In his open call, ie.) "fOut = open ("test-1", O_RDWR | O_CREAT | O_SYNC);" He has the O_CREAT flag, which will create the file "test-1" if it does not exist. Assuming this is the case, (that the file is being created at the open call), he must also specify the permission bits to be associated with the file. He corrects this later in his code with the system call to chmod. – Meevs Mar 26 '18 at 17:20