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 <sys/types.h>
     12 #include <sys/stat.h>
     13 #include <fcntl.h>
     14 #include <limits.h>
     15 #include <errno.h>
     16 #include <assert.h>
     17 
     18 #define LOG_TAG "sysutil"
     19 #include "Log.h"
     20 #include "SysUtil.h"
     21 
     22 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
     23 {
     24     off_t start, end;
     25     size_t length;
     26 
     27     assert(start_ != NULL);
     28     assert(length_ != NULL);
     29 
     30     start = lseek(fd, 0L, SEEK_CUR);
     31     end = lseek(fd, 0L, SEEK_END);
     32     (void) lseek(fd, start, SEEK_SET);
     33 
     34     if (start == (off_t) -1 || end == (off_t) -1) {
     35         LOGE("could not determine length of file\n");
     36         return -1;
     37     }
     38 
     39     length = end - start;
     40     if (length == 0) {
     41         LOGE("file is empty\n");
     42         return -1;
     43     }
     44 
     45     *start_ = start;
     46     *length_ = length;
     47 
     48     return 0;
     49 }
     50 
     51 /*
     52  * Map a file (from fd's current offset) into a private, read-only memory
     53  * segment.  The file offset must be a multiple of the page size.
     54  *
     55  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
     56  * value and does not disturb "pMap".
     57  */
     58 static int sysMapFD(int fd, MemMapping* pMap)
     59 {
     60     off_t start;
     61     size_t length;
     62     void* memPtr;
     63 
     64     assert(pMap != NULL);
     65 
     66     if (getFileStartAndLength(fd, &start, &length) < 0)
     67         return -1;
     68 
     69     memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start);
     70     if (memPtr == MAP_FAILED) {
     71         LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length,
     72             fd, (int) start, strerror(errno));
     73         return -1;
     74     }
     75 
     76     pMap->addr = memPtr;
     77     pMap->length = length;
     78     pMap->range_count = 1;
     79     pMap->ranges = malloc(sizeof(MappedRange));
     80     pMap->ranges[0].addr = memPtr;
     81     pMap->ranges[0].length = length;
     82 
     83     return 0;
     84 }
     85 
     86 static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
     87 {
     88     char block_dev[PATH_MAX+1];
     89     size_t size;
     90     unsigned int blksize;
     91     unsigned int blocks;
     92     unsigned int range_count;
     93     unsigned int i;
     94 
     95     if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) {
     96         LOGW("failed to read block device from header\n");
     97         return -1;
     98     }
     99     for (i = 0; i < sizeof(block_dev); ++i) {
    100         if (block_dev[i] == '\n') {
    101             block_dev[i] = 0;
    102             break;
    103         }
    104     }
    105 
    106     if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) {
    107         LOGW("failed to parse block map header\n");
    108         return -1;
    109     }
    110 
    111     blocks = ((size-1) / blksize) + 1;
    112 
    113     pMap->range_count = range_count;
    114     pMap->ranges = malloc(range_count * sizeof(MappedRange));
    115     memset(pMap->ranges, 0, range_count * sizeof(MappedRange));
    116 
    117     // Reserve enough contiguous address space for the whole file.
    118     unsigned char* reserve;
    119     reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
    120     if (reserve == MAP_FAILED) {
    121         LOGW("failed to reserve address space: %s\n", strerror(errno));
    122         return -1;
    123     }
    124 
    125     pMap->ranges[range_count-1].addr = reserve;
    126     pMap->ranges[range_count-1].length = blocks * blksize;
    127 
    128     int fd = open(block_dev, O_RDONLY);
    129     if (fd < 0) {
    130         LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno));
    131         return -1;
    132     }
    133 
    134     unsigned char* next = reserve;
    135     for (i = 0; i < range_count; ++i) {
    136         int start, end;
    137         if (fscanf(mapf, "%d %d\n", &start, &end) != 2) {
    138             LOGW("failed to parse range %d in block map\n", i);
    139             return -1;
    140         }
    141 
    142         void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
    143         if (addr == MAP_FAILED) {
    144             LOGW("failed to map block %d: %s\n", i, strerror(errno));
    145             return -1;
    146         }
    147         pMap->ranges[i].addr = addr;
    148         pMap->ranges[i].length = (end-start)*blksize;
    149 
    150         next += pMap->ranges[i].length;
    151     }
    152 
    153     pMap->addr = reserve;
    154     pMap->length = size;
    155 
    156     LOGI("mmapped %d ranges\n", range_count);
    157 
    158     return 0;
    159 }
    160 
    161 int sysMapFile(const char* fn, MemMapping* pMap)
    162 {
    163     memset(pMap, 0, sizeof(*pMap));
    164 
    165     if (fn && fn[0] == '@') {
    166         // A map of blocks
    167         FILE* mapf = fopen(fn+1, "r");
    168         if (mapf == NULL) {
    169             LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno));
    170             return -1;
    171         }
    172 
    173         if (sysMapBlockFile(mapf, pMap) != 0) {
    174             LOGW("Map of '%s' failed\n", fn);
    175             return -1;
    176         }
    177 
    178         fclose(mapf);
    179     } else {
    180         // This is a regular file.
    181         int fd = open(fn, O_RDONLY, 0);
    182         if (fd < 0) {
    183             LOGE("Unable to open '%s': %s\n", fn, strerror(errno));
    184             return -1;
    185         }
    186 
    187         if (sysMapFD(fd, pMap) != 0) {
    188             LOGE("Map of '%s' failed\n", fn);
    189             close(fd);
    190             return -1;
    191         }
    192 
    193         close(fd);
    194     }
    195     return 0;
    196 }
    197 
    198 /*
    199  * Release a memory mapping.
    200  */
    201 void sysReleaseMap(MemMapping* pMap)
    202 {
    203     int i;
    204     for (i = 0; i < pMap->range_count; ++i) {
    205         if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) {
    206             LOGW("munmap(%p, %d) failed: %s\n",
    207                  pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno));
    208         }
    209     }
    210     free(pMap->ranges);
    211     pMap->ranges = NULL;
    212     pMap->range_count = 0;
    213 }
    214