GEOMетрия FreeBSD
Евгений Зобнин ака j1m (j1m@list.ru)
Хакер, номер #096, стр. 116
Технология GEOM изнутри и снаружи
Технология GEOM стала одним из главных нововведений FreeBSD за последние годы. Благодаря модульной архитектуре и грамотному дизайну механизм GEOM позволил в полной мере контролировать процесс обмена данными между жестким диском и файловой системой. Пользователи и администраторы получили в распоряжение мощный инструмент, открывающий широчайшие возможности по организации хранилищ данных любой сложности.Философия GEOM
GEOM (модульный механизм преобразования запросов дискового ввода-вывода) представляет собой прослойку между файловой системой и драйвером накопителя (ATA, SATA, SCSI). В основу механизма положена идея обособленных модулей (в терминологии GEOM - классов), каждый из которых может выполнять один и только один вид преобразования. Запрос дискового ввода-вывода, проходя через модули, подвергается различной обработке и в конце пути попадает в драйвер устройства. Комбинируя модули, пользователь получает возможность работать с различными дисковыми разделами, создавать логические тома и программные RAID-массивы.Немаловажная особенность GEOM - это простота разработки классов. Для создания нового вида преобразования достаточно оформить алгоритм в виде класса, добавить необходимые структуры данных и скомпилировать код как модуль ядра. После этого модуль можно подключить к ядру и добавить экземпляр класса к существующей топологии. Надо сказать, что простота дизайна и дружелюбность к разработчикам сыграли свою роль. В FreeBSD версии 6.1 доступно более 20 классов, и с каждым релизом их будет становиться все больше.
Классы GEOM, доступные в FreeBSD 6.1
Работа с разделами:- mbr - делит диск на слайсы;
- gpt - делит диск на слайсы формата GUID (формат разделов архитектуры IA-64);
- apple - делит диск на слайсы формата Apple;
- sunlabel - делит диск на слайсы формата Sun OpenBoot PROM (используется в серверах Sun);
- pc98 - делит диск на слайсы формата PC98;
- bsd - делит слайс на разделы BSD.
RAID и логические тома:
- concat - объединяет несколько дисков в один логический дисковый раздел;
- stripe - создает массивы RAID-0 (чередование);
- mirror - создает массивы RAID-1 (зеркалирование);
- raid3 - создает массивы RAID-3 (чередование с контролем четности);
- ccd - создает массивы RAID-0 и RAID-1;
- vinum - менеджер логических томов vinum, как класс GEOM.
Безопасность:
- gbde - осуществляет шифрование на уровне диска;
- geli - другой вариант шифрования;
- shsec - реализует
shared secret devices
, когда для того чтобы получить доступ к одному накопителю, необходимо обеспечить наличие другого накопителя, например USB-флешки.
Системные:
- dev - создает файлы устройств в каталоге /dev.
- vfs - связывает GEOM и подсистему VFS.
- disk - работает с драйвером накопителя.
Другое:
- label - позволяет управлять дисковыми разделами с помощью специальных меток;
- fox - специальный класс, автоматически перенаправляющий цепь запросов с одного накопителя на другой;
- gate - позволяет создать накопитель как пользовательский процесс;
- nop - предназначен для тестирования других классов и сбора статистики;
- uzip - позволяет читать сжатые образы дисков;
- zero - класс реализует виртуальный накопитель, содержащий нули (по аналогии с /dev/zero).
Все классы, за исключением системных, которые должны присутствовать в любой топологии GEOM, могут быть подключены как модули ядра. Классы, работающие с разделами, по умолчанию включены в ядро, причем без крови их оттуда не извлечешь - через конфигурационный файл ядра это осуществить не удастся. Так сделано для того, чтобы ядро путем перебора классов (существуют правила, благодаря которым GEOM способен производить автоконфигурирование), смогло создать стандартную топологию и не выпасть во время инициализации в kernel panic.
Классы ccd и vinum достались FreeBSD в наследство от четвертой ветки, в которой они были реализованы как отдельные механизмы ядра и являлись чуть ли не единственным средством создания RAID и логических томов. По сути, это реликты, пережитки прошлого, импортированные в GEOM по просьбам ветеранов FreeBSD и от нежелания выбрасывать отлаженный код. Даже vinum, при всей своей мощи и универсальности, в большинстве случаев может быть заменен комбинацией других классов, гораздо более удобных в настройке.
Долой невнятные названия дисков
Наверняка тебе приходила в голову мысль, что было бы гораздо удобнее монтировать файловые системы, указывая в аргументах команды mount и в файле /etc/fstab легко читаемые и прозрачные имена дисков, вроде /dev/home или /dev/usr. Если так, хочу тебя обрадовать, класс label позволяет сделать именно это. Принцип его работы основан на понятии метки тома, которая является непременным атрибутом большинства файловых систем.Приведу пример. Имеется раздел /dev/ad0s1e, на котором создана файловая система ufs2, содержащая каталог /home. Попробуем смонтировать ее: mount /dev/ad0s1e /home. Одна команда и две проблемы: во-первых, имя файла никак не говорит о том, что находится на этом разделе, а во-вторых, в случае перемещения диска имя файла изменится, и придется править /etc/fstab. А теперь сделаем то же самое с использование класса label:
# kldload geom_label# umount /home# tunefs -L home /dev/ad0s1e# mount /dev/ufs/home /homeСтало намного приятнее для глаз. Даже если переключить диск на второй IDE-канал, метка будет распознана и файл /dev/ufs/home останется ассоциированным с прежним разделом. Также класс label умеет распознавать метки файловых систем FAT (/dev/msdosfs/), ISO9660 (/dev/iso9660/), EXT2 (/dev/ext2fs/), ReiserFS (/dev/reiserfs/) и NTFS (/dev/ntfs/). Кроме того, допустимо использование меток, не связанных с файловыми системами (команда
glabel create имя-метки устройство).
Gzip как класс GEOM
На сегодняшний день не существует класса, выполняющего прозрачное сжатие и распаковку данных в реальном времени. Но доступен класс, умеющий читать сжатые образы дисков, который наверняка пригодится владельцам flash-дисков и создателям LiveCD. Ниже приведен пример того, как можно создать и подключить такой образ.Пример использования класса geom_uzip
Создаем и монтируем образ диска:
# dd if=/dev/zero of=disk.ufs bs=1k count=100k# mdconfig -a -t vnode -f disk.ufs -u 0# bsdlabel -w md0 auto# newfs -m 0 -i 1024 md0c# mount /dev/md0c /mntЗаполняем виртуальный диск содержимым:
# cp -R ~/documents /mntОтключаем образ:
# umount /mnt# mdconfig -d -u 0Сжимаем содержимое виртуального диска и вновь подключаем его:
# kldload geom_uzip# mkuzip disk.ufs# mdconfig -a -t vnode -f disk.ufs.uzip -u 0# mount -r /dev/md0.uzip /mntРуки прочь от частной собственности
В ядре FreeBSD версии 6.1 можно найти 2 класса, осуществляющих низкоуровневое шифрование дисков. Первый называется gbde (Geom Based Disk Encryption). Он способен шифровать диски или разделы алгоритмом AES с симметричным ключом длиной от 128 бит. Второй, с более запоминающимся и легко произносимым названием geli, более совершенен и универсален. Класс geli может использовать специализированное оборудование для шифрования (в gbde поддержка такого железа неполная), позволяет выбирать алгоритм шифрования (AES, Blowfish или 3DES), использовать 2 независимых ключа и шифровать корневой раздел диска (во время загрузки пользователю будет предложено ввести пароль).Пример использования класса geom_bde
Инициализация:
# kldload geom_bde# gbde init ad1Создаем файловую систему:
# gbde attach ad1# dd if=/dev/random of=/dev/ad1.bde bs=64k# newfs /dev/ad1.bde# gbde detach ad1Подключаем шифрованный диск:
# gbde attach ad1# mount /dev/ad1.bde /secretКласс geli, в отличие от gbde, позволяет создать ключ из нескольких компонентов. В примере, приведенном ниже, мы используем пароль в комбинации с набором из случайных байтов в качестве основы для ключа Blowfish. При необходимости файл можно переместить на USB-флешку и спрятать ее в цветочном горшке.
Пример использования класса geom_eli
Инициализация:
# kldload# dd if=/dev/random of=~/ad1.key bs=64 count=1# geli init -s 4096 -K ~/ad1.key -a Blowfish /dev/ad1Создаем файловую систему:
# geli attach -k ~/ad1.key ad1# dd if=/dev/random of=/dev/ad1.eli bs=64k# newfs /dev/ad1.eli# geli detach ad1.eliПодключаем шифрованный диск:
# geli attach -k ~/ad1.key /dev/ad1# mount /dev/ad1.eli /mnt/secretИнтересной особенностью класса geli является также и то, что он способен генерировать специальные одноразовые ключи для шифрования разделов swap или временных файловых систем. Никаких источников для генерации ключа, будь то пароль или файл с беспорядочным набором байтов, при этом не требуется.
# dd if=/dev/random of=/dev/ad0s1b bs=64k# geli onetime -d -a 3des ad0s1b# swapon /dev/ad0s1b.eliДисковая магия
К сожалению, в рамках одной статьи невозможно описать все возможности GEOM по организации RAID-массивов. Поэтому мы остановимся только на RAID-1, как наиболее популярном и дешевом варианте RAID. За создание томов RAID-1 (зеркал) отвечает класс mirror, управляемый утилитой /sbin/gmirror. Средствами GEOM создать зеркало очень просто, поэтому, не вдаваясь в подробности, рассмотрим пример, который будет актуален в 90% случаев:Пример использования класса geom_mirror
Создаем зеркало для текущего диска:
# gmirror load# sysctl kern.geom.debugflags=16# gmirror label -b round-robin gm0 /dev/ad0Перезапускаем mirror, чтобы изменения вступили в силу:
# gmirror unload# gmirror loadПодключаем второй диск:
# gmirror insert gm0 /dev/ad2После подключения к зеркалу второго диска начнется синхронизация. Для диска емкостью 120 Гб эта операция может длиться до 5 часов. Строка round-robin в третьей команде означает алгоритм балансировки. Доступно 4 алгоритма:
- load - чтение с менее загруженного диска;
- prefer - чтение с диска, имеющего более высокий приоритет (чем раньше диск был добавлен в массив, тем выше его приоритет);
- round-robin - поочередное чтение с каждого диска (наиболее производительный алгоритм);
- split - распределение запросов на чтение больших объемов данных (4 Кб и более) по дискам (дефолтовый алгоритм).
Чтобы зеркалирование включалось во время инициализации системы, необходимо дать указание загрузчику подключать модуль geom_mirror (echo geom_mirror_load="YES" >> /boot/loader.conf) и отредактировать файл /etc/fstab таким образом, чтобы все вхождения /dev/ad0 были заменены /dev/mirror/gm0 (/dev/ad0s1a - /dev/mirror/gm0s1a и т.д.). В любой момент к массиву может быть добавлен третий диск. Для удаления диска из массива используется команда gmirror remove gm0 ad2.
Mirror очень устойчив к сбоям. Если один из дисков выйдет из строя, то после перезагрузки система продолжит нормальную работу. После того как из магазина будет доставлен и подключен новый диск, mirror автоматически его найдет и произведет синхронизацию. А в случае аварийного отключения питания синхронизация продолжится с прерванного места.
Анатомия GEOM
Механизм GEOM объектно-ориентированный. Согласно словарю ООП, модуль называется классом. В работающей системе для каждого класса может быть создан один или несколько
экземпляров, в обязанности которых как раз и входит выполнение одного из видов преобразования. Для каждого жесткого диска будет создано по одному экземпляру класса mbr, делящего диск на слайсы (разделы в терминологии MS), для каждого слайса с меткой BSD - по экземпляру класса bsd, разбивающего слайс на разделы и т.д. К каждому экземпляру класса может быть привязан один или более
поставщики
потребитель. Поставщик - это
главная дверь, через которую экземпляр представляет свои услуги, он напрямую связан с элементом каталога /dev. В случае с ATA-диском, разбитым на 2 слайса, у экземпляра класса MBR будет 2 поставщика (представленные файлами /dev/ad0s1 и /dev/ad0s2). Экземпляр другого класса подключается к поставщику через потребитель, образуя цепь, по которой будут проходить запросы ввода-вывода.

