Еще кое-что о файлах
Работа файловой системы диска
Сегодня рассмотрим процесс работы файловой системы диска немного подробнее.
Давайте, например, спрячем с глаз долой все, что имеется на диске. Причем
сделаем это так, чтобы любой посторонний (и не посвященный), используя любую
имеющуюся в его распоряжении программу просмотра диска, подумал бы, что диск
чистый. Для этой благородной цели нам понадобится стандартная программа
debug.exe и больше ничего.
В общих чертах процесс псевдоопустошения диска заключается в следующем.
Необходимо в корневом каталоге для всех 32-байтных структур, описывающих файлы
или подкаталоги, изменить 11-й байт (атрибут) таким образом, чтобы его 3-й бит
("метка тома") был установлен в единицу. Этого вполне достаточно. Поскольку ОС
получает доступ к подкаталогам через их описание в корневой директории, то,
превратив их в "метки тома", мы тем самым перекроем доступ к этим каталогам.
Не советую вам репетировать на собственном винте. Дело в том, что, упрятав все
файлы, в том числе и системные, от ОС, вы тем самым эту ОС обрушите, поскольку
Windows периодически обращается к различным DLL и, не найдя таковых, впадает в
истерику. Лично я для собственных опытов взял обычную 3,5-дюймовую дискету,
отформатировал ее, причем на финальную просьбу format.exe ввести метку тома я
решил удовлетворить этот маленький каприз и, не мудрствуя лукаво, ввел
таинственно многозначительное VOL10. Затем я создал на диске директорию с именем
DIR01, после чего записал в корень два первых подвернувшихся под руку файла -
ими оказались win.ini и himem.sys. После этого я для чистоты эксперимента набил
директорию DIR01 под завязку разной дребеденью (случайными файлами и
подкаталогами).
Вот, собственно, и все приготовления. После чего загрузил отладчик debug (кнопка
Пуск, команда "Выполнить", debug, OK) и призадумался. И было отчего. Дело в том,
что не знаю, кто как, а вот лично я совершенно не помню, с какого сектора на
дискете начинается корневой каталог. "Ну ладно, - рассуждал я. - Нулевой сектор
- загрузочный, дальше идут две копии FAT, а за ними Root. Значит, чтобы получить
номер сектора, с которого начинается корневой каталог, нужно просто умножить
длину FAT на два и прибавить единицу". Но и длину FAT дискеты я не знал -
странно, как-то раньше мне это не требовалось.
И тут меня озарило: да ведь format.exe в самом конце форматирования указал общее
количество байт на дискете (ну это я и без него знал), а также количество
кластеров на ней же - 2847. Осталось только подсчитать, сколько элементов должно
быть в FAT, чтобы адресовать 2847 кластеров. Это уж и вовсе просто. Поскольку
элементы "дискетной" FAT имеют размерность 12 бит, то есть 1,5 байта, стало
быть, информация о паре смежных кластеров дискеты хранится в трех байтах.
Разделив 2827 на два (получив количество "пар") и умножив результат на три
(получив общее количество байт в FAT), а затем разделив результат на 512, я
узнал, сколько реально секторов нужно для хранения одной копии FAT.
У меня получилось число 8,28. Но поскольку количество кластеров на диске есть
число постоянное, я прикинул, что для хранения одной копии FAT потребуется
девять секторов (и, как показало дальнейшее, предчувствия меня не обманули).
Умножив девять на два и добавив единицу, я получил искомый номер сектора, с
которого начинается Root на дискете - 19. Но 19 - это десятичное число, а debug
требуются только шестнадцатеричные.
Превращение десятичного числа в шестнадцатеричное - пара пустяков, если не
заморачиваться, не наживать себе геморрой, производя муторные вычисления на
клочке бумажки, а просто воспользоваться стандартным калькулятором Windows. Для
чего нужно запустить этот самый калькулятор, установить "Вид" > "Инженерный",
ввести любое десятичное число, а затем установить флажок "Hex". Таким нехитрым
способом я и выяснил, что десятичное 19 - это шестнадцатеричное 13. После этого
в дело вступила тяжелая артиллерия.
Напомню, что для загрузки секторов диска в память с помощью debug.exe
используется команда L формата L адрес диск сектор количество. Ну я и ввел
команду L 100 0 13 1, а затем вывел первые 128 байт командой D 100 (подробности
можете опять же посмотреть в статье "Хранилище данных"). То, что предстало моим
глазам, отражено во врезке "Сокрытие файлов на дискете". Первые 32 байта хранили
информацию о метке тома. Затем в следующих 32-х байтах шло описание директории
DIR01 и двух файлов - win.ini и himem.sys (по 32 байта для каждого). Зрелище это
было, конечно, не слишком захватывающее, поскольку интерфейс у debug.exe чисто
спартанский.
Я даже думаю, что если бы в древней Спарте были компьютеры, то бедных
спартанских мальчиков заставляли бы не только спать на подстилке из хвороста и
бегать босиком с занозами в пятках, но и работать исключительно с debug.exe. А в
остальном - программа чудесная.
Значит, что нужно было сделать? Нужно было каким-то образом так изменить байты,
расположенные по адресам со смещением 012B, 014B и 016B, чтобы
3-й бит в них стал равен 1. После этого нужно было в 13-й (19-й в десятичной
системе) сектор диска записать модифицированный фрагмент памяти. Почему такие
странные смещения: 012B, 014B и 016B? Дело в том, что поскольку байт-атрибут -
это 11-й байт в 32-байтной структуре (вся нумерация начинается с нуля), а 11 -
это 0B в шестнадцатеричной системе, то нужно к смещению, с которого начинается в
памяти соответствующая 32-байтная структура, прибавить 0B. Поскольку описание
директории DIR01 в памяти начиналось со смещения 0120 (во врезке "Сокрытие
файлов на дискете" указан полный виртуальный адрес 0FA2:0120), то после
прибавления 0B получилось 012B. То же самое для 014B и 016B.
Ну, а как изменить байт в памяти? Для этого у debug.exe есть команда E (Enter)
формата E адрес. После ввода этой команды debug выводит содержимое байта по
указанному адресу и ожидает ввода нового значения. Таким образом, введя E 12B, я
получил возможность изменить нужный мне байт-атрибут путем ввода нового
значения. Но какое значение ввести - вот в чем вопрос (от правильного ответа на
этот вопрос подчас зависит, быть или не быть данным на диске). Но меня такой
ерундой смутить нельзя.
Прежде чем пытаться устанавливать (или сбрасывать) какой-либо бит, нужно
удостовериться, а может, уже все и так в ажуре, может, он и так в порядке и
ничего делать не нужно. Для этого опять как нельзя лучше пригодится
Windows-калькулятор. Просто нужно ввести шестнадцатеричное число (предварительно
должен быть установлен флажок "Hex"), а после выбрать флажок "Bin". Ну и просто
глазами посмотреть, чему равен нужный бит - 1 или 0. Напомню только, что
нумерация битов идет справа налево, начиная с 0-го. Оказалось, что в
шестнадцатеричном числе 10 в единицу установлен только 4-й бит, а все остальные
равны нулю.
Чтобы установить нужный бит в единицу, достаточно к числу, хранящемуся в байте,
прибавить степень двойки, соответствующую номеру устанавливаемого бита.
Поскольку в данном случае нужно было установить 3-й бит, то потребовалось
прибавить 23, или 8. Вот так вот и выяснилось, что для того, чтобы установить
3-й бит 11-го байта описателя директории DIR01, нужно по адресу со смещением 12B
вместо шестнадцатеричного числа 10 записать 18. Во врезке "Сокрытие файлов на
дискете" показано, как последовательно тремя командами E были изменены байты
атрибуты для директории DIR01 и файлов win.ini и himem.sys.
После сделанных изменений командой D 100 я удостоверился, что все было выполнено
верно, и, более ничего не считая и не раздумывая ни о чем, сохранил измененный
сектор командой W 100 0 13 1. После этого я просмотрел содержимое дискеты при
помощи обозревателя Windows и остался доволен результатом, потому что диск был
абсолютно пуст. Вернее, это была оптическая иллюзия: на самом-то деле диск был
забит под горлышко, в чем легко можно было убедиться, просмотрев свойства диска.
Операция "воскрешения", то есть возврат дисков из каталогов из потустороннего
царства, заключается в обратном восстановлении исходных значений
байтов-атрибутов. В вышеупомянутой врезке проиллюстрирован весь процесс, включая
и последующее восстановление утраченного добра.
Длинные имена и прочие чудеса
Пользователи PC-клонов 14 долгих лет довольствовались убогими именами формата
8-3 (восемь символов для имени и три для расширения). Тужились, кряхтели,
записывали (на бумажках или в текстовых файлах), в каком файле что лежит, а
поделать ничего не могли. И так бы по сию пору было, если бы Microsoft не пошла
навстречу широким массам и не ввела, начиная с Windows 95, поддержку длинных
имен. Но, облегчая жизнь другим, Microsoft усложнила ее себе.
Проблема заключалась в том, что в 32-байтную структур ну никак невозможно было
втиснуть 255 символов. Можно было, конечно, оставить структуру каталогов как
есть, а длинное имя писать в начало (или конец) самого файла. Но, во-первых,
такое нововведение сделало бы невозможным совместимость с ранними версиями ОС (а
ведь ничто так не пугает разработчиков, как идея отказа от пресловутой
"совместимости снизу вверх"), а во-вторых, это сделало бы очень неуклюжей работу
со структурными файлами (файлами, представляющими собой последовательность
одинаковых структур - записей). Выход был найден, может, и не очень изящный, но
вполне работоспособный, к тому же потребовавший минимальных изменений.
Во врезке "Хранение длинного пути в цепочке 32-байтных блоков" представлена
структура фрагмента каталога, посвященная описанию файла с длинным именем "Das
ist sehr sehr sehr long name" (ничего лучше этого англо-немецкого бреда не
пришло). Как хорошо видно, для описания этого файла Windows потребовалось
использовать четыре 32-байтовых структуры. Основная (самая нижняя) ничем не
отличается от стандартного 32-байтового описателя файла. Короткое имя в этой
структуре получено очень просто: Windows взяла первые шесть отличных от пробела
символов длинного имени, перевела в верхний регистр, добавила тильду (~) и
порядковый номер 1, получилось DASIST~1. Если бы в этом же каталоге был еще один
файл с именем, начинающимся с "Das ist", то он получил бы внутреннее имя
DASIST~2 и т. д.
Само длинное имя упрятано в три 32-байтных блока. Для этого Windows взяла
цепочку кодов символов ASCII, из которых, собственно, и состоит имя, и
"разбавила" их кодом 00 (то есть после каждого значащего кода поставила 00).
Затем "порубила" получившиеся пары "код-00" на отрезки по 13 пар в каждом и
засунула их в 32-байтовые блоки, начиная с 1-го байта. 0-й байт в каждом блоке -
это просто порядковый номер блока, а у последнего блока, кроме того, в этом
байте в единицу выставлены 3-й и 5-й биты (в результате получается
шестнадцатеричное 40 плюс порядковый номер).
Кроме того, в каждом блоке зарезервированы: 11-й байт-атрибут (в нем в единицу
выставлены 0-й, 1-й, 2-й и 3-й биты, в связи с чем эти структуры Windows должна
интерпретировать, как "метку тома, представляющую собой скрытый системный файл
только для чтения"); 12-13 и 26-27 (номер кластера) байты. Размещение всех этих
32-байтных структур в каталоге происходит "задом наперед", то есть первым
записывается 32-байтный блок длинного имени с "хвостом" этого имени, затем
предпоследний фрагмент имени, затем пред-предпоследний и т. д. В самом финале
записывается стандартная 32-байтная структура, описывающая файл.
Поскольку длинное имя режется на куски по 13 символов, несложно подсчитать, что
для того, чтобы сохранить информацию о файле с максимально длинным именем,
состоящим из 255 символов, потребуется 21 блок: один для описания самого файла и
двадцать - для описания полного имени.
Длинные имена - это самые радикальные изменения, которые претерпела та часть
Windows, которая осуществляет поддержку файлов на основе FAT. Некоторые
изменения, правда, потребовались при переходе к FAT32. Возможность работы с
FAT32 появилась уже начиная с версии Windows 95 OSR 2. Каждый элемент FAT32
имеет длину 32 бита. В FAT16 для работы с большими дисками (до 2 Гб) приходилось
использовать просто устрашающе гигантские кластеры размером до 64 секторов, что
приводило к огромным потерям дискового пространства за счет плохо используемых
"хвостовых" кластеров-"шлаков" (slacks).
Ну сами посудите: скажем, сохраняете вы какой-нибудь JPEG размером 10 кб, а ОС
забирает под него целых 32 кб - обидно. В FAT32 размер кластера только для
дисков более 32 Гб равен 64-м секторам (что тоже, конечно, не подарок, учитывая,
сколько именно файлов можно записать на такой диск).
Некоторые изменения затронули также 32-байтную структуру, описывающую файлы.
Поскольку в FAT16 (и FAT12) номер первого кластера, с которого начиналось
размещение файла, хранится в 26-м и 27-м байтах (то есть, в 16-ти битах), то
пришлось выкручиваться - "недостающие" два байта были взяты в зарезервированном
ранее блоке (байты с 12-го по 21-й). Но этим изменения не ограничились. В DOS и
первоначальной Windows 95 хотя и имелись две копии FAT, но реально ОС работала
только с первой копией, вторая была скорее для проформы, то есть она могла
пригодиться только при восстановлении данных, если случайно будет запорота
первая.
Для FAT32 работа может происходить с любой из копий, что повышает надежность ОС.
Кроме того, было снято ограничение на размер корневого каталога, в котором для
FAT32 стало возможно записывать любое количество файлов, а сам корневой каталог
стало возможно размещать в любом месте диска (в FAT16 он всегда записывался
после копий FAT).
Тут следует сделать еще одно небольшое пояснение. Дело в том, что, с точки
зрения операционной системы, между каталогом и файлом вообще-то никакой разницы
нет. Разница лишь в байте-атрибуте (он равен 20 или 10 для файла и директории
соответственно). Таким образом, каталог (директорию) ОС рассматривает как файл
особого типа, хранящий информацию о некоторой группе файлов (объединение в
группу произвольно и выполняется пользователем или программами путем записи в
тот или иной каталог различных файлов). С этой точки зрения было нелогичным, что
корневой каталог не мог быть помещен в произвольном месте диска (а любые другие
файлы, включая и подкаталоги, могли быть записаны где угодно), так и ограничение
на размер корня (65 535 x 32 байт), тогда как максимально допустимый размер
любого другого файла в FAT-ОС равен 4 Гб. В ОС, использующей FAT32, эти
нелогичные ограничения исчезли как дурной сон.
Ну что еще? В FAT32 в атрибуты файла включены также время и дата создания и
последней модификации. Вот, пожалуй, и все, что можно сказать о FAT32. Во всем
остальном никакой разницы между ней и ее 16-битной прародительницей не
наблюдается.
Более надежная защита
Некоторые поверхностные исследователи безосновательно утверждают, что при
создании Windows новой технологии (Windows New Technology - Windows NT) в ход
пошли разные наработки, которые были апробированы еще при работе над OS/2.
Однако тот, кто хоть что-то слышал о файловой системе NT и "полуоси", вряд ли бы
мог такое выдумать. Да и вообще, ну что, кроме FAT-системы, могли разработать в
тесном сотрудничестве IBM и Microsoft. С другой же стороны, предположение о том,
что Microsoft могла самостоятельно разработать что-то доселе невиданное - это,
знаете ли, граничит с ненаучной фантастикой (вы не слышали, как появился продукт
MS Excel? Ну, я вам как-нибудь расскажу позднее).
Однако где искать прототипы той удивительной файловой системы, которая
называется NTFS? Я вам скажу где - среди многочисленного и весьма уважаемого
семейства UNIX. Так мы и поступим. История создания UNIX интересна сама по себе:
ОС появилась как бы слегка внепланово, в результате разработки компьютерной
игры. При создании UNIX также как бы случайно появился язык "Си" ("выросший" из
языка "Би"). Но об этом и многом другом мы поговорим как-нибудь в другой раз и в
другой рубрике. Сейчас же просто кратко познакомимся с файловой системой UNIX,
так как именно она сильно повлияла на NTFS.
Поскольку, строго говоря, не существует какой-то одной UNIX, как, скажем,
Windows, а есть целое UNIX-подобное семейство, то данный обзор файловой системы
представляет собой перечисление общих для всех (или почти всех) систем черт.
UNIX изначально разрабатывалась для мини-ЭВМ PDP-7 - многопользовательской
машины, отчего с самого начала большое внимание было уделено проблеме
совместного доступа к файлам. В чем суть проблемы? Предположим, на диске сервера
хранится какой-то файл, скажем, с каким-нибудь мутным отчетом о деятельности
фирмы. Спрашивается: как именно следует организовать доступ к этому файлу с
различных рабочих станций?
Можно поступить следующим образом: первый, кто добрался до этого файла, имеет
права делать с ним все, что заблагорассудится, а все остальные могут только
читать этот файл, но вносить в него изменения - нет. Но такой подход далеко не
всегда удобен. Например, если речь идет о базе данных, в которой хранятся
сообщения из гостевой книги, то теоретически в один и тот же файл должны иметь
возможность писать различные клиенты. Или не должны? Или клиенты должны
передавать данные серверной части, а та уже делать соответствующие изменения?
Да и вообще, как определить, кто может работать с данным файлом, а кто нет? Кто
может его только читать, а кто читать-писать, да еще и удалять в придачу?
Словом, проблем куча. Собственная любая сетевая ОС отличается от обычной
локальной как раз именно тем, что более или менее успешно разрешает эти
проблемы. И вот UNIX (во всех ее ипостасях) очень успешно справляется с
проблемами сервера, отчего различные версии этой системы (включая и дальних
потомков, таких, как Linux) весьма охотно используются для поддержки крупных
серверов.
Термин "корневой каталог" (root) появился как раз в ОС UNIX. В корневом каталоге
UNIX, как правило, содержатся следующие стандартные подкаталоги: bin - для часто
используемых программ, dev - для драйверов устройств ввода-вывода, lib - для
библиотек и usr - для пользовательских директорий.
В любой пользовательской директории также, как правило, имеется каталог bin, а
на серверах, поддерживающих веб-узлы, еще и каталог cgi-bin, в котором хранятся
CGI-скрипты, обслуживающие конкретный узел. В полном перечне путей к конкретному
каталогу, также как и в Windows (или DOS), используется слеш, но с той разницей,
что если Microsoft выбрало такой слеш: \, то UNIX использует слеш с обратным
наклоном: /. Еще одно отличие - в Windows нет различия в регистре символов,
которым задано имя файла, а в UNIX - есть. Например, для системы Windows два
имени файла - Photo.JPG и photo.jpg - рассматриваются как два идентичных имени,
а вот UNIX будет рассматривать их как имена двух разных файлов (на этом нередко
накалываются начинающие веб-мастера).
С каждым UNIX-файлом (и директорией, поскольку это тоже всего лишь файл) связано
специальное битовое изображение, описывающее права доступа к этому файлу.
Отображение содержит три поля: первое контролирует работу с файлом со стороны
владельца файла, второе - для группы, в которую входит владелец, наконец, третье
- для всякого встречного-поперечного, который случайно на этот файл наткнется в
Сети. Каждое поле описывается триадой RWX (Read-Write-eXecution). Например,
установка [RWX][R-X][--X] позволяет владельцу (скажем, программисту веб-узла)
делать с файлом все, что угодно: читать, изменять и выполнять его (если это,
скажем, скрипт). Члены группы могут только читать и выполнять (не изменяя его),
а посторонние могут только выполнять (например, запустить сценарий perl, не имея
при этом возможности загрузить сам файл на свой компьютер, чтобы исследовать
алгоритм).
С каждым файлом связан 64-байтный блок (то есть в два раза больший, чем в FAT-
Windows), который называется индексным дескриптором (i-node), в котором описаны
все сведения о файле, включая и возможные манипуляции над ним. Все i-node
расположены в начале диска и имеют последовательные номера, поэтому система
легко находит нужный i-node простым вычислением его адреса на диске. Элемент
каталога состоит из двух частей - имени файла и индексного дескриптора, поэтому
операционная система, найдя искомое имя файла, загружает его i-node и получает
все сведения об этом файле, включая и права доступа.
Форматы индексных дескрипторов несколько изменяются от одной версии UNIX к
другой, но в целом содержат следующие поля:
тип файла, девять битов защиты (RWX);
длина файла; число связей с файлом;
идентификатор владельца;
группа владельца и пр.
Самой любопытной структурой i-node являются так называемые 13 адресов на диске.
Никакой особой кабалистики тут нет, речь идет вот о чем. Первые 10 адресов из
этой "чертовой дюжины" содержат адреса блоков данных, из которых состоит файл.
Эти блоки данных в некотором роде можно считать аналогами кластеров в FAT.
Разница лишь в том, что в FAT-системе все кластеры описаны в отдельной таблице,
а в UNIX для каждого файла хранится свой собственный перечень "кластеров". Если
размер блока 1 кб, то использование первых десяти адресов позволяет хранить
файлы объемом до 10 кб. Понятно, что такой размер мало кого может удовлетворить,
и на этот счет существует 11-й адрес, указывающий на так называемый блок
косвенной адресации - структуру, содержащую 256 адресов блоков данных.
Таким образом, использование 11-ти адресов дает возможность работы с 266-ю (10 +
256) блоками данных. Если и этого мало, то имеется 12-й адрес - указатель на
блок двойной косвенной адресации, отсылающий систему к структуре, хранящей
адреса 256-ти блоков обычной косвенной адресации (итого уже имеем: 10 + 256 +
256 x 256 = 65 802 блока). Наконец, если и этого мало, то имеется 13-й адрес -
адрес блока тройной косвенной адресации, который ссылается на описание 256-ти
блоков двойной косвенной адресации, каждый из которых ссылается на 256 блоков
двойной косвенной адресации, каждый из которых… короче, таким образом можно
хранить файлы размерностью до 16 Гб, то есть в четыре раза больше, чем в
FAT-системе.
Такой запутанный механизм дает просто колоссальную защищенность данных. Если для
того, чтобы угробить данные на диске, обслуживающемся Windows 9x, достаточно
просто грохнуть обе копии FAT, что сделать не очень-то сложно (вы уже имеете об
этом представление), то для разгрома диска, защищенного UNIX, придется очень
сильно попотеть, блуждая между всеми этими бесконечными блоками одинарной,
двоичной и троичной косвенных адресаций. Так что, когда в Microsoft решили
по-настоящему удивить мир, то, создавая NTFS (NT File System), за образец в
области работы с файлами взяли именно UNIX.
В плане защиты NTFS еще более надежна, чем UNIX. Когда пользователь входит в
систему, его процесс получает от операционки маркер доступа, который имеет
специальный идентификатор безопасности (Security ID - SID) и другую
дополнительную информацию, позволяющую операционной системе легко контролировать
любые своеволия, которые решит совершить данный процесс. В NTFS каждый диск
разбит на тома. Основной структурой данных в каждом томе является MFT (Master
File Table - главная файловая таблица). MFT содержит элементы для каждого файла,
аналогичные i-node в UNIX. MFT, по сути, является файлом и может быть размещена
в любом месте диска в пределах тома. Все имена файлом кодируются не в кодах
ASCII, как в FAT, а в Unicode.
Все операции, влияющие на структуру диска, регистрируются в специальном журнале
транзакций (log file). В принципе, для понимания того, чем NTFS отличается от
FAT, сказанного вполне достаточно, а большее углубление, пожалуй, мало кому
нужно. Ибо тут мы вторгаемся в "священные рощи" системных администраторов,
которых ничто так не раздражает, как вторжение на их территорию посторонних. Так
что я умолкаю.