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