/*****************************************************************************/ /* Page11Streams.cpp Copyright (c) Ladislav Zezula 2005 */ /*---------------------------------------------------------------------------*/ /* Description : */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 15.08.05 1.00 Lad The first version of Page11Streams.cpp */ /*****************************************************************************/ #include "FileTest.h" #include "resource.h" //----------------------------------------------------------------------------- // Local structures #define MAX_STREAM_LENGTH 0x10000000 #define STREAM_TYPE_NONE 0 #define STREAM_TYPE_ADS 1 #define STREAM_TYPE_EA 2 struct TFileStream { LIST_ENTRY Entry; LPTSTR szStreamName; // Pointer to stream name (variable length) LPBYTE pbStreamData; // Pointer to stream data (variable length) DWORD cbStreamData; // Length of the stream data DWORD dwStreamType; // Stream type BYTE Buffer[1]; }; //----------------------------------------------------------------------------- // Local variables static TListViewColumns Columns[] = { {IDS_STREAM_TYPE, 60}, {IDS_STREAM_NAME, 100}, {IDS_STREAM_DATA, -1}, {0, 0} }; static LPCTSTR StreamTypes[] = { _T("NONE"), _T("ADS"), _T("EA") }; static TAnchors * pAnchors = NULL; //----------------------------------------------------------------------------- // Local functions static LPTSTR StringFromAnsi(LPCSTR szStringA, ULONG cchLength) { LPTSTR szStringT; szStringT = new TCHAR[cchLength+1]; if(szStringT != NULL) { #ifdef _UNICODE MultiByteToWideChar(CP_ACP, 0, szStringA, cchLength, szStringT, cchLength); szStringT[cchLength] = 0; #else StringCchCopyA(szStringT, szStringA, cchLength); szStringT[cchLength] = 0; #endif } return szStringT; } static LPTSTR StringFromWide(LPCWSTR szStringW, ULONG cchLength) { LPTSTR szStringT; szStringT = new TCHAR[cchLength+1]; if(szStringT != NULL) { #ifdef _UNICODE StringCchCopyW(szStringT, cchLength+1, szStringW); szStringT[cchLength] = 0; #else WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, szStringW, cchLength, szStringT, cchLength, 0, 0); szStringT[cchLength] = 0; #endif } return szStringT; } /* static void NormalizeStreamName(LPTSTR szStreamName) { LPTSTR szSrc = szStreamName; LPTSTR szTrg = szStreamName; // Skip the initial ':' if(szSrc[0] == _T(':')) szSrc++; // Copy everything until a colon or end of string while(szSrc[0] != 0 && szSrc[0] != _T(':')) *szTrg++ = *szSrc++; *szTrg = 0; } */ static void ItemDataToString( LPTSTR szStreamData, size_t nMaxChars, LPBYTE pbStreamData, ULONG cbStreamData, NTSTATUS Status) { LPBYTE pbStreamDataEnd = pbStreamData + cbStreamData; LPTSTR szStreamDataEnd = szStreamData + nMaxChars; // Iterate over all data if(NT_SUCCESS(Status)) { // Preset with default string LoadString(g_hInst, IDS_EMPTY_STREAM, szStreamData, (int)nMaxChars); // Fill the stream data while(pbStreamData < pbStreamDataEnd) { // Is there enough space for the text data? if(szStreamData + 3 > szStreamDataEnd) break; // Print the data bytes StringCchPrintf(szStreamData, (szStreamDataEnd - szStreamData), _T("%02X "), (DWORD)pbStreamData[0]); szStreamData += 3; pbStreamData++; } } else { StringCchPrintf(szStreamData, nMaxChars, _T("Error %08X when reading data"), Status); } } static int InsertStreamToListView( HWND hListView, LPCTSTR szStreamName, LPBYTE pbStreamData, DWORD dwStreamType, ULONG cbStreamData, NTSTATUS Status = STATUS_SUCCESS) { LVITEM lvi; TCHAR szStreamData[0x100]; // Insert the item type ZeroMemory(&lvi, sizeof(LVITEM)); lvi.mask = LVIF_TEXT; lvi.iItem = 0x7FFFFFFF; lvi.iSubItem = 0; lvi.pszText = (LPTSTR)StreamTypes[dwStreamType]; lvi.iItem = ListView_InsertItem(hListView, &lvi); lvi.iSubItem++; // Insert the stream name lvi.pszText = (LPTSTR)((szStreamName[0] != 0) ? szStreamName : _T("")); ListView_SetItem(hListView, &lvi); lvi.iSubItem++; // Insert the item data ItemDataToString(szStreamData, _countof(szStreamData), pbStreamData, cbStreamData, Status); lvi.pszText = (LPTSTR)szStreamData; ListView_SetItem(hListView, &lvi); return lvi.iItem; } /* static TFileStream * CreateStream(PFILE_FULL_EA_INFORMATION pFileEa) { TFileStream * pStream; size_t nSize; ULONG i; // Allocate stream nSize = sizeof(TFileStream) + (pFileEa->EaNameLength + 1) * sizeof(TCHAR) + pFileEa->EaValueLength; pStream = (TFileStream *)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, nSize); if(pStream != NULL) { // Copy the stream name pStream->szStreamName = (LPTSTR)&pStream->Buffer; for(i = 0; i < pFileEa->EaNameLength; i++) pStream->szStreamName[i] = pFileEa->EaName[i]; pStream->szStreamName[i] = 0; // Copy the EA data memcpy(&pStream->szStreamName[i+1], pFileEa-> } } */ NTSTATUS NtCreateFileStream(PUNICODE_STRING SourceName, TFileStream * pStream) { OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStatus; UNICODE_STRING FileName; NTSTATUS Status; LPWSTR szStreamName; HANDLE FileHandle = NULL; // Create the full name of the stream FileName.MaximumLength = SourceName->MaximumLength; FileName.Length = SourceName->Length; FileName.Buffer = SourceName->Buffer; if(pStream->szStreamName[0] != 0) { szStreamName = (LPWSTR)((LPBYTE)FileName.Buffer + FileName.Length); StringCchPrintfW(szStreamName, (FileName.MaximumLength - FileName.Length) / sizeof(WCHAR), L":%s", pStream->szStreamName); FileName.Length = (USHORT)(wcslen(FileName.Buffer) * sizeof(WCHAR)); } // Open the stream InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateFile(&FileHandle, FILE_WRITE_DATA | SYNCHRONIZE, &ObjAttr, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0); // Write the stream data if(NT_SUCCESS(Status)) { Status = NtWriteFile(FileHandle, NULL, NULL, NULL, &IoStatus, pStream->pbStreamData, pStream->cbStreamData, NULL, NULL); } // Close the handle if(FileHandle != NULL) NtClose(FileHandle); return Status; } static NTSTATUS ConvertStreamsToEaList( PLIST_ENTRY pHeadEntry, PFILE_FULL_EA_INFORMATION * PtrFileEa, LPDWORD pcbFileEa) { PFILE_FULL_EA_INFORMATION pFileEa = NULL; PFILE_FULL_EA_INFORMATION pLastEa = NULL; PFILE_FULL_EA_INFORMATION pEa = NULL; TFileStream * pStream; PLIST_ENTRY pListEntry; DWORD cbEntryLength = 0; DWORD cbEaLength = 0; size_t i; // Calculate total size needed for holding all EAs for(pListEntry = pHeadEntry->Flink; pListEntry != pHeadEntry; pListEntry = pListEntry->Flink) { pStream = CONTAINING_RECORD(pListEntry, TFileStream, Entry); if(pStream->dwStreamType == STREAM_TYPE_EA) { cbEntryLength = (DWORD)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + _tcslen(pStream->szStreamName) + 1 + pStream->cbStreamData); cbEaLength = cbEaLength + ((cbEntryLength + 7) & ~7); } } // If there are no EAs, do nothing if(cbEaLength != 0) { // Allocate space for complete EAs pFileEa = pEa = (PFILE_FULL_EA_INFORMATION)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, cbEaLength); if(pFileEa == NULL) return STATUS_INSUFFICIENT_RESOURCES; cbEaLength = 0; // Parse the list again and store all EAs for(pListEntry = pHeadEntry->Flink; pListEntry != pHeadEntry; pListEntry = pListEntry->Flink) { // Get the entry and the next entry pStream = CONTAINING_RECORD(pListEntry, TFileStream, Entry); if(pStream->dwStreamType == STREAM_TYPE_EA) { // Convert to the EA pEa->NextEntryOffset = 0; pEa->Flags = 0; pEa->EaNameLength = (UCHAR)_tcslen(pStream->szStreamName); pEa->EaValueLength = (USHORT)pStream->cbStreamData; // Copy the file name for(i = 0; pStream->szStreamName[i] != 0; i++) pEa->EaName[i] = (CHAR)pStream->szStreamName[i]; pEa->EaName[i] = 0; // Copy the EA data memcpy(&pEa->EaName[i + 1], pStream->pbStreamData, pStream->cbStreamData); // Calculate the length of the entry cbEntryLength = GetEaEntrySize(pEa); cbEaLength += cbEntryLength; // If this is not the last entry, put the next entry offset pLastEa = pEa; pEa->NextEntryOffset = cbEntryLength; pEa = (PFILE_FULL_EA_INFORMATION)((LPBYTE)pEa + cbEntryLength); } } // Clear next entry offset for the last EA if(pLastEa != NULL) pLastEa->NextEntryOffset = 0; } // Give EA list and its size to the caller if(PtrFileEa != NULL) PtrFileEa[0] = pFileEa; if(pcbFileEa != NULL) pcbFileEa[0] = cbEaLength; return STATUS_SUCCESS; } static NTSTATUS LoadStreamFromFile( PUNICODE_STRING FileName, LPCWSTR szPlainName, TFileStream ** ppStream) { FILE_STANDARD_INFORMATION StdInfo = {0}; OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStatus; TFileStream * pStream = NULL; NTSTATUS Status; LPCTSTR szStreamType = NULL; LPCTSTR szStreamName = NULL; HANDLE FileHandle; DWORD dwStreamType = STREAM_TYPE_NONE; size_t cchStreamName = 0; size_t nSize; // Extract the stream type from the file name szStreamType = _tcschr(szPlainName, _T('#')); if(szStreamType == NULL) return STATUS_OBJECT_NAME_INVALID; szStreamType++; // Extract the stream name from the file name szStreamName = _tcschr(szStreamType, _T('#')); if(szStreamName == NULL) return STATUS_OBJECT_NAME_INVALID; szStreamName++; // Verify the stream types if(!_wcsnicmp(szStreamType, L"ADS", 3)) { dwStreamType = STREAM_TYPE_ADS; } else if(!_wcsnicmp(szStreamType, L"EA", 2)) { dwStreamType = STREAM_TYPE_EA; } else return STATUS_UNSUCCESSFUL; // Open the file and get its size InitializeObjectAttributes(&ObjAttr, FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&FileHandle, FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_ALERT); // Query the file size if(NT_SUCCESS(Status)) { Status = NtQueryInformationFile(FileHandle, &IoStatus, &StdInfo, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation); } // Check against the maximum stream size if(NT_SUCCESS(Status)) { if(StdInfo.EndOfFile.HighPart != 0 || StdInfo.EndOfFile.LowPart > MAX_STREAM_LENGTH) Status = STATUS_BAD_FILE_TYPE; } // Create new stream if(NT_SUCCESS(Status)) { cchStreamName = _tcslen(szStreamName) + 1; nSize = sizeof(TFileStream) + cchStreamName * sizeof(TCHAR) + StdInfo.EndOfFile.LowPart; pStream = (TFileStream *)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, nSize); if(pStream == NULL) Status = STATUS_INSUFFICIENT_RESOURCES; } // Fill the stream structure if(NT_SUCCESS(Status)) { pStream->szStreamName = (LPTSTR)(pStream + 1); StringCchCopy(pStream->szStreamName, cchStreamName, szStreamName); pStream->pbStreamData = (LPBYTE)(pStream->szStreamName + _tcslen(pStream->szStreamName) + 1); pStream->cbStreamData = StdInfo.EndOfFile.LowPart; pStream->dwStreamType = dwStreamType; Status = NtReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, pStream->pbStreamData, pStream->cbStreamData, NULL, NULL); } if(!NT_SUCCESS(Status)) { RtlFreeHeap(RtlProcessHeap(), 0, pStream); pStream = NULL; } if(FileHandle != NULL) NtClose(FileHandle); if(ppStream != NULL) *ppStream = pStream; return Status; } static NTSTATUS SaveStreamToFile( LPTSTR szMainFileName, LPCTSTR szStreamName, LPBYTE pbStreamData, DWORD dwStreamType, DWORD dwStreamIndex, DWORD cbStreamData) { OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStatus; UNICODE_STRING FileName; NTSTATUS Status; HANDLE FileHandle = NULL; TCHAR szFileName[MAX_PATH]; // Construct the new file name StringCchPrintf(szFileName, _countof(szFileName), _T("%s#%s%02u#%s"), szMainFileName, StreamTypes[dwStreamType], dwStreamIndex, szStreamName); Status = FileNameToUnicodeString(&FileName, szFileName); // Create the stream file if(NT_SUCCESS(Status)) { InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateFile(&FileHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjAttr, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_ALERT, NULL, 0); } // Write the data if(NT_SUCCESS(Status)) { Status = NtWriteFile(FileHandle, NULL, NULL, NULL, &IoStatus, pbStreamData, cbStreamData, NULL, NULL); } if(FileHandle != NULL) NtClose(FileHandle); return Status; } NTSTATUS LoadStreamsFromDirectory( PLIST_ENTRY pStreamLinks, PUNICODE_STRING SourceName) { PFILE_DIRECTORY_INFORMATION DirBuffer = NULL; PFILE_DIRECTORY_INFORMATION DirEntry = NULL; OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStatus; UNICODE_STRING FolderName = {0, 0, NULL}; UNICODE_STRING FileName; UNICODE_STRING FileMask; TFileStream * pStream; NTSTATUS Status = STATUS_SUCCESS; HANDLE DirHandle = NULL; LPWSTR szPlainName = NULL; ULONG Length = 0x8000; // Now get pointer to plain file name szPlainName = wcsrchr(SourceName->Buffer, L'\\'); if(szPlainName == NULL) Status = STATUS_OBJECT_PATH_SYNTAX_BAD; else szPlainName++; // Now get pointer to directory name and to file name if(NT_SUCCESS(Status)) { // Get the folder name FolderName.MaximumLength = SourceName->MaximumLength; FolderName.Length = (USHORT)((szPlainName - SourceName->Buffer) * sizeof(WCHAR)); FolderName.Buffer = SourceName->Buffer; // Get the file mask FileMask.MaximumLength = SourceName->MaximumLength - FolderName.Length; FileMask.Length = SourceName->Length - FolderName.Length + 4; FileMask.Buffer = SourceName->Buffer + (FolderName.Length / sizeof(WCHAR)); StringCchCat(szPlainName, SourceName->MaximumLength / sizeof(WCHAR), L"#*"); // Open the directory InitializeObjectAttributes(&ObjAttr, &FolderName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&DirHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_ALERT); } // Query the directory while(NT_SUCCESS(Status)) { // Allocate space for directory info DirBuffer = DirEntry = (PFILE_DIRECTORY_INFORMATION)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, Length); if(DirBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } // Query the directory Status = NtQueryDirectoryFile(DirHandle, NULL, NULL, NULL, &IoStatus, DirBuffer, Length, FileDirectoryInformation, FALSE, &FileMask, FALSE); if(NT_SUCCESS(Status)) break; // If the buffer is not enough, reallocate the buffer and do again if(Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) { Length = Length << 1; RtlFreeHeap(RtlProcessHeap(), 0, DirBuffer); DirBuffer = NULL; Status = STATUS_SUCCESS; } } // Now read each file if(NT_SUCCESS(Status)) { for(;;) { // Skip directories if((DirEntry->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // Construct the full file name for that directory entry FileName.MaximumLength = FolderName.MaximumLength; FileName.Length = (USHORT)(FolderName.Length + DirEntry->FileNameLength); FileName.Buffer = FolderName.Buffer; memcpy(szPlainName, DirEntry->FileName, DirEntry->FileNameLength); FileName.Buffer[FileName.Length / sizeof(WCHAR)] = 0; // Load that stream from the file Status = LoadStreamFromFile(&FileName, szPlainName, &pStream); if(!NT_SUCCESS(Status)) break; // Insert the stream from the list InsertTailList(pStreamLinks, &pStream->Entry); } // Move to the next directory entry if(DirEntry->NextEntryOffset == 0) break; DirEntry = (PFILE_DIRECTORY_INFORMATION)((LPBYTE)DirEntry + DirEntry->NextEntryOffset); } } if(NT_SUCCESS(Status)) { if(IsListEmpty(pStreamLinks)) Status = STATUS_NO_SUCH_FILE; } return Status; } static NTSTATUS EaListToListView( TFileTestData * pData, HWND hDlg, HWND hListView, PFILE_FULL_EA_INFORMATION EaBuffer, bool bExportAsWell) { NTSTATUS Status = STATUS_SUCCESS; LPTSTR szEaName; LPBYTE pbEaValue; DWORD dwStreamIndex = 1; // Iterate over all file EAs and show them/save them for(;;) { // Get the name and value from the EA szEaName = StringFromAnsi(EaBuffer->EaName, EaBuffer->EaNameLength); pbEaValue = (LPBYTE)(&EaBuffer->EaName[EaBuffer->EaNameLength + 1]); // Fill them into the list view InsertStreamToListView(hListView, szEaName, pbEaValue, STREAM_TYPE_EA, EaBuffer->EaValueLength); // If we shall also save to the file, do it if(bExportAsWell) { Status = SaveStreamToFile(pData->szFileName1, szEaName, pbEaValue, STREAM_TYPE_EA, dwStreamIndex++, EaBuffer->EaValueLength); if(!NT_SUCCESS(Status)) { MessageBoxError(hDlg, IDS_FAILED_TO_SAVE_FILE, RtlNtStatusToDosError(Status), _T("EA")); break; } } // Free the converted name delete [] szEaName; // Move to tne next entry if(EaBuffer->NextEntryOffset == 0) break; EaBuffer = (PFILE_FULL_EA_INFORMATION)((LPBYTE)EaBuffer + EaBuffer->NextEntryOffset); } return Status; } static NTSTATUS StreamsToListView( TFileTestData * pData, HWND hDlg, HWND hListView, PFILE_STREAM_INFORMATION StrmBuffer, bool bExportAsWell) { OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStatus; UNICODE_STRING FileName; NTSTATUS Status = STATUS_SUCCESS; LPCTSTR szFormat; TCHAR szFileName[MAX_PATH * 2]; HANDLE hFile; LPTSTR szStreamName; LPBYTE pbStreamData; DWORD dwFlagsAndAttributes = FILE_SYNCHRONOUS_IO_ALERT; DWORD cbStreamData; DWORD dwStreamIndex = 1; // // If the parent file was open with FILE_OPEN_REPARSE_POINT, // we need to propagate this flag to the streams too. // This allows us to open file created with alternate streams that are links to non existing file // // Example: mklink.exe C:\sample_file.exe:byebye \??\nul // dwFlagsAndAttributes |= (pData->OpenFile.dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT); dwFlagsAndAttributes |= (pData->OpenFile.dwCreateOptions & FILE_OPEN_REPARSE_POINT); // Iterate over all file streams and show them/save them for(;;) { // Get the name and value from the EA szStreamName = StringFromWide(StrmBuffer->StreamName, StrmBuffer->StreamNameLength / sizeof(WCHAR)); // NormalizeStreamName(szStreamName); // Get the stream size cbStreamData = StrmBuffer->StreamSize.LowPart; if(StrmBuffer->StreamSize.QuadPart > MAX_STREAM_LENGTH) cbStreamData = StrmBuffer->StreamSize.LowPart; // Allocate buffer for stream data pbStreamData = (LPBYTE)RtlAllocateHeap(RtlProcessHeap(), 0, cbStreamData); if(pbStreamData != NULL) { // Prepare the ADS name szFormat = (szStreamName[0] != 0) ? _T("%s%s") : _T("%s"); StringCchPrintf(szFileName, _countof(szFileName), szFormat, pData->szFileName1, szStreamName); Status = FileNameToUnicodeString(&FileName, szFileName); if(!NT_SUCCESS(Status)) break; // Open the ADS InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&hFile, FILE_READ_DATA | SYNCHRONIZE, &ObjAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, dwFlagsAndAttributes); if(NT_SUCCESS(Status)) { // Read the data from the stream Status = NtReadFile(hFile, NULL, NULL, NULL, &IoStatus, pbStreamData, cbStreamData, NULL, NULL); NtClose(hFile); } // Fill them into the list view InsertStreamToListView(hListView, szStreamName, pbStreamData, STREAM_TYPE_ADS, cbStreamData, Status); FreeFileNameString(&FileName); // If we shall also save to the file, do it if(NT_SUCCESS(Status) && bExportAsWell) { Status = SaveStreamToFile(pData->szFileName1, szStreamName, pbStreamData, STREAM_TYPE_ADS, dwStreamIndex++, cbStreamData); if(!NT_SUCCESS(Status)) { MessageBoxError(hDlg, IDS_FAILED_TO_SAVE_FILE, RtlNtStatusToDosError(Status), _T("ADS")); break; } } } // Free the converted name delete [] szStreamName; // Move to tne next entry if(StrmBuffer->NextEntryOffset == 0) break; StrmBuffer = (PFILE_STREAM_INFORMATION)((LPBYTE)StrmBuffer + StrmBuffer->NextEntryOffset); } return Status; } //----------------------------------------------------------------------------- // Message handlers static int OnInitDialog(HWND hDlg, LPARAM lParam) { PROPSHEETPAGE * pPage = (PROPSHEETPAGE *)lParam; TFileTestData * pData = (TFileTestData *)pPage->lParam; HWND hListView = GetDlgItem(hDlg, IDC_STREAMS_LIST); UNREFERENCED_PARAMETER(lParam); UNREFERENCED_PARAMETER(hDlg); SetDialogData(hDlg, pPage->lParam); // Initialize the listview ListView_CreateColumns(hListView, Columns); ListView_SetExtendedListViewStyle(hListView, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT); // Configure dialog resizing if(pData->bEnableResizing) { pAnchors = new TAnchors(hDlg); pAnchors->AddAnchor(hDlg, IDC_STREAMS_TITLE, akLeft | akTop | akRight); pAnchors->AddAnchor(hDlg, IDC_STREAMS_LIST, akAll); pAnchors->AddAnchor(hDlg, IDC_QUERY_STREAMS, akLeft | akBottom); pAnchors->AddAnchor(hDlg, IDC_EXPORT_STREAMS, akLeftCenter | akBottom); pAnchors->AddAnchor(hDlg, IDC_IMPORT_STREAMS, akRight | akBottom); pAnchors->AddAnchor(hDlg, IDC_RESULT_FRAME, akLeft | akRight | akBottom); pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE_TITLE, akLeft | akBottom); pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE, akLeft | akRight | akBottom); pAnchors->AddAnchor(hDlg, IDC_INFORMATION_TITLE, akLeft | akBottom); pAnchors->AddAnchor(hDlg, IDC_INFORMATION, akLeft | akRight | akBottom); } return TRUE; } static int OnSetActive(HWND hDlg) { TFileTestData * pData = GetDialogData(hDlg); BOOL bEnabled; if((pData->OpenFile.dwCreateOptions & FILE_OPEN_BY_FILE_ID) == 0) { SetDlgItemText(hDlg, IDC_FILE_NAME, pData->szFileName1); ConvertToWin32Name(hDlg, IDC_FILE_NAME); } // Enable/disable the buttons bEnabled = (IsHandleValid(pData->hFile)) ? TRUE : FALSE; EnableDlgItems(hDlg, bEnabled, IDC_QUERY_STREAMS, IDC_EXPORT_STREAMS, IDC_IMPORT_STREAMS, 0); bEnabled = (IsHandleInvalid(pData->hFile) && pData->szFileName1[0] != 0) ? TRUE : FALSE; EnableDlgItems(hDlg, bEnabled, IDC_IMPORT_STREAMS, 0); return TRUE; } static int OnExportStreams(HWND hDlg, bool bExportAsWell) { PFILE_FULL_EA_INFORMATION EaBuffer = NULL; PFILE_STREAM_INFORMATION StrmBuffer = NULL; FILE_EA_INFORMATION FileEaInfo; TFileTestData * pData = GetDialogData(hDlg); IO_STATUS_BLOCK IoStatus = {0}; NTSTATUS Status; ULONG StrmBufferSize = sizeof(FILE_STREAM_INFORMATION); ULONG EaBufferSize; HWND hListView = GetDlgItem(hDlg, IDC_STREAMS_LIST); // Delete all items from the list view ListView_DeleteAllItems(hListView); // Retrieve the initial guess of the EAs on the file Status = NtQueryInformationFile(pData->hFile, &IoStatus, &FileEaInfo, sizeof(FILE_EA_INFORMATION), FileEaInformation); EaBufferSize = FileEaInfo.EaSize; // Query the extended attributes file while(Status == STATUS_SUCCESS) { // Allocate buffer for extended attributes EaBuffer = (PFILE_FULL_EA_INFORMATION)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, EaBufferSize); if(EaBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } // Try to query the extended attributes Status = NtQueryEaFile(pData->hFile, &IoStatus, EaBuffer, EaBufferSize, FALSE, NULL, 0, NULL, TRUE); if(NT_SUCCESS(Status) || Status == STATUS_NO_EAS_ON_FILE) break; // If the buffer is not enough, reallocate the buffer and do again if(Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) { EaBufferSize = EaBufferSize << 1; RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer); EaBuffer = NULL; Status = STATUS_SUCCESS; } } // Put the EAs in the list view if(NT_SUCCESS(Status) && EaBuffer != NULL) EaListToListView(pData, hDlg, hListView, EaBuffer, bExportAsWell); Status = STATUS_SUCCESS; // Now query the stream list while(Status == STATUS_SUCCESS) { // Allocate buffer for extended attributes StrmBuffer = (PFILE_STREAM_INFORMATION)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, StrmBufferSize); if(StrmBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } // Try to query the extended attributes Status = NtQueryInformationFile(pData->hFile, &IoStatus, StrmBuffer, StrmBufferSize, FileStreamInformation); if(NT_SUCCESS(Status)) break; // If the buffer is not enough, reallocate the buffer and do again if(Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) { StrmBufferSize = StrmBufferSize << 1; RtlFreeHeap(RtlProcessHeap(), 0, StrmBuffer); StrmBuffer = NULL; Status = STATUS_SUCCESS; } } // If succeeded, we insert the stream list to the list view if(NT_SUCCESS(Status) && StrmBuffer != NULL) StreamsToListView(pData, hDlg, hListView, StrmBuffer, bExportAsWell); // Set the status info SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status); // Free buffers if(StrmBuffer != NULL) RtlFreeHeap(RtlProcessHeap(), 0, StrmBuffer); if(EaBuffer != NULL) RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer); return TRUE; } static int OnImportStreams(HWND hDlg) { PFILE_FULL_EA_INFORMATION pFileEa = NULL; OBJECT_ATTRIBUTES ObjAttr = {0}; IO_STATUS_BLOCK IoStatus; TFileTestData * pData = GetDialogData(hDlg); UNICODE_STRING FileName; TFileStream * pStream; PLIST_ENTRY pHeadEntry; PLIST_ENTRY pListEntry; LIST_ENTRY StreamLinks; NTSTATUS Status = STATUS_SUCCESS; HANDLE FileHandle = NULL; DWORD cbFileEa = 0; HWND hListView = GetDlgItem(hDlg, IDC_STREAMS_LIST); DWORD dwErrCode = ERROR_SUCCESS; // Delete all items from the list view ListView_DeleteAllItems(hListView); InitializeListHead(&StreamLinks); pHeadEntry = &StreamLinks; // Prepare the file name InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = FileNameToUnicodeString(&FileName, pData->szFileName1); // Now reallocate the buffer so we have enough space if(NT_SUCCESS(Status)) { PWSTR NewBuffer; ULONG NewMaxLengh = (ULONG)FileName.MaximumLength + 255; // Don't exceet the maximum file name length if(NewMaxLengh > 0xFFFE) NewMaxLengh = 0xFFFE; NewBuffer = (PWSTR)RtlReAllocateHeap(RtlProcessHeap(), 0, FileName.Buffer, NewMaxLengh); // If the allocation failed, we need to free the old buffer if(NewBuffer != NULL) { FileName.MaximumLength = (USHORT)NewMaxLengh; FileName.Buffer = NewBuffer; } else { RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer); Status = STATUS_INSUFFICIENT_RESOURCES; FileName.Buffer = NULL; } } // Query the directory and load all streams from all files if(NT_SUCCESS(Status)) Status = LoadStreamsFromDirectory(&StreamLinks, &FileName); // Delete the file if(NT_SUCCESS(Status)) { Status = NtDeleteFile(&ObjAttr); if(Status == STATUS_OBJECT_NAME_NOT_FOUND || dwErrCode == STATUS_OBJECT_PATH_INVALID) Status = STATUS_SUCCESS; } // Now we have to create the main data stream if(NT_SUCCESS(Status)) { // Write all ADSs to the file for(pListEntry = pHeadEntry->Flink; pListEntry != pHeadEntry; pListEntry = pListEntry->Flink) { // Get the file stream pStream = CONTAINING_RECORD(pListEntry, TFileStream, Entry); // Write the entry. After this, the entry no longer exists!!! if(pStream->dwStreamType == STREAM_TYPE_ADS) { Status = NtCreateFileStream(&FileName, pStream); if(!NT_SUCCESS(Status)) break; } } } // Now open the file if(NT_SUCCESS(Status)) { Status = NtCreateFile(&FileHandle, FILE_WRITE_EA | SYNCHRONIZE, &ObjAttr, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_ALERT, NULL, 0); } // Convert the remaining streams to list of extended attributes if(NT_SUCCESS(Status)) { Status = ConvertStreamsToEaList(&StreamLinks, &pFileEa, &cbFileEa); } // Now apply the EAs to the file if(NT_SUCCESS(Status) && pFileEa && cbFileEa) { Status = NtSetEaFile(FileHandle, &IoStatus, pFileEa, cbFileEa); } // Set the streams to the list view while(!IsListEmpty(&StreamLinks)) { // Get the file stream pStream = CONTAINING_RECORD(StreamLinks.Flink, TFileStream, Entry); // Insert the stream to the listview if(dwErrCode == ERROR_SUCCESS) { InsertStreamToListView(hListView, pStream->szStreamName, pStream->pbStreamData, pStream->dwStreamType, pStream->cbStreamData); } // Free the stream structure RemoveEntryList(&pStream->Entry); RtlFreeHeap(RtlProcessHeap(), 0, pStream); } // Close the file if(FileHandle != NULL) NtClose(FileHandle); FreeFileNameString(&FileName); // Set the result of the operation SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status); return TRUE; } static int OnCommand(HWND hDlg, UINT nNotify, UINT nIDCtrl) { if(nNotify == BN_CLICKED) { switch(nIDCtrl) { case IDC_QUERY_STREAMS: return OnExportStreams(hDlg, false); case IDC_EXPORT_STREAMS: return OnExportStreams(hDlg, true); case IDC_IMPORT_STREAMS: return OnImportStreams(hDlg); } } return FALSE; } static int OnNotify(HWND hDlg, NMHDR * pNMHDR) { switch(pNMHDR->code) { case PSN_SETACTIVE: return OnSetActive(hDlg); } return FALSE; } //----------------------------------------------------------------------------- // Public functions INT_PTR CALLBACK PageProc11(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { // Handlers specific to our dialog switch(uMsg) { case WM_INITDIALOG: OnInitDialog(hDlg, lParam); return TRUE; case WM_SIZE: case WM_GETMINMAXINFO: return pAnchors->OnMessage(uMsg, wParam, lParam); case WM_COMMAND: return OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam)); case WM_NOTIFY: return OnNotify(hDlg, (NMHDR *)lParam); case WM_DESTROY: if(pAnchors != NULL) delete pAnchors; pAnchors = NULL; return FALSE; } return FALSE; }