backported more fixes from 256boss
[bootcensus] / src / fsmem.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <errno.h>
23 #include <alloca.h>
24 #include "fs.h"
25 #include "panic.h"
26
27 #define MAX_NAME        120
28
29 struct memfs_node;
30 struct memfs_file;
31 struct memfs_dir;
32
33 struct memfs {
34         struct memfs_node *rootdir;
35 };
36
37 struct memfs_dir {
38         struct memfs_node *clist, *ctail;
39         struct memfs_node *cur;
40 };
41
42 struct odir {
43         struct memfs_dir *dir;
44         struct memfs_node *cur;
45         struct fs_dirent dent;
46 };
47
48 struct memfs_file {
49         char *data;
50         long size, max_size;
51 };
52
53 struct ofile {
54         struct memfs_file *file;
55         long cur_pos;
56 };
57
58 struct memfs_node {
59         union {
60                 struct memfs_file file;
61                 struct memfs_dir dir;
62         };
63         int type;
64         char name[MAX_NAME + 4];
65         struct memfs_node *parent;
66         struct memfs_node *next;
67         struct fs_node *fsnode; /* we need it for crossing mounts in fs_open */
68 };
69
70
71 static void destroy(struct filesys *fs);
72
73 static struct fs_node *open(struct filesys *fs, const char *path, unsigned int flags);
74 static void close(struct fs_node *node);
75 static long fsize(struct fs_node *node);
76 static int seek(struct fs_node *node, int offs, int whence);
77 static long tell(struct fs_node *node);
78 static int read(struct fs_node *node, void *buf, int sz);
79 static int write(struct fs_node *node, void *buf, int sz);
80 static int rewinddir(struct fs_node *node);
81 static struct fs_dirent *readdir(struct fs_node *node);
82 static int rename(struct fs_node *node, const char *name);
83 static int remove(struct fs_node *node);
84
85 static struct fs_node *create_fsnode(struct filesys *fs, struct memfs_node *n);
86
87 static struct memfs_node *alloc_node(int type);
88 static void free_node(struct memfs_node *node);
89
90 static struct memfs_node *find_entry(struct memfs_node *dir, const char *name);
91 static void add_child(struct memfs_node *dir, struct memfs_node *n);
92
93
94 static struct fs_operations fs_mem_ops = {
95         destroy,
96         open, close,
97
98         fsize,
99         seek, tell,
100         read, write,
101
102         rewinddir, readdir,
103
104         rename, remove
105 };
106
107
108 struct filesys *fsmem_create(int dev, uint64_t start, uint64_t size)
109 {
110         struct filesys *fs;
111         struct memfs *memfs;
112
113         if(dev != DEV_MEMDISK) {
114                 return 0;
115         }
116
117         if(!(memfs = malloc(sizeof *memfs))) {
118                 panic("MEMFS: create failed to allocate memory\n");
119         }
120         if(!(memfs->rootdir = alloc_node(FSNODE_DIR))) {
121                 panic("MEMFS: failed to allocate root dir\n");
122         }
123
124         if(!(fs = malloc(sizeof *fs))) {
125                 panic("MEMFS: failed to allocate memory for the filesystem structure\n");
126         }
127         fs->type = FSTYPE_MEM;
128         fs->name = 0;
129         fs->fsop = &fs_mem_ops;
130         fs->data = memfs;
131
132         return fs;
133 }
134
135 static void destroy(struct filesys *fs)
136 {
137         struct memfs *memfs = fs->data;
138         free_node((struct memfs_node*)memfs->rootdir);
139         free(memfs);
140         free(fs);
141 }
142
143 static struct fs_node *open_mount(struct filesys *fs, const char *path, unsigned int flags)
144 {
145         char *newpath;
146
147         newpath = alloca(strlen(path) + 2);
148         newpath[0] = '/';
149         strcpy(newpath + 1, path);
150
151         return fs->fsop->open(fs, newpath, flags);
152 }
153
154 #define NODE_IS_MNTPT(n)        ((n)->fsnode && (n)->fsnode->mnt)
155
156 static struct fs_node *open(struct filesys *fs, const char *path, unsigned int flags)
157 {
158         struct memfs_node *node, *parent;
159         struct memfs *memfs = fs->data;
160         char name[MAX_NAME + 1];
161
162         if(path[0] == '/') {
163                 node = memfs->rootdir;
164                 path = fs_path_skipsep((char*)path);
165         } else {
166                 if(cwdnode->fs->type != FSTYPE_MEM) {
167                         return 0;
168                 }
169                 node = (struct memfs_node*)((struct odir*)cwdnode->data)->dir;
170         }
171         assert(node->type == FSNODE_DIR);
172
173         while(*path) {
174                 if(node->type != FSNODE_DIR) {
175                         /* we have more path components, yet the last one wasn't a dir */
176                         errno = ENOTDIR;
177                         return 0;
178                 }
179                 /* check if it's another filesystem hanging off this directory, and if
180                  * so, recursively call that open function to complete the operation
181                  */
182                 if(NODE_IS_MNTPT(node)) {
183                         return open_mount(node->fsnode->mnt, path, flags);
184                 }
185
186                 path = fs_path_next((char*)path, name, sizeof name);
187                 parent = node;
188
189                 if(!(node = find_entry(node, name))) {
190                         if(*path || !(flags & FSO_CREATE)) {
191                                 errno = ENOENT;
192                                 return 0;
193                         }
194                         /* create and add */
195                         if(!(node = alloc_node((flags & FSO_DIR) ? FSNODE_DIR : FSNODE_FILE))) {
196                                 errno = ENOMEM;
197                                 return 0;
198                         }
199                         strcpy(node->name, name);
200                         add_child(parent, node);
201                         return create_fsnode(fs, node);
202                 }
203         }
204
205         /* we need to check for mount points here too, because the check in the loop
206          * above is not going to be reached when the mount point is the last part of
207          * the path string (for instance opendir("/mnt/foo"))
208          */
209         if(NODE_IS_MNTPT(node)) {
210                 return open_mount(node->fsnode->mnt, path, flags);
211         }
212
213         if(flags & FSO_EXCL) {
214                 errno = EEXIST;
215                 return 0;
216         }
217         return create_fsnode(fs, node);
218 }
219
220 static struct fs_node *create_fsnode(struct filesys *fs, struct memfs_node *n)
221 {
222         struct fs_node *fsn;
223         struct ofile *of;
224         struct odir *od;
225
226         if(!(fsn = calloc(1, sizeof *fsn))) {
227                 errno = ENOMEM;
228                 return 0;
229         }
230
231         if(n->type == FSNODE_FILE) {
232                 if(!(of = malloc(sizeof *of))) {
233                         errno = ENOMEM;
234                         free(fsn);
235                         return 0;
236                 }
237                 of->file = &n->file;
238                 of->cur_pos = 0;
239                 fsn->data = of;
240         } else {
241                 if(!(od = malloc(sizeof *od))) {
242                         errno = ENOMEM;
243                         free(fsn);
244                         return 0;
245                 }
246                 od->dir = &n->dir;
247                 od->cur = n->dir.clist;
248                 fsn->data = od;
249         }
250
251         if(!n->fsnode) {
252                 n->fsnode = fsn;
253         }
254
255         fsn->fs = fs;
256         fsn->type = n->type;
257         return fsn;
258 }
259
260 static void close(struct fs_node *node)
261 {
262         if(!node) return;
263
264         free(node->data);       /* free the copy of memfs_node allocated by create_fsnode */
265         free(node);
266 }
267
268 static long fsize(struct fs_node *node)
269 {
270         struct ofile *of;
271         if(!node || node->type != FSNODE_FILE) {
272                 return -1;
273         }
274         of = node->data;
275         return of->file->size;
276 }
277
278 static int seek(struct fs_node *node, int offs, int whence)
279 {
280         struct ofile *of;
281         long new_pos;
282
283         if(!node || node->type != FSNODE_FILE) {
284                 return -1;
285         }
286
287         of = node->data;
288
289         switch(whence) {
290         case FSSEEK_SET:
291                 new_pos = offs;
292                 break;
293
294         case FSSEEK_CUR:
295                 new_pos = of->cur_pos + offs;
296                 break;
297
298         case FSSEEK_END:
299                 new_pos = of->file->size + offs;
300                 break;
301
302         default:
303                 return -1;
304         }
305
306         if(new_pos < 0) new_pos = 0;
307
308         of->cur_pos = new_pos;
309         return 0;
310 }
311
312 static long tell(struct fs_node *node)
313 {
314         struct ofile *of;
315
316         if(!node || node->type != FSNODE_FILE) {
317                 return -1;
318         }
319         of = node->data;
320         return of->cur_pos;
321 }
322
323 static int read(struct fs_node *node, void *buf, int sz)
324 {
325         struct ofile *of;
326
327         if(!node || !buf || sz < 0 || node->type != FSNODE_FILE) {
328                 return -1;
329         }
330
331         of = node->data;
332
333         if(sz > of->file->size - of->cur_pos) {
334                 sz = of->file->size - of->cur_pos;
335         }
336         memcpy(buf, of->file->data + of->cur_pos, sz);
337         of->cur_pos += sz;
338         return sz;
339 }
340
341 static int write(struct fs_node *node, void *buf, int sz)
342 {
343         struct ofile *of;
344         int total_sz, new_max_sz;
345         void *tmp;
346
347         if(!node || !buf || sz < 0 || node->type != FSNODE_FILE) {
348                 return -1;
349         }
350
351         of = node->data;
352         total_sz = of->cur_pos + sz;
353         if(total_sz > of->file->max_size) {
354                 if(total_sz < of->file->max_size * 2) {
355                         new_max_sz = of->file->max_size * 2;
356                 } else {
357                         new_max_sz = total_sz;
358                 }
359                 if(!(tmp = realloc(of->file->data, new_max_sz))) {
360                         errno = ENOSPC;
361                         return -1;
362                 }
363                 of->file->data = tmp;
364                 of->file->max_size = new_max_sz;
365         }
366
367         memcpy(of->file->data + of->cur_pos, buf, sz);
368         of->cur_pos += sz;
369         if(of->cur_pos > of->file->size) of->file->size = of->cur_pos;
370         return sz;
371 }
372
373 static int rewinddir(struct fs_node *node)
374 {
375         struct odir *od;
376
377         if(!node || node->type != FSNODE_DIR) {
378                 return -1;
379         }
380
381         od = node->data;
382         od->cur = od->dir->clist;
383         return 0;
384 }
385
386 static struct fs_dirent *readdir(struct fs_node *node)
387 {
388         struct odir *od;
389         struct memfs_node *n;
390         struct fs_dirent *fsd;
391
392         if(!node || node->type != FSNODE_DIR) {
393                 return 0;
394         }
395
396         od = node->data;
397         fsd = &od->dent;
398
399         n = od->cur;
400         if(!n) return 0;
401
402         od->cur = od->cur->next;
403
404         fsd->name = n->name;
405         fsd->data = 0;
406         fsd->type = n->type;
407         fsd->fsize = n->file.size;
408
409         return fsd;
410 }
411
412 static int rename(struct fs_node *node, const char *name)
413 {
414         struct memfs_node *n = (struct memfs_node*)((struct odir*)node->data)->dir;
415         strncpy(n->name, name, MAX_NAME);
416         n->name[MAX_NAME] = 0;
417         return 0;
418 }
419
420 static int remove(struct fs_node *node)
421 {
422         int res = -1;
423         struct odir *od = 0;
424         struct ofile *of = 0;
425         struct memfs_node *n, *par, *prev, dummy;
426
427         if(node->type == FSNODE_DIR) {
428                 od = node->data;
429                 n = (struct memfs_node*)od->dir;
430
431                 if(n->dir.clist) {
432                         errno = EEXIST;
433                         return -1;
434                 }
435         } else {
436                 of = node->data;
437                 n = (struct memfs_node*)of->file;
438         }
439         par = n->parent;
440
441         if(!par) {
442                 errno = EBUSY;
443                 return -1;
444         }
445
446         dummy.next = par->dir.clist;
447         prev = &dummy;
448         while(prev->next) {
449                 if(prev->next == n) {
450                         if(par->dir.ctail == n) {
451                                 par->dir.ctail = prev;
452                         }
453                         prev->next = n->next;
454                         free_node(n);
455                         res = 0;
456                         break;
457                 }
458                 prev = prev->next;
459         }
460         par->dir.clist = dummy.next;
461         return res;
462 }
463
464 static struct memfs_node *alloc_node(int type)
465 {
466         struct memfs_node *node;
467
468         if(!(node = calloc(1, sizeof *node))) {
469                 return 0;
470         }
471         node->type = type;
472         return node;
473 }
474
475 static void free_node(struct memfs_node *node)
476 {
477         if(!node) return;
478
479         switch(node->type) {
480         case FSNODE_FILE:
481                 free(node->file.data);
482                 break;
483
484         case FSNODE_DIR:
485                 while(node->dir.clist) {
486                         struct memfs_node *n = node->dir.clist;
487                         node->dir.clist = n->next;
488                         free_node(n);
489                 }
490                 break;
491         }
492 }
493
494 static struct memfs_node *find_entry(struct memfs_node *dnode, const char *name)
495 {
496         struct memfs_node *n;
497
498         if(strcmp(name, ".") == 0) return dnode;
499         if(strcmp(name, "..") == 0) return dnode->parent;
500
501         n = dnode->dir.clist;
502         while(n) {
503                 if(strcasecmp(n->name, name) == 0) {
504                         return n;
505                 }
506                 n = n->next;
507         }
508         return 0;
509 }
510
511 static void add_child(struct memfs_node *dnode, struct memfs_node *n)
512 {
513         if(dnode->dir.clist) {
514                 dnode->dir.ctail->next = n;
515                 dnode->dir.ctail = n;
516         } else {
517                 dnode->dir.clist = dnode->dir.ctail = n;
518         }
519         n->parent = dnode;
520 }