##__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;
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,
device_offset + i * iter->write_size)
!= (ssize_t)iter->write_size)
return -EIO;
- desc->dirty[i] = 0;
+ BITMAP_CLEAR(desc->dirty, i);
}
}
return 0;
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;
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;
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
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;
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);
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) {
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;
}
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) {
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;
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;
}
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;
}
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)
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;
} 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);
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;
}
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;
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)
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)
{
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)
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;
}
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;
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) +
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");
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;
}