Файловые операции с использованием Native API

Использование функций Native API для выполнения операций над файлами




Библиотека ntdll.dll содержит все необходимые функции для выполнения операций над файлами и каталогами. Во всех файловых функциях Native API требуется передавать полный путь. Существует функция, помогающая хранить значение текущего каталога, но сцеплять это значение с именем файла нужно самостоятельно.

Функции для установки и получения текущего каталога определены так:

NTSYSAPI ULONG NTAPI RtlGetCurrentDirectory_U(
    ULONG MaximumLength,
    PWSTR Buffer
);

NTSYSAPI NTSTATUS NTAPI RtlSetCurrentDirectory_U(
    IN PUNICODE_STRING name
);

Для доступа к файлам и каталогам используется NT-формат пути. Это полный путь к файлу с буквой диска и префиксом \??\. Например, путь до файла C:\boot.ini будет выглядеть как \??\C:\boot.ini. Привычный формат пути без префикса называется в терминологии Native API «DOS-путь». Для конвертации пути из формата DOS в NT существует функция:

NTSYSAPI BOOLEAN NTAPI RtlDosPathNameToNtPathName_U(
    IN PCWSTR DosPathName,
    OUT PUNICODE_STRING NtPathName,
    OUT PCWSTR *NtFileNamePart,
    OUT CURDIR *DirectoryInfo
);

Целесообразно сохранять путь в DOS-формате. Его можно показывать без изменений пользователю, если он хочет увидеть текущий каталог. При каждой файловой операции придётся формировать полный NT-путь к файлу вызовом соответствующей функции, или прямой склейкой пути с префиксом.

Одна из операций, которая может понадобиться это вывод списка содержимого каталога. Чтобы его вывести, программа должна получить список файлов и каталогов текущей директории. Прежде всего, надо открыть каталог функцией NtCreateFile с опцией FILE_LIST_DIRECTORY и указанием флага FILE_DIRECTORY_FILE. Полученный хэндл скармливается функции NtQueryDirectoryFile, которой передается константа FileBothDirectoryInformation и указатель на буфер данных типа FILE_BOTH_DIR_INFORMATION. Структура этого типа позволяет узнать о файлах и каталогах все их важные параметры: имя, атрибуты, размер и время создания.

typedef struct _FILE_BOTH_DIR_INFORMATION
{
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaSize;
    CCHAR ShortNameLength;
    WCHAR ShortName[12];
    WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;

При вызове функции NtQueryDirectoryFile можно использовать параметр ReturnSingleEntry = TRUE, тогда за один вызов функции в буфер будет помещена только одна структура FILE_BOTH_DIR_INFORMATION, а вызвать функцию в цикле придётся столько раз, сколько файлов в каталоге. При установке того же параметра в FALSE функция будет вызвана всего один раз, а в буфере окажется массив структур. Перемещаться по нему можно, сдвигая указатель на структуру по смещению, указанному в поле NextEntryOffset. У последнего элемента массива значение этого поля будет NULL.

Функции для стандартных файловых операций, таких как чтение из файла, запись в файл и удаление файла документированы в MSDN. Их названия NtReadFile, NtWriteFile, NtDeleteFile и их использование мало чем отличается от привычных функций WinAPI. Открывать файл можно функцией NtCreateFile. Чтобы скопировать файл, нужно просто прочитать его из одного места и записать копию в другом месте. А вот переименование файла – более комплексная операция, поэтому стоит рассмотреть её более подробно.

Переименование файла

Существует функция NtSetInformationFile, которая может производить множество различных операций над файлом. Прототип функции выглядит так:

NTSYSCALLAPI NTSTATUS NTAPI NtSetInformationFile(
    IN HANDLE FileHandle,
    IN PIO_STATUS_BLOCK IoStatusBlock,
    IN PVOID FileInformation,
    IN ULONG Length,
    IN FILE_INFORMATION_CLASS FileInformationClass
);

В параметре FileInformationClass передаётся константа FileRenameInformation, означающая операцию переименования. В сочетании с этой константой, функция получает в параметре FileInformation указатель на структуру FILE_RENAME_INFORMATION.

typedef struct _FILE_RENAME_INFORMATION
{
    BOOLEAN ReplaceIfExists;
    HANDLE  RootDirectory;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;

Структура FILE_RENAME_INFORMATION имеет переменную длину, зависящую от длины нового имени файла. Нужно выделить для структуры достаточное количество памяти. Предположим, у тебя есть буфер NewFileName с новым именем файла и его размер в переменной FileNameSize.

PFILE_RENAME_INFORMATION FileRenameInfo;

FileRenameInfo = RtlAllocateHeap(RtlGetProcessHeap(),
  HEAP_ZERO_MEMORY, sizeof(FILE_RENAME_INFORMATION) + FileNameSize);

После выделения памяти следует скопировать буфер NewFileName в поле структуры FileName и проинициализировать другие её поля. Поле ReplaceIfExists определяет, заменять ли существующий файл, если его имя совпадает с новым именем файла при переименовании. В параметре RootDirectory может содержаться хэндл другой директории, в которой должен оказаться файл после перемещения. Проще оставить это поле равным NULL, ведь для перемещения файла в другой каталог достаточно указать в поле FileName полный путь к новому расположению файла в NT-формате. Если осуществляется переименование файла, а не перемещение, в FileName должно быть только имя файла. После инициализации структуры остается только вызвать функцию для осуществления операции:

Status = NtSetInformationFile(
  FileHandle,
  &IoStatusBlock,
  FileRenameInfo,
  sizeof(FILE_RENAME_INFORMATION)+ FileNameSize,
  FileRenameInformation 
);

Размер буфера FileRenameInfo нельзя считать равным sizeof(FILE_RENAME_INFORMATION), ведь в определении структуры не учтена переменная длина поля FileName. Поэтому в четвёртом параметре Length следует передать длину структуры, к которой прибавлен размер строки FileName.



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


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