Win32 API - Trying to readfile, it's getting truncated -- why? - c

I am trying to read a file and then display the file in ASCII or HEX into hEdit. Eventually I will be running other computations on the file info but right now I just want to see it all.
Currently the code displays the first bit - "MZ" - but thats it. Somehow I am accidentally truncating the pszFileText variable, I want to be able to view the entire executable in my window.
BOOL ReadInEXEFile(HWND hEdit, LPCTSTR pszFileName)
{
HANDLE hFile;
BOOL bSuccess = FALSE;
hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize;
dwFileSize = GetFileSize(hFile, NULL);
if(dwFileSize != 0xFFFFFFFF)
{
LPSTR pszFileText;
pszFileText = GlobalAlloc(GPTR, dwFileSize + 1);
if(pszFileText != NULL)
{
DWORD dwRead;
if(ReadFile(hFile, pszFileText, dwFileSize, &dwRead, NULL))
{
pszFileText[dwFileSize] = 0; // Add null terminator
if(SetWindowText(hEdit, pszFileText))
{
bSuccess = TRUE; // It worked!
}
}
GlobalFree(pszFileText);
}
}
CloseHandle(hFile);
}
return bSuccess;
}

EXE files are binary, but you are you trying to display the raw binary data as-is, which will not work. You were on the right track thinking that you need to encode the binary data to hex before displaying it. Binary data is not displayable, but hex is.
Try this instead:
static const TCHAR Hex[] = TEXT("0123456789ABCDEF");
BOOL ReadInEXEFile(HWND hEdit, LPCTSTR pszFileName)
{
BOOL bSuccess = FALSE;
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize != INVALID_FILE_SIZE)
{
LPTSTR pszFileText = (LPTSTR) LocalAlloc(LMEM_FIXED, ((dwFileSize * 3) + 1) * sizeof(TCHAR));
if (pszFileText != NULL)
{
BYTE buffer[1024];
DWORD dwOffset = 0;
DWORD dwRead;
for (DWORD dwFilePos = 0; dwFilePos < dwFileSize; dwFilePos += dwRead)
{
if (!ReadFile(hFile, buffer, sizeof(buffer), &dwRead, NULL))
{
CloseHandle(hFile);
return FALSE;
}
if (dwRead == 0)
break;
for (DWORD idx = 0; idx < dwRead; ++idx)
{
pszFileText[dwOffset++] = Hex[(buffer[idx] & 0xF0) >> 4];
pszFileText[dwOffset++] = Hex[buffer[idx] & 0x0F];
pszFileText[dwOffset++] = TEXT(' ');
}
}
pszFileText[dwOffset] = 0; // Add null terminator
bSuccess = SetWindowText(hEdit, pszFileText);
LocalFree(pszFileText);
}
}
CloseHandle(hFile);
}
return bSuccess;
}

Two reasons:
1) If you are reading an executable, the third byte is likely to be a zero, which might terminate the string, even though you are passing the length to SetWindowText.
2) This line is wrong: pszFileText[dwFileSize + 1] = 0;. It should be pszFileText[dwFileSize] = 0;. You are writing a zero byte some place wrong, there's no telling what that might be doing.

The way you are displaying it probably depends on the data being a NUL-terminated string, and binary data has embedded NULs in it, so you only display the data up to the first NUL.
You will need to print it by yourself and use the length of the data to know how much to print instead of depending on it being a NUL-terminated C-string.

Related

Unusual characters while printing after using ReadFile

