понедельник, 22 ноября 2010 г.

Резервное копирование таблицы разделов при помощи gpart

Добавил в gpart(8) возможность выполнять резервное копирование и восстановление таблицы разделов. Уже давно что-то такое вертелось на уме и знакомые просили. Но вот, собрались трое, кому это нужно, в IRC и окончательно сформулировалась задача :)

Резервное копирование выполняется при помощи команды "gpart backup", которая в специальном текстовом формате выводит информацию о таблице разделов. Эту информацию можно перенаправить в файл, либо отдать на вход команде "gpart restore". Небольшой пример:

# gpart backup ada0
MBR 4
1 freebsd       63 20964762   [active]
2    ntfs 20964825 40965750
3 freebsd 61930575 18362295
# gpart backup ada1
GPT 128
1   freebsd-boot        34       512 boot
2   freebsd-swap       546   8388608 swap
3    freebsd-zfs   8389154 311783869 zfs
Как можно догадаться, первоя строка в выводе - это опции команды "gpart create", следующие - для "gpart add". У команды restore есть несколько ключей:

gpart restore [-lF] [-f flags] provider [...]
Ключ "-l" указывает gpart восстанавливать символические метки разделов, которые по-умолчанию не восстанавливаются. С ключем "-F" gpart перед восстановлением уничтожит таблицу разделов на носителе, куда будет восстанвливаться резервная копия. Иначе, если там будет обнаружена какая-либо таблица разделов, он просто завершится с ошибкой. Ну и ключ -f выполняет те же функции, что и у дргих подкоманд - откладывает запись всех изменений на носитель до вызова команды commit. Т.е. если вы не уверены в своих действиях - этот ключ ваш лучший товарищ.
Теперь немного примеров по использованию. Типичные действия для резервного копирования:

# gpart backup ada0 > ada0.backup
# cat ada0.backup
MBR 4
1 freebsd       63 20964762   [active]
2    ntfs 20964825 40965750
3 freebsd 61930575 18362295
Теперь этот ada0.backup на ответственное хранение в сейф. И вдруг, после очередной пьянкисмены паролей, утром вы обнаружили что таблица разделов уничтожена =)
Бежите к сейфу, загружаетесь с livefs и восстанавливаете таблицу разделов:

# gpart restore ada0 < /mnt/ada0.backup
Теперь более реальный пример. Вы захотели к уже установленной системе подключить ещё один диск и сделать зеракало на ZFS:

# gpart backup ada0 | gpart restore ada1


Нет, вы параноик и вы подключили 2 жестких диска и хотите сделать тройное зеркало, не проблема:

# gpart backup ada0 | gpart restore ada1 ada2 
Теперь пример для неопытных пользователей:

# gpart backup ada0 | gpart restore -F -f x ada1
# gpart show 
О, боже! Не тот диск, вы уничтожили диск с фотографиями вашей жены! =) Спокойно, -f вас спас:

# gpart undo ada1

Вот так вот, немного шутя можно работать с gpart. А ещё у меня новость, мои менторы освободили меня от "надзора" :) Теперь я стал самостоятельным коммитером.

 PS. Ещё раз хочу заметить, что gpart backup/restore выполняют копирование и восстановление ТОЛЬКО таблицы разделов. Данные, находящиеся на этих разделах, бэкапить и восстанавливать нужно отдельно. В том числе, и содержимое в разделы freebsd-boot нужно записывать повторно.

вторник, 9 ноября 2010 г.

sade - редактор диска, часть 6.

Вчера сделал MFC в stable/8 последних изменений в gpart. Теперь в грядущем релизе 8.2-RELEASE будут и возможность уничтожения таблицы без необходимости удаления всех её разделов, и восстановление повреждённой GPT. На ближайшее будущее новых идей относительно gpart вроде больше нет. Поэтому потихоньку вернулся к sade.

Первым делом привёл всё к работоспособному виду. На данный момент, работать оно может только в 9.0-CURRENT из-за некоторых изменений, о которых я уже раньше писал. Затем, перелопатил весь код работы с файловыми системами, который приводил меня в уныние. Я отказался от идеи сделать какой-то универсальный интерфейс frontend-backend, который изначально пытался делать. Пока из-за отсутствия опыта работы с различными FS у меня не вырисовывается стройной картины того, как можно эту работу "универсально" организовать.

