X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=libexfat%2Fnode.c;h=a5491b1b9d13bc235036b9bf635e9cd0c0e16e1e;hb=e10d9d0b4176f7cfcff9c336309efd22de1ed6d4;hp=40208745050841640c53760717ee4daaf935c510;hpb=cb1f91eb9dd1e4a4d4bd930f7040498b199ec8f5;p=android-x86%2Fexternal-exfat.git diff --git a/libexfat/node.c b/libexfat/node.c index 4020874..a5491b1 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -3,7 +3,7 @@ exFAT file system implementation library. Free exFAT implementation. - Copyright (C) 2010-2013 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 @@ -30,7 +30,6 @@ struct iterator { cluster_t cluster; off_t offset; - int contiguous; char* chunk; }; @@ -50,14 +49,14 @@ void exfat_put_node(struct exfat* ef, struct exfat_node* node) if (node->references < 0) { exfat_get_name(node, buffer, sizeof(buffer) - 1); - exfat_bug("reference counter of `%s' is below zero", buffer); + exfat_bug("reference counter of '%s' is below zero", buffer); } else if (node->references == 0 && node != ef->root) { if (node->flags & EXFAT_ATTRIB_DIRTY) { exfat_get_name(node, buffer, sizeof(buffer) - 1); - exfat_warn("dirty node `%s' with zero references", buffer); + exfat_warn("dirty node '%s' with zero references", buffer); } } } @@ -99,7 +98,6 @@ static int opendir(struct exfat* ef, const struct exfat_node* dir, exfat_bug("not a directory"); it->cluster = dir->start_cluster; it->offset = 0; - it->contiguous = IS_CONTIGUOUS(*dir); it->chunk = malloc(CLUSTER_SIZE(*ef->sb)); if (it->chunk == NULL) { @@ -109,6 +107,7 @@ static int opendir(struct exfat* ef, const struct exfat_node* dir, if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, it->cluster)) < 0) { + free(it->chunk); exfat_error("failed to read directory cluster %#x", it->cluster); return -EIO; } @@ -119,12 +118,11 @@ static void closedir(struct iterator* it) { it->cluster = 0; it->offset = 0; - it->contiguous = 0; free(it->chunk); it->chunk = NULL; } -static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent, +static bool fetch_next_entry(struct exfat* ef, const struct exfat_node* parent, struct iterator* it) { /* move iterator to the next entry in the directory */ @@ -135,23 +133,23 @@ static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent, /* reached the end of directory; the caller should check this condition too */ if (it->offset >= parent->size) - return 0; + return true; it->cluster = exfat_next_cluster(ef, parent, it->cluster); if (CLUSTER_INVALID(it->cluster)) { exfat_error("invalid cluster 0x%x while reading directory", it->cluster); - return 1; + return false; } if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, it->cluster)) < 0) { exfat_error("failed to read the next directory cluster %#x", it->cluster); - return 1; + return false; } } - return 0; + return true; } static struct exfat_node* allocate_node(void) @@ -194,7 +192,7 @@ static const struct exfat_entry* get_entry_ptr(const struct exfat* ef, } static bool check_node(const struct exfat_node* node, uint16_t actual_checksum, - uint16_t reference_checksum, uint64_t real_size) + uint16_t reference_checksum, uint64_t valid_size) { char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1]; @@ -205,26 +203,48 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum, if (actual_checksum != reference_checksum) { exfat_get_name(node, buffer, sizeof(buffer) - 1); - exfat_error("`%s' has invalid checksum (%#hx != %#hx)", buffer, + exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer, actual_checksum, reference_checksum); return false; } /* - It's unclear what is real_size field needed for. It usually equals to - the size but may contain any value less than size (including 0). + exFAT does not support sparse files but allows files with uninitialized + clusters. For such files valid_size means initialized data size and + cannot be greater than file size. See SetFileValidData() function + description in MSDN. */ - if (real_size > node->size) + if (valid_size > node->size) { exfat_get_name(node, buffer, sizeof(buffer) - 1); - exfat_error("`%s' has real size (%"PRIu64") greater than size " - "(%"PRIu64")", buffer, real_size, node->size); + exfat_error("'%s' has valid size (%"PRIu64") greater than size " + "(%"PRIu64")", buffer, valid_size, node->size); return false; } 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. @@ -244,7 +264,9 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, le16_t* namep = NULL; uint16_t reference_checksum = 0; uint16_t actual_checksum = 0; - uint64_t real_size = 0; + uint64_t valid_size = 0; + uint64_t upcase_size = 0; + le16_t* upcase_comp = NULL; *node = NULL; @@ -315,7 +337,7 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, } init_node_meta2(*node, meta2); actual_checksum = exfat_add_checksum(entry, actual_checksum); - real_size = le64_to_cpu(meta2->real_size); + valid_size = le64_to_cpu(meta2->valid_size); /* empty files must be marked as non-contiguous */ if ((*node)->size == 0 && (meta2->flags & EXFAT_FLAG_CONTIGUOUS)) { @@ -351,9 +373,9 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, if (--continuations == 0) { if (!check_node(*node, actual_checksum, reference_checksum, - real_size)) + valid_size)) goto error; - if (fetch_next_entry(ef, parent, it) != 0) + if (!fetch_next_entry(ef, parent, it)) goto error; return 0; /* entry completed */ } @@ -369,33 +391,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: @@ -452,15 +489,25 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, break; default: - if (entry->type & EXFAT_ENTRY_VALID) + if (!(entry->type & EXFAT_ENTRY_VALID)) + break; /* deleted entry, ignore it */ + if (!(entry->type & EXFAT_ENTRY_OPTIONAL)) { - exfat_error("unknown entry type 0x%hhx", entry->type); + exfat_error("unknown entry type %#hhx", entry->type); + goto error; + } + /* optional entry, warn and skip */ + exfat_warn("unknown entry type %#hhx", entry->type); + if (continuations == 0) + { + exfat_error("unexpected continuation"); goto error; } + --continuations; break; } - if (fetch_next_entry(ef, parent, it) != 0) + if (!fetch_next_entry(ef, parent, it)) goto error; } /* we never reach here */ @@ -554,13 +601,13 @@ static void reset_cache(struct exfat* ef, struct exfat_node* node) if (node->references != 0) { exfat_get_name(node, buffer, sizeof(buffer) - 1); - exfat_warn("non-zero reference counter (%d) for `%s'", + exfat_warn("non-zero reference counter (%d) for '%s'", node->references, buffer); } if (node != ef->root && (node->flags & EXFAT_ATTRIB_DIRTY)) { exfat_get_name(node, buffer, sizeof(buffer) - 1); - exfat_bug("node `%s' is dirty", buffer); + exfat_bug("node '%s' is dirty", buffer); } while (node->references) exfat_put_node(ef, node); @@ -630,7 +677,7 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node) } if (meta2.type != EXFAT_ENTRY_FILE_INFO) exfat_bug("invalid type of meta2: 0x%hhx", meta2.type); - meta2.size = meta2.real_size = cpu_to_le64(node->size); + meta2.size = meta2.valid_size = cpu_to_le64(node->size); meta2.start_cluster = cpu_to_le32(node->start_cluster); meta2.flags = EXFAT_FLAG_ALWAYS1; /* empty files must not be marked as contiguous */ @@ -652,7 +699,7 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node) } node->flags &= ~EXFAT_ATTRIB_DIRTY; - return 0; + return exfat_flush(ef); } static bool erase_entry(struct exfat* ef, struct exfat_node* node) @@ -830,7 +877,7 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir, return rc; } } - if (fetch_next_entry(ef, dir, &it) != 0) + if (!fetch_next_entry(ef, dir, &it)) { closedir(&it); return -EIO; @@ -961,7 +1008,7 @@ int exfat_mkdir(struct exfat* ef, const char* path) int rc; struct exfat_node* node; - rc = create(ef, path, EXFAT_ATTRIB_ARCH | EXFAT_ATTRIB_DIR); + rc = create(ef, path, EXFAT_ATTRIB_DIR); if (rc != 0) return rc; rc = exfat_lookup(ef, &node, path); @@ -1118,6 +1165,16 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path) exfat_put_node(ef, existing); if (rc != 0) { + /* free clusters even if something went wrong; overwise they + will be just lost */ + exfat_cleanup_node(ef, existing); + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return rc; + } + rc = exfat_cleanup_node(ef, existing); + if (rc != 0) + { exfat_put_node(ef, dir); exfat_put_node(ef, node); return rc; @@ -1190,7 +1247,7 @@ static int find_label(struct exfat* ef, cluster_t* cluster, off_t* offset) return 0; } - if (fetch_next_entry(ef, ef->root, &it) != 0) + if (!fetch_next_entry(ef, ef->root, &it)) { closedir(&it); return -EIO;