c - Usage of F_GETFL and F_SETFL

前端 未结 2 1850
你的背包
你的背包 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 <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    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
    
    0 讨论(0)
  • 2021-02-09 01:18

    1) The return of fcnl is a code that described if the function succceded and how:

    RETURN VALUE

       For a successful call, the return value depends on the operation:
    
       F_DUPFD  The new descriptor.
    
       F_GETFD  Value of file descriptor flags.
    
       F_GETFL  Value of file status flags.
    
       F_GETLEASE
                Type of lease held on file descriptor.
    
       F_GETOWN Value of descriptor owner.
    
       F_GETSIG Value of signal sent when read or write becomes possible, or
                zero for traditional SIGIO behavior.
    
       F_GETPIPE_SZ, F_SETPIPE_SZ
                The pipe capacity.
    
       F_GET_SEALS
                A bit mask identifying the seals that have been set for the
                inode referred to by fd.
    
       All other commands
                Zero.
    
       On error, -1 is returned, and errno is set appropriately.
    

    ERRORS

       EACCES or EAGAIN
              Operation is prohibited by locks held by other processes.
    
       EAGAIN The operation is prohibited because the file has been memory-
              mapped by another process.
    
       EBADF  fd is not an open file descriptor
    
       EBADF  cmd is F_SETLK or F_SETLKW and the file descriptor open mode
              doesn't match with the type of lock requested.
    
       EBUSY  cmd is F_SETPIPE_SZ and the new pipe capacity specified in arg
              is smaller than the amount of buffer space currently used to
              store data in the pipe.
    
       EBUSY  cmd is F_ADD_SEALS, arg includes F_SEAL_WRITE, and there
              exists a writable, shared mapping on the file referred to by
              fd.
    
       EDEADLK
              It was detected that the specified F_SETLKW command would
              cause a deadlock.
    
       EFAULT lock is outside your accessible address space.
    
       EINTR  cmd is F_SETLKW or F_OFD_SETLKW and the operation was
              interrupted by a signal; see signal(7).
    
       EINTR  cmd is F_GETLK, F_SETLK, F_OFD_GETLK, or F_OFD_SETLK, and the
              operation was interrupted by a signal before the lock was
              checked or acquired.  Most likely when locking a remote file
              (e.g., locking over NFS), but can sometimes happen locally.
    
       EINVAL The value specified in cmd is not recognized by this kernel.
    
       EINVAL cmd is F_ADD_SEALS and arg includes an unrecognized sealing
              bit.
    
       EINVAL cmd is F_ADD_SEALS or F_GET_SEALS and the filesystem
              containing the inode referred to by fd does not support
              sealing.
    
       EINVAL cmd is F_DUPFD and arg is negative or is greater than the
              maximum allowable value (see the discussion of RLIMIT_NOFILE
              in getrlimit(2)).
    
       EINVAL cmd is F_SETSIG and arg is not an allowable signal number.
    
       EINVAL cmd is F_OFD_SETLK, F_OFD_SETLKW, or F_OFD_GETLK, and l_pid
              was not specified as zero.
    
       EMFILE cmd is F_DUPFD and the process already has the maximum number
              of file descriptors open.
    
       ENOLCK Too many segment locks open, lock table is full, or a remote
              locking protocol failed (e.g., locking over NFS).
    
       ENOTDIR
              F_NOTIFY was specified in cmd, but fd does not refer to a
              directory.
    
       EPERM  Attempted to clear the O_APPEND flag on a file that has the
              append-only attribute set.
    
       EPERM  cmd was F_ADD_SEALS, but fd was not open for writing or the
              current set of seals on the file already includes F_SEAL_SEAL.
    

    2) Flags to be set is your choice: :

    F_SETFL (int)

       Set the file status flags to the value specified by arg.  File
       access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation
       flags (i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are
       ignored.  On Linux this command can change only the O_APPEND,
       O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.  It is not
       possible to change the O_DSYNC and O_SYNC flags; see BUGS,
       below.
    

    3) HERE you have a complete description.

    0 讨论(0)
提交回复
热议问题