IOCTL_NS(2)

ИМЯ
ioctl_ns - операции ioctl() для пространств имён Linux

ОПИСАНИЕ

  Определение отношений между пространствами имён
Следующие операции ioctl(2) позволяют определить порядок отношений между пространствами имён (смотрите user_namespaces(7) и pid_namespaces(7)). Форма вызовов:

    new_fd = ioctl(fd, request);

Здесь в каждом случае fd ссылается на файл /proc/[pid]/ns/*. При успешном выполнении обе операции возвращают новый файловый дескриптор.

NS_GET_USERNS (начиная с Linux 4.9)
    Возвращает файловый дескриптор, ссылающийся на пространство имён пользователя, которое владеет пространством имён, на которое ссылается fd.

NS_GET_PARENT (начиная с Linux 4.9)
    Возвращает файловый дескриптор, который ссылается на родительское пространство имён для пространства имён, на которое ссылается fd. Эта операция допускается только, для иерархических пространств имён (т. е., PID и пространства имён пользователя). Для пространств имён пользователя NS_GET_PARENT является синонимом NS_GET_USERNS.

Новый файловый дескриптор, возвращаемый этими операциями, открывается с флагами O_RDONLY и O_CLOEXEC (close-on-exec; смотрите fcntl(2)).

Применив fstat(2) к возвращаемому файловому дескриптору, можно получить структуру stat, в которой поля st_dev (подлежащее устройство) и st_ino (номер иноды) вместе отождествляют владельческое/родительское пространство имён. Этот номер иноды можно сравнить с номером иноды другого файла /proc/[pid]/ns/{pid,user}, чтобы определить то же ли это владельческое/родительское пространство имён.

Эти операции ioctl(2) могут завершаться со следующими ошибками:

EPERM
    Запрошенное пространство имён лежит вне области пространств имён вызывающего. Эта ошибка может случиться, если, например, владельческое пространство имён является предком текущего пространства имён пользователя вызывающего. Также она может возникнуть при попытке получить родителя первоначального пространства имён пользователя или PID.

ENOTTY
    Операция не поддерживается в этой версии ядра.

Также, операция NS_GET_PARENT может завершиться со следующей ошибкой:

EINVAL
    Значение fd ссылается на не иерархическое пространство имён.

Использование этих операций смотрите в разделе ПРИМЕРЫ.

  Определение типа пространства имён
Операция NS_GET_NSTYPE (доступна, начиная с Linux 4.11) позволяет определять тип пространства имён, на которое ссылается файловый дескриптор fd:

    nstype = ioctl(fd, NS_GET_NSTYPE);

Значение fd ссылается на файл /proc/[pid]/ns/*.

Возвращаемым значением является одно из CLONE_NEW*, которое может указываться clone(2) или unshare(2) для создания пространства имён.

  Определение владельца пространства имён пользователя
Операция NS_GET_OWNER_UID (доступна, начиная с Linux 4.11) позволяет определять пользовательский ID владельца пространства имён пользователя (т. е., эффективный пользовательский ID процесса, который создал пространство имён пользователя). Формат вызова:

    uid_t uid;
    ioctl(fd, NS_GET_OWNER_UID, &uid);

Значение fd ссылается на файл /proc/[pid]/ns/user.

Возвращаемый пользовательский ID владельца находится в третьем аргументе с типом uid_t.

Данная операция может завершиться со следующей ошибкой:

EINVAL
    Значение fd не ссылается на пространство имён пользователя.

ОШИБКИ
Любая из этих операций ioctl() может завершаться со следующими ошибками:

ENOTTY
    Значение fd не ссылается на файл /proc/[pid]/ns/*.

СООТВЕТСТВИЕ СТАНДАРТАМ
Пространства имён и операции, описанные здесь, есть только в Linux.

ПРИМЕР
В примере, показанном ниже, используются описанные выше операции ioctl(2), с помощью которых определяются отношения между пространствами имён. Далее показаны сценарии запуска этой программы.

Попытка определить родителя изначального пространства имён пользователя завершается ошибкой, так как родителя нет:

    $ ./ns_show /proc/self/ns/user p
    Родительское пространство имён находится вне области вашего пространства имён

Создаётся процесс, выполняющий sleep(1), который располагается в новом пространстве имён пользователя и UTS, и показывается, что новое пространство имён UTS связано с новым пространством имён пользователя:

    $ unshare -Uu sleep 1000 &
    [1] 23235
    $ ./ns_show /proc/23235/ns/uts u
    Устройство/инода, владеющая пространством имён пользователя: [0,3] / 4026532448
    $ readlink /proc/23235/ns/user
    user:[4026532448]

Теперь покажем, что родителем нового пространства имён пользователя из предыдущего примера является начальное пространство имён пользователя:

    $ readlink /proc/self/ns/user
    user:[4026531837]
    $ ./ns_show /proc/23235/ns/user p
    Устройство/инода родительского пространства имён: [0,3] / 4026531837

Запустим оболочку в новом пространстве имён пользователя и покажем, что внутри оболочки родительское пространство имён пользователя невозможно определить. Также невозможно определить пространство имён UTS (которое связано с изначальным пространством имён пользователя).

    $ PS1="sh2$ " unshare -U bash
    sh2$ ./ns_show /proc/self/ns/user p
    Родительское пространство имён находится вне области вашего пространства имён
    sh2$ ./ns_show /proc/self/ns/uts u
    Владеющее пространство имён пользователя находится вне области вашего пространства имён

  Исходный код программы
    /* ns_show.c
       Лицензируется под GNU General Public License v2 или новее.
    */
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include <errno.h>
    #include <sys/sysmacros.h>
    #ifndef NS_GET_USERNS
    #define NSIO    0xb7
    #define NS_GET_USERNS   _IO(NSIO, 0x1)
    #define NS_GET_PARENT   _IO(NSIO, 0x2)
    #endif
    int
    main(int argc, char *argv[])
    {
        int fd, userns_fd, parent_fd;
        struct stat sb;
        if (argc < 2) {
            fprintf(stderr, "Использование: %s /proc/[pid]/ns/[файл] [p|u]\n",
                    argv[0]);
            fprintf(stderr, "\nПоказывает результат одной или обеих"
                    "операций NS_GET_USERNS (u) и NS_GET_PARENT (p)\n"
                    "для заданного /proc/[pid]/ns/[файла]. Если не указано"
                    "'p' или 'u', то\n"
                    "используется NS_GET_USERNS.\n");
            exit(EXIT_FAILURE);
        }
        /* получаем файловый дескриптор для файла 'ns', указанного
           в argv[1] */
        fd = open(argv[1], O_RDONLY);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
        /* получаем файловый дескриптор владельческого пространства
           имён пользователя и затем показываем номер иноды
           этого пространства имён */
        if (argc < 3 || strchr(argv[2], 'u')) {
            userns_fd = ioctl(fd, NS_GET_USERNS);
            if (userns_fd == -1) {
                if (errno == EPERM)
                    printf("Родительское пространство имён находится "
                            "вне области вашего пространства имён\n");
                else
                   perror("ioctl-NS_GET_USERNS");
                exit(EXIT_FAILURE);
             }
            if (fstat(userns_fd, &sb) == -1) {
                perror("fstat-userns");
                exit(EXIT_FAILURE);
            }
            printf("Устройство/инода, владеющая пространством имён пользователя: "
                    "[%lx,%lx] / %ld\n",
                    (long) major(sb.st_dev), (long) minor(sb.st_dev),
                    (long) sb.st_ino);
            close(userns_fd);
        }
        /* получаем файловый дескриптор родительского пространства
           имён пользователя и затем показываем номер иноды
           этого пространства имён */
        if (argc > 2 && strchr(argv[2], 'p')) {
            parent_fd = ioctl(fd, NS_GET_PARENT);
            if (parent_fd == -1) {
                if (errno == EINVAL)
                    printf("Невозможно получить родительское пространство имён"
                            "не иерархического пространства имён\n");
                else if (errno == EPERM)
                    printf("Родительское пространство имён находится"
                            "вне области вашего пространства имён\n");
                else
                    perror("ioctl-NS_GET_PARENT");
                exit(EXIT_FAILURE);
            }
            if (fstat(parent_fd, &sb) == -1) {
                perror("fstat-parentns");
                exit(EXIT_FAILURE);
            }
            printf("Устройство/инода родительского пространства имён: [%lx,%lx] / %ld\n",
                    (long) major(sb.st_dev), (long) minor(sb.st_dev),
                    (long) sb.st_ino);
            close(parent_fd);
        }
        exit(EXIT_SUCCESS);
    }

СМОТРИТЕ ТАКЖЕ
fstat(2), ioctl(2), proc(5), namespaces(7)