Merge pull request #4 from namjaejeon/exfat-next

Exfat next
This commit is contained in:
Namjae Jeon 2020-06-10 09:44:55 +09:00 • committed by GitHub
commit 8fa6fe4d43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 577 additions and 512 deletions

View file

@ -30,7 +30,6 @@ script:
- mv linux ../
- mkdir ../linux-stable/fs/exfat
- cp -ar * ../linux-stable/fs/exfat/
- mkdir ../linux/fs/exfat
- cp -ar * ../linux/fs/exfat/
# Compile with 4.1 kernel
@ -43,16 +42,16 @@ script:
- make -j$((`nproc`+1)) fs/exfat/exfat.ko
# Compile with latest Torvalds' kernel
# - cd ../linux
# - yes "" | make oldconfig > /dev/null
# - echo 'obj-$(CONFIG_EXFAT) += exfat/' >> fs/Makefile
#- echo 'source "fs/exfat/Kconfig"' >> fs/Kconfig
#- echo 'CONFIG_EXFAT_FS=m' >> .config
#- echo 'CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"' >> .config
#- make -j$((`nproc`+1)) fs/exfat/exfat.ko
# - cd ../linux
# - yes "" | make oldconfig > /dev/null
# - echo 'obj-$(CONFIG_EXFAT) += exfat/' >> fs/Makefile
# - echo 'source "fs/exfat/Kconfig"' >> fs/Kconfig
# - echo 'CONFIG_EXFAT_FS=m' >> .config
# - echo 'CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"' >> .config
# - make -j$((`nproc`+1)) fs/exfat/exfat.ko
# Run xfstests testsuite
- cd ../exfat
- cd ../linux-exfat-oot
- make > /dev/null
- sudo make install > /dev/null
- sudo modprobe exfat
@ -61,15 +60,33 @@ script:
- ./configure > /dev/null
- make -j$((`nproc`+1)) > /dev/null
- sudo make install > /dev/null
- sudo cp lib/.libs/libexfat.so* /lib/
- sudo mkdir -p /mnt/scratch
- sudo mkdir -p /mnt/test
- time dd if=/dev/zero bs=1G count=10 of=test.img
- time dd if=/dev/zero bs=1G count=10 of=scratch.img
- sudo mkdir -p /mnt/full_test
# create file/director test
- truncate -s 10G full_test.img
- sudo losetup /dev/loop22 full_test.img
- sudo mkfs.exfat /dev/loop22
- sudo mount -t exfat /dev/loop22 /mnt/full_test/
- cd /mnt/full_test/
- i=1;while [ $i -le 10000 ];do sudo touch file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
- sync
- sudo fsck.exfat /dev/loop22
- sudo rm -rf *
- i=1;while [ $i -le 10000 ];do sudo mkdir file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
- sync
- sudo rm -rf *
- sudo fsck.exfat /dev/loop22
- cd -
- sudo umount /mnt/full_test/
- sudo fsck.exfat /dev/loop22
# run xfstests test
- truncate -s 100G test.img
- truncate -s 100G scratch.img
- sudo losetup /dev/loop20 test.img
- sudo losetup /dev/loop21 test.img
- sudo mkfs.exfat -c 4 /dev/loop20
- sudo mkfs.exfat -c 4 /dev/loop21
- sudo losetup /dev/loop21 scratch.img
- sudo mkfs.exfat /dev/loop20
- sudo mkfs.exfat /dev/loop21
- cd ..
- cd exfat-testsuites/
- tar xzvf xfstests-exfat.tgz > /dev/null
@ -79,21 +96,25 @@ script:
- sudo ./check generic/006
- sudo ./check generic/007
- sudo ./check generic/011
- sudo ./check generic/013
- sudo ./check generic/014
- sudo ./check generic/028
- sudo ./check generic/029
- sudo ./check generic/030
- sudo ./check generic/034
- sudo ./check generic/035
- sudo ./check generic/036
- sudo ./check generic/069
- sudo ./check generic/071
- sudo ./check generic/073
- sudo ./check generic/074
- sudo ./check generic/075
- sudo ./check generic/076
- sudo ./check generic/080
- sudo ./check generic/084
- sudo ./check generic/086
- sudo ./check generic/091
- sudo ./check generic/095
- sudo ./check generic/098
- sudo ./check generic/100
- sudo ./check generic/112
- sudo ./check generic/113
- sudo ./check generic/114
@ -101,8 +122,11 @@ script:
- sudo ./check generic/123
- sudo ./check generic/124
- sudo ./check generic/127
- sudo ./check generic/129
- sudo ./check generic/130
- sudo ./check generic/131
- sudo ./check generic/132
- sudo ./check generic/133
- sudo ./check generic/135
- sudo ./check generic/141
- sudo ./check generic/169
@ -111,8 +135,52 @@ script:
- sudo ./check generic/208
- sudo ./check generic/209
- sudo ./check generic/210
- sudo ./check generic/211
- sudo ./check generic/212
- sudo ./check generic/214
- sudo ./check generic/215
- sudo ./check generic/221
- sudo ./check generic/228
- sudo ./check generic/239
- sudo ./check generic/240
- sudo ./check generic/241
- sudo ./check generic/245
- sudo ./check generic/246
- sudo ./check generic/247
- sudo ./check generic/248
- sudo ./check generic/249
- sudo ./check generic/257
- sudo ./check generic/263
- sudo ./check generic/285
- sudo ./check generic/286
- sudo ./check generic/308
- sudo ./check generic/309
- sudo ./check generic/310
- sudo ./check generic/313
- sudo ./check generic/323
- sudo ./check generic/325
- sudo ./check generic/338
- sudo ./check generic/339
- sudo ./check generic/340
- sudo ./check generic/344
- sudo ./check generic/345
- sudo ./check generic/346
- sudo ./check generic/347
- sudo ./check generic/354
- sudo ./check generic/376
- sudo ./check generic/393
- sudo ./check generic/394
- sudo ./check generic/405
- sudo ./check generic/406
- sudo ./check generic/409
- sudo ./check generic/410
- sudo ./check generic/411
- sudo ./check generic/412
- sudo ./check generic/418
- sudo ./check generic/428
- sudo ./check generic/437
- sudo ./check generic/438
- sudo ./check generic/441
- sudo ./check generic/443
- sudo ./check generic/448
- sudo ./check generic/450
- sudo ./check generic/451
# - sudo ./check generic/452

View file

