1 /*
2 * Copyright (c) 2009-2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lk/debug.h>
9 #include <lk/trace.h>
10 #include <lk/list.h>
11 #include <lk/err.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <lib/fs.h>
15 #include <lib/bio.h>
16 #include <lk/init.h>
17 #include <kernel/mutex.h>
18
19 #define LOCAL_TRACE 0
20
21 struct fs_mount {
22 struct list_node node;
23
24 char *path;
25 bdev_t *dev;
26 fscookie *cookie;
27 int ref;
28 const struct fs_api *api;
29 };
30
31 struct filehandle {
32 filecookie *cookie;
33 struct fs_mount *mount;
34 };
35
36 struct dirhandle {
37 dircookie *cookie;
38 struct fs_mount *mount;
39 };
40
41 static mutex_t mount_lock = MUTEX_INITIAL_VALUE(mount_lock);
42 static struct list_node mounts = LIST_INITIAL_VALUE(mounts);
43 static struct list_node fses = LIST_INITIAL_VALUE(fses);
44
45 // defined by the linker, wrapping all structs in the "fs_impl" section
46 extern const struct fs_impl __start_fs_impl __WEAK;
47 extern const struct fs_impl __stop_fs_impl __WEAK;
48
find_fs(const char * name)49 static const struct fs_impl *find_fs(const char *name) {
50 for (const struct fs_impl *fs = &__start_fs_impl; fs != &__stop_fs_impl; fs++) {
51 if (!strcmp(name, fs->name))
52 return fs;
53 }
54 return NULL;
55 }
56
57
58 // find a mount structure based on the prefix of this path
59 // bump the ref to the mount structure before returning
find_mount(const char * path,const char ** trimmed_path)60 static struct fs_mount *find_mount(const char *path, const char **trimmed_path) {
61 struct fs_mount *mount;
62 size_t pathlen = strlen(path);
63
64 mutex_acquire(&mount_lock);
65 list_for_every_entry(&mounts, mount, struct fs_mount, node) {
66 size_t mountpathlen = strlen(mount->path);
67 if (pathlen < mountpathlen)
68 continue;
69
70 LTRACEF("comparing %s with %s\n", path, mount->path);
71
72 if (memcmp(path, mount->path, mountpathlen) == 0) {
73 if (trimmed_path)
74 *trimmed_path = &path[mountpathlen];
75
76 mount->ref++;
77
78 mutex_release(&mount_lock);
79 return mount;
80 }
81 }
82
83 mutex_release(&mount_lock);
84 return NULL;
85 }
86
87 // decrement the ref to the mount structure, which may
88 // cause an unmount operation
put_mount(struct fs_mount * mount)89 static void put_mount(struct fs_mount *mount) {
90 mutex_acquire(&mount_lock);
91 if ((--mount->ref) == 0) {
92 list_delete(&mount->node);
93 mount->api->unmount(mount->cookie);
94 free(mount->path);
95 if (mount->dev)
96 bio_close(mount->dev);
97 free(mount);
98 }
99 mutex_release(&mount_lock);
100 }
101
mount(const char * path,const char * device,const struct fs_api * api)102 static status_t mount(const char *path, const char *device, const struct fs_api *api) {
103 struct fs_mount *mount;
104 char temppath[FS_MAX_PATH_LEN];
105
106 strlcpy(temppath, path, sizeof(temppath));
107 fs_normalize_path(temppath);
108
109 if (temppath[0] != '/')
110 return ERR_BAD_PATH;
111
112 /* see if there's already something at this path, abort if there is */
113 mount = find_mount(temppath, NULL);
114 if (mount) {
115 put_mount(mount);
116 return ERR_ALREADY_MOUNTED;
117 }
118
119 /* open a bio device if the string is nonnull */
120 bdev_t *dev = NULL;
121 if (device && device[0] != '\0') {
122 dev = bio_open(device);
123 if (!dev)
124 return ERR_NOT_FOUND;
125 }
126
127 /* call into the fs implementation */
128 fscookie *cookie;
129 status_t err = api->mount(dev, &cookie);
130 if (err < 0) {
131 if (dev) bio_close(dev);
132 return err;
133 }
134
135 /* create the mount structure and add it to the list */
136 mount = malloc(sizeof(struct fs_mount));
137 if (!mount) {
138 if (dev) bio_close(dev);
139 return ERR_NO_MEMORY;
140 }
141 mount->path = strdup(temppath);
142 if (!mount->path) {
143 if (dev) bio_close(dev);
144 free(mount);
145 return ERR_NO_MEMORY;
146 }
147 mount->dev = dev;
148 mount->cookie = cookie;
149 mount->ref = 1;
150 mount->api = api;
151
152 list_add_head(&mounts, &mount->node);
153
154 return 0;
155
156 }
157
fs_format_device(const char * fsname,const char * device,const void * args)158 status_t fs_format_device(const char *fsname, const char *device, const void *args) {
159 const struct fs_impl *fs = find_fs(fsname);
160 if (!fs) {
161 return ERR_NOT_FOUND;
162 }
163
164 if (fs->api->format == NULL) {
165 return ERR_NOT_SUPPORTED;
166 }
167
168 bdev_t *dev = NULL;
169 if (device && device[0] != '\0') {
170 dev = bio_open(device);
171 if (!dev)
172 return ERR_NOT_FOUND;
173 }
174
175 return fs->api->format(dev, args);
176 }
177
fs_mount(const char * path,const char * fsname,const char * device)178 status_t fs_mount(const char *path, const char *fsname, const char *device) {
179 const struct fs_impl *fs = find_fs(fsname);
180 if (!fs)
181 return ERR_NOT_FOUND;
182
183 return mount(path, device, fs->api);
184 }
185
fs_unmount(const char * path)186 status_t fs_unmount(const char *path) {
187 char temppath[FS_MAX_PATH_LEN];
188
189 strlcpy(temppath, path, sizeof(temppath));
190 fs_normalize_path(temppath);
191
192 struct fs_mount *mount = find_mount(temppath, NULL);
193 if (!mount)
194 return ERR_NOT_FOUND;
195
196 // return the ref that find_mount added and one extra
197 put_mount(mount);
198 put_mount(mount);
199
200 return 0;
201 }
202
203
fs_open_file(const char * path,filehandle ** handle)204 status_t fs_open_file(const char *path, filehandle **handle) {
205 char temppath[FS_MAX_PATH_LEN];
206
207 strlcpy(temppath, path, sizeof(temppath));
208 fs_normalize_path(temppath);
209
210 LTRACEF("path %s temppath %s\n", path, temppath);
211
212 const char *newpath;
213 struct fs_mount *mount = find_mount(temppath, &newpath);
214 if (!mount)
215 return ERR_NOT_FOUND;
216
217 LTRACEF("path %s temppath %s newpath %s\n", path, temppath, newpath);
218
219 filecookie *cookie;
220 status_t err = mount->api->open(mount->cookie, newpath, &cookie);
221 if (err < 0) {
222 put_mount(mount);
223 return err;
224 }
225
226 filehandle *f = malloc(sizeof(*f));
227 f->cookie = cookie;
228 f->mount = mount;
229 *handle = f;
230
231 return 0;
232 }
233
fs_file_ioctl(filehandle * handle,int request,void * argp)234 status_t fs_file_ioctl(filehandle *handle, int request, void *argp) {
235 LTRACEF("filehandle %p, request %d, argp, %p\n", handle, request, argp);
236
237 if (unlikely(!handle || !handle->mount ||
238 !handle->mount->api || !handle->mount->api->file_ioctl)) {
239 return ERR_INVALID_ARGS;
240 }
241
242 return handle->mount->api->file_ioctl(handle->cookie, request, argp);
243 }
244
fs_create_file(const char * path,filehandle ** handle,uint64_t len)245 status_t fs_create_file(const char *path, filehandle **handle, uint64_t len) {
246 char temppath[FS_MAX_PATH_LEN];
247
248 strlcpy(temppath, path, sizeof(temppath));
249 fs_normalize_path(temppath);
250
251 const char *newpath;
252 struct fs_mount *mount = find_mount(temppath, &newpath);
253 if (!mount)
254 return ERR_NOT_FOUND;
255
256 if (!mount->api->create) {
257 put_mount(mount);
258 return ERR_NOT_SUPPORTED;
259 }
260
261 filecookie *cookie;
262 status_t err = mount->api->create(mount->cookie, newpath, &cookie, len);
263 if (err < 0) {
264 put_mount(mount);
265 return err;
266 }
267
268 filehandle *f = malloc(sizeof(*f));
269 if (!f) {
270 put_mount(mount);
271 return err;
272 }
273 f->cookie = cookie;
274 f->mount = mount;
275 *handle = f;
276
277 return 0;
278 }
279
fs_truncate_file(filehandle * handle,uint64_t len)280 status_t fs_truncate_file(filehandle *handle, uint64_t len) {
281 LTRACEF("filehandle %p, length %llu\n", handle, len);
282
283 if (unlikely(!handle))
284 return ERR_INVALID_ARGS;
285
286 return handle->mount->api->truncate(handle->cookie, len);
287 }
288
fs_remove_file(const char * path)289 status_t fs_remove_file(const char *path) {
290 char temppath[FS_MAX_PATH_LEN];
291
292 strlcpy(temppath, path, sizeof(temppath));
293 fs_normalize_path(temppath);
294
295 const char *newpath;
296 struct fs_mount *mount = find_mount(temppath, &newpath);
297 if (!mount)
298 return ERR_NOT_FOUND;
299
300 if (!mount->api->remove) {
301 put_mount(mount);
302 return ERR_NOT_SUPPORTED;
303 }
304
305 status_t err = mount->api->remove(mount->cookie, newpath);
306
307 put_mount(mount);
308
309 return err;
310 }
311
fs_read_file(filehandle * handle,void * buf,off_t offset,size_t len)312 ssize_t fs_read_file(filehandle *handle, void *buf, off_t offset, size_t len) {
313 return handle->mount->api->read(handle->cookie, buf, offset, len);
314 }
315
fs_write_file(filehandle * handle,const void * buf,off_t offset,size_t len)316 ssize_t fs_write_file(filehandle *handle, const void *buf, off_t offset, size_t len) {
317 if (!handle->mount->api->write)
318 return ERR_NOT_SUPPORTED;
319
320 return handle->mount->api->write(handle->cookie, buf, offset, len);
321 }
322
fs_close_file(filehandle * handle)323 status_t fs_close_file(filehandle *handle) {
324 status_t err = handle->mount->api->close(handle->cookie);
325 if (err < 0)
326 return err;
327
328 put_mount(handle->mount);
329 free(handle);
330 return 0;
331 }
332
fs_stat_file(filehandle * handle,struct file_stat * stat)333 status_t fs_stat_file(filehandle *handle, struct file_stat *stat) {
334 return handle->mount->api->stat(handle->cookie, stat);
335 }
336
fs_make_dir(const char * path)337 status_t fs_make_dir(const char *path) {
338 char temppath[FS_MAX_PATH_LEN];
339
340 strlcpy(temppath, path, sizeof(temppath));
341 fs_normalize_path(temppath);
342
343 const char *newpath;
344 struct fs_mount *mount = find_mount(temppath, &newpath);
345 if (!mount)
346 return ERR_NOT_FOUND;
347
348 if (!mount->api->mkdir) {
349 put_mount(mount);
350 return ERR_NOT_SUPPORTED;
351 }
352
353 status_t err = mount->api->mkdir(mount->cookie, newpath);
354
355 put_mount(mount);
356
357 return err;
358 }
359
fs_open_dir(const char * path,dirhandle ** handle)360 status_t fs_open_dir(const char *path, dirhandle **handle) {
361 char temppath[FS_MAX_PATH_LEN];
362
363 strlcpy(temppath, path, sizeof(temppath));
364 fs_normalize_path(temppath);
365
366 LTRACEF("path %s temppath %s\n", path, temppath);
367
368 const char *newpath;
369 struct fs_mount *mount = find_mount(temppath, &newpath);
370 if (!mount)
371 return ERR_NOT_FOUND;
372
373 LTRACEF("path %s temppath %s newpath %s\n", path, temppath, newpath);
374
375 if (!mount->api->opendir) {
376 put_mount(mount);
377 return ERR_NOT_SUPPORTED;
378 }
379
380 dircookie *cookie;
381 status_t err = mount->api->opendir(mount->cookie, newpath, &cookie);
382 if (err < 0) {
383 put_mount(mount);
384 return err;
385 }
386
387 dirhandle *d = malloc(sizeof(*d));
388 if (!d) {
389 put_mount(mount);
390 return ERR_NO_MEMORY;
391 }
392 d->cookie = cookie;
393 d->mount = mount;
394 *handle = d;
395
396 return 0;
397 }
398
fs_read_dir(dirhandle * handle,struct dirent * ent)399 status_t fs_read_dir(dirhandle *handle, struct dirent *ent) {
400 if (!handle->mount->api->readdir)
401 return ERR_NOT_SUPPORTED;
402
403 return handle->mount->api->readdir(handle->cookie, ent);
404 }
405
fs_close_dir(dirhandle * handle)406 status_t fs_close_dir(dirhandle *handle) {
407 if (!handle->mount->api->closedir)
408 return ERR_NOT_SUPPORTED;
409
410 status_t err = handle->mount->api->closedir(handle->cookie);
411 if (err < 0)
412 return err;
413
414 put_mount(handle->mount);
415 free(handle);
416 return 0;
417 }
418
fs_stat_fs(const char * mountpoint,struct fs_stat * stat)419 status_t fs_stat_fs(const char *mountpoint, struct fs_stat *stat) {
420 LTRACEF("mountpoint %s stat %p\n", mountpoint, stat);
421
422 if (!stat) {
423 return ERR_INVALID_ARGS;
424 }
425
426 const char *newpath;
427 struct fs_mount *mount = find_mount(mountpoint, &newpath);
428 if (!mount) {
429 return ERR_NOT_FOUND;
430 }
431
432 if (!mount->api->fs_stat) {
433 put_mount(mount);
434 return ERR_NOT_SUPPORTED;
435 }
436
437 status_t result = mount->api->fs_stat(mount->cookie, stat);
438
439 put_mount(mount);
440
441 return result;
442 }
443
444
fs_load_file(const char * path,void * ptr,size_t maxlen)445 ssize_t fs_load_file(const char *path, void *ptr, size_t maxlen) {
446 filehandle *handle;
447
448 /* open the file */
449 status_t err = fs_open_file(path, &handle);
450 if (err < 0)
451 return err;
452
453 /* stat it for size, see how much we need to read */
454 struct file_stat stat;
455 fs_stat_file(handle, &stat);
456
457 ssize_t read_bytes = fs_read_file(handle, ptr, 0, MIN(maxlen, stat.size));
458
459 fs_close_file(handle);
460
461 return read_bytes;
462 }
463
trim_name(const char * _name)464 const char *trim_name(const char *_name) {
465 const char *name = &_name[0];
466 // chew up leading spaces
467 while (*name == ' ')
468 name++;
469
470 // chew up leading slashes
471 while (*name == '/')
472 name++;
473
474 return name;
475 }
476
477
fs_normalize_path(char * path)478 void fs_normalize_path(char *path) {
479 int outpos;
480 int pos;
481 char c;
482 bool done;
483 enum {
484 INITIAL,
485 FIELD_START,
486 IN_FIELD,
487 SEP,
488 SEEN_SEP,
489 DOT,
490 SEEN_DOT,
491 DOTDOT,
492 SEEN_DOTDOT,
493 } state;
494
495 state = INITIAL;
496 pos = 0;
497 outpos = 0;
498 done = false;
499
500 /* remove duplicate path separators, flatten empty fields (only composed of .), backtrack fields with .., remove trailing slashes */
501 while (!done) {
502 c = path[pos];
503 switch (state) {
504 case INITIAL:
505 if (c == '/') {
506 state = SEP;
507 } else if (c == '.') {
508 state = DOT;
509 } else {
510 state = FIELD_START;
511 }
512 break;
513 case FIELD_START:
514 if (c == '.') {
515 state = DOT;
516 } else if (c == 0) {
517 done = true;
518 } else {
519 state = IN_FIELD;
520 }
521 break;
522 case IN_FIELD:
523 if (c == '/') {
524 state = SEP;
525 } else if (c == 0) {
526 done = true;
527 } else {
528 path[outpos++] = c;
529 pos++;
530 }
531 break;
532 case SEP:
533 pos++;
534 path[outpos++] = '/';
535 state = SEEN_SEP;
536 break;
537 case SEEN_SEP:
538 if (c == '/') {
539 // eat it
540 pos++;
541 } else if (c == 0) {
542 done = true;
543 } else {
544 state = FIELD_START;
545 }
546 break;
547 case DOT:
548 pos++; // consume the dot
549 state = SEEN_DOT;
550 break;
551 case SEEN_DOT:
552 if (c == '.') {
553 // dotdot now
554 state = DOTDOT;
555 } else if (c == '/') {
556 // a field composed entirely of a .
557 // consume the / and move directly to the SEEN_SEP state
558 pos++;
559 state = SEEN_SEP;
560 } else if (c == 0) {
561 done = true;
562 } else {
563 // a field prefixed with a .
564 // emit a . and move directly into the IN_FIELD state
565 path[outpos++] = '.';
566 state = IN_FIELD;
567 }
568 break;
569 case DOTDOT:
570 pos++; // consume the dot
571 state = SEEN_DOTDOT;
572 break;
573 case SEEN_DOTDOT:
574 if (c == '/' || c == 0) {
575 // a field composed entirely of '..'
576 // search back and consume a field we've already emitted
577 if (outpos > 0) {
578 // we have already consumed at least one field
579 outpos--;
580
581 // walk backwards until we find the next field boundary
582 while (outpos > 0) {
583 if (path[outpos - 1] == '/') {
584 break;
585 }
586 outpos--;
587 }
588 }
589 pos++;
590 state = SEEN_SEP;
591 if (c == 0)
592 done = true;
593 } else {
594 // a field prefixed with ..
595 // emit the .. and move directly to the IN_FIELD state
596 path[outpos++] = '.';
597 path[outpos++] = '.';
598 state = IN_FIELD;
599 }
600 break;
601 }
602 }
603
604 /* don't end with trailing slashes */
605 if (outpos > 0 && path[outpos - 1] == '/')
606 outpos--;
607
608 path[outpos++] = 0;
609 }
610
611