| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- /*
- * Copyright (c) 2009 Travis Geiselbrecht
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- #include <debug.h>
- #include <list.h>
- #include <err.h>
- #include <string.h>
- #include <stdlib.h>
- #include <lib/fs.h>
- #include <lib/bio.h>
- #if WITH_LIB_FS_EXT2
- #include <lib/fs/ext2.h>
- #endif
- #if WITH_LIB_FS_FAT32
- #include <lib/fs/fat32.h>
- #endif
- #define LOCAL_TRACE 0
- struct fs_type {
- const char *name;
- int (*mount)(bdev_t *, fscookie *);
- int (*unmount)(fscookie);
- int (*open)(fscookie, const char *, filecookie *);
- int (*create)(fscookie, const char *, filecookie *);
- int (*mkdir)(fscookie, const char *);
- int (*stat)(filecookie, struct file_stat *);
- int (*read)(filecookie, void *, off_t, size_t);
- int (*write)(filecookie, const void *, off_t, size_t);
- int (*close)(filecookie);
- };
- struct fs_mount {
- struct list_node node;
- char *path;
- bdev_t *dev;
- fscookie cookie;
- int refs;
- struct fs_type *type;
- };
- struct fs_file {
- filecookie cookie;
- struct fs_mount *mount;
- };
- static struct list_node mounts;
- static struct fs_type types[] = {
- #if WITH_LIB_FS_EXT2
- {
- .name = "ext2",
- .mount = ext2_mount,
- .unmount = ext2_unmount,
- .open = ext2_open_file,
- .stat = ext2_stat_file,
- .read = ext2_read_file,
- .close = ext2_close_file,
- },
- #endif
- #if WITH_LIB_FS_FAT32
- {
- .name = "fat32",
- .mount = fat32_mount,
- .unmount = fat32_unmount,
- .open = fat32_open_file,
- .create = fat32_create_file,
- .mkdir = fat32_make_dir,
- .stat = fat32_stat_file,
- .read = fat32_read_file,
- .write = fat32_write_file,
- .close = fat32_close_file,
- },
- #endif
- };
- static void test_normalize(const char *in);
- static struct fs_mount *find_mount(const char *path, const char **trimmed_path);
- void fs_init(void)
- {
- list_initialize(&mounts);
- #if 0
- test_normalize("/");
- test_normalize("/test");
- test_normalize("/test/");
- test_normalize("test/");
- test_normalize("test");
- test_normalize("/test//");
- test_normalize("/test/foo");
- test_normalize("/test/foo/");
- test_normalize("/test/foo/bar");
- test_normalize("/test/foo/bar//");
- test_normalize("/test//foo/bar//");
- test_normalize("/test//./foo/bar//");
- test_normalize("/test//./.foo/bar//");
- test_normalize("/test//./..foo/bar//");
- test_normalize("/test//./../foo/bar//");
- test_normalize("/test/../foo");
- test_normalize("/test/bar/../foo");
- test_normalize("../foo");
- test_normalize("../foo/");
- test_normalize("/../foo");
- test_normalize("/../foo/");
- test_normalize("/../../foo");
- test_normalize("/bleh/../../foo");
- test_normalize("/bleh/bar/../../foo");
- test_normalize("/bleh/bar/../../foo/..");
- test_normalize("/bleh/bar/../../foo/../meh");
- #endif
- }
- static struct fs_mount *find_mount(const char *path, const char **trimmed_path)
- {
- struct fs_mount *mount;
- size_t pathlen = strlen(path);
- list_for_every_entry(&mounts, mount, struct fs_mount, node) {
- size_t mountpathlen = strlen(mount->path);
- if (pathlen < mountpathlen)
- continue;
- LTRACEF("comparing %s with %s\n", path, mount->path);
- if (memcmp(path, mount->path, mountpathlen) == 0) {
- if (trimmed_path)
- *trimmed_path = &path[mountpathlen];
- return mount;
- }
- }
- return NULL;
- }
- static int mount(const char *path, const char *device, struct fs_type *type)
- {
- char temppath[512];
- strlcpy(temppath, path, sizeof(temppath));
- fs_normalize_path(temppath);
- if(temppath[0] != '/')
- return ERR_BAD_PATH;
- if (find_mount(temppath, NULL))
- return ERR_ALREADY_MOUNTED;
- bdev_t *dev = bio_open(device);
- if (!dev)
- return ERR_NOT_FOUND;
- fscookie cookie;
- int err = type->mount(dev, &cookie);
- if (err < 0) {
- bio_close(dev);
- return err;
- }
- /* create the mount structure and add it to the list */
- struct fs_mount *mount = malloc(sizeof(struct fs_mount));
- mount->path = strdup(temppath);
- mount->dev = dev;
- mount->cookie = cookie;
- mount->refs = 1;
- mount->type = type;
- list_add_head(&mounts, &mount->node);
- return 0;
- }
- int fs_mount(const char *path, const char *device)
- {
- return mount(path, device, &types[0]);
- }
- int fs_mount_type(const char *path, const char *device, const char *name)
- {
- size_t i;
- for (i = 0; i < countof(types); i++) {
- if (!strcmp(name, types[i].name))
- return mount(path, device, &types[i]);
- }
- return ERR_NOT_FOUND;
- }
- static void put_mount(struct fs_mount *mount)
- {
- if (!(--mount->refs)) {
- list_delete(&mount->node);
- mount->type->unmount(mount->cookie);
- free(mount->path);
- bio_close(mount->dev);
- free(mount);
- }
- }
- int fs_unmount(const char *path)
- {
- char temppath[512];
- strlcpy(temppath, path, sizeof(temppath));
- fs_normalize_path(temppath);
- struct fs_mount *mount = find_mount(temppath, NULL);
- if (!mount)
- return ERR_NOT_FOUND;
- put_mount(mount);
- return 0;
- }
- int fs_open_file(const char *path, filecookie *fcookie)
- {
- int err;
- char temppath[512];
- filecookie cookie;
- strlcpy(temppath, path, sizeof(temppath));
- fs_normalize_path(temppath);
- LTRACEF("path %s temppath %s\n", path, temppath);
- const char *newpath;
- struct fs_mount *mount = find_mount(temppath, &newpath);
- if (!mount)
- return ERR_NOT_FOUND;
- LTRACEF("path %s temppath %s newpath %s\n", path, temppath, newpath);
- err = mount->type->open(mount->cookie, newpath, &cookie);
- if (err < 0)
- return err;
- struct fs_file *f = malloc(sizeof(*f));
- f->cookie = cookie;
- f->mount = mount;
- mount->refs++;
- *fcookie = f;
- return 0;
- }
- int fs_create_file(const char *path, filecookie *fcookie)
- {
- int err;
- char temppath[512];
- filecookie cookie;
- strlcpy(temppath, path, sizeof(temppath));
- fs_normalize_path(temppath);
- const char *newpath;
- struct fs_mount *mount = find_mount(temppath, &newpath);
- if (!mount)
- return ERR_NOT_FOUND;
- if (!mount->type->create)
- return ERR_NOT_SUPPORTED;
- err = mount->type->create(mount->cookie, newpath, &cookie);
- if (err < 0)
- return err;
- struct fs_file *f = malloc(sizeof(*f));
- f->cookie = cookie;
- f->mount = mount;
- mount->refs++;
- *fcookie = f;
- return 0;
- }
- int fs_make_dir(const char *path)
- {
- char temppath[512];
- strlcpy(temppath, path, sizeof(temppath));
- fs_normalize_path(temppath);
- const char *newpath;
- struct fs_mount *mount = find_mount(temppath, &newpath);
- if (!mount)
- return ERR_NOT_FOUND;
- if (!mount->type->mkdir)
- return ERR_NOT_SUPPORTED;
- return mount->type->mkdir(mount->cookie, newpath);
- }
- int fs_read_file(filecookie fcookie, void *buf, off_t offset, size_t len)
- {
- struct fs_file *f = fcookie;
- return f->mount->type->read(f->cookie, buf, offset, len);
- }
- int fs_write_file(filecookie fcookie, const void *buf, off_t offset, size_t len)
- {
- struct fs_file *f = fcookie;
- if (!f->mount->type->write)
- return ERR_NOT_SUPPORTED;
- return f->mount->type->write(f->cookie, buf, offset, len);
- }
- int fs_close_file(filecookie fcookie)
- {
- int err;
- struct fs_file *f = fcookie;
- err = f->mount->type->close(f->cookie);
- if (err < 0)
- return err;
- put_mount(f->mount);
- free(f);
- return 0;
- }
- int fs_stat_file(filecookie fcookie, struct file_stat *stat)
- {
- struct fs_file *f = fcookie;
- return f->mount->type->stat(f->cookie, stat);
- }
- ssize_t fs_load_file(const char *path, void *ptr, size_t maxlen)
- {
- int err;
- filecookie cookie;
- /* open the file */
- err = fs_open_file(path, &cookie);
- if (err < 0)
- return err;
- /* stat it for size, see how much we need to read */
- struct file_stat stat;
- fs_stat_file(cookie, &stat);
- err = fs_read_file(cookie, ptr, 0, MIN(maxlen, stat.size));
- fs_close_file(cookie);
- return err;
- }
- static void test_normalize(const char *in)
- {
- char path[1024];
- strlcpy(path, in, sizeof(path));
- fs_normalize_path(path);
- printf("'%s' -> '%s'\n", in, path);
- }
- void fs_normalize_path(char *path)
- {
- int outpos;
- int pos;
- char c;
- bool done;
- enum {
- INITIAL,
- FIELD_START,
- IN_FIELD,
- SEP,
- SEEN_SEP,
- DOT,
- SEEN_DOT,
- DOTDOT,
- SEEN_DOTDOT,
- } state;
- state = INITIAL;
- pos = 0;
- outpos = 0;
- done = false;
- /* remove duplicate path seperators, flatten empty fields (only composed of .), backtrack fields with .., remove trailing slashes */
- while (!done) {
- c = path[pos];
- switch (state) {
- case INITIAL:
- if (c == '/') {
- state = SEP;
- } else if (c == '.') {
- state = DOT;
- } else {
- state = FIELD_START;
- }
- break;
- case FIELD_START:
- if (c == '.') {
- state = DOT;
- } else if (c == 0) {
- done = true;
- } else {
- state = IN_FIELD;
- }
- break;
- case IN_FIELD:
- if (c == '/') {
- state = SEP;
- } else if (c == 0) {
- done = true;
- } else {
- path[outpos++] = c;
- pos++;
- }
- break;
- case SEP:
- pos++;
- path[outpos++] = '/';
- state = SEEN_SEP;
- break;
- case SEEN_SEP:
- if (c == '/') {
- // eat it
- pos++;
- } else if (c == 0) {
- done = true;
- } else {
- state = FIELD_START;
- }
- break;
- case DOT:
- pos++; // consume the dot
- state = SEEN_DOT;
- break;
- case SEEN_DOT:
- if (c == '.') {
- // dotdot now
- state = DOTDOT;
- } else if (c == '/') {
- // a field composed entirely of a .
- // consume the / and move directly to the SEEN_SEP state
- pos++;
- state = SEEN_SEP;
- } else if (c == 0) {
- done = true;
- } else {
- // a field prefixed with a .
- // emit a . and move directly into the IN_FIELD state
- path[outpos++] = '.';
- state = IN_FIELD;
- }
- break;
- case DOTDOT:
- pos++; // consume the dot
- state = SEEN_DOTDOT;
- break;
- case SEEN_DOTDOT:
- if (c == '/' || c == 0) {
- // a field composed entirely of '..'
- // search back and consume a field we've already emitted
- if (outpos > 0) {
- // we have already consumed at least one field
- outpos--;
- // walk backwards until we find the next field boundary
- while (outpos > 0) {
- if (path[outpos - 1] == '/') {
- break;
- }
- outpos--;
- }
- }
- pos++;
- state = SEEN_SEP;
- if (c == 0)
- done = true;
- } else {
- // a field prefixed with ..
- // emit the .. and move directly to the IN_FIELD state
- path[outpos++] = '.';
- path[outpos++] = '.';
- state = IN_FIELD;
- }
- break;
- }
- }
- /* dont end with trailing slashes */
- if (outpos > 0 && path[outpos - 1] == '/')
- outpos--;
- path[outpos++] = 0;
- }
|