backported more fixes from 256boss
[bootcensus] / src / fs.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 <string.h>
20 #include <errno.h>
21 #include <assert.h>
22 #include <alloca.h>
23 #include "fs.h"
24 #include "mtab.h"
25 #include "panic.h"
26
27 struct filesys *fsfat_create(int dev, uint64_t start, uint64_t size);
28 struct filesys *fsmem_create(int dev, uint64_t start, uint64_t size);
29
30 static struct filesys *(*createfs[])(int, uint64_t, uint64_t) = {
31         fsmem_create,
32         fsfat_create
33 };
34
35 struct filesys *fs_mount(int dev, uint64_t start, uint64_t size, struct fs_node *parent)
36 {
37         int i;
38         struct filesys *fs;
39
40         if(!parent && rootfs) {
41                 printf("fs_mount error: root filesystem already mounted!\n");
42                 return 0;
43         }
44
45         for(i=0; i<NUM_FSTYPES; i++) {
46                 if((fs = createfs[i](dev, start, size))) {
47                         if(!parent) {
48                                 rootfs = fs;
49
50                                 fs_chdir("/");
51                         } else {
52                                 parent->mnt = fs;
53                                 mtab_add(parent, fs);
54                         }
55                         return fs;
56                 }
57         }
58
59         printf("failed to mount filesystem dev: %d, start %llu\n", dev, (unsigned long long)start);
60         return 0;
61 }
62
63 static char cwdpath[1024];
64 static char *cwdpath_end = cwdpath;
65
66 int fs_chdir(const char *path)
67 {
68         struct fs_node *node;
69         char *uppath = 0;
70         int uplen;
71
72         if(!path || !*path) {
73                 return -1;
74         }
75
76         if(strcmp(path, ".") == 0) {
77                 return 0;
78         }
79         if(strcmp(path, "..") == 0) {
80                 char *endptr;
81
82                 if(cwdpath_end <= cwdpath + 1) {
83                         return -1;
84                 }
85
86                 endptr = cwdpath + (cwdpath_end - cwdpath);
87                 while(endptr > cwdpath && *--endptr != '/');
88                 if(endptr == cwdpath) endptr++;
89
90                 uplen = endptr - cwdpath;
91                 uppath = alloca(uplen + 1);
92                 memcpy(uppath, cwdpath, uplen);
93                 uppath[uplen] = 0;
94
95                 path = uppath;
96         }
97
98         if(!(node = fs_open(path, 0))) {
99                 return -1;
100         }
101         if(node->type != FSNODE_DIR) {
102                 fs_close(node);
103                 return -1;
104         }
105
106         if(uppath) {
107                 memcpy(cwdpath, uppath, uplen + 1);
108                 cwdpath_end = cwdpath + uplen;
109
110         } else {
111                 int len = strlen(path);
112                 if(cwdpath_end - cwdpath + len > sizeof cwdpath) {
113                         panic("fs_chdir: path too long: %s\n", path);
114                 }
115                 if(path[0] == '/') {
116                         memcpy(cwdpath, path, len + 1);
117                         cwdpath_end = cwdpath + len;
118                 } else {
119                         if(cwdpath_end > cwdpath + 1) {
120                                 *cwdpath_end++ = '/';
121                         }
122                         memcpy(cwdpath_end, path, len + 1);
123                         cwdpath_end += len;
124                 }
125         }
126
127         fs_close(cwdnode);
128         cwdnode = node;
129         return 0;
130 }
131
132 char *fs_getcwd(void)
133 {
134         return cwdpath;
135 }
136
137 /* TODO normalize path */
138 struct fs_node *fs_open(const char *path, unsigned int flags)
139 {
140         struct filesys *fs;
141         struct fs_node *node;
142
143         if(!path || !*path) {
144                 return 0;
145         }
146
147         if(*path == '/') {
148                 fs = rootfs;
149         } else {
150                 if(!cwdnode) return 0;
151                 fs = cwdnode->fs;
152         }
153
154         if(!(node = fs->fsop->open(fs, path, flags))) {
155                 return 0;
156         }
157         return node;
158 }
159
160 int fs_close(struct fs_node *node)
161 {
162         struct fs_operations *fsop;
163
164         if(!node) return -1;
165
166         fsop = node->fs->fsop;
167         fsop->close(node);
168         return 0;
169 }
170
171 int fs_rename(struct fs_node *node, const char *name)
172 {
173         struct fs_operations *fsop = node->fs->fsop;
174         return fsop->rename(node, name);
175 }
176
177 int fs_remove(struct fs_node *node)
178 {
179         struct fs_operations *fsop = node->fs->fsop;
180         return fsop->remove(node);
181 }
182
183 long fs_filesize(struct fs_node *node)
184 {
185         struct fs_operations *fsop = node->fs->fsop;
186
187         if(node->type != FSNODE_FILE) {
188                 return -1;
189         }
190         return fsop->fsize(node);
191 }
192
193 int fs_seek(struct fs_node *node, int offs, int whence)
194 {
195         struct fs_operations *fsop = node->fs->fsop;
196
197         if(node->type != FSNODE_FILE) {
198                 return -1;
199         }
200         return fsop->seek(node, offs, whence);
201 }
202
203 long fs_tell(struct fs_node *node)
204 {
205         struct fs_operations *fsop = node->fs->fsop;
206
207         if(node->type != FSNODE_FILE) {
208                 return -1;
209         }
210         return fsop->tell(node);
211 }
212
213 int fs_read(struct fs_node *node, void *buf, int sz)
214 {
215         struct fs_operations *fsop = node->fs->fsop;
216
217         if(node->type != FSNODE_FILE) {
218                 return -1;
219         }
220         return fsop->read(node, buf, sz);
221 }
222
223 int fs_write(struct fs_node *node, void *buf, int sz)
224 {
225         struct fs_operations *fsop = node->fs->fsop;
226
227         if(node->type != FSNODE_FILE) {
228                 return -1;
229         }
230         return fsop->write(node, buf, sz);
231 }
232
233 int fs_rewinddir(struct fs_node *node)
234 {
235         struct fs_operations *fsop = node->fs->fsop;
236
237         if(node->type != FSNODE_DIR) {
238                 return -1;
239         }
240         return fsop->rewinddir(node);
241 }
242
243 struct fs_dirent *fs_readdir(struct fs_node *node)
244 {
245         struct fs_operations *fsop = node->fs->fsop;
246
247         if(node->type != FSNODE_DIR) {
248                 return 0;
249         }
250         return fsop->readdir(node);
251 }
252
253 /* fs utility functions */
254 char *fs_path_skipsep(char *s)
255 {
256         while(*s == '/') s++;
257         return s;
258 }
259
260 char *fs_path_next(char *s, char *namebuf, int bufsz)
261 {
262         int len;
263         char *ptr;
264
265         ptr = s = fs_path_skipsep(s);
266
267         while(*ptr && *ptr != '/') ptr++;
268
269         if(namebuf) {
270                 len = ptr - s;
271                 if(len >= bufsz) len = bufsz - 1;
272
273                 memcpy(namebuf, s, len);
274                 namebuf[len] = 0;
275         }
276
277         return fs_path_skipsep(ptr);
278 }