1 /* 2 * Copyright (C) 2008 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 * System utilities. 19 */ 20 #include "DexFile.h" 21 #include "SysUtil.h" 22 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <unistd.h> 26 #include <string.h> 27 #if !defined(__MINGW32__) 28 # include <sys/mman.h> 29 #endif 30 #include <limits.h> 31 #include <errno.h> 32 33 #include <nativehelper/JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd 34 35 36 /* 37 * Create an anonymous shared memory segment large enough to hold "length" 38 * bytes. The actual segment may be larger because mmap() operates on 39 * page boundaries (usually 4K). 40 */ 41 static void* sysCreateAnonShmem(size_t length) 42 { 43 #if !defined(__MINGW32__) 44 void* ptr; 45 46 ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, 47 MAP_SHARED | MAP_ANON, -1, 0); 48 if (ptr == MAP_FAILED) { 49 ALOGW("mmap(%d, RW, SHARED|ANON) failed: %s", (int) length, 50 strerror(errno)); 51 return NULL; 52 } 53 54 return ptr; 55 #else 56 ALOGE("sysCreateAnonShmem not implemented."); 57 return NULL; 58 #endif 59 } 60 61 /* 62 * Create a private anonymous storage area. 63 */ 64 int sysCreatePrivateMap(size_t length, MemMapping* pMap) 65 { 66 void* memPtr; 67 68 memPtr = sysCreateAnonShmem(length); 69 if (memPtr == NULL) 70 return -1; 71 72 pMap->addr = pMap->baseAddr = memPtr; 73 pMap->length = pMap->baseLength = length; 74 return 0; 75 } 76 77 /* 78 * Determine the current offset and remaining length of the open file. 79 */ 80 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) 81 { 82 off_t start, end; 83 size_t length; 84 85 assert(start_ != NULL); 86 assert(length_ != NULL); 87 88 start = lseek(fd, 0L, SEEK_CUR); 89 end = lseek(fd, 0L, SEEK_END); 90 (void) lseek(fd, start, SEEK_SET); 91 92 if (start == (off_t) -1 || end == (off_t) -1) { 93 ALOGE("could not determine length of file"); 94 return -1; 95 } 96 97 length = end - start; 98 if (length == 0) { 99 ALOGE("file is empty"); 100 return -1; 101 } 102 103 *start_ = start; 104 *length_ = length; 105 106 return 0; 107 } 108 109 #if defined(__MINGW32__) 110 int sysFakeMapFile(int fd, MemMapping* pMap) 111 { 112 /* No MMAP, just fake it by copying the bits. 113 For Win32 we could use MapViewOfFile if really necessary 114 (see libs/utils/FileMap.cpp). 115 */ 116 off_t start; 117 size_t length; 118 void* memPtr; 119 120 assert(pMap != NULL); 121 122 if (getFileStartAndLength(fd, &start, &length) < 0) 123 return -1; 124 125 memPtr = malloc(length); 126 if (read(fd, memPtr, length) < 0) { 127 ALOGW("read(fd=%d, start=%d, length=%d) failed: %s", (int) length, 128 fd, (int) start, strerror(errno)); 129 free(memPtr); 130 return -1; 131 } 132 133 pMap->baseAddr = pMap->addr = memPtr; 134 pMap->baseLength = pMap->length = length; 135 136 return 0; 137 } 138 #endif 139 140 /* 141 * Map a file (from fd's current offset) into a private, read-write memory 142 * segment that will be marked read-only (a/k/a "writable read-only"). The 143 * file offset must be a multiple of the system page size. 144 * 145 * In some cases the mapping will be fully writable (e.g. for files on 146 * FAT filesystems). 147 * 148 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 149 * value and does not disturb "pMap". 150 */ 151 int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap) 152 { 153 #if !defined(__MINGW32__) 154 off_t start; 155 size_t length; 156 void* memPtr; 157 158 assert(pMap != NULL); 159 160 if (getFileStartAndLength(fd, &start, &length) < 0) 161 return -1; 162 163 memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, 164 fd, start); 165 if (memPtr == MAP_FAILED) { 166 ALOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s", (int) length, 167 fd, (int) start, strerror(errno)); 168 return -1; 169 } 170 if (mprotect(memPtr, length, PROT_READ) < 0) { 171 /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */ 172 int err = errno; 173 ALOGV("mprotect(%p, %zd, PROT_READ) failed: %s", 174 memPtr, length, strerror(err)); 175 ALOGD("mprotect(RO) failed (%d), file will remain read-write", err); 176 } 177 178 pMap->baseAddr = pMap->addr = memPtr; 179 pMap->baseLength = pMap->length = length; 180 181 return 0; 182 #else 183 return sysFakeMapFile(fd, pMap); 184 #endif 185 } 186 187 /* 188 * Map part of a file into a shared, read-only memory segment. The "start" 189 * offset is absolute, not relative. 190 * 191 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 192 * value and does not disturb "pMap". 193 */ 194 int sysMapFileSegmentInShmem(int fd, off_t start, size_t length, 195 MemMapping* pMap) 196 { 197 #if !defined(__MINGW32__) 198 size_t actualLength; 199 off_t actualStart; 200 int adjust; 201 void* memPtr; 202 203 assert(pMap != NULL); 204 205 /* adjust to be page-aligned */ 206 adjust = start % SYSTEM_PAGE_SIZE; 207 actualStart = start - adjust; 208 actualLength = length + adjust; 209 210 memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED, 211 fd, actualStart); 212 if (memPtr == MAP_FAILED) { 213 ALOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s", 214 (int) actualLength, fd, (int) actualStart, strerror(errno)); 215 return -1; 216 } 217 218 pMap->baseAddr = memPtr; 219 pMap->baseLength = actualLength; 220 pMap->addr = (char*)memPtr + adjust; 221 pMap->length = length; 222 223 LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d", 224 (int) start, (int) length, 225 pMap->baseAddr, (int) pMap->baseLength, 226 pMap->addr, (int) pMap->length); 227 228 return 0; 229 #else 230 ALOGE("sysMapFileSegmentInShmem not implemented."); 231 return -1; 232 #endif 233 } 234 235 /* 236 * Change the access rights on one or more pages to read-only or read-write. 237 * 238 * Returns 0 on success. 239 */ 240 int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite, 241 MemMapping* pMap) 242 { 243 #if !defined(__MINGW32__) 244 /* 245 * Verify that "addr" is part of this mapping file. 246 */ 247 if (addr < pMap->baseAddr || 248 (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength) 249 { 250 ALOGE("Attempted to change %p; map is %p - %p", 251 addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength); 252 return -1; 253 } 254 255 /* 256 * Align "addr" to a page boundary and adjust "length" appropriately. 257 * (The address must be page-aligned, the length doesn't need to be, 258 * but we do need to ensure we cover the same range.) 259 */ 260 u1* alignAddr = (u1*) ((uintptr_t) addr & ~(SYSTEM_PAGE_SIZE-1)); 261 size_t alignLength = length + ((u1*) addr - alignAddr); 262 263 //ALOGI("%p/%zd --> %p/%zd", addr, length, alignAddr, alignLength); 264 int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ); 265 if (mprotect(alignAddr, alignLength, prot) != 0) { 266 int err = errno; 267 ALOGV("mprotect (%p,%zd,%d) failed: %s", 268 alignAddr, alignLength, prot, strerror(errno)); 269 return (errno != 0) ? errno : -1; 270 } 271 #endif 272 273 /* for "fake" mapping, no need to do anything */ 274 return 0; 275 } 276 277 /* 278 * Release a memory mapping. 279 */ 280 void sysReleaseShmem(MemMapping* pMap) 281 { 282 #if !defined(__MINGW32__) 283 if (pMap->baseAddr == NULL && pMap->baseLength == 0) 284 return; 285 286 if (munmap(pMap->baseAddr, pMap->baseLength) < 0) { 287 ALOGW("munmap(%p, %zd) failed: %s", 288 pMap->baseAddr, pMap->baseLength, strerror(errno)); 289 } else { 290 ALOGV("munmap(%p, %zd) succeeded", pMap->baseAddr, pMap->baseLength); 291 pMap->baseAddr = NULL; 292 pMap->baseLength = 0; 293 } 294 #else 295 /* Free the bits allocated by sysMapFileInShmem. */ 296 if (pMap->baseAddr != NULL) { 297 free(pMap->baseAddr); 298 pMap->baseAddr = NULL; 299 } 300 pMap->baseLength = 0; 301 #endif 302 } 303 304 /* 305 * Make a copy of a MemMapping. 306 */ 307 void sysCopyMap(MemMapping* dst, const MemMapping* src) 308 { 309 memcpy(dst, src, sizeof(MemMapping)); 310 } 311 312 /* 313 * Write until all bytes have been written. 314 * 315 * Returns 0 on success, or an errno value on failure. 316 */ 317 int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) 318 { 319 while (count != 0) { 320 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count)); 321 if (actual < 0) { 322 int err = errno; 323 ALOGE("%s: write failed: %s", logMsg, strerror(err)); 324 return err; 325 } else if (actual != (ssize_t) count) { 326 ALOGD("%s: partial write (will retry): (%d of %zd)", 327 logMsg, (int) actual, count); 328 buf = (const void*) (((const u1*) buf) + actual); 329 } 330 count -= actual; 331 } 332 333 return 0; 334 } 335 336 /* See documentation comment in header file. */ 337 int sysCopyFileToFile(int outFd, int inFd, size_t count) 338 { 339 const size_t kBufSize = 32768; 340 unsigned char buf[kBufSize]; 341 342 while (count != 0) { 343 size_t getSize = (count > kBufSize) ? kBufSize : count; 344 345 ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize)); 346 if (actual != (ssize_t) getSize) { 347 ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)", 348 (int) actual, getSize); 349 return -1; 350 } 351 352 if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0) 353 return -1; 354 355 count -= getSize; 356 } 357 358 return 0; 359 } 360