diff options
Diffstat (limited to 'exfat/libexfat/lookup.c')
-rw-r--r-- | exfat/libexfat/lookup.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/exfat/libexfat/lookup.c b/exfat/libexfat/lookup.c new file mode 100644 index 000000000..8c889b2b2 --- /dev/null +++ b/exfat/libexfat/lookup.c @@ -0,0 +1,223 @@ +/* + lookup.c (02.09.09) + exFAT file system implementation library. + + Copyright (C) 2010-2012 Andrew Nayenko + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "exfat.h" +#include <string.h> +#include <errno.h> +#include <inttypes.h> + +int exfat_opendir(struct exfat* ef, struct exfat_node* dir, + struct exfat_iterator* it) +{ + int rc; + + exfat_get_node(dir); + it->parent = dir; + it->current = NULL; + rc = exfat_cache_directory(ef, dir); + if (rc != 0) + exfat_put_node(ef, dir); + return rc; +} + +void exfat_closedir(struct exfat* ef, struct exfat_iterator* it) +{ + exfat_put_node(ef, it->parent); + it->parent = NULL; + it->current = NULL; +} + +struct exfat_node* exfat_readdir(struct exfat* ef, struct exfat_iterator* it) +{ + if (it->current == NULL) + it->current = it->parent->child; + else + it->current = it->current->next; + + if (it->current != NULL) + return exfat_get_node(it->current); + else + return NULL; +} + +static int compare_char(struct exfat* ef, uint16_t a, uint16_t b) +{ + if (a >= ef->upcase_chars || b >= ef->upcase_chars) + return (int) a - (int) b; + + return (int) le16_to_cpu(ef->upcase[a]) - (int) le16_to_cpu(ef->upcase[b]); +} + +static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b) +{ + while (le16_to_cpu(*a) && le16_to_cpu(*b)) + { + int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b)); + if (rc != 0) + return rc; + a++; + b++; + } + return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b)); +} + +static int lookup_name(struct exfat* ef, struct exfat_node* parent, + struct exfat_node** node, const char* name, size_t n) +{ + struct exfat_iterator it; + le16_t buffer[EXFAT_NAME_MAX + 1]; + int rc; + + *node = NULL; + + rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX, n); + if (rc != 0) + return rc; + + rc = exfat_opendir(ef, parent, &it); + if (rc != 0) + return rc; + while ((*node = exfat_readdir(ef, &it))) + { + if (compare_name(ef, buffer, (*node)->name) == 0) + { + exfat_closedir(ef, &it); + return 0; + } + exfat_put_node(ef, *node); + } + exfat_closedir(ef, &it); + return -ENOENT; +} + +static size_t get_comp(const char* path, const char** comp) +{ + const char* end; + + *comp = path + strspn(path, "/"); /* skip leading slashes */ + end = strchr(*comp, '/'); + if (end == NULL) + return strlen(*comp); + else + return end - *comp; +} + +int exfat_lookup(struct exfat* ef, struct exfat_node** node, + const char* path) +{ + struct exfat_node* parent; + const char* p; + size_t n; + int rc; + + /* start from the root directory */ + parent = *node = exfat_get_node(ef->root); + for (p = path; (n = get_comp(p, &p)); p += n) + { + if (n == 1 && *p == '.') /* skip "." component */ + continue; + rc = lookup_name(ef, parent, node, p, n); + if (rc != 0) + { + exfat_put_node(ef, parent); + return rc; + } + exfat_put_node(ef, parent); + parent = *node; + } + return 0; +} + +static bool is_last_comp(const char* comp, size_t length) +{ + const char* p = comp + length; + + return get_comp(p, &p) == 0; +} + +static bool is_allowed(const char* comp, size_t length) +{ + size_t i; + + for (i = 0; i < length; i++) + switch (comp[i]) + { + case 0x01 ... 0x1f: + case '/': + case '\\': + case ':': + case '*': + case '?': + case '"': + case '<': + case '>': + case '|': + return false; + } + return true; +} + +int exfat_split(struct exfat* ef, struct exfat_node** parent, + struct exfat_node** node, le16_t* name, const char* path) +{ + const char* p; + size_t n; + int rc; + + memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t)); + *parent = *node = exfat_get_node(ef->root); + for (p = path; (n = get_comp(p, &p)); p += n) + { + if (n == 1 && *p == '.') + continue; + if (is_last_comp(p, n)) + { + if (!is_allowed(p, n)) + { + /* contains characters that are not allowed */ + exfat_put_node(ef, *parent); + return -ENOENT; + } + rc = utf8_to_utf16(name, p, EXFAT_NAME_MAX, n); + if (rc != 0) + { + exfat_put_node(ef, *parent); + return rc; + } + + rc = lookup_name(ef, *parent, node, p, n); + if (rc != 0 && rc != -ENOENT) + { + exfat_put_node(ef, *parent); + return rc; + } + return 0; + } + rc = lookup_name(ef, *parent, node, p, n); + if (rc != 0) + { + exfat_put_node(ef, *parent); + return rc; + } + exfat_put_node(ef, *parent); + *parent = *node; + } + exfat_bug("impossible"); +} |