Scroll to navigation

PIVOT_ROOT(2) Руководство программиста Linux PIVOT_ROOT(2)

ИМЯ

pivot_root - change the root mount

СИНТАКСИС

int pivot_root(const char *new_root, const char *put_old);

Замечание: В glibc нет обёрточной функции для данного системного вызова; смотрите ЗАМЕЧАНИЯ.

ОПИСАНИЕ

pivot_root() changes the root mount in the mount namespace of the calling process. More precisely, it moves the root mount to the directory put_old and makes new_root the new root mount. The calling process must have the CAP_SYS_ADMIN capability in the user namespace that owns the caller's mount namespace.

pivot_root() changes the root directory and the current working directory of each process or thread in the same mount namespace to new_root if they point to the old root directory. (See also NOTES.) On the other hand, pivot_root() does not change the caller's current working directory (unless it is on the old root directory), and thus it should be followed by a chdir("/") call.

Накладываются следующие ограничения:

  • Аргументы new_root и put_old должны быть каталогами.
  • new_root and put_old must not be on the same mount as the current root.
  • put_old must be at or underneath new_root; that is, adding some nonnegative number of "/.." prefixes to the pathname pointed to by put_old must yield the same directory as new_root.
  • new_root must be a path to a mount point, but can't be "/". A path that is not already a mount point can be converted into one by bind mounting the path onto itself.
  • Тип распространения родительского монтирования new_root и родительского монтирования текущего корневого каталога не должно быть равно MS_SHARED; схожим образом, если put_old — существующая точка монтирования, то её тип распространения не должен быть MS_SHARED. Эти ограничения гарантируют, что pivot_root() никогда не распространит изменения в другие пространства имён монтирования.
  • Текущий корневой каталог должен быть точкой монтирования.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении возвращается 0. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.

ОШИБКИ

Вызов pivot_root() может завершиться любой из ошибок что и stat(2). Кроме это, могут возвращаться следующие ошибки:

new_root or put_old is on the current root mount. (This error covers the pathological case where new_root is "/".)
Значение new_root не является точкой монтирования.
put_old is not at or underneath new_root.
Текущий корневой каталог не является точкой монтирования (из-за предшествующего chroot(2)).
The current root is on the rootfs (initial ramfs) mount; see NOTES.
Точка монтирования new_root и её родительское монтирование имеет тип распространения MS_SHARED.
Значение put_old является точкой монтирования и имеет тип распространения MS_SHARED.
Аргумент new_root или put_old не является каталогом.
Вызывающий процесс не имеет мандата CAP_SYS_ADMIN.

ВЕРСИИ

Вызов pivot_root() появился в Linux 2.3.41.

СООТВЕТСТВИЕ СТАНДАРТАМ

Вызов pivot_root() есть только в Linux и поэтому его использование не переносимо.

ЗАМЕЧАНИЯ

В glibc нет обёртки для данного системного вызова; запускайте его с помощью syscall(2).

Интерфейсом командной строки для этого системного вызова является программа pivot_root(8).

pivot_root() allows the caller to switch to a new root filesystem while at the same time placing the old root mount at a location under new_root from where it can subsequently be unmounted. (The fact that it moves all processes that have a root directory or current working directory on the old root directory to the new root frees the old root directory of users, allowing the old root mount to be unmounted more easily.)

One use of pivot_root() is during system startup, when the system mounts a temporary root filesystem (e.g., an initrd(4)), then mounts the real root filesystem, and eventually turns the latter into the root directory of all relevant processes and threads. A modern use is to set up a root filesystem during the creation of a container.

The fact that pivot_root() modifies process root and current working directories in the manner noted in DESCRIPTION is necessary in order to prevent kernel threads from keeping the old root mount busy with their root and current working directories, even if they never access the filesystem in any way.

Для rootfs (начальная ramfs) нельзя вызвать pivot_root(). Рекомендуемым методом изменения корневой файловой системы в этом случае является удаление всего в rootfs, перемонтирование в rootfs нового корня, присоединение stdin/stdout/stderr к новой /dev/console и запуск нового init(1). Для этого существуют вспомогательные программы; смотрите switch_root(8).

