IOCTL-FAT(2)

ИМЯ
ioctl_fat - управление файловой системой FAT

ОБЗОР
#include <linux/msdos_fs.h>
#include <sys/ioctl.h>

int ioctl(int fd, FAT_IOCTL_GET_ATTRIBUTES, uint32_t *attr);
int ioctl(int fd, FAT_IOCTL_SET_ATTRIBUTES, uint32_t *attr);
int ioctl(int fd, FAT_IOCTL_GET_VOLUME_ID, uint32_t *id);
int ioctl(int fd, VFAT_IOCTL_READDIR_BOTH,
          struct __fat_dirent[2] entry);
int ioctl(int fd, VFAT_IOCTL_READDIR_SHORT,
          struct __fat_dirent[2] entry);

ОПИСАНИЕ
Системный вызов ioctl(2) можно использовать для чтения и записи метаданных файловых систем FAT, которые недоступны через другие системные вызовы.

  Чтение и установка файловых атрибутов
У файлов и каталогов и в файловой системе FAT есть битовая маска атрибутов, которую можно прочитать с помощью FAT_IOCTL_GET_ATTRIBUTES и записать с FAT_IOCTL_SET_ATTRIBUTES.

Аргумент fd содержит файловый дескриптор файла или каталога. Для создания файлового дескриптора достаточно вызвать open(2) с флагом O_RDONLY.

Аргумент attr содержит указатель на битовую маску. Назначение битов в маске:

ATTR_RO
    Данный бит означает, что файл или каталог доступен только для чтения.

ATTR_HIDDEN
    Данный бит означает, что файл или каталог скрыт.

ATTR_SYS
    Данный бит означает, что файл является системным.

ATTR_VOLUME
    Данный бит означает, что файл является меткой тома. Данный атрибут доступен только для чтения.

ATTR_DIR
    Данный бит означает, что это каталог. Данный атрибут доступен только для чтения.

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

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

  Чтение идентификатора тома
Файловые системы FAT маркируются идентификатором тома. Идентификатор тома может быть прочитан с помощью FAT_IOCTL_GET_VOLUME_ID.

Аргумент fd может быть файловым дескриптором файла или каталога файловой системы. Для создания файлового дескриптора достаточно вызвать open(2) с флагом O_RDONLY.

Аргумент id — это указатель на поле, которое будет заполнено ID тома. Обычно, идентификатор тома выдаётся пользователю как группа из двух 16-битных полей:

    printf("Volume ID %04x-%04x\n", id >> 16, id & 0xFFFF);

  Чтение коротких имён файлов каталога
Файл или каталог файловой системы FAT всегда имеет короткое имя файла, состоящее из не более чем 8 заглавных букв, необязательной точки и до 3 заглавных букв расширения файла. Если реальное имя файла не следует такой схеме, то оно сохраняется как длинное имя файла — до 255 символов UTF-16.

Короткие имена файлов в каталоге можно прочитать с помощью VFAT_IOCTL_READDIR_SHORT. С помощью VFAT_IOCTL_READDIR_BOTH можно прочитать как короткие так и длинные имена файлов.

Аргумент fd должен содержать файловый дескриптор каталога. Для создания файлового дескриптора достаточно вызвать open(2) с флагом O_RDONLY. Файловый дескриптор можно использовать только однажды для обхода всех элементов каталога повторными вызовами ioctl(2).

Параметр info представляет собой двухэлементный массив структур следующего вида:

    struct __fat_dirent {
        long            d_ino;
        __kernel_off_t  d_off;
        uint32_t short  d_reclen;
        char            d_name[256];
    };

Первый элемент массива содержит короткое имя файла. Во втором содержится длинное имя файла.

Поля d_ino и d_off заполняются только для длинных имён. Поле d_ino содержит номер inode каталога. Поле d_off содержит смещение записи файла в каталоге. Так как эти поля недоступны для коротких имён, пользовательский код должен просто игнорировать их.

В поле d_reclen содержится длина имени файла из поля d_name. Для сохранения обратной совместимости длина 0 для короткого имени указывает на достижение конца каталога. Однако предпочтительным методом определения конца каталога является проверка возвращаемого ioctl(2) значения. Если длинное имя не существует, то поле d_reclen устанавливается в 0 и d_name — строка символов длиной 0 для длинного имени файла.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
В случае ошибки возвращается -1, а errno устанавливается в значение ошибки.

При использовании VFAT_IOCTL_READDIR_BOTH и VFAT_IOCTL_READDIR_SHORT возвращается значение 1, означающее, что был прочитан новый элемент каталога и 0, когда достигнут конец каталога.

ОШИБКИ

ENOENT
    Данная ошибка может возвращаться при VFAT_IOCTL_READDIR_BOTH и VFAT_IOCTL_READDIR_SHORT, если файловый дескриптор fd указывает на на удалённый, но ещё открытый каталог.

ENOTDIR
    Данная ошибка может возвращаться при VFAT_IOCTL_READDIR_BOTH и VFAT_IOCTL_READDIR_SHORT, если файловый дескриптор fd не указывает на каталог.

ENOTTY
    Файловый дескриптор fd указывает не на объект в файловой системе FAT.

Дополнительные значения ошибок смотрите в ioctl(2).

ВЕРСИИ
VFAT_IOCTL_READDIR_BOTH и VFAT_IOCTL_READDIR_SHORT впервые появились в Linux 2.0.

