1f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 2f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 3f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 4f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * you may not use this file except in compliance with the License. 6f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * You may obtain a copy of the License at 7f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 8f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 10f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * See the License for the specific language governing permissions and 14f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * limitations under the License. 15f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 1699409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project 17f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 18f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * System utilities. 19f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 20f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include "DexFile.h" 21f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include "SysUtil.h" 22f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 23f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <stdlib.h> 24f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <stdio.h> 25f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <unistd.h> 26f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <string.h> 27f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#ifdef HAVE_POSIX_FILEMAP 2864896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden# include <sys/mman.h> 29f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#endif 30f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <limits.h> 31f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <errno.h> 32f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 3364896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd 34f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 35f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 36f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 37f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Create an anonymous shared memory segment large enough to hold "length" 38f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * bytes. The actual segment may be larger because mmap() operates on 39f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * page boundaries (usually 4K). 40f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 41f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectstatic void* sysCreateAnonShmem(size_t length) 42f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 43f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#ifdef HAVE_POSIX_FILEMAP 44f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project void* ptr; 45f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 46f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, 47f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project MAP_SHARED | MAP_ANON, -1, 0); 48f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (ptr == MAP_FAILED) { 49e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("mmap(%d, RW, SHARED|ANON) failed: %s", (int) length, 50f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project strerror(errno)); 51f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return NULL; 52f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 53f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 54f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return ptr; 55f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#else 56c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("sysCreateAnonShmem not implemented."); 57f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return NULL; 58f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#endif 59f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 60f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 6199409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project/* 6299409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project * Create a private anonymous storage area. 6399409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project */ 6499409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Projectint sysCreatePrivateMap(size_t length, MemMapping* pMap) 6599409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project{ 6699409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project void* memPtr; 6799409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project 6899409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project memPtr = sysCreateAnonShmem(length); 6999409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project if (memPtr == NULL) 7099409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project return -1; 7199409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project 7299409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project pMap->addr = pMap->baseAddr = memPtr; 7399409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project pMap->length = pMap->baseLength = length; 7499409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project return 0; 7599409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project} 7699409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project 7799409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project/* 7899409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project * Determine the current offset and remaining length of the open file. 7999409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project */ 80f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectstatic int getFileStartAndLength(int fd, off_t *start_, size_t *length_) 81f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 82f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project off_t start, end; 83f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project size_t length; 84f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 85f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(start_ != NULL); 86f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(length_ != NULL); 87f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 88f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project start = lseek(fd, 0L, SEEK_CUR); 89f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project end = lseek(fd, 0L, SEEK_END); 90f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project (void) lseek(fd, start, SEEK_SET); 91f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 92f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (start == (off_t) -1 || end == (off_t) -1) { 93c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("could not determine length of file"); 94f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 95f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 96f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 97f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project length = end - start; 98f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (length == 0) { 99c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("file is empty"); 100f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 101f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 102f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 103f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *start_ = start; 104f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *length_ = length; 105f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 106f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return 0; 107f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 108f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 109f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 110f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Pull the contents of a file into an new shared memory segment. We grab 111f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * everything from fd's current offset on. 112f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 113f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * We need to know the length ahead of time so we can allocate a segment 114f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * of sufficient size. 115f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 116f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectint sysLoadFileInShmem(int fd, MemMapping* pMap) 117f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 118f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#ifdef HAVE_POSIX_FILEMAP 119f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project off_t start; 120f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project size_t length, actual; 121f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project void* memPtr; 122f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 123f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(pMap != NULL); 124f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 125f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (getFileStartAndLength(fd, &start, &length) < 0) 126f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 127f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 128f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project memPtr = sysCreateAnonShmem(length); 129f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (memPtr == NULL) 130f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 131f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 132f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project actual = read(fd, memPtr, length); 133f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (actual != length) { 134c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("only read %d of %d bytes", (int) actual, (int) length); 135f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project sysReleaseShmem(pMap); 136f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 137f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 138f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 139f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr = pMap->addr = memPtr; 140f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseLength = pMap->length = length; 141f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 142f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return 0; 143f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#else 144c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("sysLoadFileInShmem not implemented."); 145f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 146f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#endif 147f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 148f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 149518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden#ifndef HAVE_POSIX_FILEMAP 150518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFaddenint sysFakeMapFile(int fd, MemMapping* pMap) 151518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden{ 152518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden /* No MMAP, just fake it by copying the bits. 153518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden For Win32 we could use MapViewOfFile if really necessary 154518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden (see libs/utils/FileMap.cpp). 155518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden */ 156518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden off_t start; 157518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden size_t length; 158518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden void* memPtr; 159518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden 160518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden assert(pMap != NULL); 161518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden 162518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden if (getFileStartAndLength(fd, &start, &length) < 0) 163518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden return -1; 164518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden 165518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden memPtr = malloc(length); 166518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden if (read(fd, memPtr, length) < 0) { 167e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("read(fd=%d, start=%d, length=%d) failed: %s", (int) length, 168518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden fd, (int) start, strerror(errno)); 169518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden return -1; 170518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden } 171518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden 172518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden pMap->baseAddr = pMap->addr = memPtr; 173518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden pMap->baseLength = pMap->length = length; 174518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden 175518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden return 0; 176518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden} 177518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden#endif 178518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden 179f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 180f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Map a file (from fd's current offset) into a shared, read-only memory 18196516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * segment. The file offset must be a multiple of the system page size. 182f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 183f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 184f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * value and does not disturb "pMap". 185f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 186b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFaddenint sysMapFileInShmemReadOnly(int fd, MemMapping* pMap) 187b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden{ 188b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden#ifdef HAVE_POSIX_FILEMAP 189b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden off_t start; 190b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden size_t length; 191b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden void* memPtr; 192b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 193b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden assert(pMap != NULL); 194b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 195b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden if (getFileStartAndLength(fd, &start, &length) < 0) 196b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden return -1; 197b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 198b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start); 199b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden if (memPtr == MAP_FAILED) { 200e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s", (int) length, 201b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden fd, (int) start, strerror(errno)); 202b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden return -1; 203b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden } 204b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 205b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden pMap->baseAddr = pMap->addr = memPtr; 206b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden pMap->baseLength = pMap->length = length; 207b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 208b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden return 0; 209b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden#else 210518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden return sysFakeMapFile(fd, pMap); 211b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden#endif 212b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden} 213b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 214b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden/* 215b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * Map a file (from fd's current offset) into a private, read-write memory 216b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * segment that will be marked read-only (a/k/a "writable read-only"). The 217b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * file offset must be a multiple of the system page size. 218b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * 219b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * In some cases the mapping will be fully writable (e.g. for files on 220b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * FAT filesystems). 221b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * 222b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 223b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden * value and does not disturb "pMap". 224b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden */ 225b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFaddenint sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap) 226f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 227f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#ifdef HAVE_POSIX_FILEMAP 228f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project off_t start; 229f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project size_t length; 230f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project void* memPtr; 231f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 232f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(pMap != NULL); 233f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 234f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (getFileStartAndLength(fd, &start, &length) < 0) 235f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 236f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 23796516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, 23896516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden fd, start); 239f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (memPtr == MAP_FAILED) { 240e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s", (int) length, 241f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project fd, (int) start, strerror(errno)); 242f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 243f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 24496516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden if (mprotect(memPtr, length, PROT_READ) < 0) { 245b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */ 246b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden int err = errno; 24792c1f6f1b4249e4e379452ee7b49f027052bf4ceSteve Block ALOGV("mprotect(%p, %d, PROT_READ) failed: %s", 248b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden memPtr, length, strerror(err)); 249062bf509a77fce9dfcb7e7b2e401cf2a124d83d5Steve Block ALOGD("mprotect(RO) failed (%d), file will remain read-write", err); 25096516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden } 251f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 252f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr = pMap->addr = memPtr; 253f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseLength = pMap->length = length; 254f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 255f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return 0; 256f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#else 257518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden return sysFakeMapFile(fd, pMap); 258b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden#endif 259b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden} 260b5ebe47515c9750c7347557075d3714ba7671aa9Andy McFadden 261f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 2628911f7a2222124ba724a4a9281555b74d0e098e2Andy McFadden * Map part of a file into a shared, read-only memory segment. The "start" 2638911f7a2222124ba724a4a9281555b74d0e098e2Andy McFadden * offset is absolute, not relative. 264f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 265f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 266f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * value and does not disturb "pMap". 267f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 2688911f7a2222124ba724a4a9281555b74d0e098e2Andy McFaddenint sysMapFileSegmentInShmem(int fd, off_t start, size_t length, 269f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project MemMapping* pMap) 270f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 271f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#ifdef HAVE_POSIX_FILEMAP 2728911f7a2222124ba724a4a9281555b74d0e098e2Andy McFadden size_t actualLength; 273f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project off_t actualStart; 274f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project int adjust; 275f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project void* memPtr; 276f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 277f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(pMap != NULL); 278f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 279f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* adjust to be page-aligned */ 28064896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden adjust = start % SYSTEM_PAGE_SIZE; 281f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project actualStart = start - adjust; 282f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project actualLength = length + adjust; 283f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 284f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED, 285f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project fd, actualStart); 286f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (memPtr == MAP_FAILED) { 287e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s", 288f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project (int) actualLength, fd, (int) actualStart, strerror(errno)); 289f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 290f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 291f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 292f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr = memPtr; 293f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseLength = actualLength; 294f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->addr = (char*)memPtr + adjust; 295f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->length = length; 296f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 297614dca3bb487f17ef8ea37a82a22dd6c4abc027eDan Bornstein LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d", 298f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project (int) start, (int) length, 299f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr, (int) pMap->baseLength, 300f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->addr, (int) pMap->length); 301f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 302f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return 0; 303f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#else 304c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("sysMapFileSegmentInShmem not implemented."); 305f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return -1; 306f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#endif 307f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 308f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 309f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 31096516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * Change the access rights on one or more pages to read-only or read-write. 31196516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * 31296516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * Returns 0 on success. 31396516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden */ 31496516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFaddenint sysChangeMapAccess(void* addr, size_t length, int wantReadWrite, 31596516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden MemMapping* pMap) 31696516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden{ 317518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden#ifdef HAVE_POSIX_FILEMAP 31896516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden /* 31996516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * Verify that "addr" is part of this mapping file. 32096516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden */ 32196516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden if (addr < pMap->baseAddr || 32296516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength) 32396516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden { 324c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("Attempted to change %p; map is %p - %p", 32596516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength); 32696516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden return -1; 32796516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden } 32896516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden 32996516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden /* 33096516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * Align "addr" to a page boundary and adjust "length" appropriately. 33196516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * (The address must be page-aligned, the length doesn't need to be, 33296516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden * but we do need to ensure we cover the same range.) 33396516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden */ 334b210a9f9c7ae17e2028a86d9a4e9a3b35472862aSangWook Han u1* alignAddr = (u1*) ((uintptr_t) addr & ~(SYSTEM_PAGE_SIZE-1)); 33596516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden size_t alignLength = length + ((u1*) addr - alignAddr); 33696516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden 3374308417beec548c2b2c06ecec4f7f4a965b09fb2Steve Block //ALOGI("%p/%zd --> %p/%zd", addr, length, alignAddr, alignLength); 33896516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ); 33996516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden if (mprotect(alignAddr, alignLength, prot) != 0) { 34096516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden int err = errno; 34192c1f6f1b4249e4e379452ee7b49f027052bf4ceSteve Block ALOGV("mprotect (%p,%zd,%d) failed: %s", 34296516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden alignAddr, alignLength, prot, strerror(errno)); 34396516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden return (errno != 0) ? errno : -1; 34496516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden } 345518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden#endif 34696516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden 347518925b0103405fd0fa03cde1b9e58acf76a6a64Andy McFadden /* for "fake" mapping, no need to do anything */ 34896516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden return 0; 34996516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden} 35096516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden 35196516932f1557d8f48a8b2dbbb885af01a11ef6eAndy McFadden/* 352f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Release a memory mapping. 353f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 354f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectvoid sysReleaseShmem(MemMapping* pMap) 355f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 356f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#ifdef HAVE_POSIX_FILEMAP 357f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (pMap->baseAddr == NULL && pMap->baseLength == 0) 358f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return; 359f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 360f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (munmap(pMap->baseAddr, pMap->baseLength) < 0) { 361e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("munmap(%p, %d) failed: %s", 362f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr, (int)pMap->baseLength, strerror(errno)); 363f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } else { 36492c1f6f1b4249e4e379452ee7b49f027052bf4ceSteve Block ALOGV("munmap(%p, %d) succeeded", pMap->baseAddr, pMap->baseLength); 365f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr = NULL; 366f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseLength = 0; 367f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 368f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#else 369f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* Free the bits allocated by sysMapFileInShmem. */ 370f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (pMap->baseAddr != NULL) { 371f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project free(pMap->baseAddr); 372f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseAddr = NULL; 373f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 374f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project pMap->baseLength = 0; 375f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#endif 376f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 377f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 378f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 379f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Make a copy of a MemMapping. 380f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 381f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectvoid sysCopyMap(MemMapping* dst, const MemMapping* src) 382f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 383f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project memcpy(dst, src, sizeof(MemMapping)); 384f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 385f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 38664896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden/* 38764896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden * Write until all bytes have been written. 38864896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden * 38964896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden * Returns 0 on success, or an errno value on failure. 39064896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden */ 39164896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFaddenint sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) 39264896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden{ 39364896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden while (count != 0) { 39464896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count)); 39564896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden if (actual < 0) { 39664896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden int err = errno; 397c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("%s: write failed: %s", logMsg, strerror(err)); 39864896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden return err; 39964896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden } else if (actual != (ssize_t) count) { 400062bf509a77fce9dfcb7e7b2e401cf2a124d83d5Steve Block ALOGD("%s: partial write (will retry): (%d of %zd)", 40164896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden logMsg, (int) actual, count); 40264896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden buf = (const void*) (((const u1*) buf) + actual); 40364896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden } 40464896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden count -= actual; 40564896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden } 40664896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden 40764896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden return 0; 40864896a2543ee54e47c586f4cf26f54e7fdb366bdAndy McFadden} 409650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein 410650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein/* See documentation comment in header file. */ 411650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornsteinint sysCopyFileToFile(int outFd, int inFd, size_t count) 412650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein{ 413650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein const size_t kBufSize = 32768; 414650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein unsigned char buf[kBufSize]; 415650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein 416650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein while (count != 0) { 417650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein size_t getSize = (count > kBufSize) ? kBufSize : count; 418650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein 419650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize)); 420650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein if (actual != (ssize_t) getSize) { 421e8e1ddccd616e8226b7cc1e4e9fdb327429249e8Steve Block ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)", 422650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein (int) actual, getSize); 423650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein return -1; 424650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein } 425650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein 426650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0) 427650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein return -1; 428650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein 429650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein count -= getSize; 430650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein } 431650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein 432650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein return 0; 433650177ee24fbe07cdd9ad9d8913fbf44cf44be13Dan Bornstein} 434