Рубрика:
Администрирование /
Администрирование
|
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|
Сергей Супрунов
Unionfs во FreeBSD: разбираемся в текущей реализации
В наши дни термин «файловая система» охватывает гораздо больше, чем просто способ организации данных на физическом носителе. «Многослойность» подсистемы работы с файлами не только повышает гибкость, но и предоставляет множество интересных возможностей. Одним из примеров такого «промежуточного уровня» является файловая система unionfs.
Про гибкость, vnode и файловые системы
Как известно, на «физическом» уровне файлы в файловой системе ОС семейства UNIX определяются так называемыми индексными узлами (inode), которые хранят информацию об этих файлах. В первоначальных реализациях операционных систем файловые объекты, с которыми работала система, непосредственно сопоставлялись с inode.
Со временем потребность в более гибком подходе, который позволил бы не переписывать каждый раз половину операционной системы, чтобы научить её работать с очередной файловой системой, породила промежуточный уровень между ядром операционной системы и физическим размещением данных на носителе – интерфейс vnode. То есть операционная система стала «общаться» с интерфейсом vnode, который уже, в свою очередь, преобразовывал запросы применительно к конкретной реализации файловой системы.
Следующим шагом стали так называемые наращиваемые файловые системы. Интерфейс vnode достаточно гибок, чтобы позволить размещать несколько уровней vnode один над другим. В этом случае при получении запроса на выполнение той или иной операции с файловой системой, верхний уровень vnode может либо выполнить этот запрос сам, если располагает необходимой информацией, либо передать его на нижележащий уровень (без изменений или предварительно модифицировав). Следующий уровень vnode поступает аналогичным образом, и так до тех пор, пока запрос не будет удовлетворён, либо пока не будет возвращена ошибка «Операция не поддерживается».
Во FreeBSD концепция наращиваемых файловых систем поддерживается достаточно широко, что позволяет помимо «традиционных» ФС реализовать целый ряд довольно полезных и эффективных «виртуальных» файловых систем, таких как unionfs.
Небольшое отступление про nullfs
Прежде чем переходить к основной теме статьи, коротко остановимся на файловой системе nullfs. Она позволяет монтировать каталог как «обычную» файловую систему (в частности, задавать опции монтирования, такие как «ro»). С точки зрения рассмотренной выше схемы, nullfs реализуется очень просто – это должен быть, по сути, просто «прозрачный» слой vnode. То есть запросы на поиск объекта должны прозрачно передаваться на нижележащий уровень, ответы также «транзитом» следовать обратно (см. рис. 1).
Рисунок 1. Работа файловой системы nullfs
В принципе, можно сказать, что всё, что делает nullfs, – это предоставляет доступ к «чистой» файловой системе через дополнительную точку монтирования.
Unionfs: теория
Файловая система unionfs разрабатывалась как средство объединения различных файловых систем в единое пространство имён, чтобы они воспринимались как одно целое.
Работа unionfs заключается в том, что она определённым образом накладывает один каталог на другой, делая возможным одновременное использование файлов и подкаталогов как в одном, так и в другом. То есть, в отличие от других ФС, unionfs не перекрывает при монтировании старые объекты, размещаемые в каталоге, объявленном как точка монтирования, а осуществляет объединение этих каталогов.
Очевидно, здесь есть масса нюансов. Что произойдёт, если в обоих объединяемых каталогах есть файлы или подкаталоги с одинаковыми именами? Как unionfs должна реагировать на удаление файла или каталога из «общей» папки? Можно ли использовать в качестве одного из каталогов то, что смонтировано только для чтения (например, файловую иерархию на CD-диске)? И в каком из каталогов физически создавать новые файлы? Посмотрим, как это задумано на данный момент во FreeBSD.
В реализации FreeBSD unionfs «накладывает» один каталог на другой, руководствуясь следующими принципами:
- один из каталогов становится каталогом верхнего уровня (в дальнейшем для краткости я буду называть его просто «верхним»), второй – каталогом нижнего уровня («нижним»);
- пользователю объекты каталогов доступны «сверху вниз», т.е. если запрошенный объект есть в «верхнем» каталоге, возвращается он, независимо от наличия объекта с таким именем в «нижнем» каталоге; иначе возвращается объект «нижнего» каталога; если запрошенного объекта нет ни там, ни там, возвращается ошибка «Нет такого файла или каталога» (см. рис. 2);
- рабочим каталогом является «верхний», то есть все действия пользователя по изменению данных отражаются только на каталоге верхнего уровня, не влияя на содержимое «нижнего» каталога.
Рисунок 2. Объединение двух каталогов в union-систему
Последний пункт означает, что все каталоги, кроме каталога верхнего уровня, фактически можно рассматривать как подключенные «только для чтения». Однако для пользователя это остаётся незаметным: при попытке редактировать файл из «нижнего» каталога он копируется в «верхний», перекрывая оригинальный файл (так называемое «копирование при записи»). В результате пользователь может безограничений работать с этим файлом, в то время как в первоначальном месте он остаётся неизменным (см. на рис. 2 файл f5).
Здесь выплывают два интересных момента. Допустим, файл f3 есть в обоих каталогах. Пользователь будет видеть «верхний». А если пользователь этот файл удалит? Согласно изложенному выше, при запросе f3 пользователю должен быть возвращён «нижний» файл. Но тогда получается нестыковка со здравым смыслом – ведь пользователь только что этот файл удалил, и было бы нелогично увидеть его снова! Точнее, даже не «его», а, вполне вероятно, совершенно другой файл, просто с таким же именем.
Этот случай unionfs должна обрабатывать особым образом – в «верхнем» каталоге создаётся особый элемент-заглушка (называемый элементом whiteout), и как только он будет найден по запросу на поиск объекта, сразу возвращается ошибка «Нет такого файла или каталога», без обращений к нижележащим уровням. Если файл с данным именем в дальнейшем будет создан, то он заменит собой элемент whiteout, и уже он будет перекрывать файл, размещённый на нижнем уровне (см. рис. 3).
Рисунок 3. Удаление файла, имя которого есть в обоих каталогах
Кстати, поскольку модификация «нижнего» каталога не допускается, то при попытке удалить файл, физически размещающийся там (например, f2 на рис. 2) вместо физического удаления должен создаваться элемент whiteout в «верхнем» каталоге, чтобы перекрыть этот файл.
Ещё более интересный случай – удаление и последующее восстановление каталога, имя которого присутствует на обоих уровнях. С удалением всё просто – так же, как и для файла, достаточно в «верхнем» каталоге создать элемент whiteout. Но вот если каталог после этого создать, то в нём сразу станут доступны файлы, хранящиеся в одноимённом каталоге нижнего уровня. Чтобы этого избежать, unionfs должна обрабатывать такие воссозданные каталоги особым образом – без передачи запросов на нижние уровни. То есть они должны вести себя так, как при обычном монтировании, когда верхний слой полностью перекрывает объекты нижележащего (см. рис. 4). Посмотрим, как это реализовано на практике (если вообще реализовано).
Рисунок 4. Удаление и последующее восстановление каталога
Монтирование unionfs во FreeBSD
Во FreeBSD монтирование файловой системы unionfs выполняется с помощью утилиты mount_unionfs (если вам это кажется удобнее, можно использовать mount -t unionfs). Параметров немного – помимо «-o», позволяющего задавать стандартные опции монтирования, есть ещё два ключа:
- -r – заставляет при монтировании полностью скрыть объекты нижнего уровня, в результате чего поведение unionfs становится идентично работе файловой системы nullfs;
- -b – инвертирует верхний и нижний уровни таким образом, что видимая пользователю точка монтирования становится верхним уровнем, т.е. рабочим каталогом.
Например:
# mount_unionfs /usr/home/src /usr/src
# mount -t unionfs -b /usr/home/src /usr/src
Первая команда смонтирует каталог /usr/home/src поверх /usr/src, и всё это будет доступно пользователю в точке монтирования /usr/src, причём рабочим каталогом (каталогом верхнего уровня, в котором будут происходить все изменения) будет /usr/home/src.
Вторая команда отличается от первой ключом -b, так что результирующий каталог будет смонтирован в /usr/home/src, который и станет «верхним». То есть ключ -b фактически изменяет точку монтирования.
Если монтирование не выполняется, завершаясь ошибкой «Operation not supported by device», то первое, в чём следует убедиться, – это в том, что подгружается модуль unionfs.ko:
$ kldstat
Id Refs Address Size Name
1 4 0xc0400000 3aef60 kernel
. . . .
n 1 0xc2a3b000 9000 unionfs.ko
|
При желании поддержку unionfs можно собрать и в ядре, для чего нужно добавить в конфигурационный файл ядра следующую строку (см. /usr/src/sys/conf/NOTES):
options UNIONFS #Union filesystem
По умолчанию, учитывая неустойчивость нынешней реализации, поддержка этой ФС в ядро не включается.
Применение на практике
Итак, что такое unionfs и как она работает (или, по крайней мере, должна работать), мы разобрались. Возникает вопрос: а зачем она вообще нужна? Первое, что приходит в голову, – монтирование пользовательского каталога поверх точки монтирования CD-диска. Раз нижний слой всё равно неизменяем, то доступность CD только для чтения никакой роли в данном случае не сыграет. Зато монтирование доступного на запись каталога верхнего уровня позволит, например, выполнять сборку и установку программ или системы «прямо с CD» – все временные файлы будут создаваться в пользовательском каталоге:
# mount_unionfs /usr/home/cdrom /mnt/cdrom
Теперь в каталоге /mnt/cdrom можно выполнять любые изменения файлов – на самом деле они будут выполняться в /usr/home/cdrom. Кстати, так можно не только обходить явный запрет на запись, но и избегать записи там, где она нежелательна. Например, примонтировав пользовательский каталог поверх каталога с исходниками, можно смело экспериментировать с наложением патчей и сборкой, не боясь испортить «оригинал» или замусорить дерево исходных кодов временными файлами.
Можно зайти ещё дальше – использовать в качестве каталога верхнего уровня файловую систему, размещаемую в памяти (во FreeBSD она реализуется с помощью mdmfs). В результате и скорость работы должна возрасти, и «мусор» за собой убирать вручную не придётся. Главное, чтобы выделение памяти под md-раздел было не в ущерб доступной для операций сборки:
# mount_mfs -s 50m md /tmp/md1
# mount_unionfs /tmp/md1 /usr/ports/www/apache22
Ещё один близкий по духу пример: использование unionfs для целей обучения. Понятно, что если в университете каждого студента пускать «админить» реальную систему, то довольно часто придётся заниматься аварийно-восстановительными мероприятиями. Решений у этой проблемы много – и LiveCD, и заранее сохранённые «образы» системы, с которых можно легко восстановить исходное состояние. Здесь же можно применить и unionfs – примонтировать пользовательский каталог поверх реального /etc, сохранив тем самым первозданную чистоту оригинального каталога.
Наконец, unionfs будет крайне полезна для построения jail-систем. В Solaris «зоны» используют похожую технологию – при создании зоны она наследует файлы основной системы, но если какой-то из них изменяется, то эти изменения отражаются только внутри данной зоны. Это позволяет значительно экономить дисковое пространство, выделяя его лишь «по требованию», что особенно заметно, если выполняется построение множества однотипных зон. Так же и с jail – создавая виртуальную среду, например, для хостинга или тестирования ПО, можно не копировать в неё полностью все системные файлы, а использовать каталоги основной системы, поверх которых монтировать пользовательские каталоги в unionfs (фрагмент /etc/fstab):
/bin /var/jails/outers/bin unionfs 0 0
/sbin /var/jails/outers/sbin unionfs 0 0
/lib /var/jails/outers/lib unionfs 0 0
/libexec /var/jails/outers/libexec unionfs 0 0
/usr /var/jails/outers/usr unionfs 0 0
|
В результате пользователь без дополнительных затрат дискового пространства получит себе систему, которую сможет менять как ему заблагорассудится – без угрозы для целостности основной системы.
Немного практики
Осталось посмотреть, правда ли, что unionfs сегодня находится во FreeBSD в таком плачевном состоянии, как об этом все говорят? Для эксперимента заготовим два каталога следующего содержания:
# ls -l md1
total 8
-rw-r--r-- 1 root wheel 6 22 дек 09:53 file1
-rw-r--r-- 1 root wheel 6 22 дек 09:53 file2
-rw-r--r-- 1 root wheel 6 22 дек 09:55 file5
drwxr-xr-x 2 root wheel 512 22 дек 09:54 subdir
# ls -l md2
total 2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file1
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
drwxr-xr-x 2 root wheel 512 22 дек 09:54 subdir
# ls -l md1/subdir
total 0
-rw-r--r-- 1 root wheel 6 22 дек 09:58 file1
# ls -l md2/subdir
total 0
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
|
Файлы с одинаковыми именами будем различать по размеру – в каталоге md1 они занимают 6 байтов, в md2 – нулевой длины. Выполним монтирование в точку md1 (рабочим каталогом при этом станем md2):
# mount_unionfs /tmp/md2 /tmp/md1
# ls -l /tmp/md2
total 2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file1
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
drwxr-xr-x 2 root wheel 512 22 дек 09:54 subdir
# ls -l /tmp/md1
total 4
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file1
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
-rw-r--r-- 1 root wheel 6 22 дек 09:55 file5
drwxr-xr-x 4 root wheel 512 22 дек 09:54 subdir
# ls -l md2/subdir
total 0
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
# ls -l md1/subdir
total 2
-rw-r--r-- 1 root wheel 6 22 дек 09:58 file1
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
|
Именно так и должно быть – md2 мы видим в неизменном виде, а в md1 перемешались файлы обоих каталогов, причём, несмотря на то что точкой монтирования стала md1, в первую очередь там доступны файлы из md2 (нулевой длины). Попробуем удалить файл:
# rm md2/file1
# ls -l md1
total 6
-rw-r--r-- 1 root wheel 6 22 дек 09:53 file1
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
-rw-r--r-- 1 root wheel 6 22 дек 09:55 file5
drwxr-xr-x 4 root wheel 512 22 дек 09:54 subdir
# ls -l md2
total 2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
drwxr-xr-x 2 root wheel 512 22 дек 09:54 subdir
# rm md1/file2
rm: md1/file2: Operation not supported
# rm md1/file1
# ls -l md1
total 4
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
-rw-r--r-- 1 root wheel 6 22 дек 09:55 file5
drwxr-xr-x 4 root wheel 512 22 дек 09:54 subdir
|
Ну вот – удаление напрямую из md2 (в обход unionfs) проходит без ошибок, и в md1 вместо него становится видно другой file1 (собственно, unionfs и не должна блокировать непосредственный доступ к md2, так что данное поведение нужно признать нормальным, хотя это и может привести к некоторому замешательству). Удаление из md1 файла, который физически же в md1 и размещается, тоже проходит без эксцессов. А вот попытка удалить file2, принадлежащий каталогу md2, из точки монтирования md1 заканчивается неудачей – «Операция не поддерживается». Та же судьба постигла и попытку удалить md1/subdir, так что посмотреть на практике на самые интересные моменты, связанные с функционированием unionfs, не получилось. Грустно...
# vi md1/file5
md1/file5: 1 строк, 11 символов.
# ls -l md1
total 6
-rw-r--r-- 1 root wheel 0 22 дек 10:21 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
-rw-r--r-- 1 root wheel 11 22 дек 09:55 file5
drwxr-xr-x 4 root wheel 512 22 дек 10:17 subdir
# umount md1
# ls -l md1
total 8
-rw-r--r-- 1 root wheel 6 22 дек 09:53 file1
-rw-r--r-- 1 root wheel 6 22 дек 09:53 file2
-rw-r--r-- 1 root wheel 6 22 дек 09:55 file5
drwxr-xr-x 2 root wheel 512 22 дек 09:54 subdir
# ls -l md2
total 4
-rw-r--r-- 1 root wheel 0 22 дек 10:21 file2
-rw-r--r-- 1 root wheel 0 22 дек 09:54 file3
-rw-r--r-- 1 root wheel 11 22 дек 10:25 file5
drwxr-xr-x 2 root wheel 512 22 дек 10:17 subdir
|
Ну, по крайней мере, редактирование файла прошло должным образом – в md2 была создана копия файла file5, а md1/file5 остался в неприкосновенном виде.
С надеждой на лучшее...
Как видите, unionfs – весьма полезная и удобная во многих случаях система. И всё бы было хорошо, если бы не крайне неустойчивая реализация на данный момент и не «пробелы» с удалением файлов (получается, что whiteout либо не реализован вообще, либо работает неправильно). Активное использование unionfs слишком часто приводит к «падениям» всей системы, да и сами разработчики честно предупреждают на man-странице большими буквами: «ЭТОТ ТИП ФАЙЛОВОЙ СИСТЕМЫ ПОКА ЕЩЁ ПОДДЕРЖИВАЕТСЯ НЕ ПОЛНОСТЬЮ (ЧИТАЙ: ОН НЕ РАБОТАЕТ), И ЕГО ИСПОЛЬЗОВАНИЕ НА САМОМ ДЕЛЕ МОЖЕТ УНИЧТОЖИТЬ ДАННЫЕ НА ВАШЕЙ СИСТЕМЕ. ИСПОЛЬЗУЙТЕ ЕГО НА СВОЙ СТРАХ И РИСК».
В общем, несмотря на очень большой потенциал этой технологии, нынешняя реализация с трудом годится даже для лабораторных исследований, не говоря уже про эксплуатацию на промышленных серверах.
Серьёзные проблемы в нынешней unionfs привели к тому, что японский разработчик Daichi Goto (http://people.freebsd.org/~daichi) взялся писать с нуля новую реализацию. На его домашней странице сообщается, что 2 декабря 2006 года его патч был включён в текущее дерево FreeBSD 7-CURRENT, так что будем надеяться, что рано или поздно мы получим-таки в своё распоряжение надёжную, устойчивую и эффективную unionfs.
Приложение
Unionfs в Linux
В операционной системе Linux реализация unionfs также доступна и, похоже, является более зрелой и устойчивой. Хотя на страничке в Wikipedia и говорится, что «UnionFS в Linux, в общем, не рекомендуется для промышленного использования».
В Ubuntu 6.10 (ядро 2.6.17) её поддержка «из коробки» отсутствует, но может быть установлена из репозитариев в виде пакетов union-source и union-utils.
- http://people.freebsd.org/~daichi/unionfs – страница, посвящённая новой реализации unionfs во FreeBSD.
- http://www.unionfs.org – сайт, на котором собрана информация по реализации unionfs для систем Linux.
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|