I have a simple text file that has "SakyLabs. Hello, MDE", and I am trying to reading it using ReadFile function and then printing out, but I am getting:
?SakyLabs. Hello, MDE??²
What am I doing wrong?
Here's the code:
// For CreateFileW
HANDLE createFile;
DWORD fileDesiredAccess = GENERIC_READ | GENERIC_WRITE;
DWORD fileShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD creationDisposition = CREATE_ALWAYS;
DWORD fileFlags = FILE_ATTRIBUTE_NORMAL;
// ReadFile function
BOOL readFile;
DWORD lastError;
DWORD fileSize;
LPWSTR fileBuffer;
DWORD bytesRead;
if ((wcscmp(argv[1], L"-ef") == 0))
{
createFile = CreateFileW(argv[2], fileDesiredAccess, fileShareMode, NULL, OPEN_EXISTING, fileFlags, NULL);
// To get extended error message
lastError = GetLastError();
if (createFile == INVALID_HANDLE_VALUE)
{
wprintf(L"Error %d opening the file: ", lastError);
ShowErrorMessage(lastError);
return;
}
// File was opened! Let's read it's content
fileSize = GetFileSize(createFile, NULL);
fileBuffer = (LPWSTR)malloc((fileSize) + 1);
if (fileBuffer == NULL)
{
wprintf(L"Could not allocated memory.\n");
CloseHandle(createFile);
return;
}
readFile = ReadFile(createFile, fileBuffer, fileSize, &bytesRead, NULL);
lastError = GetLastError();
if (!readFile)
{
wprintf(L"Error code %d reading the file: ", lastError);
ShowErrorMessage(lastError);
CloseHandle(createFile);
return;
}
fileBuffer[fileSize] = '\0';
// Show the content
wprintf(L"%s \n", fileBuffer);
free(fileBuffer);
CloseHandle(createFile);
}
return 0;
}
This line is wrong:
fileBuffer[fileSize] = '\0';
fileSize is expressed in raw bytes, but fileBuffer is an array of WCHAR elements, where WCHAR is 2 bytes in size. As such, you end up writing way past the end of the array into surrounding memory, which is undefined behavior.
Put another way, your file is 40 bytes in size, and you are allocating 40+1 bytes for your array. That array can hold a max of 20 WCHARs, which means its valid indexes are 0..19, but since you are using fileSize as an index, you are writing the '\0' to the WCHAR located at index 40.
To fix that, you need to change this:
fileBuffer = (LPWSTR)malloc((fileSize) + 1);
...
fileBuffer[fileSize] = '\0';
To this instead:
fileBuffer = (LPWSTR)malloc(fileSize + sizeof(WCHAR));
...
fileBuffer[fileSize / sizeof(WCHAR)] = L'\0';
Now it will write the L'\0' into index 20, which is the array element that +sizeof(WCHAR) is allocating space for.
Alternatively, you don't have to null-terminate the array at all, if you instead tell wprintf() how many WCHARs to print, eg:
wprintf(L"%.*s \n", (int)(fileSize / sizeof(WCHAR)), fileBuffer);
On a side note -
Your code is not checking if GetFileSize() failed before using fileSize.
Also, when calling ReadFile(), fileSize is the requested number of bytes to read, but bytesRead receives the number of bytes actually read. ReadFile() can return fewer bytes than requested. So, you should use bytesRead instead of fileSize when terminating/printing fileBuffer. Otherwise, call ReadFile() in a loop to ensure all of the required bytes are actually read.
And, you are leaking fileBuffer if ReadFile() fails.
1.You should initialize the buffer fileBuffer.
2.fileBuffer size should be fileSize+2.
3.FF FE is the leading BOM.
If the 16-bit units use little-endian order ("UTF-16LE"), the BOM is
the (hexadecimal) byte sequence FF FE
Update the revised code:
#include<windows.h>
#include<wchar.h>
int main()
{
// For CreateFileW
HANDLE createFile;
DWORD fileDesiredAccess = GENERIC_READ | GENERIC_WRITE;
DWORD fileShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD creationDisposition = CREATE_ALWAYS;
DWORD fileFlags = FILE_ATTRIBUTE_NORMAL;
// ReadFile function
BOOL readFile;
DWORD lastError;
DWORD fileSize;
LPWSTR fileBuffer;
DWORD bytesRead;
{
createFile = CreateFileW(L"Test.txt", fileDesiredAccess, fileShareMode, NULL, OPEN_EXISTING, fileFlags, NULL);
// To get extended error message
lastError = GetLastError();
if (createFile == INVALID_HANDLE_VALUE)
{
wprintf(L"Error %d opening the file: ", lastError);
return 0;
}
// File was opened! Let's read it's content
fileSize = GetFileSize(createFile, NULL);
fileBuffer = (LPWSTR)malloc((fileSize)+2);
if (fileBuffer == NULL)
{
wprintf(L"Could not allocated memory.\n");
CloseHandle(createFile);
return 0;
}
else
{
memset(fileBuffer,0, (fileSize)+2);
}
readFile = ReadFile(createFile, fileBuffer, fileSize, &bytesRead, NULL);
lastError = GetLastError();
if (!readFile)
{
wprintf(L"Error code %d reading the file: ", lastError);
CloseHandle(createFile);
return 0;
}
//fileBuffer[fileSize] = '\0';
// Show the content
wprintf(L"%s \n", fileBuffer+1);
free(fileBuffer);
CloseHandle(createFile);
}
return 0;
}

