PIVOT_ROOT(2) Manuel du programmeur Linux PIVOT_ROOT(2)

pivot_root - Modifier le montage racine

#include <sys/syscall.h>      /* Définition des constantes SYS_* */
#include <unistd.h>
int syscall(SYS_pivot_root, const char *new_root, const char *put_old);

Note: glibc provides no wrapper for pivot_root(), necessitating the use of syscall(2).

pivot_root() modifie le montage racine dans l'espace de noms de montage du processus appelant. Plus précisément, il déplace le montage racine dans le répertoire put_old et il fait de new_root le nouveau montage racine. Le processus appelant doit avoir la capacité CAP_SYS_ADMIN dans l'espace de noms de l'utilisateur à qui appartient l'espace de noms de montage de l'appelant.

pivot_root() change le répertoire racine et le répertoire de travail de chaque processus ou de chaque thread du même espace de noms de montage en new_root s'ils pointent vers l'ancien répertoire de montage (voir aussi les NOTES). D'un autre côté, pivot_root() ne modifie pas le répertoire de travail de l'appelant (sauf s'il est sur l'ancien répertoire racine), ainsi, il doit être suivi d'un appel chdir("/").

Les restrictions suivantes s'appliquent :

  • new_root et put_old doivent être des répertoires.
  • new_root et put_old ne doivent pas être sur le même montage que la racine actuelle.
  • put_old doit être sur new_root ou un descendant de new_root : à savoir qu'ajouter un nombre positif de préfixes « /.. » au chemin vers lequel pointe put_old doit ramener au même répertoire que new_root.
  • new_root doit être un chemin vers un point de montage ; mais il ne peut pas être « / ». Un chemin qui n'est pas un point de montage peut le devenir en montant en miroir le point sur lui-même.
  • Le type de propagation du montage parent de new_root et le montage parent du répertoire racine actuel ne doivent pas être MS_SHARED ; de même, si put_old est un point de montage existant, son type de propagation ne doit pas être MS_SHARED. Ces restrictions garantissent que pivot_root() ne propage jamais de changement sur un autre espace de noms de montage.
  • Le répertoire racine actuel doit être un point de montage.

En cas de succès, zéro est renvoyé. En cas d'erreur, -1 est renvoyé et errno est définie pour préciser l'erreur.

pivot_root() peut échouer avec une des erreurs de stat(2). Il peut aussi échouer avec les erreurs suivantes :

new_root ou put_old est sur le montage racine actuel (cette erreur vaut pour le cas pathologique où new_root est « / ».
new_root n'est pas un point de montage.
put_old n'est pas sur new_root ou un descendant.
Le répertoire racine actuel n'est pas un point de montage (du fait d'un précédent chroot(2)).
La racine actuelle est sur le montage rootfs (ramfs initial) ; voir les NOTES.
Soit le point de montage sur new_root, soit le montage parent de ce point de montage, a un type de propagation MS_SHARED.
put_old est un point de montage et il a le type de propagation MS_SHARED.
new_root ou put_old n'est pas un répertoire.
Le processus appelant n'a pas la capacité CAP_SYS_ADMIN.

pivot_root() a été introduit dans Linux 2.3.41.

pivot_root() est spécifique à Linux et donc non portable.

Une interface en ligne de commande pour cet appel système est fournie par pivot_root(8).

pivot_root() permet à un appelant de passer à un nouveau système de fichiers racine tout en mettant l'ancien montage racine dans new_root, d'où il peut être démonté (le fait de déplacer tous les processus ayant un répertoire racine ou actuel dans le répertoire racine vers une nouvelle racine libère l'ancienne racine des utilisateurs, permettant de démonter plus facilement l'ancien montage racine).

L'utilisation typique de pivot_root() est durant le démarrage du système, lorsque le système monte un système de fichiers temporaire (par exemple un initrd(4)) puis monte le véritable système de fichiers et le transforme éventuellement en racine pour tous les processus et threads concernés. Une utilisation moderne consiste à définir un système de fichiers racine pendant la création d'un conteneur.

Le fait que pivot_root() modifie les répertoires racine et de travail du processus comme indiqué dans la DESCRIPTION est nécessaire afin d'empêcher les threads du noyau d'occuper l'ancien montage racine avec leurs répertoires racine et de travail, même s'ils n'accèdent jamais au système de fichiers en aucune manière.

Le rootfs (initial ramfs) ne peut pas être pivot_root()é. La méthode recommandée pour modifier le système de fichiers racine dans ce cas consiste à tout effacer sur le rootfs, monter par-dessus la nouvelle racine, rattacher stdin/stdout/stderr au nouveau /dev/console et exécuter le nouvel init(1). Il existe des programmes d'aide pour ce processus ; voir switch_root(8).

new_root et put_old peuvent être le même répertoire. En particulier, la séquence suivante permet une opération pivot-root sans devoir créer et supprimer un répertoire temporaire :


chdir(new_root);
pivot_root(".", ".");
umount2(".", MNT_DETACH);

