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

GEOM: практические знания. Часть 2.

Продолжить начатую ранее тему устройства GEOM я хотел бы с таких понятий как tasting, orphanization и spoiling. Для пользователя системы наиболее интересным наверно покажется первый термин. Tasting - это процесс автоматической настройки geom объектов. Этот процесс инициируется подсистемой GEOM каждый раз, когда создаётся новый GEOM класс или провайдер. С точки зрения алгоритма работы эти два случая немного различаются, так tasting при создании нового провайдера выглядит примерно так:
  1. Ядро проходит в цикле по списку известных ему GEOM классов.
  2. Для каждого из них вызывается метод класса taste, который уже в зависимости от реализации класса может создать новый geom объект, либо посчитать этот новый провайдер неподходящим для использования классом.
В случае, когда создаётся новый класс, алгоритм такой:
  1. Ядро проходит в цикле по списку известных ему GEOM классов.
  2. Для каждого класса просматривается каждый существующий geom объект и все его провайдеры, которые передаются на проверку в метод taste для нового класса. А тот уже решает, подходит ему этот провайдер или нет.
Таким образом сделана возможность своего рода "plug-and-play". При загрузке модуля ядра, реализующего какой-то новый GEOM класс, у него имеется возможность автоматически найти нужный провайдер и начать выполнять свои действия.

Рассмотрим пример возможного сценария работы tasting при обнаружении жесткого диска:
  1. Драйвер жесткого диска создаёт geom объект ada0 класса DISK, тот в свою очередь создаёт провайдер ada0.
  2. Создание провайдера инициирует tasting. Последовательность может отличаться, но мы предположим такое развитие событий - вызывается метод taste класса DEV. Этот класс служит для создания файлов устройств в файловой системе devfs. Т.е. это его мы можем поблагодарить за создание /dev/ada0 (стоит заметить, что этот класс не создаёт своих провайдеров).
  3. На этом процесс tasting не заканчивается. Предположим, что в списке классов, которые знает ядро, следующим стоит класс PART. Ядро вызывает метод taste этого класса, он считывает секторы этого провайдера, в которых могут находиться таблицы разделов, и проверяет есть ли они там. Если есть, то создаются новые провайдеры, в соответствии с таблицей разделов. Это, в свою очередь, опять же инициирует tasting.
Многие GEOM классы (не все) хранят свои метаданные на диске, обычно для этого используется один последний сектор. Метод taste этих классов считывает нужный классу сектор провайдера и проверяет считанные данные, если он находит там метаданные класса, он предпринимает какие либо действия. Чтобы пользователь случайно не повредил метаданные, размер создаваемого классом провайдера обычно меньше на один сектор. Например, когда вы создаёте зеркало на основе класса MIRROR, то размер итогового mirror/gm0 будет на один сектор меньше, чем размер провайдеров, из которых он состоит. Повреждение, перезапись последнего сектора, изменение размера провайдера или размера сектора - всё это приводит к тому, что метод taste не обнаружит свои метаданные на провайдере, а значит и не будет создан geom объект этого класса. Поэтому, если вы задумали нечто такое - не удивляйтесь последствиям :)


Про tasting сказать вроде больше нечего, перейдём к orphanization. Это обратный tasting процесс, во время которого провайдеры уничтожаются. В переводе orphanization означает "осиротение" и это не просто так. Например, когда вы отключаете жесткий диск, драйвер диска посылает классу DISK команду на уничтожение. Тот, в свою очередь, инициирует процесс "осиротения" и все объекты geom, связанные с провайдером диска через консьюмеров "информируются" о том, что они стали "сиротами". Им передаётся код ошибки, который будет возвращён пользователю при любых попытка взаимодействия с осиротевшими провайдерами. Т.е. если вы попробуете записать данные на такой провайдер, то получите ошибку. Информирование выполняется через вызов базового для всех классов метода orphan, в реализации которого разработчик может предусмотреть действия, необходимые для завершения работы объекта. Ну и заканчивается этот процесс самоуничтожением всех осиротевших провайдеров и объектов. Если не углубляться в особенности реализации, то можно сказать, что "оно" всё делает "само". Само находит что ему нужно во время tasting, само уничтожается во время orphanization :)

Теперь рассмотрим spoiling. Этот механизм предназначен для защиты от устаревших метаданных. Стоит понимать, что защащются здесь не метаданные, а находящийся в памяти geom объект. Так как система оперирует с данными в памяти, важно чтобы они были синхронизированы с метаданными на диске.

