Разработка схемы фильтра

На OS X драйвер схемы фильтра обеспечивает механизм фильтрации между универсальными запросами I/O и содержанием на носители. Схема фильтра носителей соответствует на IOMedia объект, представляющий довольное настоящее в разделе и, публикует в Реестре I/O другого IOMedia объект, представляющий неотфильтрованное содержание. Поскольку драйверы схемы фильтра являются и потребителями и производителями IOMedia объекты, может быть произвольное число схем фильтра в штабеле драйвера массового хранения.

Для создания собственного драйвера схемы фильтра Вы разделяете на подклассы IOStorage и реализуйте свою функциональность фильтрации в read и write методы. Другие методы Вы реализуете, такой как init, start, и free, создайте и инициализируйте новое IOMedia объект, присоедините его к Реестру I/O и выпустите его.

Как описано в Схемах Фильтра, драйвер схемы фильтра не должен производить IOCDMedia или IODVDMedia объект, потому что эти объекты имеют требования провайдера, определенные для CD и носителей DVD, которые могут быть встречены только IOCDBlockStorageDriver или IODVDBlockStorageDriver, соответственно.

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

Демонстрационная схема фильтра, описанная в этой главе, включает код, позволяющий Вам устанавливать схему фильтра на разделе начальной загрузки. Если Вы не должны делать этого, можно пропустить части кода, реализующие это.

Пример кода в этой главе из проекта XCode, создающего драйвер схемы фильтра. Для загрузки завершенного проекта (который включает отладку и информацию об установке) посмотрите SampleFilterScheme в Ссылочной Библиотеке ADC. Обратите внимание на то, что проект SampleFilterScheme определяет две различных цели, одна из которых позволяет Вам устанавливать схему фильтра на разделе начальной загрузки. Обязательно считайте комментарии в файлах проекта перед решением который цель создать.

Для получения дополнительной информации о том, как разработать расширения ядра в целом и драйверы Набора I/O в частности посмотрите, что Расширение ядра Программирует Темы и Руководство по проектированию Драйвера устройства IOKit.

Отредактируйте свой водительский список свойств

Каждый драйвер имеет Info.plist файл, содержащий информацию о драйвере и в чем это нуждается, включая его лица. Драйвер схемы фильтра соответствует на содержании в разделе, а не на устройстве, таким образом, его индивидуальность содержит информацию, идентифицирующую определенное содержание. Как описано в Соответствии Драйвера Схемы фильтра, драйвер схемы фильтра использует Content Hint свойство для соответствия на довольной подсказке представляет дисковую утилиту в виде строки места в разделе. Для проверки драйвер загружается для содержания, Вы добавляете Content Hint свойство и связанная довольная подсказка оценивают ее словарю индивидуальности. Можно также добавить другие свойства, идентифицирующие характеристики носителей, такие как ejectability и writability.

Для поэтапных инструкций, описывающих, как создать словарь индивидуальности для драйвера и добавить дочерние элементы к нему, посмотрите Привет учебное руководство по Набору I/O в Темах Программирования Расширения ядра.

Пример кода использует следующие пять ключей свойства:

Создайте пять новых дочерних элементов MyFilterScheme словарь индивидуальности, один для каждого из этих пяти свойств Вы будете добавлять. Таблица 7-1 показывает свойства, вместе с их классами и значениями. Для тестирования примера кода с устройством замените значения такой как MySoftwareCompany_MyContentHint с фактическими значениями для Вашего содержания.

  Свойства Table 7-1 Personality для MyFilterScheme

Свойство

Класс

Значение

CFBundleIdentifier

Строка

com.MySoftwareCompany.driver.MyFilterScheme

IOClass

Строка

com_MySoftwareCompany_driver_MyFilterScheme

IOProviderClass

Строка

IOMedia

Content Hint

Строка

MySoftwareCompany_MyContent

IOMatchCategory

Строка

IOStorage

