RomeoGolf

Ср 11 Апрель 2018

USB-polygon-15: начало работы над записью

Зачем запись?

Текущее состояние проекта таково, что отладочная плата успешно имитирует флэшку с ФС FAT32 и несколькими файлами, которые читаются в ОС Windows и Linux (проверено в Win XP, 7, в Debian 8, а также в Android 4.2).

Причем, файл, который видит ОС, не обязательно должен лежать в памяти устройства. Он может подготавливаться «на лету» при обращении на чтение к определенным адресам устройства. Это можно использовать, допустим, для приема данных от неких внешних устройств, формируя файл из массива-буфера принятых данных. Можно также рассматривать чтение определенного файла, то есть, обращение на чтение к определенным адресам устройства, как команду выполнить определенное действие (если подключить к устройству некий исполнительный механизм, например).

Аналогично, и запись можно использовать не только в целях сохранения записываемой информации. Хотя на борту применяемого контроллера есть некоторый объем пользовательской flash-памяти, ее слишком мало, чтобы превратить плату в настоящую флэшку. Но операцию записи можно трактовать как команду, к тому же сопровождаемую некоторым блоком данных. Опять же, «записываемые» данные можно выдать по какому-либо протоколу из поддерживаемых контроллером аппаратно или эмулируемых программно, например, UART, SPI. I2C, да мало ли что еще, хоть бы и тупо параллельным кодом через один из портов.

С чего начать?

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

При этом современные ОС предпочитают на чтение с флэшкой работать с собственным кэшем. В результате если, скажем, в проводнике закинуть новый файл на эту «флэшку», и при этом файл будет размером меньше, чем «оставшееся свободное» место, то файл на «флэшке» как бы появится, и измененные вручную файлы сохранят изменения до разрыва связи ПК с устройством.

Хорошо бы узнать, что при этом делает ОС. В упомянутом уже документе «FAT32 File System Specification» от Microsoft (fatgen103.pdf) подробно описаны разного рода структуры данных, которые должны находиться по определенным адресам устройства с FAT. Однако не удалось найти документа, в котором бы регламентировалась работа с этими структурами. А жаль. Потому что основываясь на организации процесса записи можно идентифицировать поступающий поток данных, как принадлежащий тому или иному файлу — задача, которая перед нормальными флэшками не возникает.

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

В одном случае драйвер ФС может определить, что новое содержимое не больше старого (измеренное в кластерах, по крайней мере) и перезаписать его на прежнее место. Если же данных в файле стало значительно больше, можно начать писать на старое место, а вот для непомещающегося «хвоста» поискать место в свободной зоне. Тогда принадлежность данных файлу можно определить по адресу обращения на запись.

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

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

Для исследования этого вопроса понадобится еще один файл и кое-какие изменения, связанные с его введением.

Изготовление файла из массива в ОЗУ

Файл будет связан с массивом, находящимся в ОЗУ. В этот массив можно будет записывать всякого рода данные в процессе имитации FAT32 и надеяться, что анализ этих данных как-нибудь поможет.

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

Ввожу новый файл common.h следующего содержания:

#ifndef _COMMON_H_
#define _COMMON_H_
    /* variables */
        extern uint8_t data_PC;
        extern uint8_t data_device;
        extern uint8_t canDo;
#endif // _COMMON_H_

Здесь я объявляю уже используемые переменные data_PC и data_device, а также ввожу новую — упомянутый флаг — canDo.

В файле Lib/fake_fs.c добавляю #include "../common.h", а из файла Lib/fake_fs.c удаляю строки

    /* variables */
        extern uint8_t data_PC;
        extern uint8_t data_device;

В файле MassStorage.h удаляю строки

    uint8_t data_PC;
    uint8_t data_device;

В файле MassStorage.c также добавляю #include "common.h", а также определяю эти переменные:

uint8_t data_PC = 0;
uint8_t data_device = 0;
uint8_t canDo = 0;

Теперь надо добавить переключение флага canDo. В блоке обработки действий по срабатыванию таймера перед строкой bt_old = bt_now; добавляю

                if ((bt_now & 0x40) == 0) {canDo = 1;}
                if ((bt_now & 0x80) == 0) {canDo = 0;}

А в блоке switch (mode_out) в case 1 закрою комментарием PORTD = bt_now;, зато добавлю PORTD = canDo;, чтобы иметь возможность убедиться, что флаг разрешения некоторой операции действительно включается второй снизу кнопкой и выключается нижней.

