Skip to content

Commit 052d534

Browse files
committed
Merge tag 'exfat-for-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon: - Replace the internal table lookup algorithm with the hweight library and ffs of the bitops library. - Handle the two types of stream entry, valid data size (has been written) and data size separately. It improves compatibility with two differently sized files created on Windows. * tag 'exfat-for-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: do not zero the extended part exfat: change to get file size from DataLength exfat: using ffs instead of internal logic exfat: using hweight instead of internal logic
2 parents f16ab99 + f55c096 commit 052d534

File tree

5 files changed

+335
-92
lines changed

5 files changed

+335
-92
lines changed

fs/exfat/balloc.c

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,23 @@
55

66
#include <linux/blkdev.h>
77
#include <linux/slab.h>
8+
#include <linux/bitmap.h>
89
#include <linux/buffer_head.h>
910

1011
#include "exfat_raw.h"
1112
#include "exfat_fs.h"
1213

13-
static const unsigned char free_bit[] = {
14-
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/
15-
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/
16-
0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/
17-
0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/
18-
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/
19-
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/
20-
0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/
21-
0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/
22-
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/
23-
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/
24-
0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/
25-
0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/
26-
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/
27-
};
28-
29-
static const unsigned char used_bit[] = {
30-
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/
31-
2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/
32-
2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/
33-
4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/
34-
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/
35-
3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/
36-
4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/
37-
3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/
38-
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/
39-
4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/
40-
3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/
41-
5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/
42-
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/
43-
};
14+
#if BITS_PER_LONG == 32
15+
#define __le_long __le32
16+
#define lel_to_cpu(A) le32_to_cpu(A)
17+
#define cpu_to_lel(A) cpu_to_le32(A)
18+
#elif BITS_PER_LONG == 64
19+
#define __le_long __le64
20+
#define lel_to_cpu(A) le64_to_cpu(A)
21+
#define cpu_to_lel(A) cpu_to_le64(A)
22+
#else
23+
#error "BITS_PER_LONG not 32 or 64"
24+
#endif
4425

4526
/*
4627
* Allocation Bitmap Management Functions
@@ -200,32 +181,35 @@ unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu)
200181
{
201182
unsigned int i, map_i, map_b, ent_idx;
202183
unsigned int clu_base, clu_free;
203-
unsigned char k, clu_mask;
184+
unsigned long clu_bits, clu_mask;
204185
struct exfat_sb_info *sbi = EXFAT_SB(sb);
186+
__le_long bitval;
205187

206188
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
207-
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
208-
clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx & ~(BITS_PER_BYTE_MASK));
189+
ent_idx = ALIGN_DOWN(CLUSTER_TO_BITMAP_ENT(clu), BITS_PER_LONG);
190+
clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx);
209191
clu_mask = IGNORED_BITS_REMAINED(clu, clu_base);
210192

211193
map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
212194
map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx);
213195

214196
for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters;
215-
i += BITS_PER_BYTE) {
216-
k = *(sbi->vol_amap[map_i]->b_data + map_b);
197+
i += BITS_PER_LONG) {
198+
bitval = *(__le_long *)(sbi->vol_amap[map_i]->b_data + map_b);
217199
if (clu_mask > 0) {
218-
k |= clu_mask;
200+
bitval |= cpu_to_lel(clu_mask);
219201
clu_mask = 0;
220202
}
221-
if (k < 0xFF) {
222-
clu_free = clu_base + free_bit[k];
203+
if (lel_to_cpu(bitval) != ULONG_MAX) {
204+
clu_bits = lel_to_cpu(bitval);
205+
clu_free = clu_base + ffz(clu_bits);
223206
if (clu_free < sbi->num_clusters)
224207
return clu_free;
225208
}
226-
clu_base += BITS_PER_BYTE;
209+
clu_base += BITS_PER_LONG;
210+
map_b += sizeof(long);
227211

228-
if (++map_b >= sb->s_blocksize ||
212+
if (map_b >= sb->s_blocksize ||
229213
clu_base >= sbi->num_clusters) {
230214
if (++map_i >= sbi->map_sectors) {
231215
clu_base = EXFAT_FIRST_CLUSTER;
@@ -244,25 +228,24 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
244228
unsigned int count = 0;
245229
unsigned int i, map_i = 0, map_b = 0;
246230
unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi);
247-
unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK;
248-
unsigned char clu_bits;
249-
const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
250-
0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
231+
unsigned int last_mask = total_clus & (BITS_PER_LONG - 1);
232+
unsigned long *bitmap, clu_bits;
251233

252234
total_clus &= ~last_mask;
253-
for (i = 0; i < total_clus; i += BITS_PER_BYTE) {
254-
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
255-
count += used_bit[clu_bits];
256-
if (++map_b >= (unsigned int)sb->s_blocksize) {
235+
for (i = 0; i < total_clus; i += BITS_PER_LONG) {
236+
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
237+
count += hweight_long(*bitmap);
238+
map_b += sizeof(long);
239+
if (map_b >= (unsigned int)sb->s_blocksize) {
257240
map_i++;
258241
map_b = 0;
259242
}
260243
}
261244

262245
if (last_mask) {
263-
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
264-
clu_bits &= last_bit_mask[last_mask];
265-
count += used_bit[clu_bits];
246+
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
247+
clu_bits = lel_to_cpu(*(__le_long *)bitmap);
248+
count += hweight_long(clu_bits & BITMAP_LAST_WORD_MASK(last_mask));
266249
}
267250

268251
*ret_count = count;

fs/exfat/exfat_fs.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@ enum {
135135
#define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb))
136136
#define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \
137137
((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1))
138-
#define BITS_PER_BYTE_MASK 0x7
139-
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
138+
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1UL << ((clu) - (clu_base))) - 1)
140139

141140
#define ES_ENTRY_NUM(name_len) (ES_IDX_LAST_FILENAME(name_len) + 1)
142141
/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
@@ -208,6 +207,7 @@ struct exfat_dir_entry {
208207
unsigned char flags;
209208
unsigned short attr;
210209
loff_t size;
210+
loff_t valid_size;
211211
unsigned int num_subdirs;
212212
struct timespec64 atime;
213213
struct timespec64 mtime;
@@ -317,6 +317,7 @@ struct exfat_inode_info {
317317
loff_t i_size_aligned;
318318
/* on-disk position of directory entry or 0 */
319319
loff_t i_pos;
320+
loff_t valid_size;
320321
/* hash by i_location */
321322
struct hlist_node i_hash_fat;
322323
/* protect bmap against truncate */

