src/util/vfs.c (view raw)
1/* Copyright (c) 2013-2014 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 "util/vfs.h"
7
8#include "util/string.h"
9
10#include <fcntl.h>
11#include <dirent.h>
12
13#ifdef _WIN32
14#include <io.h>
15#include <windows.h>
16#define PATH_SEP '\\'
17#elif defined(_3DS)
18#include "util/memory.h"
19#define PATH_SEP '/'
20#else
21#include <sys/mman.h>
22#define PATH_SEP '/'
23#endif
24
25struct VFileFD {
26 struct VFile d;
27 int fd;
28#ifdef _WIN32
29 HANDLE hMap;
30#endif
31};
32
33static bool _vfdClose(struct VFile* vf);
34static off_t _vfdSeek(struct VFile* vf, off_t offset, int whence);
35static ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size);
36static ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size);
37static ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size);
38static void* _vfdMap(struct VFile* vf, size_t size, int flags);
39static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
40static void _vfdTruncate(struct VFile* vf, size_t size);
41
42static bool _vdClose(struct VDir* vd);
43static void _vdRewind(struct VDir* vd);
44static struct VDirEntry* _vdListNext(struct VDir* vd);
45static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
46
47static const char* _vdeName(struct VDirEntry* vde);
48
49struct VFile* VFileOpen(const char* path, int flags) {
50 if (!path) {
51 return 0;
52 }
53#ifdef _WIN32
54 flags |= O_BINARY;
55#endif
56 int fd = open(path, flags, 0666);
57 return VFileFromFD(fd);
58}
59
60struct VFile* VFileFromFD(int fd) {
61 if (fd < 0) {
62 return 0;
63 }
64
65 struct VFileFD* vfd = malloc(sizeof(struct VFileFD));
66 if (!vfd) {
67 return 0;
68 }
69
70 vfd->fd = fd;
71 vfd->d.close = _vfdClose;
72 vfd->d.seek = _vfdSeek;
73 vfd->d.read = _vfdRead;
74 vfd->d.readline = _vfdReadline;
75 vfd->d.write = _vfdWrite;
76 vfd->d.map = _vfdMap;
77 vfd->d.unmap = _vfdUnmap;
78 vfd->d.truncate = _vfdTruncate;
79
80 return &vfd->d;
81}
82
83bool _vfdClose(struct VFile* vf) {
84 struct VFileFD* vfd = (struct VFileFD*) vf;
85 if (close(vfd->fd) < 0) {
86 return false;
87 }
88 free(vfd);
89 return true;
90}
91
92off_t _vfdSeek(struct VFile* vf, off_t offset, int whence) {
93 struct VFileFD* vfd = (struct VFileFD*) vf;
94 return lseek(vfd->fd, offset, whence);
95}
96
97ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size) {
98 struct VFileFD* vfd = (struct VFileFD*) vf;
99 return read(vfd->fd, buffer, size);
100}
101
102ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size) {
103 struct VFileFD* vfd = (struct VFileFD*) vf;
104 size_t bytesRead = 0;
105 while (bytesRead < size - 1) {
106 size_t newRead = read(vfd->fd, &buffer[bytesRead], 1);
107 bytesRead += newRead;
108 if (!newRead || buffer[bytesRead] == '\n') {
109 break;
110 }
111 }
112 return buffer[bytesRead] = '\0';
113}
114
115ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size) {
116 struct VFileFD* vfd = (struct VFileFD*) vf;
117 return write(vfd->fd, buffer, size);
118}
119
120#ifdef _WIN32
121static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
122 struct VFileFD* vfd = (struct VFileFD*) vf;
123 int createFlags = PAGE_WRITECOPY;
124 int mapFiles = FILE_MAP_COPY;
125 if (flags & MAP_WRITE) {
126 createFlags = PAGE_READWRITE;
127 mapFiles = FILE_MAP_WRITE;
128 }
129 size_t location = lseek(vfd->fd, 0, SEEK_CUR);
130 size_t fileSize = lseek(vfd->fd, 0, SEEK_END);
131 lseek(vfd->fd, location, SEEK_SET);
132 if (size > fileSize) {
133 size = fileSize;
134 }
135 vfd->hMap = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
136 return MapViewOfFile(vfd->hMap, mapFiles, 0, 0, size);
137}
138
139static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
140 UNUSED(size);
141 struct VFileFD* vfd = (struct VFileFD*) vf;
142 UnmapViewOfFile(memory);
143 CloseHandle(vfd->hMap);
144 vfd->hMap = 0;
145}
146#elif defined(_3DS)
147static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
148 UNUSED(flags);
149 void* buffer = anonymousMemoryMap(size);
150 vf->read(vf, buffer, size);
151 vf->seek(vf, -(off_t) size, SEEK_CUR);
152 return buffer;
153}
154
155static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
156 UNUSED(vf);
157 mappedMemoryFree(memory, size);
158}
159#else
160static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
161 struct VFileFD* vfd = (struct VFileFD*) vf;
162 int mmapFlags = MAP_PRIVATE;
163 if (flags & MAP_WRITE) {
164 mmapFlags = MAP_SHARED;
165 }
166 return mmap(0, size, PROT_READ | PROT_WRITE, mmapFlags, vfd->fd, 0);
167}
168
169static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
170 UNUSED(vf);
171 munmap(memory, size);
172}
173#endif
174
175static void _vfdTruncate(struct VFile* vf, size_t size) {
176 struct VFileFD* vfd = (struct VFileFD*) vf;
177 ftruncate(vfd->fd, size);
178}
179
180struct VDirEntryDE {
181 struct VDirEntry d;
182 struct dirent* ent;
183};
184
185struct VDirDE {
186 struct VDir d;
187 DIR* de;
188 struct VDirEntryDE vde;
189 char* path;
190};
191
192struct VDir* VDirOpen(const char* path) {
193 DIR* de = opendir(path);
194 if (!de) {
195 return 0;
196 }
197
198 struct VDirDE* vd = malloc(sizeof(struct VDirDE));
199 if (!vd) {
200 return 0;
201 }
202
203 vd->d.close = _vdClose;
204 vd->d.rewind = _vdRewind;
205 vd->d.listNext = _vdListNext;
206 vd->d.openFile = _vdOpenFile;
207 vd->path = strdup(path);
208 vd->de = de;
209
210 vd->vde.d.name = _vdeName;
211
212 return &vd->d;
213}
214
215struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) {
216 char path[PATH_MAX];
217 path[PATH_MAX - 1] = '\0';
218 struct VFile* vf;
219 if (!dir) {
220 if (!realPath) {
221 return 0;
222 }
223 char* dotPoint = strrchr(realPath, '.');
224 if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
225 return 0;
226 }
227 if (dotPoint > strrchr(realPath, '/')) {
228 int len = dotPoint - realPath;
229 strncpy(path, realPath, len);
230 path[len] = 0;
231 strncat(path + len, suffix, PATH_MAX - len - 1);
232 } else {
233 snprintf(path, PATH_MAX - 1, "%s%s", realPath, suffix);
234 }
235 vf = VFileOpen(path, mode);
236 } else {
237 snprintf(path, PATH_MAX - 1, "%s%s", prefix, suffix);
238 vf = dir->openFile(dir, path, mode);
239 }
240 return vf;
241}
242
243struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode) {
244 char path[PATH_MAX];
245 path[PATH_MAX - 1] = '\0';
246 char realPrefix[PATH_MAX];
247 realPrefix[PATH_MAX - 1] = '\0';
248 if (!dir) {
249 if (!realPath) {
250 return 0;
251 }
252 const char* separatorPoint = strrchr(realPath, '/');
253 const char* dotPoint;
254 size_t len;
255 if (!separatorPoint) {
256 strcpy(path, "./");
257 separatorPoint = realPath;
258 dotPoint = strrchr(realPath, '.');
259 } else {
260 path[0] = '\0';
261 dotPoint = strrchr(separatorPoint, '.');
262
263 if (separatorPoint - realPath + 1 >= PATH_MAX - 1) {
264 return 0;
265 }
266
267 len = separatorPoint - realPath;
268 strncat(path, realPath, len);
269 path[len] = '\0';
270 ++separatorPoint;
271 }
272
273 if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
274 return 0;
275 }
276
277 if (dotPoint >= separatorPoint) {
278 len = dotPoint - separatorPoint;
279 } else {
280 len = PATH_MAX - 1;
281 }
282
283 strncpy(realPrefix, separatorPoint, len);
284 realPrefix[len] = '\0';
285
286 prefix = realPrefix;
287 dir = VDirOpen(path);
288 }
289 if (!dir) {
290 // This shouldn't be possible
291 return 0;
292 }
293 dir->rewind(dir);
294 struct VDirEntry* dirent;
295 size_t prefixLen = strlen(prefix);
296 size_t infixLen = strlen(infix);
297 unsigned next = 0;
298 while ((dirent = dir->listNext(dir))) {
299 const char* filename = dirent->name(dirent);
300 char* dotPoint = strrchr(filename, '.');
301 size_t len = strlen(filename);
302 if (dotPoint) {
303 len = (dotPoint - filename);
304 }
305 const char* separator = strnrstr(filename, infix, len);
306 if (!separator) {
307 continue;
308 }
309 len = separator - filename;
310 if (len != prefixLen) {
311 continue;
312 }
313 if (strncmp(filename, prefix, prefixLen) == 0) {
314 int nlen;
315 separator += infixLen;
316 snprintf(path, PATH_MAX - 1, "%%u%s%%n", suffix);
317 unsigned increment;
318 if (sscanf(separator, path, &increment, &nlen) < 1) {
319 continue;
320 }
321 len = strlen(separator);
322 if (nlen < (ssize_t) len) {
323 continue;
324 }
325 if (next <= increment) {
326 next = increment + 1;
327 }
328 }
329 }
330 snprintf(path, PATH_MAX - 1, "%s%s%u%s", prefix, infix, next, suffix);
331 path[PATH_MAX - 1] = '\0';
332 return dir->openFile(dir, path, mode);
333}
334
335bool _vdClose(struct VDir* vd) {
336 struct VDirDE* vdde = (struct VDirDE*) vd;
337 if (closedir(vdde->de) < 0) {
338 return false;
339 }
340 free(vdde->path);
341 free(vdde);
342 return true;
343}
344
345void _vdRewind(struct VDir* vd) {
346 struct VDirDE* vdde = (struct VDirDE*) vd;
347 rewinddir(vdde->de);
348}
349
350struct VDirEntry* _vdListNext(struct VDir* vd) {
351 struct VDirDE* vdde = (struct VDirDE*) vd;
352 vdde->vde.ent = readdir(vdde->de);
353 if (vdde->vde.ent) {
354 return &vdde->vde.d;
355 }
356
357 return 0;
358}
359
360struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode) {
361 struct VDirDE* vdde = (struct VDirDE*) vd;
362 if (!path) {
363 return 0;
364 }
365 const char* dir = vdde->path;
366 char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
367 sprintf(combined, "%s%c%s", dir, PATH_SEP, path);
368
369 struct VFile* file = VFileOpen(combined, mode);
370 free(combined);
371 return file;
372}
373
374const char* _vdeName(struct VDirEntry* vde) {
375 struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
376 if (vdede->ent) {
377 return vdede->ent->d_name;
378 }
379 return 0;
380}