bullseye
bullseye

Reputation: 81

open() system call working differently

Program 1

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
   int fd1, fd2;
   fd1 = open("dup1.txt", O_RDWR | O_CREAT| S_IREAD | S_IWRITE);
   printf("\nOriginal fd = %d", fd1);
   if(fd1 == -1){
     printf("\nFATAL Error\n");
     exit(1);
   }
}

Program 2

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
   int fd1, fd2;
   fd1 = open("dup1.txt", O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
   printf("\nOriginal fd = %d", fd1);
   if(fd1 == -1){
     printf("\nFATAL Error\n");
     exit(1);
   }
}

The program1 returns -1 for fd, if the file already exists (works fine when it doesn't), but the program2 below the first one works fine. The difference is placement of option flags.

Upvotes: 0

Views: 2610

Answers (3)

Jonathan Leffler
Jonathan Leffler

Reputation: 755026

When you use O_CREAT, open() takes three arguments and the third argument is the permissions on the file to be created:

fd1 = open("dup1.txt", O_RDWR | O_CREAT, 0644);

The permissions (S_I* flags) are never specified in the options (second) argument (which uses O_* flags) or vice versa. Theoretically the symbolic names are more portable than the octal literals, but IMNSHO octal literals are easier to interpret. You can use whatever permissions you like, but unless you're creating executable programs, you probably should not be setting the x bit for anyone, and for security reasons, you should consider not granting group or public write access (allowing group and public read access to be overridden by umask() as the user sees fit. Classically (in the days of yore when security was less of a concern), permissions were habitually 0666 by default for files.

Upvotes: 1

pjhades
pjhades

Reputation: 2038

On my machine, S_IWRITE has the same value as O_EXCL. The program:

int main() { 
    printf("%x %x\n", S_IREAD, S_IWRITE);
    printf("%x\n", O_EXCL);

    return 0; 
} 

gives:

100 80
80

So I guess, specifying the flag S_IWRITE together with O_CREAT now has the same effect as O_CREAT | O_EXCL, which causes open to fail if the file already exists.

However, I think you should invoke the system calls with the proper use of their arguments.

Upvotes: 4

Keith Thompson
Keith Thompson

Reputation: 263627

The open system call may be called with either 2 or 3 arguments.

These two calls:

fd1 = open("dup1.txt", O_RDWR | O_CREAT| S_IREAD | S_IWRITE);

and

fd1 = open("dup1.txt", O_RDWR | O_CREAT, S_IREAD | S_IWRITE);

are quite different; the first passes a second argument equal to O_RDWR | O_CREAT| S_IREAD | S_IWRITE, and the second passes a second argument equal to O_RDWR | O_CREAT and a third argument equal to S_IREAD | S_IWRITE.

The second argument should always be a bitwise "or" of one or more flags whose names start with O_.

If and only if the second argument includes the O_CREAT flag, you need to pass a second argument, which should be either a bitwise "or" of one or more flags whose names start with S_, or an octal number such as 0640 specifying permissions.

Your first call is incorrect; you shouldn't pass S_* flags in the second argument, and if the second argument includes O_CREAT, you need a third argument.

This ability to pass either two or three arguments is a bit odd. The Linux man page documents open like this:

 int open(const char *pathname, int flags);
 int open(const char *pathname, int flags, mode_t mode);

which is arguably incorrect (C doesn't have function overloading like that). POSIX describes it like this:

int open(const char *path, int oflag, ...);

making it a variadic function, similar to printf.

Upvotes: 2

Related Questions