fs/exfat/file.c

Lines changed: 172 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,76 @@
1111
#include <linux/fsnotify.h>
1212
#include <linux/security.h>
1313
#include <linux/msdos_fs.h>
14+
#include <linux/writeback.h>
1415

1516
#include "exfat_raw.h"
1617
#include "exfat_fs.h"
1718

1819
static int exfat_cont_expand(struct inode *inode, loff_t size)
1920
{
20-
struct address_space *mapping = inode->i_mapping;
21-
loff_t start = i_size_read(inode), count = size - i_size_read(inode);
22-
int err, err2;
21+
int ret;
22+
unsigned int num_clusters, new_num_clusters, last_clu;
23+
struct exfat_inode_info *ei = EXFAT_I(inode);
24+
struct super_block *sb = inode->i_sb;
25+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
26+
struct exfat_chain clu;
2327

24-
err = generic_cont_expand_simple(inode, size);
25-
if (err)
26-
return err;
28+
ret = inode_newsize_ok(inode, size);
29+
if (ret)
30+
return ret;
31+
32+
num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
33+
new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
34+
35+
if (new_num_clusters == num_clusters)
36+
goto out;
37+
38+
exfat_chain_set(&clu, ei->start_clu, num_clusters, ei->flags);
39+
ret = exfat_find_last_cluster(sb, &clu, &last_clu);
40+
if (ret)
41+
return ret;
42+
43+
clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
44+
EXFAT_EOF_CLUSTER : last_clu + 1;
45+
clu.size = 0;
46+
clu.flags = ei->flags;
47+
48+
ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters,
49+
&clu, IS_DIRSYNC(inode));
50+
if (ret)
51+
return ret;
52+
53+
/* Append new clusters to chain */
54+
if (clu.flags != ei->flags) {
55+
exfat_chain_cont_cluster(sb, ei->start_clu, num_clusters);
56+
ei->flags = ALLOC_FAT_CHAIN;
57+
}
58+
if (clu.flags == ALLOC_FAT_CHAIN)
59+
if (exfat_ent_set(sb, last_clu, clu.dir))
60+
goto free_clu;
2761

62+
if (num_clusters == 0)
63+
ei->start_clu = clu.dir;
64+
65+
out:
2866
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
29-
mark_inode_dirty(inode);
67+
/* Expanded range not zeroed, do not update valid_size */
68+
i_size_write(inode, size);
3069

31-
if (!IS_SYNC(inode))
32-
return 0;
70+
ei->i_size_aligned = round_up(size, sb->s_blocksize);
71+
ei->i_size_ondisk = ei->i_size_aligned;
72+
inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
3373

34-
err = filemap_fdatawrite_range(mapping, start, start + count - 1);
35-
err2 = sync_mapping_buffers(mapping);
36-
if (!err)
37-
err = err2;
38-
err2 = write_inode_now(inode, 1);
39-
if (!err)
40-
err = err2;
41-
if (err)
42-
return err;
74+
if (IS_DIRSYNC(inode))
75+
return write_inode_now(inode, 1);
76+
77+
mark_inode_dirty(inode);
4378

