4

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:

  1. What are all possible modes (ints) for the fd

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

  • The file "file-descriptor" flags are `O_RDONLY`, `O_WRONLY` and `O_RDWR`. If you go back to the linked reference you should be able to figure out how to get this. And with the help of a [good `fopen` reference](https://en.cppreference.com/w/c/io/fopen) it shouldn't be to hard to match those to a `fopen` (and indeed `fdopen`) mode string. – Some programmer dude May 20 '20 at 09:22
  • @Someprogrammerdude - Thanks. I did go back, several times, and I could not figure that out. That is why I come here and ask, and I am eager to see answers. It might be hidden in plain sight, at least for me. – sancho.s ReinstateMonicaCellio May 20 '20 at 09:49
  • Use `F_GETFL` to get the flags, which should include the open-mode (which might need some bitwise masking to get out). – Some programmer dude May 20 '20 at 09:55
  • @Someprogrammerdude - As posted in the OP, I got to the point of using `F_GETFL`, and knowing it includes the open-mode. But I still couldn't put together the simple descriptive lists which are the target of the question, and the connection between them. – sancho.s ReinstateMonicaCellio May 20 '20 at 10:04
  • `O_READ` is `"r"`. `O_WRITE` is `"w"` or `"a"` (depending on where you want to write to the file). `O_RDWR` could be any of the other read-write strings, depending on your needs. – Some programmer dude May 20 '20 at 10:17
  • @Someprogrammerdude - I don't seem to have `O_READ`, even if I am using gcc and this https://www.gnu.org/software/libc/manual/html_node/Access-Modes.html. And I do have `O_ACCMODE`, in `fcntl-linux.h`. – sancho.s ReinstateMonicaCellio May 20 '20 at 10:58
  • Oh sorry my mistake, I meant `O_RDONLY` and `O_WRONLY`. – Some programmer dude May 20 '20 at 11:01
  • @Someprogrammerdude - Please check the table posted by Tony Tannous and my answer. They provide interesting info, which I guess it was not quite obvious to infer from the pages I had found. – sancho.s ReinstateMonicaCellio May 21 '20 at 10:55

2 Answers2

4

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:

  1. The list does not coincide with the table given by Tony Tannous.

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

  3. 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;
}
  • Nice detailed answer, I will soon remove my answer. For future readers who wonder what table I was refering to, it can be found in [fopen](http://man7.org/linux/man-pages/man3/fopen.3.html) man page. – Tony Tannous May 21 '20 at 11:37
  • Though w+ should create file if not exist... Open for reading and writing, I'd expect O_CREAT. `The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.` Obtained from [this](https://linux.die.net/man/3/fopen) source as well. Maybe tag Barmar? `w+` creates file when not exist. Tested. – Tony Tannous May 21 '20 at 12:06
  • **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** Most of the time you know a priori how you're going to use the file, whether for reading or writing, you don't need to get the mode dynamically. – Barmar May 21 '20 at 14:23
  • @TonyTannous - I'd suggest you don't remove the answer. Given it is official info, I think it may be useful for others too. Then whoever reads it will have to get the big picture by reading the rest of the answers/comments. – sancho.s ReinstateMonicaCellio May 21 '20 at 14:38
  • @Barmar - Agreed. What do you think about the two other points? – sancho.s ReinstateMonicaCellio May 21 '20 at 14:40
  • The distinction between `r+` and `w+` matters in `fopen()`, but not `fdopen()`. `O_CREAT` and `O_TRUNC` only affect what `open()` does while it's opening the file, they don't have any persistent effect on the descriptor. – Barmar May 21 '20 at 14:45
  • @Barmar - Your comment seems to completely bridge the gap between what I found and the table posted by Tony... great! Would you post it as an answer? – sancho.s ReinstateMonicaCellio May 21 '20 at 14:48
  • @Barmar - As for point 1, one may well need to write functions that would be targeted for any mode (I need something like this), and in those cases this is still a (serious?) problem/workload. Do you see any way around this? – sancho.s ReinstateMonicaCellio May 21 '20 at 14:51
  • Can you give examples? This is the whole reason why `fdopen()` has the mode argument, because it can't be inferred automatically. – Barmar May 21 '20 at 14:54
  • So you should just pass that requirement along in your functions. – Barmar May 21 '20 at 14:55
1

From fopen you can check how they correlate with w+ r etc...

enter image description here

Tony Tannous
  • 14,154
  • 10
  • 50
  • 86
  • What it seems the OP is asking about is the open mode flags (e.g. `O_RDWR` etc.) not the file-type or protection flags which are available from `st_mode`. This is to match is against a corresponding mode-string for `fdopen`. – Some programmer dude May 20 '20 at 09:41
  • Bingo!! That table, from an official source, is exactly what I was looking for. The man page I happen to hit doesn't have the table! – sancho.s ReinstateMonicaCellio May 20 '20 at 14:31
  • Note that `O_RDONLY` (e.g.) is not meant to be used for bitwise operations, see https://stackoverflow.com/a/61923919/2707864. Actually, I think that your `if (mode & O_RDONLY)` will not behave as you expect. You may double check and fix this, for the benefit of the community. – sancho.s ReinstateMonicaCellio May 20 '20 at 22:30
  • Please check the answer I added, its findings do not seem to completely agree with the table. – sancho.s ReinstateMonicaCellio May 21 '20 at 10:42