[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/lib/fs/fs.c b/src/bsp/lk/lib/fs/fs.c
new file mode 100644
index 0000000..378e824
--- /dev/null
+++ b/src/bsp/lk/lib/fs/fs.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2009-2015 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 <trace.h>
+#include <list.h>
+#include <err.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lib/fs.h>
+#include <lib/bio.h>
+#include <lk/init.h>
+#include <kernel/mutex.h>
+
+#define LOCAL_TRACE 0
+
+struct fs_mount {
+    struct list_node node;
+
+    char *path;
+    bdev_t *dev;
+    fscookie *cookie;
+    int ref;
+    const struct fs_api *api;
+};
+
+struct filehandle {
+    filecookie *cookie;
+    struct fs_mount *mount;
+};
+
+struct dirhandle {
+    dircookie *cookie;
+    struct fs_mount *mount;
+};
+
+static mutex_t mount_lock = MUTEX_INITIAL_VALUE(mount_lock);
+static struct list_node mounts = LIST_INITIAL_VALUE(mounts);
+static struct list_node fses = LIST_INITIAL_VALUE(fses);
+
+// defined in the linker script
+extern const struct fs_impl __fs_impl_start;
+extern const struct fs_impl __fs_impl_end;
+
+static const struct fs_impl *find_fs(const char *name)
+{
+    for (const struct fs_impl *fs = &__fs_impl_start; fs != &__fs_impl_end; fs++) {
+        if (!strcmp(name, fs->name))
+            return fs;
+    }
+    return NULL;
+}
+
+
+// find a mount structure based on the prefix of this path
+// bump the ref to the mount structure before returning
+static struct fs_mount *find_mount(const char *path, const char **trimmed_path)
+{
+    struct fs_mount *mount;
+    size_t pathlen = strlen(path);
+
+    mutex_acquire(&mount_lock);
+    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];
+
+            mount->ref++;
+
+            mutex_release(&mount_lock);
+            return mount;
+        }
+    }
+
+    mutex_release(&mount_lock);
+    return NULL;
+}
+
+// decrement the ref to the mount structure, which may
+// cause an unmount operation
+static void put_mount(struct fs_mount *mount)
+{
+    mutex_acquire(&mount_lock);
+    if ((--mount->ref) == 0) {
+        list_delete(&mount->node);
+        mount->api->unmount(mount->cookie);
+        free(mount->path);
+        if (mount->dev)
+            bio_close(mount->dev);
+        free(mount);
+    }
+    mutex_release(&mount_lock);
+}
+
+static status_t mount(const char *path, const char *device, const struct fs_api *api)
+{
+    struct fs_mount *mount;
+    char temppath[FS_MAX_PATH_LEN];
+
+    strlcpy(temppath, path, sizeof(temppath));
+    fs_normalize_path(temppath);
+
+    if (temppath[0] != '/')
+        return ERR_BAD_PATH;
+
+    /* see if there's already something at this path, abort if there is */
+    mount = find_mount(temppath, NULL);
+    if (mount) {
+        put_mount(mount);
+        return ERR_ALREADY_MOUNTED;
+    }
+
+    /* open a bio device if the string is nonnull */
+    bdev_t *dev = NULL;
+    if (device && device[0] != '\0') {
+        dev = bio_open(device);
+        if (!dev)
+            return ERR_NOT_FOUND;
+    }
+
+    /* call into the fs implementation */
+    fscookie *cookie;
+    status_t err = api->mount(dev, &cookie);
+    if (err < 0) {
+        if (dev) bio_close(dev);
+        return err;
+    }
+
+    /* create the mount structure and add it to the list */
+    mount = malloc(sizeof(struct fs_mount));
+    mount->path = strdup(temppath);
+    mount->dev = dev;
+    mount->cookie = cookie;
+    mount->ref = 1;
+    mount->api = api;
+
+    list_add_head(&mounts, &mount->node);
+
+    return 0;
+
+}
+
+status_t fs_format_device(const char *fsname, const char *device, const void *args)
+{
+    const struct fs_impl *fs = find_fs(fsname);
+    if (!fs) {
+        return ERR_NOT_FOUND;
+    }
+
+    if (fs->api->format == NULL) {
+        return ERR_NOT_SUPPORTED;
+    }
+
+    bdev_t *dev = NULL;
+    if (device && device[0] != '\0') {
+        dev = bio_open(device);
+        if (!dev)
+            return ERR_NOT_FOUND;
+    }
+
+    return fs->api->format(dev, args);
+}
+
+status_t fs_mount(const char *path, const char *fsname, const char *device)
+{
+    const struct fs_impl *fs = find_fs(fsname);
+    if (!fs)
+        return ERR_NOT_FOUND;
+
+    return mount(path, device, fs->api);
+}
+
+status_t fs_unmount(const char *path)
+{
+    char temppath[FS_MAX_PATH_LEN];
+
+    strlcpy(temppath, path, sizeof(temppath));
+    fs_normalize_path(temppath);
+
+    struct fs_mount *mount = find_mount(temppath, NULL);
+    if (!mount)
+        return ERR_NOT_FOUND;
+
+    // return the ref that find_mount added and one extra
+    put_mount(mount);
+    put_mount(mount);
+
+    return 0;
+}
+
+
+status_t fs_open_file(const char *path, filehandle **handle)
+{
+    char temppath[FS_MAX_PATH_LEN];
+
+    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);
+
+    filecookie *cookie;
+    status_t err = mount->api->open(mount->cookie, newpath, &cookie);
+    if (err < 0) {
+        put_mount(mount);
+        return err;
+    }
+
+    filehandle *f = malloc(sizeof(*f));
+    f->cookie = cookie;
+    f->mount = mount;
+    *handle = f;
+
+    return 0;
+}
+
+status_t fs_file_ioctl(filehandle *handle, int request, void *argp)
+{
+    LTRACEF("filehandle %p, request %d, argp, %p\n", handle, request, argp);
+
+    if (unlikely(!handle || !handle->mount ||
+                 !handle->mount->api || !handle->mount->api->file_ioctl)) {
+        return ERR_INVALID_ARGS;
+    }
+
+    return handle->mount->api->file_ioctl(handle->cookie, request, argp);
+}
+
+status_t fs_create_file(const char *path, filehandle **handle, uint64_t len)
+{
+    char temppath[FS_MAX_PATH_LEN];
+
+    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->api->create) {
+        put_mount(mount);
+        return ERR_NOT_SUPPORTED;
+    }
+
+    filecookie *cookie;
+    status_t err = mount->api->create(mount->cookie, newpath, &cookie, len);
+    if (err < 0) {
+        put_mount(mount);
+        return err;
+    }
+
+    filehandle *f = malloc(sizeof(*f));
+    f->cookie = cookie;
+    f->mount = mount;
+    *handle = f;
+
+    return 0;
+}
+
+status_t fs_remove_file(const char *path)
+{
+    char temppath[FS_MAX_PATH_LEN];
+
+    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->api->remove) {
+        put_mount(mount);
+        return ERR_NOT_SUPPORTED;
+    }
+
+    status_t err = mount->api->remove(mount->cookie, newpath);
+
+    put_mount(mount);
+
+    return err;
+}
+
+ssize_t fs_read_file(filehandle *handle, void *buf, off_t offset, size_t len)
+{
+    return handle->mount->api->read(handle->cookie, buf, offset, len);
+}
+
+ssize_t fs_write_file(filehandle *handle, const void *buf, off_t offset, size_t len)
+{
+    if (!handle->mount->api->write)
+        return ERR_NOT_SUPPORTED;
+
+    return handle->mount->api->write(handle->cookie, buf, offset, len);
+}
+
+status_t fs_close_file(filehandle *handle)
+{
+    status_t err = handle->mount->api->close(handle->cookie);
+    if (err < 0)
+        return err;
+
+    put_mount(handle->mount);
+    free(handle);
+    return 0;
+}
+
+status_t fs_stat_file(filehandle *handle, struct file_stat *stat)
+{
+    return handle->mount->api->stat(handle->cookie, stat);
+}
+
+status_t fs_make_dir(const char *path)
+{
+    char temppath[FS_MAX_PATH_LEN];
+
+    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->api->mkdir) {
+        put_mount(mount);
+        return ERR_NOT_SUPPORTED;
+    }
+
+    status_t err = mount->api->mkdir(mount->cookie, newpath);
+
+    put_mount(mount);
+
+    return err;
+}
+
+status_t fs_open_dir(const char *path, dirhandle **handle)
+{
+    char temppath[FS_MAX_PATH_LEN];
+
+    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);
+
+    if (!mount->api->opendir) {
+        put_mount(mount);
+        return ERR_NOT_SUPPORTED;
+    }
+
+    dircookie *cookie;
+    status_t err = mount->api->opendir(mount->cookie, newpath, &cookie);
+    if (err < 0) {
+        put_mount(mount);
+        return err;
+    }
+
+    dirhandle *d = malloc(sizeof(*d));
+    d->cookie = cookie;
+    d->mount = mount;
+    *handle = d;
+
+    return 0;
+}
+
+status_t fs_read_dir(dirhandle *handle, struct dirent *ent)
+{
+    if (!handle->mount->api->readdir)
+        return ERR_NOT_SUPPORTED;
+
+    return handle->mount->api->readdir(handle->cookie, ent);
+}
+
+status_t fs_close_dir(dirhandle *handle)
+{
+    if (!handle->mount->api->closedir)
+        return ERR_NOT_SUPPORTED;
+
+    status_t err = handle->mount->api->closedir(handle->cookie);
+    if (err < 0)
+        return err;
+
+    put_mount(handle->mount);
+    free(handle);
+    return 0;
+}
+
+status_t fs_stat_fs(const char *mountpoint, struct fs_stat *stat)
+{
+    LTRACEF("mountpoint %s stat %p\n", mountpoint, stat);
+
+    if (!stat) {
+        return ERR_INVALID_ARGS;
+    }
+
+    const char *newpath;
+    struct fs_mount *mount = find_mount(mountpoint, &newpath);
+    if (!mount) {
+        return ERR_NOT_FOUND;
+    }
+
+    if (!mount->api->fs_stat) {
+        put_mount(mount);
+        return ERR_NOT_SUPPORTED;
+    }
+
+    status_t result = mount->api->fs_stat(mount->cookie, stat);
+
+    put_mount(mount);
+
+    return result;
+}
+
+
+ssize_t fs_load_file(const char *path, void *ptr, size_t maxlen)
+{
+    filehandle *handle;
+
+    /* open the file */
+    status_t err = fs_open_file(path, &handle);
+    if (err < 0)
+        return err;
+
+    /* stat it for size, see how much we need to read */
+    struct file_stat stat;
+    fs_stat_file(handle, &stat);
+
+    ssize_t read_bytes = fs_read_file(handle, ptr, 0, MIN(maxlen, stat.size));
+
+    fs_close_file(handle);
+
+    return read_bytes;
+}
+
+const char *trim_name(const char *_name)
+{
+    const char *name = &_name[0];
+    // chew up leading spaces
+    while (*name == ' ')
+        name++;
+
+    // chew up leading slashes
+    while (*name == '/')
+        name++;
+
+    return name;
+}
+
+
+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;
+}
+