Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Голландия или Нидерланды - энциклопедия жизни" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
| ← Сентябрь 2001 → | ||||||
|
1
|
2
|
|||||
|---|---|---|---|---|---|---|
|
3
|
4
|
5
|
7
|
8
|
9
|
|
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
|
17
|
18
|
19
|
20
|
21
|
22
|
23
|
|
24
|
25
|
26
|
27
|
28
|
29
|
30
|
Автор
Статистика
6 подписчиков
0 за неделю
0 за неделю
Голландия. Информация об эмиграции
Служба рассылок Subscribe.Ru проекта Citycat.Ru
-*--------------------------------------------------------------------------
Здравствуйте, уважаемые подписчики.
----------------------------------------------------------------------------
Прошу меня извинить за задержку с выпуском, все как-то не до
того... дела...
_____________________________________________________________
Выпуск No9
Сегодняшний выпуск посвящен чтению файлов с файловой системы
ext2fs. Эту систему мы, скорее всего, возьмем за базовую для
начала. Как я уже говорил FAT для наших целей мало подходит. Тот
boot sector, который я публиковал до этого - можно забыть, от него
уже почти ничего не осталось. Связано это с тем, что мы отошли от
linux, наша система будет совсем другой. Я, конечно, предвижу
трудности связанные с переносом программного обеспечения. Возможно
продумаем возможность эмуляции существующих операционных систем.
Время покажет.
Так же прошу меня простить, что мы занимаемся тут всякой ерундой с
бутсекторами и файловыми системами, но до сих пор так и не начали
писать собственно ядро. Задача эта не столь, тривиальна и нужно
многое продумать, чтобы не было потом горько и обидно за бесцельно
написанный код.
Чтение ext2fs
В прошлом выпуске я описывал структуру этой файловой системы. Как
вы поняли, (я надеюсь) в файловой системе присутствует Super Block
и дескрипторы групп. Эта информация хранится в начале раздела.
Super Block во 2-м килобайте, дескрипторы групп - в третьем.
Стоит заметить, что первый килобайт для нужд файловой системы не
используется и может быть целиком использован для boot sector'а
(правда он уже будет не сектор, а килобайт :). Но для этого
следует подгрузить второй сектор boot'а.
А для инициализации файловой системы нам нужно загрузить super
block и дескрипторы групп, они же понадобятся нам для работы с
файловой системой.
Это все можно загрузить одновременно, как мы и сделаем.
mov ax, 0x7e0
mov es, ax
mov ax, 1
mov cx, 5
call load_block
Для этого мы используем уже знакомую процедуру загрузки блока, но
эта процедура станет значительно короче, потому что никаких
процентов мы больше не будем выводить.
В es засылается адрес, следующий за загруженным загрузочным
сектором (Загружается он, как мы помним, по адресу 7c00h, и имеет
длину 200h байт, следовательно свободная память начинается с
адреса 7e00h, а сегмент для этого адреса равен 7e0h). В ax
засылается номер сектора с которого начинается блок (в нашем
случае это первый сектор, загрузочный сектор является нулевым). в
cx засылается длина загружаемых данных в секторах (1 -
дополнительная часть boot sector'а, 2 - Super Block ext2, 2 -
дескрипторы групп. Всего 5 секторов).
Теперь вызовем процедуру инициализации файловой системы. Эта
процедура достаточно проста, и проверяет только соответствие magic
номера файловой системы и вычисляет размеры блока для работы.
sb equ 0x8000
ext2_init:
pusha
cmp word [sb + ext2_sb.magic], 0xef53
jz short .right
mov si, bad_sb
call outstring
popa
stc
ret
bad_sb: db 'Bad ext2 super block!', 0ah, 0dh, 0
В случае несоответствия magic номера происходит вывод сообщения об
ошибке и выход из подпрограммы. Чтобы сигнализировать об ошибке
используется бит C регистра flags.
.right:
mov ax, 1024
mov cl, [sb + ext2_sb.log_block_size]
shl ax, cl
mov [block_size], al ; Размер блока в байтах
shr ax, 2
mov [block_dword_size], ax ; Размер блока в dword
shr ax, 2
mov [block_seg_size], ax ; Размер блока в параграфах
shr ax, 5
mov [block_sect_size], ax ; Размер блока в секторах
popa
clc
ret
block_size: dw 1024
block_dword_size: dw 256
block_seg_size: dw 64
block_sect_size: dw 2
Все эти значения нам понадобятся для работы. А теперь рассмотрим
процедуру загрузки одного блока файловой системы.
ext2_load_block:
pusha
mov cx, [block_sect_size]
mul cx
call load_block
mov ax, es
add ax, [block_seg_size]
mov es, ax ; смещаем es
popa
ret
При входе в эту процедуру ax содержит номер блока (блоки
нумеруются с нуля), es содержит адрес памяти для загрузки
содержимого блока.
Номер блока нам надо преобразовать в номер сектора, для этого мы
умножаем его на длину блока в секторах. А в cx у нас уже записана
длина блока в секторах, то есть все готово для вызова процедуры
load_block.
После считывания блока мы модифицируем регистр es, чтобы
последующие блоки грузить следом за этим... в принципе
модифицирование указателя можно перенести в другое место, в
процедуру загрузки файла, это будет наверное даже проще и
компактнее, но сразу я об этом не подумал. :(
Но пошли дальше... основной структурой описывающей файл в ext2fs
является inode. Inode храняться в таблицах, по одной таблице на
каждую группу. Количество inode в группе зафиксировано в супер
блоке. Итак, процедура загрузки inode:
ext2_get_inode:
pusha
push es
dec ax
xor dx, dx
div word [sb + ext2_sb.inodes_per_group]
Поделив номер inode на количество inode в группе, в ax мы получаем
номер группы, в которой находится inode, в dx получаем номер inode
в группе.
shl ax, gd_bit_size
mov bx, ax
mov bx, [gd + bx + ext2_gd.inode_table]
ax умножаем на размер записи о группе (делается это сдвигом, но,
по сути, то же самое умножение) и получаем смещение группы в
таблице дескрипторов групп. gd - базовый адрес таблицы групп.
Последняя операция извлекает из дескриптора группы адрес таблицы
inode этой группы (адрес задается в блоках файловой системы)
который у нас пока будет храниться в bx.
mov ax, dx
shl ax, inode_bit_size
Теперь разберемся с inode. Определим его смещение в таблице inode
группы.
xor dx, dx
div word [block_size]
add ax, bx
Поделив это значение на размер блока мы получим номер блока
относительно начала таблицы inode (ax), и смещение inode в блоке
(dx). К номеру блока (bx) прибавим блок, в котором находится
inode.
mov bx, tmp_block >> 4
mov es, bx
call ext2_load_block
Загрузим этот блок в память.
push ds
pop es
mov si, dx
add si, tmp_block
mov di, inode
mov cx, ext2_i_size >> 1
rep movsw
Восстановим содержимое сегментного регистра es и перепишем inode
из блока в отведенное для него место.
pop es
popa
ret
Inode загружен. Теперь по нему можно загружать файл. Здесь все не
столь однозначно. Процедура загрузки файла состоит из нескольких
модулей. Потому что помимо прямых ссылок inode может содержать
косвенные ссылки на блоки. В принципе можно ограничить возможности
считывающей подпрограммы необходимым минимумом, полная поддержка
обеспечивает загрузку файлов до 4 гигабайт размером. Естественно в
реальном режиме мы такими файлами оперировать не сможем, да это и
не нужно. Но сейчас мы рассмотрим полную поддержку:
ext2_load_inode:
pusha
xor ax, ax
mov si, inode + ext2_i.block
mov cx, EXT2_NDIR_BLOCKS
call dir_blocks
cmp ax, [inode + ext2_i.blocks]
jz short .exit
В inode храняться прямые ссылки на 12 блоков файловой системы.
Такие блоки мы загружаем с помощью процедуры dir_blocks (она будет
описана ниже). Данный этап может загрузить максимум 12/24/48
килобайт файла (в зависимости от размера блока fs 1/2/4
килобайта). После окончания работы процедуры проверяем, все ли
содержимое файла уже загружено или еще нет. Если нет, то загрузка
продолжается по косвенной таблице блоков. Косвенная таблица - это
отдельный блок в файловой системе, который содержит в себе таблицу
блоков.
mov cx, 1
call idir_blocks
cmp ax, [inode + ext2_i.blocks]
jz short .exit
В inode только одна косвенная таблица первого уровня (cx=1). Для
загрузки блоков из такой таблицы мы используем процедуру
idir_blocks. Это позволяет нам, в зависимости от размера блока
загрузить 268/1048/4144 килобайта файла. Если файл еще не загружен
до конца, то используется косвенная таблица второго уровня.
mov cx, 1
call ddir_blocks
cmp ax, [inode + ext2_i.blocks]
jz short .exit
В inode также только одна косвенная таблица второго уровня (cx=1).
Для загрузки блоков из такой таблицы мы используем процедуру
ddir_blocks. Это позволяет нам, в зависимости от размера блока
загрузить 64.2/513/4100 мегабайт файла. Если файл опять не
загружен до конца (где же столько памяти взять), то используется
косвенная таблица третьего уровня. Ради этого мы уже не будем
вызывать подпрограмм, а обработаем ее в этой процедуре.
push ax
push es
mov ax, tmp3_block >> 4
mov es, ax
lodsw
call ext2_load_block
pop es
pop ax
mov si, tmp3_block
mov cx, [block_dword_size]
call ddir_blocks
В inode и эта таблица присутствует только в одном экземпляре (куда
же больше?). Это, крайняя возможность, позволяет нам, в
зависимости от размера блока, загрузить 16/256.5/4100 гигабайт
файла. Что уже является пределом даже для размера файловой системы
(4 терабайта).
.exit:
popa
ret
Конечно, такие крайности нам при старте будут не к чему, с учетом,
что мы находимся в реальном режиме и не можем адресовать больше
~600к памяти.
Кратко рассмотрю вспомогательные функции:
dir_blocks:
.repeat:
push ax
lodsw
call ext2_load_block
add si, 2
pop ax
inc ax
cmp ax, [inode + ext2_i.blocks]
jz short .exit
loop .repeat
.exit:
ret
Эта функция загружает прямые блоки. Ради простоты я пока не
обрабатывал блоки номер которых превышает 16 бит. Это создает
ограничение на размер файловой системы в 65 мегабайт, а реально
еще меньше, поскольку load_block у нас тоже не оперирует с
секторами, номер которых больше 16 бит, ограничение по размеру
уменьшается до 32 мегабайт. В дальнейшем эти ограничения мы
конечно обойдем, а пока достаточно.
В этой функции стоит проверка количества загруженных блоков, для
того чтобы вовремя выйти из процедуры считывания.
idir_blocks:
.repeat:
push ax
push es
mov ax, tmp_block >> 4
mov es, ax
lodsw
call ext2_load_block
add si, 2
pop es
pop ax
push si
push cx
mov si, tmp_block
mov cx, [block_dword_size]
call dir_blocks
pop cx
pop si
cmp ax, [inode + ext2_i.blocks]
jz short .exit
loop .repeat
.exit:
ret
Эта функция обращается в свою очередь к функции dir_blocks,
предварительно загрузив в память содержимое косвенного блока. так
же имеет контроль длины файла.
Функция ddir_blocks в точности аналогична этой, только для
считывания вызывает не dir_blocks, а idir_blocks, поскольку адреса
блоков в ней дважды косвенны.
Но мы еще не рассмотрели самого главного. Процедуры, которая по
пути файла может загрузить его с диска. Начнем.
ext2_load_file:
pusha
cmp byte [si], '/'
jnz short .error_exit
Если путь файла не начинается со слэш, то это в данном случае
является ошибкой. Мы не оперируем понятием текущий каталог!
mov ax, INODE_ROOT ; root_inode
call ext2_get_inode
Загружаем корневой inode - он имеет номер 2.
.cut_slash:
cmp byte [si], '/'
jnz short .by_inode
inc si
jmp short .cut_slash
Уберем лидирующий слэш... или несколько слэшей, такое не является
ошибкой.
.by_inode:
push es
call ext2_load_inode
pop es
Загрузим содержимое файла. Директории, в том числе и корневая,
являются такими же файлами, как и все остальные, только содержат в
себе записи о находящихся в директории файлах.
mov ax, [inode + ext2_i.mode]
and ax, IMODE_MASK
cmp ax, IMODE_REG
jnz short .noreg_file
По inode установим тип файла.
Если файл не регулярный, то это может быть директорией. Это
проконтролируем ниже.
cmp byte [si], 0
jnz short .error_exit
Если это файл, который нам надлежит скачать - то в [si] будет
содержаться 0, означающий что мы обработали весь путь.
.ok_exit:
clc
jmp short .exit
А поскольку содержимое файла уже загружено, то можем со спокойной
совестью вернуть управление. Битом C сообщив, что все закончилось
хорошо.
.noreg_file:
cmp ax, IMODE_DIR
jnz short .error_exit
Если этот inode не является директорией, то это или не
поддерживаемый тип файла или ошибка в пути.
mov dx, [inode + ext2_i.size]
xor bx, bx
Если то, что мы загрузили, является директорией, то со смещения 0
(bx) в этом файле содержится список записей о файлах. Нам нужно
выбрать среди них нужную. В dx сохраним длину файла, по ней будем
определять коней директории.
.walk_dir:
lea di, [es:bx + ext2_de.name]
mov cx, [es:bx + ext2_de.name_len] ; длина имени
push si
repe cmpsb
mov al, [si]
pop si
test cx, cx
jnz short .notfind
Сравниваем имена из директории с именем, на которое указывает si.
Если не совпадает - перейдем на следующую запись (чуть ниже)
cmp al, '/'
jz short .normal_path
test al, al
jnz short .notfind
Если совпал, то в пути после имени должно содержаться либо '/'
либо 0 - символ конца строки. Если это не так, значит это не
подходящий файл.
.normal_path:
mov ax, [es:bx + ext2_de.inode]
call ext2_get_inode
Загружаем очередной inode.
add si, [es:bx + ext2_de.name_len]
cmp byte [si], '/'
jz short .cut_slash
jmp short .by_inode
И переходим к его обработке. Это продолжается до тех пор, пока не
пройдем весь путь.
.notfind:
sub dx, [es:bx + ext2_de.rec_len]
add bx, [es:bx + ext2_de.rec_len]
test dx, dx
jnz short .walk_dir
Если путь не совпадает, и если в директории еще есть записи -
продолжаем проверку.
.error_exit:
mov si, bad_dir
call outstring
stc
Иначе выводим сообщение об ошибке
.exit:
popa
ret
И прекращаем работу.
Вот и весь алгоритм. Не смотря на большой размер этого
повествования, код занимает всего около 450 байт. А если убрать
параноидальные функции, то и того меньше. Не стоит пытаться
откомпилировать этот код, все эти модули вы сможете найти на нашем
сайте, ссылка на который приведена ниже. Здесь я все это привел
для того чтобы объяснить как и что. Надеюсь у меня это получается
хоть как-то. Если кто-то что-то не понимает - пишите мне, мой
адрес вы всегда можете найти чуть ниже.
В следующем выпуске мы с вами рассмотрим форматы выполняемых
файлов, используемые в unix. Это нам тоже потребуется на этапе
загрузки.
И наберитесь немного терпения... скоро мы начнем писать ядро.
Отправлено 2001-09-06 для 4463 подписчиков.
ведущий рассылки Dron - dron@infosec.ru
Сайт проекта - http://spawnhole.narod.ru/asmos/asmos.html
Архив Рассылки - http://subscribe.ru/archive/comp.soft.prog.asmos
При поддержке http://www.Kalashnikoff.ru
---------------------------------------------------------------------------
Подписка на рассылки Subscribe.Ru:
Операционная система "с нуля" на Ассемблере и Cи
http://subscribe.ru/catalog/comp.soft.prog.asmos
Ассемблер? Это просто! Учимся программировать
http://subscribe.ru/catalog/comp.prog.assembler
Ассемблер? Это просто! Учимся программировать (FAQ)
http://subscribe.ru/catalog/comp.soft.prog.faq
---------------------------------------------------------------------------
(C)Москва, 2001. Авторское право принадлежит Валяеву А.Ю. Публичное
размещение материала из рассылки, а также его использование полностью или
частично в коммерческих или иных подобных целях без письменного согласия
автора влечет ответственность за нарушение авторских прав.
-*--------------------------------------------------------------------------
Отписаться: http://subscribe.ru/member/unsub?grp=comp.soft.prog.asmos
http://subscribe.ru/ mailto:ask@subscribe.ru
|
| В избранное | ||
