hex.pp.ua

Расширенные атрибуты файлов в Windows

Расширенные атрибуты NTFS и FAT16




Расширенные атрибуты файлов — дополнительный набор атрибутов, поддерживаемый Windows в файловых системах NTFS, FAT16 и HPFS. Расширенные атрибуты (extended attributes, EA) поддерживаются начиная с Windows NT и во всех последующих операционных системах на ядре NT. Поддержка расширенных атрибутов была добавлена в Windows для совместимости с операционной системой OS/2, в которой они широко использовались. В Windows эти атрибуты почти не используются программным обеспечением, но, тем не менее, их поддержка не была убрана и присутствует даже в Windows 7.

Каждый расширенный атрибут имеет строку названия, максимальный размер которой 255 символов, и байт флагов атрибута. Максимальный размер данных расширенного атрибута — 64 килобайта, причём это ограничение распространяется не на один отдельный атрибут, а на все атрибуты целиком.

В NTFS расширенные атрибуты прицеплены к файлу в виде потока ::$EA. Увидеть этот поток у файла можно с помошью программы NTFS Stream Explorer. Она предназначена для работы с альтернативными файловыми потоками, но может отображать и наличие потока EA. На иллюстрации видно, что у файла test.dat нулевой длины есть поток расширенных атрибутов размером 23 байта.

Расширенные атрибуты
Буфер расширенных атрибутов

Прочитать содержимое потока ::$EA напрямую нельзя. Это приведёт к выводу такого сообщения:

E:\CODE\ea\bin>more < test.dat::$EA
Отказано в доступе.

В FAT16 для расширенных атрибутов система создаёт в корне диска системный файл «EA DATA. SF», там и хранится их содержимое. А вот в FAT32 поддержка расширенных атрибутов отсутствует, при копировании файла на эту файловую систему EA теряются.

EA DATA. SF
Файл EA DATA. SF

При копировании файла с расширенными атрибутами на раздел FAT32 информация EA молча теряется. Windows не выводит никаких сообщений о потере информации. Это отличает дополнительные атрибуты от схожих по концепции альтернативных файловых потоков, поддержка которых есть в NTFS. Например в Windows 7 при попытке скопировать с NTFS на FAT32 файл, содержащий в себе альтернативные потоки NTFS, выводится сообщение «Действительно скопировать файл без его свойств?». Но если копировать туда же файл с EA, никаких сообщений не будет.

скопировать файл без его свойств
Сообщение при потери потока

Стандартных WinAPI функций специально для работы с расширенными атрибутами нет. Отсутствуют также стандартные утилиты Windows для работы с ними. Даже утилита fsutil, которая умеет делать много интересных операций над файлами, не поможет при работе с EA. Этим объясняется то, что данная технология почти никем не используется в Windows. Чтение расширенных атрибутов возможно через использование функции BackupRead (способ описан тут, но в примере описывается чтение потоков NTFS, а не EA), или через использование NT Native API (недокументированных функций библиотеки ntdll.dll).

Я написал консольное приложение EA.EXE, способное читать, писать, удалять расширенные атрибуты и выводить список EA. Программа использует функции из ntdll для работы с EA.

Имена расширенных атрибутов

Имя расширенного атрибута состоит из ASCII-символов. Латинские буквы приводятся к верхнему регистру, регистр символов не различается при обращении к атрибуту. Набор символов имени файла не должен содержать следующие запрещённые символы: значения ASCII 0x00 - 0x1F, символы \ / : * ? " < > | , + = [ ] ;. Максимальная длина имени 255 символов.

API расширенных атрибутов

В ntdll.dll содержатся две функции, с помощью которых реализуется доступ к расширенным атрибутам. Их прототипы:

NTSYSCALLAPI
NTSTATUS
NTAPI
NtSetEaFile(
    IN HANDLE FileHandle,
    IN PIO_STATUS_BLOCK IoStatusBlock,
    PVOID EaBuffer,
    ULONG EaBufferSize
);

NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryEaFile(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID Buffer,
    IN ULONG Length,
    IN BOOLEAN ReturnSingleEntry,
    IN PVOID EaList OPTIONAL,
    IN ULONG EaListLength,
    IN PULONG EaIndex OPTIONAL,
    IN BOOLEAN RestartScan
);

