all repos — mgba @ 7c0f3842a064ec6bd6722d93da9dec399f48ee54

mGBA Game Boy Advance Emulator

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, const 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	munmap(memory, size);
141}
142#else
143static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
144	struct VFileFD* vfd = (struct VFileFD*) vf;
145	int createFlags = PAGE_WRITECOPY;
146	int mapFiles = FILE_MAP_COPY;
147	if (flags & MAP_WRITE) {
148		createFlags = PAGE_READWRITE;
149		mapFiles = FILE_MAP_WRITE;
150	}
151	size_t fileSize;
152	struct stat stat;
153	if (fstat(vfd->fd, &stat) < 0) {
154		return 0;
155	}
156	fileSize = stat.st_size;
157	if (size > fileSize) {
158		size = fileSize;
159	}
160	struct HandleMappingTuple tuple = {0};
161	tuple.handle = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
162	tuple.mapping = MapViewOfFile(tuple.handle, mapFiles, 0, 0, size);
163	*HandleMappingListAppend(&vfd->handles) = tuple;
164	return tuple.mapping;
165}
166
167static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
168	UNUSED(size);
169	struct VFileFD* vfd = (struct VFileFD*) vf;
170	size_t i;
171	for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) {
172		if (HandleMappingListGetPointer(&vfd->handles, i)->mapping == memory) {
173			CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle);
174			HandleMappingListShift(&vfd->handles, i, 1);
175			break;
176		}
177	}
178	UnmapViewOfFile(memory);
179}
180#endif
181
182static void _vfdTruncate(struct VFile* vf, size_t size) {
183	struct VFileFD* vfd = (struct VFileFD*) vf;
184	ftruncate(vfd->fd, size);
185}
186
187static ssize_t _vfdSize(struct VFile* vf) {
188	struct VFileFD* vfd = (struct VFileFD*) vf;
189	struct stat stat;
190	if (fstat(vfd->fd, &stat) < 0) {
191		return -1;
192	}
193	return stat.st_size;
194}
195
196static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
197	UNUSED(buffer);
198	UNUSED(size);
199	struct VFileFD* vfd = (struct VFileFD*) vf;
200#ifndef _WIN32
201#ifdef __HAIKU__
202	futimens(vfd->fd, NULL);
203#else
204	futimes(vfd->fd, NULL);
205#endif
206	if (buffer && size) {
207		return msync(buffer, size, MS_SYNC) == 0;
208	}
209	return fsync(vfd->fd) == 0;
210#else
211	HANDLE h = (HANDLE) _get_osfhandle(vfd->fd);
212	FILETIME ft;
213	SYSTEMTIME st;
214	GetSystemTime(&st);
215	SystemTimeToFileTime(&st, &ft);
216	SetFileTime(h, NULL, &ft, &ft);
217	return FlushFileBuffers(h);
218#endif
219}