Home | History | Annotate | Download | only in minzip
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      3  *
      4  * System utilities.
      5  */
      6 #include <stdlib.h>
      7 #include <stdio.h>
      8 #include <unistd.h>
      9 #include <string.h>
     10 #include <sys/mman.h>
     11 #include <limits.h>
     12 #include <errno.h>
     13 #include <assert.h>
     14 
     15 #define LOG_TAG "minzip"
     16 #include "Log.h"
     17 #include "SysUtil.h"
     18 
     19 /*
     20  * Having trouble finding a portable way to get this.  sysconf(_SC_PAGE_SIZE)
     21  * seems appropriate, but we don't have that on the device.  Some systems
     22  * have getpagesize(2), though the linux man page has some odd cautions.
     23  */
     24 #define DEFAULT_PAGE_SIZE   4096
     25 
     26 
     27 /*
     28  * Create an anonymous shared memory segment large enough to hold "length"
     29  * bytes.  The actual segment may be larger because mmap() operates on
     30  * page boundaries (usually 4K).
     31  */
     32 static void* sysCreateAnonShmem(size_t length)
     33 {
     34     void* ptr;
     35 
     36     ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
     37             MAP_SHARED | MAP_ANON, -1, 0);
     38     if (ptr == MAP_FAILED) {
     39         LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
     40             strerror(errno));
     41         return NULL;
     42     }
     43 
     44     return ptr;
     45 }
     46 
     47 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
     48 {
     49     off_t start, end;
     50     size_t length;
     51 
     52     assert(start_ != NULL);
     53     assert(length_ != NULL);
     54 
     55     start = lseek(fd, 0L, SEEK_CUR);
     56     end = lseek(fd, 0L, SEEK_END);
     57     (void) lseek(fd, start, SEEK_SET);
     58 
     59     if (start == (off_t) -1 || end == (off_t) -1) {
     60         LOGE("could not determine length of file\n");
     61         return -1;
     62     }
     63 
     64     length = end - start;
     65     if (length == 0) {
     66         LOGE("file is empty\n");
     67         return -1;
     68     }
     69 
     70     *start_ = start;
     71     *length_ = length;
     72 
     73     return 0;
     74 }
     75 
     76 /*
     77  * Pull the contents of a file into an new shared memory segment.  We grab
     78  * everything from fd's current offset on.
     79  *
     80  * We need to know the length ahead of time so we can allocate a segment
     81  * of sufficient size.
     82  */
     83 int sysLoadFileInShmem(int fd, MemMapping* pMap)
     84 {
     85     off_t start;
     86     size_t length, actual;
     87     void* memPtr;
     88 
     89     assert(pMap != NULL);
     90 
     91     if (getFileStartAndLength(fd, &start, &length) < 0)
     92         return -1;
     93 
     94     memPtr = sysCreateAnonShmem(length);
     95     if (memPtr == NULL)
     96         return -1;
     97 
     98     actual = read(fd, memPtr, length);
     99     if (actual != length) {
    100         LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
    101         sysReleaseShmem(pMap);
    102         return -1;
    103     }
    104 
    105     pMap->baseAddr = pMap->addr = memPtr;
    106     pMap->baseLength = pMap->length = length;
    107 
    108     return 0;
    109 }
    110 
    111 /*
    112  * Map a file (from fd's current offset) into a shared, read-only memory
    113  * segment.  The file offset must be a multiple of the page size.
    114  *
    115  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
    116  * value and does not disturb "pMap".
    117  */
    118 int sysMapFileInShmem(int fd, MemMapping* pMap)
    119 {
    120     off_t start;
    121     size_t length;
    122     void* memPtr;
    123 
    124     assert(pMap != NULL);
    125 
    126     if (getFileStartAndLength(fd, &start, &length) < 0)
    127         return -1;
    128 
    129     memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
    130     if (memPtr == MAP_FAILED) {
    131         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
    132             fd, (int) start, strerror(errno));
    133         return -1;
    134     }
    135 
    136     pMap->baseAddr = pMap->addr = memPtr;
    137     pMap->baseLength = pMap->length = length;
    138 
    139     return 0;
    140 }
    141 
    142 /*
    143  * Map part of a file (from fd's current offset) into a shared, read-only
    144  * memory segment.
    145  *
    146  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
    147  * value and does not disturb "pMap".
    148  */
    149 int sysMapFileSegmentInShmem(int fd, off_t start, long length,
    150     MemMapping* pMap)
    151 {
    152     off_t dummy;
    153     size_t fileLength, actualLength;
    154     off_t actualStart;
    155     int adjust;
    156     void* memPtr;
    157 
    158     assert(pMap != NULL);
    159 
    160     if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
    161         return -1;
    162 
    163     if (start + length > (long)fileLength) {
    164         LOGW("bad segment: st=%d len=%ld flen=%d\n",
    165             (int) start, length, (int) fileLength);
    166         return -1;
    167     }
    168 
    169     /* adjust to be page-aligned */
    170     adjust = start % DEFAULT_PAGE_SIZE;
    171     actualStart = start - adjust;
    172     actualLength = length + adjust;
    173 
    174     memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
    175                 fd, actualStart);
    176     if (memPtr == MAP_FAILED) {
    177         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
    178             (int) actualLength, fd, (int) actualStart, strerror(errno));
    179         return -1;
    180     }
    181 
    182     pMap->baseAddr = memPtr;
    183     pMap->baseLength = actualLength;
    184     pMap->addr = (char*)memPtr + adjust;
    185     pMap->length = length;
    186 
    187     LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
    188         (int) start, (int) length,
    189         pMap->baseAddr, (int) pMap->baseLength,
    190         pMap->addr, (int) pMap->length);
    191 
    192     return 0;
    193 }
    194 
    195 /*
    196  * Release a memory mapping.
    197  */
    198 void sysReleaseShmem(MemMapping* pMap)
    199 {
    200     if (pMap->baseAddr == NULL && pMap->baseLength == 0)
    201         return;
    202 
    203     if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
    204         LOGW("munmap(%p, %d) failed: %s\n",
    205             pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
    206     } else {
    207         LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
    208         pMap->baseAddr = NULL;
    209         pMap->baseLength = 0;
    210     }
    211 }
    212 
    213