]> git.sven.stormbind.net Git - sven/exfatprogs.git/blobdiff - lib/exfat_dir.c
New upstream version 1.2.2
[sven/exfatprogs.git] / lib / exfat_dir.c
index 7c145f4a54978893f03ea6f07e81d001fca14324..98e820f9404e1011a5f78fe9ff2ac153fcd1e3d4 100644 (file)
@@ -237,7 +237,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;
@@ -356,9 +356,8 @@ 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)
@@ -379,6 +378,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;
@@ -407,18 +412,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);
@@ -430,7 +434,7 @@ 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 {
@@ -472,7 +476,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;
@@ -485,19 +489,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)
@@ -506,6 +508,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)
 {
@@ -514,12 +530,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)
@@ -548,8 +569,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;
 }
@@ -678,7 +702,7 @@ int exfat_update_file_dentry_set(struct exfat *exfat,
        return 0;
 }
 
-int exfat_find_free_cluster(struct exfat *exfat,
+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) +
@@ -805,7 +829,7 @@ static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
        if ((need_dset && !inode->dentry_set) || inode->is_contiguous)
                return -EINVAL;
 
-       err = exfat_find_free_cluster(exfat, exfat->start_clu, new_clu);
+       err = find_free_cluster(exfat, exfat->start_clu, new_clu);
        if (err) {
                exfat->start_clu = EXFAT_FIRST_CLUSTER;
                exfat_err("failed to find an free cluster\n");