X-Git-Url: https://git.sven.stormbind.net/?p=sven%2Fexfat-utils.git;a=blobdiff_plain;f=libexfat%2Fnode.c;h=fa04e257709eb8d5e949c74ab22d19c8738fd000;hp=4dd4dc6a4c6085a8a8f3622609951451aa7b2d87;hb=62ddc92d38808bea3aba8fe70e3a417197e9d42f;hpb=fc55662109c1fccea581594f8d6af49da79f456b diff --git a/libexfat/node.c b/libexfat/node.c index 4dd4dc6..fa04e25 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -3,7 +3,7 @@ exFAT file system implementation library. Free exFAT implementation. - Copyright (C) 2010-2015 Andrew Nayenko + Copyright (C) 2010-2016 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 @@ -227,6 +227,26 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum, return true; } +static void decompress_upcase(uint16_t* output, const le16_t* source, + size_t size) +{ + size_t si; + size_t oi; + + for (oi = 0; oi < EXFAT_UPCASE_CHARS; oi++) + output[oi] = oi; + + for (si = 0, oi = 0; si < size && oi < EXFAT_UPCASE_CHARS; si++) + { + uint16_t ch = le16_to_cpu(source[si]); + + if (ch == 0xffff && si + 1 < size) /* indicates a run */ + oi += le16_to_cpu(source[++si]); + else + output[oi++] = ch; + } +} + /* * Reads one entry in directory at position pointed by iterator and fills * node structure. @@ -247,6 +267,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, uint16_t reference_checksum = 0; uint16_t actual_checksum = 0; uint64_t valid_size = 0; + uint64_t upcase_size = 0; + le16_t* upcase_comp = NULL; *node = NULL; @@ -371,33 +393,48 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, le32_to_cpu(upcase->start_cluster)); goto error; } - if (le64_to_cpu(upcase->size) == 0 || - le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) || - le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0) + upcase_size = le64_to_cpu(upcase->size); + if (upcase_size == 0 || + upcase_size > EXFAT_UPCASE_CHARS * sizeof(uint16_t) || + upcase_size % sizeof(uint16_t) != 0) { exfat_error("bad upcase table size (%"PRIu64" bytes)", - le64_to_cpu(upcase->size)); + upcase_size); goto error; } - ef->upcase = malloc(le64_to_cpu(upcase->size)); - if (ef->upcase == NULL) + upcase_comp = malloc(upcase_size); + if (upcase_comp == NULL) { exfat_error("failed to allocate upcase table (%"PRIu64" bytes)", - le64_to_cpu(upcase->size)); + upcase_size); rc = -ENOMEM; goto error; } - ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t); - if (exfat_pread(ef->dev, ef->upcase, le64_to_cpu(upcase->size), + /* read compressed upcase table */ + if (exfat_pread(ef->dev, upcase_comp, upcase_size, exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0) { + free(upcase_comp); exfat_error("failed to read upper case table " "(%"PRIu64" bytes starting at cluster %#x)", - le64_to_cpu(upcase->size), + upcase_size, le32_to_cpu(upcase->start_cluster)); goto error; } + + /* decompress upcase table */ + ef->upcase = calloc(EXFAT_UPCASE_CHARS, sizeof(uint16_t)); + if (ef->upcase == NULL) + { + free(upcase_comp); + exfat_error("failed to allocate decompressed upcase table"); + rc = -ENOMEM; + goto error; + } + decompress_upcase(ef->upcase, upcase_comp, + upcase_size / sizeof(uint16_t)); + free(upcase_comp); break; case EXFAT_ENTRY_BITMAP: