Работа файловой системы диска
Возможно, кое-кто, читая статью "Хранилище данных" (Upgrade # 40 (130)), испытал некое чувство досады. В самом деле, определенным деталям в ней было уделено несколько поверхностное внимание. Что делать - журнал не резиновый, а рассказать хотелось сразу о многом. Сегодня рассмотрим процесс работы файловой системы диска немного подробнее. Давайте, например, спрячем с глаз долой все, что имеется на диске. Причем сделаем это так, чтобы любой посторонний (и не посвященный), используя любую имеющуюся в его распоряжении программу просмотра диска, подумал бы, что диск чистый. Для этой благородной цели нам понадобится стандартная программа 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, сказанного вполне достаточно, а большее углубление, пожалуй, мало кому нужно. Ибо тут мы вторгаемся в "священные рощи" системных администраторов, которых ничто так не раздражает, как вторжение на их территорию посторонних. Так что я умолкаю.