Как стать автором
Обновить

Работа с альтернативными потоками данных через WinAPI

Время на прочтение 5 мин
Количество просмотров 11K
Автор оригинала: FlexHex
В прошлой статье я описал, что такое альтернативные потоки и как их можно использовать. Там были примеры работы с ними через командную строку, но можно делать тоже самое и программно стандартными средствами WinAPI.
Ниже дан свободный перевод части статьи, касающейся данного вопроса.

Проверка типа FS


Первым делом нужно проверить, поддерживает ли файловая система альтернативные потоки, иначе пытаться работать с ними нет смысла.
char szVolName[MAX_PATH], szFSName[MAX_PATH];
DWORD dwSN, dwMaxLen, dwVolFlags;
::GetVolumeInformation("C:\\", szVolName, MAX_PATH, &dwSN,
                      &dwMaxLen, &dwVolFlags, szFSName, MAX_PATH);

if (dwVolFlags & FILE_NAMED_STREAMS)
{
// File system supports named streams
}
else
{
// Named streams are not supported
}


* This source code was highlighted with Source Code Highlighter.

Создание альтернативного потока


Как и обычный файл, альтернативный поток можно создать при помощи функции CreateFile:
HANDLE hFile = ::CreateFile("file.dat:alt", ...

Если файл не существовал, то создастся файл нулевой длины с указанным именем основного потока.

Удаление альтернативного потока


Удаление альтернативного потока не сложней удаления обычного файла. Для этого подходит функция DeleteFile, полностью поддерживающая потоки:
::DeleteFile("file.dat:alt");

При удалении стандартного потока, удалятся также и все его альтернативные.

Копирование альтернативного потока


Для копирования альтернативных потоков вы можете использовать Win32 API функции CopyFile / CopyFileEx. Однако эти функции могут дать неожиданный результат. Могут быть два варианта копирования.

Стандартный поток в стандартный поток: обычная файловая операция, при этом все именованные потоки копируются вместе с файлом. Если файл существовал то он заменяется.

Именованный поток в стандартный поток: в данном случае копируется только выбранный поток. Если файл назначения существует, то он будет удален со всеми потоками (если есть), и будет создан новый файл лишь со стандартным потоком, в который и будет происходить копирование.

Копирование в простом цикле  чтение/запись дает ожидаемый результат:
HANDLE hInFile = ::CreateFile(szFromStream, GENERIC_READ, FILE_SHARE_READ, NULL,
                             OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
HANDLE hOutFile = ::CreateFile(szToStream, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                              CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

BYTE buf[64*1024];
DWORD dwBytesRead, dwBytesWritten;

do
{
    ::ReadFile(hInFile, buf, sizeof(buf), &dwBytesRead, NULL);
    if (dwBytesRead) ::WriteFile(hOutFile, buf, dwBytesRead, &dwBytesWritten, NULL);
}
while (dwBytesRead == sizeof(buf));

::CloseHandle(hInFile);
::CloseHandle(hOutFile);


* This source code was highlighted with Source Code Highlighter.

Список потоков


Получить список потоков можно используя некоторые функции Native API. Ниже будет приведен код получения списка потоков из файла:
// Open a file and obtain stream information

BYTE InfoBlock[64 * 1024]; // Buffer must be large enough
PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;
IO_STATUS_BLOCK ioStatus;

HANDLE hFile = ::CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, OPEN_EXISTING, 0, NULL);
NtQueryInformationFile(hFile, &ioStatus, InfoBlock,
                      sizeof(InfoBlock), FileStreamInformation);
::CloseHandle(hFile);


* This source code was highlighted with Source Code Highlighter.

Посложней обстоит дело с получение списка потоков директории:
// Open a directory and obtain stream information

// Obtain backup privilege in case we don't have it
HANDLE hToken;
TOKEN_PRIVILEGES tp;
::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
::LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tp.Privileges[0].Luid);
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
::CloseHandle(hToken);

HANDLE hFile = ::CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

BYTE InfoBlock[64 * 1024]; // Buffer must be large enough
PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;
IO_STATUS_BLOCK ioStatus;

pStreamInfo->StreamNameLength = 0; // Zero in this field means empty info block
NtQueryInformationFile(hFile, &ioStatus, InfoBlock,
                      sizeof(InfoBlock), FileStreamInformation);
::CloseHandle(hFile);


* This source code was highlighted with Source Code Highlighter.

Теперь получив информацию о файле/директории можем вывывести собственно сам список:
WCHAR wszStreamName[MAX_PATH];

for (;;)
{
// Check if stream info block is empty (directory may have no stream)
    if (pStreamInfo->StreamNameLength == 0) break; // No stream found

// Get null-terminated stream name
    memcpy(wszStreamName, pStreamInfo->StreamName, pStreamInfo->StreamNameLength);
    wszStreamName[pStreamInfo->StreamNameLength / sizeof(WCHAR)] = L'\0';

    print("%S", wszStreamName);

    if (pStreamInfo->NextEntryOffset == 0) break// No more stream records
    pStreamInfo = (PFILE_STREAM_INFORMATION)
                 ((LPBYTE)pStreamInfo + pStreamInfo->NextEntryOffset);  // Next stream record
}


* This source code was highlighted with Source Code Highlighter.


Примеры на VC++ консольных программ
Теги:
Хабы:
+22
Комментарии 11
Комментарии Комментарии 11

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн