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