git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / file / nbio / nbio_unixmmap.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (nbio_unixmmap.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <file/nbio.h>
31
32 #if defined(HAVE_MMAP) && defined(BSD)
33
34 #ifdef _WIN32
35 #include <direct.h>
36 #else
37 #include <unistd.h>
38 #endif
39 #include <fcntl.h>
40 #include <sys/mman.h>
41
42 #ifdef __APPLE__
43
44 #ifndef O_CLOEXEC
45 #define O_CLOEXEC 0x1000000
46 #endif
47
48 #else
49
50 #ifndef O_CLOEXEC
51 #define O_CLOEXEC 0
52 #endif
53
54 #endif
55
56 struct nbio_mmap_unix_t
57 {
58    void* ptr;
59    size_t len;
60    int fd;
61    int map_flags;
62 };
63
64 static void *nbio_mmap_unix_open(const char * filename, unsigned mode)
65 {
66    static const int o_flags[] =   { O_RDONLY,  O_RDWR|O_CREAT|O_TRUNC, O_RDWR,               O_RDONLY,  O_RDWR|O_CREAT|O_TRUNC };
67    static const int map_flags[] = { PROT_READ, PROT_WRITE|PROT_READ,   PROT_WRITE|PROT_READ, PROT_READ, PROT_WRITE|PROT_READ   };
68
69    size_t len;
70    void* ptr                       = NULL;
71    struct nbio_mmap_unix_t* handle = NULL;
72    int fd                          = open(filename, o_flags[mode]|O_CLOEXEC, 0644);
73    if (fd < 0)
74       return NULL;
75
76    len = lseek(fd, 0, SEEK_END);
77    if (len != 0)
78       ptr = mmap(NULL, len, map_flags[mode], MAP_SHARED, fd, 0);
79
80    if (ptr == MAP_FAILED)
81    {
82       close(fd);
83       return NULL;
84    }
85
86    handle            = malloc(sizeof(struct nbio_mmap_unix_t));
87    handle->fd        = fd;
88    handle->map_flags = map_flags[mode];
89    handle->len       = len;
90    handle->ptr       = ptr;
91    return handle;
92 }
93
94 static void nbio_mmap_unix_begin_read(void *data)
95 {
96    /* not needed */
97 }
98
99 static void nbio_mmap_unix_begin_write(void *data)
100 {
101    /* not needed */
102 }
103
104 static bool nbio_mmap_unix_iterate(void *data)
105 {
106    return true; /* not needed */
107 }
108
109 static void nbio_mmap_unix_resize(void *data, size_t len)
110 {
111    struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
112    if (!handle)
113       return;
114    if (len < handle->len)
115    {
116       /* this works perfectly fine if this check is removed, but it
117        * won't work on other nbio implementations */
118       /* therefore, it's blocked so nobody accidentally relies on it */
119       abort();
120    }
121
122    if (ftruncate(handle->fd, len) != 0)
123       abort(); /* this one returns void and I can't find any other
124                   way for it to report failure */
125
126    munmap(handle->ptr, handle->len);
127
128    handle->ptr = mmap(NULL, len, handle->map_flags, MAP_SHARED, handle->fd, 0);
129    handle->len = len;
130
131    if (handle->ptr == MAP_FAILED)
132       abort();
133 }
134
135 static void *nbio_mmap_unix_get_ptr(void *data, size_t* len)
136 {
137    struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
138    if (!handle)
139       return NULL;
140    if (len)
141       *len = handle->len;
142    return handle->ptr;
143 }
144
145 static void nbio_mmap_unix_cancel(void *data)
146 {
147    /* not needed */
148 }
149
150 static void nbio_mmap_unix_free(void *data)
151 {
152    struct nbio_mmap_unix_t* handle = (struct nbio_mmap_unix_t*)data;
153    if (!handle)
154       return;
155    close(handle->fd);
156    munmap(handle->ptr, handle->len);
157    free(handle);
158 }
159
160 nbio_intf_t nbio_mmap_unix = {
161    nbio_mmap_unix_open,
162    nbio_mmap_unix_begin_read,
163    nbio_mmap_unix_begin_write,
164    nbio_mmap_unix_iterate,
165    nbio_mmap_unix_resize,
166    nbio_mmap_unix_get_ptr,
167    nbio_mmap_unix_cancel,
168    nbio_mmap_unix_free,
169    "nbio_mmap_unix",
170 };
171 #else
172 nbio_intf_t nbio_mmap_unix = {
173    NULL,
174    NULL,
175    NULL,
176    NULL,
177    NULL,
178    NULL,
179    NULL,
180    NULL,
181    "nbio_mmap_unix",
182 };
183
184 #endif