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
22#include "windows.h"
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
32MapFile*
33mapfile_open(const char* path, int oflag, int share_mode)
34{
35#ifdef WIN32
36    DWORD win32_share;
37    DWORD win32_desired_access = GENERIC_READ;
38    DWORD win32_disposition = OPEN_EXISTING;
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
91    return (MapFile*)(ptrdiff_t)file_handle;
92}
93
94int
95mapfile_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
105    return close((int)(ptrdiff_t)handle);
106#endif  // WIN32
107}
108
109ssize_t
110mapfile_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 {
125        ret = read((int)(ptrdiff_t)handle, buf, nbyte);
126    } while (ret < 0 && errno == EINTR);
127    return ret;
128#endif  // WIN32
129}
130
131ssize_t
132mapfile_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
145    ssize_t res = lseek((int)(ptrdiff_t)handle, offset, SEEK_SET);
146    return res >= 0 ? mapfile_read(handle, buf, nbyte) : res;
147#endif  // WIN32
148}
149
150void*
151mapfile_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 =
226        mmap(0, map_size, PROT_READ, MAP_SHARED, (int)(ptrdiff_t)handle, map_offset);
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
238int
239mapfile_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}
251