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
| Architecture | Numéro | ABI | Point d'entrée |
|---|---|---|---|
| x86 (i386) | 295 | i386 | sys_openat |
| x64 (x86_64) | 257 | common | sys_openat |
| ARM64 (aarch64) | 56 | — | sys_openat |
Historique noyau
Introduit dans Linux 2.6.16.
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().
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) = 3La 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.