@ -58,9 +58,8 @@ static int exfat_allocate_bitmap(struct super_block *sb,
need_map_size = ((EXFAT_DATA_CLUSTER_COUNT(sbi) - 1) / BITS_PER_BYTE)
+ 1;
if (need_map_size != map_size) {
exfat_msg(sb, KERN_ERR,
"bogus allocation bitmap size(need : %u, cur : %lld)",
need_map_size, map_size);
exfat_err(sb, "bogus allocation bitmap size(need : %u, cur : %lld)",
need_map_size, map_size);
/*
* Only allowed when bogus allocation
* bitmap size is large
@ -91,7 +90,6 @@ static int exfat_allocate_bitmap(struct super_block *sb,
}
}
sbi->pbr_bh = NULL;
return 0;
}
@ -137,8 +135,6 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
{
int i;
brelse(sbi->pbr_bh);
for (i = 0; i < sbi->map_sectors; i++)
__brelse(sbi->vol_amap[i]);
@ -195,8 +191,7 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu)
(1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
if (ret_discard == -EOPNOTSUPP) {
exfat_msg(sb, KERN_ERR,
"discard not supported by device, disabling");
exfat_err(sb, "discard not supported by device, disabling");
opts->discard = 0;
}
}

228
dir.c
View file

@ -33,35 +33,30 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, unsigned short *uniname)
{
int i;
struct exfat_dentry *ep;
struct exfat_entry_set_cache *es;
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES, &ep);
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
if (!es)
return;
if (es->num_entries < 3)
goto free_es;
ep += 2;
/*
* First entry : file entry
* Second entry : stream-extension entry
* Third entry : first file-name entry
* So, the index of first file-name dentry should start from 2.
*/
for (i = 2; i < es->num_entries; i++, ep++) {
for (i = 2; i < es->num_entries; i++) {
struct exfat_dentry *ep = exfat_get_dentry_cached(es, i);
/* end of name entry */
if (exfat_get_entry_type(ep) != TYPE_EXTEND)
goto free_es;
break;
exfat_extract_uni_name(ep, uniname);
uniname += EXFAT_FILE_NAME_LEN;
}
free_es:
kfree(es);
exfat_free_dentry_set(es, false);
}
/* read a directory entry from the opened directory */
@ -138,12 +133,12 @@ static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry)
ep->dentry.file.create_tz,
ep->dentry.file.create_time,
ep->dentry.file.create_date,
ep->dentry.file.create_time_ms);
ep->dentry.file.create_time_cs);
exfat_get_entry_time(sbi, &dir_entry->mtime,
ep->dentry.file.modify_tz,
ep->dentry.file.modify_time,
ep->dentry.file.modify_date,
ep->dentry.file.modify_time_ms);
ep->dentry.file.modify_time_cs);
exfat_get_entry_time(sbi, &dir_entry->atime,
ep->dentry.file.access_tz,
ep->dentry.file.access_time,
@ -466,12 +461,12 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
&ep->dentry.file.create_tz,
&ep->dentry.file.create_time,
&ep->dentry.file.create_date,
&ep->dentry.file.create_time_ms);
&ep->dentry.file.create_time_cs);
exfat_set_entry_time(sbi, &ts,
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
&ep->dentry.file.modify_date,
&ep->dentry.file.modify_time_ms);
&ep->dentry.file.modify_time_cs);
exfat_set_entry_time(sbi, &ts,
&ep->dentry.file.access_tz,
&ep->dentry.file.access_time,
@ -501,7 +496,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
int ret = 0;
int i, num_entries;
sector_t sector;
unsigned short chksum;
u16 chksum;
struct exfat_dentry *ep, *fep;
struct buffer_head *fbh, *bh;
@ -510,7 +505,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
return -EIO;
num_entries = fep->dentry.file.num_ext + 1;
chksum = exfat_calc_chksum_2byte(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, NULL);
@ -518,7 +513,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
ret = -EIO;
goto release_fbh;
}
chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
CS_DEFAULT);
brelse(bh);
}
@ -595,62 +590,33 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
return 0;
}
int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
struct exfat_entry_set_cache *es, int sync)
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct buffer_head *bh;
sector_t sec = es->sector;
unsigned int off = es->offset;
int chksum_type = CS_DIR_ENTRY, i, num_entries = es->num_entries;
unsigned int buf_off = (off - es->offset);
unsigned int remaining_byte_in_sector, copy_entries, clu;
int chksum_type = CS_DIR_ENTRY, i;
unsigned short chksum = 0;
struct exfat_dentry *ep;
for (i = 0; i < num_entries; i++) {
chksum = exfat_calc_chksum_2byte(&es->entries[i], DENTRY_SIZE,
chksum, chksum_type);
for (i = 0; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
chksum_type);
chksum_type = CS_DEFAULT;
}
ep = exfat_get_dentry_cached(es, 0);
ep->dentry.file.checksum = cpu_to_le16(chksum);
es->modified = true;
}
es->entries[0].dentry.file.checksum = cpu_to_le16(chksum);
void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
{
int i;
while (num_entries) {
/* write per sector base */
remaining_byte_in_sector = (1 << sb->s_blocksize_bits) - off;
copy_entries = min_t(int,
EXFAT_B_TO_DEN(remaining_byte_in_sector),
num_entries);
bh = sb_bread(sb, sec);
if (!bh)
goto err_out;
memcpy(bh->b_data + off,
(unsigned char *)&es->entries[0] + buf_off,
EXFAT_DEN_TO_B(copy_entries));
exfat_update_bh(sb, bh, sync);
brelse(bh);
num_entries -= copy_entries;
if (num_entries) {
/* get next sector */
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
clu = exfat_sector_to_cluster(sbi, sec);
if (es->alloc_flag == ALLOC_NO_FAT_CHAIN)
clu++;
else if (exfat_get_next_cluster(sb, &clu))
goto err_out;
sec = exfat_cluster_to_sector(sbi, clu);
} else {
sec++;
}
off = 0;
buf_off += EXFAT_DEN_TO_B(copy_entries);
}
for (i = 0; i < es->num_bh; i++) {
if (es->modified)
exfat_update_bh(es->sb, es->bh[i], sync);
brelse(es->bh[i]);
}
return 0;
err_out:
return -EIO;
kfree(es);
}
static int exfat_walk_fat_chain(struct super_block *sb,
@ -725,9 +691,8 @@ static int exfat_dir_readahead(struct super_block *sb, sector_t sec)
return 0;
if (sec < sbi->data_start_sector) {
exfat_msg(sb, KERN_ERR,
"requested sector is invalid(sect:%llu, root:%llu)",
(unsigned long long)sec, sbi->data_start_sector);
exfat_err(sb, "requested sector is invalid(sect:%llu, root:%llu)",
(unsigned long long)sec, sbi->data_start_sector);
return -EIO;
}
@ -755,7 +720,7 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
sector_t sec;
if (p_dir->dir == DIR_DELETED) {
exfat_msg(sb, KERN_ERR, "abnormal access to deleted dentry\n");
exfat_err(sb, "abnormal access to deleted dentry");
return NULL;
}
@ -826,39 +791,45 @@ static bool exfat_validate_entry(unsigned int type,
}
}
struct exfat_dentry *exfat_get_dentry_cached(
struct exfat_entry_set_cache *es, int num)
{
int off = es->start_off + num * DENTRY_SIZE;
struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)];
char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb);
return (struct exfat_dentry *)p;
}
/*
* Returns a set of dentries for a file or dir.
*
* Note that this is a copy (dump) of dentries so that user should
* call write_entry_set() to apply changes made in this entry set
* to the real device.
* Note It provides a direct pointer to bh->data via exfat_get_dentry_cached().
* User should call exfat_get_dentry_set() after setting 'modified' to apply
* changes made in this entry set to the real device.
*
* in:
* sb+p_dir+entry: indicates a file/dir
* type: specifies how many dentries should be included.
* out:
* file_ep: will point the first dentry(= file dentry) on success
* return:
* pointer of entry set on success,
* NULL on failure.
*/
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
struct exfat_chain *p_dir, int entry, unsigned int type,
struct exfat_dentry **file_ep)
struct exfat_chain *p_dir, int entry, unsigned int type)
{
int ret;
int ret, i, num_bh;
unsigned int off, byte_offset, clu = 0;
unsigned int entry_type;
sector_t sec;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_entry_set_cache *es;
struct exfat_dentry *ep, *pos;
unsigned char num_entries;
struct exfat_dentry *ep;
int num_entries;
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
struct buffer_head *bh;
if (p_dir->dir == DIR_DELETED) {
exfat_msg(sb, KERN_ERR, "access to deleted dentry\n");
exfat_err(sb, "access to deleted dentry");
return NULL;
}
@ -867,11 +838,18 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
if (ret)
return NULL;
es = kzalloc(sizeof(*es), GFP_KERNEL);
if (!es)
return NULL;
es->sb = sb;
es->modified = false;
/* byte offset in cluster */
byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi);
/* byte offset in sector */
off = EXFAT_BLK_OFFSET(byte_offset, sb);
es->start_off = off;
/* sector offset in cluster */
sec = EXFAT_B_TO_BLK(byte_offset, sb);
@ -879,78 +857,46 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
bh = sb_bread(sb, sec);
if (!bh)
return NULL;
goto free_es;
es->bh[es->num_bh++] = bh;
ep = (struct exfat_dentry *)(bh->b_data + off);
entry_type = exfat_get_entry_type(ep);
if (entry_type != TYPE_FILE && entry_type != TYPE_DIR)
goto release_bh;
ep = exfat_get_dentry_cached(es, 0);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto free_es;
num_entries = type == ES_ALL_ENTRIES ?
ep->dentry.file.num_ext + 1 : type;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
es = kmalloc(struct_size(es, entries, num_entries), GFP_KERNEL);
#else
es = kmalloc((offsetof(struct exfat_entry_set_cache, entries) +
(num_entries) * sizeof(struct exfat_dentry)), GFP_KERNEL);
#endif
if (!es)
goto release_bh;
es->num_entries = num_entries;
es->sector = sec;
es->offset = off;
es->alloc_flag = p_dir->flags;
pos = &es->entries[0];
while (num_entries) {
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto free_es;
/* copy dentry */
memcpy(pos, ep, sizeof(struct exfat_dentry));
if (--num_entries == 0)
break;
if (((off + DENTRY_SIZE) & (sb->s_blocksize - 1)) <
(off & (sb->s_blocksize - 1))) {
/* get the next sector */
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
if (es->alloc_flag == ALLOC_NO_FAT_CHAIN)
clu++;
else if (exfat_get_next_cluster(sb, &clu))
goto free_es;
sec = exfat_cluster_to_sector(sbi, clu);
} else {
sec++;
}
brelse(bh);
bh = sb_bread(sb, sec);
if (!bh)
num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
for (i = 1; i < num_bh; i++) {
/* get the next sector */
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
clu++;
else if (exfat_get_next_cluster(sb, &clu))
goto free_es;
off = 0;
ep = (struct exfat_dentry *)bh->b_data;
sec = exfat_cluster_to_sector(sbi, clu);
} else {
ep++;
off += DENTRY_SIZE;
sec++;
}
pos++;
bh = sb_bread(sb, sec);
if (!bh)
goto free_es;
es->bh[es->num_bh++] = bh;
}
if (file_ep)
*file_ep = &es->entries[0];
brelse(bh);
/* validiate cached dentries */
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto free_es;
}
return es;
free_es:
kfree(es);
release_bh:
brelse(bh);
exfat_free_dentry_set(es, false);
return NULL;
}
@ -1059,7 +1005,7 @@ rewind:
}
if (entry_type == TYPE_STREAM) {
unsigned short name_hash;
u16 name_hash;
if (step != DIRENT_STEP_STRM) {
step = DIRENT_STEP_FILE;

View file

@ -72,10 +72,8 @@ enum {
#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
#define FAT_CACHE_SIZE 128
#define FAT_CACHE_HASH_SIZE 64
#define BUF_CACHE_SIZE 256
#define BUF_CACHE_HASH_SIZE 64
/* Enough size to hold 256 dentry (even 512 Byte sector) */
#define DIR_CACHE_SIZE (256*sizeof(struct exfat_dentry)/512+1)
#define EXFAT_HINT_NONE -1
#define EXFAT_MIN_SUBDIR 2
@ -140,7 +138,7 @@ struct exfat_dentry_namebuf {
struct exfat_uni_name {
/* +3 for null and for converting */
unsigned short name[MAX_NAME_LENGTH + 3];
unsigned short name_hash;
u16 name_hash;
unsigned char name_len;
};
@ -171,14 +169,12 @@ struct exfat_hint {
};
struct exfat_entry_set_cache {
/* sector number that contains file_entry */
sector_t sector;
/* byte offset in the sector */
unsigned int offset;
/* flag in stream entry. 01 for cluster chain, 03 for contig. */
int alloc_flag;
struct super_block *sb;
bool modified;
unsigned int start_off;
int num_bh;
struct buffer_head *bh[DIR_CACHE_SIZE];
unsigned int num_entries;
struct exfat_dentry entries[];
};
struct exfat_dir_entry {
@ -232,7 +228,7 @@ struct exfat_sb_info {
unsigned int root_dir; /* root dir cluster */
unsigned int dentries_per_clu; /* num of dentries per cluster */
unsigned int vol_flag; /* volume dirty flag */
struct buffer_head *pbr_bh; /* buffer_head of PBR sector */
struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
unsigned int map_clu; /* allocation bitmap start cluster */
unsigned int map_sectors; /* num of allocation bitmap sectors */
@ -458,8 +454,7 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
int entry, int order, int num_entries);
int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
int entry);
int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
struct exfat_entry_set_cache *es, int sync);
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
int exfat_calc_num_entries(struct exfat_uni_name *p_uniname);
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
@ -470,9 +465,11 @@ int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
sector_t *sector);
struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
int num);
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
struct exfat_chain *p_dir, int entry, unsigned int type,
struct exfat_dentry **file_ep);
struct exfat_chain *p_dir, int entry, unsigned int type);
void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
/* inode.c */
@ -499,8 +496,6 @@ int exfat_nls_to_utf16(struct super_block *sb,
struct exfat_uni_name *uniname, int *p_lossy);
int exfat_create_upcase_table(struct super_block *sb);
void exfat_free_upcase_table(struct exfat_sb_info *sbi);
unsigned short exfat_high_surrogate(unicode_t u);
unsigned short exfat_low_surrogate(unicode_t u);
/* exfat/misc.c */
void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
@ -512,12 +507,20 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
fmt, ## args)
void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...)
__printf(3, 4) __cold;
#define exfat_err(sb, fmt, ...) \
exfat_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
#define exfat_warn(sb, fmt, ...) \
exfat_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__)
#define exfat_info(sb, fmt, ...) \
exfat_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__)
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 tz, __le16 time, __le16 date, u8 time_ms);
u8 tz, __le16 time, __le16 date, u8 time_cs);
void exfat_truncate_atime(struct timespec64 *ts);
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_ms);
unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type);
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
unsigned int size, unsigned char flags);