Функция NtSetEaFile служит для записи информации в расширенные атрибуты, а также для удаления атрибутов. Функция NtQueryEaFile предназначена для чтения данных EA или перечисления расширенных атрибутов у файла. Эти функции работают со структурой FILE_FULL_EA_INFORMATION, имеющей следующий формат:

typedef struct _FILE_FULL_EA_INFORMATION
{
    ULONG NextEntryOffset;
    UCHAR Flags;
    UCHAR EaNameLength;
    USHORT EaValueLength;
    CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

Поле NextEntryOffset содержит смещение следующей структуры в буфере данных. Поле нужно при использовании NtQueryEaFile, когда она возвращает в буфере сразу несколько атрибутов. Поле Flags содержит флаги атрибутов. По-умолчанию поле флагов имеет значение 0, допустимо также значение FILE_NEED_EA, равное 0x80, которое означает, что имеющий этот флаг атрибут важен для обработки данных файла.

Поля EaNameLength и EaValueLength это размеры имени атрибута и данных атрибута. EaName это указатель на начало имени атрибута. Сразу после имени атрибута начинаются данные. Общий размер структуры FILE_FULL_EA_INFORMATION не может превышать 64 кб. При наличии в буфере нескольких структур их общий размер также не может превышать этот размер.

Запись расширенного атрибута

Определим в константе EA_BUF_LEN максимальный размер буфера EA, равный 0xFFFF. Тогда максимальный размер данных атрибута MAX_EA_DATA_LEN равен размеру буфера, от которого нужно отнять 8 (заголовок структуры) и ещё отнять 2 (минимально короткое имя атрибута, 1 буква + нулевой символ). Перед записью нужно убедиться, что записываемые данные не превышают этого значения.

Файл для записи EA данных нужно открыть с флагом FILE_FLAG_BACKUP_SEMANTICS. Далее следует сформировать структуру FILE_FULL_EA_INFORMATION, правильно указать в ней размеры имени и данных, выставить флаги и скопировать в структуру строку имени и данные атрибута по правильным смещениям.

Затем вызывается функция NtSetEaFile и анализируется её возвращаемое значение NTSTATUS. В результате ошибки попытки записи расширенных атрибутов функция может вернуть следующие коды ошибок:

// Неправильное имя расширенного атрибута 
#define STATUS_INVALID_EA_NAME           ((NTSTATUS)0x80000013L)
// Структура EA сформирована неправильно
#define STATUS_EA_LIST_INCONSISTENT      ((NTSTATUS)0x80000014L)
// Структура EA превышает максимальный размер
#define STATUS_EA_TOO_LARGE              ((NTSTATUS)0xC0000050L)
// Расширенные атрибуты не поддерживаются
#define STATUS_EAS_NOT_SUPPORTED         ((NTSTATUS)0xC000004FL)
// Структура EA повреждена
#define STATUS_EA_CORRUPT_ERROR          ((NTSTATUS)0xC0000053L)

Код ошибки STATUS_EAS_NOT_SUPPORTED может означать не только отсутствие поддержки расширенных атрибутов у файловой системы, но и невозможность использовать EA у конкретного файла. Если у файла есть reparse данные, то есть файл является точкой повторной обработки (например, симлинком), то у такого файла расширенные атрибуты не поддерживаются. В файловой системе NTFS файл не может одновременно являться и reparse point'ом, и содержать расширенные атрибуты EA, только что-то одно.

BOOL WriteEA(LPCSTR file_name, LPCSTR ascii_caps_name, 
            IN PVOID buf, UINT buf_len, UCHAR flags)
{
  IO_STATUS_BLOCK iosb;
  CHAR ea_buf[EA_BUF_LEN];
  PFILE_FULL_EA_INFORMATION ffei 
    = (PFILE_FULL_EA_INFORMATION) ea_buf;
  HANDLE FileHandle;  
  NTSTATUS ntst;
  
  if (buf_len > MAX_EA_DATA_LEN)
  {
    return FALSE;
  }

  ZeroMemory(&ea_buf, EA_BUF_LEN);
  FileHandle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE,
    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (INVALID_HANDLE_VALUE == FileHandle) 
  {
    return FALSE;
  }

  ffei->EaNameLength = strnlen(ascii_caps_name, MAX_EA_NAME_LEN);
  ffei->EaValueLength = buf_len;
  ffei->Flags = flags;

  memcpy(ffei->EaName, ascii_caps_name, ffei->EaNameLength);
  memcpy(ffei->EaName + ffei->EaNameLength + 1, 
    buf, ffei->EaValueLength);
    
  ntst = NtSetEaFile(FileHandle, &iosb, ffei, EA_BUF_LEN);

  return NT_SUCCESS(ntst);
}

Чтение расширенного атрибута

Для чтения расширенного атрибута следует получить EA-буфер полностью и найти в буфере ту структуру, которая содержит атрибут с искомым именем (EaName). Буфер читается с помощью функции NtQueryEaFile. Если параметр ReturnSingleEntry установлен в TRUE, функция с каждым вызовом возвращает только одну структуру, если FALSE — в буфер пишутся сразу все структуры, а переход между ними осуществляется через поле NextEntryOffset.

Данные читаются из буфера по смещению EaName + EaNameLength + 1. Параметр RestartScan перезапускает выдачу атрибутов сначала (если используется ReturnSingleEntry). Параметр EaList может содержать указатель на дополнительный список структур FILE_GET_EA_INFORMATION, содержащий список EA, которые нужно получить. Тем самым можно получить в буфере не все EA, а какой-то определённый набор. Параметр EaIndex позволяет обращаться к атрибуту по индексу.

В результате ошибки попытки чтения расширенных атрибутов, функция NtQueryEaFile может вернуть следующие коды ошибок:

// Доступ запрещён
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022)
// Файл не содержит расширенных атрибутов
#define STATUS_NO_EAS_ON_FILE ((NTSTATUS)0xC0000052)  
// Буфер слишком мал
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023)  
// Переполнение буфера
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005)

