Работа с файлами устройств для устройств хранения

В этой главе описываются, как разработать приложение, использующее Набор I/O и функции POSIX, чтобы определить местоположение устройства хранения CDROM на OS X и открыть ее для чтения.

Фрагменты кода в этой главе основываются на примере приложения CDROMSample, доступный полностью в Примере кода> Аппаратные средства и Драйверы> Хранение.

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

Доступ устройства хранения в основанном на Intel Macintosh

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

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

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

Если Вы решили, что свопинг байта требуется в Вашем приложении, можно реализовать его одним из двух способов:

Для предотвращения беспорядка, лучше выбрать только одну из этих двух схем и быть непротиворечивым в его реализации всюду по приложению. Какой бы ни Вы выбираете, однако, убедиться использовать условные подкачивающие байт макросы, определенные в libkern/OSByteOrder.h (даже при том, что этот заголовочный файл находится в платформе Ядра, ее макросы доступны приложениям). При использовании их макросы компилятор оптимизирует код, таким образом, подпрограммы выполняются, только если они необходимы для архитектуры, в которой работает приложение.

Доступ к устройству хранения CDROM

Для передачи с устройством хранения (таким как устройство CDROM) из приложения Mac Вы используете функции Набора I/O, чтобы найти устройство и получить путь к его файлу устройств. Можно тогда использовать функции POSIX для выполнения таких операций как открытие и закрытие устройства и чтение от него.

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

Пример кода, показанный в этой главе, является от XCode “проектом” Инструмента CoreFoundation. Проект создает инструмент, не имеющий никакого пользовательского интерфейса и отправляющий его вывод в консоль. Можно просмотреть вывод или путем выполнения инструмента в XCode или путем выполнения утилиты Console, в которой можно найти /Applications/Utilities/Console, прежде, чем запустить инструмент.

