I'm using the CreateFileMapping and MapViewOfFile functions to map a file into memory. After a certain point, I call VirtualProtect to change its protection from read-only to read and write. This call fails and GetLastError gives ERROR_INVALID_PARAMETER.
Here is a simplified version of my code that demonstrates the problem.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main() {
HANDLE fd, md;
char *addr;
DWORD old;
BOOL ok;
fd = CreateFile("filename", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
md = CreateFileMapping(fd, NULL, PAGE_READWRITE, 0, 100, NULL);
addr = MapViewOfFile(md, FILE_MAP_READ, 0, 0, 100);
ok = VirtualProtect(addr, 100, PAGE_READWRITE, &old);
if (!ok) {
// we fall into this if block
DWORD err = GetLastError();
// this outputs "error protecting: 87"
printf("error protecting: %u\n", err);
return 1;
}
UnmapViewOfFile(addr);
CloseHandle(md);
CloseHandle(fd);
return 0;
}
What am I doing wrong here? Am I not allowed to call VirtualProtect on a region containing a mapped file?
Start out by creating the view with FILE_MAP_READ | FILE_MAP_WRITE and protect with PAGE_READONLY. Now you have no trouble making it PAGE_READWRITE later:
addr = MapViewOfFile(md, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 100);
ok = VirtualProtect(addr, 100, PAGE_READONLY, &old);
//...
ok = VirtualProtect(addr, 100, PAGE_READWRITE, &old);
What happens in your code is that VirtualProtectEx (invoked by VirtualProtect of yours) fails with error STATUS_SECTION_PROTECTION (0xC000004E) - "A view to a section specifies a protection that is incompatible with the protection of the initial view" and this seems to be what you did indeed by creating a section view with more restrictive protection (FILE_MAP_READ).
This topic doesn't seem to be documented with enough details, so I think you'd better simply follow what Hans suggested.
According to http://msdn.microsoft.com/en-us/library/aa366556(v=vs.85).aspx this should be legal. According to VirtualProtect documentation, the new flags must be compatible with the "VirtualAlloc" flags - if this transfer to the "MapViewOfFile" flags, I'd suspect that you can tighten but not loosen the protection. Try mapping readwrite and changing protection to readonly.
Related
I want to modify a single attribute on a file (e.g the read-only attribute). In order to do that, it looks like I have to query the current file attributes with either GetFileAttributes or GetFileInformationByHandle, then set the new attributes with either SetFileAttributes or SetFileInformationByHandle: https://learn.microsoft.com/en-us/windows/win32/fileio/retrieving-and-changing-file-attributes
However that is inherently racy, as the file attributes may change between the query and the update. Is there a method to update file attributes atomically? I would expect there to be an API like ModifyFileAttributes(DWORD addAttributes, DWORD rmAttributes) which would do its best to work atomically. Transactional NTFS is not an option for me because a) it's deprecated b) only works on NTFS.
Thanks!
As mentioned in the comment, FILE_SHARE_READ is a trade-off. The following code is adapted from SetFileInformationByHandle function. SetFileInformationByHandle for hFile2 is ERROR_ACCESS_DENIED.
#include <Windows.h>
#include <Tchar.h>
int main()
{
//...
HANDLE hFile1 = CreateFile(TEXT("tempfile"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
0,
NULL);
HANDLE hFile2 = CreateFile(TEXT("tempfile"),
GENERIC_READ,
FILE_SHARE_READ| FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL);
if (hFile1 != INVALID_HANDLE_VALUE && hFile2 != INVALID_HANDLE_VALUE)
{
HANDLE hFile = hFile1;
//HANDLE hFile = hFile2;
FILE_BASIC_INFO fdi{};
fdi.FileAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_NORMAL;
BOOL fResult = SetFileInformationByHandle(hFile,
FileBasicInfo,
&fdi,
sizeof(FILE_BASIC_INFO));
if (fResult)
{
// File will be deleted upon CloseHandle.
_tprintf(TEXT("SetFileInformationByHandle marked tempfile for deletion\n"));
// ...
// Now use the file for whatever temp data storage you need,
// it will automatically be deleted upon CloseHandle or
// application termination.
// ...
}
else
{
_tprintf(TEXT("error %lu: SetFileInformationByHandle could not mark tempfile for deletion\n"),
GetLastError());
}
CloseHandle(hFile);
// At this point, the file is closed and deleted by the system.
}
else
{
_tprintf(TEXT("error %lu: could not create tempfile\n"),
GetLastError());
}
//...
}
I am writing a small program to communicate with a USB to UART bridge (CP210x).
The program is fairly simple, but when ever I decrease the size of the outbound message array below (wm_size < 25) I get a segmentation fault.
The code yielding the segmentation fault looks as follows:
HANDLE prog_com_port;
prog_com_port = CreateFileA("\\\\.\\COM4",
GENERIC_READ | GENERIC_WRITE, //Request Read and Write Acess
0, //Block others from sharing Acess
NULL, //Disable Security features
OPEN_EXISTING, //Only open existing device
FILE_ATTRIBUTE_NORMAL, //Used to set atributes and flags
NULL);
//Handle to
if(prog_com_port == INVALID_HANDLE_VALUE)
{
printf("Opening COM port failed - INVALID_HANDLE_VALUE\n");
CloseHandle(prog_com_port);
return 1;
}
else
printf("COM port was opened successfully \n");
SetupCommState(prog_com_port);
Sleep(2);
#define wm_size 25
int messageLength = 2;
char W_message[wm_size] = {0x01,0x01};
long unsigned int * bytes_sent;
BOOL Status;
Status = WriteFile(prog_com_port, // Handle to the Serial port
&W_message, // Pointer to message buffer
messageLength, // No of bytes to write
bytes_sent, // Pointer to number of bytes written
NULL);
if(!Status)
{
printf("Failed to write to COM port - 0x00 \n");
CloseHandle(prog_com_port);
return 2;
}
CloseHandle(prog_com_port);
My logic tells me that setting wm_size = 2 is enough but apparently that is wrong. Can someone tell me why?
I played around with the size of wm_size and found experiemtally that 25 fixed the problem.
wm_size = 2 is enough, the problem is elsewhere: bytes_sent is a pointer that points nowhere.
Your "fix" didn't fix anything. You are experiencing undefined behaviour (which includes apparently working fine).
You want this (all comments are mine):
DWORD messageLength = 2; // use DWORD
DWORD bytes_sent; // use DWORD
char W_message[] = {0x01,0x01}; // you don't need wm_size
Status = WriteFile(prog_com_port,
W_message, // remove &, W_message decays into a pointer
messageLength,
&bytes_sent, // put &, you need a pointer here
NULL);
or even better: you don't need messageLength:
Status = WriteFile(prog_com_port,
W_message, // remove &
sizeof(W_message), // assuming W_message contains chars
&bytes_sent, // put &
NULL);
// now bytes_sent contains the number of bytes sent (hopefully 2),
// providing WriteFile succeeded (Status is TRUE)
The usage of DWORD is highly recommended, so the types of the arguments passed to to WriteFile actually match the declaration (and the documentation). Also be aware that LPDWORD in all Microsoft documentation and header files is is the
same thing as DWORD*.
On POSIX systems, I am able to use the mmap function to read the contents of a file faster than getline, getc, etc. This is important in the program that I am developing as it is expected to read very large files into memory; iteratively collecting lines using getline is too costly. Portability is also a requirement of my software, so if I use mmap, I need to find a way to memory map files using the WinApi, as I'd rather not compile through cygwin/msys. From a cursory search I identified this MSDN article which describes very briefly a way to map files into memory, however, from trawling through documentation I can't make head nor tails of how to actually implement it, and I'm stuck on finding example snippets of code, like there are for POSIX mmap.
How do I use the WinApi's memory mapping options to read a file into a char*?
How do I use the WinApi's memory mapping options to read a file into a
char*?
Under Windows, when you map a file in memory, you get a pointer to the memory location where the first byte of the file has been mapped. You can cast that pointer to whatever datatype you like, including char*.
In other words, it is Windows which decide where the mapped data will be in memory. You cannot provide a char* and expect Windows will load data there.
This means that if you already have a char* and want the data from the file in the location pointed by that char*, then you have to copy it. Not a good idea in terms of performances.
Here is a simple program dumping a text file by mapping the file into memory and then displaying all ASCII characters. Tested with MSVC2019.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
TCHAR *lpFileName = TEXT("hello.txt");
HANDLE hFile;
HANDLE hMap;
LPVOID lpBasePtr;
LARGE_INTEGER liFileSize;
hFile = CreateFile(lpFileName,
GENERIC_READ, // dwDesiredAccess
0, // dwShareMode
NULL, // lpSecurityAttributes
OPEN_EXISTING, // dwCreationDisposition
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
0); // hTemplateFile
if (hFile == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFile failed with error %d\n", GetLastError());
return 1;
}
if (!GetFileSizeEx(hFile, &liFileSize)) {
fprintf(stderr, "GetFileSize failed with error %d\n", GetLastError());
CloseHandle(hFile);
return 1;
}
if (liFileSize.QuadPart == 0) {
fprintf(stderr, "File is empty\n");
CloseHandle(hFile);
return 1;
}
hMap = CreateFileMapping(
hFile,
NULL, // Mapping attributes
PAGE_READONLY, // Protection flags
0, // MaximumSizeHigh
0, // MaximumSizeLow
NULL); // Name
if (hMap == 0) {
fprintf(stderr, "CreateFileMapping failed with error %d\n", GetLastError());
CloseHandle(hFile);
return 1;
}
lpBasePtr = MapViewOfFile(
hMap,
FILE_MAP_READ, // dwDesiredAccess
0, // dwFileOffsetHigh
0, // dwFileOffsetLow
0); // dwNumberOfBytesToMap
if (lpBasePtr == NULL) {
fprintf(stderr, "MapViewOfFile failed with error %d\n", GetLastError());
CloseHandle(hMap);
CloseHandle(hFile);
return 1;
}
// Display file content as ASCII charaters
char *ptr = (char *)lpBasePtr;
LONGLONG i = liFileSize.QuadPart;
while (i-- > 0) {
fputc(*ptr++, stdout);
}
UnmapViewOfFile(lpBasePtr);
CloseHandle(hMap);
CloseHandle(hFile);
printf("\nDone\n");
}
I have mapped a file of unknown size (around 4-6 GiB) in Windows platform and got a pointer to the start of the file data returned from the MapFileView function. But how can I know that I have reached the end of the file when I access the data using the pointer sequentially?
Here is the code I have so far written and it successfully maps the file and returns the pointer:
#include <Windows.h>
#include <stdio.h>
#include <inttypes.h>
int main()
{
HANDLE hFile = CreateFile("Test.bin",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (!hFile)
{
printf("Could not create file (%lu).\n", GetLastError());
exit(1) ;
}
HANDLE hMapFile = CreateFileMappingA(hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL);
if (!hMapFile)
{
printf("Could not create file mapping object (%lu).\n", GetLastError());
CloseHandle(hFile);
exit(1);
}
int32_t* pBuf = (int32_t*) MapViewOfFile(hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (!pBuf)
{
printf("Could not map file (%lu).\n", GetLastError());
CloseHandle(hFile);
CloseHandle(hMapFile);
exit(1);
};
UnmapViewOfFile(pBuf);
CloseHandle(hFile);
CloseHandle(hMapFile);
exit(0);
}
So I wanted to read equal sized different parts of the file simultaneously in multiple threads. I believe mapped file is the right choice for this purpose. Advice about any other faster and possible approaches is highly appreciated.
I have researched some similar questions in the forum and I suppose this is the closest topic I could find:
Read all contents of memory mapped file or Memory Mapped View Accessor without knowing the size of it
But this answer is using C# and is not written using the WinAPI, therefore, I couldn't understand their process.
Thanks in advance :)
Call GetFileSizeEx to get the size of a file, and use this in combination with the base address and the current read address to determine where the end address is.
I'm trying to prevent a third-party DLL in my process from reading a file I've opened, and I've found it to be... well, impossible.
No matter what I do, no matter what share flags I specify, their call always seems to succeed!
Here is the screenshot from Process Monitor -- the first CreateFile call is mine, and the rest are theirs:
How is this even possible? Why is the "Share Mode: None" lying to me, and how can I prevent this?
This code below is an example that reproduces the problem:
#include <stdio.h>
#include <Windows.h>
int main()
{
LPCTSTR file = TEXT("C:\\Test1234.xml");
HANDLE hFile1 =
CreateFile(file, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_ALWAYS, 0, NULL);
HANDLE hFile2 =
CreateFile(file, FILE_READ_DATA, 0, NULL, OPEN_ALWAYS, 0, NULL);
DWORD n;
BYTE buf[1];
printf("%d\n", ReadFile(hFile2, buf, sizeof(buf), &n, NULL));
CloseHandle(hFile1);
CloseHandle(hFile2);
DeleteFile(file);
}
Share modes are enforced for actually reading and writing the DATA of the file. Attributes (like file size, timestamps, etc) are not covered by the sharing rules and there is no way to prevent their access short of ACLs.
The best you can to is open the file for R/W/D access and not specify SHARE_READ|WRITE|DELETE.
Weird, but true.