Драйвер объявляет свои зависимости от других загружаемых расширений ядра и компонентов в ядре в OSBundleLibraries словарь. Каждая зависимость имеет строковое значение, объявляющее самую раннюю версию зависимости, с которой драйвер совместим.

Демонстрационный драйвер зависит от одного загружаемого расширения от Семейства систем хранения и трех компонентов ядра. Добавить эти зависимости к OSBundleLibraries словарь, Вы создаете новый дочерний элемент для каждой зависимости. Таблица 7-2 показывает зависимости, которые Вы добавляете для демонстрационного драйвера.

Табличные 7-2  зависимости для MyFilterScheme

Свойство

Класс

Значение

com.apple.iokit.IOStorageFamily

Строка

1.1

com.apple.kernel.iokit

Строка

1.1

com.apple.kernel.libkern

Строка

1.1

com.apple.kernel.mach

Строка

1.1

Наконец, чтобы позволить этой схеме фильтра отфильтровать загрузочный том, необходимо гарантировать, что это загружается во время начальной загрузки так, чтобы это могло быть установлено поверх загрузочного тома. Чтобы сделать это, Вы добавляете OSBundleRequired свойство к верхнему уровню Вашего Info.plist файл и дает ему строковое значение Local-Root. Если Вы не должны фильтровать раздел начальной загрузки, не добавляйте эту пару значения свойства к Вашему Info.plist файл.

Создание схемы фильтра

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

Отредактируйте заголовочный файл

Заголовочный файл для демонстрационного драйвера схемы фильтра включает десять объявлений метода и два внешних заголовочных файла. В интересах краткости пример кода включает только сжатую версию стандартных комментариев, сопровождающих каждое объявление метода. Можно найти полностью прокомментированные версии этих объявлений метода в IOMedia.h и IOStorage.h (оба из которых находятся в /System/Library/Frameworks/IOKit.framework/Headers/storage).

Отредактируйте MyFilterScheme.h файл для соответствия кода в Перечислении 7-1.

Перечисление 7-1  заголовочный файл MyFilterScheme

#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOStorage.h>
 
class com_MySoftwareCompany_driver_MyFilterScheme : public IOStorage {
 
    OSDeclareDefaultStructors(com_MySoftwareCompany_driver_MyFilterScheme)
 
protected:
 
    IOMedia*    _childMedia;
 
    // Free all of this object's outstanding resources.
 
    virtual void free(void);
 
    // The handleOpen method grants or denies permission to access this
    // object to an interested client.
 
    virtual bool handleOpen(IOService*   client,
                            IOOptionBits options,
                            void*        access);
 
    // The handleIsOpen method determines whether the specified client,
    // or any client if none is specified, presently has an open
    // on this object.
 
    virtual bool handleIsOpen(const IOService* client) const;
 
     // The handleClose method closes the client's access to this object.
 
    virtual void handleClose(IOService* client, IOOptionBits options);
 
    // Attach the passed-in media object to the device tree plane.
    // This is necessary if you want to stack this filter scheme on top
    // of the boot volume. You do not need to include this method if you
    // do not need to filter the boot volume.
 
    virtual bool attachMediaObjectToDeviceTree(IOMedia* media);
 
    // Detach the passed-in media object from the device tree plane.
    // This is necessary if you want to stack this filter scheme on top
    // of the boot volume. You do not need to include this method if you
    // do not need to filter the boot volume.
 
    virtual void detachMediaObjectFromDeviceTree(IOMedia* media);
 
public:
 
     // Initialize this object's minimal state.
 
    virtual bool init(OSDictionary* properties = 0);
 
     // Publish the new IOMedia object that represents the filtered content.
 
    virtual bool start(IOService* provider);
 
    // Clean up after the published media object before terminating.
 
    virtual void stop(IOService* provider);
 
    // Read data from the storage object at the specified byte offset into
    // the specified buffer, asynchronously.   When the read completes,
    // the caller will be notified via the specified completion action.
    // The buffer will be retained for the duration of the read.
 
    virtual void read(IOService*           client,
                      UInt64               byteStart,
                      IOMemoryDescriptor*  buffer,
                      IOStorageCompletion  completion);
 