Решил начать с малого. Переписал редактор файловых систем, теперь он предназначен для работы с UFS и весь код этой самой работы сосредоточен в файле ufsed.c. Что уже сдеалано: основное окно редактора предоставляет возможность посмотреть список найденных разделов с типом freebsd-ufs (в терминалогии gpart). При навигации по этому списку отображается информация о файловой системе, если она имеется на этом разделе, примерно вот так это выглядит:
Логика такая. Что делал пользователь, когда у него не было sade? :) Он действовал по такому алгоритму:
  1. Создание таблицы разделов;
  2. Создание слайса FreeBSD;
  3. Создание разметки bsdlabel;
  4. Создание файловой системы;
  5. Сохранение информации о ней в /etc/fstab.
Я решил придерживаться этого алгоритма внутри sade. Запустив sade, заходим в редактор разделов и выбираем из списка устройство, на котором создаём таблицу разделов, внутри неё создаём слайсы. Внутри слайса, если у него тип "freebsd" создаём таблицу BSD и размечаем разделы, выбирая "freebsd-ufs" или "freebsd-swap". Этим редакотором покрываются  первые 3 пункта + возможность записи загрузочного кода.
 Далее мы сохраняем всё что насоздавали в редакторе разделов, выходим в главное меню и заходим в редактор файловых систем. Там видим выше указанное окно. Сейчас у нас есть возможность создать новые файловые системы либо изменить уже существующие. Диалог создания файловой системы:
Простейшую историю команд я реализовал, т.е. теперь любые действия не выполняются немедленно, а сохраняются в историю, которая применяется во время сохранения изменений, либо откатывается полностью. В общем-то, так же, как и в редакторе разделов.
Для существующих файловых систем можно выбрать команду "Modify", которая открывает следующий диалог:
По сути этот диалог является UI для утилиты tunefs. А предыдущий - для newfs. На данный момент редактор не делает никаких изменений, т.е. команды записываются в историю, но не выполняются, даже при сохранении. Нужно обдумать как это более правильно сделать. 
Дальше планирую сделать возможность редактировать fstab, ну и, собственно, само сохранение изменений.

среда, 27 октября 2010 г.

VirtualBox и NS_ERROR_FAILURE (0x80004005)

Некоторое время назад у меня перестал работать VirtualBox. При запуске виртуальной машины он выдавал сообщение:

Код ошибки: NS_ERROR_FAILURE (0x80004005)
Компонент: Machine
Интерфейс: IMachine {6d9212cb-a5c0-48b7-bbc1-3fa2ba2ee6d2}
После чего это произошло я как-то не уследил, не очень часто им пользуюсь, но бывает удобно. Ну сломался и сломался, не очень-то и нужен был. Поиск в Интернете приводил на линуксовые форумы, даже на forums.freebsd.org есть тема. Всё сводилось к тому, что что-то не так с модулем vboxdrv. С которым вроде бы всё было в порядке, он вполне соответствовал версии virtualbox-ose и загружался без ошибок.

Выходили новые версии VirtualBox'а, обновление не помогало. Решил посмотреть для начала в Makefile портов. И вот, читая emulators/virtualbox-ose-kmod/Makefile обнаружил там такие строчки:

 SRC_BASE?=      /usr/src
...
.if !exists(${SRC_BASE}/sys/kern/bus_if.m)
IGNORE=         requires kernel sources
.endif
Вот тут-то до меня и дошло в чём проблема. Я последнее время обновлял систему и ядро из другого каталога, не из /usr/src. У меня в рабочем каталоге постоянно обновлённые исходники системы, там я их и редактирую, и там же компилирую при необходимости что-то протестировать. Поэтому, /usr/src у меня оказались заброшенными. А порт-то собирался с использованием /usr/src! Сразу же попробовал переопределить SRC_BASE и всё получилось. Так что, теперь в поисковике можно будет найти ещё одно решение этой проблемы :)

вторник, 26 октября 2010 г.

Восстановление GPT при помощи gpart

Вчера, наконец-то, внёс поддержку gpart recover в head/. Через две недели планирую сделать MFC в 8-stable, если всё будет хорошо. Но хотелось бы успеть до заморозки кода перед началом подготовки к релизу, иначе в 8-ку это всё попадёт уже нескоро.

Немного подробнее. Прежде всего стоит заметить, что теперь при обнаружении повреждений метаданных таблица GPT будет помечена как corrupt и с ней будут запрещены любые действия. Т.е. если раньше вы получали в консоль сообщения о повреждённой GPT и могли по-просту не обращать на них внимания, то теперь игнорировать не получится :)

Запрещать любые действия было бы не логично, если не предложить что-то взамен. Взамен предлагается своего рода гарантия, что вы ничего случайно не сломаете, а так же возможность починить таблицу разделов, либо, если она вам не нужна - уничтожить её совсем. Для возможности уничтожения таблицы пришлось переделать "gpart destroy -F". Теперь форсированное уничтожение выполняется внутри ядра, а не в userspace как раньше.