FAT_IOCTL_GET_ATTRIBUTES и FAT_IOCTL_SET_ATTRIBUTES впервые появились в Linux 2.6.12.

FAT_IOCTL_GET_VOLUME_ID появился в версии 3.11 ядра Linux.

СООТВЕТСТВИЕ СТАНДАРТАМ
Данный программный интерфейс существует только в Linux.

ПРИМЕР

  Переключение флага архивирования
В следующей программе показано использование ioctl(2) для изменения атрибутов файлов. Программа читает и показывает атрибут архивирования файла. После изменения значения атрибута на противоположный, программа читает и показывает атрибут ещё раз.

Пример сеанса работы программы с файлом /mnt/user/foo:

    # ./toggle_fat_archive_flag /mnt/user/foo
    Флаг архивирования установлен
    Переключение флага архивирования
    Флаг архивирования сброшен

  Исходный код программы (toggle_fat_archive_flag.c)
    #include <fcntl.h>
    #include <linux/msdos_fs.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    /*
     * Читает атрибуты файла в файловой системе FAT.
     * Выводит состояние флага архивирования.
     */
    static uint32_t
    readattr(int fd)
    {
        uint32_t attr;
        int ret;
        ret = ioctl(fd, FAT_IOCTL_GET_ATTRIBUTES, &attr);
        if (ret == -1) {
            perror("ioctl");
            exit(EXIT_FAILURE);
        }
        if (attr & ATTR_ARCH)
            printf("Флаг архивирования установлен\n");
        else
            printf("Флаг архивирования сброшен\n");
        return attr;
    }
    int
    main(int argc, char *argv[])
    {
        uint32_t attr;
        int fd;
        int ret;
        if (argc != 2) {
            printf("Использование: %s ИМЯ_ФАЙЛА\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        fd = open(argv[1], O_RDONLY);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
        /*
         * Читает и показывает атрибуты файлов в FAT.
         */
        attr = readattr(fd);
        /*
         * Инвертирует атрибут архивирования.
         */
        printf("Переключение флага архивирования\n");
        attr ^= ATTR_ARCH;
        /*
         * Записывает изменённые атрибуты файлов в FAT.
         */
        ret = ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
        if (ret == -1) {
            perror("ioctl");
            exit(EXIT_FAILURE);
        }
        /*
         * Читает и показывает атрибуты файлов в FAT.
         */
        readattr(fd);
        close(fd);
        exit(EXIT_SUCCESS);
    }

  Чтение идентификатора тома
Следующий пример кода демонстрирует использование ioctl(2) для вывода идентификатора тома файловой системы FAT.

Пример сеанса работы программы с файлом /mnt/user:

    $ ./display_fat_volume_id /mnt/user
    Volume ID 6443-6241

  Исходный код программы (display_fat_volume_id.c)
    #include <fcntl.h>
    #include <linux/msdos_fs.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    int
    main(int argc, char *argv[])
    {
        uint32_t id;
        int fd;
        int ret;
        if (argc != 2) {
            printf("Использование: %s ИМЯ_ФАЙЛА\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        fd = open(argv[1], O_RDONLY);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
        /*
         * Читает ID тома.
         */
        ret = ioctl(fd, FAT_IOCTL_GET_VOLUME_ID, &id);
        if (ret == -1) {
            perror("ioctl");
            exit(EXIT_FAILURE);
        }
        /*
         * Форматирует вывод в виде двух групп по 16 бит каждая.
         */
        printf("Volume ID %04x-%04x\n", id >> 16, id & 0xFFFF);
        close(fd);
        exit(EXIT_SUCCESS);
    }

  Вывод содержимого каталога
Следующий пример кода демонстрирует использование ioctl(2) для вывода содержимого каталога.

Пример сеанса работы программы с файлом /mnt/user:

    $ ./fat_dir /mnt/user
    . -> ”
    .. -> ”
    ALONGF~1.TXT -> 'a long filename.txt'
    UPPER.TXT -> ”
    LOWER.TXT -> 'lower.txt'

  Исходный код программы
    #include <fcntl.h>
    #include <linux/msdos_fs.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    int
    main(int argc, char *argv[])
    {
        struct __fat_dirent entry[2];
        int fd;
        int ret;
        if (argc != 2) {
            printf("Использование: %s КАТАЛОГ\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        /*
         * Открывает файловый дескриптор каталога.
         */
        fd = open(argv[1], O_RDONLY | O_DIRECTORY);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
        for (;;) {
            /*
             * Читает следующий элемент из каталога.
             */
            ret = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, entry);
            /*
             * Если произошла ошибка, то возвращает -1.
             * Если достигнут конец каталога, то
             * возвращает 0.
             * Для обратной совместимости при достижении конца каталога
             * также d_reclen == 0.
             */
            if (ret < 1)
                break;
            /*
             * Выводит короткое и длинное имя.
             */
            printf("%s -> '%s'\n", entry[0].d_name, entry[1].d_name);
        }
        if (ret == -1) {
            perror("VFAT_IOCTL_READDIR_BOTH");
            exit(EXIT_FAILURE);
        }
        /*
         * Закрывает файловый дескриптор.
         */
        close(fd);
        exit(EXIT_SUCCESS);
    }

СМОТРИТЕ ТАКЖЕ
ioctl(2)