Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //
     18 // Shared file mapping class.
     19 //
     20 
     21 #define LOG_TAG "filemap"
     22 
     23 #include <utils/FileMap.h>
     24 #include <utils/Log.h>
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 
     29 #ifdef HAVE_POSIX_FILEMAP
     30 #include <sys/mman.h>
     31 #endif
     32 
     33 #include <string.h>
     34 #include <memory.h>
     35 #include <errno.h>
     36 #include <assert.h>
     37 
     38 using namespace android;
     39 
     40 /*static*/ long FileMap::mPageSize = -1;
     41 
     42 
     43 /*
     44  * Constructor.  Create an empty object.
     45  */
     46 FileMap::FileMap(void)
     47     : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
     48       mDataPtr(NULL), mDataLength(0)
     49 {
     50 }
     51 
     52 /*
     53  * Destructor.
     54  */
     55 FileMap::~FileMap(void)
     56 {
     57     assert(mRefCount == 0);
     58 
     59     //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength);
     60 
     61     mRefCount = -100;       // help catch double-free
     62     if (mFileName != NULL) {
     63         free(mFileName);
     64     }
     65 #ifdef HAVE_POSIX_FILEMAP
     66     if (munmap(mBasePtr, mBaseLength) != 0) {
     67         LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength);
     68     }
     69 #endif
     70 #ifdef HAVE_WIN32_FILEMAP
     71     if ( UnmapViewOfFile(mBasePtr) == 0) {
     72         LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr,
     73               GetLastError() );
     74     }
     75     CloseHandle(mFileMapping);
     76     CloseHandle(mFileHandle);
     77 #endif
     78 }
     79 
     80 
     81 /*
     82  * Create a new mapping on an open file.
     83  *
     84  * Closing the file descriptor does not unmap the pages, so we don't
     85  * claim ownership of the fd.
     86  *
     87  * Returns "false" on failure.
     88  */
     89 bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly)
     90 {
     91 #ifdef HAVE_WIN32_FILEMAP
     92     int     adjust;
     93     off_t   adjOffset;
     94     size_t  adjLength;
     95 
     96     if (mPageSize == -1) {
     97         SYSTEM_INFO  si;
     98 
     99         GetSystemInfo( &si );
    100         mPageSize = si.dwAllocationGranularity;
    101     }
    102 
    103     DWORD  protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;
    104 
    105     mFileHandle  = (HANDLE) _get_osfhandle(fd);
    106     mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
    107     if (mFileMapping == NULL) {
    108         LOGE("CreateFileMapping(%p, %lx) failed with error %ld\n",
    109               mFileHandle, protect, GetLastError() );
    110         return false;
    111     }
    112 
    113     adjust    = offset % mPageSize;
    114     adjOffset = offset - adjust;
    115     adjLength = length + adjust;
    116 
    117     mBasePtr = MapViewOfFile( mFileMapping,
    118                               readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
    119                               0,
    120                               (DWORD)(adjOffset),
    121                               adjLength );
    122     if (mBasePtr == NULL) {
    123         LOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n",
    124               adjOffset, adjLength, GetLastError() );
    125         CloseHandle(mFileMapping);
    126         mFileMapping = INVALID_HANDLE_VALUE;
    127         return false;
    128     }
    129 #endif
    130 #ifdef HAVE_POSIX_FILEMAP
    131     int     prot, flags, adjust;
    132     off_t   adjOffset;
    133     size_t  adjLength;
    134 
    135     void* ptr;
    136 
    137     assert(mRefCount == 1);
    138     assert(fd >= 0);
    139     assert(offset >= 0);
    140     assert(length > 0);
    141 
    142     /* init on first use */
    143     if (mPageSize == -1) {
    144 #if NOT_USING_KLIBC
    145         mPageSize = sysconf(_SC_PAGESIZE);
    146         if (mPageSize == -1) {
    147             LOGE("could not get _SC_PAGESIZE\n");
    148             return false;
    149         }
    150 #else
    151         /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */
    152         mPageSize = 4096;
    153 #endif
    154     }
    155 
    156     adjust   = offset % mPageSize;
    157 try_again:
    158     adjOffset = offset - adjust;
    159     adjLength = length + adjust;
    160 
    161     flags = MAP_SHARED;
    162     prot = PROT_READ;
    163     if (!readOnly)
    164         prot |= PROT_WRITE;
    165 
    166     ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
    167     if (ptr == MAP_FAILED) {
    168     	// Cygwin does not seem to like file mapping files from an offset.
    169     	// So if we fail, try again with offset zero
    170     	if (adjOffset > 0) {
    171     		adjust = offset;
    172     		goto try_again;
    173     	}
    174 
    175         LOGE("mmap(%ld,%ld) failed: %s\n",
    176             (long) adjOffset, (long) adjLength, strerror(errno));
    177         return false;
    178     }
    179     mBasePtr = ptr;
    180 #endif /* HAVE_POSIX_FILEMAP */
    181 
    182     mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
    183     mBaseLength = adjLength;
    184     mDataOffset = offset;
    185     mDataPtr = (char*) mBasePtr + adjust;
    186     mDataLength = length;
    187 
    188     assert(mBasePtr != NULL);
    189 
    190     LOGV("MAP: base %p/%d data %p/%d\n",
    191         mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength);
    192 
    193     return true;
    194 }
    195 
    196 /*
    197  * Provide guidance to the system.
    198  */
    199 int FileMap::advise(MapAdvice advice)
    200 {
    201 #if HAVE_MADVISE
    202     int cc, sysAdvice;
    203 
    204     switch (advice) {
    205         case NORMAL:        sysAdvice = MADV_NORMAL;        break;
    206         case RANDOM:        sysAdvice = MADV_RANDOM;        break;
    207         case SEQUENTIAL:    sysAdvice = MADV_SEQUENTIAL;    break;
    208         case WILLNEED:      sysAdvice = MADV_WILLNEED;      break;
    209         case DONTNEED:      sysAdvice = MADV_DONTNEED;      break;
    210         default:
    211                             assert(false);
    212                             return -1;
    213     }
    214 
    215     cc = madvise(mBasePtr, mBaseLength, sysAdvice);
    216     if (cc != 0)
    217         LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno));
    218     return cc;
    219 #else
    220 	return -1;
    221 #endif // HAVE_MADVISE
    222 }
    223