|
Еще кое-что о файлах

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