Расширенные атрибуты файлов — дополнительный набор атрибутов, поддерживаемый 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
При копировании файла с расширенными атрибутами на раздел 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)
#define STATUS_EA_LIST_INCONSISTENT ((NTSTATUS)0x80000014L)
#define STATUS_EA_TOO_LARGE ((NTSTATUS)0xC0000050L)
#define STATUS_EAS_NOT_SUPPORTED ((NTSTATUS)0xC000004FL)
#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 содержит только имя удаляемого атрибута.
WriteEA("filename", "attribute", NULL, 0, 0);
Исходный код
Скачать консольную программу EA.EXE для работы с расширенными атрибутами.
В архиве содержится программа и её исходный код на Си.
|