    // Write data into the storage object at the specified byte offset from
    // the specified buffer, asynchronously.   When the write completes, the
    // caller will be notified via the specified completion action.
    // The buffer will be retained for the duration of the write.
 
    virtual void write(IOService*           client,
                       UInt64               byteStart,
                       IOMemoryDescriptor*  buffer,
                       IOStorageCompletion  completion);
 
    // Flush the cached data in the storage object, if any, synchronously.
    // The I/O Kit provides for data caches at the driver level, but
    // Apple discourages this because it is rarely needed. In the majority
    // of cases, a pass-through implementation is sufficient.
 
    virtual IOReturn synchronizeCache(IOService* client);
 
    // Obtain this object's provider.  This method returns IOMedia,
    // rather than the less-specific OSObject, as a convenience.
 
    virtual IOMedia* getProvider() const;
};

Отредактируйте файл C++

Файл C++ обеспечивает код для реализации выбранных методов. Демонстрационный водительский файл C++ содержит все элементы, требуемые для разделенного на подклассы драйвера схемы фильтра даже при том, что он не выполняет фильтрации. Для реализации схемы фильтрации добавьте код к read и write методы.

Пример кода во включает два метода, которые необходимо реализовать, если Вы хотите свою схему фильтра отфильтровать загрузочный том:

  • attachMediaObjectToDeviceTree

  • detachMediaObjectFromDeviceTree

attachMediaObjectToDeviceTree метод, призван Ваш start подпрограмма после вызова к стандарту attach метод, присоединяющий новые носители к Вашей схеме фильтра. Этот метод отсоединяет Ваш родительский объект схемы фильтра от Открыть дерева устройств Firmware и присоединяет дочерний объект схемы фильтра к Открыть дереву устройств Firmware в его месте. Это должно быть сделано перед публикацией нового мультимедийного объекта в Реестре I/O с помощью registerService метод. Второй метод, detachMediaObjectFromDeviceTree метод, выполняет работу наоборот в Вашем stop подпрограмма.

Для понимания, почему эта реконструкция узлов дерева устройств необходима она помогает знать больше о процессе начальной загрузки OS X. Когда Вы включаете свой компьютер, Открываете, Firmware определяет который объем загрузиться от. Это тогда загружает вторичного футболиста (названный BootX) от того объема и переходов к нему. BootX загружает и выполняет ядро, передавая ему параметры, от которых это наследовалось, Открывают Firmware, включая дерево устройств.

После того, как ядро подходит, оно должно смонтировать корневой объем. К этому времени Откройте, Firmware больше не работает, таким образом, ядро решает, что корневой объем путем интерпретации параметра Открывает, Firmware передал ему ранее. Параметр содержит корневое свойство пути /chosen узел в открыть дереве устройств Firmware. Ядро ищет Реестр I/O узел, чей Открывают, путь Firmware соответствует корневой путь. Ядро использует этот узел в качестве корневого устройства.

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

Отредактируйте MyFilterScheme.cpp файл для соответствия кода в Перечислении 7-2.

Перечисление 7-2  файл C++ MyFilterScheme

#include <IOKit/assert.h>   // For debugging purposes.
#include <IOKit/IOLib.h>
#include "MyFilterScheme.h"
 
// This definition allows you to use the more convenient "super" in
// place of "IOStorage", where appropriate.
#define super IOStorage
 
// This macro must appear before you define any of your class's methods.
// Note that you must use the literal name of the superclass here,
// not "super" as defined above.
OSDefineMetaClassAndStructors(com_MySoftwareCompany_driver_MyFilterScheme,
                                IOStorage)
 