View file

@ -8,12 +8,15 @@
#include <linux/types.h>
#define PBR_SIGNATURE 0xAA55
#define BOOT_SIGNATURE 0xAA55
#define EXBOOT_SIGNATURE 0xAA550000
#define STR_EXFAT "EXFAT " /* size should be 8 */
#define EXFAT_MAX_FILE_LEN 255
#define VOL_CLEAN 0x0000
#define VOL_DIRTY 0x0002
#define ERR_MEDIUM 0x0004
#define EXFAT_EOF_CLUSTER 0xFFFFFFFFu
#define EXFAT_BAD_CLUSTER 0xFFFFFFF7u
@ -55,7 +58,7 @@
/* checksum types */
#define CS_DIR_ENTRY 0
#define CS_PBR_SECTOR 1
#define CS_BOOT_SECTOR 1
#define CS_DEFAULT 2
/* file attributes */
@ -69,57 +72,35 @@
#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
ATTR_SUBDIR | ATTR_ARCHIVE)
#define PBR64_JUMP_BOOT_LEN 3
#define PBR64_OEM_NAME_LEN 8
#define PBR64_RESERVED_LEN 53
#define BOOTSEC_JUMP_BOOT_LEN 3
#define BOOTSEC_FS_NAME_LEN 8
#define BOOTSEC_OLDBPB_LEN 53
#define EXFAT_FILE_NAME_LEN 15
/* EXFAT BIOS parameter block (64 bytes) */
struct bpb64 {
__u8 jmp_boot[PBR64_JUMP_BOOT_LEN];
__u8 oem_name[PBR64_OEM_NAME_LEN];
__u8 res_zero[PBR64_RESERVED_LEN];
} __packed;
/* EXFAT EXTEND BIOS parameter block (56 bytes) */
struct bsx64 {
__le64 vol_offset;
__le64 vol_length;
__le32 fat_offset;
__le32 fat_length;
__le32 clu_offset;
__le32 clu_count;
__le32 root_cluster;
__le32 vol_serial;
__u8 fs_version[2];
__le16 vol_flags;
__u8 sect_size_bits;
__u8 sect_per_clus_bits;
__u8 num_fats;
__u8 phy_drv_no;
__u8 perc_in_use;
__u8 reserved2[7];
} __packed;
/* EXFAT PBR[BPB+BSX] (120 bytes) */
struct pbr64 {
struct bpb64 bpb;
struct bsx64 bsx;
} __packed;
/* Common PBR[Partition Boot Record] (512 bytes) */
struct pbr {
union {
__u8 raw[64];
struct bpb64 f64;
} bpb;
union {
__u8 raw[56];
struct bsx64 f64;
} bsx;
__u8 boot_code[390];
__le16 signature;
/* EXFAT: Main and Backup Boot Sector (512 bytes) */
struct boot_sector {
__u8 jmp_boot[BOOTSEC_JUMP_BOOT_LEN];
__u8 fs_name[BOOTSEC_FS_NAME_LEN];
__u8 must_be_zero[BOOTSEC_OLDBPB_LEN];
__le64 partition_offset;
__le64 vol_length;
__le32 fat_offset;
__le32 fat_length;
__le32 clu_offset;
__le32 clu_count;
__le32 root_cluster;
__le32 vol_serial;
__u8 fs_revision[2];
__le16 vol_flags;
__u8 sect_size_bits;
__u8 sect_per_clus_bits;
__u8 num_fats;
__u8 drv_sel;
__u8 percent_in_use;
__u8 reserved[7];
__u8 boot_code[390];
__le16 signature;
} __packed;
struct exfat_dentry {
@ -136,8 +117,8 @@ struct exfat_dentry {
__le16 modify_date;
__le16 access_time;
__le16 access_date;
__u8 create_time_ms;
__u8 modify_time_ms;
__u8 create_time_cs;
__u8 modify_time_cs;
__u8 create_tz;
__u8 modify_tz;
__u8 access_tz;

View file

@ -177,9 +177,8 @@ int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
return 0;
/* check cluster validation */
if (p_chain->dir < 2 && p_chain->dir >= sbi->num_clusters) {
exfat_msg(sb, KERN_ERR, "invalid start cluster (%u)",
p_chain->dir);
if (!is_valid_cluster(sbi, p_chain->dir)) {
exfat_err(sb, "invalid start cluster (%u)", p_chain->dir);
return -EIO;
}
@ -313,8 +312,7 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
return 0;
release_bhs:
exfat_msg(sb, KERN_ERR, "failed zeroed sect %llu\n",
(unsigned long long)blknr);
exfat_err(sb, "failed zeroed sect %llu\n", (unsigned long long)blknr);
for (i = 0; i < n; i++)
bforget(bhs[i]);
return err;
@ -345,9 +343,8 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
/* find new cluster */
if (hint_clu == EXFAT_EOF_CLUSTER) {
if (sbi->clu_srch_ptr < EXFAT_FIRST_CLUSTER) {
exfat_msg(sb, KERN_ERR,
"sbi->clu_srch_ptr is invalid (%u)\n",
sbi->clu_srch_ptr);
exfat_err(sb, "sbi->clu_srch_ptr is invalid (%u)\n",
sbi->clu_srch_ptr);
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
}
@ -357,8 +354,8 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}
/* check cluster validation */
if (hint_clu < EXFAT_FIRST_CLUSTER && hint_clu >= sbi->num_clusters) {
exfat_msg(sb, KERN_ERR, "hint_cluster is invalid (%u)\n",
if (!is_valid_cluster(sbi, hint_clu)) {
exfat_err(sb, "hint_cluster is invalid (%u)",
hint_clu);
hint_clu = EXFAT_FIRST_CLUSTER;
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {

40
file.c
View file

@ -105,11 +105,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
unsigned int num_clusters_new, num_clusters_phys;
unsigned int last_clu = EXFAT_FREE_CLUSTER;
struct exfat_chain clu;
struct exfat_dentry *ep, *ep2;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
struct exfat_entry_set_cache *es = NULL;
int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
/* check if the given file ID is opened */
@ -162,12 +160,15 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
/* update the directory entry */
if (!evict) {
struct timespec64 ts;
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es;
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
ES_ALL_ENTRIES, &ep);
ES_ALL_ENTRIES);
if (!es)
return -EIO;
ep2 = ep + 1;
ep = exfat_get_dentry_cached(es, 0);
ep2 = exfat_get_dentry_cached(es, 1);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
ts = current_time(inode);
@ -178,16 +179,16 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
&ep->dentry.file.modify_date,
&ep->dentry.file.modify_time_ms);
&ep->dentry.file.modify_time_cs);
ep->dentry.file.attr = cpu_to_le16(ei->attr);
/* File size should be zero if there is no cluster allocated */
if (ei->start_clu == EXFAT_EOF_CLUSTER) {
ep->dentry.stream.valid_size = 0;
ep->dentry.stream.size = 0;
ep2->dentry.stream.valid_size = 0;
ep2->dentry.stream.size = 0;
} else {
ep->dentry.stream.valid_size = cpu_to_le64(new_size);
ep->dentry.stream.size = ep->dentry.stream.valid_size;
ep2->dentry.stream.valid_size = cpu_to_le64(new_size);
ep2->dentry.stream.size = ep->dentry.stream.valid_size;
}
if (new_size == 0) {
@ -198,10 +199,8 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
}
if (exfat_update_dir_chksum_with_entry_set(sb, es,
inode_needs_sync(inode)))
return -EIO;
kfree(es);
exfat_update_dir_chksum_with_entry_set(es);
exfat_free_dentry_set(es, inode_needs_sync(inode));
}
/* cut off from the FAT chain */
@ -300,6 +299,7 @@ int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
#endif
generic_fillattr(inode, stat);
exfat_truncate_atime(&stat->atime);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
stat->result_mask |= STATX_BTIME;
stat->btime.tv_sec = ei->i_crtime.tv_sec;
@ -374,6 +374,7 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
}
setattr_copy(inode, attr);
exfat_truncate_atime(&inode->i_atime);
mark_inode_dirty(inode);
out:
@ -381,12 +382,13 @@ out:
}
const struct file_operations exfat_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap = generic_file_mmap,
.fsync = generic_file_fsync,
.splice_read = generic_file_splice_read,
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap = generic_file_mmap,
.fsync = generic_file_fsync,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
};
const struct inode_operations exfat_file_inode_operations = {

55
inode.c
View file

@ -21,7 +21,6 @@
static int __exfat_write_inode(struct inode *inode, int sync)
{
int ret = -EIO;
unsigned long long on_disk_size;
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es = NULL;
@ -45,11 +44,11 @@ static int __exfat_write_inode(struct inode *inode, int sync)
exfat_set_vol_flags(sb, VOL_DIRTY);
/* get the directory entry of given file or directory */
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES,
&ep);
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES);
if (!es)
return -EIO;
ep2 = ep + 1;
ep = exfat_get_dentry_cached(es, 0);
ep2 = exfat_get_dentry_cached(es, 1);
ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode));
@ -58,12 +57,12 @@ static int __exfat_write_inode(struct inode *inode, int sync)
&ep->dentry.file.create_tz,
&ep->dentry.file.create_time,
&ep->dentry.file.create_date,
&ep->dentry.file.create_time_ms);
&ep->dentry.file.create_time_cs);
exfat_set_entry_time(sbi, &inode->i_mtime,
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
&ep->dentry.file.modify_date,
&ep->dentry.file.modify_time_ms);
&ep->dentry.file.modify_time_cs);
exfat_set_entry_time(sbi, &inode->i_atime,
&ep->dentry.file.access_tz,
&ep->dentry.file.access_time,
@ -79,9 +78,9 @@ static int __exfat_write_inode(struct inode *inode, int sync)
ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
ret = exfat_update_dir_chksum_with_entry_set(sb, es, sync);
kfree(es);
return ret;
exfat_update_dir_chksum_with_entry_set(es);
exfat_free_dentry_set(es, sync);
return 0;
}
int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
@ -112,8 +111,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
int ret, modified = false;
unsigned int last_clu;
struct exfat_chain new_clu;
struct exfat_dentry *ep;
struct exfat_entry_set_cache *es = NULL;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
@ -224,34 +221,28 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
num_clusters += num_to_be_allocated;
*clu = new_clu.dir;
if (ei->dir.dir != DIR_DELETED) {
if (ei->dir.dir != DIR_DELETED && modified) {
struct exfat_dentry *ep;
struct exfat_entry_set_cache *es;
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
ES_ALL_ENTRIES, &ep);
ES_ALL_ENTRIES);
if (!es)
return -EIO;
/* get stream entry */
ep++;
ep = exfat_get_dentry_cached(es, 1);
/* update directory entry */
if (modified) {
if (ep->dentry.stream.flags != ei->flags)
ep->dentry.stream.flags = ei->flags;
ep->dentry.stream.flags = ei->flags;
ep->dentry.stream.start_clu =
cpu_to_le32(ei->start_clu);
ep->dentry.stream.valid_size =
cpu_to_le64(i_size_read(inode));
ep->dentry.stream.size =
ep->dentry.stream.valid_size;
if (le32_to_cpu(ep->dentry.stream.start_clu) !=
ei->start_clu)
ep->dentry.stream.start_clu =
cpu_to_le32(ei->start_clu);
ep->dentry.stream.valid_size =
cpu_to_le64(i_size_read(inode));
ep->dentry.stream.size =
ep->dentry.stream.valid_size;
}
if (exfat_update_dir_chksum_with_entry_set(sb, es,
inode_needs_sync(inode)))
return -EIO;
kfree(es);
exfat_update_dir_chksum_with_entry_set(es);
exfat_free_dentry_set(es, inode_needs_sync(inode));
} /* end of if != DIR_DELETED */

