]> git.sven.stormbind.net Git - sven/exfatprogs.git/blobdiff - lib/exfat_dir.c
releasing package exfatprogs version 1.2.3-1
[sven/exfatprogs.git] / lib / exfat_dir.c
index 499b672f34fb977553c464f0dc64f32855df1b55..7b99af114c48cc5b6cbba2618b29085b836491f5 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,18 +40,19 @@ 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];
-       device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
+       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,
                                        desc->buffer + i * iter->write_size,
                                        iter->write_size,
                                        device_offset + i * iter->write_size)
                                        != (ssize_t)iter->write_size)
                                return -EIO;
-                       desc->dirty[i] = 0;
+                       BITMAP_CLEAR(desc->dirty, i);
                }
        }
        return 0;
@@ -168,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;
@@ -182,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;
@@ -224,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
@@ -236,7 +243,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
        iter->de_file_offset = 0;
        iter->next_read_offset = iter->read_size;
        iter->max_skip_dentries = 0;
-       iter->dot_name_num = 0;
+       iter->invalid_name_num = 0;
 
        if (iter->parent->size == 0)
                return EOF;
@@ -256,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);
@@ -264,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) {
@@ -279,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;
 }
@@ -291,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) {
@@ -299,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;
@@ -307,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;
 }
 
@@ -332,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;
 }
@@ -355,13 +365,15 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
        struct exfat_dentry *dentry = NULL;
        off_t free_file_offset = 0, free_dev_offset = 0;
        struct exfat_de_iter de_iter;
-       int dentry_count;
+       int dentry_count, empty_dentry_count = 0;
        int retval;
-       bool last_is_free = false;
 
-       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)
@@ -378,6 +390,12 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
                        goto out;
                }
 
+               if (!IS_EXFAT_DELETED(dentry->type)) {
+                       if (filter->in.dentry_count == 0 ||
+                           empty_dentry_count < filter->in.dentry_count)
+                               empty_dentry_count = 0;
+               }
+
                dentry_count = 1;
                if (dentry->type == filter->in.type) {
                        retval = 0;
@@ -406,18 +424,17 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
                        } else if (retval < 0) {
                                goto out;
                        }
-                       last_is_free = false;
-               } else if ((dentry->type == EXFAT_LAST ||
-                           IS_EXFAT_DELETED(dentry->type))) {
-                       if (!last_is_free) {
+               } else if (IS_EXFAT_DELETED(dentry->type)) {
+                       if (empty_dentry_count == 0) {
                                free_file_offset =
                                        exfat_de_iter_file_offset(&de_iter);
                                free_dev_offset =
                                        exfat_de_iter_device_offset(&de_iter);
-                               last_is_free = true;
                        }
-               } else {
-                       last_is_free = false;
+
+                       if (filter->in.dentry_count == 0 ||
+                           empty_dentry_count < filter->in.dentry_count)
+                               empty_dentry_count++;
                }
 
                exfat_de_iter_advance(&de_iter, dentry_count);
@@ -429,15 +446,13 @@ out:
                        exfat_de_iter_file_offset(&de_iter);
                filter->out.dev_offset =
                        exfat_de_iter_device_offset(&de_iter);
-       } else if (retval == EOF && last_is_free) {
+       } else if (retval == EOF && empty_dentry_count) {
                filter->out.file_offset = free_file_offset;
                filter->out.dev_offset = free_dev_offset;
        } else {
                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;
 }
 
@@ -471,7 +486,7 @@ static int filter_lookup_file(struct exfat_de_iter *de_iter,
                if (retval || name_de->type != EXFAT_NAME)
                        return 1;
 
-               len = MIN(name_len, ENTRY_NAME_MAX);
+               len = MIN(name_len + 1, ENTRY_NAME_MAX);
                if (memcmp(name_de->dentry.name.unicode_0_14,
                           name, len * 2) != 0)
                        return 1;
@@ -484,19 +499,17 @@ static int filter_lookup_file(struct exfat_de_iter *de_iter,
        return 0;
 }
 
-int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
-                     const char *name, struct exfat_lookup_filter *filter_out)
+int exfat_lookup_file_by_utf16name(struct exfat *exfat,
+                                struct exfat_inode *parent,
+                                __le16 *utf16_name,
+                                struct exfat_lookup_filter *filter_out)
 {
        int retval;
-       __le16 utf16_name[PATH_MAX + 2] = {0, };
-
-       retval = (int)exfat_utf16_enc(name, utf16_name, sizeof(utf16_name));
-       if (retval < 0)
-               return retval;
 
        filter_out->in.type = EXFAT_FILE;
        filter_out->in.filter = filter_lookup_file;
        filter_out->in.param = utf16_name;
+       filter_out->in.dentry_count = 0;
 
        retval = exfat_lookup_dentry_set(exfat, parent, filter_out);
        if (retval < 0)
@@ -505,6 +518,20 @@ int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
        return 0;
 }
 
+int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
+                     const char *name, struct exfat_lookup_filter *filter_out)
+{
+       int retval;
+       __le16 utf16_name[PATH_MAX + 2] = {0, };
+
+       retval = (int)exfat_utf16_enc(name, utf16_name, sizeof(utf16_name));
+       if (retval < 0)
+               return retval;
+
+       return exfat_lookup_file_by_utf16name(exfat, parent, utf16_name,
+                       filter_out);
+}
+
 void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
                                uint16_t *checksum, bool primary)
 {
@@ -513,12 +540,17 @@ void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
 
        bytes = (uint8_t *)dentry;
 
-       *checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[0];
-       *checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[1];
+       /* use += to avoid promotion to int; UBSan complaints about signed overflow */
+       *checksum = (*checksum << 15) | (*checksum >> 1);
+       *checksum += bytes[0];
+       *checksum = (*checksum << 15) | (*checksum >> 1);
+       *checksum += bytes[1];
 
        i = primary ? 4 : 2;
-       for (; i < sizeof(*dentry); i++)
-               *checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[i];
+       for (; i < sizeof(*dentry); i++) {
+               *checksum = (*checksum << 15) | (*checksum >> 1);
+               *checksum += bytes[i];
+       }
 }
 
 static uint16_t calc_dentry_set_checksum(struct exfat_dentry *dset, int dcount)
@@ -547,8 +579,11 @@ uint16_t exfat_calc_name_hash(struct exfat *exfat,
                ch = exfat->upcase_table[le16_to_cpu(name[i])];
                ch = cpu_to_le16(ch);
 
-               chksum = ((chksum << 15) | (chksum >> 1)) + (ch & 0xFF);
-               chksum = ((chksum << 15) | (chksum >> 1)) + (ch >> 8);
+               /* use += to avoid promotion to int; UBSan complaints about signed overflow */
+               chksum = (chksum << 15) | (chksum >> 1);
+               chksum += ch & 0xFF;
+               chksum = (chksum << 15) | (chksum >> 1);
+               chksum += ch >> 8;
        }
        return chksum;
 }
@@ -588,7 +623,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;
 
@@ -816,9 +851,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;
        }