вторник, 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. Хорошо, если это именно то, чего вы хотели :)

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

15 комментариев:

  1. Подскажите, пожалуйста, каким образом добавить опцию "recovery" в рабочей системе? Имею:

    # uname -a
    FreeBSD 8.0-RELEASE FreeBSD 8.0-RELEASE #0: Sat Nov 21 15:02:08 UTC 2009 root@mason.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64

    ОтветитьУдалить
  2. Думаю, что вам можно загрузиться с live cd и выполнить восстановление.

    ОтветитьУдалить
  3. диск - почти 28Tb, 8.2 release x64
    сделал по мануалу man gpart
    gpart create -s GPT da0
    gpart bootcode -b /boot/pmbr da0
    gpart add -b 34 -s 128 -t freebsd-boot da0
    gpart bootcode -p /boot/gptboot -i 1 da0
    gpart add -b 162 -s 60G -t freebsd-ufs da0
    gpart add -t freebsd-swap -s 4G da0
    gpart add -t freebsd-ufs -s 400G da0
    gpart add -t freebsd-ufs -s 57620447037 da0

    на загрузке имею
    > Invalid backup GPT header

    после чего бутится и работает нормально...
    что не так?

    ОтветитьУдалить
  4. а что показывается gpart list после загрузки?
    При помощи чего сделан da0 на 28Т?

    ОтветитьУдалить
  5. srv3# gpart list
    Geom name: da0
    state: OK
    fwheads: 255
    fwsectors: 63
    last: 58593525726
    first: 34
    entries: 128
    scheme: GPT
    Providers:
    1. Name: da0p1
    Mediasize: 65536 (64K)
    Sectorsize: 512
    Mode: r0w0e0
    rawuuid: 5009d3cb-ffaa-11e0-9a06-00259029f58f
    rawtype: 83bd6b9d-7f41-11dc-be0b-001560b84f0f
    label: (null)
    length: 65536
    offset: 17408
    type: freebsd-boot
    index: 1
    end: 161
    start: 34
    2. Name: da0p2
    Mediasize: 64424509440 (60G)
    Sectorsize: 512
    Mode: r1w1e2
    rawuuid: 6edd9317-ffaa-11e0-9a06-00259029f58f
    rawtype: 516e7cb6-6ecf-11d6-8ff8-00022d09712b
    label: (null)
    length: 64424509440
    offset: 82944
    type: freebsd-ufs
    index: 2
    end: 125829281
    start: 162
    3. Name: da0p3
    Mediasize: 4294967296 (4.0G)
    Sectorsize: 512
    Mode: r1w1e1
    rawuuid: 86686b15-ffaa-11e0-9a06-00259029f58f
    rawtype: 516e7cb5-6ecf-11d6-8ff8-00022d09712b
    label: (null)
    length: 4294967296
    offset: 64424592384
    type: freebsd-swap
    index: 3
    end: 134217889
    start: 125829282
    4. Name: da0p4
    Mediasize: 429496729600 (400G)
    Sectorsize: 512
    Mode: r1w1e2
    rawuuid: cbfdf724-ffaa-11e0-9a06-00259029f58f
    rawtype: 516e7cb6-6ecf-11d6-8ff8-00022d09712b
    label: (null)
    length: 429496729600
    offset: 68719559680
    type: freebsd-ufs
    index: 4
    end: 973078689
    start: 134217890
    5. Name: da0p5
    Mediasize: 29501668882944 (27T)
    Sectorsize: 512
    Mode: r1w1e2
    rawuuid: 1db1073f-ffab-11e0-9a06-00259029f58f
    rawtype: 516e7cb6-6ecf-11d6-8ff8-00022d09712b
    label: (null)
    length: 29501668882944
    offset: 498216289280
    type: freebsd-ufs
    index: 5
    end: 58593525726
    start: 973078690
    Consumers:
    1. Name: da0
    Mediasize: 29999885189120 (27T)
    Sectorsize: 512
    Mode: r4w4e11
    ===========
    сделано на вполне железном рейде - 3ware 9750, 12 дисков по 3T (драйвер tws c официального сайта)

    ОтветитьУдалить
  6. Раз уж GEOM_PART посчитал что "state: OK", значит заголовок вполне себе корректный.
    Это сообщение от gptboot и наиболее вероятная причина в том, что он не может корректно определить размер диска.
    Это можно проверить, если в файле sys/boot/i386/common/gpt.c в функции gptread_hdr() добавить в сообщение об ошибке вывод LBA, с которого оно пытается читать. Что-то типа такого:
    printf("%s: unable to read %s GPT header from LBA %llu\n", BOOTPROG, which, (unsigned long long)hdrlba);

    После этого пересобрать gptboot и переустановить его..

    ОтветитьУдалить
  7. собственно, это у меня уже третий такой случай.
    все три - на 3ware рейдах, 9500, 9650, 9750

    к сожалению, все три уже в продакшене, экспериментирвоать не могу.
    размеры - от 2.7T до 27T

    ОтветитьУдалить
  8. Может заинтересует. Захотелось перенести систему в лоб с одного винта на другой:
    ada0p1 - freebsd-boot
    ada0p2 - freebsd-zfs

    dd if=/dev/ada0 of=/dev/ada1 bs=8M
    gpart recover ada1

    Теперь ada1 вроде как корректный, но zpool не позволяет с ним что-то сделать - идентификаторы gpt одинаковые. Создать пул/добавить - "/dev/ada1p2 is part of active pool", удалить - "no such device in pool"

    ОтветитьУдалить
  9. Проще, быстрее и правильнее было бы подключить второй диск в зеркало, затем по окончанию синхронизации отключить из зеркала первый.

    ОтветитьУдалить
  10. Здравствуйте Андрей!
    Подскажите пожалуйста, в FreeBSD 10.1-RELEASE-p16 поменялся синтаксис?
    # gpart recover ada2
    gpart: arg0 'ada2': Invalid argument

    После подключения диска к Windows7 через USB адаптор слетела разметка, метка диска, название пула, данные на месте и видны через 16-ти ричный редактор акрониса.
    Диск размечался так:
    # camcontrol devlist
    ***
    at scbus4 target 0 lun 0 (ada2,pass2)
    ***
    # gpart create -s GPT ada2
    ada2 created
    # gpart add -l COPY -t freebsd-zfs ada2
    ada2p1 added
    # gpart show
    ***
    => 34 5860533101 ada2 GPT (2.7T)
    34 6 - free - (3.0K)
    40 5860533088 1 freebsd-zfs (2.7T)
    5860533128 7 - free - (3.5K)

    # zpool create TEST gpt/COPY
    # zfs create TEST/TEST_COPY
    # zpool list
    NAME SIZE ALLOC FREE FRAG EXPANDSZ CAP DEDUP HEALTH ALTROOT
    TEST 2,72T 444K 2,72T 0% - 0% 1.00x ONLINE -
    Заполнил данными, экспортировал пул, погасил, отключил, подключил к Windows7, посмотрел в диспетчере, отключил, подключил к FREEBSD
    # dmesg | grep ada2
    ada2 at ahcich5 bus 0 scbus5 target 0 lun 0
    ada2: ATA-9 SATA 3.x device
    ada2: Serial Number WD-WMC4N0D6WSDS
    ada2: 300.000MB/s transfers (SATA 2.x, UDMA6, PIO 8192bytes)
    ada2: Command Queueing enabled
    ada2: 2861588MB (5860533168 512 byte sectors: 16H 63S/T 16383C)
    ada2: quirks=0x1<4K>
    ada2: Previously was known as ad14
    GEOM: ada2: the secondary GPT header is not in the last LBA.
    GEOM_PART: integrity check failed (ada2, GPT)

    # sysctl kern.geom.part.check_integrity=0
    kern.geom.part.check_integrity: 1 -> 0
    # gpart status ada2
    gpart: No such geom: ada2.
    # gpart recover ada2
    gpart: arg0 'ada2': Invalid argument

    ОтветитьУдалить
    Ответы
    1. gpart recover работает только если таблица разделов существует в ядре и помечена как CORRUPTED. В вашем случае видимо не так.
      Можете загрузиться в режиме bootverbose, тогда integrity check напишет что конкретно ему не нравится. Ну и, если вы уверены, что все размеры и смещения, которы приведены тут были действительно такими, то можно просто пересоздать таблицу разделов.

      Удалить
    2. Пересоздал поверх таблицу разделов, ZFS pool оказался на месте.
      gpart backup "наше всё"

      Удалить
  11. Добрый день, Андрей!
    Помогите, пожалуйста, решить две проблемы.

    Работаю на компе "ASUS Z87-PRO" с прошивкой UEFI "Z87-PRO-ASUS-2103.cap" (2014г)

    Первая трабла: Дистрибутивы FreeBSD-9.1, 10.0 .....10.2.
    Установка в режимах MBR или GPT.
    1. В случае единственного в системе HDD загрузка после уст-ки нормальная.
    2. При подключении в систему других дисков (Linux, FreeBSD) загрузка нарушается:
    2.1. Зависание на стадии "Root mount waiting for: usbus1"
    или "mountroot> waiting for device /dev/ada0p2 ", например.
    2.2. В случае просто остановки: "mountroot>" - удается вручную задать "правильный" раздел,
    например: "ufs:/dev/ada1p2" и загрузка выполняется нормально.

    Вторая трабла: Дистрибутивы FreeBSD-10.0 .....10.2.
    Невозможно произвести установку в режиме "Manual".
    А именно: не удается создать или отредактировать разделы и пр. из-за отказа клав. и мыши
    после выбора этого режима.
    В режиме "Auto" (испол. весь диск) установка идет нормально.
    На "9-ке" это не наблюдается.
    Спасибо.

    ОтветитьУдалить
    Ответы
    1. На мой взгляд это одна и та же проблема. Во время загрузки ядро не успевает дождаться инициализации USB контроллера. Или что-то вроде того. В BIOS'ах бывает настройка про поддержку legacy USB устройств. Кажется она помогает от пропадания клавиатуры. Ещё можно попробовать добавить в loader.conf настройку таймаута ожидания kern.cam.boot_delay (вроде бы в миллисекундах).

      Удалить