pivot_root(".", ".")

Значения new_root и put_old могут быть одним каталогом. В частности, разрешена следующая последовательность операции pivot-root без необходимости создания и удаления временного каталога:


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

This sequence succeeds because the pivot_root() call stacks the old root mount point on top of the new root mount point at /. At that point, the calling process's root directory and current working directory refer to the new root mount point (new_root). During the subsequent umount() call, resolution of "." starts with new_root and then moves up the list of mounts stacked at /, with the result that old root mount point is unmounted.

Исторические замечания

For many years, this manual page carried the following text:

pivot_root() may or may not change the current root and the current working directory of any processes or threads which use the old root directory. The caller of pivot_root() must ensure that processes with root or current working directory at the old root operate correctly in either case. An easy way to ensure this is to change their root and current working directory to new_root before invoking pivot_root().

This text, written before the system call implementation was even finalized in the kernel, was probably intended to warn users at that time that the implementation might change before final release. However, the behavior stated in DESCRIPTION has remained consistent since this system call was first implemented and will not change now.

ПРИМЕРЫ

Представленная ниже программа демонстрирует использование pivot_root() внутри пространства монтирования, которое создаётся с помощью clone(2). После перехода (pivoting) в корневой каталог с именем из первого параметра командной строки, потомок, созданный clone(2), выполняет программу с именем из оставшихся параметров командной строки.

Для работы программы создадим каталог, который будет служить новой корневой файловой системой, и поместим копию исполняемого файла busybox(1) (скомпонованного статически) в этот каталог.


$ mkdir /tmp/rootfs
$ ls -id /tmp/rootfs    # выведем номер иноды нового корневого каталога
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 /          # сравните с номером иноды выше
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 /* начальная функция клонированного потомка */ child(void *arg) {
char **args = arg;
char *new_root = args[0];
const char *put_old = "/oldrootfs";
char path[PATH_MAX];
/* Проверим, что 'new_root' и его родительское монтирование
не имеют общего распространения (из-за чего pivot_root() вернул бы
ошибку), и отключим распространение событий монтирования в
первоначальное пространство имён монтирования */
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1)
errExit("mount-MS_PRIVATE");
/* Проверим, что 'new_root' является точкой монтирования */
if (mount(new_root, new_root, NULL, MS_BIND, NULL) == -1)
errExit("mount-MS_BIND");
/* Создадим каталог, в котором перейдёт старый корень */
snprintf(path, sizeof(path), "%s/%s", new_root, put_old);
if (mkdir(path, 0777) == -1)
errExit("mkdir");
/* и переведём корневую файловую систему */
if (pivot_root(new_root, path) == -1)
errExit("pivot_root");
/* сменим текущий рабочий каталог на «/» */
if (chdir("/") == -1)
errExit("chdir");
/* размонтируем старый корень и удалим точку монтирования */
if (umount2(put_old, MNT_DETACH) == -1)
perror("umount2");
if (rmdir(put_old) == -1)
perror("rmdir");
/* запустим команду, указанную в argv[1]... */
execv(args[1], &args[1]);
errExit("execv"); } int main(int argc, char *argv[]) {
/* создадим дочерний процесс в новом пространстве имён монтирования */
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");
/* предок попадает сюда; ждём потомка */
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)

ЗАМЕЧАНИЯ

Эта страница является частью проекта Linux man-pages версии 5.10. Описание проекта, информацию об ошибках и последнюю версию этой страницы можно найти по адресу https://www.kernel.org/doc/man-pages/.

ПЕРЕВОД

Русский перевод этой страницы руководства был сделан Alexey, Azamat Hackimov <azamat.hackimov@gmail.com>, kogamatranslator49 <r.podarov@yandex.ru>, Kogan, Max Is <ismax799@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.

1 ноября 2020 г. Linux