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

    пятница, 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". Он дал несколько советов и попросил не останавливаться только на том, что уже сдалал, а подойти к проблеме более широко. Так что, появился простор для творчества, но времени всёравно нет :)

    понедельник, 7 июня 2010 г.

    Преступление и наказание.

    Давненько я ничего сюда не писал. В связи с ремонтом в квартире всё никак не могу найти времени..

    Итак, к чему такой subj? Май прошёл для меня плодотворно. Не только в связи с ремонтом в квартире, который проходит, на мой взгляд, ударными темпами, но и из-за событий связанных со мной и FreeBSD. 3-го июня меня "наказали" коммит битом в дерево src. Теперь я официально стал src-коммитером FreeBSD. :-)

    Длилась эта процедура чуть больше месяца. Всё началось с предложения Константина (kib@) стать коммитером, на которое я дал согласие. Затем он написал обращение в FreeBSD Core Team с предложением "наказать" меня за мои деяния. На что core@ дало согласие. Моими менторами стали Константин Белоусов (kib@) и Александр Мотин (mav@). Далее последовали создание аккаунта на кластере freebsd.org и первые, традиционные коммиты.

    Менторы курируют начинающих разработчиков. Советуясь с ними я должен освоить основные правила, которые применяются в проекте. Ну и главное, я должен согласовывать с ними все изменения, которые я собираюсь вносить в дерево исходных кодов FreeBSD. У меня два ментора. Причина в том, что сфера моих интересов слабо пересекается с интересами моих менторов. В качестве своих интересов я обозначил: подсистемы ядра GEOM, ATA, IPFW и файловые системы. Вот этим и буду заниматься в ближайшем будущем. Так же, я планирую перевести разработку sade в svn репозиторий FreeBSD. Так будет и мне проще, да и вдруг, кто-то решит помощь оказать?

    четверг, 29 апреля 2010 г.

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

    Немного новостей о ходе работы. Марсель закоммитил два исправления, о которых я упоминал ранее. Теперь, вроде бы нет препятствующих разработке багов.

    Я же выделил код и объявления структур и функций для работы с устройствами, партициями и файловыми системами в отдельную группу файлов. Позже оформлю их в виде библиотеки. Написал код для получения информации о файловых системах и сохранения его в список, которой используется внутри редактора файловых систем. Сделал базовую реализацию редактора, пока что он отображает список партиций имеющих тип freebsd-swap и freebsd-ufs:
    На список команд не обращайте внимание, это copy-paste из редактора партиций. В принципе, кроме того что видно в списке сейчас, информационная структура хранит ещё данные о том смонтирована ли файловая система и опции монтирования из /etc/fstab.
    Сделал так же проверку на наличие меток glabel, под которыми данное устройство может быть смонтировано или записано в fstab. Правда пока эта проверка достаточно формальная, потому как перебрать все возможные метки какие могут быть - достаточно нетривиальная задача.

    Дальше буду думать, какие команды добавить в этот редактор и потихоньку их реализовывать.
    Ещё надо что-то придумать с механизмом commit/undo для этого редактора. Небольшой набросок механизма ведения истории действий с возможностью отката и сохранения я сделал, но ясной картины как я буду его использовать пока нет.

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

    gpart resize - лёд тронулся.

    Ну вот, переодическое "капанье на мозг" :-) дало первые результаты. Marcel закоммитил мой патч, добавляющий поддержку изменения размера провайдеров внутри схемы. Константин Белоусов (kib@) посоветовал не забыть через некоторое время напомнить ему сделать MFC. Думаю недельки через 3-4 можно будет начинать "напоминать" :-)

    А пока, отправил ему письмо с благодарностью и ссылками на следующие два патча :-)

    gpart recover - восстановление таблицы GPT

    Когда я писал патч для изменения размеров партиций, я пытался решить свою задачу, что, в принципе, мне удалось. Тут в списке рассылки freebsd-geom@ появился человек, заинтересованный в изменении размера носителя, на котором находится схема GPT. У него используется RAID контроллер, который позволяет наращивать дисковое пространство, что-то вроде `zpool attach`. Вставил ещё диск, подключил его к рейду и вот - у тебя уже больше свободного места. Проблема только в том, что GPT хранит две копии метаданных - в начале диска и в конце.
    Соответственно, после увеличения размера носителя, вторая копия будет находится уже не в конце и система при подключении этой GPT схемы выдаст в лог такие строчки:
    GEOM: md0: the secondary GPT table is corrupt or invalid.
    GEOM: md0: using the primary only -- recovery suggested.
    Но, на данный момент, штатной возможности восстановить метаданные нет. Она есть в планах на wiki.freebsd.org. И я уже спрашивал Марселя о том, что он думает на этот счёт, но ответа не получил. Поэтому немного почитав код реализации схемы GPT, сделал набросок патча, который помимо восстановления метаданных позволяет выполнять такую "махинацию" с изменением размера носителя. Патч и пример использования можно посмотреть тут.

    четверг, 22 апреля 2010 г.

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

    После небольшого перерыва решил продолжить эпопею с sade.

    От marcel@ по прежнему тишина, поэтому думаю придётся отказаться от использования geom_part.so в пользу libgeom. В связи с этим предстоит частичное дублирование кода из geom_part.so у себя. В основном это будет код записи загрузочного кода и автоподбора параметров. Но зато стандартно и без лишних костылей...

    После непродолжительных обсуждений в IRC и некоторого обдумывания придумал, как примерно будет выглядеть интерфейс для работы с ZFS и создания файловых систем. К тому же решил, что будет полезной возможность создания RAID при помощи GEOM классов. Поэтому добавил главное меню, в котором пока 3 пункта:
    [Image]
    Пока что работает только второй, но дело сдвинулось с мёртвой точки.

    По моей логике работа с sade будет выглядеть примерно так:
    1. Сначала пользователь может зайти в раздел RAID (если ему нужно это) и там выбрать устройства, которые он планирует объединить в RAID. Причём кроме обычных gmirror и gstripe думаю туда же добавить и возможность создания ZFS пула из нескольких устройств (т.е. mirror, raidz, raidz2). Возможно стоит туда же втиснуть возможность конфигурирования gjournal, geli,... Но про последние пока не думал, да и не использовал я их по сути.
    2. После создания конфигурации для RAID, либо сразу, если она не нужна, пользователь заходит во второй пункт. Тут уже открывается список устройств, из которых можно выбрать только одно (единовременно) и дальше типовой функционал gpart. Создание схемы, добавление партиций на неё и запись загрузочного кода. Сюда же входит создание разметки BSD. Для ZFS будет отдельный редактор, больше похожий на редактор файловых систем. Он будет открываться при выборе ZFS пула из списка устройств. Или когда пользователь выберет в списке возможных схем "создание ZFS пула".

      Вообще, работу с ZFS думаю реализовать по аналогии с партициями. Создание пула соответствует созданию схемы, а создание датасетов - созданию партиций. Т.е. например, когда в редакторе партиций нажать Enter на партиции с типом freebsd-zfs, то будет предложено создать пул на ней (если конечно там его ещё нет), после чего откроется редактор ZFS, в котором можно создавать датасеты.
    3. После создания партиций пользователь должен будет выйти в главное меню и выбрать третий пункт. Это будет тоже отдельный редактор, над созданием которого сейчас я начинаю работать. Это будет список, пока я планирую выводить 4 столбика:
      имя_провайдера<--->тип_ФС<--->Размер<--->Точка монтирования

      В список поддерживаемых ФС пока планирую включить: ufs, zfs, swap. Ещё там может отображаться "empty" и "unknown". "empty" для свежесозданных партиций, а "unknown" для случая когда нет прав для того чтобы определить тип ФС или ещё чего-то непредвиденного.
    Вот как-то так..

    среда, 14 апреля 2010 г.

    Монтирование флэшек с FAT в XFCE

    Уже довольно давно пользуюсь XFCE4 в качестве DE. XFCE и Xorg установлены с поддержкой HAL. У меня лично монтирование флешек во FreeBSD проблем не вызывает - "sudo mount_msdosfs /dev/da0s1 ..." и делов-то. Но настраивая компьютер родителям, подумал что им, далёким от IT людям, такой способ не очень подходит. В принципе, при подключении флэшки на рабочем столе XFCE появляется иконка и монтирование выполняется вполне автоматически, только вот проблемка - файлы с кириллицей в именах в файловом менеджере не видно. Да ещё эти права доступа...

    И вот, решил "доковырять" то, что давно уже нужно было сделать - монтирование с нужными опциями. Через PolicyKit настроил разрешения на монтирование для пользователя homeuser, под котором будут заходить в систему родители:
    <match user="homeuser">
           <match action="org.freedesktop.hal.storage.mount-removable">
               <return result="yes"/>
    
           </match>
           <match action="org.freedesktop.hal.storage.mount-fixed">
               <return result="yes"/>
           </match>
           <match action="org.freedesktop.hal.storage.eject">
               <return result="yes"/>
    
           </match>
           <match action="org.freedesktop.hal.storage.unmount-others">
               <return result="yes"/>
           </match>
       </match>
    
    После этого занялся поиском того, как настроить опции "по-умолчанию" для монтируемых устройств. Сначала я думал сделать это внутри политик HAL, но потом случайно наткнулся на способ, который предоставляет XFCE.

    В файле /usr/local/etc/xdg/xfce4/mount.rc можно выбрать значения опций по-умолчанию для монтирования различных типов файловых систем. Вот такие опции выбрал я:
    [vfat]
    # FreeBSD specific option
    longnames=true
    -L=ru_RU.UTF-8
    -D=CP866
    -m=0664
    -M=0775
    -u=homeuser
    
    Список возможных опций можно посмотреть в выводе lshal после подключения флэшки. Искать нужно строку volume.mount.valid_options. В ней содержатся разрешённые HAL'ом опции, которые можно передать ему для монтирования. Их же можно найти в файле /usr/local/share/hal/fdi/policy/10osvendor/20-storage-methods.fdi. По-умолчанию для FreeBSD эти опции разрешены и соответствуют перечисленным в мануале mount_msdosfs(8).

    В результате, при подключении флэшки на рабочем столе появляется иконка диска, на которой можно  вызвать команду "Подключить том", либо просто два раза кликнуть и откроется Thunar с уже смонтированным содержимым. Причём, файлы с кириллицей в именах будут корректно отображаться и пользователь сможет записывать и удалять их.

    воскресенье, 11 апреля 2010 г.

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

    По совету Константина (kib@) опубликовал RFC в списках рассылки freebsd-current@ и freebsd-geom@ на тему переписывания sade(8). Как оказалось я не единственный, кто этим занимается, но другим, пока что, показать совсем нечего. Поэтому решил продолжать сам.

    Закончил все функции редактора и нашёл ещё один "полубаг" в реализации схемы APM. Класс не находит метаданные схемы APM, если после её создания не было создано ниодной партиции. Но в общем-то это не так страшно.

    Из плюсов этого RFC было то, что ответил Marcel. Написал что он очень занят, но патчи нужно всётаки коммитить, попросил переслать всё, что я ему посылал до этого. Переслал. Опять тишина...

    Исправил баг в схеме MBR, который вызывал панику при отмене удаления схемы. Модифицировал подобным же образом все схемы, которые могут зависеть от геометрии провайдера. Ответа пока тоже нет...

    Выложил всё, что накодил для sade вместе со скриншотами. Если кто желает, можете потестить, только осторожно, механизмы вызова паники я вроде бы описывал ранее.

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

    По загрузочному коду довольно много неясности вносит наличие множества различных вариантов и отсутсвие опыта использования большинства из них :)
    Для разных схем есть различные реализации загрузочного кода, из которых сам я пробовал только mbr и boot0 для MBR схемы, и gptzfsboot с pmbr для схемы GPT. А ведь есть ещё и другие схемы, которые используются на аппартных архитектурах, которые я вообще не видел, таких как PC98 и APM. Наверное, сделаю только то, что сам пробовал, а дальше уж видно будет.

    По файловым системам тоже пока вопросов больше чем ответов. По сути, FreeBSD работает только с двумя файловыми системами - UFS и ZFS. С ZFS отдельная история, так как она не обычная файловая система, а ещё и в некоторой степени LVM. Поэтому, думаю что стоит начать с реализации только UFS. Дальше опять вопросы - где должен быть реализован функционал создания файловых систем? Внутри редактора партиций или же в отдельном редакторе? С точки зрения моей логики, вроде бы удобнее создать файловую систему сразу после создания партиции. Но опять же, куда в редакторе размещать все элементы интерфейса? Где отображать точку монтирования, опции для newfs и т.п.?

    Короче, работы ещё полно...

    среда, 7 апреля 2010 г.

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

    Фреймворк для создания "нестандартных" диалогов практически готов, по крайней мере он почти обеспечивает мои текущие нужды. Называется он "customdlg". Появилось пара мыслей по его улучшению, но это пока можно отложить на будущее. Как говорится - нет предела совершенству :).

    После фреймворка я продолжил реализацию оставшихся функций редактора партиций. На данный момент уже работают:
    • Create Slice - создание партиций;
    • Delete Slice - удаление партиций; когда все партиции удалены эта же команда предлагает уничтожить текущую схему (gpart destroy);
    • Undo All Changes - отмена несохранённых изменений (gpart undo);
    • Write Changs - сохранение изменений на провайдер (gpart commit);
    • Set Active - установка аттрибута активной партиции для схем MBR и PC98 (gpart set -a active);
    • Inspect Slices - откытие субредактора партиций для схемы BSD, т.е. если имеется партиция с типом "freebsd" то нажав на ней Enter открывается тот же редактор партиций (если необходимо, с предложением создать схему BSD на провайдере) но слегка ограниченный.
    В принципе, нереализованными остались только команды "Change Type" и "Set Label", которые я решил объединить в один диалог. Но это дело получаса работы. И тогда можно будет анонсировать куда-нибудь в freebsd-hackers@ или freebsd-geom@, посмотреть что люди думают, может какие-то предложения будут дельные.

    Да, ещё.  В процессе реализации функционала и тестирования было обнаружено два бага в реализации класса PART. Первый заключается в том, что созданную схему "BSD" и "VTOC8" нельзя отменить при помощи команды "gpart undo". Эту проблему я решил, осталось дождаться, что ответит мэйнтейнер gpart. Ну и второй - он уже более неприятен. После создания схемы MBR без разделов, попытке удалить её и отменить удаление, в модуле geom_part_mbr происходит деление на ноль и паника. Покопавшись немного простого решения не нашёл, пока открыл PR. Если Marcel до завтра не ответит, буду пытаться решать сам.

    четверг, 1 апреля 2010 г.

    sade - редактор диска

    После изучения GEOM класса PART и беглого просмотра кода редактирования слайсов в sysinstall и "отпочковавшегося"  от него sade, захотелось научить их делать это "правильно" :)

    Но чем дальше я читал код sade и думал как бы его лучше изменить, тем всё грустнее мне казалась эта перспектива. Поэтому я начал с малого, набросал код функций для типовых операций, проводимых с диском. Сначала это были функции получения списка устройств, пригодных для разметки, и получения таблицы партиций на устройстве. Эти действия легко реализуются при помощи libgeom. Попутно изучал более подробно код sade и пробовал различные примеры по использованию libdialog и ncurses.

    Когда встала задача написания функций редактирования таблиц партиций, то мне показалось целесообразным использовать уже написанный код библиотеки geom_part, которая используется утилитой gpart(8). Всё бы ничего, но как оказалось, библиотека не предназначена для этого. Т.е. в рамках приложения gpart(8) она это вполне успешно делает, но вот из своего приложения её использовать нельзя, так как библиотека обрабатывает ошибки при помощи вызовов err(3) и abort(3). Пришлось отучить её от этого, после чего получилось использовать её код.

    В своих же функциях мне просто осталось правильно сформировать запрос, заполнив нужные параметры, и вызвать функцию из библиотеки geom_part.

    Очень полезной оказалась и встроенная поддержка отложенных операций в классе PART, т.е. из своего приложения можно выполнять различные действия с таблицей партиций, а при необходимости можно сделать откат, либо сохранить все изменения на диск.

    Далее я принялся за интерфейс пользователя. По-началу дело шло неплохо. Вот так к примеру выглядит окно выбора устройств:
    Приятным бонусом от использования libgeom стало появление в списке устройств различных GEOM провайдеров. Вообще, полный список поддерживаемых устройств выглядит так:
            {"DISK",        "disk device"},
            {"MD",          "memory backed virtual disk"},
            {"ZFS::ZVOL",   "ZFS volume"},
            {"MIRROR",      "GEOM based mirror (RAID1)"},
            {"STRIPE",      "GEOM based stripe (RAID0)"},
            {"RAID3",       "GEOM based RAID3 array"},
            {"CONCAT",      "GEOM based concatenated disk"},
            {"ELI",         "GEOM based encrypted disk"},
            {"JOURNAL",     "GEOM based journalled disk"},
            {"MULTIPATH",   "GEOM based disk with multiple path"},
    
    Список редактируется легко, так что добавить новое, либо что-то убрать из него не составить труда.
    После выбора устройства по логике должен открываться редактор партиций, за основу я взял существующий в sysinstall и sade, но слегка изменил и улучшил его. Вот так он выглядит:
    Пока что он ничего полезного не делает, кроме отображения списка партиций и навигации по нему. В дополнение к этому я сделал ещё один диалог, который открывается при выборе устройства, на котором ещё нет таблиц партиций:
    После выбора типа таблицы она создаётся на носителе и открывается редактор партиций.

    Вот. Это пока то, что удалось реализовать без особых сложностей. Теперь встала задача добавления новых диалогов. Как оказалось библиотека libdialog предоставляет довольно скудные возможности для создания диалогов, отличающихся от стандартных. А те что предоставляет, выглядят как-то "топорно". Поэтому... Пишу вот сейчас некий "фреймворк" для создания нестандартных диалогов. Вот таким, к примеру, планируется диалог добавления нового слайса:
    БОльшая часть "фреймворка" уже написана. Осталось немного и можно будет продолжить реализовывать функции редактора партиций. Ну и ещё остались пока нерешённые вопросы:
    1. Что делать с ZFS?
    2. Как создавать файловые системы, надо ли их монтировать, надо ли их в fstab прописывать и т.п.

    понедельник, 22 марта 2010 г.

    gpart: изменение размера раздела

    Доделал поддержку изменения размера разделов для GEOM класса PART. На данный момент идеи "что бы туда ещё добавить" закончились, поэтому решил выложить патчи.

    Немного о цели патчей. Были у меня несколько машин, на которые я уже давно для тестов ставил FreeBSD с ZFS. Они полностью установлены на ZFS, но было оставлено свободное пространство "на всякий случай". Там используется GPT "схема" со "стандартной" разбивкой на три партиции: freebsd-boot, freebsd-swap и freebsd-zfs. Свободное место было оставлено в конце диска. И вот, в один прекрасный момент захотелось использовать свободное место. Но создавать отдельную партицию не хотелось после успешных экспериментов по расширению пула ZFS.

    После постановки задачи "как это сделать" очевидных решений найдено не было. Первое, что пришло на ум - поправить в таблице GPT размер партиции. Решил попробовать, подампил таблицу в файлы, поизучал формат... Решил посмотреть, что там в коде реализации. Оказалось, что "заготовка" базового метода resize уже имелась в коде класса. Только она ничего не делала, а возвращала ошибку ENOSYS.

    Я связался с автором класса PART - Marcel Moolenaar. Обсудил с ним что и как лучше реализовать. После чего взялся за эксперименты. Что в итоге получилось, можно увидеть здесь. Ход работы можно проследить в perforce.

    Если в кратце, то в начале пришлось заново вспомнить изучить для чего в дереве кода ядра используются файлы с расширением "*.m". Они применяются для создания подобия ООП в коде ядра. Как я уже упоминал ранее, структура класса PART интересна своей модульностью. Сам класс имеет базовые методы работы с партициями, а все детали реализации описываются в отдельных модулях схем. Так вот, в .m файлах описан программный "интерфейс" подобных модулей. Эти файлы обрабатываются awk скриптом во время компиляции ядра и генерируются файлы .c и .h.

    Грубо говоря, благодаря им и создаётся иллюзия ООП. Хотя на мой взгляд всё довольно условно :) Итак, реализовав базовый алгоритм изменения размера в ядре и добавив новую команду в утилиту управления gpart, я добавил поддержку в GPT схему. Потестировал на md(4) дисках - вроде работает и даже не паникует :)
    Решил попробовать на рабочей системе, попробовал - размер изменился, перезагрузился - пул "подхватил" свободное пространство и я счастлив :)

    После этого добавил поддержку в оставшиеся схемы, немного потестировал на том же md(4) и вроде всё. Да, только в EBR не стал добавлять, там как-то не всё гладко в реализации.

    Пример использования:
    # gpart show md0
    =>    34  409533  md0  GPT  (200M)
          34  204800    1  freebsd-zfs  (100M)
      204834  204733       - free -  (100M)
    
    # gpart resize -s 150m -i 1 md0
    md0p1 resized
    # gpart show md0
    =>    34  409533  md0  GPT  (200M)
          34  307200    1  freebsd-zfs  (150M)
      307234  102333       - free -  (50M)
    
    # gpart resize -i 1 md0
    md0p1 resized
    # gpart show md0
    =>    34  409533  md0  GPT  (200M)
          34  409533    1  freebsd-zfs  (200M)
    
    
    У команды resize есть два параметра "-i index" и "-s size". Указание индекса обязательно, а вот размер может быть вычислен автоматически. Изменяться может только смещение конца партиции, смещение начала всегда остаётся неизменным.

    И ещё важное замечание - изменяются только метаданные таблицы разделов, изменение размеров файловой системы нужно производить сторонними методами, например growfs для UFS. ZFS сама видит увеличение. Уменьшение разделов не запрещено и работает, но что на это скажет файловая система раздела - мне, честно говоря, не очень хочется узнавать :)

    четверг, 18 марта 2010 г.

    Использование gpart

    Утилита gpart служит для управления GEOM классом PART. На самом деле это хардлинк на утилиту geom(8), которая является универсальной утилитой управления для многих GEOM классов.

    Как я уже упоминал в предыдущей заметке, класс PART служит для работы с таблицами партиций и предоставляет доступ к провайдерам этих партиций. С появлением этой утилиты можно уже забыть такие утилиты как fdisk и bsdlabel, они морально устарели. Большинство их действий можно выполнять при помощи утилиты gpart. Нужно только понять и запомнить основные понятия - схема и партиция. Прежде чем создавать партиции, необходимо выбрать тип таблицы - схему. Например, для привычной разметки с использованием MBR необходимо выполнить команду 'gpart create' с указанием имени схемы в опции "-s mbr":
    # gpart create -f x -s mbr md0
    md0 created
    # gpart show md0
    =>     17  1023978  md0  MBR  (500M)
           17  1023978       - free -  (500M)
    
    # gpart commit md0
    
    Опция "-f x" указывает классу не вносить изменения на провайдер носителя сразу. Т.е. если вы не уверены в своих действиях, вы можете с каждой командой указывать эту опцию и после того как будете уверены, выполнить команду `gpart commit`, либо отменить изменения командой `gpart undo`. Не имеет значения, какой флаг вы укажете после опции "-f", т. к. класс отслеживает только наличие опции и её отличие от значения, принятого по-умолчанию. Если не указывать опцию "-f", то её значение по-умолчанию будет установлено в "-f C", что приведёт к автоматическому выполнению команды `gpart commit`.

    Таблица MBR может содержать максимум 4 записи, т.е. не более 4 партиций. После того как схема создана можно создавать партиции при помощи команды `gpart add`. Каждая партиция обладает несколькими обязательными свойствами - смещение начала, смещение конца, индекс в таблице и тип партиции. А так же одно дополнительное свойство, которым обладают партиции некоторых схем - символьная метка.  Все эти параметры видны пользователю, за исключением смещения конца. Смещение заменяется на вычисляемый из него размер.
    К тому же, для создания партиции при помощи утилиты gpart нужен только один обязательный параметр - это тип партиции, остальные параметры могут быть вычеслены автоматически.

    Тип партиции может быть задан символьным именем либо специальным номером, в зависимости от используемой схемы. Поддерживаемые символьные имена можно увидеть в мануале gpart(8). При необходимости задать какой-то специфический для схемы тип партиции, он указывается после знака восклицания. Например, для схемы MBR тип партиции FreeBSD представлен номером 165, который можно указать как "-t !165" (не забываем экранировать знак восклицания, если выполняем команду из shell - "\!165"), либо символьным именем "-t freebsd".

    Размер и смещение начала партиции по-умолчанию задаются в блоках, размер которых обычно соответствует размеру физического сектора, т.е. 512 байт. Для того чтобы не вычислять количество блоков, эти параметры можно указывать с суффиксом размера: b, k, m, g, t, p, e - т.е. от байт до экзабайт. Но есть одна особенность реализации - парсер при разборе этих параметров ещё не знает какой размер сектора имеет носитель, поэтому если он отличается от 512 байт, то лучше указывать размер в секторах, иначе размер будет вычислен неправильно.  Размер партиции указывается после опции "-s", а смещение начала - после "-b". При опускании любого из этих параметров они выбираются автоматически: смещение начала - на первый доступный блок, размер - на максимально возможный из доступного свободного пространства.

    Вычисление индекса партиции обычно можно оставить утилите gpart. Только если вам не нужна какая-нибудь особенная конфигурация, например, чтобы система устанавливалась на ad0s3 нужно указать параметр "-i 3".

    Итак, схема создана, создадим партицию:
    # gpart add -s 400m -t freebsd md0
    md0s1 added
    # gpart show md0
    =>     17  1023978  md0  MBR  (500M)
           17   819196    1  freebsd  (400M)
       819213   204782       - free -  (100M)
    
    
    Как видно, партиция была создана с типом "freebsd" и размером в 400 Мбайт, индекс и смещение начала были выбраны автоматически.

    Теперь на созданной партиции можно сделать дополнительную разметку для BSD схемы:
    # gpart create -s bsd md0s1
    md0s1 created
    # gpart show md0
    =>     17  1023978  md0  MBR  (500M)
           17   819196    1  freebsd  (400M)
       819213   204782       - free -  (100M)
    
    # gpart show md0s1
    =>     0  819196  md0s1  BSD  (400M)
           0  819196         - free -  (400M)
    
    
    Теперь добавим партиции, только вместо bsdlabel используем тот же gpart:
    # gpart add -s 100m -t freebsd-ufs md0s1
    md0s1a added
    # gpart add -s 100m -t freebsd-swap md0s1
    md0s1b added
    # gpart add -t freebsd-ufs md0s1
    md0s1d added
    # gpart show md0s1
    =>     0  819196  md0s1  BSD  (400M)
           0  204800      1  freebsd-ufs  (100M)
      204800  204800      2  freebsd-swap  (100M)
      409600  409596      4  freebsd-ufs  (200M)
    
    
    Всё достаточно легко и просто. Для записи загрузочного кода можно использовать всю ту же утилиту. Для этого предназначена команда `gpart bootcode`. Например, аналогом команды `bsdlabel -B` будет:
    # gpart bootcode -b /boot/boot md0s1
    md0s1 has bootcode
    
    Аналогично можно записать загрузочный код для схемы MBR:
    # gpart bootcode -b /boot/mbr md0
    md0 has bootcode
    
    Если же необходимо установить менеджер загрузки, то нужно всего лишь выбрать другой образ загрузочного кода - /boot/boot0. Так же, не стоит забывать, что у схемы MBR есть аттрибут активного раздела, чтобы его установить нужно использовать команду `gpart set`:
    # gpart set -a active -i 1 md0
    md0s1 has active set
    # gpart show md0
    =>     17  1023978  md0  MBR  (500M)
           17   819196    1  freebsd  [active]  (400M)
       819213   204782       - free -  (100M)
    
    
    В принципе, этого набора команд достаточно для работы. Если иметь понятие, что и для чего нужно сделать - то сложностей в использовании вообще не вижу. Даже если такая необходимость возникает нечасто, все команды утилиты gpart имеют описание:
    # gpart 
    usage: gpart add [-b start] [-s size] -t type [-i index] [-l label] [-f flags] geom
           gpart bootcode [-b bootcode] [-p partcode] [-i index] [-f flags] geom
           gpart commit geom
           gpart create -s scheme [-n entries] [-f flags] provider
           gpart delete -i index [-f flags] geom
           gpart destroy [-f flags] geom
           gpart modify -i index [-l label] [-t type] [-f flags] geom
           gpart set -a attrib -i index [-f flags] geom
           gpart show [-lr] [geom ...]
           gpart undo geom
           gpart unset -a attrib -i index [-f flags] geom
           gpart help
           gpart list [name ...]
           gpart status [-s] [name ...]
           gpart load [-v]
           gpart unload [-v]
    

    среда, 10 марта 2010 г.

    Немного о GEOM классе PART

    В 7-ую версию FreeBSD был добавлен GEOM класс PART, который заменил собой несколько классов, реализующих поддержку таблиц партиций, таких как BSD, MBR, PC98.

    Не слишком углубляясь в технические подробности реализации назначение класса PART можно представить в виде некоторого модульного механизма ядра для управления различными таблицами партиций.

    Каждый пользователь FreeBSD в той или иной мере знаком как минимум с двумя типами таблиц партиций - это MBR и BSD, а в последнее время, стал пользоваться популярностью ещё один тип - GPT. В терминологии класса PART эти типы таблиц представлены "схемами". А модульность реализации позволяет включать поддержку различных схем в виде отдельных модулей ядра. Для этого реализация класса включает в себя инфраструктуру с базовыми действиями схожими для различных типов таблиц партиций. А реализация особенностей каждой из "схем" содержится в отдельных модулях, которые благодаря интерфейсу kobj(9) подключаются в базовую инфраструктуру класса.

    Когда подсистема GEOM инициализирует класс PART, базовая инфраструктура класса выполняет "пробу" для каждой зарегистрированной схемы. Т.е. класс выполняет поиск метаданных схемы на носителях. Так, например, схема MBR реализует поддержку таблицы основных разделов, содержащейся в главной загрузочной записи MBR. Если "схема" обнаружит свои метаданные на носителе, она создаёт "провайдеры", через которые осуществляется доступ к партициям. Для пользователя партиции этой схемы выглядят как <имя_провайдера>s%d, где %d - номер индекса партиции в таблице. Т.е. привычные всем имена типа ad0s1 можно разделить на имя провайдера носителя ad0 и имя партиции s1, где 1 - это номер партиции в таблице.

    Схема BSD реализует поддержку bsd-разделов. Это те, которые раньше создавались утилитой bsdlabel (или ещё раньше disklabel). Имена партиций этой схемы именуются латинскими буквами, например, ad0s1a. Здесь ad0 - имя провайдера носителя, s1 - имя провайдера схемы MBR, т.е. на диске ad0 была создана схема MBR и уже на первой партиции этой схемы была создана схема BSD c партицией, определяемой именем "a".

    Схема GPT реализует поддержку GUID таблицы разделов. Имена партиций при использовании этой схемы формируются в зависимости от типа. Тип задаётся при создании партиции, о нём я расскажу чуть позже. Но в общем, имя может быть либо "p%d" либо "s%d", где %d - номер индекса в таблице. Обычно это "p", имя "s" используется только для типа партиции "freebsd", обозначающего, что на данной партиции будет использоваться BSD схема.

    Ещё одна знакомая схема - EBR. Она используется для разметки партиций внутри расширенных разделов. И осталось ещё три неупомянутых схемы, поддержка которых реализована на сегодняшний день в классе PART: APM используемая в Apple Mac, PC98 используемая в NEC PC-9801, и VTOC8 используемая  на Sun'овских UltraSPARC.

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

    среда, 24 февраля 2010 г.

    JA2 во FreeBSD

    Много лет назад, когда я ещё жил в общежитии университета, у нас ещё не было ни локальной сети, ни Интернета. Тогда мы с соседями убивали время за играми... Мне особенно нравились различные RPG и пошаговые стратегии. Одной из таких игр была Jagged Alliance 2. Довольно неплохая игра для того времени.

    Я обнаружил её в портах FreeBSD, и что интересно, сейчас она уже распространяется с открытым исходным кодом. Захотелось посмотреть, как она будет выглядеть сейчас. Установка игры из порта games/jaggedalliance2 не вызвала никаких сложностей. Проблему вызвало только отсутствие файлов ресурсов игры - карты, озвучка, ролики и т.п. Они в состав порта не входили. Нужны были оригинальные диски с игрой. Диски когда-то у меня были (я покупал 1С-овскую "Gold" версию), но вот найти их мне не удалось, смутно припоминалось, что возможно я подарил их племяннику жены...

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

    После этого я создал на своём ZFS пуле новый датасет установиви свойство casesensitivity в значение insensitive для того чтобы не возникло проблем с регистром символов в именах файлов и каталогов:
    > sudo zfs create -o casesensitivity=insensitive -o mountpoint=/usr/local/share/ja2 zroot/data
    
    И скопировал с компьютера жены каталог Data в созданный датасет. После этого запустил из коммандной строки ja2, игра тут же закрылась и в домашнем каталоге создался подкаталог ~/.ja2 в котором я отредактировал файл ~/.ja2/ja2.ini и указал там путь к файлам ресурсов:
    #Tells ja2-stracciatella where the binary datafiles are located
    data_dir = /usr/local/share/ja2
    
    Но игра и в этот раз не запустилась, ругнувшись на файлы ресурсов... Посмотрев в содержимое архива с исходниками я обнаружил, что там в параметрах make упоминается RUSSIAN_GOLD, которого нет среди опций порта. А ведь ISO'шки с игрой у меня были именно версии Gold. Я быстренько поправил Makefile порта добавив ещё одну опцию RUSSIAN_GOLD и пересобрал игру. Запустив новый бинарник игра запустилась в окошечке и вполне неплохо выглядела. По Alt+Enter она может разворачиваться на полный экран и обратно.


    После этого я решил оформить патч и отправить PR для внесения изменений в порт. Патч получился таким:
    --- jaggedalliance2/Makefile     2009-06-14 12:41:39.000000000 +0400
    +++ jaggedalliance2/Makefile     2010-02-01 08:27:31.825416359 +0300
    @@ -7,6 +7,7 @@
     PORTNAME=       ja2
     PORTVERSION=    0.12
    +PORTREVISION=   1
     CATEGORIES=     games
     MASTER_SITES=   http://deponie.yamagi.org/freebsd/distfiles/ \
                     http://tron.homeunix.org/ja2/
    @@ -34,7 +35,8 @@ OPTIONS=       DUTCH   "Compile for Dutch versio
                     GERMAN  "Compile for German version (choose ONE)"       Off \
                     ITALIAN "Compile for Italian version (choose ONE)"      Off \
                     POLISH  "Compile for Polish version (choose ONE)"       Off \
    -                RUSSIAN "Compile for Russian version (choose ONE)"      Off
    +                RUSSIAN "Compile for Russian version (choose ONE)"      Off \
    +                RUSSIAN_GOLD "Compile for Russian Gold version (choose ONE)"    Off
     .include 
    @@ -52,6 +54,8 @@ MAKE_ARGS+=    LNG=ITALIAN
     MAKE_ARGS+=     LNG=POLISH
     .elifdef WITH_RUSSIAN
     MAKE_ARGS+=     LNG=RUSSIAN
    +.elifdef WITH_RUSSIAN_GOLD
    +MAKE_ARGS+=     LNG=RUSSIAN_GOLD
     .else
     IGNORE= no language has been chosen. Please choose the appropriate language for your game CD
     .endif
    
    Вроде все изменения тривиальные и объяснять нечего... Сам PR можно посмотреть здесь. Через три недели патч был одобрен и изменения были внесены. Такая большая задержка получилась из-за ожидания ответа мэйнтейнера, который так и не ответил...

    суббота, 20 февраля 2010 г.

    Переопределение настроек DHCP на клиенте

    Когда появляется задача использовать в локальной сети какие-то дополнительные настройки помимо тех, которые предлагает использовать DHCP сервер, есть несколько способов сделать это.
    На мой взгляд наиболее правильный и простой способ (ну или, по крайней мере, один из них) - использовать возможности dhclient.

    Вот так, к примеру, выглядит /etc/dhclient.conf на нетбуке, с которого я пишу это сообщение:
    interface "wlan0" {
     send host-name "btr-nb.properlan.net";
     prepend domain-name-servers 10.0.0.3;
     append classless-routes 29,192,168,212,0,10,0,12,12;
     request subnet-mask, broadcast-address,
      classless-routes, routers, domain-name,
      domain-name-servers, host-name;
    }
    
    Здесь есть две дополнительные команды prepend и append, которые позволяют изменить настройки, выдаваемые DHCP сервером. В моём случае это адрес DNS сервера и дополнительный статический маршрут.

    В общем-то, вроде ничего сложного, об этих командах можно прочитать в руководстве dhclient.conf(5). Команда append добавляет указанное значение к выбранной опции, а prepend добавляет к указанному значению значение, выдаваемое сервером. Есть и ещё одна команда - supersede, она переопределяет значение указанной опции. Сложность может вызвать разве что выбор формата, в котором записать значение опции. Даже в приведённом мной примере не для каждого покажется очевидным, что запись "29,192,168,212,0,10,0,12,12" означает "192.168.212.0/29 10.0.12.12". Мне в какой-то степени это проще, ведь я добавлял поддержку этой опции в dhclient. :)

    Формат каждой опции можно узнать в исходном коде dhclient'а. К счатью, это вполне по силам любому, знание языков программирования для этого не нужно. Для этого достаточно просмотреть файл /usr/src/sbin/dhclient/tables.c:
    /*
     * DHCP Option names, formats and codes, from RFC1533.
     *
     * Format codes:
     *
     * e - end of data
     * I - IP address
     * l - 32-bit signed integer
     * L - 32-bit unsigned integer
     * s - 16-bit signed integer
     * S - 16-bit unsigned integer
     * b - 8-bit signed integer
     * B - 8-bit unsigned integer
     * t - ASCII text
     * f - flag (true or false)
     * A - array of whatever precedes (e.g., IA means array of IP addresses)
     */
    
    struct universe dhcp_universe;
    struct option dhcp_options[256] = {
            { "pad", "",                                    &dhcp_universe, 0 },
            { "subnet-mask", "I",                           &dhcp_universe, 1 },
            { "time-offset", "l",                           &dhcp_universe, 2 },
            { "routers", "IA",                              &dhcp_universe, 3 },
    
    В комментарии перечислены буквенные коды возможных форматов опций, например "I" - IP-адрес, а "IA" - список IP адресов. Так, для опции domain-name-servers указан код формата "IA". Это значит, что в dhclient.conf эту опцию можно задать списком IP адресов, перечисленных через запятую. Для опции classless-routes указан код формата "BA" - список беззнаковых чисел. Но для того чтобы знать, какие числа указывать, нужно изучить RFC 3442. Если в кратце - сначала идёт маска сети, затем значащие цифры номера сети и потом адрес шлюза. Но длинна номера сети может различаться в зависимости от маски, поэтому лучше всётаки изучить RFC.
    Ну и в качестве дополнительного бонуса, в файле tables.c можно найти все поддерживаемые опции.

    четверг, 18 февраля 2010 г.

    Баг с отображением активных наборов правил в ipfw

    Общаясь в IRC RusNet узнал, что в 8-ой версии FreeBSD странно работает команда ipfw set show, которая должна показывать, номера активных и заблокированных наборов правил. А именно - не смотря на явное отключение наборов, вывод команды не изменяется. Т.е. другими словами, команда всегда показывает одно и то же - все наборы активны.

    После небольшого исследования исходного кода подтвердилось наличие проблемы. Немного порывшись в истории коммитов нашёлся и источник - r200855. Проблема заключается в том, что код операции IP_FW_GET был переписан (в рамках подготовки к вливанию новой версии ipfw3), но не достаточно оттестирован, как это обычно и случается :).

    О проблеме сразу же сообщил виновнику, но воз и ныне там, так что нужно будет заняться исправлением, как только времени побольше появится...

    суббота, 13 февраля 2010 г.

    Потенциально опасное использование ldd(1)

    Прочитал тут на RSDN интересную тему об особенностях работы ldd(1). Там же, в одном из сообщений обнаружил ссылку на статью "ldd arbitrary code execution". Если в кратце, то опасность заключается в возможности исполнения кода программы, путь к которой передаётся в параметрах ldd(1). Решил проверить во FreeBSD, так ли всё опасно на самом деле? Оказалось - да.

    Действительно, если скомпилировать программу с указанием специального загрузчика для неё, то получаем не совсем то, что хочется увидеть при исследовании её зависимостей. Так как на самом деле зависимости показывает не сама утилита ldd(1), а загрузчик, который эти зависимости ищет и загружает при нормальном запуске программы. ldd(1) всего лишь выставляет переменную окружения, при обнаружении которой ld-elf.so.1 выводит список зависимостей и завершает выполнение программы. Как написано в статьях по ранее приведённым ссылкам, в linux ldd вообще представляет собой shell скрипт.

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

    Там же, в статье, есть пример сценария по которому, можно "уговорить" системного администратора запустить эту программу. И если он будет так неосторожен, что сделает это с правами суперпользователя, то...

    Я не стал тестировать с uClibc, просто скопировал себе в домашний каталог исходники rtld. Слегка подправив Makefile и код rtld.c, (по аналогии с тем, как это сделано в статье) и взяв пример программы для теста, вот что я получил:

    > ./test
    Nothing.
    > ldd ./test
    ./test:
    All your box are belong to me.

    Из этого я для себя сделал такой вывод: прежде чем что-то проверять утилитой ldd(1), сначала надо воспользоваться утилитой readelf(1) или objdump(1). Например так:

    > readelf -l ./test
    
    Elf file type is EXEC (Executable file)
    Entry point 0x80483a0
    There are 6 program headers, starting at offset 52
    
    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4
      INTERP         0x0000f4 0x080480f4 0x080480f4 0x0001e 0x0001e R   0x1
          [Requesting program interpreter: /home/butcher/bin/ld-elf.so.1]
      LOAD           0x000000 0x08048000 0x08048000 0x005c3 0x005c3 R E 0x1000
      LOAD           0x0005c4 0x080495c4 0x080495c4 0x000f4 0x000fc RW  0x1000
      DYNAMIC        0x0005d4 0x080495d4 0x080495d4 0x000b0 0x000b0 RW  0x4
      NOTE           0x000114 0x08048114 0x08048114 0x00018 0x00018 R   0x4
    
    > objdump -sj .interp ./test
    
    ./test:     file format elf32-i386-freebsd
    
    Contents of section .interp:
     80480f4 2f686f6d 652f6275 74636865 722f6269  /home/butcher/bi
     8048104 6e2f6c64 2d656c66 2e736f2e 3100      n/ld-elf.so.1
    Обращать внимение нужно на выделенные строки, если они отличаются от /libexec/ld-elf.so.1, то лучше не испытывать судьбу и не использовать ldd(1) с этой программой.
    А так же вывод №2: никогда не запускать утилиту ldd(1) от суперпользователя.

    суббота, 6 февраля 2010 г.

    Начало.

    Решил создать блог. Пока ещё не понял для чего и как часто я буду им пользоваться. Но иногда бывают моменты, когда хочется что-то записать для себя или поделиться с другими. Наверно, для этого я и буду его использовать.. Время покажет.