56
misc.c
View file

@ -32,7 +32,7 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
exfat_msg(sb, KERN_ERR, "error, %pV\n", &vaf);
exfat_err(sb, "error, %pV", &vaf);
va_end(args);
}
@ -47,7 +47,7 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
!(sb->s_flags & MS_RDONLY)) {
sb->s_flags |= MS_RDONLY;
#endif
exfat_msg(sb, KERN_ERR, "Filesystem has been set read-only");
exfat_err(sb, "Filesystem has been set read-only");
}
}
@ -81,7 +81,7 @@ static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 tz, __le16 time, __le16 date, u8 time_ms)
u8 tz, __le16 time, __le16 date, u8 time_cs)
{
u16 t = le16_to_cpu(time);
u16 d = le16_to_cpu(date);
@ -90,11 +90,12 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
t >> 11, (t >> 5) & 0x003F, (t & 0x001F) << 1);
/* time_ms field represent 0 ~ 199(1990 ms) */
if (time_ms) {
ts->tv_sec += time_ms / 100;
ts->tv_nsec = (time_ms % 100) * 10 * NSEC_PER_MSEC;
}
/* time_cs field represent 0 ~ 199cs(1990 ms) */
if (time_cs) {
ts->tv_sec += time_cs / 100;
ts->tv_nsec = (time_cs % 100) * 10 * NSEC_PER_MSEC;
} else
ts->tv_nsec = 0;
if (tz & EXFAT_TZ_VALID)
/* Adjust timezone to UTC0. */
@ -106,7 +107,7 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
/* Convert linear UNIX date to a EXFAT time/date pair. */
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_ms)
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs)
{
struct tm tm;
u16 t, d;
@ -122,9 +123,9 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
*time = cpu_to_le16(t);
*date = cpu_to_le16(d);
/* time_ms field represent 0 ~ 199(1990 ms) */
if (time_ms)
*time_ms = (tm.tm_sec & 1) * 100 +
/* time_cs field represent 0 ~ 199cs(1990 ms) */
if (time_cs)
*time_cs = (tm.tm_sec & 1) * 100 +
ts->tv_nsec / (10 * NSEC_PER_MSEC);
/*
@ -134,17 +135,36 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
*tz = EXFAT_TZ_VALID;
}
unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type)
/* atime has only a 2-second resolution */
void exfat_truncate_atime(struct timespec64 *ts)
{
ts->tv_sec = round_down(ts->tv_sec, 2);
ts->tv_nsec = 0;
}
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
{
int i;
unsigned char *c = (unsigned char *)data;
u8 *c = (u8 *)data;
for (i = 0; i < len; i++, c++) {
if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY))
if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3)))
continue;
chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) +
(unsigned short)*c;
chksum = ((chksum << 15) | (chksum >> 1)) + *c;
}
return chksum;
}
u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
{
int i;
u8 *c = (u8 *)data;
for (i = 0; i < len; i++, c++) {
if (unlikely(type == CS_BOOT_SECTOR &&
(i == 106 || i == 107 || i == 112)))
continue;
chksum = ((chksum << 31) | (chksum >> 1)) + *c;
}
return chksum;
}

