blob: 8389a9d3d8e513e8df5629130277ec3d63078a12 [file] [log] [blame]
Travis Geiselbrechtf86c43c2010-05-06 14:17:34 -07001/*
2 * Copyright (c) 2009 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <list.h>
25#include <err.h>
26#include <string.h>
27#include <stdlib.h>
28#include <lib/fs.h>
29#include <lib/bio.h>
30
31#if WITH_LIB_FS_EXT2
32#include <lib/fs/ext2.h>
33#endif
34#if WITH_LIB_FS_FAT32
35#include <lib/fs/fat32.h>
36#endif
37
38#define LOCAL_TRACE 0
39
40struct fs_type {
41 const char *name;
42 int (*mount)(bdev_t *, fscookie *);
43 int (*unmount)(fscookie);
44 int (*open)(fscookie, const char *, filecookie *);
45 int (*create)(fscookie, const char *, filecookie *);
46 int (*mkdir)(fscookie, const char *);
47 int (*stat)(filecookie, struct file_stat *);
48 int (*read)(filecookie, void *, off_t, size_t);
49 int (*write)(filecookie, const void *, off_t, size_t);
50 int (*close)(filecookie);
51};
52
53struct fs_mount {
54 struct list_node node;
55 char *path;
56 bdev_t *dev;
57 fscookie cookie;
58 int refs;
59 struct fs_type *type;
60};
61
62struct fs_file {
63 filecookie cookie;
64 struct fs_mount *mount;
65};
66
67static struct list_node mounts;
68
69static struct fs_type types[] = {
70#if WITH_LIB_FS_EXT2
71 {
72 .name = "ext2",
73 .mount = ext2_mount,
74 .unmount = ext2_unmount,
75 .open = ext2_open_file,
76 .stat = ext2_stat_file,
77 .read = ext2_read_file,
78 .close = ext2_close_file,
79 },
80#endif
81#if WITH_LIB_FS_FAT32
82 {
83 .name = "fat32",
84 .mount = fat32_mount,
85 .unmount = fat32_unmount,
86 .open = fat32_open_file,
87 .create = fat32_create_file,
88 .mkdir = fat32_make_dir,
89 .stat = fat32_stat_file,
90 .read = fat32_read_file,
91 .write = fat32_write_file,
92 .close = fat32_close_file,
93 },
94#endif
95};
96
97static void test_normalize(const char *in);
98static struct fs_mount *find_mount(const char *path, const char **trimmed_path);
99
100void fs_init(void)
101{
102 list_initialize(&mounts);
103#if 0
104 test_normalize("/");
105 test_normalize("/test");
106 test_normalize("/test/");
107 test_normalize("test/");
108 test_normalize("test");
109 test_normalize("/test//");
110 test_normalize("/test/foo");
111 test_normalize("/test/foo/");
112 test_normalize("/test/foo/bar");
113 test_normalize("/test/foo/bar//");
114 test_normalize("/test//foo/bar//");
115 test_normalize("/test//./foo/bar//");
116 test_normalize("/test//./.foo/bar//");
117 test_normalize("/test//./..foo/bar//");
118 test_normalize("/test//./../foo/bar//");
119 test_normalize("/test/../foo");
120 test_normalize("/test/bar/../foo");
121 test_normalize("../foo");
122 test_normalize("../foo/");
123 test_normalize("/../foo");
124 test_normalize("/../foo/");
125 test_normalize("/../../foo");
126 test_normalize("/bleh/../../foo");
127 test_normalize("/bleh/bar/../../foo");
128 test_normalize("/bleh/bar/../../foo/..");
129 test_normalize("/bleh/bar/../../foo/../meh");
130#endif
131}
132
133static struct fs_mount *find_mount(const char *path, const char **trimmed_path)
134{
135 struct fs_mount *mount;
136 size_t pathlen = strlen(path);
137
138 list_for_every_entry(&mounts, mount, struct fs_mount, node) {
139 size_t mountpathlen = strlen(mount->path);
140 if (pathlen < mountpathlen)
141 continue;
142
143 LTRACEF("comparing %s with %s\n", path, mount->path);
144
145 if (memcmp(path, mount->path, mountpathlen) == 0) {
146 if (trimmed_path)
147 *trimmed_path = &path[mountpathlen];
148
149 return mount;
150 }
151 }
152
153 return NULL;
154}
155
156static int mount(const char *path, const char *device, struct fs_type *type)
157{
158 char temppath[512];
159
160 strlcpy(temppath, path, sizeof(temppath));
161 fs_normalize_path(temppath);
162
163 if(temppath[0] != '/')
164 return ERR_BAD_PATH;
165
166 if (find_mount(temppath, NULL))
167 return ERR_ALREADY_MOUNTED;
168
169 bdev_t *dev = bio_open(device);
170 if (!dev)
171 return ERR_NOT_FOUND;
172
173 fscookie cookie;
174 int err = type->mount(dev, &cookie);
175 if (err < 0) {
176 bio_close(dev);
177 return err;
178 }
179
180 /* create the mount structure and add it to the list */
181 struct fs_mount *mount = malloc(sizeof(struct fs_mount));
182 mount->path = strdup(temppath);
183 mount->dev = dev;
184 mount->cookie = cookie;
185 mount->refs = 1;
186 mount->type = type;
187
188 list_add_head(&mounts, &mount->node);
189
190 return 0;
191}
192
193int fs_mount(const char *path, const char *device)
194{
195 return mount(path, device, &types[0]);
196}
197
198int fs_mount_type(const char *path, const char *device, const char *name)
199{
200 size_t i;
201
202 for (i = 0; i < countof(types); i++) {
203 if (!strcmp(name, types[i].name))
204 return mount(path, device, &types[i]);
205 }
206
207 return ERR_NOT_FOUND;
208}
209
210static void put_mount(struct fs_mount *mount)
211{
212 if (!(--mount->refs)) {
213 list_delete(&mount->node);
214 mount->type->unmount(mount->cookie);
215 free(mount->path);
216 bio_close(mount->dev);
217 free(mount);
218 }
219}
220
221int fs_unmount(const char *path)
222{
223 char temppath[512];
224
225 strlcpy(temppath, path, sizeof(temppath));
226 fs_normalize_path(temppath);
227
228 struct fs_mount *mount = find_mount(temppath, NULL);
229 if (!mount)
230 return ERR_NOT_FOUND;
231
232 put_mount(mount);
233
234 return 0;
235}
236
237
238int fs_open_file(const char *path, filecookie *fcookie)
239{
240 int err;
241 char temppath[512];
242 filecookie cookie;
243
244 strlcpy(temppath, path, sizeof(temppath));
245 fs_normalize_path(temppath);
246
247 LTRACEF("path %s temppath %s\n", path, temppath);
248
249 const char *newpath;
250 struct fs_mount *mount = find_mount(temppath, &newpath);
251 if (!mount)
252 return ERR_NOT_FOUND;
253
254 LTRACEF("path %s temppath %s newpath %s\n", path, temppath, newpath);
255
256 err = mount->type->open(mount->cookie, newpath, &cookie);
257 if (err < 0)
258 return err;
259
260 struct fs_file *f = malloc(sizeof(*f));
261 f->cookie = cookie;
262 f->mount = mount;
263 mount->refs++;
264 *fcookie = f;
265
266 return 0;
267}
268
269int fs_create_file(const char *path, filecookie *fcookie)
270{
271 int err;
272 char temppath[512];
273 filecookie cookie;
274
275 strlcpy(temppath, path, sizeof(temppath));
276 fs_normalize_path(temppath);
277
278 const char *newpath;
279 struct fs_mount *mount = find_mount(temppath, &newpath);
280 if (!mount)
281 return ERR_NOT_FOUND;
282
283 if (!mount->type->create)
284 return ERR_NOT_SUPPORTED;
285
286 err = mount->type->create(mount->cookie, newpath, &cookie);
287 if (err < 0)
288 return err;
289
290 struct fs_file *f = malloc(sizeof(*f));
291 f->cookie = cookie;
292 f->mount = mount;
293 mount->refs++;
294 *fcookie = f;
295
296 return 0;
297}
298
299int fs_make_dir(const char *path)
300{
301 char temppath[512];
302
303 strlcpy(temppath, path, sizeof(temppath));
304 fs_normalize_path(temppath);
305
306 const char *newpath;
307 struct fs_mount *mount = find_mount(temppath, &newpath);
308 if (!mount)
309 return ERR_NOT_FOUND;
310
311 if (!mount->type->mkdir)
312 return ERR_NOT_SUPPORTED;
313
314 return mount->type->mkdir(mount->cookie, newpath);
315}
316
317int fs_read_file(filecookie fcookie, void *buf, off_t offset, size_t len)
318{
319 struct fs_file *f = fcookie;
320
321 return f->mount->type->read(f->cookie, buf, offset, len);
322}
323
324int fs_write_file(filecookie fcookie, const void *buf, off_t offset, size_t len)
325{
326 struct fs_file *f = fcookie;
327
328 if (!f->mount->type->write)
329 return ERR_NOT_SUPPORTED;
330
331 return f->mount->type->write(f->cookie, buf, offset, len);
332}
333
334int fs_close_file(filecookie fcookie)
335{
336 int err;
337 struct fs_file *f = fcookie;
338
339 err = f->mount->type->close(f->cookie);
340 if (err < 0)
341 return err;
342
343 put_mount(f->mount);
344 free(f);
345 return 0;
346}
347
348int fs_stat_file(filecookie fcookie, struct file_stat *stat)
349{
350 struct fs_file *f = fcookie;
351
352 return f->mount->type->stat(f->cookie, stat);
353}
354
355ssize_t fs_load_file(const char *path, void *ptr, size_t maxlen)
356{
357 int err;
358 filecookie cookie;
359
360 /* open the file */
361 err = fs_open_file(path, &cookie);
362 if (err < 0)
363 return err;
364
365 /* stat it for size, see how much we need to read */
366 struct file_stat stat;
367 fs_stat_file(cookie, &stat);
368
369 err = fs_read_file(cookie, ptr, 0, MIN(maxlen, stat.size));
370
371 fs_close_file(cookie);
372
373 return err;
374}
375
376static void test_normalize(const char *in)
377{
378 char path[1024];
379
380 strlcpy(path, in, sizeof(path));
381 fs_normalize_path(path);
382 printf("'%s' -> '%s'\n", in, path);
383}
384
385void fs_normalize_path(char *path)
386{
387 int outpos;
388 int pos;
389 char c;
390 bool done;
391 enum {
392 INITIAL,
393 FIELD_START,
394 IN_FIELD,
395 SEP,
396 SEEN_SEP,
397 DOT,
398 SEEN_DOT,
399 DOTDOT,
400 SEEN_DOTDOT,
401 } state;
402
403 state = INITIAL;
404 pos = 0;
405 outpos = 0;
406 done = false;
407
408 /* remove duplicate path seperators, flatten empty fields (only composed of .), backtrack fields with .., remove trailing slashes */
409 while (!done) {
410 c = path[pos];
411 switch (state) {
412 case INITIAL:
413 if (c == '/') {
414 state = SEP;
415 } else if (c == '.') {
416 state = DOT;
417 } else {
418 state = FIELD_START;
419 }
420 break;
421 case FIELD_START:
422 if (c == '.') {
423 state = DOT;
424 } else if (c == 0) {
425 done = true;
426 } else {
427 state = IN_FIELD;
428 }
429 break;
430 case IN_FIELD:
431 if (c == '/') {
432 state = SEP;
433 } else if (c == 0) {
434 done = true;
435 } else {
436 path[outpos++] = c;
437 pos++;
438 }
439 break;
440 case SEP:
441 pos++;
442 path[outpos++] = '/';
443 state = SEEN_SEP;
444 break;
445 case SEEN_SEP:
446 if (c == '/') {
447 // eat it
448 pos++;
449 } else if (c == 0) {
450 done = true;
451 } else {
452 state = FIELD_START;
453 }
454 break;
455 case DOT:
456 pos++; // consume the dot
457 state = SEEN_DOT;
458 break;
459 case SEEN_DOT:
460 if (c == '.') {
461 // dotdot now
462 state = DOTDOT;
463 } else if (c == '/') {
464 // a field composed entirely of a .
465 // consume the / and move directly to the SEEN_SEP state
466 pos++;
467 state = SEEN_SEP;
468 } else if (c == 0) {
469 done = true;
470 } else {
471 // a field prefixed with a .
472 // emit a . and move directly into the IN_FIELD state
473 path[outpos++] = '.';
474 state = IN_FIELD;
475 }
476 break;
477 case DOTDOT:
478 pos++; // consume the dot
479 state = SEEN_DOTDOT;
480 break;
481 case SEEN_DOTDOT:
482 if (c == '/' || c == 0) {
483 // a field composed entirely of '..'
484 // search back and consume a field we've already emitted
485 if (outpos > 0) {
486 // we have already consumed at least one field
487 outpos--;
488
489 // walk backwards until we find the next field boundary
490 while (outpos > 0) {
491 if (path[outpos - 1] == '/') {
492 break;
493 }
494 outpos--;
495 }
496 }
497 pos++;
498 state = SEEN_SEP;
499 if (c == 0)
500 done = true;
501 } else {
502 // a field prefixed with ..
503 // emit the .. and move directly to the IN_FIELD state
504 path[outpos++] = '.';
505 path[outpos++] = '.';
506 state = IN_FIELD;
507 }
508 break;
509 }
510 }
511
512 /* dont end with trailing slashes */
513 if (outpos > 0 && path[outpos - 1] == '/')
514 outpos--;
515
516 path[outpos++] = 0;
517}
518