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