How does the combination of flags in system call open() work? - c

fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
My tuition tells me that this O_WRONLY | O_CREAT | O_TRUNC means the system call allows to write and create (if any) and truncate file (if any).
But isn't it | is one of the bitwise operator and means OR?
How does the system interpret the combination? I tried to use what I learn about bitwise operation to interpret this combination, but I just get lost. Can someone explain this?

Bitwise OR and flags are a bit unintuitive.
Say you have the following flags:
int FLAG_1 = 0x1; // in binary, 0001
int FLAG_2 = 0x2; // in binary, 0010
int FLAG_3 = 0x4; // in binary, 0100
When you use:
int flags = FLAG_1 | FLAG_3;
the value of flags in binary is 0101. That means, the following flags have been set: FLAG_1 AND FLAG_3.
Given the value of flags, you can check whether FLAG_3 has been set by using bitwise AND.
if ( flags & FLAG_3 != 0 )
{
// FLAG_3 has been set.
}
Same logic is used when you combine the flags as O_WRONLY | O_CREAT | O_TRUNC.
More information on bitmasking can be found at http://en.wikipedia.org/wiki/Mask_(computing).

Related

Why are the open system call mode values different for equivalent code?

Below code and the quoted statement are from Linux System Programming by Robert Love.
I don't understand how are the modes in the first and the second code examples the same.
From the first example, I calculated the mode values as
USR -> 2+1 = 3
GRP -> 2+1 = 3
OTH -> 1 = 1
So my expectation was 0331, but it the book says it's 0664.
What's wrong in my logic?
Copied from the book below:
int fd;
fd = open (file, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IROTH);
if(fd == -1)
/* error */
Trading portability (in theory at least) for readability, we could
have written the following, to identical effect:
int fd;
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if(fd == -1)
/*error*/
For whichever reason, you have chosen to use the wrong values for the constants.
Instead of S_IRUSR being 0400, you decided it was 0200.
Instead of S_IWUSR being 0200, you decided it was 0100.
etc
If you use the correct values for the constants, you will get the same result.

How to get the mode of a file descriptor?

