Подтвердить что ты не робот

"unshare" не работает должным образом в C api

Эта последовательность команд работает:

unshare --fork --pid --mount 
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts

Однако соответствующая C-программа не работает должным образом (кажется, она не отключает предыдущий /proc, а также обеспечивает EBUSY, пытающуюся отключить devpts):

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("My pid: %i\n", getpid()); // It prints 1 as expected

umount("/proc"); // Returns 0

system("mount"); // Should print error on mtab, but it prints the previous mounted filesystems

mount("proc", "/proc", "proc",
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL));  // Returns 0

umount("/dev/pts");  // Returns -1 errno = 0 (??)

mount("devpts", "/dev/pts", "devpts", 
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL) ); // Returns -1 errno = EBUSY

Здесь я пропустил проверку ошибок для чтения

Я думаю, что unshare или unmount не работают так, как ожидаете: даже если он возвращает ноль, кажется, что он не отключает /proc (если я пытаюсь выполнить system("mount")после этого, он печатает смонтированные файловые системы).

4b9b3361

Ответ 1

Я обнаружил проблему с проверкой исходного кода команды unshare. /proc должен быть размонтирован с помощью MS_PRIVATE | MS_REC и установлен без них, это, по сути, должно гарантировать, что монтирование имеет эффект только в текущем (новом) пространстве имен. Вторая проблема заключается в невозможности umount /dev/pts без влияния на глобальное пространство имен (это вызвано внутренней процедурой драйвера devpts). Чтобы иметь частный /dev/pts, единственный способ - установить его с помощью выделенной опции -o newinstance. Наконец, /dev/ptmx также должен быть связан с привязкой.

Следовательно, это рабочий код C, как ожидалось:

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("New PID after unshare is %i", getpid());

if (mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL)) {
    printf("Cannot umount proc! errno=%i", errno);
    exit(1);
}

if (mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL)) {
    printf("Cannot mount proc! errno=%i", errno);
    exit(1);
}


if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance") ) {
    printf("Cannot mount pts! errno=%i", errno);
    exit(1);
}

if (mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_BIND, NULL) ) {
    printf("Cannot mount ptmx! errno=%i", errno);
    exit(1);
}

Ответ 2

Несмотря на ваш комментарий, что

"иногда" umount возвращает 0 "иногда" -1, но в конце он не отключает /proc вообще

в 10000 испытаниях кода из вашего pastebin, umount() всегда терпел неудачу для меня, возвращая -1 и не размонтируя /proc. Я не склонен полагать, что umount() когда-либо возвращает 0, несмотря на то, что не выполнил запрошенную размонтирование, но, если когда-либо это произойдет, это будет ошибкой в ​​umount(). Если вы можете на самом деле обосновать такую ​​ошибку, тогда ответ, настроенный сообществом, будет состоять в том, чтобы подать отчет об ошибках против glibc.


Тогда возникает вопрос, почему и как твой bash script ведет себя по-другому. На самом деле это, похоже, не делает.

Во-первых, у вас есть неправильное ожидание команды unshare(1). В отличие от функции unshare(2) команда unshare не влияет на оболочку, в которой она выполнена. Вместо этого он запускает отдельный процесс, который имеет свои личные копии указанных пространств имен. Обычно вы должны указать команду для запуска этого процесса в командной строке unshare, и на самом деле на странице руководства программы указано, что выполнение этого является обязательным.

Эмпирически я нахожу, что если я не могу указать такую ​​команду - как и вы, то unshare запускает новую оболочку в качестве целевого процесса. В частности, когда я запускаю ваш script (с достаточной привилегией использовать unshare), я сразу получаю новое приглашение, но это приглашение новой оболочки, работающей на переднем плане. Это сразу становится очевидным для меня, потому что приглашение отличается (ваше приглашение может не отличаться при этих обстоятельствах). В этой точке нет сообщения об ошибке и т.д. Из umount, потому что он еще не запущен. Если я вручную попытаюсь выполнить umount proc в подселле (unshare d), он завершится неудачей, когда "устройство занято" - это аналог того, что пытается выполнить ваша программа C.