При использовании версии OS X до v10.1 этот инструмент должен быть выполнен с полномочиями пользователя root, потому что /dev/rdisk* узлы принадлежат корню в тех версиях. В OS X v10.1 и позже, /dev/*disk* узлы для съемных носителей принадлежат, в настоящее время входил в систему пользователь (узлы для несъемных носителей все еще принадлежат корню). Если необходимо, можно использовать sudo(8) команда для запуска инструмента с полномочиями пользователя root как показано ниже (Вас попросят предоставить Ваш admin пароль):

sudo open /YourDirectoryPath/CDROMSample.app

Включая заголовочные файлы и установку основной функции

Перечисление 1-1 показывает заголовочные файлы, которые необходимо будет включать в основной файл для примера кода в этой главе. (Некоторые из этих заголовков включают других; более короткий список возможен.) За исключением CoreFoundation.h, эти заголовки обычно являются частью IOKit.framework или System.framework.

  Заголовочные файлы перечисления 1-1 для включения для примера кода устройства хранения

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <paths.h>
#include <sys/param.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOCDMedia.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOCDTypes.h>
#include <IOKit/storage/IOMediaBSDClient.h>
#include <CoreFoundation/CoreFoundation.h>

Перечисление 1-2 показывает a main функция для нахождения устройства CDROM с Набором I/O и доступом к нему с функциями POSIX. main функция выполняет свою работу путем вызывания следующих функций, показанных в других разделах:

Тип kern_return_t определяется в std_types.h.

Константа KERN_SUCCESS определяется в kern_return.h.

Перечисление 1-2  , Находящее устройство CDROM и читающее сектор

int main( void )
{
    kern_return_t kernResult;
    io_iterator_t mediaIterator;
    char deviceFilePath[ MAXPATHLEN ];
 
    kernResult = MyFindEjectableCDMedia( &mediaIterator );
    if ( kernResult != KERN_SUCCESS )
        return 0;
 
    kernResult = MyGetDeviceFilePath( mediaIterator, deviceFilePath,
                    sizeof( deviceFilePath ) );
    if ( kernResult != KERN_SUCCESS )
        return 0;
 
    // Now open the device we found, read a sector, and close the device.
    if ( deviceFilePath[ 0 ] != '\0' )
    {
        int fileDescriptor;
 
        fileDescriptor = MyOpenDrive( deviceFilePath );
        if (fileDescriptor != -1 )
        {
            if ( MyReadSector( fileDescriptor ) )
                printf( "Sector read successfully.\n" );
            else
                printf( "Could not read sector.\n" );
 
            MyCloseDrive( fileDescriptor );
            printf( "Device closed.\n" );
        }
    }
    else
        printf( "No ejectable CD media found.\n" );
 
    // Release the iterator.
    IOObjectRelease( mediaIterator );
 
    return 0;
}

main функционируйте выпускает итератор, возвращенный MyFindEjectableCDMedia функция, также выпускающая объекты итератора.

Нахождение всех выбрасываемых носителей CD

MyFindEjectableCDMedia функция, показанная в Перечислении 1-3, устанавливает соединение с Набором I/O путем вызова IOMasterPort функция, возвращающая порт Маха. Это тогда создает соответствующий словарь путем вызова IOServiceMatching, передача константы kIOCDMediaClass (определенный в IOCDMedia.h). Это устанавливает словарь, соответствующий все устройства классу провайдера IOCDMediaClass; все устройства хранения данных CD в Реестре I/O являются экземплярами этого класса или подкласса.

Соответствующий словарь является словарем пар ключ/значение, описывающих свойства устройства Набора I/O или другой службы. Каждый объект IOMedia в Реестре I/O имеет свойство с ключом kIOMediaEjectableKey и значение, которое является true если носители являются действительно выбрасываемыми. В этой выборке мы интересуемся только выбрасываемыми носителями, таким образом, MyFindEjectableCDMedia функция совершенствовала соответствующий словарь путем вызова CFDictionarySetValue добавить ключ kIOMediaEjectableKey и значение kCFBooleanTrue.

Константы kIOMediaEjectableKey и kIOCDMediaClass определяются в IOMedia.h в Kernel.framework. Константа kCFBooleanTrue Базовая постоянная Основа. При необходимости в большей информации о процессе использования соответствия словарей для нахождения устройств в Реестре I/O, посмотрите Аппаратные средства Доступа Из Приложений.

Затем, MyFindEjectableCDMedia передает словарь функции Набора I/O IOServiceGetMatchingServices получить объект итератора, идентифицирующий все устройства CDROM с выбрасываемыми носителями в Реестре I/O. Если успешный, MyFindEjectableCDMedia использует его параметр указателя для возврата объекта итератора. Функция вызова ответственна за выпуск этого объекта.

Наконец, MyFindEjectableCDMedia возвращает значение результата, указывающее, нашло ли оно какие-либо выбрасываемые носители CD. Константа KERN_SUCCESS определяется в kern_return.h.

Перечисление 1-3  , Находящее все выбрасываемые носители CD

kern_return_t MyFindEjectableCDMedia( io_iterator_t *mediaIterator )
{
    mach_port_t         masterPort;
    kern_return_t       kernResult;
    CFMutableDictionaryRef   classesToMatch;
 
    kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
    if ( kernResult != KERN_SUCCESS )
    {
        printf( "IOMasterPort returned %d\n", kernResult );
        return kernResult;
    }
    // CD media are instances of class kIOCDMediaClass.
    classesToMatch = IOServiceMatching( kIOCDMediaClass );
    if ( classesToMatch == NULL )
        printf( "IOServiceMatching returned a NULL dictionary.\n" );
    else
    {
        // Each IOMedia object has a property with key kIOMediaEjectableKey
        // which is true if the media is indeed ejectable. So add this
        // property to the CFDictionary for matching.
        CFDictionarySetValue( classesToMatch,
                        CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
    }
    kernResult = IOServiceGetMatchingServices( masterPort,
                                classesToMatch, mediaIterator );
    if ( (kernResult != KERN_SUCCESS) || (*mediaIterator == NULL) )
        printf( "No ejectable CD media found.\n kernResult = %d\n",
                    kernResult );
    return kernResult;
}

Получение пути к файлу устройств для устройства CDROM

Перечисление 1-4 показывает MyGetDeviceFilePath функция. Параметры к этой функции указывают итератор по выбрасываемым устройствам хранения данных CD, указателю на хранение для пути файла устройств и максимальному размеру пути. Функциональные возвраты, в deviceFilePath параметр, путь к файлу устройств, включая имя файла, для первого такое устройство это находит в итераторе.

MyGetDeviceFilePath функция исследует первый объект в переданном итераторе. Несмотря на то, что много компьютеров имеют всего одно устройство CDROM, итератор мог фактически содержать объекты для многократных устройств; однако, эта функция смотрит на только первое.

MyGetDeviceFilePath функция выполняет следующие шаги:

  1. Это вызывает функцию Набора I/O IORegistryEntryCreateCFProperty, передача ключа kIOBSDNameKey (определенный в IOBSD.h), для получения a CFTypeRef к имени файла устройств.

  2. Если вызов к IORegistryEntryCreateCFProperty успешно, MyGetDeviceFilePath создает путь устройства к устройству. Сделать это, функцию:

    • Копирует строку‘/dev/’(определенный константой _PATH_DEV в заголовке paths.h) к месту хранения, указанному deviceFilePath параметр

    • Связывает строку ‘r’ до конца пути устройства, чтобы гарантировать, что код получает доступ к неструктурированному устройству

    • Вызовы CFStringGetCString функция для кодирования Базового представления Основы имени устройства как струна до

  3. Если MyGetDeviceFilePath в состоянии создать имя файла устройств успешно, оно распечатывает строку и выпускает CFTypeRef. Полное имя файла устройств будет чем-то как /dev/rdisk0.

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

Наконец, MyGetDeviceFilePath возвращает значение результата, указывающее, получила ли функция успешно путь устройства для устройства CDROM.

Перечисление 1-4  , Получающее файл устройств, соединяет каналом для первых выбрасываемых носителей CD в переданном итераторе

kern_return_t MyGetDeviceFilePath( io_iterator_t mediaIterator,
                        char *deviceFilePath, CFIndex maxPathSize )
{
    io_object_t nextMedia;
    kern_return_t kernResult = KERN_FAILURE;
 
    *deviceFilePath = '\0';
    nextMedia = IOIteratorNext( mediaIterator );
    if ( nextMedia )
    {
        CFTypeRef   deviceFilePathAsCFString;
        deviceFilePathAsCFString = IORegistryEntryCreateCFProperty(
                                nextMedia, CFSTR( kIOBSDNameKey ),
                                kCFAllocatorDefault, 0 );
       *deviceFilePath = '\0';
        if ( deviceFilePathAsCFString )
        {
            size_t devPathLength;
            strcpy( deviceFilePath, _PATH_DEV );
            // Add "r" before the BSD node name from the I/O Registry
            // to specify the raw disk node. The raw disk node receives
            // I/O requests directly and does not go through the
            // buffer cache.
            strcat( deviceFilePath, "r");
            devPathLength = strlen( deviceFilePath );
            if ( CFStringGetCString( deviceFilePathAsCFString,
                                     deviceFilePath + devPathLength,
                                     maxPathSize - devPathLength,
                                     kCFStringEncodingASCII ) )
            {
                printf( "BSD path: %s\n", deviceFilePath );
                kernResult = KERN_SUCCESS;
            }
            CFRelease( deviceFilePathAsCFString );
        }
    }
    IOObjectRelease( nextMedia );
 
    return kernResult;
}

Открытие устройства

Открыть устройство хранения данных CD, MyOpenDrive функция, показанная в Перечислении 1-5, вызывает open функция, передавая путь файла устройств и константу O_RDONLY, который указывает, что устройство должно быть открыто для чтения только. open функция и O_RDONLY оба определяются в fcntl.h, который является частью System.framework. Можно получить больше информации о open функция путем ввода man 2 open в Окне терминала.

MyOpenDrive функционируйте возвращает значение, которое это получает от open функция; на ошибке это также распечатывает сообщение об ошибке.

Перечисление 1-5  , Открывающее устройство, указано его путем файла устройств

int MyOpenDrive( const char *deviceFilePath )
{
    int fileDescriptor;
 
    fileDescriptor = open( deviceFilePath, O_RDONLY );
    if ( fileDescriptor == -1 )
    {
        printf( "Error opening device %s: \n", deviceFilePath );
        perror( NULL );
    }
    return fileDescriptor;
}

Чтение сектора от устройства

Перечисление 1-6 показывает функцию, MyReadSector, это читает сектор носителей. Вызывающая сторона этой функции передает дескриптор файла для файла устройств. Устройство, как предполагается, открыто. MyReadSector первое использование DKIOCGETBLOCKSIZE ioctl для получения предпочтительного размера блока для носителей. Затем это выделяет буфер предпочтительного размера блока и попыток считать сектор, с помощью read функция, определяемая в unistd.h.

Перечисление 1-6  Читая сектор носителей, учитывая дескриптор файла

Boolean MyReadSector( int fileDescriptor )
{
    char *buffer;
    size_t numBytes;
    u_int32_t blockSize;
 
    if ( ioctl( fileDescriptor, DKIOCGETBLOCKSIZE, &blockSize ) == -1)
    {
        perror( "Error getting preferred block size." );
        // Set a reasonable block size instead.
        // kCDSectorSizeCDDA is defined in IOCDTypes.h as 2352.
        blockSize = kCDSectorSizeCDDA;
    }
    buffer = malloc( blockSize );
    numBytes = read( fileDescriptor, buffer, blockSize );
    free( buffer );
    return numBytes == blockSize ? true : false;
}

Закрытие устройства

Перечисление 1-7 показывает MyCloseDrive функция. Закрыть устройство CDROM, MyCloseDrive вызовы close функция (определенный в unistd.h), передавая дескриптор файла для файла устройств. Дескриптор файла был получен MyOpenDrive функция.

Перечисление 1-7  , Закрывающее устройство, учитывая его дескриптор файла

void MyCloseDrive( int fileDescriptor )
{
    close( fileDescriptor );
}