Skip to content
/linux-syscalls

Fichiers & E/S · Section 2

openat(2)

Ouvre un fichier relativement à un descripteur de répertoire — le remplaçant moderne de open().

Signature

#include <fcntl.h>

int openat(int dirfd, const char * pathname, int flags, mode_t mode);
dirfd
Descripteur de répertoire qui ancre pathname quand pathname est relatif. AT_FDCWD signifie « répertoire de travail ». Un pathname absolu rend dirfd ignoré (mais il est validé ; passer AT_FDCWD en cas de doute).
pathname
Chemin du fichier à ouvrir. Si absolu, dirfd est ignoré. Si relatif, dirfd l'ancre.
flags
Idem open() : mode d'accès OU avec des drapeaux de création/état (O_CREAT, O_CLOEXEC, etc.).
mode
Bits de permission appliqués uniquement avec O_CREAT ou O_TMPFILE.

Description

openat() ouvre le fichier en pathname, mais interprète pathname relativement au répertoire désigné par dirfd plutôt qu'au répertoire de travail du processus. Avec dirfd == AT_FDCWD, openat() se comporte exactement comme open(). Les autres arguments — flags, mode — sont identiques à open(). La glibc moderne implémente open() via openat() avec AT_FDCWD ; sur aarch64 le syscall hérité open() n'existe pas du tout, seulement openat() et openat2(). Au-delà du confort, openat() est la brique des opérations de système de fichiers sans course : conserver dirfd ouvert avec O_DIRECTORY permet de traverser une arborescence sans recomposer de chemins absolus, immunisant contre les échanges de liens symboliques et les renames aux niveaux supérieurs.

Numéros par architecture

ArchitectureNuméroABIPoint d'entrée
x86 (i386)295i386sys_openat
x64 (x86_64)257commonsys_openat
ARM64 (aarch64)56—sys_openat

Historique noyau

Introduit dans Linux 2.6.16.

  1. 2.6.16

    openat() a été ajouté en 2.6.16 avec la famille *at (fstatat, fchmodat, unlinkat…) pour permettre les traversées sans course et un répertoire de travail par thread, indépendant de chdir().

  2. 5.6

    openat2() (Linux 5.6) étend openat() avec une structure open_how portant des drapeaux de résolution comme RESOLVE_NO_SYMLINKS, RESOLVE_NO_MAGICLINKS, RESOLVE_BENEATH et RESOLVE_IN_ROOT — permettant aux serveurs d'ouvrir des chemins sous un répertoire fixe sans chroot.

seccomp & conteneurs

Docker default profile

Autorisé

Podman default profile

Autorisé

openat() est dans tous les profils par défaut. Le bloquer tue le processus — la libc moderne ne peut ouvrir aucun fichier sans lui. Le filtrage d'arguments sur dirfd est ardu car les fd sont dynamiques ; le levier pratique est le filtrage des resolve flags de openat2() ou le couplage de openat() avec Landlock pour le confinement par chemin.

libseccomp

seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat),  0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat2), 0);

Exemple strace

$ strace -e openat ls /etc
openat(AT_FDCWD, "/etc", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

La plupart des chemins de code passent par openat() plutôt que open() car la glibc réécrit l'appel. strace affiche dirfd comme AT_FDCWD quand applicable, et déballe les flags. -y résout dirfd en chemin ; -e trace=openat,open,openat2 filtre la famille ensemble.

Sécurité & observabilité

openat() est le point d'observation moderne de chaque ouverture de fichier sous Linux. Le tracepoint eBPF sys_enter_openat (et le hook LSM file_open) capture dirfd, le pathname complet et les drapeaux. Les rootkits hookent souvent openat() pour cacher les fichiers correspondant à leurs motifs à ls / find — la piste d'audit noyau diverge alors de la vue userspace, ce qui est la détection canonique. La famille *at permet aussi un sandboxing plus serré : un processus pré-ouvre un descripteur de répertoire, abandonne d'autres privilèges, et n'accède plus qu'aux chemins sous ce répertoire via openat() — un chroot du pauvre.

Erreurs

EACCES
Permission de recherche refusée sur un composant, ou fichier non lisible/écrit comme demandé.
EBADF
dirfd n'est ni AT_FDCWD ni un descripteur valide.
EEXIST
—
ENOENT
Un composant de pathname n'existe pas, ou O_CREAT absent et le fichier manquant.
ENOTDIR
pathname est relatif et dirfd désigne un non-répertoire.
EISDIR
—
ELOOP
—
ENAMETOOLONG
—
EMFILE
—
ENFILE
—

Drapeaux

AT_FDCWD
-100
Valeur sentinelle pour dirfd signifiant « répertoire de travail courant ». Définie à -100 dans <fcntl.h>.
AT_EMPTY_PATH
0x1000
Quand positionné (Linux 2.6.39+), un pathname vide opère sur dirfd lui-même — utile avec les descripteurs O_PATH pour fstatat/linkat. Requiert CAP_DAC_READ_SEARCH hors du processus.
AT_SYMLINK_NOFOLLOW
0x100
Ne pas déréférencer le composant final s'il est un lien symbolique.

Syscalls liés