• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   */
sysCreateAnonShmem(size_t length)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   */
sysCreatePrivateMap(size_t length,MemMapping * pMap)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   */
getFileStartAndLength(int fd,off_t * start_,size_t * length_)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  #ifndef HAVE_POSIX_FILEMAP
sysFakeMapFile(int fd,MemMapping * pMap)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          return -1;
130      }
131  
132      pMap->baseAddr = pMap->addr = memPtr;
133      pMap->baseLength = pMap->length = length;
134  
135      return 0;
136  }
137  #endif
138  
139  /*
140   * Map a file (from fd's current offset) into a private, read-write memory
141   * segment that will be marked read-only (a/k/a "writable read-only").  The
142   * file offset must be a multiple of the system page size.
143   *
144   * In some cases the mapping will be fully writable (e.g. for files on
145   * FAT filesystems).
146   *
147   * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
148   * value and does not disturb "pMap".
149   */
sysMapFileInShmemWritableReadOnly(int fd,MemMapping * pMap)150  int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
151  {
152  #ifdef HAVE_POSIX_FILEMAP
153      off_t start;
154      size_t length;
155      void* memPtr;
156  
157      assert(pMap != NULL);
158  
159      if (getFileStartAndLength(fd, &start, &length) < 0)
160          return -1;
161  
162      memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
163              fd, start);
164      if (memPtr == MAP_FAILED) {
165          ALOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s", (int) length,
166              fd, (int) start, strerror(errno));
167          return -1;
168      }
169      if (mprotect(memPtr, length, PROT_READ) < 0) {
170          /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
171          int err = errno;
172          ALOGV("mprotect(%p, %d, PROT_READ) failed: %s",
173              memPtr, length, strerror(err));
174          ALOGD("mprotect(RO) failed (%d), file will remain read-write", err);
175      }
176  
177      pMap->baseAddr = pMap->addr = memPtr;
178      pMap->baseLength = pMap->length = length;
179  
180      return 0;
181  #else
182      return sysFakeMapFile(fd, pMap);
183  #endif
184  }
185  
186  /*
187   * Map part of a file into a shared, read-only memory segment.  The "start"
188   * offset is absolute, not relative.
189   *
190   * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
191   * value and does not disturb "pMap".
192   */
sysMapFileSegmentInShmem(int fd,off_t start,size_t length,MemMapping * pMap)193  int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
194      MemMapping* pMap)
195  {
196  #ifdef HAVE_POSIX_FILEMAP
197      size_t actualLength;
198      off_t actualStart;
199      int adjust;
200      void* memPtr;
201  
202      assert(pMap != NULL);
203  
204      /* adjust to be page-aligned */
205      adjust = start % SYSTEM_PAGE_SIZE;
206      actualStart = start - adjust;
207      actualLength = length + adjust;
208  
209      memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
210                  fd, actualStart);
211      if (memPtr == MAP_FAILED) {
212          ALOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s",
213              (int) actualLength, fd, (int) actualStart, strerror(errno));
214          return -1;
215      }
216  
217      pMap->baseAddr = memPtr;
218      pMap->baseLength = actualLength;
219      pMap->addr = (char*)memPtr + adjust;
220      pMap->length = length;
221  
222      LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d",
223          (int) start, (int) length,
224          pMap->baseAddr, (int) pMap->baseLength,
225          pMap->addr, (int) pMap->length);
226  
227      return 0;
228  #else
229      ALOGE("sysMapFileSegmentInShmem not implemented.");
230      return -1;
231  #endif
232  }
233  
234  /*
235   * Change the access rights on one or more pages to read-only or read-write.
236   *
237   * Returns 0 on success.
238   */
sysChangeMapAccess(void * addr,size_t length,int wantReadWrite,MemMapping * pMap)239  int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
240      MemMapping* pMap)
241  {
242  #ifdef HAVE_POSIX_FILEMAP
243      /*
244       * Verify that "addr" is part of this mapping file.
245       */
246      if (addr < pMap->baseAddr ||
247          (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
248      {
249          ALOGE("Attempted to change %p; map is %p - %p",
250              addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
251          return -1;
252      }
253  
254      /*
255       * Align "addr" to a page boundary and adjust "length" appropriately.
256       * (The address must be page-aligned, the length doesn't need to be,
257       * but we do need to ensure we cover the same range.)
258       */
259      u1* alignAddr = (u1*) ((uintptr_t) addr & ~(SYSTEM_PAGE_SIZE-1));
260      size_t alignLength = length + ((u1*) addr - alignAddr);
261  
262      //ALOGI("%p/%zd --> %p/%zd", addr, length, alignAddr, alignLength);
263      int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
264      if (mprotect(alignAddr, alignLength, prot) != 0) {
265          int err = errno;
266          ALOGV("mprotect (%p,%zd,%d) failed: %s",
267              alignAddr, alignLength, prot, strerror(errno));
268          return (errno != 0) ? errno : -1;
269      }
270  #endif
271  
272      /* for "fake" mapping, no need to do anything */
273      return 0;
274  }
275  
276  /*
277   * Release a memory mapping.
278   */
sysReleaseShmem(MemMapping * pMap)279  void sysReleaseShmem(MemMapping* pMap)
280  {
281  #ifdef HAVE_POSIX_FILEMAP
282      if (pMap->baseAddr == NULL && pMap->baseLength == 0)
283          return;
284  
285      if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
286          ALOGW("munmap(%p, %d) failed: %s",
287              pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
288      } else {
289          ALOGV("munmap(%p, %d) succeeded", pMap->baseAddr, pMap->baseLength);
290          pMap->baseAddr = NULL;
291          pMap->baseLength = 0;
292      }
293  #else
294      /* Free the bits allocated by sysMapFileInShmem. */
295      if (pMap->baseAddr != NULL) {
296        free(pMap->baseAddr);
297        pMap->baseAddr = NULL;
298      }
299      pMap->baseLength = 0;
300  #endif
301  }
302  
303  /*
304   * Make a copy of a MemMapping.
305   */
sysCopyMap(MemMapping * dst,const MemMapping * src)306  void sysCopyMap(MemMapping* dst, const MemMapping* src)
307  {
308      memcpy(dst, src, sizeof(MemMapping));
309  }
310  
311  /*
312   * Write until all bytes have been written.
313   *
314   * Returns 0 on success, or an errno value on failure.
315   */
sysWriteFully(int fd,const void * buf,size_t count,const char * logMsg)316  int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
317  {
318      while (count != 0) {
319          ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
320          if (actual < 0) {
321              int err = errno;
322              ALOGE("%s: write failed: %s", logMsg, strerror(err));
323              return err;
324          } else if (actual != (ssize_t) count) {
325              ALOGD("%s: partial write (will retry): (%d of %zd)",
326                  logMsg, (int) actual, count);
327              buf = (const void*) (((const u1*) buf) + actual);
328          }
329          count -= actual;
330      }
331  
332      return 0;
333  }
334  
335  /* See documentation comment in header file. */
sysCopyFileToFile(int outFd,int inFd,size_t count)336  int sysCopyFileToFile(int outFd, int inFd, size_t count)
337  {
338      const size_t kBufSize = 32768;
339      unsigned char buf[kBufSize];
340  
341      while (count != 0) {
342          size_t getSize = (count > kBufSize) ? kBufSize : count;
343  
344          ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize));
345          if (actual != (ssize_t) getSize) {
346              ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)",
347                  (int) actual, getSize);
348              return -1;
349          }
350  
351          if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0)
352              return -1;
353  
354          count -= getSize;
355      }
356  
357      return 0;
358  }
359