Cette séquence réussit parce que l'appel pivot_root() place le point de montage racine au sommet du nouveau point de montage racine sur /. Alors, le répertoire racine et celui de travail du processus appelant se rapportent au nouveau point de montage racine (new_root). Lors de l'appel umount() suivant, la résolution de "." commence par new_root puis monte la liste des points de montage empilés dans /, d'où il résulte que l'ancien point de montage est démonté.

Pendant de nombreuses années, cette page de manuel comportait le texte suivant :

pivot_root() peut changer ou non les répertoires racine et de travail en cours de tous les processus et threads qui utilisaient l'ancien répertoire racine. L'appelant de pivot_root() doit s'assurer que les processus ayant pour racine ou répertoire de travail l'ancien répertoire se comportent correctement. Le meilleur moyen est de modifier leur répertoire de travail et répertoire racine pour new_root avant d'invoquer pivot_root().

Ce texte, écrit avant même la finalisation de l'implémentation de l'appel système dans le noyau, visait sans doute à avertir les utilisateurs à ce moment là que l'implémentation pourrait changer avant la publication définitive. Toutefois, le comportement indiqué dans DESCRIPTION est resté valable depuis la première implémentation de cet appel système et il ne changera pas maintenant.

Le programme ci-dessous montre l'utilisation de pivot_root() dans un espace de noms de montage créé en utilisant clone(2). Après avoir changé le répertoire racine appelé dans le premier paramètre de la ligne de commande du programme, l'enfant créé par clone(2) exécute alors le programme nommé dans les autres paramètres de la ligne de commande.

Nous montrons le programme en créant un répertoire qui servira de nouveau système de fichiers racine et en mettant une copie de l'exécutable busybox(1) (liée statiquement) dans ce répertoire.


$ mkdir /tmp/rootfs
$ ls -id /tmp/rootfs    # Numéro d’inœud dans le nouveau répertoire racine
319459 /tmp/rootfs
$ cp $(which busybox) /tmp/rootfs
$ PS1='bbsh$ ' sudo ./pivot_root_demo /tmp/rootfs /busybox sh
bbsh$ PATH=/
bbsh$ busybox ln busybox ln
bbsh$ ln busybox echo
bbsh$ ln busybox ls
bbsh$ ls
busybox  echo     ln       ls
bbsh$ ls -id /          # Comparaison avec le numéro d’inœud au-dessus
319459 /
bbsh$ echo 'hello world'
hello world

/* pivot_root_demo.c */
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <limits.h>
#include <sys/mman.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \

} while (0) static int pivot_root(const char *new_root, const char *put_old) {
return syscall(SYS_pivot_root, new_root, put_old); } #define STACK_SIZE (1024 * 1024) static int /* Fonction de démarrage pour l’enfant cloné */ child(void *arg) {
char **args = arg;
char *new_root = args[0];
const char *put_old = "/oldrootfs";
char path[PATH_MAX];
/* Ensure that 'new_root' and its parent mount don't have
shared propagation (which would cause pivot_root() to
return an error), and prevent propagation of mount
events to the initial mount namespace. */
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1)
errExit("mount-MS_PRIVATE");
/* Ensure that 'new_root' is a mount point. */
if (mount(new_root, new_root, NULL, MS_BIND, NULL) == -1)
errExit("mount-MS_BIND");
/* Create directory to which old root will be pivoted. */
snprintf(path, sizeof(path), "%s/%s", new_root, put_old);
if (mkdir(path, 0777) == -1)
errExit("mkdir");
/* And pivot the root filesystem. */
if (pivot_root(new_root, path) == -1)
errExit("pivot_root");
/* Switch the current working directory to "/". */
if (chdir("/") == -1)
errExit("chdir");
/* Unmount old root and remove mount point. */
if (umount2(put_old, MNT_DETACH) == -1)
perror("umount2");
if (rmdir(put_old) == -1)
perror("rmdir");
/* Exécuter la commande indiquée dans argv[1]... */
execv(args[1], &args[1]);
errExit("execv"); } int main(int argc, char *argv[]) {
/* Create a child process in a new mount namespace. */
char *stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (stack == MAP_FAILED)
errExit("mmap");
if (clone(child, stack + STACK_SIZE,
CLONE_NEWNS | SIGCHLD, &argv[1]) == -1)
errExit("clone");
/* Parent falls through to here; wait for child. */
if (wait(NULL) == -1)
errExit("wait");
exit(EXIT_SUCCESS); }

chdir(2), chroot(2), mount(2), stat(2), initrd(4), mount_namespaces(7), pivot_root(8), switch_root(8)

Cette page fait partie de la publication 5.13 du projet man-pages Linux. Une description du projet et des instructions pour signaler des anomalies et la dernière version de cette page peuvent être trouvées à l'adresse https://www.kernel.org/doc/man-pages/.

La traduction française de cette page de manuel a été créée par Christophe Blaess https://www.blaess.fr/christophe/, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Philippe MENGUAL <jpmengual@debian.org>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.

22 mars 2021 Linux