releasing package exfatprogs version 1.2.8-1
[sven/exfatprogs.git] / lib / exfat_dir.c
index 98e820f9404e1011a5f78fe9ff2ac153fcd1e3d4..3189f71e39687ecf6119b54b12f92dd4fb23a395 100644 (file)
@@ -27,6 +27,12 @@ static struct path_resolve_ctx path_resolve_ctx;
                        ##__VA_ARGS__);                 \
 })
 
+static inline struct buffer_desc *exfat_de_iter_get_buffer(
+               struct exfat_de_iter *iter, unsigned int block)
+{
+       return &iter->buffer_desc[block % iter->exfat->buffer_count];
+}
+
 static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
 {
        off_t device_offset;
@@ -34,10 +40,10 @@ static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
        struct buffer_desc *desc;
        unsigned int i;
 
-       desc = &iter->buffer_desc[block & 0x01];
+       desc = exfat_de_iter_get_buffer(iter, block);
 
        for (i = 0; i < iter->read_size / iter->write_size; i++) {
-               if (desc->dirty[i]) {
+               if (BITMAP_GET(desc->dirty, i)) {
                        device_offset = exfat_c2o(exfat, desc->p_clus) +
                                desc->offset;
                        if (exfat_write(exfat->blk_dev->dev_fd,
@@ -46,7 +52,7 @@ static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
                                        device_offset + i * iter->write_size)
                                        != (ssize_t)iter->write_size)
                                return -EIO;
-                       desc->dirty[i] = 0;
+                       BITMAP_CLEAR(desc->dirty, i);
                }
        }
        return 0;
@@ -169,7 +175,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
        off_t device_offset;
        ssize_t ret;
 
-       desc = &iter->buffer_desc[block & 0x01];
+       desc = exfat_de_iter_get_buffer(iter, block);
        if (block == 0) {
                desc->p_clus = iter->parent->first_clus;
                desc->offset = 0;
@@ -183,7 +189,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
                if (block > iter->parent->size / iter->read_size)
                        return EOF;
 
-               prev_desc = &iter->buffer_desc[(block-1) & 0x01];
+               prev_desc = exfat_de_iter_get_buffer(iter, block - 1);
                if (prev_desc->offset + 2 * iter->read_size <=
                                exfat->clus_size) {
                        desc->p_clus = prev_desc->p_clus;
@@ -225,7 +231,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
        iter->exfat = exfat;
        iter->parent = dir;
        iter->write_size = exfat->sect_size;
-       iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB;
+       iter->read_size = exfat_get_read_size(exfat);
        if (exfat->clus_size <= 32 * KB)
                iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2);
        else
@@ -257,6 +263,7 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
        off_t next_de_file_offset;
        ssize_t ret;
        unsigned int block;
+       struct buffer_desc *bd;
 
        next_de_file_offset = iter->de_file_offset +
                        ith * sizeof(struct exfat_dentry);
@@ -265,9 +272,6 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
        if (next_de_file_offset + sizeof(struct exfat_dentry) >
                iter->parent->size)
                return EOF;
-       /* the dentry must be in current, or next block which will be read */
-       if (block > iter->de_file_offset / iter->read_size + 1)
-               return -ERANGE;
 
        /* read next cluster if needed */
        if (next_de_file_offset >= iter->next_read_offset) {
@@ -280,8 +284,8 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
        if (ith + 1 > iter->max_skip_dentries)
                iter->max_skip_dentries = ith + 1;
 
-       *dentry = (struct exfat_dentry *)
-                       (iter->buffer_desc[block & 0x01].buffer +
+       bd = exfat_de_iter_get_buffer(iter, block);
+       *dentry = (struct exfat_dentry *)(bd->buffer +
                        next_de_file_offset % iter->read_size);
        return 0;
 }
@@ -292,6 +296,7 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
        off_t next_file_offset;
        unsigned int block;
        int ret, sect_idx;
+       struct buffer_desc *bd;
 
        ret = exfat_de_iter_get(iter, ith, dentry);
        if (!ret) {
@@ -300,7 +305,8 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
                block = (unsigned int)(next_file_offset / iter->read_size);
                sect_idx = (int)((next_file_offset % iter->read_size) /
                                iter->write_size);
-               iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1;
+               bd = exfat_de_iter_get_buffer(iter, block);
+               BITMAP_SET(bd->dirty, sect_idx);
        }
 
        return ret;
@@ -308,8 +314,11 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
 
 int exfat_de_iter_flush(struct exfat_de_iter *iter)
 {
-       if (write_block(iter, 0) || write_block(iter, 1))
-               return -EIO;
+       unsigned int i;
+
+       for (i = 0; i < iter->exfat->buffer_count; i++)
+               if (write_block(iter, i))
+                       return -EIO;
        return 0;
 }
 
@@ -333,7 +342,7 @@ off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter)
                return EOF;
 
        block = iter->de_file_offset / iter->read_size;
-       bd = &iter->buffer_desc[block & 0x01];
+       bd = exfat_de_iter_get_buffer(iter, block);
        return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset +
                iter->de_file_offset % iter->read_size;
 }
@@ -359,9 +368,12 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
        int dentry_count, empty_dentry_count = 0;
        int retval;
 
-       bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size);
-       if (!bd)
-               return -ENOMEM;
+       if (!exfat->lookup_buffer) {
+               exfat->lookup_buffer = exfat_alloc_buffer(exfat);
+               if (!exfat->lookup_buffer)
+                       return -ENOMEM;
+       }
+       bd = exfat->lookup_buffer;
 
        retval = exfat_de_iter_init(&de_iter, exfat, parent, bd);
        if (retval == EOF || retval)
@@ -441,8 +453,6 @@ out:
                filter->out.file_offset = exfat_de_iter_file_offset(&de_iter);
                filter->out.dev_offset = EOF;
        }