Ранее я рассказывал о счётчиках доступа, которые по-совместительству служат для ограничения доступа. Этими же счётчиками подсистема GEOM пользуется для активации механизма spoiling. Когда кто-либо обращается к провайдеру, он явно или неявно вызывает функцию g_access, в которой указывается желаемый режим доступа. Например, вы записываете что-то на диск, для этого вам нужно получить "разрешение" на запись, при этом счётчик w увеличивается. Как только вы прекращаете запись - счётчик уменьшается. Переход состояния счётчика w из нуля в значение большее нуля вызывает механизм spoiling. При этом все консьюмеры этого провайдера предупреждаются о том, что кто-то открыл провайдера для записи. Никто не знает, что вы туда будете записывать, но если geom объекту это важно (там могут быть его метаданные), то он примет информацию к сведению. Обычно "принять к сведению" означает, что geom объект самоуничтожится (так же, как он это делает во время orphanization). Но стоит вам завершить запись, чтобы значение счётчика w перешло в ноль, будет иницирован процесс re-tasting по алгоритму, используемому при создании нового провайдера.

Таким образом срабатывает защита от устаревших метаданных. Если вы измените метаданные, или же просто произведёте запись не связанную с ними, то GEOM класс, использующий провайдера, заново обнаружит свои метаданные на нём и автоматически сконфигурируется. Если же вы удалите или повредите метаданные, то spoiling уничтожит geom объект, использующий провайдер. Жизненный пример на эту тему - gpart vs. bsdlabel:

# gpart show md0 md0s1
=>     9  204786  md0  MBR  (100M)
       9  204786    1  freebsd  (100M)

=>     0  204786  md0s1  BSD  (100M)
       0   51200      1  freebsd-ufs  (25M)
   51200   51200      2  freebsd-ufs  (25M)
  102400   51200      4  freebsd-ufs  (25M)
  153600   51186         - free -  (25M)

# bsdlabel -w md0s1
# gpart show md0 md0s1
=>     9  204786  md0  MBR  (100M)
       9  204786    1  freebsd  (100M)

=>     0  204786  md0s1  BSD  (100M)
       0      16         - free -  (8.0K)
      16  204770      1  !0  (100M)

В первом выводе gpart show есть два geom объекта класса PART. md0 - представляет таблицу разделов MBR, а md0s1 - является партицией MBR, провайдером объекта md0, и, в то же время, содержит разметку BSD. Выполнив bsdlabel -w мы выполнили запись на раздел md0s1, изменив метаданные объекта md0s1. При этом, в момент вызова команды bsdlabel она открыла провайдера md0s1 для записи, что инициировало spoiling - самоуничтожение объекта md0s1 и всех его партиций. После завершения записи на провайдере md0s1 снова были найдены метаданные разметки BSD, но на этот раз там один раздел.

Ещё один пример, эксплуатирующий эти особенности GEOM - это запуск механизма re-tasting в ручную. Хотя, честно говоря, мне он ниразу не понадобился, но в списках рассылки вчтречаются люди, которым это нужно. Делается это просто, нужно всего лишь "открыть" провайдер для записи и "закрыть" его, например так:

# true > /dev/ada2 

Это инициирует re-tasting и GEOM классы заново "обследуют" этот провайдер.


