src/util/vfs/vfs-fd.c (view raw)
1/* Copyright (c) 2013-2020 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/vfs.h>
7
8#include <fcntl.h>
9#include <sys/stat.h>
10#ifndef _WIN32
11#include <sys/mman.h>
12#include <sys/time.h>
13#else
14#include <windows.h>
15#endif
16
17#include <mgba-util/vector.h>
18
19#ifdef _WIN32
20struct HandleMappingTuple {
21 HANDLE handle;
22 void* mapping;
23};
24
25DECLARE_VECTOR(HandleMappingList, struct HandleMappingTuple);
26DEFINE_VECTOR(HandleMappingList, struct HandleMappingTuple);
27#endif
28
29struct VFileFD {
30 struct VFile d;
31 int fd;
32#ifdef _WIN32
33 struct HandleMappingList handles;
34#endif
35};
36
37static bool _vfdClose(struct VFile* vf);
38static off_t _vfdSeek(struct VFile* vf, off_t offset, int whence);
39static ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size);
40static ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size);
41static void* _vfdMap(struct VFile* vf, size_t size, int flags);
42static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
43static void _vfdTruncate(struct VFile* vf, size_t size);
44static ssize_t _vfdSize(struct VFile* vf);
45static bool _vfdSync(struct VFile* vf, void* buffer, size_t size);
46
47struct VFile* VFileOpenFD(const char* path, int flags) {
48 if (!path) {
49 return 0;
50 }
51#ifdef _WIN32
52 flags |= O_BINARY;
53 wchar_t wpath[PATH_MAX];
54 MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath) / sizeof(*wpath));
55 int fd = _wopen(wpath, flags, _S_IREAD | _S_IWRITE);
56#else
57 int fd = open(path, flags, 0666);
58#endif
59 return VFileFromFD(fd);
60}
61
62struct VFile* VFileFromFD(int fd) {
63 if (fd < 0) {
64 return 0;
65 }
66
67 struct stat stat;
68 if (fstat(fd, &stat) < 0 || (stat.st_mode & S_IFDIR)) {
69 close(fd);
70 return 0;
71 }
72
73 struct VFileFD* vfd = malloc(sizeof(struct VFileFD));
74 if (!vfd) {
75 return 0;
76 }
77
78 vfd->fd = fd;
79 vfd->d.close = _vfdClose;
80 vfd->d.seek = _vfdSeek;
81 vfd->d.read = _vfdRead;
82 vfd->d.readline = VFileReadline;
83 vfd->d.write = _vfdWrite;
84 vfd->d.map = _vfdMap;
85 vfd->d.unmap = _vfdUnmap;
86 vfd->d.truncate = _vfdTruncate;
87 vfd->d.size = _vfdSize;
88 vfd->d.sync = _vfdSync;
89#ifdef _WIN32
90 HandleMappingListInit(&vfd->handles, 4);
91#endif
92
93 return &vfd->d;
94}
95
96bool _vfdClose(struct VFile* vf) {
97 struct VFileFD* vfd = (struct VFileFD*) vf;
98#ifdef _WIN32
99 size_t i;
100 for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) {
101 UnmapViewOfFile(HandleMappingListGetPointer(&vfd->handles, i)->mapping);
102 CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle);
103 }
104 HandleMappingListDeinit(&vfd->handles);
105#endif
106 if (close(vfd->fd) < 0) {
107 return false;
108 }
109 free(vfd);
110 return true;
111}
112
113off_t _vfdSeek(struct VFile* vf, off_t offset, int whence) {
114 struct VFileFD* vfd = (struct VFileFD*) vf;
115 return lseek(vfd->fd, offset, whence);
116}
117
118ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size) {
119 struct VFileFD* vfd = (struct VFileFD*) vf;
120 return read(vfd->fd, buffer, size);
121}
122
123ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size) {
124 struct VFileFD* vfd = (struct VFileFD*) vf;
125 return write(vfd->fd, buffer, size);
126}
127
128#ifndef _WIN32
129static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
130 struct VFileFD* vfd = (struct VFileFD*) vf;
131 int mmapFlags = MAP_PRIVATE;
132 if (flags & MAP_WRITE) {
133 mmapFlags = MAP_SHARED;
134 }
135 return mmap(0, size, PROT_READ | PROT_WRITE, mmapFlags, vfd->fd, 0);
136}
137
138static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
139 UNUSED(vf);
140 msync(memory, size, MS_SYNC);
141 munmap(memory, size);
142}
143#else
144static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
145 struct VFileFD* vfd = (struct VFileFD*) vf;
146 int createFlags = PAGE_WRITECOPY;
147 int mapFiles = FILE_MAP_COPY;
148 if (flags & MAP_WRITE) {
149 createFlags = PAGE_READWRITE;
150 mapFiles = FILE_MAP_WRITE;
151 }
152 size_t fileSize;
153 struct stat stat;
154 if (fstat(vfd->fd, &stat) < 0) {
155 return 0;
156 }
157 fileSize = stat.st_size;
158 if (size > fileSize) {
159 size = fileSize;
160 }
161 struct HandleMappingTuple tuple = {0};
162 tuple.handle = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
163 tuple.mapping = MapViewOfFile(tuple.handle, mapFiles, 0, 0, size);
164 *HandleMappingListAppend(&vfd->handles) = tuple;
165 return tuple.mapping;
166}
167
168static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
169 UNUSED(size);
170 struct VFileFD* vfd = (struct VFileFD*) vf;
171 FlushViewOfFile(memory, size);
172 size_t i;
173 for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) {
174 if (HandleMappingListGetPointer(&vfd->handles, i)->mapping == memory) {
175 UnmapViewOfFile(memory);
176 CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle);
177 HandleMappingListShift(&vfd->handles, i, 1);
178 break;
179 }
180 }
181}
182#endif
183
184static void _vfdTruncate(struct VFile* vf, size_t size) {
185 struct VFileFD* vfd = (struct VFileFD*) vf;
186 ftruncate(vfd->fd, size);
187}
188
189static ssize_t _vfdSize(struct VFile* vf) {
190 struct VFileFD* vfd = (struct VFileFD*) vf;
191 struct stat stat;
192 if (fstat(vfd->fd, &stat) < 0) {
193 return -1;
194 }
195 return stat.st_size;
196}
197
198static bool _vfdSync(struct VFile* vf, void* buffer, size_t size) {
199 UNUSED(buffer);
200 UNUSED(size);
201 struct VFileFD* vfd = (struct VFileFD*) vf;
202#ifndef _WIN32
203#ifdef __HAIKU__
204 futimens(vfd->fd, NULL);
205#else
206 futimes(vfd->fd, NULL);
207#endif
208 if (buffer && size) {
209 return msync(buffer, size, MS_ASYNC) == 0;
210 }
211 return fsync(vfd->fd) == 0;
212#else
213 HANDLE h = (HANDLE) _get_osfhandle(vfd->fd);
214 FILETIME ft;
215 SYSTEMTIME st;
216 GetSystemTime(&st);
217 SystemTimeToFileTime(&st, &ft);
218 SetFileTime(h, NULL, &ft, &ft);
219 if (buffer && size) {
220 return FlushViewOfFile(buffer, size);
221 }
222 return FlushFileBuffers(h);
223#endif
224}