android_system_core/libutils/FileMap.cpp
Xavier Ducrohet 1a55aa5e34 Fix windows specific behavior of FileMap
The implementation of the FileMap destructor would
close the file, only on Windows, which did not match
the behavior on mac/linux.

This is because calling munmap does not close the file
descriptor. It must be closed separately, before or after
munmap.

On Windows, the file must also be closed manually,
before or after closing the mappingFile.

The change basically removes the closing file from
the windows-specific part of the destructor, to
make behavior more consistent on all platforms
where the caller to FileMap is responsible for closing
its own file (since FileMap receives an opened file).

Change-Id: I5e3cfffbb870d5f3595802ccac57dbc1dbf1ce6e
2014-10-16 12:09:06 -07:00

222 lines
5.8 KiB
C++

/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Shared file mapping class.
//
#define LOG_TAG "filemap"
#include <utils/FileMap.h>
#include <utils/Log.h>
#if defined(HAVE_WIN32_FILEMAP) && !defined(__USE_MINGW_ANSI_STDIO)
# define PRId32 "I32d"
# define PRIx32 "I32x"
# define PRId64 "I64d"
#else
#include <inttypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_POSIX_FILEMAP
#include <sys/mman.h>
#endif
#include <string.h>
#include <memory.h>
#include <errno.h>
#include <assert.h>
using namespace android;
/*static*/ long FileMap::mPageSize = -1;
// Constructor. Create an empty object.
FileMap::FileMap(void)
: mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
mDataPtr(NULL), mDataLength(0)
{
}
// Destructor.
FileMap::~FileMap(void)
{
assert(mRefCount == 0);
//printf("+++ removing FileMap %p %zu\n", mDataPtr, mDataLength);
mRefCount = -100; // help catch double-free
if (mFileName != NULL) {
free(mFileName);
}
#ifdef HAVE_POSIX_FILEMAP
if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
ALOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength);
}
#endif
#ifdef HAVE_WIN32_FILEMAP
if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr,
GetLastError() );
}
if (mFileMapping != INVALID_HANDLE_VALUE) {
CloseHandle(mFileMapping);
}
#endif
}
// Create a new mapping on an open file.
//
// Closing the file descriptor does not unmap the pages, so we don't
// claim ownership of the fd.
//
// Returns "false" on failure.
bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length,
bool readOnly)
{
#ifdef HAVE_WIN32_FILEMAP
int adjust;
off64_t adjOffset;
size_t adjLength;
if (mPageSize == -1) {
SYSTEM_INFO si;
GetSystemInfo( &si );
mPageSize = si.dwAllocationGranularity;
}
DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;
mFileHandle = (HANDLE) _get_osfhandle(fd);
mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
if (mFileMapping == NULL) {
ALOGE("CreateFileMapping(%p, %" PRIx32 ") failed with error %" PRId32 "\n",
mFileHandle, protect, GetLastError() );
return false;
}
adjust = offset % mPageSize;
adjOffset = offset - adjust;
adjLength = length + adjust;
mBasePtr = MapViewOfFile( mFileMapping,
readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
0,
(DWORD)(adjOffset),
adjLength );
if (mBasePtr == NULL) {
ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %" PRId32 "\n",
adjOffset, adjLength, GetLastError() );
CloseHandle(mFileMapping);
mFileMapping = INVALID_HANDLE_VALUE;
return false;
}
#endif
#ifdef HAVE_POSIX_FILEMAP
int prot, flags, adjust;
off64_t adjOffset;
size_t adjLength;
void* ptr;
assert(mRefCount == 1);
assert(fd >= 0);
assert(offset >= 0);
assert(length > 0);
// init on first use
if (mPageSize == -1) {
#if NOT_USING_KLIBC
mPageSize = sysconf(_SC_PAGESIZE);
if (mPageSize == -1) {
ALOGE("could not get _SC_PAGESIZE\n");
return false;
}
#else
// this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM
mPageSize = 4096;
#endif
}
adjust = offset % mPageSize;
try_again:
adjOffset = offset - adjust;
adjLength = length + adjust;
flags = MAP_SHARED;
prot = PROT_READ;
if (!readOnly)
prot |= PROT_WRITE;
ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
if (ptr == MAP_FAILED) {
// Cygwin does not seem to like file mapping files from an offset.
// So if we fail, try again with offset zero
if (adjOffset > 0) {
adjust = offset;
goto try_again;
}
ALOGE("mmap(%lld,%zu) failed: %s\n",
(long long)adjOffset, adjLength, strerror(errno));
return false;
}
mBasePtr = ptr;
#endif // HAVE_POSIX_FILEMAP
mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
mBaseLength = adjLength;
mDataOffset = offset;
mDataPtr = (char*) mBasePtr + adjust;
mDataLength = length;
assert(mBasePtr != NULL);
ALOGV("MAP: base %p/%zu data %p/%zu\n",
mBasePtr, mBaseLength, mDataPtr, mDataLength);
return true;
}
// Provide guidance to the system.
int FileMap::advise(MapAdvice advice)
{
#if HAVE_MADVISE
int cc, sysAdvice;
switch (advice) {
case NORMAL: sysAdvice = MADV_NORMAL; break;
case RANDOM: sysAdvice = MADV_RANDOM; break;
case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break;
case WILLNEED: sysAdvice = MADV_WILLNEED; break;
case DONTNEED: sysAdvice = MADV_DONTNEED; break;
default:
assert(false);
return -1;
}
cc = madvise(mBasePtr, mBaseLength, sysAdvice);
if (cc != 0)
ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno));
return cc;
#else
return -1;
#endif // HAVE_MADVISE
}