Home | History | Annotate | Download | only in libutils
      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 (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
     67         ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength);
     68     }
     69 #endif
     70 #ifdef HAVE_WIN32_FILEMAP
     71     if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
     72         ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr,
     73               GetLastError() );
     74     }
     75     if (mFileMapping != INVALID_HANDLE_VALUE) {
     76         CloseHandle(mFileMapping);
     77     }
     78     CloseHandle(mFileHandle);
     79 #endif
     80 }
     81 
     82 
     83 /*
     84  * Create a new mapping on an open file.
     85  *
     86  * Closing the file descriptor does not unmap the pages, so we don't
     87  * claim ownership of the fd.
     88  *
     89  * Returns "false" on failure.
     90  */
     91 bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length,
     92         bool readOnly)
     93 {
     94 #ifdef HAVE_WIN32_FILEMAP
     95     int     adjust;
     96     off64_t adjOffset;
     97     size_t  adjLength;
     98 
     99     if (mPageSize == -1) {
    100         SYSTEM_INFO  si;
    101 
    102         GetSystemInfo( &si );
    103         mPageSize = si.dwAllocationGranularity;
    104     }
    105 
    106     DWORD  protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;
    107 
    108     mFileHandle  = (HANDLE) _get_osfhandle(fd);
    109     mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
    110     if (mFileMapping == NULL) {
    111         ALOGE("CreateFileMapping(%p, %lx) failed with error %ld\n",
    112               mFileHandle, protect, GetLastError() );
    113         return false;
    114     }
    115 
    116     adjust    = offset % mPageSize;
    117     adjOffset = offset - adjust;
    118     adjLength = length + adjust;
    119 
    120     mBasePtr = MapViewOfFile( mFileMapping,
    121                               readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
    122                               0,
    123                               (DWORD)(adjOffset),
    124                               adjLength );
    125     if (mBasePtr == NULL) {
    126         ALOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n",
    127               adjOffset, adjLength, GetLastError() );
    128         CloseHandle(mFileMapping);
    129         mFileMapping = INVALID_HANDLE_VALUE;
    130         return false;
    131     }
    132 #endif
    133 #ifdef HAVE_POSIX_FILEMAP
    134     int     prot, flags, adjust;
    135     off64_t adjOffset;
    136     size_t  adjLength;
    137 
    138     void* ptr;
    139 
    140     assert(mRefCount == 1);
    141     assert(fd >= 0);
    142     assert(offset >= 0);
    143     assert(length > 0);
    144 
    145     /* init on first use */
    146     if (mPageSize == -1) {
    147 #if NOT_USING_KLIBC
    148         mPageSize = sysconf(_SC_PAGESIZE);
    149         if (mPageSize == -1) {
    150             ALOGE("could not get _SC_PAGESIZE\n");
    151             return false;
    152         }
    153 #else
    154         /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */
    155         mPageSize = 4096;
    156 #endif
    157     }
    158 
    159     adjust   = offset % mPageSize;
    160 try_again:
    161     adjOffset = offset - adjust;
    162     adjLength = length + adjust;
    163 
    164     flags = MAP_SHARED;
    165     prot = PROT_READ;
    166     if (!readOnly)
    167         prot |= PROT_WRITE;
    168 
    169     ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
    170     if (ptr == MAP_FAILED) {
    171     	// Cygwin does not seem to like file mapping files from an offset.
    172     	// So if we fail, try again with offset zero
    173     	if (adjOffset > 0) {
    174     		adjust = offset;
    175     		goto try_again;
    176     	}
    177 
    178         ALOGE("mmap(%ld,%ld) failed: %s\n",
    179             (long) adjOffset, (long) adjLength, strerror(errno));
    180         return false;
    181     }
    182     mBasePtr = ptr;
    183 #endif /* HAVE_POSIX_FILEMAP */
    184 
    185     mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
    186     mBaseLength = adjLength;
    187     mDataOffset = offset;
    188     mDataPtr = (char*) mBasePtr + adjust;
    189     mDataLength = length;
    190 
    191     assert(mBasePtr != NULL);
    192 
    193     ALOGV("MAP: base %p/%d data %p/%d\n",
    194         mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength);
    195 
    196     return true;
    197 }
    198 
    199 /*
    200  * Provide guidance to the system.
    201  */
    202 int FileMap::advise(MapAdvice advice)
    203 {
    204 #if HAVE_MADVISE
    205     int cc, sysAdvice;
    206 
    207     switch (advice) {
    208         case NORMAL:        sysAdvice = MADV_NORMAL;        break;
    209         case RANDOM:        sysAdvice = MADV_RANDOM;        break;
    210         case SEQUENTIAL:    sysAdvice = MADV_SEQUENTIAL;    break;
    211         case WILLNEED:      sysAdvice = MADV_WILLNEED;      break;
    212         case DONTNEED:      sysAdvice = MADV_DONTNEED;      break;
    213         default:
    214                             assert(false);
    215                             return -1;
    216     }
    217 
    218     cc = madvise(mBasePtr, mBaseLength, sysAdvice);
    219     if (cc != 0)
    220         ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno));
    221     return cc;
    222 #else
    223 	return -1;
    224 #endif // HAVE_MADVISE
    225 }
    226