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