I mean to use fdopen
FILE *fdopen(int fd, const char *mode);
In man pages, it is stated that "The mode of the stream (one of the values "r", "r+", "w", "w+", "a", "a+") must be compatible with the mode of the file descriptor."
So I have to first know the mode of fd (which I guess is an int) to choose an appropriate const char *mode for the stream.
I understand I should use fcntl
int fcntl(int fd, int cmd);
to "manipulate file descriptor" (in the following, I quote from this official source).
It can operate on:
File descriptor flags
The following commands manipulate the flags associated with a file
descriptor.
...
File status flags
Each open file description has certain associated status flags,
initialized by open(2)...
(I wouldn't know the difference between the two.
Given that fcntl refers entirely to file descriptors, I guess the second title should be "File descriptor status flags", and thus we would have "flags" and "status flags"... confusing to me. I haven't seen any specification of this). I mention this in passing here, I am putting together a specific question on this.
From the description, I guess I should go for the latter.
In this case, when cmd=F_GETFL, the return value is "the file access mode and the file status flags". "The file status flags and their semantics are described in open(2)".
Now I couldn't make sense of, after reading the sources cited:
What are all possible modes (ints) for the fd
Consequently, what are all combinations mode(fd) <-> mode(stream) that are "compatible".
I guess one should be able to put together two list and join with arrows.
Related:
Can I get the access mode of a `FILE*`?
Specification of file descriptors (I asked this)
How to catch file mode?
I Wanna know the Internal Members of struct FILE, the latest ones
How to make sense of O_RDONLY = 0? (I asked this)
https://www.gnu.org/software/libc/manual/html_node/Access-Modes.html
https://www.gnu.org/software/libc/manual/html_node/File-Status-Flags.html#File-Status-Flags
After learning from answers and comments, here and in How to make sense of O_RDONLY = 0?, I put together code below.
From there, I obtained the following info about file descriptor status "words" (I wouldn't like to use the term "flags", see Note below, taken from this comment) and file opening modes.
*** Flag O_RDONLY = 0 = 0 = x0000
*** Flag O_WRONLY = 1 = 1 = x0001
*** Flag O_RDWR = 2 = 10 = x0002
*** Flag O_CREAT = 64 = 1000000 = x0040
*** Flag O_TRUNC = 512 = 1000000000 = x0200
*** Flag O_APPEND = 1024 = 10000000000 = x0400
*** Flag O_WRONLY | O_CREAT | O_TRUNC = 577 = 1001000001 = x0241
*** Flag O_WRONLY | O_CREAT | O_APPEND = 1089 = 10001000001 = x0441
*** Flag O_RDWR | O_CREAT | O_TRUNC = 578 = 1001000010 = x0242
*** Flag O_RDWR | O_CREAT | O_APPEND = 1090 = 10001000010 = x0442
*** Mode r F_GETFL -> 32768 = 1000000000000000 = x8000
*** Mode w F_GETFL -> 32769 = 1000000000000001 = x8001
*** Mode a F_GETFL -> 33793 = 1000010000000001 = x8401
*** Mode r+ F_GETFL -> 32770 = 1000000000000010 = x8002
*** Mode w+ F_GETFL -> 32770 = 1000000000000010 = x8002
*** Mode a+ F_GETFL -> 33794 = 1000010000000010 = x8402
Numbers in the three columns are in decimal, binary and hex.
Looking for the "strange" x8000, I found in fcntl-linux.h
# ifdef __USE_GNU
...
# define AT_RECURSIVE 0x8000 /* Apply to the entire subtree. */
...
# endif
So, except for that flag, present in all modes, the association would be
r <-> O_RDONLY
w <-> O_WRONLY
a <-> O_WRONLY | O_APPEND
r+ <-> O_RDWR
w+ <-> O_RDWR
a+ <-> O_RDWR | O_APPEND
Now this provides a couple of intriguing findings to me:
The list does not coincide with the table given by Tony Tannous.
The word for r+ is the same as for w+.
This provides a challenge for the coder, as to which mode to use with fdopen when the word is O_RDWR (both r+ and w+ would be ok).
As per this, I expected w+ to have also O_CREAT (as in the table mentioned above).
I also expected w to have it.
To write completely portable code, it seems that whenever using fdopen one has to write code as I wrote to automatically find the connection mode <-> word.
(actually, part of the work I did was a manual identification, and further code is needed).
EDIT:
The explanation for points 1 and 2 as per comments is that the table shows the match between fopen modes and open flags, i.e., during creation.
But what I obtained with fcntl is the flags persistent after creation, not those used during creation.
As also explained here, O_CREAT and O_TRUNC belong to the category of File creation flags and thus are not persistent.
On the other hand, O_APPEND belongs to the category File status flags and is persistent.
"The distinction between these two groups of flags is that the file creation flags affect the semantics of the open operation itself, while the file status flags affect the semantics of subsequent I/O operations." [ref]
Note:
The man page for open(2) first describes the file access modes, and then adds "In addition, zero or more file creation flags and file status flags can be bitwise-or'd in flags...." But it (correctly) does not mention that file access mode can be bitwise operated on. For me, the word "flag" is an absolute misnomer, and misleading.
Code (any function to_binary to get the binary form can be used):
int main() {
const char fname[100] = "test.txt";
const char modes[][4] = { "r", "w", "a", "r+", "w+", "a+" };
const size_t nmodes = sizeof(modes) / sizeof(modes[0]);
const int flags[] = { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_APPEND,
O_WRONLY | O_CREAT | O_TRUNC,
O_WRONLY | O_CREAT | O_APPEND,
O_RDWR | O_CREAT | O_TRUNC,
O_RDWR | O_CREAT | O_APPEND
};
const char flags_str[][100] = { "O_RDONLY", "O_WRONLY", "O_RDWR", "O_CREAT", "O_TRUNC", "O_APPEND",
"O_WRONLY | O_CREAT | O_TRUNC",
"O_WRONLY | O_CREAT | O_APPEND",
"O_RDWR | O_CREAT | O_TRUNC",
"O_RDWR | O_CREAT | O_APPEND"
};
const size_t nflags = sizeof(flags) / sizeof(flags[0]);
for (size_t iflag = 0 ; iflag < nflags ; iflag++) {
const int flag = flags[iflag];
const char * flag_str = flags_str[iflag];
char nbin[33];
to_binary(flag, nbin);
printf( "*** Flag %30s = %5d = %12s = x%04x\n", flag_str, flag, nbin, flag);
}
for (size_t imode = 0 ; imode < nmodes ; imode++) {
const char * mode = modes[imode];
FILE * fp1 = fopen(fname, mode);
int fd1 = fileno(fp1);
int retval = fcntl(fd1, F_GETFL);
char nbin[33];
to_binary(retval, nbin);
printf( "*** Mode %2s F_GETFL -> %5d = %12s = x%04x", mode, retval, nbin, retval);
fclose(fp1);
}
return 0;
}
From fopen you can check how they correlate with w+ r etc...

linux syscall open() bitmask

I want to know which purpose has the following line of code in Linux syscall int open(const char *pathname, int flags):
if (flags & ~(O_RDONLY | O_WRONLY | O_CREAT | O_RDWR | O_TRUNC | O_APPEND))
{
return -1;
}
This line checks that your file have only these properties available :
O_RDONLY : read-only file
O_WRONLY : write-only file
O_CREAT : create the file if it does not exist
O_RDWR : read-write file
O_TRUNC : If the file already exists and is a regular file and the access
mode allows writing (i.e., is O_RDWR or O_WRONLY) it will be
truncated to length 0.
O_APPEND : the file is opened in append mode
Without knowing the actual values of that O_RDONLY, O_WRONLY, and O_RDWR are assigned, it's not really possible to determine what that code actually does.
The code
if (flags & ~(O_RDONLY | O_WRONLY | O_CREAT | O_RDWR | O_TRUNC | O_APPEND))
{
return -1;
}
is non-portable and strictly speaking invalid outside of the actual system implementation that has access to and control of the actual values of the flags. Per POSIX, the flags O_RDONLY, O_WRONLY, and O_RDWR are NOT bit flags:
Values for oflag are constructed by a bitwise-inclusive OR of flags from the following list, defined in . Applications shall specify exactly one of the first five values (file access modes) below in the value of oflag:
O_EXEC
Open for execute only (non-directory files). The result is unspecified if this flag is applied to a directory.
O_RDONLY
Open for reading only.
O_RDWR
Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
O_SEARCH
Open directory for search only. The result is unspecified if this flag is applied to a non-directory file.
O_WRONLY
Open for writing only.
Since those flags are not bit-based but actual value-based, their presence or absence can not be detected via bitwise operations without knowledge of their actual values on the system in use.
This code uses more than one of those flags, violating the POSIX specification:
O_RDONLY | O_WRONLY | O_CREAT | O_RDWR | O_TRUNC | O_APPEND
It can in theory produce a nonsensical bit value that can not be used in a valid bitwise comparison to anything.
The RATIONALE section of the open() POSIX documentation even addresses the non-bit flag-based nature of the open() flags:
RATIONALE
In historical implementations the value of O_RDONLY is zero. Because of that, it is not possible to detect the presence of O_RDONLY and another option. Future implementations should encode O_RDONLY and O_WRONLY as bit flags so that:
O_RDONLY | O_WRONLY == O_RDWR
That comment only makes sense if O_RDONLY, O_WRONLY and O_RDWR are not bit-based flags.

Flags mask read and write posix

Checking the access mode of the file is slightly more complex, since the O_RDONLY (0), O_WRONLY (1), and O_RDWR (2) constants don’t correspond to single bits in the open file status flags. Therefore, to make this check, we mask the flags value with the constant O_ACCMODE, and then test for equality with one of the constants:
accessMode = flags & O_ACCMODE;
if (accessMode == O_WRONLY || accessMode == O_RDWR)
printf("file is writable\n");
I want to understand how the expressiin flags & O_ACCMODE work
Sorry for bad formatting i'm writing from my phone
The file modes are mutually exclusive. You can't be read-only and write-only, and you can't be read-write and either of the other two.
O_ACCMODE is equal to 3 so bits 1 and 2 are on.
00000000 (O_RDONLY)
& 00000011 (O_ACCMODE)
--------
00000000 <-- the result being compared
where 00000000 equals read-only so (accessMode == O_RDONLY) returns true.
The same for the others.
00000001 (O_WRONLY)
& 00000011 (O_ACCMODE)
---------
00000001 <-- the result being compared
O_WRONLY is 1 so (accessMode == O_WRONLY) is "is 1 equal to 1" which naturally returns true.
I don't think the above answer given by #Duck here is correct. Given the examples it makes no sense to mask them.
The reason you need to mask is that
flags = fcntl(fd, F_GETFL);
returns more than just these two bits. In fact the return value could be something like this:
1000000000000001
for write only file.
We mask in order to get rid of those other bits, which are not related to the read/write permissions.
In this O_ACCMODE is used as and mask to get access mode bits.

How to calculate the bit flags/enum flags for oflag in open()?

I'm working on a networking assignment and we are tasked with creating a remote file access server with a protocol we were given. My difficulties come in the lack of information I can find that explains the process of calculating the bits for the oflag argument in open().
I receive a message from a client to open a file and in the message I parse characters for the flags to use in oflag. Specifically they are:
R - O_RDONLY
W - O_WRONLY
RW - O_RDWR
A - O_APPEND
C - O_CREAT
T - O_TRUNC
E - O_EXCL
I went around Google and searched bitwise operations, enumeration flags, bit flags, calculating bit flags, etc. and couldn't find something that was useful in figuring out how to create the bits for oflag. Maybe I just didn't know what I was looking for and overlooked useful information?
Could someone please:
Point me in the direction/provide links to documentation/example of how to calculate the bits/# I ought to put into oflags given my parsed characters or
Show me the enumeration types for the flags and the ordering they ought to go in
Thanks a bunch for you help and if I wasn't clear on my problem or what I am trying to do, just let me know and I will clarify ASAP.
The O_... flags are numbers each with a different single bit set. For example on my system they are defined in fcntl.h as
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_CREAT 0100 /* not fcntl */
#define O_EXCL 0200 /* not fcntl */
#define O_NOCTTY 0400 /* not fcntl */
#define O_TRUNC 01000 /* not fcntl */
#define O_APPEND 02000
You use | (logical OR) to combine the flags and pass in a single number to open with all the bits set for each option you want. So e.g. open("file", O_RDWR | O_CREAT).
You can compute an int and pass that into open too if you want.
int flags = 0;
if (...)
flags |= O_RDWR;
...
open('file', flags);
The usual expression would be something like O_RDWR | O_CREAT. And note that O_RDWR is exactly O_RDONLY | O_WRONLY
You can do something like this:
char *flags = "r";
int oflag = 0;
if (strchr(flags,'r')) oflag |= O_RDONLY;
and so on for the rest of the flags.

Resources