Why does open make my file descriptor 0? - c

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.

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

Call to open() to create a file fails if the file exists

I'm trying to create a C code which will create a file for which I can read or write from/to. This file can already exist, or need to be created from scratch. If it already exists within the directory, I want it to created from scratch, in other words delete all the contents.
FD = open("p.txt", O_RDWR | O_CREAT | O_TRUNC);
I've tried using that for the time being. I encounter a problem though. If the file doesn't exist, it creates it and returns a positive file descriptor.
If the file however already exists, a -1 FD is returned. So I must be missing a flag?
I assumed O_TRUNC would be enough to clear the contents of a file?
FD = open("p.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
When a Unix call returns -1, check the value of the errno variable. It contains the reason for the error. Don't speculate as to what might be the problem until you've seen the error code. You can call strerror or perror to get a message describing the numerical value stored in errno.
Also, as others have noted, when you pass O_CREAT to open, you must pass a third argument which determines the file's permission if it's created. (If you don't )
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
int main() {
int fd = open("p.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {
perror("opening p.txt");
exit(1);
}
/* … */
}

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.

llvalue required as left operand of assignment

I wan't to open a file in append mode or erase all it's contents based on $flag
static void do_redirect(int filedes, const char *filename, int flag){
int rc;
int fd;
flag == 1 ? fd = open(filename, O_CREAT|O_RDWR|O_TRUNC, 0644) :
fd = open(filename, O_CREAT| O_RDWR, 0644);
}
Why do I get llvalue required as left operand of assignment?
Because of precedence. The expression is parenthesised implicitly
(flag == 1 ? fd = open(filename, O_CREAT|O_RDWR|O_TRUNC, 0644) : fd) = open(filename, O_CREAT| O_RDWR, 0644);
and the value of the conditional expression is not an lvalue.
Write
flag == 1 ? (fd = open(filename, O_CREAT|O_RDWR|O_TRUNC, 0644)) :
(fd = open(filename, O_CREAT| O_RDWR, 0644));
or move the conditional expression inside the open call.
It's a question of precedence.
I would suggest you rewrite it as such:
fd = open(filename, O_CREATE | O_RDWR | (flag == 1 ? O_TRUNC : 0), 0644);
Why you are getting lvalue error?
You conditional expression is something like:
flag == 1 ? fp = 1 : fp = 2;
that is parsed in C like:
(flag == 1 ? fp = 1 : fp) = 2;
^ expression = value
and you can't assign a value to an expression that that reason for lvalue error.
The correct answer is given by #Daniel Fischer by adding parenthesis you can rectify your conditional expression(actually overriding precedence).
But interesting this you expression is correct in C++ language!
Read Charles Bailey's answer here: Conditional operator differences between C and C++. You will also find an elaborate answer there why you are getting lvalue error.
(my answer is just a reference to there)
Although #Daniel Fischer and #Joachim Pileborg answer you two tricks you can also achieve same like this:
fd = flag == 1 ? -2 : 3 ;
Yes but this is possible only in your case because you are using fd in both true/false expression

Resources