Файловые системы (ФС) в Unix доступны через промежуточный слой абстракции - виртуальную файловую систему (VFS). VFS организована в виде дерева, в котором узлы ветвления это всегда каталоги, а листья - любые допустимые в VFS объекты (файлы, каталоги, символические ссылки, сокеты, и т.д.). Чтобы подключить в VFS новый носитель данных, выполняется операция монтирования, которая сопоставляет одному из каталогов, уже присутствующему в дереве, ссылку на корневой каталог в ФС на носителе. Доступ к ранее существовавшему содержимому каталога до отмены операции монтирования прекращается. Говорят, что в результате монтирования носитель становится смонтированным в каталог. Корневой каталог всей VFS также должен быть смонтирован на корневой каталог какой-либо конкретной ФС.
Имена объектов в VFS - это байтовые строки с завершающим нулевым байтом. Интерпретацией кодировки символов VFS не занимается. При записи путей символ "слэш" "/" используется как разделитель каталогов и потому не может быть использован в именах объектов VFS. Кроме слэша внутри имён объектов не может присутствовать нулевой байт '\0'. Длина имени не должна превышать NAME_MAX (определена в файле linux/limits.h как 255 байт). В остальном в VFS ограничений на имена нет, но такие ограничения могут быть обусловлены реализацией конкретной файловой системы.
В каждом каталоге существует два зарезервированных имени "." и "..", обозначающие текущий и вышележащий каталоги соответственно. Если ФС на носителе не поддерживает такие имена, то они должны быть сэмулированы драйвером. При выполнении монтирования значение "." берётся из корневого каталога на носителе, а ".." из того каталога, в который было произведено монтирование. В корневом каталоге (см. ниже) ".." ссылается на сам каталог как и ".".
С каждым процессом в Unix связано два каталога:
Эти каталоги используются при разборе путей в VFS теми системными вызовами,
которые получают пути в качестве аргументов
(open("path", ...)
, unlink("path")
и т.п.). Если путь начинается с символа
"слэш" "/" то он называется абсолютным и отсчитывается от корневого каталога
процесса, а если начинается с любого другого символа, то называется
относительным и отсчитывается от текущего рабочего каталога.
Разбор пути идет по следующим правилам (man path_resolution
):
Смена текущего каталога выполняется вызовом chdir(const char *dir)
или fchdir(int fd)
. Второй способ требует предварительно получить файловый
дескриптор, ссылающийся на каталог.
Смена корневого каталога процесса выполняется вызовом
chroot(const char *dir)
. В новом корневом каталоге имя ".."
указывает на него самого, что не позволяет с помощью абсолютных путей
подняться по дереву выше корневого каталога процесса. После выполнения этого
вызова вся файловая система "выше" указанного корневого каталога становится
невидимой для последующих системных вызовов, но открытые файлы за пределами
видимости остаются доступными. Более того, текущий каталог может оказаться
за пределами корневого, так что при практическом применении, например
для ограничения доступа процесса к ФС, надо сочетать
chroot()
и chdir()
.
Чтобы запустить программу с переопределённым корневым каталогом используется программа
chroot NEWROOT [COMMAND [ARG]...]
Если команда COMMAND не указана, то выполняется shell, указанный в файле passwd для текущего пользователя.
Внутри chroot
выполняются следующая последовательность вызовов:
chroot("NEWROOT");
chdir("/");
execve("COMMAND", ...);
Последний вызов означает, что исполняемый файл COMMAND и динамические библиотеки необходимые для его выполнения должны находиться внутри нового корневого каталога.
Если мы хотим запереть пользователя внутри chroot
нам необходимо скопировать
вовнутрь /bin/bash, /lib/libc.so, /etc/passwd, /dev/tty и ещё ряд важных
файлов, полный состав которых зависит от версии ОС и набора решаемых в "тюрьме"
задач.