all repos — mgba @ 5a1ec94b02758b2fc4528572dda54ac0d648ef64

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	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, const 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_SYNC) == 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		FlushViewOfFile(buffer, size);
221	}
222	return FlushFileBuffers(h);
223#endif
224}