PicoFat — миниатюрная библиотека работы с файловой системой FAT, предназначенная для работы в условиях очень малого ресурса памяти. Не требует даже 512-байтового буфера сектора, так как считывает сектора короткими фрагментами.
На данный момент сделано только чтение файлов, что для многих приложений (типа звонков со сменными мелодиями, информационных табло) вполне достаточно. Реализация записи — в процессе разработки.
Параметр | Значение |
---|---|
Поддерживаемые файловые системы | |
— MBR | да, только первый основной раздел |
— FAT12 | нет, инициализация работает, нет считывания |
— FAT16 | да |
— FAT32 | да |
Максимальный объём носителя | 4294967296 секторов |
(2 Тибайт при размере сектора 512 байт) | |
Размеры сектора | 512, 1024, 2048, 4096 |
Операции | |
— чтение | да |
— запись | нет |
— форматирование | нет |
— дефрагментирование | нет |
Требуемое ОЗУ | |
— на носитель | 24 байт (может быть немного больше) |
— на открытый файл | 20 байт (может быть немного больше) |
— локальные данные | ~ 80 байт |
Размер программного кода | ~ 2,5 Кибайт |
Библиотека состоит из двух файлов:
picofat.c
— компилируемый файл в на языке си, «внутренности» библиотеки.
picofat.h
— заголовочный файл для программ на си и си-плюс-плюс, включающий объявления функций, типво данных, констант.
Помимо этого в пакет приложен простой тестовый файл, который можно собрать с библиотекой на ПК, и проверить работу библиотеки с использованием файла образа диска.
Всё, касающееся работы с FAT можно условно поделить на три уровня (от верхнего к нижнему):
Таким образом библиотеке необходимо два программных интерфейса:
Библиотека, таким образом, управляемая через фронтенд приложением, через бэкенд работает с драйвером устройства.
В первую очередь для работы библиотеки необходимо обеспечить бэкенд для работы с физическим носителем в лице драйвера. В данном случае та часть, которая будет взаимодействовать с библиотекой, состоит из всего лишь одной функции: pfat_rawread
. Это, конечно, не исключает необходимости правильной инициализации устройства, и обеспечения прочих сервисных функций, но это всё уже никак напрямую с библиотекой PicoFAT не связано, и каким образом это будет сделано, не имеет никакого значения.
Единственная функция бэкенда будет выглядеть примерно таким образом:
int pfat_rawread(pfat_device dev, void *buffer, unsigned long sec, unsigned int offset, unsigned int length) { ... // читаем данные в буфер if(success) return 0; else return -1; }
Более подробно — см. описание функции pfat_rawread.
Примерный сценарий работы с библиотекой имеет следующий вид:
Причём пункты 3—6 могут выполнять многократно и даже параллельно. Инициализация и завершение работы драйвера никак не связанно с библиотекой, тем не менее устройство должно быть инициализировано перед началом работы с FAT и отключено только после окончания работы.
Инициализация библиотеки FAT производится вызовом функции pfat_init, которой передаётся указатель на уже созданную структуру pfat_volume. При этом в структуре должен быть уже прописан элемент dev
, если это требуется.
init_block_device(my_device); // любая функция инициализации физического устройства my_volume->dev = my_device; // сохраняем дескриптор физического устройства; status = pfat_init(my_volume); // инициализируем библиотеку;
Работа с носителем информации из библиотеки обеспечивается посредством вызовов импортируемой функции драйвера.
В данной версии требуется только функция чтения с носителя, pfat_rawread
.
int pfat_rawread(pfat_device dev, void *buffer, unsigned long sec, unsigned int offset, unsigned int length);
Параметры:
pfat_device
) определён как void *
, однако его можно переопределить как любой другой (кроме массива), библиотека не производит над ним никаких операций.
offset
, элементу 1 — байт offset+1
и т. д.
Результат:
Библиотека не учитывает возвращаемое значение каким-либо иным образом, важен только знак. В ситуации, когда удалось считать только часть данных функция должна возвратить ошибку, так как библиотека не учитывает такую ситуацию.
Несмотря на то, что теоретически значение параметров offset
, length
или их суммы, могут быть за пределами длины сектора, библиотека, если загрузочные сектора инициализированы правильно, никогда не использует такие значения, а значит такая ситуация не требует особой обработки драйвером, однако в диагностических целях возможно возвращать ошибку.
Библиотека не тестировалась, но должна правильно работать с секторами длиной не только 512 байтов, но и любыми другими разрешёнными файловой системой FAT: 1024, 2048 или 4096 байтов. И если такие носители возможны, работа с ними организована описанным образом. Смысл номера сектора в этом случае не меняется: каждый номер должен соответствовать отдельному сектору диска соответствующей длины (а не 512-байтному).
В качестве отступления хочу отметить, что нельзя прямо, ни простой переупаковкой секторов, ни сектор в сектор, скопировать образ диска с одним размером секторов, на диск с другим размером. Именно поэтому правильная работа с номерами секторов необходима для работы с диском, и именно поэтому я не заменил отдельные параметры sec
и offset
одним адресом относительно начала — процедура предварительного выяснения размера сектора у драйвера и пересчёта этих самых смещений (особенно если учесть возникающую необходмость в 64-битной арифметике) только бессмысленно усложнит процедуры.
Для для работы библиотекой используются две структуры данных. Первая, pfat_volume
, содержит необходимую информацию о накопителе целиком, и нужна в единственном экземпляре. Другая, pfat_file
— используется для доступа к отдельным файлам, и создаётся отдельно для каждого открытого файла.
Библиотека не использует динамической памяти, поэтому место под эти структуры должно предоставлять приложение. Они могут быть как статически скомпилированы, так создаваться в стеке или динамически выделяться.
Для упрощения работы с библиотекой в заголовочном файле определено много макросов, правда, всем ими пользоваться не требуется. Вот полный их перечень:
Функциональная группа | Макрос | Значение | Описание |
---|---|---|---|
Работа с кодами ошибок | pfat_is_error(x) | ((x)<0) | Макрос проверки на ошибку |
pfaterr_format | (-2) | Ошибка формата данных | |
pfaterr_read | (-3) | Ошибка считывания | |
Работа с директориями | FATDIR_SIZE | 0x20 | Размер директории |
ld_FATDIR_SIZE | 5 | log2 размера | |
Смещения элементов файловых записей | FATDIR_Name | 0x00 | Имя файла до точки |
FATDIR_Ext | 0x08 | Расширение (после точки) | |
FATDIR_Attr | 0x0B | Атрибуты | |
FATDIR_NTRes | 0x0C | Резерв | |
FATDIR_CrtTimeTenth | 0x0D | Сотые секунд времени создания | |
FATDIR_CrtTime | 0x0E | Время создания | |
FATDIR_CrtDate | 0x10 | Дата создания | |
FATDIR_LstAccDate | 0x12 | Дата последнего доступа | |
FATDIR_FstClusHi | 0x14 | Начальный кластер (старшая часть) | |
FATDIR_WrtTime | 0x16 | Время записи | |
FATDIR_WrtDate | 0x18 | Дата записи | |
FATDIR_FstClusLo | 0x1A | Начальный кластер (младшая часть) | |
FATDIR_FileSize | 0x1C | Размер файла | |
Атрибуты файловых записей | FATATTR_READ_ONLY | 0x01 | Только для чтения |
FATATTR_HIDDEN | 0x02 | Скрытый | |
FATATTR_SYSTEM | 0x04 | Системный | |
FATATTR_VOLUME_ID | 0x08 | Метка носителя | |
FATATTR_DIRECTORY | 0x10 | Директория | |
FATATTR_ARCHIVE | 0x20 | Изменён с момента архивации | |
FATATTR_LONG_FILE_NAME | 0x0F | Маркер длинного имени (LFN) | |
Маркеры особых файловых записей | FATNAME_FREE | 0xE5 | Удалённый файл |
FATNAME_END | 0x00 | Конец директории |
Для работы с FAT используется всего несколько функций:
int pfat_init(struct pfat_volume *vol);
Аргументы:
pfat_device
, который будет использоваться для доступа к носителю.
Результат:
Функция производит инициализацию структуры vol
посредство чтения системных областей диска. В случае, если структура этих областей разрушена, может возвратить ошибку. Впрочем, проверка валидности нестрогая, поэтому возможна неправильная работа в этом случае, хотя тяжёлых последствий (зависания, перезагрузки, повреждения данных) это вызвать не должно.
На данный момент реализована только инициализация первого основного раздела на носителях с таблицей разделов MBR. Работа без MBR, как и работа с другими разделами не предусмотрена, однако может быть сделана. Однако по моему мнению на текущий в этом нет необходимости.
int pfat_reopen(struct pfat_file *f, char *name, struct pfat_volume *vol);
Аргументы:
Результат:
Так как система не использует динамического выделения памяти, место под файловый дескриптор должно быть выделено заранее любым способом, и библиотекой предоставляется функция именно в таком варианте, функционально подобном стандартному freopen. Текущее содержимого дескриптора теряется, однако нет ничего страшного при повторном его использовании — дескриптор не требует специальной процедуры закрытия (и поэтому отсутствует соответствующая функция).
Несмотря на то, что FAT — традиционно регистронезависимая система, обработка регистра букв не предусмотрена библиотекой, таким образом путь к файлу требуется давать точно в таком регистре, в котором он сохранён в FAT. А так как длинные имена библиотекой не поддерживаются, то все имена должны быть только в верхнем регистре.
Функция позволяет открывать и считывать директории точно таким же образом, как и файлы, достаточно указать в качестве пути имя директории. При этом директория считывается как массив записей по 32 байта в полном соответствии со спецификацией FAT. Для работы с директориями в заголовочнов файле определена серия констант FATDIR_*
, FATATTR_*
, FATNAME_*
.
При инициализации текущая позиция в файле устанавливается на его начало, после чего он может быть считан функцией pfat_read
.
int pfat_read(struct pfat_file *f, void *buffer, int bytes);
Аргументы:
pfat_reopen
.
bytes
.
Результат:
Последовательно считывает данные из файла в буфер. При этом перемещает текущую позицию на следующие по порядку данные. Если при считывании упирается в конец файла, считывает остатки, после чего чтение ничего не выдаёт — возвращается 0. Директории считываются точно таким же образом, как файлы. Приложению предстаёт голая структура директории FAT, которая, тем не менее достаточно проста в плане разбора. Именно поэтому библиотека не даёт отдельных функций для работы с директориями.