Skip to content
/linux-syscalls

File & I/O · Section 2

fcntl(2)

Multiplexed file descriptor control — flags, locks, leases, pipe sizes, and duplication.

Signature

#include <fcntl.h>
#include <unistd.h>

int fcntl(int fd, int cmd, ... arg);
fd
Target file descriptor.
cmd
Operation code (see flags table for the common ones).
arg
Operation-specific. May be an int (F_SETFL flags, F_DUPFD min-fd), a pointer to struct flock (F_*LK commands), or absent (F_GETFL).

Description

fcntl() is a multiplexer for per-descriptor operations. The cmd argument selects the operation; the third argument is variadic and operation-specific. Important commands: F_GETFL / F_SETFL read and modify the open-file status flags (O_NONBLOCK, O_APPEND, O_ASYNC) — note that O_RDONLY/O_WRONLY/O_RDWR are NOT changeable via F_SETFL. F_GETFD / F_SETFD manage descriptor flags (only FD_CLOEXEC is defined). F_DUPFD duplicates fd to the lowest available number ≥ arg; F_DUPFD_CLOEXEC does the same atomically with close-on-exec set. F_SETLK / F_SETLKW / F_GETLK implement POSIX advisory record locks (per-process — fundamentally broken across multi-threaded programs, which is why F_OFD_* was added). F_SETPIPE_SZ resizes a pipe's kernel buffer. Modern code increasingly prefers dedicated syscalls (dup3, pipe2) over fcntl() for the same operations.

Architecture mapping

ArchitectureNumberABIEntry point
x86 (i386)55i386sys_fcntl
x64 (x86_64)72commonsys_fcntl

Kernel history

Introduced in Linux 1.0.

  1. 1.0

    fcntl() has been part of Linux since 1.0 with broadly POSIX semantics.

  2. 2.6.0

    File leases (F_SETLEASE) and pipe-size control (F_SETPIPE_SZ) were added — leases let userspace cooperate with the kernel on file-lease breaking for cache coherency; pipe-size control lets high-throughput producers/consumers buy more buffering than the default 64 KiB.

  3. 3.15

    Open-file-description locks (F_OFD_SETLK, F_OFD_SETLKW, F_OFD_GETLK) were added to fix POSIX record locks' century-old defect — POSIX locks are tied to the PID and silently released when *any* descriptor on the file is closed, making them useless in multi-threaded programs. OFD locks are tied to the open-file description and behave sanely.

seccomp & containers

Docker default profile

Allowed

Podman default profile

Allowed

fcntl() is on every default profile and effectively un-blockable: every dynamic loader uses F_GETFD/F_SETFD on inherited fds. Argument filtering on cmd is theoretically possible (allow F_GETFL, F_SETFL, F_GETFD, F_SETFD, F_DUPFD, F_DUPFD_CLOEXEC; deny F_SETOWN, F_SETLEASE, F_NOTIFY) but the wins are small unless you have a specific threat model.

libseccomp

seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);

strace example

$ strace -e fcntl bash -c 'true'
fcntl(0, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fcntl(0, F_DUPFD, 10)                   = 10
fcntl(255, F_SETFD, FD_CLOEXEC)         = 0

fcntl() lines decode cmd symbolically and flag-tables for F_GETFL/F_SETFL. The startup sequence of any non-trivial binary contains 5-20 fcntl() calls from glibc setting up stdio buffering, CLOEXEC on the runtime fds, and signal-driven I/O for the GC of some runtimes.

Security & observability

fcntl() with F_SETOWN can deliver SIGIO to a target PID — historically a way to send signals to processes you don't own, mostly closed since 2.6 era hardening. F_NOTIFY (deprecated) once exposed information leaks. F_SETLEASE lets userspace participate in kernel-cache invalidation; misuse can deadlock the filesystem. For container hardening, the most actionable insight is that fcntl(F_SETFD, FD_CLOEXEC) on every long-lived fd is mandatory hygiene; tools like checkpoint/restore (CRIU) lean on this.

Errors

EACCES
F_SETLK / F_SETLKW on a fd that is conflicting (file mode lock semantics).
EAGAIN
F_SETLK: another process holds a conflicting lock and we asked not to wait.
EBADF
fd is not an open descriptor (or, for F_DUPFD, arg is outside the allowed range).
EDEADLK
F_SETLKW would deadlock against another lock holder.
EFAULT
EINTR
EINVAL
cmd not recognised, or arg invalid for cmd.
EMFILE
ENOLCK
Kernel out of resources for the lock table.
EPERM
F_SETLEASE / F_SETOWN: caller lacks the necessary privilege/capability.

Flags

F_DUPFD
0
F_GETFD
1
F_SETFD
2
F_GETFL
3
Return the open-file status flags (O_ACCMODE | other flags). Used to convert a blocking fd to non-blocking via 'flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK)'.
F_SETFL
4
Set the open-file status flags. Cannot change O_RDONLY/O_WRONLY/O_RDWR or O_CREAT/O_EXCL/O_TRUNC — those are fixed at open time.
F_GETLK
5
F_SETLK
6
F_SETLKW
7
F_DUPFD_CLOEXEC
1030
Like F_DUPFD but atomically sets FD_CLOEXEC on the new descriptor. Use this in any forking program.
F_SETPIPE_SZ
1031
Set the kernel buffer size for a pipe. Capped at /proc/sys/fs/pipe-max-size (default 1 MiB).
F_GETPIPE_SZ
1032
F_OFD_GETLK
36
F_OFD_SETLK
37
Open-file-description record lock. Unlike F_SETLK, the lock is tied to the open-file description (not the PID), so multi-threaded programs holding the same fd see consistent locking semantics. The 2014 fix for one of POSIX's longest-standing footguns.
F_OFD_SETLKW
38
FD_CLOEXEC
1
Descriptor flag: close on execve. The single most important flag for any fd you don't want leaking to child processes.

Related syscalls