How to get the mode of a file descriptor? - c

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

Related

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.

How to open a file which overwrite existing content

I try to open a file like this in linux. It will over-write an existing one if exits. That is what I want.
fout = open(out_file_name, O_WRONLY | O_CREAT, 644);
However, if the existing is 1024 bytes, when I open in above way and write 800 new bytes.
I still see the 224 bytes at the end of previous content.
How can I make it just have the 800 bytes that I have been written?
You want to use the O_TRUNC flag to open(), by OR-ing it with the existing flags you have above:
int fout = open(out_file_name, O_WRONLY | O_CREAT | O_TRUNC, 644);
This will truncate the file. Below is the information in the man page for open(2).
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. Otherwise the effect of
O_TRUNC is unspecified.

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.

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"

Why does open make my file descriptor 0?

I'm working on a program that is using a pipe and forks and need to change the write end to an output file. But when I open a file the file descriptor is 0 which is usually stdin but which I think is the cause of some of my problems. Here is my code
if (outputfd = open("file", O_RDWR | O_CREAT | O_TRUNC) == -1)
{
// open failed
}
Can someone let me know why it is 0? Or how to fix it?
outputfd in your line of code is not the output file descriptor but rather is equal to FALSE (0). This is because the file descriptor returned by open is not == -1
It should read:
outputfd = open("file", O_RDWR | O_CREAT | O_TRUNC);
if (outputfd < 0)
{
// error handling code
}
Or it should read:
if ( ( outputfd = open("file", O_RDWR | O_CREAT | O_TRUNC) ) == -1)
{
// error handling code
}
Note that this required 3 extra parentheses - one right parenthesis and two left.
It's because you're comparing it to -1.
outputfd doesn't get the result of open. It gets the result of the check for -1.
Just illustrating doron's answer:
>> outputfd = open("file", O_RDWR | O_CREAT | O_TRUNC) == -1)
Let's simplify: first remove errors and add extra punctutation to make it look like an actual stement
outputfd = open("file", O_RDWR | O_CREAT | O_TRUNC) == -1;
Now, replace function parameters with a placeholder
outputfd = open(<PLACEHOLDER>) == -1;
Add parenthesis
outputfd = (open(<PLACEHOLDER>) == -1);
When is the result of open() -1? When the operation failed. So let's assume the operation didn't fail and replace the open with a positive number
outputfd = (<POSITIVENUMBER> == -1);
No positive number can ever be equal to -1 (barring conversion problems) so the equality test is always false ... and false, in C is, by definition, 0
outputfd = 0;
In C, relational operators have higher precedence than assignment operators.

Resources