GetFileAttributesW causes Access violation reading location x (GetLastError returns 8)

I am currently trying to implement a program that loads multiple files into memory, for further processing. To see if the file exists I made a function that uses GetFileAttributesW to check if the file indeed exists.
The first file gets loaded correctly, but once I try to load the second file, I get the access violation (GetLastError returns 8; ERROR_NOT_ENOUGH_MEMORY). I can safely rule out that I don't have enough RAM, as the files are max. 500kb in size and I am never loading more than 20 (I have 16GB of RAM).
I also have rights to access he file, etc.
inline BOOL FileExists(const TCHAR* szPath)
{
DWORD dwAttrib = GetFileAttributesW(szPath); // ERROR here (1st iteration everything is fine)
return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
.
.
.
FILE_DATA LoadFileIntoMemory(const TCHAR* FileName)
{
PTCHAR FinalPath = VirtualAlloc(NULL, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
PTCHAR FilePath = L"C:\\Users\\invasi0nZ\\"; //
concat(FinalPath, FilePath, MAX_PATH);
concat(FinalPath, FileName, MAX_PATH);
if (!FileExists(FinalPath))
{
memset(FinalPath, 0, MAX_PATH);
FilePath = L"C:\\Users\\invasi0nZ\\Documents\\";
concat(FinalPath, FilePath, MAX_PATH);
concat(FinalPath, FileName, MAX_PATH);
}
HANDLE File = CreateFileW(FinalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (File == INVALID_HANDLE_VALUE)
{
return (FILE_DATA){ NULL, NULL };
}
int FileSize = GetFileSize(File, NULL);
PBYTE RawFile = VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
DWORD dwRead;
size_t FileSize = GetFileSize(File, NULL);
ReadFile(File, RawFile, FileSize, &dwRead, NULL);
CloseHandle(File);
VirtualFree(FinalPath, sizeof(FinalPath), MEM_FREE);
return (FILE_DATA) { RawFile, FileSize };
}
.
.
.
void LoadAllFiles(Array FileNames)
{
for (int i = 0; i < FileNames.used; i++)
{
FILE_DATA file_data = LoadFileIntoMemory(FileNames.array[i].file_name);
// Store file_data, etc.
}
// Do stuff with files here
}
As far as I see I am closing all required handles and freeing everything that I can without breaking the program.
Thank you very much in advance!
You are not allocating enough memory with VirtualAlloc(), thus your concat() function can cause a buffer overflow. Since you are compiling with UNICODE enabled, TCHAR is wchar_t, which is 2 bytes in size. You need to take that size into account when allocating memory for FinalPath, as well as when clearing FinalPath with memset().
Change this:
PTCHAR FinalPath = VirtualAlloc(NULL, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
...
memset(FinalPath, 0, MAX_PATH);
To this:
PTCHAR FinalPath = VirtualAlloc(NULL, sizeof(TCHAR) * MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
...
memset(FinalPath, 0, sizeof(TCHAR) * MAX_PATH);
There are other problems with your code, too. You are not checking if VirtualAlloc() is successful before using the returned pointer. You are leaking the allocated memory if CreateFileW() fails. And you are declaring FileSize twice.
Try this instead:
FILE_DATA LoadFileIntoMemory(const TCHAR* FileName)
{
PTCHAR FinalPath = VirtualAlloc(NULL, sizeof(TCHAR) * MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!FinalPath)
return (FILE_DATA){ NULL, 0 };
PTCHAR FilePath = TEXT("C:\\Users\\invasi0nZ\\"); //
concat(FinalPath, FilePath, MAX_PATH);
concat(FinalPath, FileName, MAX_PATH);
if (!FileExists(FinalPath))
{
memset(FinalPath, 0, sizeof(TCHAR) * MAX_PATH);
FilePath = TEXT("C:\\Users\\invasi0nZ\\Documents\\");
concat(FinalPath, FilePath, MAX_PATH);
concat(FinalPath, FileName, MAX_PATH);
}
HANDLE File = CreateFile(FinalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (File == INVALID_HANDLE_VALUE)
{
VirtualFree(FinalPath, 0, MEM_RELEASE);
return (FILE_DATA){ NULL, 0 };
}
VirtualFree(FinalPath, 0, MEM_RELEASE);
DWORD FileSize = GetFileSize(File, NULL);
if (FileSize == INVALID_FILE_SIZE)
{
CloseHandle(File);
return (FILE_DATA){ NULL, 0 };
}
PBYTE RawFile = VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!RawFile)
{
CloseHandle(File);
return (FILE_DATA){ NULL, 0 };
}
DWORD dwRead;
if (!ReadFile(File, RawFile, FileSize, &dwRead, NULL))
{
VirtualFree(RawFile, 0, MEM_RELEASE);
RawFile = NULL;
dwRead = 0;
}
CloseHandle(File);
return (FILE_DATA) { RawFile, dwRead };
}
That being said, you don't really need FileExists() at all, as CreateFile() can tell you whether the file exists or not, thus avoiding a race condition in your code if another process creates/deletes the file after you check for its existence but before you open it.
You should also get rid of VirtualAlloc() for FinalPath, you don't need to allocate that string dynamically. Just declare the array statically instead.
And the Win32 API has functions for concatenating path segments together, so you don't need to write your own. It also has functions for querying the paths of the user's profile and Documents folders, you should not hard-code those paths.
Try something more like this instead:
#include <windows.h>
#include <pathcch.h>
#include <shlobj.h>
HANDLE OpenFileInFolder(CSIDL FolderID, const TCHAR *FileName)
{
TCHAR FilePath[MAX_PATH];
HRESULT Res = SHGetFolderPath(NULL, FolderID, NULL, SHGFP_TYPE_CURRENT, FilePath);
if (Res != S_OK)
{
SetLastError(Res);
return INVALID_HANDLE_VALUE;
}
Res = PathCchCombine(FilePath, MAX_PATH, FilePath, FileName);
if (Res != S_OK)
{
SetLastError(Res);
return INVALID_HANDLE_VALUE;
}
return CreateFile(FilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
FILE_DATA LoadFileIntoMemory(const TCHAR* FileName)
{
HANDLE File = OpenFileInFolder(CSIDL_PROFILE, FileName);
if ((File == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_FILE_NOT_FOUND))
File = OpenFileInFolder(CSIDL_MYDOCUMENTS, FileName);
if (File == INVALID_HANDLE_VALUE)
return (FILE_DATA){ NULL, 0 };
DWORD FileSize = GetFileSize(File, NULL);
if (FileSize == INVALID_FILE_SIZE)
{
CloseHandle(File);
return (FILE_DATA){ NULL, 0 };
}
PBYTE RawFile = VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!RawFile)
{
CloseHandle(File);
return (FILE_DATA){ NULL, 0 };
}
DWORD dwRead;
if (!ReadFile(File, RawFile, FileSize, &dwRead, NULL))
{
VirtualFree(RawFile, 0, MEM_RELEASE);
RawFile = NULL;
dwRead = 0;
}
CloseHandle(File);
return (FILE_DATA) { RawFile, dwRead };
}

Launching an APP from volume ID

I'm trying to launch an .exe file in windows using and a volume ID. The volume do not have a letter. Here is what I've tryed.
ShellExecute
When I call ShellExecute with the volume ID, Windows' explorer open the correct directory. Same thing if I try to open a folder inside the volume. However, if I try to open an exe file, nothing happen.
This is the way I call ShellExecute, which is part of windows.h :
char path[MAX_PATH] = R"(\\?\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\App.exe)";
LONG_PTR returnValue = (LONG_PTR) ShellExecute(GetDesktopWindow(),NULL, path,NULL,NULL,SW_SHOW);
The error code returned is 2 :
The system cannot find the file specified.
CreateProcessW
After following the comments, I am now using . My new code look like this :
char path[MAX_PATH] = R"(\\?\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\launcher.exe)";
STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcessW(path, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
else {
DWORD dw = GetLastError();
printf("ERROR CODE: %d", dw);
}
But it still doesn't work. Error is the same (2).
Changing the path notation
From "\\?\Volume{ID}" to "\\.\Volume{ID}".
The strange thing is that I can list the drive's files or start a console process but I cannot launch any GUI EXE files. Error code remains 2.
Is the anythink I am missing here ? Any help are welcome. Thank you.
Answer
I've got confuse with the comments, using "\\.\Volume{ID}\myApp.exe" worked well if used with CreateProcess, not CreateProcessW.
There is the fully functional code (Don't forget to include windows.h).
char path[MAX_PATH] = R"(\\.\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\App.exe)";
STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcess(path, NULL, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo))
{
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
else
{
DWORD dw = GetLastError();
printf("ERROR CODE WHILE STARTING: %d", dw);
}
problem in win32 subsystem - it not use native for windows NT Paths, but Win32 Paths which first must be converted to NT Paths before passing to kernel.
also some functions, like ShellExecuteEx or CreateProcessW accept not any valid win32 path, but only restricted subset - so called Drive Letter form. if pass path in form \\?\Volume{guid}\* - ShellExecuteEx and CreateProcessW always fail with this path (so called volume path) even if path correct (CreateFileW open this path). funny that CreateProcessW will be work with path \\.\Volume{guid}\* (if replace ? to . at [2] position) but ShellExecuteEx not worked with this path too.
only one reliable solution here - convert this volume form path to Drive Letter form. this can be done with help IOCTL_MOUNTMGR_QUERY_POINTS - need get list of all MOUNTMGR_MOUNT_POINT in system and do 2 loops by this list - first found device name by existing volume symlink. then in loop #2 - by already known device name found - dos-device name and got it drive letter
#include <mountmgr.h>
ULONG NtVolumePathToDosPath(PUNICODE_STRING VolumePath)
{
if (!MOUNTMGR_IS_NT_VOLUME_NAME(VolumePath))
{
return ERROR_PATH_NOT_FOUND;
}
static volatile UCHAR guz;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 0x400;
union {
PVOID buf;
PMOUNTMGR_MOUNT_POINTS pmmp;
};
HANDLE hFile = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
static MOUNTMGR_MOUNT_POINT mmp;
ULONG dwError = NOERROR;
do
{
if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
if (!DeviceIoControl(hFile, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(mmp), buf, cb, &rcb, 0))
{
dwError = GetLastError();
rcb = pmmp->Size;
continue;
}
dwError = ERROR_PATH_NOT_FOUND;
if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
{
PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;
//loop #1: search for DeviceName linked to VolumePath
do
{
UNICODE_STRING SymbolicLinkName = {
MountPoints->SymbolicLinkNameLength,
SymbolicLinkName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
};
if (MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName))
{
if (RtlEqualUnicodeString(&SymbolicLinkName, VolumePath, TRUE))
{
// found DeviceName
UNICODE_STRING _DeviceName = {
MountPoints->DeviceNameLength,
_DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
NumberOfMountPoints = pmmp->NumberOfMountPoints;
MountPoints = pmmp->MountPoints;
// loop #2: search for "drive letter" linked to DeviceName
do
{
UNICODE_STRING DeviceName = {
MountPoints->DeviceNameLength,
DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
if (RtlEqualUnicodeString(&_DeviceName, &DeviceName, FALSE))
{
SymbolicLinkName.MaximumLength = SymbolicLinkName.Length = MountPoints->SymbolicLinkNameLength;
SymbolicLinkName.Buffer = (PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset);
if (MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
{
PWSTR szVolumePath = VolumePath->Buffer + 48;
*--szVolumePath = ':';
*--szVolumePath = SymbolicLinkName.Buffer[12];
*--szVolumePath = '\\';
*--szVolumePath = '?';
*--szVolumePath = '\\';
*--szVolumePath = '\\';
VolumePath->Buffer = szVolumePath;
dwError = NOERROR;
break;
}
}
} while (MountPoints++, --NumberOfMountPoints);
break;
}
}
} while (MountPoints++, --NumberOfMountPoints);
}
break;
} while (dwError == ERROR_MORE_DATA);
CloseHandle(hFile);
return dwError;
}
ULONG TestExecByVolumePath(PCWSTR szVolumePath)
{
size_t size = wcslen(szVolumePath) * sizeof(WCHAR);
if (size >= MAXUSHORT || size < 98)
{
return ERROR_PATH_NOT_FOUND;
}
UNICODE_STRING VolumePath;
VolumePath.Length = 96;
VolumePath.MaximumLength = (USHORT)size + sizeof(WCHAR);
memcpy(VolumePath.Buffer = (PWSTR)alloca(VolumePath.MaximumLength), szVolumePath, VolumePath.MaximumLength);
if (!MOUNTMGR_IS_DOS_VOLUME_NAME(&VolumePath))
{
return ERROR_PATH_NOT_FOUND;
}
VolumePath.Buffer[1] = '?';
ULONG dwErr = NtVolumePathToDosPath(&VolumePath);
if (dwErr == NOERROR)
{
SHELLEXECUTEINFOW sei = {sizeof(sei), 0, 0, L"open", VolumePath.Buffer, 0, 0, SW_SHOWDEFAULT };
if (!ShellExecuteExW(&sei))
{
dwErr = GetLastError();
}
}
return dwErr;
}
TestExecByVolumePath(L"\\\\?\\Volume{***}\\Windows\\System32\\notepad.exe");

How to extend a volume programmatically

My requirement is to extend drive volume through program. When I used IOCTL_DISK_GROW_PARTITION in DeviceIO to extend it, the disk management shows the new modified size while the size of the drive in This PC (My Computer) remains unchanged.
BOOL DeviceIoControl(
(HANDLE) hDevice, // handle to device
IOCTL_DISK_GROW_PARTITION, // dwIoControlCode
(LPVOID) lpInBuffer, // input buffer
(DWORD) nInBufferSize, // size of the input buffer
NULL, // lpOutBuffer
0, // nOutBufferSize
(LPDWORD) lpBytesReturned, // number of bytes returned
(LPOVERLAPPED) lpOverlapped // OVERLAPPED structure
);
Through some analysis I found that while using this API the MBR of the disk is modified but the cluster bitmap of drive is not changed. I want to know the correct way of using this DeviceIO to expand a volume or some other API to do the same process.
need understand different between disk driver, which maintain info about disk layout and partitions (it size, offset from disk begin, style (gpt or mbr) ) and file system, which mount this partition.
IOCTL_DISK_GROW_PARTITION - this ioctl is handled by disk driver and extend partition, but this can not have effect for file system, which not handle this ioctl and have no knowledge at all that partition was extended. so you need additional ioctl use FSCTL_EXTEND_VOLUME - this ioctl already send and handle to file-system.
so if we have to do next steps
send IOCTL_DISK_GROW_PARTITION with
DISK_GROW_PARTITION as input buffer
send IOCTL_DISK_UPDATE_DRIVE_SIZE with DISK_GEOMETRY
as output buffer
send IOCTL_DISK_GET_PARTITION_INFO_EX with
PARTITION_INFORMATION_EX as output for get actual size of
partition now.
calculate new size of the volume, in sectors
LONGLONG SectorsPerPartition = PartitionEntry->PartitionLength.QuadPart / dg.BytesPerSector;
(dg we got at step 2 and PartitionEntry at step 3)
finally use FSCTL_EXTEND_VOLUME
full code can be like next
int __cdecl SortPartitions(PPARTITION_INFORMATION_EX PartitionEntry1, PPARTITION_INFORMATION_EX PartitionEntry2)
{
if (!PartitionEntry1->PartitionNumber) return PartitionEntry2->PartitionNumber ? -1 : 0;
if (!PartitionEntry2->PartitionNumber) return +1;
if (PartitionEntry1->StartingOffset.QuadPart < PartitionEntry2->StartingOffset.QuadPart) return -1;
if (PartitionEntry1->StartingOffset.QuadPart > PartitionEntry2->StartingOffset.QuadPart) return +1;
return 0;
}
DWORD ExtendTest(HANDLE hDisk)
{
STORAGE_DEVICE_NUMBER sdn;
ULONG dwBytesRet;
if (!DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesRet, NULL))
{
return GetLastError();
}
if (sdn.DeviceType != FILE_DEVICE_DISK || sdn.PartitionNumber != 0)
{
return ERROR_GEN_FAILURE;
}
GET_LENGTH_INFORMATION gli;
if (!DeviceIoControl(hDisk, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), &dwBytesRet, NULL))
{
return GetLastError();
}
DbgPrint("Disk Length %I64x (%I64u)\n", gli.Length.QuadPart, gli.Length.QuadPart);
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
};
ULONG cb = 0, rcb, PartitionCount = 4;
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buf, cb, &dwBytesRet, NULL))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
qsort(PartitionEntry, PartitionCount, sizeof(PARTITION_INFORMATION_EX),
(int (__cdecl *)(const void *, const void *))SortPartitions );
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
LARGE_INTEGER EndOffset;
LARGE_INTEGER MaximumOffset = PartitionCount != 1 ? (PartitionEntry + 1)->StartingOffset : gli.Length;
EndOffset.QuadPart = PartitionEntry->StartingOffset.QuadPart + PartitionEntry->PartitionLength.QuadPart;
if (EndOffset.QuadPart > MaximumOffset.QuadPart)
{
//??
__debugbreak();
}
else if (EndOffset.QuadPart < MaximumOffset.QuadPart)
{
DISK_GROW_PARTITION dgp;
dgp.PartitionNumber = PartitionEntry->PartitionNumber;
dgp.BytesToGrow.QuadPart = MaximumOffset.QuadPart - EndOffset.QuadPart;
WCHAR sz[128];
swprintf(sz, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%d\\Partition%u", sdn.DeviceNumber, dgp.PartitionNumber);
HANDLE hPartition = CreateFile(sz, FILE_READ_ACCESS|FILE_WRITE_ACCESS, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hPartition != INVALID_HANDLE_VALUE)
{
// +++ begin extend
BOOL fOk = FALSE;
DISK_GEOMETRY dg;
if (DeviceIoControl(hPartition, IOCTL_DISK_GROW_PARTITION, &dgp, sizeof(dgp), 0, 0, &dwBytesRet, 0) &&
DeviceIoControl(hPartition, IOCTL_DISK_UPDATE_DRIVE_SIZE, 0, 0, &dg, sizeof(dg), &dwBytesRet, 0) &&
DeviceIoControl(hPartition, IOCTL_DISK_GET_PARTITION_INFO_EX, 0, 0, PartitionEntry, sizeof(*PartitionEntry), &dwBytesRet, 0)
)
{
LONGLONG SectorsPerPartition = PartitionEntry->PartitionLength.QuadPart / dg.BytesPerSector;
fOk = DeviceIoControl(hPartition, FSCTL_EXTEND_VOLUME, &SectorsPerPartition,
sizeof(SectorsPerPartition), 0, 0, &dwBytesRet, 0);
}
if (!fOk)
{
GetLastError();
}
//--- end extend
CloseHandle(hPartition);
}
}
// else EndOffset.QuadPart == MaximumOffset.QuadPart - partition can not be extended
} while (PartitionEntry++, --PartitionCount);
}
return NOERROR;
}
switch (ULONG err = GetLastError())
{
case ERROR_MORE_DATA:
PartitionCount = pdli->PartitionCount;
continue;
case ERROR_BAD_LENGTH:
case ERROR_INSUFFICIENT_BUFFER:
PartitionCount <<= 1;
continue;
default:
return err;
}
}
}
DWORD ExtendTest()
{
HANDLE hDisk = CreateFileW(L"\\\\?\\PhysicalDrive0", FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hDisk != INVALID_HANDLE_VALUE)
{
DWORD err = ExtendTest(hDisk);
CloseHandle(hDisk);
return err;
}
return GetLastError();
}

Using ReadFile to read entire PhysicalDrive contents

I am fairly new to C, and I am trying to write a small application that will read the entire raw contents of a drive.
Here is my code;
int main(int argc, char *argv[]) {
HANDLE hFile;
DWORD dwBytesRead;
char buff[512];
hFile = CreateFile("\\\\.\\PhysicalDrive2", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if(hFile == INVALID_HANDLE_VALUE){
printf("%d",GetLastError());
return;
}
SetFilePointer(hFile, 512*0, NULL, FILE_BEGIN);
ReadFile(hFile, buff, 512, &dwBytesRead, NULL);
CloseHandle(hFile);
return 0;
}
How do I put the ReadFile into a loop to read all the data on the drive? I eventually need to save the contents of the buffer to the disk.
Thanks
The loop might look like this:
hFile = CreateFile(...);
if (hFile == INVALID_HANDLE_VALUE)
{
// handle error
}
while (true)
{
unsigned char buff[32768]; // needs to be a multiple of sector size
DWORD dwBytesRead;
if (!ReadFile(hFile, buff, sizeof buff, &dwBytesRead, NULL))
{
// handle error
}
if (dwBytesRead == 0)
{
break; // we reached the end
}
// do something with the dwBytesRead that were read
}
CloseHandle(hFile);

Resources