X-Git-Url: https://git.sven.stormbind.net/?p=sven%2Fexfat-utils.git;a=blobdiff_plain;f=libexfat%2Fcluster.c;h=15cd9aa339da22d8616be9d333c53021cf1ea219;hp=0418932ec802f66590fcf29139a2d075aa64e335;hb=HEAD;hpb=4c15e072cf41b39ffa0c4354c64856a9c8414edf diff --git a/libexfat/cluster.c b/libexfat/cluster.c index 0418932..15cd9aa 100644 --- a/libexfat/cluster.c +++ b/libexfat/cluster.c @@ -2,11 +2,12 @@ cluster.c (03.09.09) exFAT file system implementation library. - Copyright (C) 2010-2012 Andrew Nayenko + Free exFAT implementation. + Copyright (C) 2010-2018 Andrew Nayenko - This program is free software: you can redistribute it and/or modify + 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 - the Free Software Foundation, either version 3 of the License, or + the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -14,13 +15,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "exfat.h" #include #include +#include /* * Sector to absolute offset. @@ -64,7 +67,7 @@ static cluster_t s2c(const struct exfat* ef, off_t sector) static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes) { uint64_t cluster_size = CLUSTER_SIZE(*ef->sb); - return (bytes + cluster_size - 1) / cluster_size; + return DIV_ROUND_UP(bytes, cluster_size); } cluster_t exfat_next_cluster(const struct exfat* ef, @@ -76,11 +79,13 @@ cluster_t exfat_next_cluster(const struct exfat* ef, if (cluster < EXFAT_FIRST_DATA_CLUSTER) exfat_bug("bad cluster 0x%x", cluster); - if (IS_CONTIGUOUS(*node)) + if (node->is_contiguous) return cluster + 1; fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start)) + cluster * sizeof(cluster_t); - exfat_pread(ef->dev, &next, sizeof(next), fat_offset); + if (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0) + return EXFAT_CLUSTER_BAD; /* the caller should handle this and print + appropriate error message */ return le32_to_cpu(next); } @@ -98,70 +103,92 @@ cluster_t exfat_advance_cluster(const struct exfat* ef, for (i = node->fptr_index; i < count; i++) { node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster); - if (CLUSTER_INVALID(node->fptr_cluster)) - break; + if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster)) + break; /* the caller should handle this and print appropriate + error message */ } node->fptr_index = count; return node->fptr_cluster; } -static cluster_t find_bit_and_set(uint8_t* bitmap, cluster_t start, - cluster_t end) +static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end) { - const cluster_t mid_start = (start + 7) / 8 * 8; - const cluster_t mid_end = end / 8 * 8; - cluster_t c; - cluster_t byte; - - for (c = start; c < mid_start; c++) - if (BMAP_GET(bitmap, c) == 0) - { - BMAP_SET(bitmap, c); - return c + EXFAT_FIRST_DATA_CLUSTER; - } + const size_t start_index = start / sizeof(bitmap_t) / 8; + const size_t end_index = DIV_ROUND_UP(end, sizeof(bitmap_t) * 8); + size_t i; + size_t start_bitindex; + size_t end_bitindex; + size_t c; + + for (i = start_index; i < end_index; i++) + { + if (bitmap[i] == ~((bitmap_t) 0)) + continue; + start_bitindex = MAX(i * sizeof(bitmap_t) * 8, start); + end_bitindex = MIN((i + 1) * sizeof(bitmap_t) * 8, end); + for (c = start_bitindex; c < end_bitindex; c++) + if (BMAP_GET(bitmap, c) == 0) + { + BMAP_SET(bitmap, c); + return c + EXFAT_FIRST_DATA_CLUSTER; + } + } + return EXFAT_CLUSTER_END; +} - for (byte = mid_start / 8; byte < mid_end / 8; byte++) - if (bitmap[byte] != 0xff) - { - cluster_t bit; - - for (bit = 0; bit < 8; bit++) - if (!(bitmap[byte] & (1u << bit))) - { - bitmap[byte] |= (1u << bit); - return byte * 8 + bit + EXFAT_FIRST_DATA_CLUSTER; - } - } +static int flush_nodes(struct exfat* ef, struct exfat_node* node) +{ + struct exfat_node* p; - for (c = mid_end; c < end; c++) - if (BMAP_GET(bitmap, c) == 0) - { - BMAP_SET(bitmap, c); - return c + EXFAT_FIRST_DATA_CLUSTER; - } + for (p = node->child; p != NULL; p = p->next) + { + int rc = flush_nodes(ef, p); + if (rc != 0) + return rc; + } + return exfat_flush_node(ef, node); +} - return EXFAT_CLUSTER_END; +int exfat_flush_nodes(struct exfat* ef) +{ + return flush_nodes(ef, ef->root); } -void exfat_flush_cmap(struct exfat* ef) +int exfat_flush(struct exfat* ef) { - exfat_pwrite(ef->dev, ef->cmap.chunk, (ef->cmap.chunk_size + 7) / 8, - exfat_c2o(ef, ef->cmap.start_cluster)); - ef->cmap.dirty = 0; + if (ef->cmap.dirty) + { + if (exfat_pwrite(ef->dev, ef->cmap.chunk, + BMAP_SIZE(ef->cmap.chunk_size), + exfat_c2o(ef, ef->cmap.start_cluster)) < 0) + { + exfat_error("failed to write clusters bitmap"); + return -EIO; + } + ef->cmap.dirty = false; + } + + return 0; } -static void set_next_cluster(const struct exfat* ef, int contiguous, +static bool set_next_cluster(const struct exfat* ef, bool contiguous, cluster_t current, cluster_t next) { off_t fat_offset; le32_t next_le32; if (contiguous) - return; + return true; fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start)) + current * sizeof(cluster_t); next_le32 = cpu_to_le32(next); - exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset); + if (exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset) < 0) + { + exfat_error("failed to write the next cluster %#x after %#x", next, + current); + return false; + } + return true; } static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint) @@ -181,29 +208,29 @@ static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint) return EXFAT_CLUSTER_END; } - ef->cmap.dirty = 1; + ef->cmap.dirty = true; return cluster; } static void free_cluster(struct exfat* ef, cluster_t cluster) { - if (CLUSTER_INVALID(cluster)) - exfat_bug("attempting to free invalid cluster"); - if (cluster < EXFAT_FIRST_DATA_CLUSTER || - cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size) - exfat_bug("bad cluster 0x%x (0x%x)", cluster, ef->cmap.size); + if (cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size) + exfat_bug("caller must check cluster validity (%#x, %#x)", cluster, + ef->cmap.size); BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER); - ef->cmap.dirty = 1; + ef->cmap.dirty = true; } -static void make_noncontiguous(const struct exfat* ef, cluster_t first, +static bool make_noncontiguous(const struct exfat* ef, cluster_t first, cluster_t last) { cluster_t c; for (c = first; c < last; c++) - set_next_cluster(ef, 0, c, c + 1); + if (!set_next_cluster(ef, false, c, c + 1)) + return false; + return true; } static int shrink_file(struct exfat* ef, struct exfat_node* node, @@ -223,9 +250,9 @@ static int grow_file(struct exfat* ef, struct exfat_node* node, { /* get the last cluster of the file */ previous = exfat_advance_cluster(ef, node, current - 1); - if (CLUSTER_INVALID(previous)) + if (CLUSTER_INVALID(*ef->sb, previous)) { - exfat_error("invalid cluster in file"); + exfat_error("invalid cluster 0x%x while growing", previous); return -EIO; } } @@ -236,37 +263,41 @@ static int grow_file(struct exfat* ef, struct exfat_node* node, /* file does not have clusters (i.e. is empty), allocate the first one for it */ previous = allocate_cluster(ef, 0); - if (CLUSTER_INVALID(previous)) + if (CLUSTER_INVALID(*ef->sb, previous)) return -ENOSPC; node->fptr_cluster = node->start_cluster = previous; allocated = 1; /* file consists of only one cluster, so it's contiguous */ - node->flags |= EXFAT_ATTRIB_CONTIGUOUS; + node->is_contiguous = true; } while (allocated < difference) { next = allocate_cluster(ef, previous + 1); - if (CLUSTER_INVALID(next)) + if (CLUSTER_INVALID(*ef->sb, next)) { if (allocated != 0) shrink_file(ef, node, current + allocated, allocated); return -ENOSPC; } - if (next != previous - 1 && IS_CONTIGUOUS(*node)) + if (next != previous - 1 && node->is_contiguous) { /* it's a pity, but we are not able to keep the file contiguous anymore */ - make_noncontiguous(ef, node->start_cluster, previous); - node->flags &= ~EXFAT_ATTRIB_CONTIGUOUS; - node->flags |= EXFAT_ATTRIB_DIRTY; + if (!make_noncontiguous(ef, node->start_cluster, previous)) + return -EIO; + node->is_contiguous = false; + node->is_dirty = true; } - set_next_cluster(ef, IS_CONTIGUOUS(*node), previous, next); + if (!set_next_cluster(ef, node->is_contiguous, previous, next)) + return -EIO; previous = next; allocated++; } - set_next_cluster(ef, IS_CONTIGUOUS(*node), previous, EXFAT_CLUSTER_END); + if (!set_next_cluster(ef, node->is_contiguous, previous, + EXFAT_CLUSTER_END)) + return -EIO; return 0; } @@ -288,18 +319,21 @@ static int shrink_file(struct exfat* ef, struct exfat_node* node, { cluster_t last = exfat_advance_cluster(ef, node, current - difference - 1); - if (CLUSTER_INVALID(last)) + if (CLUSTER_INVALID(*ef->sb, last)) { - exfat_error("invalid cluster in file"); + exfat_error("invalid cluster 0x%x while shrinking", last); return -EIO; } previous = exfat_next_cluster(ef, node, last); - set_next_cluster(ef, IS_CONTIGUOUS(*node), last, EXFAT_CLUSTER_END); + if (!set_next_cluster(ef, node->is_contiguous, last, + EXFAT_CLUSTER_END)) + return -EIO; } else { previous = node->start_cluster; node->start_cluster = EXFAT_CLUSTER_FREE; + node->is_dirty = true; } node->fptr_index = 0; node->fptr_cluster = node->start_cluster; @@ -307,23 +341,31 @@ static int shrink_file(struct exfat* ef, struct exfat_node* node, /* free remaining clusters */ while (difference--) { - if (CLUSTER_INVALID(previous)) + if (CLUSTER_INVALID(*ef->sb, previous)) { - exfat_error("invalid cluster in file"); + exfat_error("invalid cluster 0x%x while freeing after shrink", + previous); return -EIO; } + next = exfat_next_cluster(ef, node, previous); - set_next_cluster(ef, IS_CONTIGUOUS(*node), previous, - EXFAT_CLUSTER_FREE); + if (!set_next_cluster(ef, node->is_contiguous, previous, + EXFAT_CLUSTER_FREE)) + return -EIO; free_cluster(ef, previous); previous = next; } return 0; } -static void erase_raw(struct exfat* ef, size_t size, off_t offset) +static bool erase_raw(struct exfat* ef, size_t size, off_t offset) { - exfat_pwrite(ef->dev, ef->zero_cluster, size, offset); + if (exfat_pwrite(ef->dev, ef->zero_cluster, size, offset) < 0) + { + exfat_error("failed to erase %zu bytes at %"PRId64, size, offset); + return false; + } + return true; } static int erase_range(struct exfat* ef, struct exfat_node* node, @@ -338,28 +380,31 @@ static int erase_range(struct exfat* ef, struct exfat_node* node, cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1; cluster = exfat_advance_cluster(ef, node, begin / CLUSTER_SIZE(*ef->sb)); - if (CLUSTER_INVALID(cluster)) + if (CLUSTER_INVALID(*ef->sb, cluster)) { - exfat_error("invalid cluster in file"); + exfat_error("invalid cluster 0x%x while erasing", cluster); return -EIO; } /* erase from the beginning to the closest cluster boundary */ - erase_raw(ef, MIN(cluster_boundary, end) - begin, - exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb)); + if (!erase_raw(ef, MIN(cluster_boundary, end) - begin, + exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb))) + return -EIO; /* erase whole clusters */ while (cluster_boundary < end) { cluster = exfat_next_cluster(ef, node, cluster); /* the cluster cannot be invalid because we have just allocated it */ - if (CLUSTER_INVALID(cluster)) - exfat_bug("invalid cluster in file"); - erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster)); + if (CLUSTER_INVALID(*ef->sb, cluster)) + exfat_bug("invalid cluster 0x%x after allocation", cluster); + if (!erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster))) + return -EIO; cluster_boundary += CLUSTER_SIZE(*ef->sb); } return 0; } -int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size) +int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size, + bool erase) { uint32_t c1 = bytes2clusters(ef, node->size); uint32_t c2 = bytes2clusters(ef, size); @@ -379,13 +424,16 @@ int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size) if (rc != 0) return rc; - rc = erase_range(ef, node, node->size, size); - if (rc != 0) - return rc; + if (erase) + { + rc = erase_range(ef, node, node->size, size); + if (rc != 0) + return rc; + } exfat_update_mtime(node); node->size = size; - node->flags |= EXFAT_ATTRIB_DIRTY; + node->is_dirty = true; return 0; }