Когда я выхожу из подоболочки, остальная часть script запускается с ошибкой umount и оба mount. Этого следует ожидать, потому что основной script разделяет пространство имен mount.


Весьма правдоподобно, что /proc действительно занят и поэтому не может быть размонтирован даже для процесса с частной копией пространства имен mount. Вероятно, такой процесс сам использует свою частную копию монтирования /proc. Напротив, я обнаружил, что могу успешно размонтировать /dev/pts в процессе с пространством имен без разделяемого пространства, но не в процессе, который разделяет системную копию этого пространства имен.

Ответ 3

Я думаю, что проблема связана с системой ( "mount" ), которая порождает оболочку и не переносит umount. Попробуйте открыть файл в /proc/after umount и посмотреть, как он работает, как ожидалось.

Смотрите это -

unshare(CLONE_NEWPID | CLONE_NEWNS );
int rc = 0;
int pid = fork();
if (pid != 0) {
        int status;
        waitpid(-1, &status, 0);
        return status;
}

printf(">>> My pid: %d\n", getpid()); // It prints 1 as expected
rc = umount2("/proc", MNT_FORCE); // Returns 0
printf(">>> umount returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

rc = open("/proc/cpuinfo", O_RDONLY);
printf(">>> open returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

Ответ 4

unshare bash!= unshare c

unshare - запустить программу с некоторыми пространствами имен, не разделенными с родителями

Таким образом, в основном с --fork вы форкируете из /bin/sh из/bin/bash (независимо от того, что вы выполняете с помощью script) с параметрами --pid и --mount. "fork", за которым следует "unshare"

unshare - дизассемблировать части контекста выполнения процесса (текущий процесс) Вы не разделяетесь с init, а затем на forking.

CLONE_NEWPID - это флаг "clone" не "unshare"

Итак, в зависимости от того, чего вы пытаетесь достичь, я предполагаю, что вы пытаетесь сделать "/proc" и "/dev/pts" исключительными для дочернего процесса.

Вот небольшой пример с локальными папками mount --bind:

# mkdir mnt point
# touch point/point.txt
# mount --bind point mnt
# ls mnt
point.txt

# ./unshare
My pid: 28377
Child:
point.txt
Parent:

# ls mnt

код:

#define _GNU_SOURCE
#include <sched.h>

int main(int argc, char *argv[])
{
        /** umount global */
        system("umount mnt/");
        int pid = fork();
        if (pid != 0) {
                int status;
                waitpid(-1, &status, 0);
                printf("Parent:\n");
                /* and here we don't */
                system("ls mnt/");
                return status;
        }
        /* unshare */
        unshare(CLONE_FS | CLONE_NEWNS);
        printf("My pid: %i\n", getpid()); // It prints 1 as expected
        /* mount exclusively */
        system("mount --bind point/ mnt/");
        printf("Child:\n");
        /* here we see it */
        system("ls mnt/"); 

        return 0;
}

есть также хороший пример с bash: http://karelzak.blogspot.ru/2009/12/unshare1.html

продолжение:

mount зависит от /etc/mtab, который не всегда является символической ссылкой на /proc/mounts

то проверьте /etc/mtab с помощью ls -la.

также проверьте код для umount на /dev/pts с помощью:

int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
  printf("Error on umount: %s\n", strerror(errsv));
}

Я уверен, что он используется - проверьте его с помощью fuser/dev/pts/

** EDITED **

Наконец - я не уверен, что вы можете umount procfs только в пространстве имен (я думаю, что это невозможно)

но вы можете смонтировать свою собственную копию procfs в своем пространстве имен:

# mount -t proc proc /proc/

Теперь только ваш процесс отображается через ps -e.