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 <sys/types.h> 12 #include <sys/stat.h> 13 #include <fcntl.h> 14 #include <limits.h> 15 #include <errno.h> 16 #include <assert.h> 17 18 #define LOG_TAG "sysutil" 19 #include "Log.h" 20 #include "SysUtil.h" 21 22 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) 23 { 24 off_t start, end; 25 size_t length; 26 27 assert(start_ != NULL); 28 assert(length_ != NULL); 29 30 start = lseek(fd, 0L, SEEK_CUR); 31 end = lseek(fd, 0L, SEEK_END); 32 (void) lseek(fd, start, SEEK_SET); 33 34 if (start == (off_t) -1 || end == (off_t) -1) { 35 LOGE("could not determine length of file\n"); 36 return -1; 37 } 38 39 length = end - start; 40 if (length == 0) { 41 LOGE("file is empty\n"); 42 return -1; 43 } 44 45 *start_ = start; 46 *length_ = length; 47 48 return 0; 49 } 50 51 /* 52 * Map a file (from fd's current offset) into a private, read-only memory 53 * segment. The file offset must be a multiple of the page size. 54 * 55 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero 56 * value and does not disturb "pMap". 57 */ 58 static int sysMapFD(int fd, MemMapping* pMap) 59 { 60 off_t start; 61 size_t length; 62 void* memPtr; 63 64 assert(pMap != NULL); 65 66 if (getFileStartAndLength(fd, &start, &length) < 0) 67 return -1; 68 69 memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start); 70 if (memPtr == MAP_FAILED) { 71 LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length, 72 fd, (int) start, strerror(errno)); 73 return -1; 74 } 75 76 pMap->addr = memPtr; 77 pMap->length = length; 78 pMap->range_count = 1; 79 pMap->ranges = malloc(sizeof(MappedRange)); 80 pMap->ranges[0].addr = memPtr; 81 pMap->ranges[0].length = length; 82 83 return 0; 84 } 85 86 static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) 87 { 88 char block_dev[PATH_MAX+1]; 89 size_t size; 90 unsigned int blksize; 91 unsigned int blocks; 92 unsigned int range_count; 93 unsigned int i; 94 95 if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) { 96 LOGW("failed to read block device from header\n"); 97 return -1; 98 } 99 for (i = 0; i < sizeof(block_dev); ++i) { 100 if (block_dev[i] == '\n') { 101 block_dev[i] = 0; 102 break; 103 } 104 } 105 106 if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) { 107 LOGW("failed to parse block map header\n"); 108 return -1; 109 } 110 111 blocks = ((size-1) / blksize) + 1; 112 113 pMap->range_count = range_count; 114 pMap->ranges = malloc(range_count * sizeof(MappedRange)); 115 memset(pMap->ranges, 0, range_count * sizeof(MappedRange)); 116 117 // Reserve enough contiguous address space for the whole file. 118 unsigned char* reserve; 119 reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 120 if (reserve == MAP_FAILED) { 121 LOGW("failed to reserve address space: %s\n", strerror(errno)); 122 return -1; 123 } 124 125 pMap->ranges[range_count-1].addr = reserve; 126 pMap->ranges[range_count-1].length = blocks * blksize; 127 128 int fd = open(block_dev, O_RDONLY); 129 if (fd < 0) { 130 LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno)); 131 return -1; 132 } 133 134 unsigned char* next = reserve; 135 for (i = 0; i < range_count; ++i) { 136 int start, end; 137 if (fscanf(mapf, "%d %d\n", &start, &end) != 2) { 138 LOGW("failed to parse range %d in block map\n", i); 139 return -1; 140 } 141 142 void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); 143 if (addr == MAP_FAILED) { 144 LOGW("failed to map block %d: %s\n", i, strerror(errno)); 145 return -1; 146 } 147 pMap->ranges[i].addr = addr; 148 pMap->ranges[i].length = (end-start)*blksize; 149 150 next += pMap->ranges[i].length; 151 } 152 153 pMap->addr = reserve; 154 pMap->length = size; 155 156 LOGI("mmapped %d ranges\n", range_count); 157 158 return 0; 159 } 160 161 int sysMapFile(const char* fn, MemMapping* pMap) 162 { 163 memset(pMap, 0, sizeof(*pMap)); 164 165 if (fn && fn[0] == '@') { 166 // A map of blocks 167 FILE* mapf = fopen(fn+1, "r"); 168 if (mapf == NULL) { 169 LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno)); 170 return -1; 171 } 172 173 if (sysMapBlockFile(mapf, pMap) != 0) { 174 LOGW("Map of '%s' failed\n", fn); 175 return -1; 176 } 177 178 fclose(mapf); 179 } else { 180 // This is a regular file. 181 int fd = open(fn, O_RDONLY, 0); 182 if (fd < 0) { 183 LOGE("Unable to open '%s': %s\n", fn, strerror(errno)); 184 return -1; 185 } 186 187 if (sysMapFD(fd, pMap) != 0) { 188 LOGE("Map of '%s' failed\n", fn); 189 close(fd); 190 return -1; 191 } 192 193 close(fd); 194 } 195 return 0; 196 } 197 198 /* 199 * Release a memory mapping. 200 */ 201 void sysReleaseMap(MemMapping* pMap) 202 { 203 int i; 204 for (i = 0; i < pMap->range_count; ++i) { 205 if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) { 206 LOGW("munmap(%p, %d) failed: %s\n", 207 pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno)); 208 } 209 } 210 free(pMap->ranges); 211 pMap->ranges = NULL; 212 pMap->range_count = 0; 213 } 214