Pull to refresh

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

Development for Windows
Translation
Original author: 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++ консольных программ
Tags:альтернативные потоки данныхaltdsWinAPIwindows
Hubs: Development for Windows
Total votes 24: ↑23 and ↓1 +22
Views4.8K

Popular right now

Data Engineer / Инженер данных
from 100,000 to 150,000 ₽AnyclassМоскваRemote job
Automation Engineer (Remote work)
from 1,500 to 1,500 €Cyntegrity Germany GmbHТомскRemote job
Senior/Middle+ .Net разработчик
from 3,000 $ArtezioМогилев
Machine Learning Engineer - NLP
from 1,500 to 2,500 €InsideDNARemote job

Top of the last 24 hours