X-Git-Url: http://git.sven.stormbind.net/?p=sven%2Fexfat-utils.git;a=blobdiff_plain;f=libexfat%2Fnode.c;h=cce1de7c0c93eeed5b96887e3f848bf5e369be4a;hp=4cbb3e2d6d06f976d9e1097726662cb8a3e1056e;hb=refs%2Ftags%2Fupstream%2F1.0.0;hpb=70a4b10edcf53a90140e6dd80ccaa045f3647ad7 diff --git a/libexfat/node.c b/libexfat/node.c index 4cbb3e2..cce1de7 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -2,7 +2,7 @@ node.c (09.10.09) exFAT file system implementation library. - Copyright (C) 2010-2012 Andrew Nayenko + Copyright (C) 2010-2013 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 @@ -115,7 +115,8 @@ static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent, it->cluster = exfat_next_cluster(ef, parent, it->cluster); if (CLUSTER_INVALID(it->cluster)) { - exfat_error("invalid cluster while reading directory"); + exfat_error("invalid cluster 0x%x while reading directory", + it->cluster); return 1; } exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb), @@ -182,6 +183,7 @@ 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; *node = NULL; @@ -246,18 +248,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); - /* There are two fields that contain file size. Maybe they plan - to add compression support in the future and one of those - fields is visible (uncompressed) size and the other is real - (compressed) size. Anyway, currently it looks like exFAT does - not support compression and both fields must be equal. */ - if (le64_to_cpu(meta2->real_size) != (*node)->size) - { - exfat_error("real size does not equal to size " - "(%"PRIu64" != %"PRIu64")", - le64_to_cpu(meta2->real_size), (*node)->size); - goto error; - } + real_size = le64_to_cpu(meta2->real_size); /* empty files must be marked as non-contiguous */ if ((*node)->size == 0 && (meta2->flags & EXFAT_FLAG_CONTIGUOUS)) { @@ -269,11 +260,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, if (((*node)->flags & EXFAT_ATTRIB_DIR) && (*node)->size % CLUSTER_SIZE(*ef->sb) != 0) { - char buffer[EXFAT_NAME_MAX + 1]; - - exfat_get_name(*node, buffer, EXFAT_NAME_MAX); - exfat_error("directory `%s' has invalid size %"PRIu64" bytes", - buffer, (*node)->size); + exfat_error("directory has invalid size %"PRIu64" bytes", + (*node)->size); goto error; } --continuations; @@ -292,10 +280,34 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, namep += EXFAT_ENAME_MAX; if (--continuations == 0) { + /* + There are two fields that contain file size. Maybe they + plan to add compression support in the future and one of + those fields is visible (uncompressed) size and the other + is real (compressed) size. Anyway, currently it looks like + exFAT does not support compression and both fields must be + equal. + + There is an exception though: pagefile.sys (its real_size + is always 0). + */ + if (real_size != (*node)->size) + { + char buffer[EXFAT_NAME_MAX + 1]; + + exfat_get_name(*node, buffer, EXFAT_NAME_MAX); + exfat_error("`%s' real size does not equal to size " + "(%"PRIu64" != %"PRIu64")", buffer, + real_size, (*node)->size); + goto error; + } if (actual_checksum != reference_checksum) { - exfat_error("invalid checksum (0x%hx != 0x%hx)", - actual_checksum, reference_checksum); + char buffer[EXFAT_NAME_MAX + 1]; + + exfat_get_name(*node, buffer, EXFAT_NAME_MAX); + exfat_error("`%s' has invalid checksum (0x%hx != 0x%hx)", + buffer, actual_checksum, reference_checksum); goto error; } if (fetch_next_entry(ef, parent, it) != 0) @@ -310,7 +322,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, upcase = (const struct exfat_entry_upcase*) entry; if (CLUSTER_INVALID(le32_to_cpu(upcase->start_cluster))) { - exfat_error("invalid cluster in upcase table"); + exfat_error("invalid cluster 0x%x in upcase table", + le32_to_cpu(upcase->start_cluster)); goto error; } if (le64_to_cpu(upcase->size) == 0 || @@ -337,9 +350,11 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, case EXFAT_ENTRY_BITMAP: bitmap = (const struct exfat_entry_bitmap*) entry; - if (CLUSTER_INVALID(le32_to_cpu(bitmap->start_cluster))) + ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster); + if (CLUSTER_INVALID(ef->cmap.start_cluster)) { - exfat_error("invalid cluster in clusters bitmap"); + exfat_error("invalid cluster 0x%x in clusters bitmap", + ef->cmap.start_cluster); goto error; } ef->cmap.size = le32_to_cpu(ef->sb->cluster_count) - @@ -351,7 +366,6 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, le64_to_cpu(bitmap->size), (ef->cmap.size + 7) / 8); goto error; } - ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster); /* FIXME bitmap can be rather big, up to 512 MB */ ef->cmap.chunk_size = ef->cmap.size; ef->cmap.chunk = malloc(le64_to_cpu(bitmap->size)); @@ -443,17 +457,40 @@ int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir) return 0; } -static void reset_cache(struct exfat* ef, struct exfat_node* node) +static void tree_attach(struct exfat_node* dir, struct exfat_node* node) +{ + node->parent = dir; + if (dir->child) + { + dir->child->prev = node; + node->next = dir->child; + } + dir->child = node; +} + +static void tree_detach(struct exfat_node* node) { - struct exfat_node* child; - struct exfat_node* next; + if (node->prev) + node->prev->next = node->next; + else /* this is the first node in the list */ + node->parent->child = node->next; + if (node->next) + node->next->prev = node->prev; + node->parent = NULL; + node->prev = NULL; + node->next = NULL; +} - for (child = node->child; child; child = next) +static void reset_cache(struct exfat* ef, struct exfat_node* node) +{ + while (node->child) { - reset_cache(ef, child); - next = child->next; - free(child); + struct exfat_node* p = node->child; + reset_cache(ef, p); + tree_detach(p); + free(p); } + node->flags &= ~EXFAT_ATTRIB_CACHED; if (node->references != 0) { char buffer[EXFAT_NAME_MAX + 1]; @@ -461,10 +498,8 @@ static void reset_cache(struct exfat* ef, struct exfat_node* node) exfat_warn("non-zero reference counter (%d) for `%s'", node->references, buffer); } - while (node->references--) + while (node->references) exfat_put_node(ef, node); - node->child = NULL; - node->flags &= ~EXFAT_ATTRIB_CACHED; } void exfat_reset_cache(struct exfat* ef) @@ -549,30 +584,6 @@ static void erase_entry(struct exfat* ef, struct exfat_node* node) } } -static void tree_detach(struct exfat_node* node) -{ - if (node->prev) - node->prev->next = node->next; - else /* this is the first node in the list */ - node->parent->child = node->next; - if (node->next) - node->next->prev = node->prev; - node->parent = NULL; - node->prev = NULL; - node->next = NULL; -} - -static void tree_attach(struct exfat_node* dir, struct exfat_node* node) -{ - node->parent = dir; - if (dir->child) - { - dir->child->prev = node; - node->next = dir->child; - } - dir->child = node; -} - static int shrink_directory(struct exfat* ef, struct exfat_node* dir, off_t deleted_offset) { @@ -896,27 +907,33 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path) } if (existing != NULL) { - if (existing->flags & EXFAT_ATTRIB_DIR) + /* remove target if it's not the same node as source */ + if (existing != node) { - if (node->flags & EXFAT_ATTRIB_DIR) - rc = exfat_rmdir(ef, existing); + if (existing->flags & EXFAT_ATTRIB_DIR) + { + if (node->flags & EXFAT_ATTRIB_DIR) + rc = exfat_rmdir(ef, existing); + else + rc = -ENOTDIR; + } else - rc = -ENOTDIR; + { + if (!(node->flags & EXFAT_ATTRIB_DIR)) + rc = exfat_unlink(ef, existing); + else + rc = -EISDIR; + } + exfat_put_node(ef, existing); + if (rc != 0) + { + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return rc; + } } else - { - if (!(node->flags & EXFAT_ATTRIB_DIR)) - rc = exfat_unlink(ef, existing); - else - rc = -EISDIR; - } - exfat_put_node(ef, existing); - if (rc != 0) - { - exfat_put_node(ef, dir); - exfat_put_node(ef, node); - return rc; - } + exfat_put_node(ef, existing); } rc = find_slot(ef, dir, &cluster, &offset,