Теперь буду делать файл. Сперва заготовка. В MassStorage.c среди определений переменных добавлю uint8_t data[128] = { 0 };, в common.c дописываю extern uint8_t data[128];. В результате готова структура данных, которая будет файлом.

Надо внедрить эту заготовку в файловую систему, это уже в файле LIB/fake_fs.c. В таблице файлов после записи, соответствующей файлу «USERDATA», добавляю новую запись:

    {"DATA    BIN", SIZE_OF_DATA, readData},

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

#define SIZE_OF_DATA    128
uint8_t * readData(uint8_t *data_buf, uint32_t size, uint32_t offset);

А потом определить эту функцию:

uint8_t * readData(uint8_t *data_buf, uint32_t size, uint32_t offset) {
    if ((offset + 16) > SIZE_OF_DATA) {
        memset(data_buf, 0, 16);
    if (offset < SIZE_OF_DATA) memcpy(data_buf, &(data[0]) + offset, SIZE_OF_DATA - offset);
    } else {
        memcpy(data_buf, &(data[0]) + offset, 16);
    }
    return data_buf;
}

Функция похожа на те, которые позволяли читать текстовые константы из памяти программ, но читает из ОЗУ (из заданного массива) двоичные данные. Теперь можно сформировать, допустим, массив адресов, которые устройство получает в командах записи, и потом прочитать этот массив в виде файла data.bin. Массив, правда, небольшой, но уж сколько есть… Оперативка на данный момент занята уже наполовину, то есть, массив можно еще увеличить, но пока не буду, погляжу по обстоятельствам.

Использование полученного файла

Для начала хочу посмотреть, какие адреса передаются в устройство вместе с командами записи. В файле fake_fs.c есть функция, вызываемая при обработке записи: WriteBlocks. В ней, в свою очередь, вызывается функция process_data, которой в параметрах передается текущая пачка из 16 байтов, адрес сектора (BlockAddress) и смещение пачки в секторе (BytesInBlockDiv16). Сейчас эта функция не делает ничего полезного, а только перебрасывает первый байт принимаемых данных в глобальную переменную data_PC, это было нужно для демонстрации взаимного обмена «ПК – устройство». Оставлю эти четыре строчки, а ниже добавлю следующее:

    static uint8_t ind = 0;
    static uint32_t d = 5;

    if (BytesInBlockDiv16 == 0) {
        if (ind < 128) {
            data[ind++] = (BlockAddress >> 0) & 0xFF;
            data[ind++] = (BlockAddress >> 8) & 0xFF;
            data[ind++] = (BlockAddress >> 16) & 0xFF;
            data[ind++] = (BlockAddress >> 24) & 0xFF;
        }
    }

То есть, при получении запроса на чтение первого байта (BytesInBlockDiv16 == 0) какого-либо сектора в массив data записывается адрес этого сектора. И так до тех пор, пока данные помещаются в массив.

Запись в Windows 7

Откомпилировал, прошил, запустил. Дальше пробую записывать файл на устройство, изменять существующий на устройстве файл и удалять, а после — читать файл DATA.BIN. Разумеется, с перезапуском устройства перед каждой пробой.

Для анализа пригодится табличка с распределением «данных» по адресам:

Данные Размер
(в секторах)
Сектор начала (dec) Занимаемые сектора (hex)
PBR
(boot sector)
1 62 0x3E
FAT1 8 63 0x3F .. 0x46
FAT2 8 71 0x47 .. 0x4E
root directory
(корневой каталог)
8 79 0x4F .. 0x56
KEY_FILE.TXT 8 87 0x57 .. 0x5E
README.TXT 8 95 0x5F .. 0x66
TESTFILE.TXT 2048 103 0x67 .. 0x866
USERDATA.TXT 8 2151 0x867 .. 0x86E
DATA.BIN 8 2159 0x86F .. 0x876
Чистое
пространство
2167 0x877 …

И вот что прочиталось из массива data в результате таких записей-стираний:

Если ничего не записывать, то в файле DATA.BIN остаются нули.

Запись пустого файла:

00000000: 3e 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  >...O...P...Q...
00000010: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000020: 56 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  V...O...P...Q...
00000030: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000040: 56 00 00 00 3f 00 00 00 47 00 00 00 3e 00 00 00  V...?...G...>...
00000050: 3f 00 00 00 47 00 00 00 00 00 00 00 00 00 00 00  ?...G...........
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Видно, что сперва ОС что-то пишет в PBR, потом в корневой каталог (зачем-то два раза), а потом в первые сектора обеих FAT (не знаю, зачем). Дальше имеющихся данных не пишет ничего, что логично: пустой файл занимает место только в каталоге.

Запись короткого текстового файла (с содержимым «12345», хоть это и не принципиально):

00000000: 3e 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  >...O...P...Q...
00000010: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000020: 56 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  V...O...P...Q...
00000030: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000040: 56 00 00 00 3f 00 00 00 40 00 00 00 41 00 00 00  V...?...@...A...
00000050: 47 00 00 00 48 00 00 00 49 00 00 00 4f 00 00 00  G...H...I...O...
00000060: 50 00 00 00 51 00 00 00 52 00 00 00 53 00 00 00  P...Q...R...S...
00000070: 54 00 00 00 55 00 00 00 56 00 00 00 4f 00 00 00  T...U...V...O...

Опять же, операция начинается с записи в PBR, потом дважды в корневой каталог. Потом что-то пишется по три сектора в обе FAT, потом дважды в корневой каталог, а дальше — не знаю, массив кончился.

Замена текста в «блокноте» в файле KEY_FILE.TXT (на «dummy text») с последующей записью и выходом:

00000000: 3e 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  >...O...P...Q...
00000010: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000020: 56 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  V...O...P...Q...
00000030: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000040: 56 00 00 00 57 00 00 00 00 00 00 00 00 00 00 00  V...W...........
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Выполняется запись в PBR, дважды — в корневой каталог, затем один раз один сектор в области изменяемого файла.

Удаление этого же файла:

00000000: 3e 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  >...O...P...Q...
00000010: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000020: 56 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  V...O...P...Q...
00000030: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000040: 56 00 00 00 3f 00 00 00 47 00 00 00 3e 00 00 00  V...?...G...>...
00000050: 3f 00 00 00 47 00 00 00 00 00 00 00 00 00 00 00  ?...G...........
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

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

Для примера еще редактирование файла README.TXT:

00000000: 3e 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  >...O...P...Q...
00000010: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000020: 56 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  V...O...P...Q...
00000030: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000040: 56 00 00 00 5f 00 00 00 00 00 00 00 00 00 00 00  V..._...........
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Полностью идентично редактированию KEY_FILE.TXT, только последний адрес для записи соответствует первому сектору кластера файла README.TXT.

И редактирование USERDATA.TXT:

00000000: 3e 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  >...O...P...Q...
00000010: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000020: 56 00 00 00 4f 00 00 00 50 00 00 00 51 00 00 00  V...O...P...Q...
00000030: 52 00 00 00 53 00 00 00 54 00 00 00 55 00 00 00  R...S...T...U...
00000040: 56 00 00 00 67 08 00 00 00 00 00 00 00 00 00 00  V...g...........
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Аналогично: отличается лишь последний адрес, соответствующий первому сектору кластера файла USERDATA.TXT.

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

Запись в Debian 8

Теперь повторю эти операции — запись файла на устройство и изменение существующего с последующим чтением файла DATA.BIN — в Linux.

Если с «флэшкой» ничего не делать, а сразу после того, как она автоопределится в системе, считать DATA.BIN, то в нем, в отличие от Windows, будет записан первый байт: 0x3E, то есть, система что-то сразу пишет в PBR.

Попробовал перебросить на устройство файл и получил тот же результат. Ну, я в курсе, что в Windows по умолчанию включена «безопасная запись», при которой в конце записи на флэшку буфер записи сразу весь сбрасывается, а в Linux по умолчанию это дело, наоборот, отключено. Поэтому после записи даю еще и команду sync.

Вот что дает запись на устройство пустого файла:

0000000: 3e 00 00 00 4f 00 00 00 4f 00 00 00 00 00 00 00  >...O...O.......
0000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Всего лишь двойную запись в первый сектор корневого каталога. А вот запись короткого — «12345» — текстового файла:

0000000: 3e 00 00 00 49 00 00 00 4f 00 00 00 41 00 00 00  >...I...O...A...
0000010: 77 08 00 00 4f 00 00 00 00 00 00 00 00 00 00 00  w...O...........
0000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Непонятная запись в третий сектор кластера FAT2 (где лежит элемент, соответствующий первому кластеру свободной области), затем в начало корневого каталога, потом запись в тот же сектор FAT1, что и FAT2, следом запись в начало чистой области и еще раз в корневой каталог.