-       if (bd)
-               exfat_free_buffer(bd, 2);
        return retval;
 }
 
@@ -567,7 +577,6 @@ uint16_t exfat_calc_name_hash(struct exfat *exfat,
 
        for (i = 0; i < len; i++) {
                ch = exfat->upcase_table[le16_to_cpu(name[i])];
-               ch = cpu_to_le16(ch);
 
                /* use += to avoid promotion to int; UBSan complaints about signed overflow */
                chksum = (chksum << 15) | (chksum >> 1);
@@ -613,7 +622,7 @@ int exfat_build_file_dentry_set(struct exfat *exfat, const char *name,
 
        name_len = retval / 2;
        dcount = 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX);
-       dset = calloc(1, dcount * DENTRY_SIZE);
+       dset = calloc(dcount, DENTRY_SIZE);
        if (!dset)
                return -ENOMEM;
 
@@ -682,7 +691,7 @@ int exfat_update_file_dentry_set(struct exfat *exfat,
 
                dset[1].dentry.stream.name_len = (__u8)name_len;
                dset[1].dentry.stream.name_hash =
-                       exfat_calc_name_hash(exfat, utf16_name, name_len);
+                       cpu_to_le16(exfat_calc_name_hash(exfat, utf16_name, name_len));
 
                for (i = 2; i < dcount; i++) {
                        dset[i].type = EXFAT_NAME;
@@ -702,15 +711,9 @@ int exfat_update_file_dentry_set(struct exfat *exfat,
        return 0;
 }
 
-static int find_free_cluster(struct exfat *exfat,
-                            clus_t start, clus_t *new_clu)
+static int __find_free_cluster(struct exfat *exfat, clus_t *new_clu,
+               clus_t start, clus_t end)
 {
-       clus_t end = le32_to_cpu(exfat->bs->bsx.clu_count) +
-               EXFAT_FIRST_CLUSTER;
-
-       if (!exfat_heap_clus(exfat, start))
-               return -EINVAL;
-
        while (start < end) {
                if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap,
                                           start, new_clu))
@@ -720,22 +723,63 @@ static int find_free_cluster(struct exfat *exfat,
                start = *new_clu + 1;
        }
 
-       end = start;
-       start = EXFAT_FIRST_CLUSTER;
-       while (start < end) {
-               if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap,
-                                          start, new_clu))
-                       goto out_nospc;
-               if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu))
-                       return 0;
-               start = *new_clu + 1;
-       }
-
-out_nospc:
        *new_clu = EXFAT_EOF_CLUSTER;
+
        return -ENOSPC;
 }
 
+static int find_free_cluster(struct exfat *exfat,
+                            clus_t start, clus_t *new_clu)
+{
+       clus_t end = le32_to_cpu(exfat->bs->bsx.clu_count) +
+               EXFAT_FIRST_CLUSTER;
+
+       if (!exfat_heap_clus(exfat, start))
+               return -EINVAL;
+
+       if (__find_free_cluster(exfat, new_clu, start, end) == 0)
+               return 0;
+
+       return __find_free_cluster(exfat, new_clu, EXFAT_FIRST_CLUSTER, start);
+}
+
+/* Find multiple contiguous free clusters */
+int exfat_find_free_cluster(struct exfat *exfat, int clu_count,
+                           clus_t *new_clu)
+{
+       int ret, i;
+       clus_t clu;
+       clus_t start = EXFAT_FIRST_CLUSTER;
+       clus_t end = le32_to_cpu(exfat->bs->bsx.clu_count) +
+               EXFAT_FIRST_CLUSTER;
+
+find_again:
+       ret = __find_free_cluster(exfat, &clu, start, end);
+       if (ret < 0)
+               return ret;
+
+       *new_clu = clu;
+       start = clu + 1;
+
+       for (i = 1; i < clu_count && start < end; i++) {
+               ret = __find_free_cluster(exfat, &clu, start, end);
+               if (ret < 0)
+                       return ret;
+
+               if (clu != start) {
+                       start = clu;
+                       goto find_again;
+               }
+
+               start = clu + 1;
+       }
+
+       if (start >= end)
+               return -ENOSPC;
+
+       return 0;
+}
+
 static int exfat_map_cluster(struct exfat *exfat, struct exfat_inode *inode,
                             off_t file_off, clus_t *mapped_clu)
 {
@@ -819,8 +863,8 @@ static int exfat_write_dentry_set(struct exfat *exfat,
        return 0;
 }
 
-static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
-                              clus_t *new_clu)
+int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
+               clus_t *new_clu)
 {
        clus_t last_clu;
        int err;
@@ -841,9 +885,8 @@ static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
                return -EIO;
 
        /* zero out the new cluster */
-       if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster,
-                       exfat->clus_size, exfat_c2o(exfat, *new_clu)) !=
-           (ssize_t)exfat->clus_size) {
+       if (exfat_write_zero(exfat->blk_dev->dev_fd, exfat->clus_size,
+                               exfat_c2o(exfat, *new_clu))) {
                exfat_err("failed to fill new cluster with zeroes\n");
                return -EIO;
        }