| /* | 
 |  * 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> | 
 |  | 
 | #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 %u\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, %d) failed\n", mBasePtr, (int) mBaseLength); | 
 |     } | 
 | #endif | 
 | #ifdef HAVE_WIN32_FILEMAP | 
 |     if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { | 
 |         ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, | 
 |               GetLastError() ); | 
 |     } | 
 |     if (mFileMapping != INVALID_HANDLE_VALUE) { | 
 |         CloseHandle(mFileMapping); | 
 |     } | 
 |     CloseHandle(mFileHandle); | 
 | #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, %lx) failed with error %ld\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(%ld, %ld) failed with error %ld\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(%ld,%ld) failed: %s\n", | 
 |             (long) adjOffset, (long) 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/%d data %p/%d\n", | 
 |         mBasePtr, (int) mBaseLength, mDataPtr, (int) 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 | 
 | } |