c - Usage of F_GETFL and F_SETFL

前端 未结 2 1852
你的背包
你的背包 2021-02-09 00:32

While trying to use fcntl() with command F_GETFL and F_SETFL, I got some questions:

  1. Why the flag returned by fcntl(

2条回答
  •  爱一瞬间的悲伤
    2021-02-09 00:57

    You asked:

    Why the flag returned by fcntl(fd, F_GETFL) only include a subset of bits of what I set when open file? Does it only show the ones that are modifiable?

    No; it only shows the ones that are "remembered" by the system, such as O_RDWR. These can really be called "flags". Some of the other bits ORed into the oflag parameter are more like "imperative instructions" to the open system call: for example, O_CREAT says "please create this file if it doesn't exist" and O_TRUNC says "please truncate it", neither of which are "flags". A file that was truncated on creation is indistinguishable from a file that was not truncated on creation: they're both just "files". So after open is done creating or truncating the file, it doesn't bother to "remember" that history. It only "remembers" important things, like whether the file is open for reading or writing.

    Edited to add: These different kinds of flags have semi-official names. O_RDWR is the "access mode" (remembered, non-modifiable); O_APPEND is an "operating mode" (remembered, usually modifiable); O_TRUNC is an "open-time flag" (pertains to the open operation itself, not to the file descriptor; therefore not remembered). Notice that the "access mode" is not modifiable — you can't use fcntl to turn a read-only fd into a write-only fd.

    When use fcntl(fd, F_SETFL, flag), how should I pass the flag param, do I need to read flag via fcntl(fd, F_GETFL) first, then modify it and pass it? Or internally it just do a bit & operation with the new param?

    F_SETFL overwrites the flags with exactly what you pass in (although it will ignore your puny attempts to set bits-that-aren't-really-flags, such as O_TRUNC). IF you want to set a specific flag and leave the other flags as-is, then you must F_GETFL the old flags, | the new flag in, and then F_SETFL the result. This must be done as two separate system calls; there is no atomic or thread-safe way to accomplish it as far as I know.

    Where can I find a full list of the 32 (or less) bits of open file flags?

    In fcntl.h or its documentation (man fcntl). For example, on my MacBook the man page says:

    The flags for the F_GETFL and F_SETFL commands are as follows:
    
          O_NONBLOCK   Non-blocking I/O; if no data is available to a read call, or if a write operation would block, the read or write
                       call returns -1 with the error EAGAIN.
    
          O_APPEND     Force each write to append at the end of file; corresponds to the O_APPEND flag of open(2).
    
          O_ASYNC      Enable the SIGIO signal to be sent to the process group when I/O is possible, e.g., upon availability of data to be
                       read.
    

    In other words, there are exactly three bits you can set (or unset) on OS X. Whereas on Linux, the man page says this:

    On Linux this command can change only the O_APPEND, O_ASYNC,
    O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
    

    Incidentally, some Linux filesystems have the concept of an "append-only file" at the filesystem level; if you open one of those files and then try to clear the resulting descriptor's O_APPEND flag, you'll get an EPERM error. The "append-only"-ness of a file can be controlled at the filesystem level using the chattr utility.


    Here's a more systematic version of your test program. It might not be of interest to you, but I learned something by writing it, so I'm leaving it here. :)

    #include 
    #include 
    #include 
    
    int main() {
        int fd = open("/tmp/fd_share.txt", O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0644);
    
        // append to empty file
        write(fd, "aaaaaaaaaa", 10);
    
        off_t cur = lseek(fd, 1, SEEK_SET);
        printf("offset after being set to 1: %ld\n", (long)cur);
    
        // append
        write(fd, "bbbbbbbb", 8);
    
        cur = lseek(fd, 0, SEEK_CUR);
        printf("offset after appending bbbbbbbb: %ld\n", (long)cur);
    
        cur = lseek(fd, 2, SEEK_SET);
        printf("offset after being set to 2: %ld\n", (long)cur);
    
        // now toggle "append mode" to FALSE
        int open_flag = fcntl(fd, F_GETFL);
        if (fcntl(fd, F_SETFL, open_flag & ~O_APPEND) == -1) {
            printf("failed to set flag\n");
            return 0;
        }
    
        cur = lseek(fd, 0, SEEK_CUR);
        printf("offset after unsetting O_APPEND: %ld\n", (long)cur);
    
        cur = lseek(fd, 3, SEEK_SET);
        printf("offset after being set to 3: %ld\n", (long)cur);
    
        // write without appending
        write(fd, "cccc", 4);
    
        cur = lseek(fd, 0, SEEK_CUR);
        printf("offset after writing cccc: %ld\n", (long)cur);
    
        // now toggle "append mode" to TRUE
        open_flag = fcntl(fd, F_GETFL);
        if (fcntl(fd, F_SETFL, open_flag | O_APPEND) == -1) {
            printf("failed to set flag\n");
            return 0;
        }
    
        cur = lseek(fd, 0, SEEK_CUR);
        printf("offset after unsetting O_APPEND: %ld\n", (long)cur);
    
        // append
        write(fd, "dd", 2);
    
        cur = lseek(fd, 0, SEEK_CUR);
        printf("offset after appending dd: %ld\n", (long)cur);
    
        close(fd);
    }
    

    The output of this program on my MacBook (as it should be on any POSIX system AFAIK) is:

    offset after being set to 1: 1
    offset after appending bbbbbbbb: 18
    offset after being set to 2: 2
    offset after unsetting O_APPEND: 2
    offset after being set to 3: 3
    offset after writing cccc: 7
    offset after unsetting O_APPEND: 7
    offset after appending dd: 20
    

提交回复
热议问题