Что бы знать какие типы повреждений возможно восстановить в GPT при помощи gpart, нужно иметь представление о том, как устроена GPT. Если коротко, то состоит она из заголовка и самой таблицы разделов. Всё это дублируется. Заголовок основной таблицы находится во втором секторе диска, за ним следует таблица разделов, её размер может быть различным. Заголовок резервной таблицы находится в последнем секторе, таблица располагается в предшествующих ему секторах. Содержание таблиц идентично и должно иметь одинаковую контрольную сумму. А вот заголовки отличаются, в них сохраняются номера секторов, в которых находится сам заголовок и его копия, номер начального сектора таблицы и границы пространства для использования партициями. Часть этой информации отображается в выводе команды gpart list:

> gpart list ada1
Geom name: ada1
state: OK
fwheads: 16
fwsectors: 63
last: 320173022
first: 34
entries: 128
scheme: GPT
Здесь first и last - номера секторов, ограничивающих доступное пространство для разделов GPT, entries - максимальное количество записей в таблице, другими словами максимальное количество партиций.

Так же, ещё одним обязательным условием для работы с GPT является наличие PMBR, который занимает первый сектор. Если повредить содержимое PMBR, то класс PART не будет даже искать GPT на диске. Такова особенность. Поэтому если ваша GPT не обнаруживается совсем, ядро не выдаёт никаких сообщений, связанных с GPT, первым делом стоит восстановить PMBR. Его копию можно найти в файле /boot/pmbr. Нужно всего лишь записать его в первый сектор диска. Это автоматически инициирует поиск метаданных различными GEOM классами, в том числе и GEOM_PART_GPT.

Теперь о возможных повреждениях. Первое - это повреждение основного заголовка или таблицы GPT. При обнаружении такого повреждения ядро выдаст сообщение:
GEOM: provider: the primary GPT table is corrupt or invalid.
GEOM: provider: using the secondary instead -- recovery strongly advised.
Здесь provider - это имя диска, например ad0. Кроме этого сообщения, которое обычно можно увидеть только во время загрузки системы, о том что ваша GPT повреждена могут расказать команды gpart show, list и status.
> gpart show
=>        34  1250263661  ada0  GPT  (596G) [CORRUPT]
          34         256     1  freebsd-boot  (128K)
         290     8388608     2  freebsd-swap  (4.0G)
     8388898  1241874797     3  freebsd-zfs  (592G)

> gpart list ada0 | grep state
state: CORRUPT
Следующий тип - повреждение резервной копии заголовка или таблицы GPT. Как частный случай сюда же относится вариант несоответствия резервной и основной копий (например, когда в основной копии заголовок и таблица с одними данными, а в резервной - с другими, но сами по себе они являются вполне корректными). В это случае GPART просто воспользуется данными из основной копии. Сообщение от ядра в этом случае будет таким:
GEOM: provider: the secondary GPT table is corrupt or invalid.
GEOM: provider: using the primary only -- recovery suggested.
Третий случай, когда таблица GPT будет помечена как повреждённая - это неверное расположение заголовка резервной копии GPT. Такое может случится, например если у вас GPT создана на каком-то виртуальном носителе, который умеет расширяться путём добавления новых дисков. Либо, просто, например, вы создали GPT на gmirror устройстве, но забыли загрузить класс geom_mirror. В этом случае размер провайдера увеличится, так как gmirror резервирует пространство под свои метаданные.

Теперь, собственно про восстановление. Всё что нужно сделать - правильно выбрать носитель, на котором восстанавливать GPT и выполнить команду:
# gpart recover ada0
В моём примере это ada0. Почему я выделил слово "правильно"? Вернёмся к примеру, в котором GPT создана поверх gmirror. Так вот, если забыть загрузить gmirror, то GPT будет найдена на том диске, на котором создан gmirror. И соответсвенно, если выполнить gpart recover для этого диска, то все параметры заголовка GPT будут перерасчитаны, а значит изменится и значение last - границы последнего доступного сектора, а так же, в последний сектор диска будет записан заголовок резервной копии GPT, который уничтожит метаданные gmirror. Хорошо, если это именно то, чего вы хотели :)

Подитожив всё написанное выше, хочется ещё раз напомнить, что восстановление данных это ответственный процесс, нельзя делать его не обдумав. Это относится не только к теме данной заметки.

понедельник, 27 сентября 2010 г.

Новая опция для "gpart destroy"

Давненько я ничего сюда не писал. Почти месяц был без доступа к сети. Тяжело :)

