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 "android/utils/mapfile.h"
19
20#include "stddef.h"
21#include "sys/types.h"
22#include "errno.h"
23#ifdef  WIN32
24#include "windows.h"
25#else   // WIN32
26#include <sys/mman.h>
27#endif  // WIN32
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <unistd.h>
31
32#include "android/utils/eintr_wrapper.h"
33
34MapFile*
35mapfile_open(const char* path, int oflag, int share_mode)
36{
37#ifdef WIN32
38    DWORD win32_share;
39    DWORD win32_desired_access = GENERIC_READ;
40    DWORD win32_disposition = OPEN_EXISTING;
41    DWORD win32_flags;
42
43    /* Convert to Win32 desired access. */
44    if ((oflag & O_RDWR) == O_RDWR) {
45        win32_desired_access = GENERIC_READ | GENERIC_WRITE;
46    } else if ((oflag & O_ACCMODE) == O_RDONLY) {
47        win32_desired_access = GENERIC_READ;
48    } else if ((oflag & O_WRONLY) == O_WRONLY) {
49        win32_desired_access = GENERIC_WRITE;
50    }
51
52    /* Convert to Win32 sharing. */
53    win32_share = 0;
54    if ((share_mode & S_IWRITE) != 0) {
55        win32_share |= FILE_SHARE_WRITE;
56    }
57    if ((share_mode & S_IREAD) != 0) {
58        win32_share |= FILE_SHARE_READ;
59    }
60
61    /* Convert to Win32 disposition. */
62    if ((oflag & O_CREAT) == O_CREAT) {
63        if ((oflag & O_EXCL) == O_EXCL) {
64            win32_disposition = CREATE_NEW;
65        } else {
66            win32_disposition = OPEN_ALWAYS;
67        }
68    } if ((oflag & O_TRUNC) == O_TRUNC) {
69        win32_desired_access = TRUNCATE_EXISTING;
70    } else {
71        win32_disposition = OPEN_EXISTING;
72    }
73
74    /* Convert to Win32 flags. */
75    win32_flags = 0;
76#if defined(O_DSYNC)
77    if ((oflag & O_DSYNC) == O_DSYNC ||
78        (oflag & O_RSYNC) == O_RSYNC ||
79        (oflag & O_RSYNC) == O_SYNC) {
80        win32_flags |= FILE_FLAG_WRITE_THROUGH;
81    }
82#endif  // O_DSYNC
83
84    HANDLE file_handle = CreateFile(path, win32_desired_access, win32_share,
85                                    NULL, win32_disposition, win32_flags, NULL);
86    if (file_handle == INVALID_HANDLE_VALUE) {
87        errno = GetLastError();
88    }
89#else   // WIN32
90    int file_handle = open(path, oflag, share_mode);
91#endif  // WIN32
92
93    return (MapFile*)(ptrdiff_t)file_handle;
94}
95
96int
97mapfile_close(MapFile* handle)
98{
99#ifdef WIN32
100    if (CloseHandle(handle)) {
101        return 0;
102    } else {
103        errno = GetLastError();
104        return -1;
105    }
106#else   // WIN32
107    return close((int)(ptrdiff_t)handle);
108#endif  // WIN32
109}
110
111ssize_t
112mapfile_read(MapFile* handle, void* buf, size_t nbyte)
113{
114#ifdef WIN32
115    ssize_t ret_bytes;
116    DWORD read_bytes;
117    if (ReadFile(handle, buf, nbyte, &read_bytes, NULL)) {
118        ret_bytes = (ssize_t)read_bytes;
119    } else {
120        errno = GetLastError();
121        ret_bytes = -1;
122    }
123    return ret_bytes;
124#else   // WIN32
125    return HANDLE_EINTR(read((int)(ptrdiff_t)handle, buf, nbyte));
126#endif  // WIN32
127}
128
129ssize_t
130mapfile_read_at(MapFile* handle, size_t offset, void* buf, size_t nbyte)
131{
132#ifdef WIN32
133    LARGE_INTEGER convert;
134    convert.QuadPart = offset;
135    if ((SetFilePointer(handle, convert.LowPart, &convert.HighPart,
136                        FILE_BEGIN) == INVALID_SET_FILE_POINTER) &&
137            (GetLastError() != NO_ERROR)) {
138        errno = GetLastError();
139        return -1;
140    }
141    return mapfile_read(handle, buf, nbyte);
142#else   // WIN32
143    ssize_t res = lseek((int)(ptrdiff_t)handle, offset, SEEK_SET);
144    return res >= 0 ? mapfile_read(handle, buf, nbyte) : res;
145#endif  // WIN32
146}
147
148void*
149mapfile_map(MapFile* handle,
150            size_t offset,
151            size_t size,
152            int prot,
153            void** mapped_offset,
154            size_t* mapped_size)
155{
156    void* mapped_at = NULL;
157    size_t align_mask;
158    size_t map_offset;
159    size_t map_size;
160
161  /* Get the mask for mapping offset alignment. */
162#ifdef  WIN32
163    DWORD win32_prot;
164    DWORD win32_map;
165    HANDLE map_handle;
166    LARGE_INTEGER converter;
167    SYSTEM_INFO sys_info;
168    GetSystemInfo(&sys_info);
169    align_mask = sys_info.dwAllocationGranularity - 1;
170#else   // WIN32
171    align_mask = getpagesize() - 1;
172#endif  // WIN32
173
174    /* Adjust mapping offset and mapping size accordingly to
175     * the mapping alignment requirements. */
176    map_offset = offset & ~align_mask;
177    map_size = (size_t)(offset - map_offset + size);
178
179    /* Make sure mapping size doesn't exceed 4G. */
180    if (map_size < size) {
181        errno = EFBIG;
182        return NULL;
183    }
184
185    /* Map the section. */
186#ifdef  WIN32
187    /* Convert to Win32 page protection and mapping type. */
188    win32_prot = PAGE_READONLY;
189    win32_map = FILE_MAP_READ;
190    if (prot != PROT_NONE) {
191        if ((prot & (PROT_WRITE | PROT_EXEC)) == 0) {
192            win32_prot = PAGE_READONLY;
193            win32_map = FILE_MAP_READ;
194        } else if ((prot & (PROT_WRITE | PROT_EXEC)) ==
195                   (PROT_WRITE | PROT_EXEC)) {
196            win32_prot = PAGE_EXECUTE_READWRITE;
197            win32_map = FILE_MAP_WRITE;
198        } else if ((prot & PROT_WRITE) == PROT_WRITE) {
199            win32_prot = PAGE_READWRITE;
200            win32_map = FILE_MAP_WRITE;
201        } else if ((prot & PROT_EXEC) == PROT_EXEC) {
202            win32_prot = PAGE_EXECUTE_READ;
203            win32_map = FILE_MAP_READ;
204        }
205    }
206
207    converter.QuadPart = map_offset + map_size;
208    map_handle = CreateFileMapping(handle, NULL, win32_prot,
209                                   converter.HighPart, converter.LowPart, NULL);
210    if (map_handle != NULL) {
211        converter.QuadPart = map_offset;
212        mapped_at = MapViewOfFile(map_handle, win32_map, converter.HighPart,
213                                  converter.LowPart, map_size);
214        /* Memory mapping (if successful) will hold extra references to the
215        * mapping, so we can close it right after we mapped file view. */
216        CloseHandle(map_handle);
217    }
218    if (mapped_at == NULL) {
219        errno = GetLastError();
220        return NULL;
221    }
222#else   // WIN32
223    mapped_at =
224        mmap(0, map_size, PROT_READ, MAP_SHARED, (int)(ptrdiff_t)handle, map_offset);
225    if (mapped_at == MAP_FAILED) {
226        return NULL;
227    }
228#endif  // WIN32
229
230    *mapped_offset = (char*)mapped_at + (offset - map_offset);
231    *mapped_size = size;
232
233    return mapped_at;
234}
235
236int
237mapfile_unmap(void* mapped_at, size_t len)
238{
239#ifdef WIN32
240    if (!UnmapViewOfFile(mapped_at)) {
241        errno = GetLastError();
242        return -1;
243    }
244    return 0;
245#else   // WIN32
246    return munmap(mapped_at, len);
247#endif  // WIN32
248}
249