File & I/O · Section 2
lseek(2)
Reposition a file descriptor's read/write offset.
Signature
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);- fd
- Seekable file descriptor (regular file, block device).
- offset
- Signed byte offset, interpreted per whence.
- whence
- SEEK_SET (absolute), SEEK_CUR (relative), SEEK_END (from EOF), SEEK_DATA (next data after offset), SEEK_HOLE (next hole after offset).
Description
lseek() repositions the file offset of fd to offset bytes interpreted according to whence: SEEK_SET (absolute from start), SEEK_CUR (relative to current), SEEK_END (relative to end — pass negative offsets to move backwards). It returns the resulting absolute offset, or (off_t) -1 with errno on failure. lseek() doesn't perform I/O — it just adjusts the kernel's per-fd position cursor. Seeking past the end of a writable file and writing creates a sparse hole (the intervening pages are zero on read but unallocated on disk). lseek() doesn't work on pipes, sockets, FIFOs, or terminals — they have no random-access offset and return -1/ESPIPE. On 32-bit ABIs with 64-bit off_t, the kernel exposes _llseek() (which returns the offset via an out-parameter to avoid the 32-bit return value clipping); modern glibc transparently selects the right syscall.
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 19 | i386 | sys_lseek |
| x64 (x86_64) | 8 | common | sys_lseek |
Kernel history
Introduced in Linux 1.0.
1.0
lseek() is one of the original Linux syscalls. The 32-bit signature limits offset to 2 GiB on i386; _llseek() (1995) added the 64-bit path.
3.1
SEEK_DATA and SEEK_HOLE were standardised so userspace can efficiently walk sparse files without reading every byte. Supported by ext4, XFS, btrfs and tmpfs; on filesystems that don't expose hole info, the kernel falls back to whole-file as one data region.
seccomp & containers
Docker default profile
Allowed
Podman default profile
Allowed
lseek() and _llseek() are on every default profile. No useful argument-level filtering — applications use whence and offset freely.
libseccomp
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(_llseek), 0);strace example
$ strace -e lseek dd if=/etc/hostname of=/dev/null bs=1 count=4 skip=2
lseek(0, 2, SEEK_SET) = 2lseek() lines pair with read()/write() to form the full I/O motion. The most informative use is following a sparse-file walker: SEEK_DATA → read → SEEK_HOLE → SEEK_DATA → read alternations. -e lseek filters.
Security & observability
lseek() is rarely a direct security signal. The one notable pattern: log-rotation evasion where malware seeks backwards in a log file and overwrites earlier bytes (instead of appending) to hide entries — though most modern logs are append-only via O_APPEND, which makes the seek+write race a no-op. eBPF tracepoint sys_enter_lseek is generally too noisy for production monitoring.
Errors
- EBADF
- fd is not an open descriptor.
- EINVAL
- whence is invalid, or offset would produce an invalid absolute position (negative).
- ENXIO
- SEEK_DATA / SEEK_HOLE: offset is beyond EOF.
- EOVERFLOW
- Resulting offset exceeds off_t range (32-bit non-LFS programs hitting a 4 GiB+ position).
- ESPIPE
- fd refers to a pipe, socket, or FIFO — no random access.
Flags
- SEEK_SET
- 0
- —
- SEEK_CUR
- 1
- —
- SEEK_END
- 2
- —
- SEEK_DATA
- 3
- Seek to the start of the next region containing data at or after offset. Used by cp --sparse and rsync to skip holes efficiently.
- SEEK_HOLE
- 4
- Seek to the next hole at or after offset. Combined with SEEK_DATA gives an O(holes+data) enumeration of a sparse file's layout.