| /* | 
 |  * 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(__MINGW32__) && !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> | 
 |  | 
 | #if !defined(__MINGW32__) | 
 | #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); | 
 |     } | 
 | #if defined(__MINGW32__) | 
 |     if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { | 
 |         ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr, | 
 |               GetLastError() ); | 
 |     } | 
 |     if (mFileMapping != INVALID_HANDLE_VALUE) { | 
 |         CloseHandle(mFileMapping); | 
 |     } | 
 | #else | 
 |     if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { | 
 |         ALOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength); | 
 |     } | 
 | #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) | 
 | { | 
 | #if defined(__MINGW32__) | 
 |     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; | 
 |     } | 
 | #else // !defined(__MINGW32__) | 
 |     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 // !defined(__MINGW32__) | 
 |  | 
 |     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. | 
 | #if !defined(_WIN32) | 
 | int FileMap::advise(MapAdvice advice) | 
 | { | 
 |     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 | 
 | int FileMap::advise(MapAdvice /* advice */) | 
 | { | 
 |     return -1; | 
 | } | 
 | #endif |