Skip to content
/linux-syscalls

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

ArchitectureNumberABIEntry point
x86 (i386)19i386sys_lseek
x64 (x86_64)8commonsys_lseek

Kernel history

Introduced in Linux 1.0.

  1. 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.

  2. 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)                   = 2

lseek() 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.

Related syscalls