Внутренний формат документов MS WORD
Страница 6. Структура каталога


 

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.

 
« Предыдущая статья   Следующая статья »