За это время pjd@ успел наломать дров - пользуясь отсутствием Марселя он внес кучу изменений в geom(8) и в gpart(8). В частности, он изменил интерфейс взаимодействия между утилитой geom(8) и ядром. В связи с этим мне предстоит значительно повозится, исправляя sade, когда дойдут до неё руки...

А на данный момент, я добавил поддержку опции "-F" для команды "gpart destroy". Опция эта позволяет уничтожить таблицу разделов без необходимости удаления каждой её партиции. На мой взгляд - давно востребованная функция. Я даже где-то в списках рассылки видел шелл скрипт, выполняющий эту задачу.

Немного подробностей. Патч с реализацией этого функционала был готов ещё в конце июля, но тогда он ожидал одобрения от marcel@, затем от моих менторов. Так время тянулось в ожидании, а потом я попал в дтп и на месяц ушёл в оффлайн. Вернувшись, пришлось исправлять и патч (спасибо pjd :).

Марсель одобрил идею, которая заключалась в реализации всего функционала внутри gpart(8), без изменений внутри ядра. А именно, если пользователь указывает ключ "-F", то внутри gpart(8) в цикле удаляются все разделы таблицы, а затем уже уничтожается таблица.

С MFC этого функционала возникнут некоторые трудности, благодаря всё тем же изменения от pjd@, но проблема вроде бы решаемая. Так что через недельки полторы этот функционал появится и в 8-STABLE,

пятница, 23 июля 2010 г.

Максимальный размер freebsd-boot партиции

Один мой коллега из далёкого филиала пытался установить ZFS-only FreeBSD. Так как опыта в этом деле у него было немного, продолжалось это у него почти неделю (а он настырный :). Так вот, я его консультировал по IRC на сколько позволяли мои телепатические возможности и время, в итоге он сделал это, с чем я его и поздравляю. Обещал написать заметку на эту тему..

Но в общем-то, к чему я это рассказываю. Обнаружилась неожиданная особенность. Он вопреки всем мануалам, которых уже полно в Сети, создал партицию с типом freebsd-boot в таблице GPT размером в 1 МБ. В итоге, при загрузке он получал сообщение "Boot loader too large". Покопавшись немного в исходниках я выяснил, что это сообщение выдаёт код PMBR. На ассемблере последний раз я писал, наверное, ещё в универе, так что пришлось немного "помедитировать" над его кодом, хорошо что в нём отличные комментарии :)

В итоге решил добавить некоторые изменения в мануал gpart(8):
  • для параметров -s и -b можно использовать суффиксы k, m, g и т.д.;
  • размер партиции freebsd-boot не должен быть больше 545 Кбайт.

пятница, 9 июля 2010 г.

Отчёт за июнь

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

Июнь прошёл. Прогрессом в разработке sade похвастаться не могу. Его нет, т.к. нет времени.
Выложил исходный код в SVN. Из разработчиков написали двое - один спросил "что нужно для сборки?", второй - "узнал что есть такой редактор, где посмотреть?" :)
Первый собрал, понравилось, пожелал удачи :) Второй больше ничего не ответил.

По поводу того, что бы использовать sade в инсталляторе - в связи с последними событиями по импорту инсталлятора PC-BSD, думаю эта тема уже не актуальна. Так что будет просто удобная утилита для работы. В принципе, мне так даже проще :)

По просьбе двух товарищей с IRC канала #FreeBSD@RusNet закоммитил код новой netgraph ноды ng_patch и уже сделал MFC в 8-ку. Так же смержил почти все изменения в gpart из CURRENT в 8-STABLE. Включая и поддержку "gpart resize", MFC которой столкнулось с вопросом от Константина kib@ - "А не ломает ли оно KBI?". Другими словами, не повлияет ли изменение интерфейса в ядре на уже скомпилированные модули ядра. В результате пришлось вновь ковыряться в исходниках интерфейса KOBJ :) Ну и простой эксперимент показал, что подобные изменения не влияют на KBI. Новое ядро с расширенным интерфейсом g_part (там добавился новый метод resize) успешно подгружает старые модули g_part_xxx и они работают.

На очереди к MFC исправление для упомянутой мной ранее "особенности" реализации в определении размера сектора утилитой gpart. Теперь она научилась правильно определять размер сектора и больше подобные проблемы возникать не должны.

Так же, успел пообщаться с Марселем на тему реализации функции "gpart recover". Он дал несколько советов и попросил не останавливаться только на том, что уже сдалал, а подойти к проблеме более широко. Так что, появился простор для творчества, но времени всёравно нет :)