Работу механизма GEOM гораздо проще понять на примере, чем изучая голую теорию и представляя себе якорные цепи сухогрузов. На рисунке изображена стандартная топология GEOM: ATA-диск, разбитый на 2 слайса; один из слайсов разбит на 2 раздела. В начале цепи находится экземпляр класса disk, который напрямую взаимодействует с драйвером ATA-контроллера. К его поставщику подключены экземпляры классов mbr и dev. Первый делит диск на 2 слайса, сверяясь с таблицей разделов, расположенной в секторе MBR. Второй помещает файл устройства (ad0) в каталог /dev. Экземпляр класса mbr имеет 2 поставщика по количеству слайсов. К первому подключены экземпляры классов bsd и dev. Второй слайс не является BSD-слайсом, поэтому к нему подключен только dev. Экземпляр класса bsd делит слайс ad0s1 на 2 раздела и поэтому также имеет 2 поставщика. К каждому из них подключены экземпляры класса dev, которые создают специальные файлы /dev/ad0s1 и /dev/ad0s2 и замыкают цепь.
Рассмотрим, как происходит чтение блоков данных, запрашиваемых файловой системой. Ситуация такая: на разделе /dev/ad0s1a создана ФС, она запрашивает пятый блок раздела. Что же при этом происходит? Экземпляр класса bsd знает только ограниченный объем информации - то, что некое адресное пространство (он и понятия не имеет, что это один из слайсов диска), представленное поставщиком экземпляра класса, расположенного на одно звено ниже, разделено на 2 раздела. Эту информацию он берет из заголовка, находящегося в начале адресного пространства. Обязанность экземпляра класса bsd - правильно вычислить смещение, чтобы попасть именно в тот блок, который запрашивает ФС. Чтобы это сделать, необходимо сложить смещение от начала раздела и номер блока. Раздел, с которого ФС запрашивает блок, является первым, поэтому в результате получается опять же пятый блок. Экземпляр класса bsd отправляет запрос на чтение пятого блока ниже. Экземпляр mbr, который точно так же видит ниже только адресное пространство, получает запрос, подвергает его обработке и отправляет запрос еще ниже - поставщику disk, который направляет запрос драйверу ATA-контроллера. В результате файловая система получает именно тот блок, какой и заказывала. На самом деле, конечно же, все сложнее (при вычислении адреса класс должен учитывать заголовки, пересчитывать размеры блоков и тому подобное), но не будем вдаваться в подробности.
Отдельные классы механизма GEOM очень ограничены в
знанияхо друг друге. Информация, представляемая поставщиками, сводится лишь к данным об имени, размере сектора и общего размера адресного пространства. Отсутствие перекрестных ссылок между классами и общая немногословность каждого из них делает GEOM очень гибким, не привязанным к конкретной топологии, механизмом. Чтобы получить возможность работы с разделами Apple, достаточно заменить экземпляр класса mbr на экземпляр класса apple. А чтобы задействовать шифрование, достаточно подключить экземпляр шифрующего класса к любому поставщику. GEOM безразлично, каким образом будут распределены экземпляры классов, но некоторые классы откажутся инициализироваться, если не будут соблюдены определенные условия. Например, класс mbr требует наличия в адресном пространстве валидной метки MBR, и в случае ее отсутствия экземпляр не будет включен в цепь.
Пара слов об авторе GEOM
Механизм GEOM был спроектирован и реализован легендарным FreeBSD-хакером Полом-Хеннингом Кампом (Poul-Henning Kamp) при спонсорской поддержке NAI Labs. Первая alpha-версия кода была создана для FreeBSD 4.9, а после выхода релиза 5.0 GEOM стал официальной частью ядра. На развитие и реализацию идеи GEOM автору потребовалось около восьми лет. Причины такой длительной разработки он объясняет словами: Я хотел сделать это правильно.
Пол-Хеннинг также известен как автор кода VFS namecache, jail, devfs, gbde и драйверов для некоторых ATA-, SCSI- и USB-устройств. Ему принадлежит замечательное высказывание, которое он привел в письме, анонсирующем GEOM:
Лучший способ уничтожить FreeBSD - это дать нашей инфраструктуре прогнить.
Зачем шифровать раздел swap?
Некоторых читателей может озадачить фраза шифрование swap-раздела.
Что там может быть интересного? Просто куски программ, случайные страницы памяти, - скажут они. И будут не правы. Действительно, раздел подкачки обычно заполнен случайными фрагментами кода, не уместившегося в оперативной памяти. Но стоит включить воображение, как становиться ясно, что в swap также могут попасть фрагменты данных, содержащие конфиденциальную информацию, пароли, обрывки личного дневника и даже семейный секрет приготовления тыквенного пирога!
INFO
Многими классами GEOM можно управлять с помощью стандартной утилиты /sbin/geom. К примеру, вместо команды geom mirror load можно набирать gmirror load.Для наблюдения за статистикой ввода-вывода воспользуйся утилитой /usr/sbin/gstat.





