Traditional POSIX record locks have a property that can surprise code with several descriptors for the same file: lock ownership is associated with the process, and closing a descriptor for that file can release the process’s locks on it. Linux open file description locks move ownership to the kernel open file description referenced by a descriptor.
The interface still uses fcntl() and struct flock, but the ownership boundary changes. F_OFD_SETLK, F_OFD_SETLKW, and F_OFD_GETLK make byte-range locking track the open file description rather than the process identity.
The lock owner sits behind the descriptor
A file descriptor is an integer in a process descriptor table. It refers to an open file description, which carries state associated with an open instance of a file. dup(), dup2(), F_DUPFD, and descriptors inherited through fork() can refer to the same open file description.
An OFD lock is associated with that shared object. Duplicating the descriptor therefore does not create a second lock owner.
fd 4 ----\
-> open file description A -> inode
fd 9 ----/ |
+-> OFD byte-range lockA separate open() of the same pathname normally creates another open file description:
fd 4 -> open file description A -> inode
fd 7 -> open file description B -> inodeOFD locks acquired through A and B can conflict even when both descriptors exist in the same process. This makes the ownership model usable between threads when each thread independently opens the file.
Closing a duplicate does not release the shared lock
For OFD locks, automatic release occurs when the last descriptor referencing the relevant open file description is closed. If fd_a is duplicated into fd_b, closing only fd_a leaves the open file description reachable through fd_b, so its OFD locks remain.
That differs materially from traditional F_SETLK process-associated record locks. On Linux, closing any descriptor for the same file in that process can remove those process-associated locks, including locks established through another descriptor.
The OFD rule aligns lock lifetime with the lifetime of the open file description:
open -> fd 4 -> OFD A -> lock held
dup -> fd 9 -> OFD A -> lock held
close(fd 4) -> OFD A -> lock held
close(fd 9) -> OFD A destroyed -> lock releasedExplicitly applying F_UNLCK can release a range earlier.
fork preserves the ownership relationship
After fork(), inherited descriptors in the child refer to the same open file descriptions as the corresponding descriptors in the parent. An OFD lock therefore remains attached to the shared description across that boundary.
This is not equivalent to creating an independent child-owned copy of the lock. Parent and child descriptors that reference the same open file description operate on the same OFD lock owner. A lock change through one such descriptor affects the lock state associated with that description.
Traditional process-associated record locks use a different ownership model and are not inherited by the child as child-owned locks.
Range semantics still come from struct flock
The ownership change does not remove byte-range semantics. The request still uses struct flock fields such as l_type, l_whence, l_start, and l_len.
#define _GNU_SOURCE
#include <fcntl.h>
#include <string.h>
struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 4096;
lock.l_len = 4096;
lock.l_pid = 0;
int rc = fcntl(fd, F_OFD_SETLK, &lock);For OFD lock operations, l_pid must be zero. F_OFD_SETLK is nonblocking and fails with EAGAIN when an incompatible lock prevents acquisition. F_OFD_SETLKW waits for the conflicting lock to be released and can return EINTR when interrupted by a caught signal.
A read lock permits compatible read locks on the same region, while a write lock conflicts with overlapping read or write locks owned through a different conflicting lock owner.
Duplicate descriptors are compatible with themselves
Requests made through descriptors that reference the same open file description are treated as belonging to one owner. An overlapping request can convert existing lock state rather than conflict with itself. The resulting range may be split, shrunk, or coalesced as lock types and ranges change.
This property is distinct from two separate calls to open(). Separate open file descriptions can contend with each other, including when they are held by separate threads inside one process.
That distinction is the core concurrency boundary:
dup(existing_fd) -> shared OFD identity
open(path, ...) -> new OFD identityChoosing between those operations can therefore determine whether two execution contexts share a lock owner or can block each other.
OFD and traditional record locks still interact
OFD locks are not an isolated namespace from traditional POSIX record locks. An incompatible OFD lock and traditional record lock on the same file region conflict, even when the requests originate from the same process and descriptor.
Code cannot safely treat F_OFD_SETLK and F_SETLK as independent coordination systems over the same ranges. Mixing them requires accounting for cross-type conflicts as well as their different ownership and release rules.
flock() is another locking interface with its own platform and filesystem considerations. Similar ownership intuition does not make these APIs interchangeable.
Blocking OFD locks omit deadlock detection
Linux does not perform deadlock detection for OFD locks. A blocking F_OFD_SETLKW request can therefore participate in a lock cycle without the kernel reporting the cycle through the deadlock-detection behavior available for traditional process-associated record locking.
Applications using several byte ranges or several files need a lock-ordering policy if cyclic waits are possible. The kernel’s OFD ownership model solves descriptor-lifetime problems; it does not supply a global dependency analysis for blocking lock acquisition.
The useful boundary is lifetime identity
OFD locks matter most when process identity is too coarse for the required ownership lifetime. A duplicated or inherited descriptor can deliberately share one lock owner, while an independently opened descriptor can represent another owner inside the same process.
That makes descriptor topology part of the locking design. The significant question is not merely which integer descriptor issued fcntl(), but which open file description that descriptor reaches and how long references to that description remain alive.