PIVOT_ROOT - Linux手册页

时间:2019-08-20 17:59:07  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-06-09

名称

ivot_root-更改根安装

语法

intivot_root(const char * new_root,const char * put_old);

注意:此系统调用没有glibc包装器。请参阅注释。

说明

ivot_root()更改调用进程的安装名称空间中的根安装。更准确地说,它将根目录安装移动到目录put_old并使new_root成为新的根目录安装。调用进程必须在拥有调用者的安装命名空间的用户命名空间中具有CAP_SYS_ADMIN功能。

如果它们指向旧的根目录,则pivot_root()会将同一装入命名空间中每个进程或线程的根目录和当前工作目录更改为new_root。 (另请参见注释。)另一方面,pivot_root()不会更改调用者的当前工作目录(除非它位于旧的根目录中),因此应在其后进行chdir(" /")调用。

适用以下限制:

-
new_root和put_old必须是目录。
-
new_root和put_old不得与当前根目录位于同一装载上。
-
put_old必须位于new_root或以下。也就是说,在put_old所指向的路径名上添加非负数的" / .."前缀必须产生与new_root相同的目录。
-
new_root必须是安装点的路径,但不能是/。通过将路径绑定到自身,可以将尚未成为安装点的路径转换为一个路径。
-
new_root的父挂载和当前根目录的父挂载的传播类型不能为MS_SHARED。同样,如果put_old是现有的安装点,则其传播类型不得为MS_SHARED。这些限制确保了pivot_root()永远不会将任何更改传播到另一个安装名称空间。
-
当前的根目录必须是安装点。

返回值

成功时,返回零。如果出错,则返回-1,并正确设置errno。

错误说明

ivot_root()可能会因与stat(2)相同的错误而失败。此外,它可能因以下错误而失败:

EBUSY
new_root或put_old在当前的根目录上。 (此错误涵盖了new_root为/的病理情况。)
EINVAL
new_root不是安装点。
EINVAL
put_old不在new_root或其下方。
EINVAL
当前的根目录不是安装点(由于较早的chroot(2))。
EINVAL
当前根目录位于rootfs(初始ramfs)装载中;请参阅注释。
EINVAL
new_root上的安装点或该安装点的父安装具有传播类型MS_SHARED。
EINVAL
put_old是安装点,传播类型为MS_SHARED。
ENOTDIR
new_root或put_old不是目录。
EPERM
调用进程不具有CAP_SYS_ADMIN功能。

版本

在Linux 2.3.41中引入了axis_root()。

遵循规范

ivot_root()是特定于Linux的,因此不可移植。

备注

Glibc没有为该系统调用提供包装器;使用syscall(2)调用它。

ivot_root(8)提供了用于此系统调用的命令行界面。

ivot_root()允许调用者切换到新的根文件系统,同时将旧的根挂载放置在new_root下的某个位置,该位置随后可从中卸载。 (将所有在旧根目录上具有根目录或当前工作目录的进程移动到新根目录的事实释放了用户的旧根目录,从而使旧根挂载更加容易地卸载。)

在系统启动期间,pivot_root()的一种用法是,当系统挂载临时的根文件系统(例如initrd(4)),然后挂载真实的根文件系统,并最终将后者转换为所有相关进程的根目录时,线程。一种现代用途是在创建容器期间设置根文件系统。

为了防止内核线程使旧的根目录挂载它们的根目录和当前工作目录繁忙,即使它们从不访问文件系统中的文件系统,pivot_root()也会以DESCRIPTION中指出的方式修改进程根目录和当前工作目录这一事实是必要的。任何方式。

无法对rootfs(初始ramfs)进行pivot_root()。在这种情况下,建议的更改根文件系统的方法是删除rootfs中的所有内容,用新的根目录覆盖rootfs,将stdin / stdout / stderr附加到新的/ dev / console上,并执行新的init(1)。存在用于此过程的帮助程序;参见switch_root(8)。

pivot_root(dq.dq, dq.dq)

new_root和put_old可能是同一目录。特别是,以下序列允许进行透视根操作,而无需创建和删除临时目录:

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

此序列成功,因为pivot_root()调用将旧的根安装点堆叠在/的新的根安装点之上。那时,调用进程的根目录和当前工作目录引用了新的根安装点(new_root)。在随后的umount()调用期间,解析为。以new_root开头,然后在/处堆叠的安装列表上移,结果是卸载了旧的根安装点。

Historical notes

多年来,此手册页包含以下文本:

ivot_root()可能会或可能不会更改使用旧根目录的任何进程或线程的当前根目录和当前工作目录。无论哪种情况,pivot_root()的调用者都必须确保以root或当前工作目录位于旧root的进程能够正确运行。确保这一点的一种简单方法是在调用ivot_root()之前将其根目录和当前工作目录更改为new_root。

在系统调用实现尚未在内核中最终确定之前编写的这段文字可能旨在警告用户当时实现可能在最终版本发布之前更改。但是,自从首次实现此系统调用以来,DESCRIPTION中描述的行为一直保持一致,并且现在不会更改。

示例

下面的程序演示了在使用clone(2)创建的安装名称空间内对ivot_root()的使用。转到程序第一个命令行参数中指定的根目录后,由clone(2)创建的子级将执行其余命令行参数中指定的程序。

我们通过创建一个目录来演示该程序,该目录将用作新的根文件系统,并将(静态链接的)busybox(1)可执行文件的副本放置在该目录中。

$ mkdir /tmp/rootfs
$ ls -id /tmp/rootfs    # Show inode number of new root directory
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 /          # Compare with inode number above
319459 /
bbsh$ echo aqhello worldaq
hello world

Program source

/* 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              /* Startup function for cloned child */
child(void *arg)
{
    char **args = arg;
    char *new_root = args[0];
    const char *put_old = "/oldrootfs";
    char path[PATH_MAX];

    /* Ensure that aqnew_rootaq and its parent mount donaqt 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 aqnew_rootaq 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");

    /* Execute the command specified in 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),pivotroot(8),switch_root(8)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/