69
namei.c
View file

@ -168,16 +168,10 @@ static int exfat_utf8_d_hash(const struct dentry *dentry, struct qstr *qstr)
return charlen;
/*
* Convert to UTF-16: code points above U+FFFF are encoded as
* surrogate pairs.
* exfat_toupper() works only for code points up to the U+FFFF.
*/
if (u > 0xFFFF) {
hash = partial_name_hash(exfat_high_surrogate(u), hash);
hash = partial_name_hash(exfat_low_surrogate(u), hash);
} else {
hash = partial_name_hash(exfat_toupper(sb, u), hash);
}
hash = partial_name_hash(u <= 0xFFFF ? exfat_toupper(sb, u) : u,
hash);
}
qstr->hash = end_name_hash(hash);
@ -212,14 +206,9 @@ static int exfat_utf8_d_cmp(const struct dentry *parent,
if (u_a <= 0xFFFF && u_b <= 0xFFFF) {
if (exfat_toupper(sb, u_a) != exfat_toupper(sb, u_b))
return 1;
} else if (u_a > 0xFFFF && u_b > 0xFFFF) {
if (exfat_low_surrogate(u_a) !=
exfat_low_surrogate(u_b) ||
exfat_high_surrogate(u_a) !=
exfat_high_surrogate(u_b))
return 1;
} else {
return 1;
if (u_a != u_b)
return 1;
}
}
@ -640,6 +629,7 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
inode->i_mtime = inode->i_atime = inode->i_ctime =
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&inode->i_atime);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@ -655,8 +645,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
int ret, dentry, num_entries, count;
struct exfat_chain cdir;
struct exfat_uni_name uni_name;
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es = NULL;
struct super_block *sb = dir->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(dir);
@ -723,10 +711,14 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
info->num_subdirs = count;
} else {
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES, &ep);
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es;
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES);
if (!es)
return -EIO;
ep2 = ep + 1;
ep = exfat_get_dentry_cached(es, 0);
ep2 = exfat_get_dentry_cached(es, 1);
info->type = exfat_get_entry_type(ep);
info->attr = le16_to_cpu(ep->dentry.file.attr);
@ -744,6 +736,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
exfat_fs_error(sb,
"non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)",
i_size_read(dir), ei->dir.dir, ei->entry);
exfat_free_dentry_set(es, false);
return -EIO;
}
@ -751,18 +744,18 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
ep->dentry.file.create_tz,
ep->dentry.file.create_time,
ep->dentry.file.create_date,
ep->dentry.file.create_time_ms);
ep->dentry.file.create_time_cs);
exfat_get_entry_time(sbi, &info->mtime,
ep->dentry.file.modify_tz,
ep->dentry.file.modify_time,
ep->dentry.file.modify_date,
ep->dentry.file.modify_time_ms);
ep->dentry.file.modify_time_cs);
exfat_get_entry_time(sbi, &info->atime,
ep->dentry.file.access_tz,
ep->dentry.file.access_time,
ep->dentry.file.access_date,
0);
kfree(es);
exfat_free_dentry_set(es, false);
if (info->type == TYPE_DIR) {
exfat_chain_set(&cdir, info->start_clu,
@ -829,8 +822,8 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
if (d_unhashed(alias)) {
WARN_ON(alias->d_name.hash_len !=
dentry->d_name.hash_len);
exfat_msg(sb, KERN_INFO,
"rehashed a dentry(%p) in read lookup", alias);
exfat_info(sb, "rehashed a dentry(%p) in read lookup",
alias);
d_drop(dentry);
d_rehash(alias);
} else if (!S_ISDIR(i_mode)) {
@ -879,7 +872,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
exfat_chain_dup(&cdir, &ei->dir);
entry = ei->entry;
if (ei->dir.dir == DIR_DELETED) {
exfat_msg(sb, KERN_ERR, "abnormal access to deleted dentry");
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
goto unlock;
}
@ -919,6 +912,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
#else
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&dir->i_atime);
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -930,6 +924,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
#else
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&inode->i_atime);
exfat_unhash_inode(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
exfat_d_version_set(dentry, inode_query_iversion(dir));
@ -994,6 +989,7 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
inode->i_mtime = inode->i_atime = inode->i_ctime =
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&inode->i_atime);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@ -1065,7 +1061,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
entry = ei->entry;
if (ei->dir.dir == DIR_DELETED) {
exfat_msg(sb, KERN_ERR, "abnormal access to deleted dentry");
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
goto unlock;
}
@ -1077,9 +1073,8 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
err = exfat_check_dir_empty(sb, &clu_to_free);
if (err) {
if (err == -EIO)
exfat_msg(sb, KERN_ERR,
"failed to exfat_check_dir_empty : err(%d)",
err);
exfat_err(sb, "failed to exfat_check_dir_empty : err(%d)",
err);
goto unlock;
}
@ -1100,9 +1095,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
if (err) {
exfat_msg(sb, KERN_ERR,
"failed to exfat_remove_entries : err(%d)",
err);
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
goto unlock;
}
ei->dir.dir = DIR_DELETED;
@ -1118,6 +1111,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
#else
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&dir->i_atime);
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -1130,6 +1124,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
#else
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&inode->i_atime);
exfat_unhash_inode(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
exfat_d_version_set(dentry, inode_query_iversion(dir));
@ -1345,8 +1340,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
return -EINVAL;
if (ei->dir.dir == DIR_DELETED) {
exfat_msg(sb, KERN_ERR,
"abnormal access to deleted source dentry");
exfat_err(sb, "abnormal access to deleted source dentry");
return -ENOENT;
}
@ -1368,8 +1362,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
new_ei = EXFAT_I(new_inode);
if (new_ei->dir.dir == DIR_DELETED) {
exfat_msg(sb, KERN_ERR,
"abnormal access to deleted target dentry");
exfat_err(sb, "abnormal access to deleted target dentry");
goto out;
}
@ -1510,6 +1503,7 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&new_dir->i_atime);
if (IS_DIRSYNC(new_dir))
exfat_sync_inode(new_dir);
else
@ -1554,8 +1548,7 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
if (S_ISDIR(new_inode->i_mode))
drop_nlink(new_inode);
} else {
exfat_msg(sb, KERN_WARNING,
"abnormal access to an inode dropped");
exfat_warn(sb, "abnormal access to an inode dropped");
WARN_ON(new_inode->i_nlink == 0);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)