44-
return filemap_fdatawait_range(mapping, start, start + count - 1);
79+
return 0;
80+
81+
free_clu:
82+
exfat_free_cluster(inode, &clu);
83+
return -EIO;
4584
}
4685

4786
static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
@@ -146,6 +185,9 @@ int __exfat_truncate(struct inode *inode)
146185
ei->start_clu = EXFAT_EOF_CLUSTER;
147186
}
148187

188+
if (i_size_read(inode) < ei->valid_size)
189+
ei->valid_size = i_size_read(inode);
190+
149191
if (ei->type == TYPE_FILE)
150192
ei->attr |= EXFAT_ATTR_ARCHIVE;
151193

@@ -474,15 +516,124 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
474516
return blkdev_issue_flush(inode->i_sb->s_bdev);
475517
}
476518

519+
static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
520+
{
521+
int err;
522+
struct inode *inode = file_inode(file);
523+
struct address_space *mapping = inode->i_mapping;
524+
const struct address_space_operations *ops = mapping->a_ops;
525+
526+
while (start < end) {
527+
u32 zerofrom, len;
528+
struct page *page = NULL;
529+
530+
zerofrom = start & (PAGE_SIZE - 1);
531+
len = PAGE_SIZE - zerofrom;
532+
if (start + len > end)
533+
len = end - start;
534+
535+
err = ops->write_begin(file, mapping, start, len, &page, NULL);
536+
if (err)
537+
goto out;
538+
539+
zero_user_segment(page, zerofrom, zerofrom + len);
540+
541+
err = ops->write_end(file, mapping, start, len, len, page, NULL);
542+
if (err < 0)
543+
goto out;
544+
start += len;
545+
546+
balance_dirty_pages_ratelimited(mapping);
547+
cond_resched();
548+
}
549+
550+
out:
551+
return err;
552+
}
553+
554+
static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
555+
{
556+
ssize_t ret;
557+
struct file *file = iocb->ki_filp;
558+
struct inode *inode = file_inode(file);
559+
struct exfat_inode_info *ei = EXFAT_I(inode);
560+
loff_t pos = iocb->ki_pos;
561+
loff_t valid_size;
562+
563+
inode_lock(inode);
564+
565+
valid_size = ei->valid_size;
566+
567+
ret = generic_write_checks(iocb, iter);
568+
if (ret < 0)
569+
goto unlock;
570+
571+
if (pos > valid_size) {
572+
ret = exfat_file_zeroed_range(file, valid_size, pos);
573+
if (ret < 0 && ret != -ENOSPC) {
574+
exfat_err(inode->i_sb,
575+
"write: fail to zero from %llu to %llu(%zd)",
576+
valid_size, pos, ret);
577+
}
578+
if (ret < 0)
579+
goto unlock;
580+
}
581+
582+
ret = __generic_file_write_iter(iocb, iter);
583+
if (ret < 0)
584+
goto unlock;
585+
586+
inode_unlock(inode);
587+
588+
if (pos > valid_size)
589+
pos = valid_size;
590+
591+
if (iocb_is_dsync(iocb) && iocb->ki_pos > pos) {
592+
ssize_t err = vfs_fsync_range(file, pos, iocb->ki_pos - 1,
593+
iocb->ki_flags & IOCB_SYNC);
594+
if (err < 0)
595+
return err;
596+
}
597+
598+
return ret;
599+
600+
unlock:
601+
inode_unlock(inode);
602+
603+
return ret;
604+
}
605+
606+
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
607+
{
608+
int ret;
609+
struct inode *inode = file_inode(file);
610+
struct exfat_inode_info *ei = EXFAT_I(inode);
611+
loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
612+
loff_t end = min_t(loff_t, i_size_read(inode),
613+
start + vma->vm_end - vma->vm_start);
614+
615+
if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
616+
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
617+
if (ret < 0) {
618+
exfat_err(inode->i_sb,
619+
"mmap: fail to zero from %llu to %llu(%d)",
620+
start, end, ret);
621+
return ret;
622+
}
623+
}
624+
625+
return generic_file_mmap(file, vma);
626+
}
627+
477628
const struct file_operations exfat_file_operations = {
478629
.llseek = generic_file_llseek,
479630
.read_iter = generic_file_read_iter,
480-
.write_iter = generic_file_write_iter,
631+
.write_iter = exfat_file_write_iter,
481632
.unlocked_ioctl = exfat_ioctl,
482633
#ifdef CONFIG_COMPAT
483634
.compat_ioctl = exfat_compat_ioctl,
484635
#endif
485-
.mmap = generic_file_mmap,
636+
.mmap = exfat_file_mmap,
486637
.fsync = exfat_file_fsync,
487638
.splice_read = filemap_splice_read,
488639
.splice_write = iter_file_splice_write,

0 commit comments

Comments
 (0)