// Define the methods to implement.
bool com_MySoftwareCompany_driver_MyFilterScheme::init(OSDictionary*
                                            properties = 0)
{
    //
    // Initialize this object's minimal state.
    //
 
    // Call superclass's init.
 
    if (super::init(properties) == false)  return false;
 
    // Initialize state.
 
    _childMedia = 0;
 
    return true;
}
 
 
void com_MySoftwareCompany_driver_MyFilterScheme::free(void)
{
    //
    // Free all of this object's outstanding resources.
    //
 
    if ( _childMedia )  _childMedia->release();
 
    // Call superclass's free.
    super::free();
}
 
 
IOMedia* com_MySoftwareCompany_driver_MyFilterScheme::getProvider(void)
    const
{
 
    return (IOMedia*) IOService::getProvider();
}
 
 
bool com_MySoftwareCompany_driver_MyFilterScheme::start(IOService* provider)
{
    //
    // Publish the new media object that represents the filtered content.
    //
 
    IOMedia* media = OSDynamicCast (IOMedia, provider);
 
    // State assumptions.
 
    assert(media);
 
    // Call superclass's start.
 
    if ( super::start(provider) == false )
        return false;
 
    // Attach and register the new media object.
 
    IOMedia* childMedia = new IOMedia;
 
    if ( childMedia )
    {
        if ( childMedia->init(
                /* base               */ 0,
                /* size               */ media->getSize(),
                /* preferredBlockSize */ media->getPreferredBlockSize(),
                /* isEjectable        */ media->isEjectable(),
                /* isWhole            */ false,
                /* isWritable         */ media->isWritable(),
                /* contentHint        */ "Apple_HFS" ) )
        {
            // Set a name for this partition.
 
            UInt32 partitionID = 1;
 
            char name[24];
            sprintf(name, "MySoftwareCompany_Filtered %ld", partitionID);
            childMedia->setName(name);
 
            // Set a location value (partition number) for this partition.
 
            char location[12];
            sprintf(location, "%ld", partitionID);
            childMedia->setLocation(location);
 
            // Attach the new media to this driver
 
            _childMedia = childMedia;
 
            childMedia->attach(this);
 
            // Move parent node to child node.
            (void) attachMediaObjectToDeviceTree(childMedia);
 
            // Publish the new media object.
            childMedia->registerService();
 
            return true;
        }
        else
        {
            childMedia->release();
            childMedia = 0;
        }
    }
 
    return false;
}
 
void com_MySoftwareCompany_driver_MyFilterScheme::stop(IOService* provider)
{
    // Clean up after the media object before terminating.
 
    // State assumptions.
    assert(_childMedia);
 
    // Detach the media object previously attached in start().
    if (_childMedia)
        detachMediaObjectFromDeviceTree(_childMedia);
 
    super::stop(provider);
}
 
bool com_MySoftwareCompany_driver_MyFilterScheme::handleOpen(IOService*
                                            client,
                                            IOOptionBits options,
                                            void* argument)
{
    return getProvider()->open(this, options, (IOStorageAccess) argument);
}
 
 
bool com_MySoftwareCompany_driver_MyFilterScheme::handleIsOpen(const
                                            IOService* client) const
{
    return getProvider()->isOpen(this);
}
 
 
void com_MySoftwareCompany_driver_MyFilterScheme::handleClose(IOService*
                                        client, IOOptionBits options)
{
    getProvider()->close(this, options);
}
 
bool com_MySoftwareCompany_driver_MyFilterScheme::attachMediaObjectToDeviceTree(
                                            IOMedia* media)
{
    //
    // Attach the given media object to the device tree plane.
    //
 
    IORegistryEntry* child;
 
    if ((child = getParentEntry(gIOServicePlane))) {
 
        IORegistryEntry* parent;
 
        if ((parent = child->getParentEntry(gIODTPlane))) {
 
            const char* location = child->getLocation(gIODTPlane);
            const char* name     = child->getName(gIODTPlane);
 
            if (media->attachToParent(parent, gIODTPlane)) {
                media->setLocation(location, gIODTPlane);
                media->setName(name, gIODTPlane);
 
                child->detachFromParent(parent, gIODTPlane);
 
                return true;
            }
        }
    }
 
    return false;
}
 
