2009 г.
Записки исследователя NTFS
Artem Baranov
Содержание
- Общие сведения
- Общие концепции NTFS
- Таблица MFT
- Атрибуты
- Каталоги
- Распространенные атрибуты
- Ссылки
Общие сведения
NTFS (NT File System) – файловая система, которая разрабатывалась Microsoft специально для Windows NT. Написание NTFS шло параллельно с разработкой самой этой ОС. Известно, что NT зарождалась не в вакууме; в ее основу были положены идеи серверной системы VMS «high-end» класса компании DEC (в 1998 она была куплена Compaq, которая затем была поглощена Hewlett-Packard). ОС VMS работала на железе DEC, включая 32-битные компьютеры VAX и 64-битные машины на основе процессора Alpha. Каким-то образом команда, работавшая над созданием VMS, перекочевала в Microsoft для создания NT. Во главе этой команды находился Дэйв Катлер (Dave Cutler), крестный отец NT, разработчик ядра системы. Основная версия причины перехода Катлера в Microsoft – закрытие проекта разработки нового RISC-процессора и соответствующей ОС, в котором он работал вместе со своей командой инженеров. В результате, не без участия Билла Гейтса, Дэйв со своей командой оказался в Microsoft. Кстати, Гейтс был очень заинтересован в создании новой ОС, потому что у Microsoft на тот момент была только Windows, пригодная для домашних пользователей, а выхода на рынок серверов не было.
Архитекторами NTFS были Том Миллер (Tom Miller) и Гэри Кимура (Gary Kimura), которые работали программистами в команде Катлера в DEC. Так же, как NT влила в себя идеи VMS, NTFS основывалась на идеях файловой системы VMS – Files-11, в которой, в частности, поддерживались списки контроля доступом (ACL), а также ввод/вывод, ориентированный на записи (record-oriented I/O) [1] [2].
NTFS разрабатывалась, прежде всего, как ФС для ОС корпоративного уровня, какой и являлась NT. Предыдущие файловые системы FAT и HPFS (ФС для OS/2) были способны удовлетворить потребностям рядовых пользователей, но не имели достаточных возможностей для корпоративной среды, таких как защищенность файлов, отказоустойчивость и восстанавливаемость. В связи с этим Microsoft выработала следующие требования к новой ФС класса «high end». (Эти требования были перечислены у Хелен Кастер в «Основах Windows NT и NTFS», первой книге, которая проливала свет на внутреннее устройство NT).
- Восстанавливаемость. В случае сбоя диска, NTFS должна привести себя в рабочее, целостное состояние. В лучшем случае, NTFS вернется в состояние, которое непосредственно предшествовало сбою, в худшем какая-то пользовательская информация может быть потеряна, но том останется в рабочем состоянии. Восстанавливаемость реализуется через модель обработки транзакций. Транзакция не что иное, как операция I/O, которая изменяет структуры NTFS. Если такая операция не завершается полностью, то NTFS откатывает свои структуры данных до состояния перед выполнением транзакции.
- Защита от несанкционированного доступа. Файлы и каталоги NTFS обладают дескрипторами защиты, что позволяет контролировать доступ к ним в рамках общей модели безопасности NT. Подобными дескрипторами защиты обладают все объекты NT (процессы, потоки, разделы и пр.).
- Отказоустойчивость. Если восстанавливаемость гарантирует целостность тома после сбоя, то отказоустойчивость может гарантировать восстановление пользовательских данных. Это реализуется за счет избыточности данных и RAID. Дублирование данных осуществляется на уровне диспетчера томов, который копирует записываемые данные на другом диске.
- Диски и файлы большого объема. В NTFS реализуется очень эффективная поддержка больших дисков и файлов. Для хранения номера кластера используется 64 бита или 8 байт. Т. о. NTFS позволяет адресовывать 2^64 кластеров, при стандартном размере кластера в 4KB и максимальном 64KB. Под размер файлов также выделено 8 байт, что позволяет адресовывать очень большие файлы.
- Поддержка POSIX. Также как NT, NTFS полностью соответствует стандарту POSIX 1003.1. Главная особенность формата заключается в назначении имен файлам с учетом регистра символов, а также реализация жестких связей (hard links). Жесткие связи позволяют файлам из разных каталогов ссылаться на одни и те же данные. Жесткая связь создается для существующего файла и указывает на его данные, хотя для пользователя такой объект выглядит как файл.
Разработка новой файловой системы, которая должна удовлетворять вышеперечисленным требованиям очень сложный процесс и здесь разработчики учли опыт MS в разработке FAT и HPFS. Том Миллер так описывает процесс разработки новой ФС:
Работа над файловой системой – это, пожалуй, самое ужасное [при разработке операционной системы]. Если обнаружилась в ядре системы, или что-нибудь странное происходит с дисплеем, то можно просто перезагрузить машину и продолжать работу. Но если что-то случилось с жестким диском, то часто дальнейшая работа вообще невозможна. Всех страшно раздражают ошибки в файловой системе. Никому не хочется, чтобы его постоянная память стала не постоянной.
NTFS имеет следующие версии.
- Версия 1.0. Была выпущена вместе с NT 3.1 в середине 1993. Включает самый основной функционал ФС.
- Версия 1.1. Была выпущена вместе с NT 3.5 в 1994.
- Версия 1.2. Была написана для NT 3.51 (середина 1995) и для NT 4 (середина 1996); последняя, также известна как «NTFS 4.0». Добавлена поддержка сжатия файлов, именованных потоков, защита файлов на основе списков ACL.
- Версия 3.0. Поставляется с Windows 2000 (известна как «NTFS V5.0»). Добавлена поддержка дисковых квот, шифрования, разреженных файлов (sparse files), точек повторного разбора (reparse points), служебный метакаталог $Extend и его файлы
- Версия 3.1. Поставляется с Windows XP (осень 2001, «NTFS V5.1»), Windows 2003 (весна 2003, «NTFS V5.2»), Windows Vista (середина 2005, «NTFS V6.0»).
Общие концепции NTFS
- Метафайлы NTFS – служебные файлы, используемые NTFS для поддержания своей внутренней структуры.
- Том (volume) NTFS – раздел, отформатированный под NTFS.
- Главная загрузочная запись (Master Boot Record) – первый сектор жесткого диска, который содержит загрузочный код (bootstrap code) и таблицу разделов (partition table). Загрузочный код читает таблицу разделов, ищет активный (флаг 0x80) раздел и передает управление на его первый сектор (загрузочный сектор).
- Загрузочный сектор (boot sector) – первый сектор тома, в котором хранятся параметры ФС. Также в загрузочный сектор входит код, который отвечает за чтение ntldr в память. Такой код несет смысл только если том является системным (т. е. только для диска C). В процессе монтирования тома, ntfs.sys проводит валидацию загрузочного сектора и признает его своим в случае совпадение необходимых параметров.
- Системный том (system volume) – том, на котором располагается ntldr. Для NT системным томом может быть только первый том – С:. Загрузочный том (boot volume) – том, на котором располагается папка windows (winnt).
Формат загрузочного сектора NTFS описывается следующей таблицей (как и все остальные описываемые структуры, наилучшим руководством по ним может служить книга Брайана Кэрриэ «Криминалистический анализ файловых систем»).
Смещение |
Размер в байтах |
Описание |
0x0 |
3 |
Команда перехода на загрузочный код. |
0x3 |
8 |
Сигнатура ‘NTFS ’. |
0xB |
2 |
Количество байт на сектор |
0xD |
1 |
Количество секторов в кластере. |
0xE |
2 |
Должно равняться нулю. |
0x10 |
1 |
Должно равняться нулю. |
0x11 |
2 |
Должно равняться нулю. |
0x13 |
2 |
Должно равняться нулю. |
0x15 |
1 |
Тип носителя |
0x16 |
2 |
Должно равняться нулю. |
0x18 |
2 |
Должно равняться нулю. |
0x1A |
2 |
Должно равняться нулю. |
0x1C |
4 |
Не используется. |
0x20 |
4 |
Должно равняться нулю. |
0x24 |
4 |
Не используется. |
0x28 |
8 |
Число секторов в томе. |
0x30 |
8 |
Стартовый кластер MFT. |
0x38 |
8 |
Стартовый кластер копии MFT. |
0x40 |
1 |
Кластеров на запись MFT. |
0x41 |
3 |
Не используется. |
0x44 |
1 |
Кластеров на индексную запись. |
0x45 |
3 |
Не используется. |
0x48 |
8 |
Серийный номер тома. Уникален. Создается в процессе форматирования. |
0x50 |
4 |
Не используется. |
0x54 |
426 |
Загрузочный код. |
0x1FE |
2 |
Маркер конца. Равен 0xAA55. |
Соответствующие структуры имеют вид (определения взяты из исходников Linux-NTFS Project, там же можно найти очень много информации о структурах ntfs). Так же как и FAT, NTFS хранит часть информации в блоке, называемом BIOS Parameter Block, который входит в состав boot sector.
typedef struct _BIOS_PARAMETER_BLOCK
{
/*0x0b*/USHORT bytes_per_sector; /* Размер сектора, в байтах */
/*0x0d*/UCHAR sectors_per_cluster; /* Секторов в кластере */
/*0x0e*/USHORT reserved_sectors; /* должен быть ноль */
/*0x10*/UCHAR fats; /* должен быть ноль */
/*0x11*/USHORT root_entries; /* должен быть ноль */
/*0x13*/USHORT sectors; /* должен быть ноль */
/*0x15*/UCHAR media_type; /* тип носителя, 0xf8 = hard disk */
/*0x16*/USHORT sectors_per_fat; /* должен быть ноль */
/*0x18*/USHORT sectors_per_track; /* не используется */
/*0x1a*/USHORT heads; /* не используется */
/*0x1c*/ULONG hidden_sectors; /* не используется */
/*0x20*/ULONG large_sectors; /* должен быть ноль */
/* sizeof() = 25 (0x19) bytes */
} BIOS_PARAMETER_BLOCK, *PBIOS_PARAMETER_BLOCK;
typedef struct _NTFS_BOOT_SECTOR
{
/*0x00*/UCHAR jump[3]; /* переход на загрузочный код */
/*0x03*/ULARGE_INTEGER oem_id; /* сигнатура "NTFS ". */
/*0x0b*/BIOS_PARAMETER_BLOCK bpb;
/*0x24*/UCHAR physical_drive; /* не используется */
/*0x25*/UCHAR current_head; /* не используется */
/*0x26*/UCHAR extended_boot_signature; /* не используется */
/*0x27*/UCHAR reserved2; /* не используется */
/*0x28*/ULARGE_INTEGER number_of_sectors; /* Количество секторов на томе. */
/*0x30*/ULARGE_INTEGER mft_lcn; /* Стартовый кластер MFT. */
/*0x38*/ULARGE_INTEGER mftmirr_lcn;/* Стартовый кластер копии MFT */
/*0x40*/CHAR clusters_per_mft_record; /* Размер MFT записи в кластерах. */
/*0x41*/UCHAR reserved0[3]; /* зарезервировано */
/*0x44*/CHAR clusters_per_index_record;/* Размер индексной записи в кластерах. */
/*0x45*/UCHAR reserved1[3]; /* зарезервировано */
/*0x48*/ULARGE_INTEGER volume_serial_number; /* уникальный серийный номер тома */
/*0x50*/ULONG checksum; /* не используется */
/*0x54*/UCHAR bootstrap[426]; /* загрузочный-код */
/*0x1fe*/USHORT end_of_sector_marker; /* конец загрузочного сектора, сигнатура 0xaa55 */
/* sizeof() = 512 (0x200) bytes */
} NTFS_BOOT_SECTOR, *PNTFS_BOOT_SECTOR;
При проверке факта, что том является NTFS, необходимо прежде всего проверить сигнатуру «NTFS ».
if( NtfsBootSector->oem_id.QuadPart != 0x202020205346544E )
return FALSE;
Потом некоторые параметры бут сектора, как например кол-во байт в секторе и количество секторов в кластере на кратность двойки в степени.
if( NtfsBootSector->bpb.bytes_per_sector < 0x100 ||
NtfsBootSector->bpb.bytes_per_sector > 0x1000 ) return FALSE;
//check sectors per cluster
switch( NtfsBootSector->bpb.sectors_per_cluster ) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
break;
default:
return FALSE;
}
Все проверки в функции ntfs_boot_sector_is_ntfs из Linux-NTFS Project.
Таблица MFT
Об MFT слышал, наверное, каждый и информация по ней есть и у Руссиновича и в статьях, например у Касперски в «NTFS изнутри и снаружи», все же я продублирую и обобщу ее.
В NTFS ключевым местом для хранения информации о файлах является таблица MFT (Master File Table). В ней содержится информацию обо всех файлах в системе. MFT состоит из записей (mft record) фиксированного размера, размер записи указывается в параметре clusters_per_mft_record структуры бут сектора.
Ключевые моменты NTFS:
- Любой объект файловой системы представляется хотя бы одной записью в MFT и, по сути, является файлом. Например, бут сектор - файл $Boot, сам MFT – $Mft.
- Файл представляет собой набор атрибутов, в которых хранятся все данные. Например, атрибут $STANDART_INFORMATION хранит информацию о файле. Атрибутом также являются и данные. Если файл имеет альтернативные потоки (streams), то таких атрибутов несколько, по числу потоков.
- LCN – смещение в кластерах относительно тома, VCN – смещение в кластерах относительно файла. Для перевода LCN в смещение на томе следует использовать формулу offs = lcn * bytes_per_sector * sectors_per_cluster, соответствующие значения хранятся в BIOS_PARAMETER_BLOCK в бут секторе.
MFT состоит из заголовка записи MFT_RECORD, за которым следуют данные записи – атрибуты.
typedef struct _MFT_RECORD
{
/*0x00*/ ULONG signature; //сигнатура 'FILE'
/*0x04*/ USHORT usa_offs;
/*0x06*/ USHORT usa_count;
/*0x08*/ ULARGE_INTEGER lsn;
/*0x10*/ USHORT sequence_number;
/*0x12*/ USHORT link_count;
/*0x14*/ USHORT attrs_offset;
/*0x16*/ USHORT flags;//флаги, см. MFT_RECORD_FLAGS
/*0x18*/ ULONG bytes_in_use;
/*0x1C*/ ULONG bytes_allocated;
/*0x20*/ ULARGE_INTEGER base_mft_record; //адрес базовой MFT-записи
/*0x28*/ USHORT next_attr_instance;
/*0x2A*/ USHORT reserved;
/*0x2C*/ ULONG mft_record_number;
//size - 48 b
} MFT_RECORD, *PMFT_RECORD;
Начало записи MFT идентифицируется сигнатурой “FILE”. Флаги указывают, является ли запись каталогом, или файлом и используется ли запись вообще.
typedef enum {
MFT_RECORD_NOT_USED = 0, //запись не используется
MFT_RECORD_IN_USE = 1, //запись используется
MFT_RECORD_IS_DIRECTORY = 2 //запись описывает каталог
} MFT_RECORD_FLAGS;
Если файловая запись используется и описывает каталог, тогда поле flags равно MFT_RECORD_IN_USE | MFT_RECORD_IS_DIRECTORY (3). Для файла, flags равен MFT_RECORD_IN_USE.
Записи адресуются через структуру
struct MFT_REF
{
unsigned __int64 index : 48; //индекс элемента в таблице
unsigned __int64 ordinal : 16; //порядковый номер
};
Для адресации записей используется младшее поле index, а ordinal нужно для определения несоответствий внутри самой файловой системы.
Смещение первого элемента MFT (в кластерах), т. е. его начало хранится в поле mft_lcn. Размер записи хранится в поле clusters_per_mft_record. Особенность этого поля заключается в том, что оно может хранить отрицательное значение, в таком случае это указание на то, что размер записи задается не в кластерах, а в байтах, причем задается не количество байт, а отрицательное значение степени двойки (логарифм двойки). Для получения количества байт нужно сделать инверсию значения в положительное и возвести двойку в степень этого значения. Например, так.
if( boot_sect.clusters_per_mft_record > 0 )
bpmftrec = bps * spc * boot_sect.clusters_per_mft_record;
else
bpmftrec = 2 << ~boot_sect.clusters_per_mft_record;
Задача с поиском элемента в MFT осложняется тем, что сам файл MFT может быть фрагментирован и тогда линейная индексация его как массива не представляется возможной. Для последующего чтения записей в MFT, лучше сразу скэшировать информацию об отрезках для файла MFT.
Первые записи MFT стандартизированы и описывают служебные файлы самой NTFS.
Утилита Руссиновича nfi позволяет сдампить элементы в MFT и просмотреть их атрибуты, например, nfi C.
Содержание Вперёд