Dees Troy | b7ae098 | 2013-09-10 20:47:35 +0000 | [diff] [blame] | 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 | |
Ethan Yonker | 75bf041 | 2014-11-21 13:54:27 -0600 | [diff] [blame] | 98 | pMap->baseAddr = pMap->addr = memPtr; |
| 99 | pMap->baseLength = pMap->length = length; |
| 100 | |
| 101 | actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length)); |
Dees Troy | b7ae098 | 2013-09-10 20:47:35 +0000 | [diff] [blame] | 102 | if (actual != length) { |
| 103 | LOGE("only read %d of %d bytes\n", (int) actual, (int) length); |
| 104 | sysReleaseShmem(pMap); |
| 105 | return -1; |
| 106 | } |
| 107 | |
Dees Troy | b7ae098 | 2013-09-10 20:47:35 +0000 | [diff] [blame] | 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 | |