X-Git-Url: https://git.sven.stormbind.net/?p=sven%2Fexfat-utils.git;a=blobdiff_plain;f=libexfat%2Fnode.c;fp=libexfat%2Fnode.c;h=90002eb9a03f2b0d59a1a44eed3f339d55656a83;hp=fa04e257709eb8d5e949c74ab22d19c8738fd000;hb=0e295d91bb7f18afee1a0b0d587e5a0cf58bf2fb;hpb=62ddc92d38808bea3aba8fe70e3a417197e9d42f diff --git a/libexfat/node.c b/libexfat/node.c index fa04e25..90002eb 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -30,7 +30,6 @@ struct iterator { cluster_t cluster; off_t offset; - int contiguous; char* chunk; }; @@ -95,21 +94,35 @@ static off_t co2o(struct exfat* ef, cluster_t cluster, off_t offset) static int opendir(struct exfat* ef, const struct exfat_node* dir, struct iterator* it) { + char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1]; + if (!(dir->flags & EXFAT_ATTRIB_DIR)) - exfat_bug("not a directory"); + { + exfat_get_name(dir, buffer, sizeof(buffer) - 1); + exfat_bug("'%s' is not a directory", buffer); + } + if (CLUSTER_INVALID(dir->start_cluster)) + { + exfat_get_name(dir, buffer, sizeof(buffer) - 1); + exfat_error("'%s' directory starts with invalid cluster %#x", buffer, + dir->start_cluster); + return -EIO; + } it->cluster = dir->start_cluster; it->offset = 0; - it->contiguous = IS_CONTIGUOUS(*dir); it->chunk = malloc(CLUSTER_SIZE(*ef->sb)); if (it->chunk == NULL) { - exfat_error("out of memory"); + exfat_error("failed to allocate memory for directory cluster"); return -ENOMEM; } if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, it->cluster)) < 0) { - exfat_error("failed to read directory cluster %#x", it->cluster); + free(it->chunk); + exfat_get_name(dir, buffer, sizeof(buffer) - 1); + exfat_error("failed to read '%s' directory cluster %#x", buffer, + it->cluster); return -EIO; } return 0; @@ -119,7 +132,6 @@ static void closedir(struct iterator* it) { it->cluster = 0; it->offset = 0; - it->contiguous = 0; free(it->chunk); it->chunk = NULL; } @@ -194,9 +206,10 @@ 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 valid_size) + uint16_t reference_checksum, uint64_t valid_size, int cluster_size) { char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1]; + bool ret = true; /* Validate checksum first. If it's invalid all other fields probably @@ -207,7 +220,7 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum, exfat_get_name(node, buffer, sizeof(buffer) - 1); exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer, actual_checksum, reference_checksum); - return false; + ret = false; } /* @@ -221,10 +234,49 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum, exfat_get_name(node, buffer, sizeof(buffer) - 1); exfat_error("'%s' has valid size (%"PRIu64") greater than size " "(%"PRIu64")", buffer, valid_size, node->size); - return false; + ret = false; } - return true; + /* + Empty file must have zero start cluster. Non-empty file must start + with a valid cluster. Directories cannot be empty (i.e. must always + have a valid start cluster), but we will check this later in opendir() + to give user a chance to read current directory. + */ + if (node->size == 0 && node->start_cluster != EXFAT_CLUSTER_FREE) + { + exfat_get_name(node, buffer, sizeof(buffer) - 1); + exfat_error("'%s' is empty but start cluster is %#x", buffer, + node->start_cluster); + ret = false; + } + if (node->size > 0 && CLUSTER_INVALID(node->start_cluster)) + { + exfat_get_name(node, buffer, sizeof(buffer) - 1); + exfat_error("'%s' points to invalid cluster %#x", buffer, + node->start_cluster); + ret = false; + } + + /* Empty file or directory must be marked as non-contiguous. */ + if (node->size == 0 && (node->flags & EXFAT_ATTRIB_CONTIGUOUS)) + { + exfat_get_name(node, buffer, sizeof(buffer) - 1); + exfat_error("'%s' is empty but marked as contiguous (%#x)", buffer, + node->flags); + ret = false; + } + + /* Directory size must be aligned on at cluster boundary. */ + if ((node->flags & EXFAT_ATTRIB_DIR) && node->size % cluster_size != 0) + { + exfat_get_name(node, buffer, sizeof(buffer) - 1); + exfat_error("'%s' directory size %"PRIu64" is not divisible by %d", buffer, + node->size, cluster_size); + ret = false; + } + + return ret; } static void decompress_upcase(uint16_t* output, const le16_t* source, @@ -340,21 +392,6 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, init_node_meta2(*node, meta2); actual_checksum = exfat_add_checksum(entry, actual_checksum); 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)) - { - exfat_error("empty file marked as contiguous (0x%hhx)", - meta2->flags); - goto error; - } - /* directories must be aligned on at cluster boundary */ - if (((*node)->flags & EXFAT_ATTRIB_DIR) && - (*node)->size % CLUSTER_SIZE(*ef->sb) != 0) - { - exfat_error("directory has invalid size %"PRIu64" bytes", - (*node)->size); - goto error; - } --continuations; break; @@ -375,7 +412,7 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, if (--continuations == 0) { if (!check_node(*node, actual_checksum, reference_checksum, - valid_size)) + valid_size, CLUSTER_SIZE(*ef->sb))) goto error; if (!fetch_next_entry(ef, parent, it)) goto error;