forked from KolibriOS/kolibrios
32b4fcb9ab
git-svn-id: svn://kolibrios.org@3539 a494cfbc-eb01-0410-851d-a64ba20cac60
392 lines
38 KiB
Plaintext
392 lines
38 KiB
Plaintext
; Copyright (c) 2008-2009, diamond
|
||
; All rights reserved.
|
||
;
|
||
; Redistribution and use in source and binary forms, with or without
|
||
; modification, are permitted provided that the following conditions are met:
|
||
; * Redistributions of source code must retain the above copyright
|
||
; notice, this list of conditions and the following disclaimer.
|
||
; * Redistributions in binary form must reproduce the above copyright
|
||
; notice, this list of conditions and the following disclaimer in the
|
||
; documentation and/or other materials provided with the distribution.
|
||
; * Neither the name of the <organization> nor the
|
||
; names of its contributors may be used to endorse or promote products
|
||
; derived from this software without specific prior written permission.
|
||
;
|
||
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka <Lrz> ''AS IS'' AND ANY
|
||
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
; DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
|
||
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
;*****************************************************************************
|
||
|
||
Нет повести печальнее на свете,
|
||
Чем повесть о заклинившем Reset'е...
|
||
|
||
Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает
|
||
Windows, для носителей с размером сектора 512 байт.
|
||
|
||
=====================================================================
|
||
|
||
Требования для работы:
|
||
1) Все используемые файлы должны быть читабельны.
|
||
2) Минимальный процессор - 80386.
|
||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||
4) Пути к используемым файлам не должны содержать символических ссылок NTFS
|
||
(жёсткие ссылки допускаются).
|
||
5) Используемые файлы не должны быть сжатыми или разреженными файлами
|
||
(актуально для NTFS, для FAT выполнено автоматически).
|
||
|
||
=====================================================================
|
||
|
||
Документация в тему (ссылки проверялись на валидность 08.08.2008):
|
||
официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
|
||
в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf
|
||
русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip
|
||
спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys
|
||
и file://C:/ntldr либо file://C:/bootmgr
|
||
неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543
|
||
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
||
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
||
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
||
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
||
официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx
|
||
официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx
|
||
формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx
|
||
|
||
=====================================================================
|
||
|
||
Схема используемой памяти:
|
||
600-2000 код загрузчика (и данные)
|
||
2000-3000 стек
|
||
3000-3200 сектор MBR
|
||
3200-3400 бутсектор логического диска
|
||
3400-3C00 информация о кэше для таблиц FAT16/FAT32:
|
||
для FAT16 - массив на 0x100 байт, каждый байт равен
|
||
0 или 1 в зависимости от того, загружен ли
|
||
соответствующий сектор таблицы FAT16;
|
||
для FAT32 - 100h входов по 8 байт: 4 байта
|
||
(две ссылки - вперёд и назад) для организации L2-списка
|
||
всех прочитанных секторов в порядке возрастания
|
||
последнего времени использования + 4 байта для номера
|
||
сектора; при переполнении кэша выкидывается элемент из
|
||
головы списка, то есть тот, к которому дольше всех
|
||
не было обращений
|
||
3400-3440 информация о кэше для файловых записей NTFS в
|
||
таком же формате, как и кэш для FAT32, но на 8 входов
|
||
3480-34C0 заголовки для кэшей записей индекса NTFS
|
||
3500-3D00 информация о кэшах записей индекса NTFS: с каждой
|
||
файловой записью связан свой кэш для
|
||
соответствующего индекса
|
||
4000-8000 место для информации об атрибутах для NTFS
|
||
60000-80000 таблица FAT12 / место под таблицу FAT16 /
|
||
кэш для таблицы FAT32 / кэш для структур NTFS
|
||
80000-90000 текущий рассматриваемый кластер
|
||
90000-92000 FAT: кэш для корневой папки
|
||
92000-... FAT: кэш для некорневых папок (каждой папке отводится
|
||
2000h байт = 100h входов, одновременно в кэше
|
||
может находиться не более 7 папок;
|
||
точный размер определяется размером доступной
|
||
физической памяти - как правило, непосредственно
|
||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||
|
||
=====================================================================
|
||
|
||
Основной процесс загрузки.
|
||
0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется
|
||
размещением команды install=c:\kordldr.win в первой строке config.sys;
|
||
при этом основной загрузчик системы загружает kordldr.win как обычный
|
||
com-файл, в какой-то сегмент по смещению 100h и передаёт управление
|
||
в начало кода (xxxx:0100).
|
||
0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется
|
||
добавлением строки наподобие c:\kordldr.win="KordOS" в секцию
|
||
[operating systems] файла boot.ini; если загружаемый файл имеет размер
|
||
не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS'
|
||
(в случае kordldr.win так и есть), то основной загрузчик каждой из
|
||
этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт
|
||
управление на адрес 0D00:0256.
|
||
0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями
|
||
с базой данных основного загрузчика через bcdedit и подробно описана в
|
||
инструкции к kordldr.win; основной загрузчик загружает целиком
|
||
kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода.
|
||
1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная
|
||
им программа окажется в свою очередь загрузчиком, и в этом случае
|
||
kordldr.win оказывается в условиях, когда основной загрузчик уже
|
||
установил какое-то окружение, в частности, перехватил некоторые
|
||
прерывания. Поэтому перед остальными действиями загрузчик должен
|
||
восстановить систему в начальное состояние. (При загрузке под
|
||
NT-линейкой такой проблемы не возникает, поскольку там основной
|
||
загрузчик ничего в системе не трогает.) Поэтому перед собственно
|
||
инициализацией KordOS при работе из-под DOS/9x производятся
|
||
дополнительные действия. Первым делом kordldr проверяет, какой из
|
||
случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт
|
||
управление не на начало кода): определяет значение ip (команда call
|
||
помещает в стек адрес следующей после call инструкции, команда pop si
|
||
выталкивает его в регистр si), и если оно равно 100h, то kordldr
|
||
загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения
|
||
у пользователя (поскольку в этой схеме kordldr загружается всегда,
|
||
он должен оставить возможность продолжить загрузку DOS/9x). Если
|
||
пользователь хочет продолжить обычную загрузку, kordldr завершается.
|
||
Иначе используется тот факт, что при выдаче прерывания перезагрузки
|
||
int 19h система предварительно снимает все свои перехваты BIOSовских
|
||
прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что
|
||
kordldr устанавливает свой обработчик трассировочного прерывания,
|
||
устанавливает флаг трассировки и передаёт управление DOSовскому
|
||
обработчику. Обработчик трассировочного прерывания ничего не делает
|
||
до тех пор, пока следующей инструкцией не оказывается int 19h, а
|
||
в этот момент отбирает управление и продолжает загрузку KordOS.
|
||
При этом BIOSовские обработчики восстановлены за исключением,
|
||
быть может, прерывания таймера int 8, которое, возможно, восстановлено
|
||
до команды jmp far на оригинальный обработчик. В последнем случае его
|
||
нужно восстановить явно.
|
||
2. Загрузчик перемещает свой код на адрес 0000:0600.
|
||
3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0,
|
||
настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы
|
||
все данные можно было адресовать через [bp+N] с однобайтовым N
|
||
(в дальнейшем они так и будут адресоваться для освобождения ds и
|
||
экономии на размере кода). Разрешает прерывания на случай, если
|
||
они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся
|
||
с весёлой рожицы (символ с ASCII-кодом 2).
|
||
4. Определяет характеристики жёсткого диска, указанного в качестве
|
||
загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h),
|
||
если LBA не поддерживается, то определяет геометрию - число дорожек
|
||
и число секторов на дорожке (функция 8 прерывания 13h), эти параметры
|
||
нужны функции чтения с диска.
|
||
5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска.
|
||
Цель цикла - для каждого логического диска попытаться загрузиться с
|
||
него (действия по загрузке с конкретного логического диска начинаются
|
||
с метки not_extended), при ошибке загрузки управление передаётся
|
||
назад этому циклу (метка next_partition), и поиск подходящего раздела
|
||
продолжается. На выходе заполняется одна переменная partition_start,
|
||
имеющая смысл начала текущего рассматриваемого логического диска,
|
||
но по ходу дела из-за приколов таблиц разделов используются ещё четыре
|
||
переменных. cur_partition_ofs - фактически счётчик цикла, формально
|
||
указатель на текущий вход в текущей загрузочной записи. Сама
|
||
загрузочная запись считывается в память начиная с адреса 3000h.
|
||
Три оставшихся нужны для правильной работы с расширенными разделами.
|
||
В каждой загрузочной записи помещается не более 4 записей о разделах.
|
||
Поэтому главной загрузочной записи, размещающейся в первом физическом
|
||
секторе диска, может не хватить, и обычно создаётся так называемый
|
||
расширенный раздел с расширенными загрузочными записями, формат
|
||
которых почти идентичен главной. Расширенный раздел может быть только
|
||
один, но в нём может быть много логических дисков и расширенных
|
||
загрузочных записей. Расширенные загрузочные записи организованы
|
||
в односвязный список, в каждой такой записи первый вход указывает
|
||
на соответствующий логический диск, а второй - на следующую расширенную
|
||
загрузочную запись.
|
||
При этом в главной загрузочной записи все адреса разделов являются
|
||
абсолютными номерами секторов. В расширенных же записях адреса разделов
|
||
относительны, причём с разными базами: адрес логического диска
|
||
указывается относительно расширенной записи, а адрес следующей
|
||
расширенной записи указывается относительно начала расширенного
|
||
раздела. Такой разнобой выглядит несколько странно, но имеет место
|
||
быть. Три оставшихся переменных содержат: extended_part_start -
|
||
начало расширенного раздела; extended_parent - текущая рассматриваемая
|
||
расширенная загрузочная запись; extended_part_cur - следующая
|
||
загрузочная запись для рассмотрения.
|
||
Цикл выглядит так: просматриваются все разделы, указанные в текущей
|
||
(главной или расширенной) загрузочной записи; для нормальных разделов
|
||
(они же логические диски) происходит переход на not_extended, где
|
||
устанавливается partition_start и начинается собственно загрузка
|
||
(последующие шаги); при встрече с разделом, тип которого указывает
|
||
на расширенность (5 или 0xF), код запоминает начало этого раздела
|
||
(в главной загрузочной записи такой тип означает расширенный раздел,
|
||
в расширенной - только указатель на следующую расширенную запись,
|
||
в обоих случаях он может встретиться только один раз в данной записи);
|
||
когда код доходит до конца списка, все нормальные разделы, описываемые
|
||
в этой записи, уже просмотрены, так что код с чистой совестью переходит
|
||
к следующей расширенной записи. Если он её не встретил, значит, уже
|
||
все логические разделы были подвергнуты попыткам загрузиться, и все
|
||
безрезультатно, так что выводится ругательство и работа останавливается
|
||
(jmp $).
|
||
Может возникнуть вопрос, зачем нужна такая сложная схема и почему
|
||
нельзя узнать нужный логический диск заранее или хотя бы ограничиться
|
||
первым попавшимся логическим диском, не крутя цикл. Так вот, вариант
|
||
с предварительным определением нужного раздела в данном случае не
|
||
используется, поскольку повлёк бы за собой нетривиальные лишние
|
||
действия по установке (в текущем виде установку можно провести вручную,
|
||
и она сводится к указанию системному загрузчику на существование
|
||
kordldr); кстати, в альтернативной версии загрузки после
|
||
Windows-загрузчика, когда установка осуществляется не вручную, а
|
||
специальной программой под Windows, используется модифицированная
|
||
версия, в которой как раз начальный физический сектор нужного раздела
|
||
прописывается установщиком. Сам kordldr не может установить, с какого
|
||
раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан
|
||
быть файлом на диске C:\). Вариант с первым попавшимся логическим
|
||
диском был реализован в первой версии загрузчика, но по ходу дела
|
||
обнаружилось, что таки нужно крутить цикл: во-вторых, может быть
|
||
приятным, что сама система может стоять вовсе не на системном C:\, а и
|
||
на других дисках; во-первых, диск C: может и не быть первым логическим
|
||
разделом - Vista любит создавать скрытый первичный раздел перед
|
||
системным, и тогда диск C: становится вторым логическим.
|
||
6. Извещает пользователя о том, что происходит попытка загрузки с очередного
|
||
логического диска.
|
||
7. Читает первый сектор логического диска и определяет файловую систему.
|
||
И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе
|
||
и должно совпадать с характеристикой физического носителя, то есть
|
||
200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число
|
||
секторов в кластере и должно быть степенью двойки.
|
||
Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со
|
||
смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано
|
||
быть ненулевым).
|
||
Критерий FAT: загрузчик вычисляет число кластеров, определяет
|
||
предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению
|
||
+38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29).
|
||
После определения типа файловой системы извещает пользователя об
|
||
определённом типе. Если файловая система не распознана, выдаёт
|
||
соответствующее сообщение и переходит к следующему логическому диску.
|
||
8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы -
|
||
константу '12'; устанавливает указатель на функцию получения следующего
|
||
в цепочке FAT кластера на FAT12-обработчик; считывает в память всю
|
||
таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке
|
||
чтения пытается использовать другие копии FAT.
|
||
8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы -
|
||
константу '16'; устанавливает указатель на функцию получения следующего
|
||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||
о кэше секторов FAT (массив байт с возможными значениями 0 и 1,
|
||
означающими, был ли уже загружен соответствующий сектор - всего в
|
||
таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не
|
||
загружен, все байты нулевые.
|
||
8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы -
|
||
константу '32'; устанавливает указатель на функцию получения следующего
|
||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||
о кэше секторов FAT (формат информации описан выше, в распределении
|
||
используемой загрузчиком памяти) - ни один сектор ещё не загружен.
|
||
8г. Общее для FAT-томов: определяет значения служебных переменных
|
||
root_start (первый сектор корневого каталога в FAT12/16, игнорируется
|
||
при обработке FAT32-томов), data_start (начало данных с поправкой,
|
||
вводимой для того, чтобы кластер N начинался с сектора
|
||
N*sectors_per_cluster+data_start), root_clus (первый кластер корневого
|
||
каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию
|
||
загрузки файла на FAT-обработчик.
|
||
8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы -
|
||
константу 'nt'; определяет значение служебной переменной frs_size
|
||
(размер в байтах файловой записи, File Record Segment), для полной
|
||
корректности проверяет, что это значение (равное 0x400 байт на всех
|
||
реальных NTFS-томах - единственный способ изменить его заключается
|
||
в пересоздании всех системных структур вручную) не превосходит 0x1000
|
||
и кратно размеру сектора 0x200 байт; инициализирует кэш файловых
|
||
записей - ничего ещё не загружено; считывает первый кластер $MFT
|
||
и загружает информацию о расположении на диске всей таблицы $MFT
|
||
(атрибут 0x80, $Data); устанавливает указатель на функцию загрузки
|
||
файла на NTFS-обработчик.
|
||
9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного
|
||
загрузчика. При обнаружении ошибки переходит на обработчик ошибок с
|
||
соответствующим сообщением.
|
||
10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск),
|
||
ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h),
|
||
может быть изменён путём модификации константы в исходнике или
|
||
специальным установщиком), bx=идентификатор файловой системы (берётся
|
||
из стека, куда ранее был засунут на шаге 8), ds:si=указатель на
|
||
callback-функцию.
|
||
11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000.
|
||
|
||
Функция обратного вызова для вторичного загрузчика:
|
||
предоставляет возможность чтения файла.
|
||
Вход и выход описаны в спецификации на загрузчик.
|
||
Чтение файла:
|
||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||
ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным
|
||
кодом должна указывать на 0:dat.
|
||
2. Разбирает переданные параметры и вызывает процедуру загрузки файла.
|
||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||
|
||
Вспомогательные процедуры.
|
||
Процедура чтения секторов (read):
|
||
на входе должно быть установлено:
|
||
ss:bp = 0:dat
|
||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||
eax = стартовый сектор (относительно начала логического диска)
|
||
cx = число секторов (должно быть больше нуля)
|
||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||
флаг CF установлен, если возникла ошибка чтения
|
||
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
||
устройстве, прибавляя номер первого сектора логического диска,
|
||
найденный при переборе дисков.
|
||
2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации
|
||
CHS-версия: все читаемые секторы были на одной дорожке.
|
||
LBA-версия: число читаемых секторов не превосходило 7Fh (требование
|
||
спецификации EDD BIOS).
|
||
CHS-версия:
|
||
3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как
|
||
единица плюс остаток от деления абсолютного номера на число секторов
|
||
на дорожке; дорожка рассчитывается как остаток от деления частного,
|
||
полученного на предыдущем шаге, на число дорожек, а цилиндр - как
|
||
частное от этого же деления. Если число секторов для чтения больше,
|
||
чем число секторов до конца дорожки, уменьшает число секторов для
|
||
чтения.
|
||
4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов,
|
||
dh=головка, (младшие 6 бит cl)=сектор,
|
||
(старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер).
|
||
5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска
|
||
и повторяет попытку чтения, всего делается не более трёх попыток
|
||
(несколько попыток нужно в случае дискеты для гарантии того, что
|
||
мотор раскрутился). Если все три раза происходит ошибка чтения,
|
||
переходит на код обработки ошибок с сообщением "Read error".
|
||
6. В соответствии с числом прочитанных на текущей итерации секторов
|
||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||
работу, иначе возвращается на шаг 3.
|
||
LBA-версия:
|
||
3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
||
итерации) до 7Fh.
|
||
4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
||
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
||
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
||
клали).
|
||
5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки
|
||
ошибок с сообщением "Read error". Очищает стек от пакета,
|
||
сформированного на предыдущем шаге.
|
||
6. В соответствии с числом прочитанных на текущей итерации секторов
|
||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||
работу, иначе возвращается на шаг 3.
|
||
|
||
Процедура обработки ошибок (find_error_si и find_error_sp):
|
||
на входе: указатель на сообщение об ошибке в si либо на верхушке стека
|
||
0. Если вызывается find_error_si, она помещает переданный указатель в стек.
|
||
1. Если ошибка произошла в процессе работы callback-функции, то
|
||
(метка error_in_callback) обработчик просто возвращает управление
|
||
вызвавшему коду, рапортуя о ненайденном файле.
|
||
2. Если же ошибка произошла до передачи управления вторичному загрузчику,
|
||
обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>"
|
||
и (восстановив стек) переходит к следующему логическому диску.
|
||
|
||
Процедура чтения файла/атрибута по известному размещению на диске
|
||
(read_file_chunk):
|
||
на входе должно быть установлено:
|
||
ds:si = указатель на информацию о размещении
|
||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||
ecx = лимит числа секторов для чтения, старшее слово должно быть 0
|
||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||
флаг CF установлен, если возникла ошибка чтения
|
||
1. Определяет, является ли атрибут резидентным (возможно только в NTFS
|
||
и означает, что данные файла/атрибута уже были целиком прочитаны при
|
||
обработке информации о файле) или нерезидентным (означает, что данные
|
||
хранятся где-то на диске, и имеется информация о том, где именно).
|
||
2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует
|
||
данные по месту назначения (с учётом указанного лимита).
|
||
3. Для нерезидентных атрибутов информация состоит из пар <размер очередного
|
||
фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура
|
||
читает фрагменты, пока файл не закончится или пока не будет достигнут
|
||
указанный лимит.
|
||
|
||
Процедура просмотра кэша (cache_lookup):
|
||
на входе должно быть установлено:
|
||
eax = искомое значение
|
||
ss:si = указатель на структуру-заголовок кэша
|
||
на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение
|
||
было только что добавлено, и сброшен, если оно уже было в кэше.
|
||
1. Просматривает кэш в поисках указанного значения. Если значение найдено
|
||
(при этом флаг CF оказывается сброшенным), переходит к шагу 4.
|
||
2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в
|
||
голове двусвязного списка), иначе добавляет к кэшу ещё один вход.
|
||
3. Устанавливает в полученном входе указанное значение. Устанавливает флаг
|
||
CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5.
|
||
4. Удаляет вход из списка.
|
||
5. Добавляет сектор в конец списка (самый новый вход).
|