Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 1 | /* Copyright (C) 2007-2010 The Android Open Source Project |
| 2 | ** |
| 3 | ** This software is licensed under the terms of the GNU General Public |
| 4 | ** License version 2, as published by the Free Software Foundation, and |
| 5 | ** may be copied, distributed, and modified under those terms. |
| 6 | ** |
| 7 | ** This program is distributed in the hope that it will be useful, |
| 8 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | ** GNU General Public License for more details. |
| 11 | */ |
| 12 | |
| 13 | /* |
| 14 | * Contains implementation of routines that implement platform-independent |
| 15 | * file I/O. |
| 16 | */ |
| 17 | |
| 18 | #include "stddef.h" |
| 19 | #include "sys/types.h" |
| 20 | #include "errno.h" |
| 21 | #ifdef WIN32 |
Raphael | aea1b87 | 2010-04-14 12:16:28 -0700 | [diff] [blame] | 22 | #include "windows.h" |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 23 | #else // WIN32 |
| 24 | #include <sys/mman.h> |
| 25 | #endif // WIN32 |
| 26 | #include <sys/stat.h> |
| 27 | #include <fcntl.h> |
| 28 | #include <unistd.h> |
| 29 | |
| 30 | #include "mapfile.h" |
| 31 | |
| 32 | MapFile* |
| 33 | mapfile_open(const char* path, int oflag, int share_mode) |
| 34 | { |
| 35 | #ifdef WIN32 |
| 36 | DWORD win32_share; |
David 'Digit' Turner | 4e024bb | 2010-09-22 14:19:28 +0200 | [diff] [blame] | 37 | DWORD win32_desired_access = GENERIC_READ; |
| 38 | DWORD win32_disposition = OPEN_EXISTING; |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 39 | DWORD win32_flags; |
| 40 | |
| 41 | /* Convert to Win32 desired access. */ |
| 42 | if ((oflag & O_RDWR) == O_RDWR) { |
| 43 | win32_desired_access = GENERIC_READ | GENERIC_WRITE; |
| 44 | } else if ((oflag & O_ACCMODE) == O_RDONLY) { |
| 45 | win32_desired_access = GENERIC_READ; |
| 46 | } else if ((oflag & O_WRONLY) == O_WRONLY) { |
| 47 | win32_desired_access = GENERIC_WRITE; |
| 48 | } |
| 49 | |
| 50 | /* Convert to Win32 sharing. */ |
| 51 | win32_share = 0; |
| 52 | if ((share_mode & S_IWRITE) != 0) { |
| 53 | win32_share |= FILE_SHARE_WRITE; |
| 54 | } |
| 55 | if ((share_mode & S_IREAD) != 0) { |
| 56 | win32_share |= FILE_SHARE_READ; |
| 57 | } |
| 58 | |
| 59 | /* Convert to Win32 disposition. */ |
| 60 | if ((oflag & O_CREAT) == O_CREAT) { |
| 61 | if ((oflag & O_EXCL) == O_EXCL) { |
| 62 | win32_disposition = CREATE_NEW; |
| 63 | } else { |
| 64 | win32_disposition = OPEN_ALWAYS; |
| 65 | } |
| 66 | } if ((oflag & O_TRUNC) == O_TRUNC) { |
| 67 | win32_desired_access = TRUNCATE_EXISTING; |
| 68 | } else { |
| 69 | win32_disposition = OPEN_EXISTING; |
| 70 | } |
| 71 | |
| 72 | /* Convert to Win32 flags. */ |
| 73 | win32_flags = 0; |
| 74 | #if defined(O_DSYNC) |
| 75 | if ((oflag & O_DSYNC) == O_DSYNC || |
| 76 | (oflag & O_RSYNC) == O_RSYNC || |
| 77 | (oflag & O_RSYNC) == O_SYNC) { |
| 78 | win32_flags |= FILE_FLAG_WRITE_THROUGH; |
| 79 | } |
| 80 | #endif // O_DSYNC |
| 81 | |
| 82 | HANDLE file_handle = CreateFile(path, win32_desired_access, win32_share, |
| 83 | NULL, win32_disposition, win32_flags, NULL); |
| 84 | if (file_handle == INVALID_HANDLE_VALUE) { |
| 85 | errno = GetLastError(); |
| 86 | } |
| 87 | #else // WIN32 |
| 88 | int file_handle = open(path, oflag, share_mode); |
| 89 | #endif // WIN32 |
| 90 | |
David 'Digit' Turner | d9b6cb9 | 2010-10-20 19:07:28 +0200 | [diff] [blame] | 91 | return (MapFile*)(ptrdiff_t)file_handle; |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | int |
| 95 | mapfile_close(MapFile* handle) |
| 96 | { |
| 97 | #ifdef WIN32 |
| 98 | if (CloseHandle(handle)) { |
| 99 | return 0; |
| 100 | } else { |
| 101 | errno = GetLastError(); |
| 102 | return -1; |
| 103 | } |
| 104 | #else // WIN32 |
David 'Digit' Turner | d9b6cb9 | 2010-10-20 19:07:28 +0200 | [diff] [blame] | 105 | return close((int)(ptrdiff_t)handle); |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 106 | #endif // WIN32 |
| 107 | } |
| 108 | |
| 109 | ssize_t |
| 110 | mapfile_read(MapFile* handle, void* buf, size_t nbyte) |
| 111 | { |
| 112 | #ifdef WIN32 |
| 113 | ssize_t ret_bytes; |
| 114 | DWORD read_bytes; |
| 115 | if (ReadFile(handle, buf, nbyte, &read_bytes, NULL)) { |
| 116 | ret_bytes = (ssize_t)read_bytes; |
| 117 | } else { |
| 118 | errno = GetLastError(); |
| 119 | ret_bytes = -1; |
| 120 | } |
| 121 | return ret_bytes; |
| 122 | #else // WIN32 |
| 123 | ssize_t ret; |
| 124 | do { |
David 'Digit' Turner | d9b6cb9 | 2010-10-20 19:07:28 +0200 | [diff] [blame] | 125 | ret = read((int)(ptrdiff_t)handle, buf, nbyte); |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 126 | } while (ret < 0 && errno == EINTR); |
| 127 | return ret; |
| 128 | #endif // WIN32 |
| 129 | } |
| 130 | |
| 131 | ssize_t |
| 132 | mapfile_read_at(MapFile* handle, size_t offset, void* buf, size_t nbyte) |
| 133 | { |
| 134 | #ifdef WIN32 |
| 135 | LARGE_INTEGER convert; |
| 136 | convert.QuadPart = offset; |
| 137 | if ((SetFilePointer(handle, convert.LowPart, &convert.HighPart, |
| 138 | FILE_BEGIN) == INVALID_SET_FILE_POINTER) && |
| 139 | (GetLastError() != NO_ERROR)) { |
| 140 | errno = GetLastError(); |
| 141 | return -1; |
| 142 | } |
| 143 | return mapfile_read(handle, buf, nbyte); |
| 144 | #else // WIN32 |
David 'Digit' Turner | d9b6cb9 | 2010-10-20 19:07:28 +0200 | [diff] [blame] | 145 | ssize_t res = lseek((int)(ptrdiff_t)handle, offset, SEEK_SET); |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 146 | return res >= 0 ? mapfile_read(handle, buf, nbyte) : res; |
| 147 | #endif // WIN32 |
| 148 | } |
| 149 | |
| 150 | void* |
| 151 | mapfile_map(MapFile* handle, |
| 152 | size_t offset, |
| 153 | size_t size, |
| 154 | int prot, |
| 155 | void** mapped_offset, |
| 156 | size_t* mapped_size) |
| 157 | { |
| 158 | void* mapped_at = NULL; |
| 159 | size_t align_mask; |
| 160 | size_t map_offset; |
| 161 | size_t map_size; |
| 162 | |
| 163 | /* Get the mask for mapping offset alignment. */ |
| 164 | #ifdef WIN32 |
| 165 | DWORD win32_prot; |
| 166 | DWORD win32_map; |
| 167 | HANDLE map_handle; |
| 168 | LARGE_INTEGER converter; |
| 169 | SYSTEM_INFO sys_info; |
| 170 | GetSystemInfo(&sys_info); |
| 171 | align_mask = sys_info.dwAllocationGranularity - 1; |
| 172 | #else // WIN32 |
| 173 | align_mask = getpagesize() - 1; |
| 174 | #endif // WIN32 |
| 175 | |
| 176 | /* Adjust mapping offset and mapping size accordingly to |
| 177 | * the mapping alignment requirements. */ |
| 178 | map_offset = offset & ~align_mask; |
| 179 | map_size = (size_t)(offset - map_offset + size); |
| 180 | |
| 181 | /* Make sure mapping size doesn't exceed 4G. */ |
| 182 | if (map_size < size) { |
| 183 | errno = EFBIG; |
| 184 | return NULL; |
| 185 | } |
| 186 | |
| 187 | /* Map the section. */ |
| 188 | #ifdef WIN32 |
| 189 | /* Convert to Win32 page protection and mapping type. */ |
| 190 | win32_prot = PAGE_READONLY; |
| 191 | win32_map = FILE_MAP_READ; |
| 192 | if (prot != PROT_NONE) { |
| 193 | if ((prot & (PROT_WRITE | PROT_EXEC)) == 0) { |
| 194 | win32_prot = PAGE_READONLY; |
| 195 | win32_map = FILE_MAP_READ; |
| 196 | } else if ((prot & (PROT_WRITE | PROT_EXEC)) == |
| 197 | (PROT_WRITE | PROT_EXEC)) { |
| 198 | win32_prot = PAGE_EXECUTE_READWRITE; |
| 199 | win32_map = FILE_MAP_WRITE; |
| 200 | } else if ((prot & PROT_WRITE) == PROT_WRITE) { |
| 201 | win32_prot = PAGE_READWRITE; |
| 202 | win32_map = FILE_MAP_WRITE; |
| 203 | } else if ((prot & PROT_EXEC) == PROT_EXEC) { |
| 204 | win32_prot = PAGE_EXECUTE_READ; |
| 205 | win32_map = FILE_MAP_READ; |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | converter.QuadPart = map_offset + map_size; |
| 210 | map_handle = CreateFileMapping(handle, NULL, win32_prot, |
| 211 | converter.HighPart, converter.LowPart, NULL); |
| 212 | if (map_handle != NULL) { |
| 213 | converter.QuadPart = map_offset; |
| 214 | mapped_at = MapViewOfFile(map_handle, win32_map, converter.HighPart, |
| 215 | converter.LowPart, map_size); |
| 216 | /* Memory mapping (if successful) will hold extra references to the |
| 217 | * mapping, so we can close it right after we mapped file view. */ |
| 218 | CloseHandle(map_handle); |
| 219 | } |
| 220 | if (mapped_at == NULL) { |
| 221 | errno = GetLastError(); |
| 222 | return NULL; |
| 223 | } |
| 224 | #else // WIN32 |
| 225 | mapped_at = |
David 'Digit' Turner | d9b6cb9 | 2010-10-20 19:07:28 +0200 | [diff] [blame] | 226 | mmap(0, map_size, PROT_READ, MAP_SHARED, (int)(ptrdiff_t)handle, map_offset); |
Vladimir Chtchetkine | 8339d18 | 2010-03-25 10:57:29 -0700 | [diff] [blame] | 227 | if (mapped_at == MAP_FAILED) { |
| 228 | return NULL; |
| 229 | } |
| 230 | #endif // WIN32 |
| 231 | |
| 232 | *mapped_offset = (char*)mapped_at + (offset - map_offset); |
| 233 | *mapped_size = size; |
| 234 | |
| 235 | return mapped_at; |
| 236 | } |
| 237 | |
| 238 | int |
| 239 | mapfile_unmap(void* mapped_at, size_t len) |
| 240 | { |
| 241 | #ifdef WIN32 |
| 242 | if (!UnmapViewOfFile(mapped_at)) { |
| 243 | errno = GetLastError(); |
| 244 | return -1; |
| 245 | } |
| 246 | return 0; |
| 247 | #else // WIN32 |
| 248 | return munmap(mapped_at, len); |
| 249 | #endif // WIN32 |
| 250 | } |