На этом пока всё. На счёт продолжения не уверен, но, возможно, будет ещё одна заметка.

    пятница, 10 декабря 2010 г.

    GEOM: практические знания. Часть 1.

    Многие пользователи FreeBSD слабо представляют себе что такое GEOM, для чего он нужен и как он работает. По разговорам в IRC и обсуждениям в списках рассылки видно, что даже те из пользователей, которые часто и много где используют различные GEOM классы, имеют смутное представлени о происходящих внутри GEOM процессах. В этой заметке я попробую прояснить некоторые моменты, которыми часто интересуются.

    Начнём с определений. Что такое GEOM класс? Классом он называется в связи с похожестью на классы в Си++. Т.е. это некоторая структура, содержащая в себе методы-функции которые реализуют возможности этого класса. Набор методов стандартный для всех классов. Грубо говоря, разработка какого-то нового класса - это новая реализация этих методов.

    GEOM класс может иметь объекты (а может и не иметь). Если проводить аналогии с классами Си++, то это переменные этого класса. Например, есть GEOM класс MIRROR, он предоставляет возможность создавать зеркала. Когда вы загружаете модуль ядра, либо он у вас вкомпилирован туда, то ядро "знает" о существовании такого класса. Но когда вы создаёте зеркало, вы создаёте объект этого класса.

    Каждый объект geom может иметь "провайдера" (provider), даже несколько. Провайдер, это в общем-то то, ради чего GEOM и придуман. Они предоставляют пользователю услугу реализуемую GEOM классом. Тот же GEOM класс MIRROR предоставляет нам провайдера, например mirror/gm0, работая с которым мы получаем "услугу", реализующую программное зеркало.

    Другим важным понятием является консьюмер (consumer). Консьюмер - это то, через что geom объект получает доступ к провайдерам других объектов geom. Пример: у вас есть два жестких диска. Кстати говоря, драйверы дисков создают geom объекты класса DISK. Вы можете выполнить команду "geom DISK list", чтобы просмотреть информацию об объектах класса DISK:

     > geom DISK list
    Geom name: ada0
    Providers:
    1. Name: ada0
       Mediasize: 41110142976 (38G)
       Sectorsize: 512
       Mode: r0w0e0
       fwsectors: 63
       fwheads: 16
    
    Geom name: ada1
    Providers:
    1. Name: ada1
       Mediasize: 163928604672 (153G)
       Sectorsize: 512
       Mode: r2w2e5
       fwsectors: 63
       fwheads: 16
    
    Как видно, у меня тут два диска ada0 и ada1. Они у меня разные да и заняты данными, так что портить для демонстрации я их не буду. Создам два идентичных объекта класса MD:

    # mdconfig -s 100m
    md0
    # mdconfig -s 100m
    md1
    # geom MD list
    Geom name: md0
    Providers:
    1. Name: md0
       Mediasize: 104857600 (100M)
       Sectorsize: 512
       Mode: r0w0e0
       type: swap
       length: 104857600
       fwsectors: 0
       fwheads: 0
       unit: 0
    
    Geom name: md1
    Providers:
    1. Name: md1
       Mediasize: 104857600 (100M)
       Sectorsize: 512
       Mode: r0w0e0
       type: swap
       length: 104857600
       fwsectors: 0
       fwheads: 0
       unit: 1
    Как видно из вывода команд, у нас есть два объекта md0 и md1, у каждого из них по одному провайдеру, соответственно md0 и md1. Теперь, при создании geom объекта класса MIRROR его консьюмеры подключатся к провайдерам объектов md0 и md1, и будет создан провайдер mirror/gm0:

    # gmirror label gm0 md0 md1
    # geom MIRROR list
    Geom name: gm0
    State: COMPLETE
    Components: 2
    Balance: load
    Slice: 4096
    Flags: NONE
    GenID: 0
    SyncID: 1
    ID: 7231867
    Providers:
    1. Name: mirror/gm0
       Mediasize: 104857088 (100M)
       Sectorsize: 512
       Mode: r0w0e0
    Consumers:
    1. Name: md0   Mediasize: 104857600 (100M)
       Sectorsize: 512
       Mode: r1w1e1
       State: ACTIVE
       Priority: 0
       Flags: NONE
       GenID: 0
       SyncID: 1
       ID: 2647920667
    2. Name: md1
       Mediasize: 104857600 (100M)
       Sectorsize: 512
       Mode: r1w1e1
       State: ACTIVE
       Priority: 1
       Flags: NONE
       GenID: 0
       SyncID: 1
       ID: 4248479672
    
    Думаю этого должно быть достаточно для понимания, если недостаточно, попробуйте прочитать ещё раз :)

    Глядя на вывод команды geom <КЛАСС> list можно увидеть много полезной информации, для каждого класса "полезность" определил разработчик, поэтому у каких-то классов больше, у других - меньше, у третьих вообще ничего нет...

    Я бы хотел обратить внимание на строку содержащую слово "Mode:" и набор букв с цифрами. Она есть у всех провайдеров и консьюмеров и показывает режим доступа, который осуществляется по отношению к ним на данный момент. В первом выводе "geom MD list" режим доступа к провайдерам отображается как "r0w0e0". Это означает, что никто не пытается с них читать (r = 0), никто не пытается на них записывать (w = 0) и никто не пытается получить эксклюзивный доступ к ним (e = 0).

    Эти счётчики служат не только для отображения информации о текущем состоянии провайдеров и консьюмеров. Они так же являются ограничителями доступа. Например, если кто-то уже получил эксклюзивный доступ к провайдеру, то любой другой желающий получить доступ к нему будет "обрадован" сообщением об ошибке EPERM - "Operation not permitted". Наверное, многие из вас сталкивались с этим сообщением при попытке записать загрузочный код на провайдер, который содержит смонтированные разделы. Именно эта "защита от прострела ноги" отключается переменной "kern.geom.debugflags=16".

    Если выполнить команду "geom MD list" сейчас, после создания зеркала на них, то мы увидим, что у провайдеров изменился режим:

    # geom MD list
    Geom name: md0
    Providers:
    1. Name: md0
       Mediasize: 104857600 (100M)
       Sectorsize: 512
       Mode: r1w1e1
       type: swap
       length: 104857600
       fwsectors: 0
       fwheads: 0
       unit: 0
    
    Geom name: md1
    Providers:
    1. Name: md1
       Mediasize: 104857600 (100M)
       Sectorsize: 512
       Mode: r1w1e1
       type: swap
       length: 104857600
       fwsectors: 0
       fwheads: 0
       unit: 1
    
    Это постарались консьюмеры объекта mirror/gm0. Т.е. теперь, эти провайдеры защищены от случайной записи с вашей стороны, а значит и объект mirror/gm0 не будет неприятно удивлён испорченными данными. Поэтому использование "kern.geom.debugflags=16" является плохой практикой.

    ... Продолжение следует ...

    понедельник, 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,