Код функции для чтения расширенных атрибутов:

BOOL ReadEA(LPCSTR file_name, LPCSTR ascii_caps_name, 
            OUT PVOID buf, OUT PUINT buf_len)
{
  IO_STATUS_BLOCK iosb;
  CHAR ea_buf[EA_BUF_LEN];
  PFILE_FULL_EA_INFORMATION ffei 
    = (PFILE_FULL_EA_INFORMATION) ea_buf;
  HANDLE FileHandle;
  NTSTATUS ntst;

  ZeroMemory(&ea_buf, EA_BUF_LEN);
  FileHandle = CreateFile(file_name, 
    GENERIC_READ | GENERIC_WRITE,
    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (INVALID_HANDLE_VALUE == FileHandle) 
  {
    return FALSE;
  }

  while (TRUE)
  {
    ntst =  NtQueryEaFile(FileHandle, &iosb, ffei,
      EA_BUF_LEN, TRUE, NULL, NULL, NULL, FALSE);    
    if (!NT_SUCCESS(ntst))
    {
      break;
    }
    if (0 == _strnicmp(ffei->EaName, ascii_caps_name, 
                      MAX_EA_NAME_LEN))
    {
      memcpy(buf, ffei->EaName + ffei->EaNameLength + 1, 
                  ffei->EaValueLength);
      *buf_len = ffei->EaValueLength;
      return TRUE;
    }
  }
  
  return FALSE;
}

Удаление расширенного атрибута

Удаление расширенного атрибута из файла реализуется через вызов NtSetEaFile, со структурой, в которой поле EaValueLength равно NULL, а EaName содержит только имя удаляемого атрибута.

// Удалить атрибут attribute из файла filename
WriteEA("filename", "attribute", NULL, 0, 0);

Исходный код

Скачать консольную программу EA.EXE для работы с расширенными атрибутами. В архиве содержится программа и её исходный код на Си.

Кроме того, моя программа NTFS Stream Explorer, которая имеет графический интерфейс, теперь тоже поддерживает редактирование расширенных атрибутов файлов.

система комментирования CACKLE

Автор: амдф
Дата: 10.02.2011


Разделы сайта
Главная
Блог
Native API
NTFS и ReFS
Микроконтроллеры
Справочник NTDLL
Коды NTSTATUS
Разное

Избранное
NTFS Stream Explorer
Native Shell
Тенгвар

Остальное
nvpnhcknn (архив)
English pages
Контакты

Ленты atom
Лента Atom сайта Лента Atom блога



При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт.