2
votes

I'm working with file mappings on Windows but having some troubles with them. First off, I have the necessity to partially map a file and setting the start and the end of it dynamically.

My code is the following:

long fiveMB = 5 * pow(2, 20);
for(int i=0;i<parts;i++){
    long start = (i)*fiveMB;
    long end = (i + 1)*fiveMB;
    long realEnd = end;
    if (roundedDim<realEnd)
        realEnd = dim;

    long chunkDim = realEnd - start;
    LARGE_INTEGER fileMapStart.QuadPart = (start/granularity)*granularity;
    LARGE_INTEGER mapViewSize.QuadPart = (start%granularity) + chunkDim;
    LARGE_INTEGER fileMapSize.QuadPart = start + chunkDim;
    long offset = start - fileMapStart.QuadPart;

    HANDLE fileMappingH= CreateFileMapping(fileH, NULL, PAGE_READONLY, fileMapSize.HighPart, fileMapSize.LowPart, NULL);

    if(fileMappingH == INVALID_HANDLE_VALUE || fileMappingH == NULL){
       printf("Error mapping file: %d\n",GetLastError());
       CloseHandle(fileH);
       return 1;
    }

    char *mapView = (char *)MapViewOfFile(fileMappingH, FILE_MAP_READ, fileMapStart.HighPart, fileMapStart.LowPart, mapViewSize.QuadPart);
    if ((LPVOID)mapView == NULL) {
        printf("Error mapView: %d\n", GetLastError());
        CloseHandle(fileMappingH);
        CloseHandle(file);
        return 1;
    }

    mapView += offset;

    /* doing all the stuff */

    UnmapViewOfFile((LPVOID)mapView);
    CloseHandle(fileMappingH);
}

As far as I know, only MapViewOfFile requires the starting byte to be aligned with the system granularity, so I didn't bother to fix the maximum file mapping size for that.

I tried this code on a 1448 KB file (printing out dim I get 1482159 bytes) while calculating the available memory via GlobalMemoryStatusEx(&memstatus) and memstatus.ullAvailVirtual I get 2092208128 bytes but still stuck on having the CreateFileMapping call failed and with error code 8, ERROR_NOT_ENOUGH_MEMORY.

I also tried calling CreateFileMapping(fileH, NULL, PAGE_READONLY, 0, 0, NULL) to memory map the whole file, but instead there were problems on MapViewOfFile, error 5, ERROR_ACCESS_DENIED.

I don't understand what I'm doing wrong here, since I successfully did it with mmap on a Linux version of the same project.

Thanks anyone who may help.


EDITS:

  • c was a leftover, I actually meant i

  • added UnmapViewOfFile and CloseHandle calls

1
c is undefined. - stark
at first - call CreateFileMapping in loop - this is design error. you need call this only once. at second on error CreateFileMapping return 0 but not INVALID_HANDLE_VALUE - no sense check it for INVALID_HANDLE_VALUE - RbMm
about error ERROR_NOT_ENOUGH_MEMORY - several ntstatus codes is converted to this error. so here lost on win32 layer. need call RtlGetLastNtStatus() - may be you got for example STATUS_SECTION_TOO_BIG- this mean that you set size for section more than file size and because you use PAGE_READONLY file can not be extended - RbMm
@ifelsemonkey the only RAM the file mapping uses is the one specified by the MapViewOfFile call; so if you know which is the right file offset and how many bytes you want to check from there on, you simply do that by correctly specifying the last parameter for MapViewOfFile, which in my code is mapViewSize.QuadPart. Here you can see the whole code, link to the peculiar MapViewOfFile function call - Emanuele Giona
@ifelsemonkey yes, but in CreateFileMapping you have to put the whole file size; in order to examine your 3 bytes at a given offset has to be specified through MapViewOfFile: be aware that each size involved in such calls has to be aligned with the filesystem granularity! So if you want to read those 3 bytes, you have to map the a chunk of file which correctly starts at the expected granularity, with 3 bytes size and then take the offset into account to localize such bytes - Emanuele Giona