void com_MySoftwareCompany_driver_MyFilterScheme::detachMediaObjectFromDeviceTree
                                            (IOMedia* media)
{
    //
    // Detach the given media object from the device tree plane.
    //
 
    IORegistryEntry* child;
 
    if ((child = getParentEntry(gIOServicePlane))) {
 
        IORegistryEntry * parent;
 
        if ((parent = media->getParentEntry(gIODTPlane))) {
 
            const char* location = media->getLocation(gIODTPlane);
            const char* name     = media->getName(gIODTPlane);
 
            if (child->attachToParent(parent, gIODTPlane)) {
                child->setLocation(location, gIODTPlane);
                child->setName(name, gIODTPlane);
            }
 
            media->detachFromParent(parent, gIODTPlane);
        }
    }
}
 
void com_MySoftwareCompany_driver_MyFilterScheme::read(IOService* __attribute__ ((unused)) client,
                                        UInt64 byteStart,
                                        IOMemoryDescriptor* buffer,
                                        IOStorageCompletion completion)
{
    // Add filtering code here.
       getProvider()->read(this, byteStart, buffer, completion);
}
 
 
void com_MySoftwareCompany_driver_MyFilterScheme::write(IOService* __attribute__ ((unused)) client,
                                        UInt64 byteStart,
                                        IOMemoryDescriptor* buffer,
                                        IOStorageCompletion completion)
{
   // Add filtering code here.
    getProvider()->write(this, byteStart, buffer, completion);
}
 
 
IOReturn com_MySoftwareCompany_driver_MyFilterScheme::synchronizeCache(
                                                    IOService* client)
{
    return getProvider()->synchronizeCache(this);
}

Тестирование схемы фильтра

Для тестирования демонстрационного драйвера схемы фильтра необходимо сначала создать образ диска для него для соответствия на. Вы делаете это использование инструментов командной строки hdiutil, который создает и управляет образами дисков и newfs_hfs, который создает файловую систему на образе диска. Для полной документации относительно этих команд посмотрите man страницы.

Для создания образа диска откройте окно в Терминальном приложении (расположенный в /Applications/Utilities/Terminal) и введите следующие команды.

$ hdiutil create -megabytes 5 -partitionType MySoftwareCompany_MyContent
                            ~/MySoftwareCompany_MyContent_Example.dmg

Затем Вы присоединяете образ диска, не монтируя его, потому что это еще не содержит систему правильного файла. В версии 10.2 OS X и позже, используйте эту команду:

$ hdiutil attach -nomount ~/MySoftwareCompany_MyContent_Example.dmg

В версии OS X до 10,2, используйте эту команду ( -nomount опция была добавлена в версии 10.2 OS X):

$ hdiutil attach ~/MySoftwareCompany_MyContent_Example.dmg

hdiutil attach команда выводит на экран имя специального устройства, связанное с каждым разделом на образе диска:

/dev/disk1          Apple_partition_scheme
/dev/disk1s1        Apple_partition_map
/dev/disk1s2        MySoftwareCompany_MyContent

Затем используйте newfs_hfs команда для создания файловой системы на образе диска. Поскольку необходимо всегда использовать сырые данные, некэшируемый диск, Вы добавляете r к дисковому узлу, представляющему раздел с Вашим содержанием.

$ newfs_hfs -v "My_Volume_Name" /dev/rdisk1s2

-v опция позволяет Вам указывать имя тома.

После создания образа диска и его файловой системы можно принять суперпользователь (или root) полномочия и использование kextload команда для загрузки демонстрационного драйвера схемы фильтра. Поочередно, если root учетной записи принадлежит драйвер схемы фильтра, можно скопировать его в /System/Library/Extensions, перезагрузка и драйвер загрузятся автоматически.

Использовать kextload команда, введите следующую строку в Окне терминала:

$ kextload -v MyFilterScheme.kext

-v опция делает kextload предоставьте больше многословной информации.

После успешной загрузки драйвера можно использовать Дисковую копию для открытия, образ диска (дважды щелкните по образу диска в Средстве поиска).