52
nls.c
View file

@ -503,21 +503,17 @@ static int exfat_utf8_to_utf16(struct super_block *sb,
unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN,
(wchar_t *)uniname, MAX_NAME_LENGTH + 2);
if (unilen < 0) {
exfat_msg(sb, KERN_ERR,
"failed to %s (err : %d) nls len : %d",
__func__, unilen, len);
exfat_err(sb, "failed to %s (err : %d) nls len : %d",
__func__, unilen, len);
return unilen;
}
if (unilen > MAX_NAME_LENGTH) {
exfat_msg(sb, KERN_ERR,
"failed to %s (estr:ENAMETOOLONG) nls len : %d, unilen : %d > %d",
__func__, len, unilen, MAX_NAME_LENGTH);
exfat_err(sb, "failed to %s (estr:ENAMETOOLONG) nls len : %d, unilen : %d > %d",
__func__, len, unilen, MAX_NAME_LENGTH);
return -ENAMETOOLONG;
}
p_uniname->name_len = unilen & 0xFF;
for (i = 0; i < unilen; i++) {
if (*uniname < 0x0020 ||
exfat_wstrchr(bad_uni_chars, *uniname))
@ -529,7 +525,7 @@ static int exfat_utf8_to_utf16(struct super_block *sb,
*uniname = '\0';
p_uniname->name_len = unilen;
p_uniname->name_hash = exfat_calc_chksum_2byte(upname, unilen << 1, 0,
p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0,
CS_DEFAULT);
if (p_lossy)
@ -537,22 +533,9 @@ static int exfat_utf8_to_utf16(struct super_block *sb,
return unilen;
}
#define PLANE_SIZE 0x00010000
#define SURROGATE_MASK 0xfffff800
#define SURROGATE_PAIR 0x0000d800
#define SURROGATE_LOW 0x00000400
#define SURROGATE_BITS 0x000003ff
unsigned short exfat_high_surrogate(unicode_t u)
{
return ((u - PLANE_SIZE) >> 10) + SURROGATE_PAIR;
}
unsigned short exfat_low_surrogate(unicode_t u)
{
return ((u - PLANE_SIZE) & SURROGATE_BITS) | SURROGATE_PAIR |
SURROGATE_LOW;
}
static int __exfat_utf16_to_nls(struct super_block *sb,
struct exfat_uni_name *p_uniname, unsigned char *p_cstring,
@ -638,7 +621,7 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
*uniname = '\0';
p_uniname->name_len = unilen;
p_uniname->name_hash = exfat_calc_chksum_2byte(upname, unilen << 1, 0,
p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0,
CS_DEFAULT);
if (p_lossy)
@ -670,7 +653,8 @@ static int exfat_load_upcase_table(struct super_block *sb,
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);
unsigned int sect_size = sb->s_blocksize;
unsigned int i, index = 0, checksum = 0;
unsigned int i, index = 0;
u32 chksum = 0;
int ret;
unsigned char skip = false;
unsigned short *upcase_table;
@ -687,9 +671,8 @@ static int exfat_load_upcase_table(struct super_block *sb,
bh = sb_bread(sb, sector);
if (!bh) {
exfat_msg(sb, KERN_ERR,
"failed to read sector(0x%llx)\n",
(unsigned long long)sector);
exfat_err(sb, "failed to read sector(0x%llx)\n",
(unsigned long long)sector);
ret = -EIO;
goto free_table;
}
@ -697,13 +680,6 @@ static int exfat_load_upcase_table(struct super_block *sb,
for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
unsigned short uni = get_unaligned_le16(bh->b_data + i);
checksum = ((checksum & 1) ? 0x80000000 : 0) +
(checksum >> 1) +
*(((unsigned char *)bh->b_data) + i);
checksum = ((checksum & 1) ? 0x80000000 : 0) +
(checksum >> 1) +
*(((unsigned char *)bh->b_data) + (i + 1));
if (skip) {
index += uni;
skip = false;
@ -716,15 +692,15 @@ static int exfat_load_upcase_table(struct super_block *sb,
index++;
}
}
chksum = exfat_calc_chksum32(bh->b_data, i, chksum, CS_DEFAULT);
brelse(bh);
}
if (index >= 0xFFFF && utbl_checksum == checksum)
if (index >= 0xFFFF && utbl_checksum == chksum)
return 0;
exfat_msg(sb, KERN_ERR,
"failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)\n",
index, checksum, utbl_checksum);
exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
index, chksum, utbl_checksum);
ret = -EINVAL;
free_table:
exfat_free_upcase_table(sbi);

321
super.c
View file

@ -62,6 +62,7 @@ static void exfat_put_super(struct super_block *sb)
sync_blockdev(sb->s_bdev);
exfat_set_vol_flags(sb, VOL_CLEAN);
exfat_free_bitmap(sbi);
brelse(sbi->boot_bh);
mutex_unlock(&sbi->s_lock);
call_rcu(&sbi->rcu, exfat_delayed_free);
@ -113,8 +114,8 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct pbr64 *bpb;
bool sync = 0;
struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
bool sync;
/* flags are not changed */
if (sbi->vol_flag == new_flag)
@ -132,27 +133,18 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
#endif
return 0;
if (!sbi->pbr_bh) {
sbi->pbr_bh = sb_bread(sb, 0);
if (!sbi->pbr_bh) {
exfat_msg(sb, KERN_ERR, "failed to read boot sector");
return -ENOMEM;
}
}
p_boot->vol_flags = cpu_to_le16(new_flag);
bpb = (struct pbr64 *)sbi->pbr_bh->b_data;
bpb->bsx.vol_flags = cpu_to_le16(new_flag);
if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->pbr_bh))
if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->boot_bh))
sync = true;
else
sync = false;
set_buffer_uptodate(sbi->pbr_bh);
mark_buffer_dirty(sbi->pbr_bh);
set_buffer_uptodate(sbi->boot_bh);
mark_buffer_dirty(sbi->boot_bh);
if (sync)
sync_dirty_buffer(sbi->pbr_bh);
sync_dirty_buffer(sbi->boot_bh);
return 0;
}
@ -176,7 +168,6 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",iocharset=utf8");
else if (sbi->nls_io)
seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
seq_printf(m, ",bps=%ld", sb->s_blocksize);
if (opts->errors == EXFAT_ERRORS_CONT)
seq_puts(m, ",errors=continue");
else if (opts->errors == EXFAT_ERRORS_PANIC)
@ -247,6 +238,12 @@ enum {
Opt_errors,
Opt_discard,
Opt_time_offset,
/* Deprecated options */
Opt_utf8,
Opt_debug,
Opt_namecase,
Opt_codepage,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
@ -284,6 +281,14 @@ static const struct fs_parameter_spec exfat_param_specs[] = {
#endif
fsparam_flag("discard", Opt_discard),
fsparam_s32("time_offset", Opt_time_offset),
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
NULL),
__fsparam(NULL, "debug", Opt_debug, fs_param_deprecated,
NULL),
__fsparam(fs_param_is_u32, "namecase", Opt_namecase,
fs_param_deprecated, NULL),
__fsparam(fs_param_is_u32, "codepage", Opt_codepage,
fs_param_deprecated, NULL),
{}
};
@ -351,6 +356,11 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
return -EINVAL;
opts->time_offset = result.int_32;
break;
case Opt_utf8:
case Opt_debug:
case Opt_namecase:
case Opt_codepage:
break;
default:
return -EINVAL;
}
@ -371,6 +381,13 @@ enum {
Opt_err_ro,
Opt_err,
Opt_discard,
Opt_time_offset,
/* Deprecated options */
Opt_utf8,
Opt_debug,
Opt_namecase,
Opt_codepage,
Opt_fs,
};
@ -386,6 +403,10 @@ static const match_table_t exfat_tokens = {
{Opt_err_panic, "errors=panic"},
{Opt_err_ro, "errors=remount-ro"},
{Opt_discard, "discard"},
{Opt_codepage, "codepage=%u"},
{Opt_namecase, "namecase=%u"},
{Opt_debug, "debug"},
{Opt_utf8, "utf8"},
{Opt_err, NULL}
};
@ -461,6 +482,22 @@ static int parse_options(struct super_block *sb, char *options, int silent,
case Opt_discard:
opts->discard = 1;
break;
case Opt_time_offset:
if (match_int(&args[0], &option))
return -EINVAL;
/*
* Make the limit 24 just in case someone
* invents something unusual.
*/
if (option < -24 * 60 || option > 24 * 60)
return -EINVAL;
opts->time_offset = option;
break;
case Opt_utf8:
case Opt_debug:
case Opt_namecase:
case Opt_codepage:
break;
default:
if (!silent) {
exfat_msg(sb, KERN_ERR,
@ -556,159 +593,213 @@ static int exfat_read_root(struct inode *inode)
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
CURRENT_TIME_SEC;
#endif
exfat_truncate_atime(&inode->i_atime);
exfat_cache_init_inode(inode);
return 0;
}
static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb,
struct buffer_head **prev_bh)
static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect)
{
struct pbr *p_pbr = (struct pbr *) (*prev_bh)->b_data;
unsigned short logical_sect = 0;
logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
if (!is_power_of_2(logical_sect) ||
logical_sect < 512 || logical_sect > 4096) {
exfat_msg(sb, KERN_ERR, "bogus logical sector size %u",
logical_sect);
return NULL;
exfat_err(sb, "bogus logical sector size %u", logical_sect);
return -EIO;
}
if (logical_sect < sb->s_blocksize) {
exfat_msg(sb, KERN_ERR,
"logical sector size too small for device (logical sector size = %u)",
logical_sect);
return NULL;
exfat_err(sb, "logical sector size too small for device (logical sector size = %u)",
logical_sect);
return -EIO;
}
if (logical_sect > sb->s_blocksize) {
struct buffer_head *bh = NULL;
__brelse(*prev_bh);
*prev_bh = NULL;
brelse(sbi->boot_bh);
sbi->boot_bh = NULL;
if (!sb_set_blocksize(sb, logical_sect)) {
exfat_msg(sb, KERN_ERR,
"unable to set blocksize %u", logical_sect);
return NULL;
exfat_err(sb, "unable to set blocksize %u",
logical_sect);
return -EIO;
}
bh = sb_bread(sb, 0);
if (!bh) {
exfat_msg(sb, KERN_ERR,
"unable to read boot sector (logical sector size = %lu)",
sb->s_blocksize);
return NULL;
sbi->boot_bh = sb_bread(sb, 0);
if (!sbi->boot_bh) {
exfat_err(sb, "unable to read boot sector (logical sector size = %lu)",
sb->s_blocksize);
return -EIO;
}
*prev_bh = bh;
p_pbr = (struct pbr *) bh->b_data;
}
return p_pbr;
return 0;
}
/* mount the file system volume */
static int __exfat_fill_super(struct super_block *sb)
static int exfat_read_boot_sector(struct super_block *sb)
{
int ret;
struct pbr *p_pbr;
struct pbr64 *p_bpb;
struct buffer_head *bh;
struct boot_sector *p_boot;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
/* set block size to read super block */
sb_min_blocksize(sb, 512);
/* read boot sector */
bh = sb_bread(sb, 0);
if (!bh) {
exfat_msg(sb, KERN_ERR, "unable to read boot sector");
sbi->boot_bh = sb_bread(sb, 0);
if (!sbi->boot_bh) {
exfat_err(sb, "unable to read boot sector");
return -EIO;
}
p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
/* PRB is read */
p_pbr = (struct pbr *)bh->b_data;
/* check the validity of PBR */
if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) {
exfat_msg(sb, KERN_ERR, "invalid boot record signature");
ret = -EINVAL;
goto free_bh;
/* check the validity of BOOT */
if (le16_to_cpu((p_boot->signature)) != BOOT_SIGNATURE) {
exfat_err(sb, "invalid boot record signature");
return -EINVAL;
}
/* check logical sector size */
p_pbr = exfat_read_pbr_with_logical_sector(sb, &bh);
if (!p_pbr) {
ret = -EIO;
goto free_bh;
if (memcmp(p_boot->fs_name, STR_EXFAT, BOOTSEC_FS_NAME_LEN)) {
exfat_err(sb, "invalid fs_name"); /* fs_name may unprintable */
return -EINVAL;
}
/*
* res_zero field must be filled with zero to prevent mounting
* must_be_zero field must be filled with zero to prevent mounting
* from FAT volume.
*/
if (memchr_inv(p_pbr->bpb.f64.res_zero, 0,
sizeof(p_pbr->bpb.f64.res_zero))) {
ret = -EINVAL;
goto free_bh;
if (memchr_inv(p_boot->must_be_zero, 0, sizeof(p_boot->must_be_zero)))
return -EINVAL;
if (p_boot->num_fats != 1 && p_boot->num_fats != 2) {
exfat_err(sb, "bogus number of FAT structure");
return -EINVAL;
}
p_bpb = (struct pbr64 *)p_pbr;
if (!p_bpb->bsx.num_fats) {
exfat_msg(sb, KERN_ERR, "bogus number of FAT structure");
ret = -EINVAL;
goto free_bh;
}
sbi->sect_per_clus = 1 << p_bpb->bsx.sect_per_clus_bits;
sbi->sect_per_clus_bits = p_bpb->bsx.sect_per_clus_bits;
sbi->cluster_size_bits = sbi->sect_per_clus_bits + sb->s_blocksize_bits;
sbi->sect_per_clus = 1 << p_boot->sect_per_clus_bits;
sbi->sect_per_clus_bits = p_boot->sect_per_clus_bits;
sbi->cluster_size_bits = p_boot->sect_per_clus_bits +
p_boot->sect_size_bits;
sbi->cluster_size = 1 << sbi->cluster_size_bits;
sbi->num_FAT_sectors = le32_to_cpu(p_bpb->bsx.fat_length);
sbi->FAT1_start_sector = le32_to_cpu(p_bpb->bsx.fat_offset);
sbi->FAT2_start_sector = p_bpb->bsx.num_fats == 1 ?
sbi->FAT1_start_sector :
sbi->FAT1_start_sector + sbi->num_FAT_sectors;
sbi->data_start_sector = le32_to_cpu(p_bpb->bsx.clu_offset);
sbi->num_sectors = le64_to_cpu(p_bpb->bsx.vol_length);
sbi->num_FAT_sectors = le32_to_cpu(p_boot->fat_length);
sbi->FAT1_start_sector = le32_to_cpu(p_boot->fat_offset);
sbi->FAT2_start_sector = le32_to_cpu(p_boot->fat_offset);
if (p_boot->num_fats == 2)
sbi->FAT2_start_sector += sbi->num_FAT_sectors;
sbi->data_start_sector = le32_to_cpu(p_boot->clu_offset);
sbi->num_sectors = le64_to_cpu(p_boot->vol_length);
/* because the cluster index starts with 2 */
sbi->num_clusters = le32_to_cpu(p_bpb->bsx.clu_count) +
sbi->num_clusters = le32_to_cpu(p_boot->clu_count) +
EXFAT_RESERVED_CLUSTERS;
sbi->root_dir = le32_to_cpu(p_bpb->bsx.root_cluster);
sbi->root_dir = le32_to_cpu(p_boot->root_cluster);
sbi->dentries_per_clu = 1 <<
(sbi->cluster_size_bits - DENTRY_SIZE_BITS);
sbi->vol_flag = le16_to_cpu(p_bpb->bsx.vol_flags);
sbi->vol_flag = le16_to_cpu(p_boot->vol_flags);
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED;
if (le16_to_cpu(p_bpb->bsx.vol_flags) & VOL_DIRTY) {
sbi->vol_flag |= VOL_DIRTY;
exfat_msg(sb, KERN_WARNING,
"Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
/* check consistencies */
if (sbi->num_FAT_sectors << p_boot->sect_size_bits <
sbi->num_clusters * 4) {
exfat_err(sb, "bogus fat length");
return -EINVAL;
}
if (sbi->data_start_sector <
sbi->FAT1_start_sector + sbi->num_FAT_sectors * p_boot->num_fats) {
exfat_err(sb, "bogus data start sector");
return -EINVAL;
}
if (sbi->vol_flag & VOL_DIRTY)
exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
if (sbi->vol_flag & ERR_MEDIUM)
exfat_warn(sb, "Medium has reported failures. Some data may be lost.");
/* exFAT file size is limited by a disk volume size */
sb->s_maxbytes = (u64)(sbi->num_clusters - EXFAT_RESERVED_CLUSTERS) <<
sbi->cluster_size_bits;
/* check logical sector size */
if (exfat_calibrate_blocksize(sb, 1 << p_boot->sect_size_bits))
return -EIO;
return 0;
}
static int exfat_verify_boot_region(struct super_block *sb)
{
struct buffer_head *bh = NULL;
u32 chksum = 0;
__le32 *p_sig, *p_chksum;
int sn, i;
/* read boot sector sub-regions */
for (sn = 0; sn < 11; sn++) {
bh = sb_bread(sb, sn);
if (!bh)
return -EIO;
if (sn != 0 && sn <= 8) {
/* extended boot sector sub-regions */
p_sig = (__le32 *)&bh->b_data[sb->s_blocksize - 4];
if (le32_to_cpu(*p_sig) != EXBOOT_SIGNATURE)
exfat_warn(sb, "Invalid exboot-signature(sector = %d): 0x%08x",
sn, le32_to_cpu(*p_sig));
}
chksum = exfat_calc_chksum32(bh->b_data, sb->s_blocksize,
chksum, sn ? CS_DEFAULT : CS_BOOT_SECTOR);
brelse(bh);
}
/* boot checksum sub-regions */
bh = sb_bread(sb, sn);
if (!bh)
return -EIO;
for (i = 0; i < sb->s_blocksize; i += sizeof(u32)) {
p_chksum = (__le32 *)&bh->b_data[i];
if (le32_to_cpu(*p_chksum) != chksum) {
exfat_err(sb, "Invalid boot checksum (boot checksum : 0x%08x, checksum : 0x%08x)",
le32_to_cpu(*p_chksum), chksum);
brelse(bh);
return -EINVAL;
}
}
brelse(bh);
return 0;
}
/* mount the file system volume */
static int __exfat_fill_super(struct super_block *sb)
{
int ret;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
ret = exfat_read_boot_sector(sb);
if (ret) {
exfat_err(sb, "failed to read boot sector");
goto free_bh;
}
ret = exfat_verify_boot_region(sb);
if (ret) {
exfat_err(sb, "invalid boot region");
goto free_bh;
}
ret = exfat_create_upcase_table(sb);
if (ret) {
exfat_msg(sb, KERN_ERR, "failed to load upcase table");
exfat_err(sb, "failed to load upcase table");
goto free_bh;
}
ret = exfat_load_bitmap(sb);
if (ret) {
exfat_msg(sb, KERN_ERR, "failed to load alloc-bitmap");
exfat_err(sb, "failed to load alloc-bitmap");
goto free_upcase_table;
}
ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
if (ret) {
exfat_msg(sb, KERN_ERR, "failed to scan clusters");
exfat_err(sb, "failed to scan clusters");
goto free_alloc_bitmap;
}
@ -719,7 +810,7 @@ free_alloc_bitmap:
free_upcase_table:
exfat_free_upcase_table(sbi);
free_bh:
brelse(bh);
brelse(sbi->boot_bh);
return ret;
}
@ -741,10 +832,10 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
if (opts->discard) {
struct request_queue *q = bdev_get_queue(sb->s_bdev);
if (!blk_queue_discard(q))
exfat_msg(sb, KERN_WARNING,
"mounting with \"discard\" option, but the device does not support discard");
opts->discard = 0;
if (!blk_queue_discard(q)) {
exfat_warn(sb, "mounting with \"discard\" option, but the device does not support discard");
opts->discard = 0;
}
}
#else
struct exfat_sb_info *sbi;
@ -779,14 +870,14 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
sb->s_op = &exfat_sops;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
sb->s_time_gran = 1;
sb->s_time_gran = 10 * NSEC_PER_MSEC;
sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS;
sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS;
#endif
err = __exfat_fill_super(sb);
if (err) {
exfat_msg(sb, KERN_ERR, "failed to recognize exfat type");
exfat_err(sb, "failed to recognize exfat type");
goto check_nls_io;
}
@ -802,8 +893,8 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
else {
sbi->nls_io = load_nls(sbi->options.iocharset);
if (!sbi->nls_io) {
exfat_msg(sb, KERN_ERR, "IO charset %s not found",
sbi->options.iocharset);
exfat_err(sb, "IO charset %s not found",
sbi->options.iocharset);
err = -EINVAL;
goto free_table;
}
@ -816,7 +907,7 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
root_inode = new_inode(sb);
if (!root_inode) {
exfat_msg(sb, KERN_ERR, "failed to allocate root inode.");
exfat_err(sb, "failed to allocate root inode");
err = -ENOMEM;
goto free_table;
}
@ -830,7 +921,7 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
#endif
err = exfat_read_root(root_inode);
if (err) {
exfat_msg(sb, KERN_ERR, "failed to initialize root inode.");
exfat_err(sb, "failed to initialize root inode");
goto put_inode;
}
@ -839,7 +930,7 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
sb->s_root = d_make_root(root_inode);
if (!sb->s_root) {
exfat_msg(sb, KERN_ERR, "failed to get the root dentry");
exfat_err(sb, "failed to get the root dentry");
err = -ENOMEM;
goto put_inode;
}
@ -853,6 +944,7 @@ put_inode:
free_table:
exfat_free_upcase_table(sbi);
exfat_free_bitmap(sbi);
brelse(sbi->boot_bh);
check_nls_io:
unload_nls(sbi->nls_io);
@ -981,6 +1073,7 @@ static void __exit exit_exfat_fs(void)
module_init(init_exfat_fs);
module_exit(exit_exfat_fs);
MODULE_ALIAS_FS("exfat");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("exFAT filesystem support");
MODULE_AUTHOR("Samsung Electronics Co., Ltd.");