C - Using F_GETFL and F_SETFL

When trying to use fcntl()

with the command F_GETFL

and F_SETFL

I have questions:

  • Why fcntl(fd, F_GETFL)

    does the flag returned only include a subset of the bits that I set when I opened the file? Does it only show the ones that can be changed?

  • When using fcntl(fd, F_SETFL, flag)

    , how do I pass the param parameter, I need to first read the flag through fcntl(fd, F_GETFL)

    and then change and pass it? Or, internally, is it just performing an operation &

    on the new parameter?

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

Code - [dup_fd_share.c]:

// prove duplicated fd shared file offset and open file status,
// TLPI exercise 5.5

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#define BUF_SIZE 100

void fd_share() {
    char *fp = "/tmp/fd_share.txt";
    char *buf = "abc\n";
    int write_size = 4;
    int fd, fd2;
    off_t cur, cur2;
    int open_flag, open_flag2;

    // open
    int flag = O_RDWR | O_CREAT | O_TRUNC | O_APPEND;
    printf("file flag param: %x\n", flag);
    fd = open(fp, flag, 0644);

    // dup
    fd2 = dup(fd);

    // initial offset
    cur = lseek(fd, 0, SEEK_CUR);
    printf("fd[%d] offset: %ld\n", fd, cur);
    cur2= lseek(fd2, 0, SEEK_CUR);
    printf("fd[%d] offset: %ld\n", fd2, cur2);

    // write, offset change,
    write(fd, buf, 4);
    printf("write %d chars\n", write_size);

    // new offset
    cur = lseek(fd, 0, SEEK_CUR);
    printf("fd[%d] offset: %ld\n", fd, cur);
    cur2= lseek(fd2, 0, SEEK_CUR);
    printf("fd[%d] offset: %ld\n", fd2, cur2);

    // get original open file flag,
    open_flag = fcntl(fd, F_GETFL);
    printf("fd[%d] open flag: %x\n", fd, open_flag);
    open_flag2 = fcntl(fd2, F_GETFL);
    printf("fd[%d] open flag: %x\n", fd2, open_flag2);

    // change open file flag,
    open_flag &= ~O_APPEND;
    if(fcntl(fd, F_SETFL, open_flag) == -1) {
        printf("failed to set flag\n");
        return;
    }
    printf("change open file flag, remove %s\n", "O_APPEND");

    open_flag = fcntl(fd, F_GETFL);
    printf("fd[%d] open flag: %x\n", fd, open_flag);
    open_flag2 = fcntl(fd2, F_GETFL);
    printf("fd[%d] open flag: %x\n", fd2, open_flag2);

    close(fd);
}

int main(int argc, char *argv[]) {
    fd_share();
    return 0;
}

      

Output:

file flag param: 642

fd[3] offset: 0
fd[4] offset: 0
write 4 chars
fd[3] offset: 4
fd[4] offset: 4

fd[3] open flag: 402
fd[4] open flag: 402
change open file flag, remove O_APPEND
fd[3] open flag: 2
fd[4] open flag: 2

      

+3


source to share


2 answers


You asked:

Why does the flag returned by fcntl (fd, F_GETFL) only include a subset of the bits I set when I opened the file? Does it display only those that can be changed?

Not; it only displays those that are "remembered" by the system, for example O_RDWR

. They can really be called "flags". Some of the other ORed bits in the parameter are oflag

more like "imperative instructions" to the system call open

: for example, it O_CREAT

says "create this file if it doesn't exist" and O_TRUNC

says "shorten it", none of which is a "flag". A file that was truncated at creation is indistinguishable from a file that was not truncated at creation: they are both just "files". So after the open

file is created or trimmed, it doesn't bother to "remember" the story. It only "remembers" important things, such as whether a file is open for reading or writing.

Edited to add: These flags of different types have semi-official names. O_RDWR

- this is the "access mode" (memorable, not changeable); O_APPEND

is a "mode of operation" (remembered, usually modified); O_TRUNC

is the "open time flag" (refers to the operation itself open

, not the file descriptor, so it is not remembered). Note that the "access mode" is not modified - you cannot use fcntl

to turn a read-only fd into a write-only fd.

When using fcntl(fd, F_SETFL, flag)

, how do I pass the param parameter, I need to first read the flag through fcntl(fd, F_GETFL)

and then change and pass it? Or, internally, is it just performing an operation &

on the new parameter?

F_SETFL

overwrites flags exactly with what you are passing through (although it will ignore your leisurely attempts to set bits-aren't-really-flags, for example O_TRUNC

). If you want to set a specific flag and leave the rest of the flags as they are, then you have to use the F_GETFL

old flags, |

enter a new flag, and then the F_SETFL

result. This must be done as two separate system calls; at least there is no atomic or thread safe way to achieve this as far as I know.

Where can I find a complete list of 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, three bits can be set (or disabled) on OS X. If 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.

      

By the way, some Linux filesystems have a file-only attachment concept at the filesystem level; if you open one of these files and then try to clear the resulting handle O_APPEND

, you will get an error EPERM

. The "append-only" feature of a file can be controlled at the file system level using a utility chattr

.


Here is a more systematic version of your test program. This may not interest you, but I learned something by writing it, so I leave 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 with any POSIX AFAIK system):

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

      

+1


source


1) The return of fcnl is the code described if the function is succceded and as:

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) The flags to be set are 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.

+3


source







All Articles