From 3a0fcb2bd280dbe1ede286b46f13b806c3810cc1 Mon Sep 17 00:00:00 2001 From: relan Date: Tue, 22 Feb 2022 20:41:29 +0300 Subject: [PATCH 1/3] Rename newsize to uoffset. Because that's what it actually is: the unsigned version of offset. --- libexfat/io.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/libexfat/io.c b/libexfat/io.c index 0fc35d28..91b5bec9 100644 --- a/libexfat/io.c +++ b/libexfat/io.c @@ -390,27 +390,27 @@ ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, void* buffer, size_t size, off_t offset) { - uint64_t newsize = offset; + uint64_t uoffset = offset; cluster_t cluster; char* bufp = buffer; off_t lsize, loffset, remainder; if (offset < 0) return -EINVAL; - if (newsize >= node->size) + if (uoffset >= node->size) return 0; if (size == 0) return 0; - cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb)); + cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while reading", cluster); return -EIO; } - loffset = newsize % CLUSTER_SIZE(*ef->sb); - remainder = MIN(size, node->size - newsize); + loffset = uoffset % CLUSTER_SIZE(*ef->sb); + remainder = MIN(size, node->size - uoffset); while (remainder > 0) { if (CLUSTER_INVALID(*ef->sb, cluster)) @@ -432,13 +432,13 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, } if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime) exfat_update_atime(node); - return MIN(size, node->size - newsize) - remainder; + return MIN(size, node->size - uoffset) - remainder; } ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, const void* buffer, size_t size, off_t offset) { - uint64_t newsize = offset; + uint64_t uoffset = offset; int rc; cluster_t cluster; const char* bufp = buffer; @@ -446,29 +446,29 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, if (offset < 0) return -EINVAL; - if (newsize > node->size) + if (uoffset > node->size) { - rc = exfat_truncate(ef, node, newsize, true); + rc = exfat_truncate(ef, node, uoffset, true); if (rc != 0) return rc; } - if (newsize + size > node->size) + if (uoffset + size > node->size) { - rc = exfat_truncate(ef, node, newsize + size, false); + rc = exfat_truncate(ef, node, uoffset + size, false); if (rc != 0) return rc; } if (size == 0) return 0; - cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb)); + cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while writing", cluster); return -EIO; } - loffset = newsize % CLUSTER_SIZE(*ef->sb); + loffset = uoffset % CLUSTER_SIZE(*ef->sb); remainder = size; while (remainder > 0) { From 4e3bb7bdd4d2f3467ea20247c071abc1e53f343e Mon Sep 17 00:00:00 2001 From: relan Date: Sat, 19 Feb 2022 12:46:30 +0300 Subject: [PATCH 2/3] Maintain valid_size in exfat_node. --- libexfat/cluster.c | 1 + libexfat/exfat.h | 1 + libexfat/mount.c | 2 +- libexfat/node.c | 14 +++++++------- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libexfat/cluster.c b/libexfat/cluster.c index 0f2e91b0..ffd76d5c 100644 --- a/libexfat/cluster.c +++ b/libexfat/cluster.c @@ -432,6 +432,7 @@ int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size, } exfat_update_mtime(node); + node->valid_size = size; node->size = size; node->is_dirty = true; return 0; diff --git a/libexfat/exfat.h b/libexfat/exfat.h index 939fec06..01829cc2 100644 --- a/libexfat/exfat.h +++ b/libexfat/exfat.h @@ -89,6 +89,7 @@ struct exfat_node bool is_cached : 1; bool is_dirty : 1; bool is_unlinked : 1; + uint64_t valid_size; uint64_t size; time_t mtime, atime; le16_t name[EXFAT_NAME_MAX + 1]; diff --git a/libexfat/mount.c b/libexfat/mount.c index f0ca0e3d..67f228a2 100644 --- a/libexfat/mount.c +++ b/libexfat/mount.c @@ -305,7 +305,7 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster); ef->root->fptr_cluster = ef->root->start_cluster; ef->root->name[0] = cpu_to_le16('\0'); - ef->root->size = rootdir_size(ef); + ef->root->valid_size = ef->root->size = rootdir_size(ef); if (ef->root->size == 0) { exfat_free(ef); diff --git a/libexfat/node.c b/libexfat/node.c index c280b174..278541d6 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -144,6 +144,7 @@ static void init_node_meta1(struct exfat_node* node, static void init_node_meta2(struct exfat_node* node, const struct exfat_entry_meta2* meta2) { + node->valid_size = le64_to_cpu(meta2->valid_size); node->size = le64_to_cpu(meta2->size); node->start_cluster = le32_to_cpu(meta2->start_cluster); node->fptr_cluster = node->start_cluster; @@ -206,8 +207,7 @@ static bool check_entries(const struct exfat_entry* entry, int n) } static bool check_node(const struct exfat* ef, struct exfat_node* node, - le16_t actual_checksum, const struct exfat_entry_meta1* meta1, - const struct exfat_entry_meta2* meta2) + le16_t actual_checksum, const struct exfat_entry_meta1* meta1) { int cluster_size = CLUSTER_SIZE(*ef->sb); uint64_t clusters_heap_size = @@ -234,12 +234,11 @@ static bool check_node(const struct exfat* ef, struct exfat_node* node, cannot be greater than file size. See SetFileValidData() function description in MSDN. */ - if (le64_to_cpu(meta2->valid_size) > node->size) + if (node->valid_size > node->size) { exfat_get_name(node, buffer); exfat_error("'%s' has valid size (%"PRIu64") greater than size " - "(%"PRIu64")", buffer, le64_to_cpu(meta2->valid_size), - node->size); + "(%"PRIu64")", buffer, node->valid_size, node->size); ret = false; } @@ -328,7 +327,7 @@ static int parse_file_entries(struct exfat* ef, struct exfat_node* node, init_node_meta2(node, meta2); init_node_name(node, entries + 2, mandatory_entries - 2); - if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1, meta2)) + if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1)) return -EIO; return 0; @@ -659,7 +658,8 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node) NULL, &meta1->atime_tzo); meta1->adate = edate; meta1->atime = etime; - meta2->size = meta2->valid_size = cpu_to_le64(node->size); + meta2->valid_size = cpu_to_le64(node->valid_size); + meta2->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 */ From ac101a8f9a5120581b1ce25134393bcd276b1774 Mon Sep 17 00:00:00 2001 From: relan Date: Tue, 22 Feb 2022 20:37:53 +0300 Subject: [PATCH 3/3] Implement valid size handling. Handle valid size on truncate, read and write operations. --- libexfat/cluster.c | 8 ++++++-- libexfat/io.c | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libexfat/cluster.c b/libexfat/cluster.c index ffd76d5c..5c604f0f 100644 --- a/libexfat/cluster.c +++ b/libexfat/cluster.c @@ -426,13 +426,17 @@ int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size, if (erase) { - rc = erase_range(ef, node, node->size, size); + rc = erase_range(ef, node, node->valid_size, size); if (rc != 0) return rc; + node->valid_size = size; + } + else + { + node->valid_size = MIN(node->valid_size, size); } exfat_update_mtime(node); - node->valid_size = size; node->size = size; node->is_dirty = true; return 0; diff --git a/libexfat/io.c b/libexfat/io.c index 91b5bec9..2dd5b132 100644 --- a/libexfat/io.c +++ b/libexfat/io.c @@ -402,6 +402,23 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, if (size == 0) return 0; + if (uoffset + size > node->valid_size) + { + ssize_t bytes = 0; + + exfat_debug("Read %ld %zu from %lu %lu file", offset, size, node->valid_size, node->size); + if (uoffset < node->valid_size) + { + bytes = exfat_generic_pread(ef, node, buffer, + node->valid_size - uoffset, offset); + if (bytes < 0 || (size_t) bytes < node->valid_size - uoffset) + return bytes; + } + memset(buffer + bytes, 0, + MIN(size - bytes, node->size - node->valid_size)); + return MIN(size, node->size - uoffset); + } + cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); if (CLUSTER_INVALID(*ef->sb, cluster)) { @@ -487,6 +504,7 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, bufp += lsize; loffset = 0; remainder -= lsize; + node->valid_size = MAX(node->valid_size, uoffset + size - remainder); cluster = exfat_next_cluster(ef, node, cluster); } if (!(node->attrib & EXFAT_ATTRIB_DIR))