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