1 Answers

3
votes

As far as I know, MapViewOfFile only requires the starting byte to be aligned with the system granularity, so I didn't bother to fix the maximum file mapping size for that.

this is root of error - really from MapViewOfFile

dwNumberOfBytesToMap [in]

The number of bytes of a file mapping to map to the view. All bytes must be within the maximum size specified by CreateFileMapping. If this parameter is 0 (zero), the mapping extends from the specified offset to the end of the file mapping.

if we use 0 as MaximumSize in CreateFileMapping the maximum size of the file mapping object is equal to the current size of the file. and :

if an application specifies a size for the file mapping object that is larger than the size of the actual named file on disk and if the page protection allows write access (that is, the flProtect parameter specifies PAGE_READWRITE or PAGE_EXECUTE_READWRITE), then the file on disk is increased to match the specified size of the file mapping object.

and about GetLastError and win32 errors at all. the errors in most case returned from kernel as NTSTATUS code. the win32 layer converts the specified NTSTATUS code to its equivalent system error code via RtlNtStatusToDosError. unfortunately this conversion not injective - the many different NTSTATUS code can convert to same win32 error and we lost sensitive info here.

so in some case much more better call RtlGetLastNtStatus() instead of GetlastError() - this give to as much more info about error.

CreateFileMapping call failed and with error code ERROR_NOT_ENOUGH_MEMORY.

based on error ERROR_NOT_ENOUGH_MEMORY we can think that not enough memory in system (STATUS_NO_MEMORY). but also another status - STATUS_SECTION_TOO_BIG converted to the ERROR_NOT_ENOUGH_MEMORY. the CreateFileMapping is thin shell over ZwCreateSection the STATUS_SECTION_TOO_BIG returned when:

The value of MaximumSize is too big. This occurs when either MaximumSize is greater than the system-defined maximum for sections, or if MaximumSize is greater than the specified file and the section is not writable.

and this is exactly your case: you use PAGE_READONLY in call CreateFileMapping - so section is not writable and fileMapSize is greater than the specified file (size for the file mapping object that is larger than the size of the actual file on disk)

MapViewOfFile return ERROR_ACCESS_DENIED.

again GetLastError() plays here with us a cruel joke. the initial status is not STATUS_ACCESS_DENIED how we can wait, but STATUS_INVALID_VIEW_SIZE. this status also converted to ERROR_ACCESS_DENIED. the MapViewOfFile got it when not all bytes within the maximum size specified by CreateFileMapping

and call CreateFileMapping multiple time in loop - this is design error - need call this api only once, before loop. in loop only exist sense call MapViewOfFile. the test code can be:

void TestMap(PCWSTR lpFileName, ULONG dwChunkSize)
{
    HANDLE hFile = CreateFileW(lpFileName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        FILE_STANDARD_INFO fsi;
        if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fsi, sizeof(fsi)))
        {
            if (HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_READONLY, 0, 0, 0))
            {
                if (ULONG n = (ULONG)((fsi.EndOfFile.QuadPart + (dwChunkSize - 1)) / dwChunkSize))
                {
                    LARGE_INTEGER ofs = {};
                    do 
                    {
                        if (PVOID pv = MapViewOfFile(hSection, FILE_MAP_READ, ofs.HighPart, ofs.LowPart, --n ? dwChunkSize : 0))
                        {
                            UnmapViewOfFile(pv);
                        }
                        else
                        {
                            RtlGetLastNtStatus();
                        }
                    } while (ofs.QuadPart += dwChunkSize, n);
                }

                CloseHandle(hSection);
            }
            else
            {
                RtlGetLastNtStatus();
            }
        }
        CloseHandle(hFile);
    }
    else
    {
        RtlGetLastNtStatus();
    }
}