А вот реакция на редактирование файла KEY_FILE.TXT, то есть, замену его содержимого на «dummy text» в редакторе gedit (птому что он в Debian по умолчанию) с сохранением ихменений и выходом:

0000000: 3e 00 00 00 77 08 00 00 4f 00 00 00 49 00 00 00  >...w...O...I...
0000010: 41 00 00 00 47 00 00 00 4f 00 00 00 3f 00 00 00  A...G...O...?...
0000020: 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  O...............
0000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Сначала идет запись в начало чистой области! И только потом документирование этого дела в FAT 1 и 2, а также в корневом каталоге. Отзыв на редактирование README.TXT абсолютно аналогичен, байт в байт. Чуть-чуть отличается редактирование файла USERDATA.TXT:

0000000: 3e 00 00 00 77 08 00 00 4f 00 00 00 49 00 00 00  >...w...O...I...
0000010: 41 00 00 00 41 00 00 00 49 00 00 00 4f 00 00 00  A...A...I...O...
0000020: 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  O...............
0000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

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

К слову, попробовал отредактировать KEY_FILE.TXT и сохранить, потом прочитать DATA.BIN без команды sync, эффект тот же, что и с командой sync, а вот USERDATA.TXT без sync дает эффект чуть короче, почему-то:

0000000: 3e 00 00 00 77 08 00 00 4f 00 00 00 49 00 00 00  >...w...O...I...
0000010: 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  A...............
0000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

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

К слову, текстовый вывод бинарного файла добывался командой

xxd -g1 /media/vfat/DATA.BIN

в Linux и ею же, но с другим путем к «флэшке» в Cygwin

Данные в команде записи

Жаль, что задача по результатам «исследований» несколько усложнилась. Но хотелось бы все же посмотреть, а что же пишется-то?

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

Вношу небольшие изменения в функцию обработки «записанных» данных process_data:

    /*
    if (BytesInBlockDiv16 == 0) {
        if (ind < 128) {
            data[ind++] = (BlockAddress >> 0) & 0xFF;
            data[ind++] = (BlockAddress >> 8) & 0xFF;
            data[ind++] = (BlockAddress >> 16) & 0xFF;
            data[ind++] = (BlockAddress >> 24) & 0xFF;
        }
    }
    */

    if (BlockAddress >= 0x57) {
        if (ind < 128) {
            for (uint8_t i = 0; i < 16; i++) {
                data[ind++] = data_buf[i];
            }
        }
    }

То есть, закрыл вывод адресов секторов, зато вставил вывод непосредственно данных, подряд. Откомпилировал, прошил, запустил. На устройстве открыл файл KEY_FILE.TXT и заменил его содержимое следующим текстом:

this is the dummy text
lorem ipsum et cetera

Затем посмотрел, что получилось в файле DATA.BIN:

00000000: 74 68 69 73 20 69 73 20 74 68 65 20 64 75 6d 6d  this is the dumm
00000010: 79 20 74 65 78 74 0d 0a 6c 6f 72 65 6d 20 69 70  y text..lorem ip
00000020: 73 75 6d 20 65 74 20 63 65 74 65 72 61 00 00 00  sum et cetera...
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

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

Итого

Во-первых, запись на устройство с фиктивной ФС FAT32 вполне возможна.

Во-вторых, разные ОС при записи данных на флэшку поступают по-разному.

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

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

Можно решить задачу идентификации данных, если предварительно читать специальный файл (обрабатывая эту операцию чтения и получая данные о назначении массива, который пойдет с командой записи), а потом писать куда угодно. Например, получить handle файла при помощи функции CreateFile(), затем сперва прочитать его первый сектор с помощью ReadFile(), затем посредством WriteFile() зпаписать, используя все тот же handle.

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

Еще можно принять записываемые данные и адреса обращения к служебным сектрам в некоторый буфер, а уже потом анализировать назначение этих данных и использовать соответствующим образом. Это увеличит скорость «записи», однако потребует немало памяти под буфер. В моем случае это очень маленький объем, сейчас осталось около 230 байтов оперативки, с учетом масива data — около 350. Так что ничего полезного не получится, порции будут маловаты.

Далее попробую использовать вариант с последовательными чтением-записью.


Теги: