Страница 6 из 8 3.3. Структура каталога Вопреки нашим предварительным предположениям, каталогов внутри составного файла не несколько, он - один единственный! Сей объект также представлен цепочкой секторов внутри "главной" FAT, а номер его стартового сектора можно найти в заголовке составного файла по смещению 30h. Каталог состоит из 128-байтовых записей следующей структуры: struct DIR_ENTRY { BYTE ИмяОбъекта[64]; // +00 - имя объекта в UNICODE WORD РазмерИмени; // +40h - фактическая длина имени BYTE ТипОбъекта; // +42h - 1,2,3 или 5 BYTE НеИспользуется1; // +43h DWORD Предыдущий; // +44h - предыдущий объект DWORD Следующий; // +48h - следующий объект DWORD Первый; // +4Сh - первый подчиненный DWORD НеИспользуется2[9]; // +50h DWORD СтартСектор; // +74h - стартовый сектор объекта DWORD РазмерОбъекта; // +78h - размер объекта в байтах DWORD НеИспользуется3; // +7Сh };
| Хотя записи в каталогах обычно называются термином "entry" (вхождение), разработчики OLE решили (IMHO, напрасно!) взглянуть на них с точки зрения объектного подхода и назвали их (а также те объекты, которые они описывают) словом "property" (свойство). Остается только сокрушенно покачать головой по этому поводу и продолжать называть вещи пусть и "нелегальными", но более понятными именами. Имя объекта записывается латинскими буквами и хранится в формате UNICODE, т.е. для того, чтобы перевести его в удобочитаемую форму, потребуется собрать вместе все нечетные байты. Имейте в виду: самый первый символ имени может оказаться "нечитабельным" и иметь значение, например, 05h ("трефы") или 01h ("рожица"). Не пугайтесь, это так и должно быть. Фактическая длина имени хранится в записи по смещению 40h. Тип объекта описан байтом, хранящимся по смещению 42h: - 1 - это подкаталог нижнего уровня;
- 2 - поток (т.е. набор каких-то данных);
- 3 - lock-bytes (?)
- 5 - корневой каталог.
Местоположение объекта задается номером его стартового сектора (см. смещение 74h), под которым его и нужно искать в FAT. А фактическая длина объекта (например, потока) располагается по смещению 78h. Необходимо иметь в виду: если длина объекта >= 1000h = 4096, то он располагается в больших 512- байтовых блоках (т.е. в секторах). В противном случае значение по адресу 74h указывает на стартовый "блочок" в области 64-байтовых "блочков". Сама эта область представлена в каталоге как "Root Entry", и искать ее следует в FAT больших блоков (даже если ее длина <4096) - вот она, недостававшая нам в предыдущем разделе информация! Три поля по смещениям 44h, 48h и 4Сh описывают отношения страшинства между объектами. Для каталогов и подкаталогов (но не для потков!) в поле "Первый" хранится номер записи для какого-то своего дочернего объекта, которые, в свою очередь, имеют своих "Следующих" и т. д. Таким образом, все записи в каталоге упорядочиваются в виде дерева. Вот дамп одной из записей в каталоге: 05 00 44 00-6F 00 63 00-75 00 6D 00-65 00 06 00 ..D.o.c.u.m.e.n 74 00 53 00-75 00 6D 00-6D 00 61 00-72 00 79 00 t.S.u.m.m.a.r.y 49 00 06 00-66 00 6F 00-72 00 6D 00-61 00 74 00 I.n.f.o.r.m.a.t 69 00 6F 00-6E 00 00 00-00 00 00 00-00 00 00 00 .i.o.n......... 38 00 02 01-02 00 00 00-04 00 00 00-FF FF FF FF 8.............. 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00 ............... 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00 ............... 00 00 00 00-10 00 00 00-00 10 00 00-00 00 00 00 ...............
Легко видеть, что: - имя объекта \5DocumentSummaryInformation;
- длина имени 38h = 56 байтов;
- тип объекта = 2, т.е. это поток;
- предыдущий объект имеет номер = 2;
- следующий объект имеет номер = 4;
- стартовый сектор = 10h, т.е. файловая позиция = 2200h;
- размер = 1000h = 4096 байтов, т.е. искать его надо в "больших блоках".
А вот пример полного каталога: # Имя Тип Пред След Перв Старт Разм
0 Root Entry 5 -1 -1 3 24 1440 1 \1Table 2 -1 -1 -1 8 1000 2 WordDocument 2 5 -1 -1 0 1000 3 \5SummaryInformation 2 2 4 -1 10 1000 4 \5DocumentSummaryInformation 2 -1 -1 -1 18 1000 5 Macros 1 1 C B 0 0 6 VBA 1 -1 -1 7 0 0 7 ThisDocument 2 9 8 -1 0 44D 8 _VBA_PROJECT 2 -1 -1 -1 12 AA5 9 dir 2 -1 -1 -1 3D 2A3 A PROJECTwm 2 -1 -1 -1 48 29 B PROJECT 2 6 A -1 49 151 C \1CompObj 2 -1 D -1 4F 6A D ObjectPool 1 -1 -1 -1 0 0
Попробуем выполнить упорядочивание. Начнем с узла "0" ("Root Entry"), его первым "сыном" является узел "3". Собрав воедино все узлы, связанные с узлом "0" и между собой, получим следующее множество: {1, 2, 3, 4, 5, С, D}. Оно образует первый уровень дерева. На первом уровне присутствуют три объекта со статусом каталогов: "5" и "D". Дочерними объектами каталога "5" являются узел "B" и все связанные с ним объекты: {A, B, 6}. Каталог "D" не имеет дочерних объектов, т.е. он пуст. И так далее... Можно написать несложный рекурсивный алгоритм, который построит желаемое дерево: Итак, с составным файлом можно работать не только через OLE API, но и напрямую - например, из DOS-овской программы. Данный раздел является существенно упорядоченным и несколько дополненным изложением статьи M. Schwartz. LAOLA file system. |