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 #ifdef HAVE_POSIX_FILEMAP 28 # include <sys/mman.h> 29 #endif 30 #include <limits.h> 31 #include <errno.h> 32 33 #include <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 #ifdef HAVE_POSIX_FILEMAP 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 /* 110 * Pull the contents of a file into an new shared memory segment. We grab 111 * everything from fd's current offset on. 112 * 113 * We need to know the length ahead of time so we can allocate a segment 114 * of sufficient size. 115 */ 116 int sysLoadFileInShmem(int fd, MemMapping* pMap) 117 { 118 #ifdef HAVE_POSIX_FILEMAP 119 off_t start; 120 size_t length, actual; 121 void* memPtr; 122 123 assert(pMap != NULL); 124 125 if (getFileStartAndLength(fd, &start, &length) < 0) 126 return -1; 127 128 memPtr = sysCreateAnonShmem(length); 129 if (memPtr == NULL) 130 return -1; 131 132 actual = read(fd, memPtr, length); 133 if (actual != length) { 134 ALOGE("only read %d of %d bytes", (int) actual, (int) length); 135 sysReleaseShmem(pMap); 136 return -1; 137 } 138 139 pMap->baseAddr = pMap->addr = memPtr; 140 pMap->baseLength = pMap->length = length; 141 142 return 0; 143 #else 144 ALOGE("sysLoadFileInShmem not implemented."); 145 return -1; 146 #endif 147 } 148 149 #ifndef HAVE_POSIX_FILEMAP 150 int sysFakeMapFile(int fd, MemMapping* pMap) 151 { 152 /* No MMAP, just fake it by copying the bits. 153 For Win32 we could use MapViewOfFile if really necessary 154 (see libs/utils/FileMap.cpp). 155 */ 156 off_t start; 157 size_t length; 158 void* memPtr; 159 160 assert(pMap != NULL); 161 162 if (getFileStartAndLength(fd, &start, &length) < 0) 163 return -1; 164 165 memPtr = malloc(length); 166 if (read(fd, memPtr, length) < 0) { 167 ALOGW("read(fd=%d, start=%d, length=%d) failed: %s", (int) length, 168 fd, (int) start, strerror(errno)); 169 return -1; 170 } 171 172 pMap->baseAddr = pMap->addr = memPtr; 173 pMap->baseLength = pMap->length = length; 174 175 return 0; 176 } 177 #endif 178 179 /* 180 * Map a file (from fd's current offset) into a shared, read-only memory 181 * segment. The file offset must be a multiple of the system page size. 182 * 183 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 184 * value and does not disturb "pMap". 185 */ 186 int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap) 187 { 188 #ifdef HAVE_POSIX_FILEMAP 189 off_t start; 190 size_t length; 191 void* memPtr; 192 193 assert(pMap != NULL); 194 195 if (getFileStartAndLength(fd, &start, &length) < 0) 196 return -1; 197 198 memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start); 199 if (memPtr == MAP_FAILED) { 200 ALOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s", (int) length, 201 fd, (int) start, strerror(errno)); 202 return -1; 203 } 204 205 pMap->baseAddr = pMap->addr = memPtr; 206 pMap->baseLength = pMap->length = length; 207 208 return 0; 209 #else 210 return sysFakeMapFile(fd, pMap); 211 #endif 212 } 213 214 /* 215 * Map a file (from fd's current offset) into a private, read-write memory 216 * segment that will be marked read-only (a/k/a "writable read-only"). The 217 * file offset must be a multiple of the system page size. 218 * 219 * In some cases the mapping will be fully writable (e.g. for files on 220 * FAT filesystems). 221 * 222 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 223 * value and does not disturb "pMap". 224 */ 225 int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap) 226 { 227 #ifdef HAVE_POSIX_FILEMAP 228 off_t start; 229 size_t length; 230 void* memPtr; 231 232 assert(pMap != NULL); 233 234 if (getFileStartAndLength(fd, &start, &length) < 0) 235 return -1; 236 237 memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, 238 fd, start); 239 if (memPtr == MAP_FAILED) { 240 ALOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s", (int) length, 241 fd, (int) start, strerror(errno)); 242 return -1; 243 } 244 if (mprotect(memPtr, length, PROT_READ) < 0) { 245 /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */ 246 int err = errno; 247 ALOGV("mprotect(%p, %d, PROT_READ) failed: %s", 248 memPtr, length, strerror(err)); 249 ALOGD("mprotect(RO) failed (%d), file will remain read-write", err); 250 } 251 252 pMap->baseAddr = pMap->addr = memPtr; 253 pMap->baseLength = pMap->length = length; 254 255 return 0; 256 #else 257 return sysFakeMapFile(fd, pMap); 258 #endif 259 } 260 261 /* 262 * Map part of a file into a shared, read-only memory segment. The "start" 263 * offset is absolute, not relative. 264 * 265 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 266 * value and does not disturb "pMap". 267 */ 268 int sysMapFileSegmentInShmem(int fd, off_t start, size_t length, 269 MemMapping* pMap) 270 { 271 #ifdef HAVE_POSIX_FILEMAP 272 size_t actualLength; 273 off_t actualStart; 274 int adjust; 275 void* memPtr; 276 277 assert(pMap != NULL); 278 279 /* adjust to be page-aligned */ 280 adjust = start % SYSTEM_PAGE_SIZE; 281 actualStart = start - adjust; 282 actualLength = length + adjust; 283 284 memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED, 285 fd, actualStart); 286 if (memPtr == MAP_FAILED) { 287 ALOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s", 288 (int) actualLength, fd, (int) actualStart, strerror(errno)); 289 return -1; 290 } 291 292 pMap->baseAddr = memPtr; 293 pMap->baseLength = actualLength; 294 pMap->addr = (char*)memPtr + adjust; 295 pMap->length = length; 296 297 LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d", 298 (int) start, (int) length, 299 pMap->baseAddr, (int) pMap->baseLength, 300 pMap->addr, (int) pMap->length); 301 302 return 0; 303 #else 304 ALOGE("sysMapFileSegmentInShmem not implemented."); 305 return -1; 306 #endif 307 } 308 309 /* 310 * Change the access rights on one or more pages to read-only or read-write. 311 * 312 * Returns 0 on success. 313 */ 314 int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite, 315 MemMapping* pMap) 316 { 317 #ifdef HAVE_POSIX_FILEMAP 318 /* 319 * Verify that "addr" is part of this mapping file. 320 */ 321 if (addr < pMap->baseAddr || 322 (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength) 323 { 324 ALOGE("Attempted to change %p; map is %p - %p", 325 addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength); 326 return -1; 327 } 328 329 /* 330 * Align "addr" to a page boundary and adjust "length" appropriately. 331 * (The address must be page-aligned, the length doesn't need to be, 332 * but we do need to ensure we cover the same range.) 333 */ 334 u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1)); 335 size_t alignLength = length + ((u1*) addr - alignAddr); 336 337 //ALOGI("%p/%zd --> %p/%zd", addr, length, alignAddr, alignLength); 338 int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ); 339 if (mprotect(alignAddr, alignLength, prot) != 0) { 340 int err = errno; 341 ALOGV("mprotect (%p,%zd,%d) failed: %s", 342 alignAddr, alignLength, prot, strerror(errno)); 343 return (errno != 0) ? errno : -1; 344 } 345 #endif 346 347 /* for "fake" mapping, no need to do anything */ 348 return 0; 349 } 350 351 /* 352 * Release a memory mapping. 353 */ 354 void sysReleaseShmem(MemMapping* pMap) 355 { 356 #ifdef HAVE_POSIX_FILEMAP 357 if (pMap->baseAddr == NULL && pMap->baseLength == 0) 358 return; 359 360 if (munmap(pMap->baseAddr, pMap->baseLength) < 0) { 361 ALOGW("munmap(%p, %d) failed: %s", 362 pMap->baseAddr, (int)pMap->baseLength, strerror(errno)); 363 } else { 364 ALOGV("munmap(%p, %d) succeeded", pMap->baseAddr, pMap->baseLength); 365 pMap->baseAddr = NULL; 366 pMap->baseLength = 0; 367 } 368 #else 369 /* Free the bits allocated by sysMapFileInShmem. */ 370 if (pMap->baseAddr != NULL) { 371 free(pMap->baseAddr); 372 pMap->baseAddr = NULL; 373 } 374 pMap->baseLength = 0; 375 #endif 376 } 377 378 /* 379 * Make a copy of a MemMapping. 380 */ 381 void sysCopyMap(MemMapping* dst, const MemMapping* src) 382 { 383 memcpy(dst, src, sizeof(MemMapping)); 384 } 385 386 /* 387 * Write until all bytes have been written. 388 * 389 * Returns 0 on success, or an errno value on failure. 390 */ 391 int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) 392 { 393 while (count != 0) { 394 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count)); 395 if (actual < 0) { 396 int err = errno; 397 ALOGE("%s: write failed: %s", logMsg, strerror(err)); 398 return err; 399 } else if (actual != (ssize_t) count) { 400 ALOGD("%s: partial write (will retry): (%d of %zd)", 401 logMsg, (int) actual, count); 402 buf = (const void*) (((const u1*) buf) + actual); 403 } 404 count -= actual; 405 } 406 407 return 0; 408 } 409 410 /* See documentation comment in header file. */ 411 int sysCopyFileToFile(int outFd, int inFd, size_t count) 412 { 413 const size_t kBufSize = 32768; 414 unsigned char buf[kBufSize]; 415 416 while (count != 0) { 417 size_t getSize = (count > kBufSize) ? kBufSize : count; 418 419 ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize)); 420 if (actual != (ssize_t) getSize) { 421 ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)", 422 (int) actual, getSize); 423 return -1; 424 } 425 426 if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0) 427 return -1; 428 429 count -= getSize; 430 } 431 432 return 0; 433 } 434