1
0
Fork 0

Description for this pull request:

* Bug fixes
   - Fix memory leak on mount failure with iocharset= option.
   - Fix Incorrect update of stream entry.
   - Fix cluster range validation error.
 
 * Clean-up codes
   - Remove unused code and unneeded assignment.
   - Rename variables in exfat structure as specification.
   - Reorganize boot sector analysis code.
   - Simplify exfat_utf8_d_hash and exfat_utf8_d_cmp().
   - Optimize exfat entry cache functions.
   - Improve wording of EXFAT_DEFAULT_IOCHARSET config option.
 
 * New Feature
   - Add boot region verification.
 -----BEGIN PGP SIGNATURE-----
 
 iQJMBAABCgA2FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAl7fQBwYHG5hbWphZS5q
 ZW9uQHNhbXN1bmcuY29tAAoJEGcL+wNRRCEIyWMP/2CqlryPilKiXj/C2n9r2s5O
 7NNABC7xhyILk9fGz/mUOGohqBQXNNbZUDS17m2xbygw3vkXYN72ejDb/1DLVU8E
 LsYd85Pj8l7kMnOmjXKNLetoql1S3nm19PgIB7GYNI/BfeBFXcyxQdOTOlwq28w7
 PkfnWhnvnIxTfbTJj6EFB5tPYDycpm32LiUSQqsAmy2i0pC9WY6w4PnJz/c8wiqe
 +LZkLtZ1blGSKLY6C1FotVi7OmjiRWm0e+sdPE/Rsaxb/nnL/S7Nt03GPHZMkGxm
 eVq5MBUadQAr61duIWKcF7dFUmqqVTAO/bgYrxB4ljd/1j1lwWwZjD7iLnbsOfOy
 +Go5NsDoLEySKp7JSkLJ8S6mdKsAyAf4TK8diZlIGGfF7jV6puo3h9yDk0e6U0/G
 E613f60O5bymQWe9STLiJwMo65M7rjzuT3WUcTFuf58LqS6UR+ngq089V4lV720N
 USxZu7wtO5m0j5feXY72x6E/xaL1wqbMuHr0defQZ9CN8JZKCRtthletjI8TVDOZ
 hxIASZacQdWkWBL4mCs3lmaflSaD32J7RxPSqnQHMxrB6UVh9lT97rQBGGnbyRyL
 2Hqwe8cUk/ki6fOmpNvyIUh01S+wtgVGuAAEoKPEIKGmDw1KeAGXOpVX1NPcbZWT
 s7HTy7H3SfAnNAED8+Ct
 =Dgtx
 -----END PGP SIGNATURE-----

Merge tag 'exfat-for-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat

Pull exfat update from Namjae Jeon:
 "Bug fixes:
   - Fix memory leak on mount failure with iocharset= option
   - Fix incorrect update of stream entry
   - Fix cluster range validation error

  Clean-ups:
   - Remove unused code and unneeded assignment
   - Rename variables in exfat structure as specification
   - Reorganize boot sector analysis code
   - Simplify exfat_utf8_d_hash and exfat_utf8_d_cmp()
   - Optimize exfat entry cache functions
   - Improve wording of EXFAT_DEFAULT_IOCHARSET config option

 New Feature:
   - Add boot region verification"

* tag 'exfat-for-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: Fix potential use after free in exfat_load_upcase_table()
  exfat: fix range validation error in alloc and free cluster
  exfat: fix incorrect update of stream entry in __exfat_truncate()
  exfat: fix memory leak in exfat_parse_param()
  exfat: remove unnecessary reassignment of p_uniname->name_len
  exfat: standardize checksum calculation
  exfat: add boot region verification
  exfat: separate the boot sector analysis
  exfat: redefine PBR as boot_sector
  exfat: optimize dir-cache
  exfat: replace 'time_ms' with 'time_cs'
  exfat: remove the assignment of 0 to bool variable
  exfat: Remove unused functions exfat_high_surrogate() and exfat_low_surrogate()
  exfat: Simplify exfat_utf8_d_hash() for code points above U+FFFF
  exfat: Improve wording of EXFAT_DEFAULT_IOCHARSET config option
  exfat: Use a more common logging style
  exfat: Simplify exfat_utf8_d_cmp() for code points above U+FFFF
alistair/sunxi64-5.8
Linus Torvalds 2020-06-09 11:24:59 -07:00
commit ad57a1022f
12 changed files with 423 additions and 469 deletions

View File

@ -16,6 +16,7 @@ config EXFAT_DEFAULT_IOCHARSET
depends on EXFAT_FS
help
Set this to the default input/output character set to use for
converting between the encoding is used for user visible filename and
UTF-16 character that exfat filesystem use, and can be overridden with
the "iocharset" mount option for exFAT filesystems.
converting between the encoding that is used for user visible
filenames and the UTF-16 character encoding that the exFAT
filesystem uses. This can be overridden with the "iocharset" mount
option for the exFAT filesystems.

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
@ -192,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;
}
}

View File

@ -32,35 +32,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 */
@ -137,12 +132,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,
@ -461,12 +456,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,
@ -496,7 +491,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;
@ -505,7 +500,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);
@ -513,7 +508,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);
}
@ -590,62 +585,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,
@ -720,9 +686,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;
}
@ -750,7 +715,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;
}
@ -821,39 +786,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;
}
@ -862,11 +833,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);
@ -874,72 +852,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;
es = kmalloc(struct_size(es, entries, num_entries), GFP_KERNEL);
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;
}
@ -1048,7 +1000,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

@ -71,10 +71,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
@ -139,7 +137,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;
};
@ -170,14 +168,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 {
@ -231,7 +227,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 */
@ -451,8 +447,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,
@ -463,9 +458,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 */
@ -492,8 +489,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, ...)
@ -505,13 +500,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

@ -169,9 +169,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;
}
@ -305,8 +304,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;
@ -337,9 +335,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;
}
@ -349,8 +346,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) {

View File

@ -96,11 +96,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 */
@ -153,28 +151,31 @@ 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);
ts = current_time(inode);
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);
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) {
@ -185,10 +186,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 */

View File

@ -19,7 +19,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;
@ -43,11 +42,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));
@ -56,12 +55,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,
@ -77,9 +76,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)
@ -110,8 +109,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);
@ -222,34 +219,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 */

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);
}
@ -41,7 +41,7 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
sb->s_id);
} else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) {
sb->s_flags |= SB_RDONLY;
exfat_msg(sb, KERN_ERR, "Filesystem has been set read-only");
exfat_err(sb, "Filesystem has been set read-only");
}
}
@ -75,7 +75,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);
@ -84,10 +84,10 @@ 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;
@ -101,7 +101,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;
@ -113,9 +113,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);
/*
@ -136,17 +136,29 @@ void exfat_truncate_atime(struct timespec64 *ts)
ts->tv_nsec = 0;
}
unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type)
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;
}

View File

@ -147,16 +147,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);
@ -185,14 +179,9 @@ static int exfat_utf8_d_cmp(const struct dentry *dentry, unsigned int len,
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;
}
}
@ -611,8 +600,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);
@ -671,10 +658,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);
@ -692,7 +683,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);
kfree(es);
exfat_free_dentry_set(es, false);
return -EIO;
}
@ -700,18 +691,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,
@ -778,8 +769,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)) {
@ -824,7 +815,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;
}
@ -979,7 +970,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;
}
@ -991,9 +982,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;
}
@ -1014,9 +1004,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;
@ -1245,8 +1233,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;
}
@ -1268,8 +1255,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;
}
@ -1431,8 +1417,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);
}
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =

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);

View File

@ -49,7 +49,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->pbr_bh);
brelse(sbi->boot_bh);
mutex_unlock(&sbi->s_lock);
call_rcu(&sbi->rcu, exfat_delayed_free);
@ -101,8 +101,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 = (struct pbr64 *)sbi->pbr_bh->b_data;
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)
@ -116,18 +116,18 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
if (sb_rdonly(sb))
return 0;
bpb->bsx.vol_flags = cpu_to_le16(new_flag);
p_boot->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;
}
@ -273,9 +273,8 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
break;
case Opt_charset:
exfat_free_iocharset(sbi);
opts->iocharset = kstrdup(param->string, GFP_KERNEL);
if (!opts->iocharset)
return -ENOMEM;
opts->iocharset = param->string;
param->string = NULL;
break;
case Opt_errors:
opts->errors = result.uint_32;
@ -366,151 +365,208 @@ static int exfat_read_root(struct inode *inode)
return 0;
}
static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb)
static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct pbr *p_pbr = (struct pbr *) (sbi->pbr_bh)->b_data;
unsigned short logical_sect = 0;
logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits;
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) {
brelse(sbi->pbr_bh);
sbi->pbr_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;
}
sbi->pbr_bh = sb_bread(sb, 0);
if (!sbi->pbr_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;
}
p_pbr = (struct pbr *)sbi->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 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 */
sbi->pbr_bh = sb_bread(sb, 0);
if (!sbi->pbr_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 *)sbi->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);
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;
}
@ -521,7 +577,7 @@ free_alloc_bitmap:
free_upcase_table:
exfat_free_upcase_table(sbi);
free_bh:
brelse(sbi->pbr_bh);
brelse(sbi->boot_bh);
return ret;
}
@ -539,8 +595,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
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");
exfat_warn(sb, "mounting with \"discard\" option, but the device does not support discard");
opts->discard = 0;
}
}
@ -555,7 +610,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
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;
}
@ -567,8 +622,8 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
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;
}
@ -581,7 +636,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
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;
}
@ -590,7 +645,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
inode_set_iversion(root_inode, 1);
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;
}
@ -599,7 +654,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
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;
}
@ -613,7 +668,7 @@ put_inode:
free_table:
exfat_free_upcase_table(sbi);
exfat_free_bitmap(sbi);
brelse(sbi->pbr_bh);
brelse(sbi->boot_bh);
check_nls_io:
unload_nls(sbi->nls_io);
@ -630,7 +685,12 @@ static int exfat_get_tree(struct fs_context *fc)
static void exfat_free(struct fs_context *fc)
{
kfree(fc->s_fs_info);
struct exfat_sb_info *sbi = fc->s_fs_info;
if (sbi) {
exfat_free_iocharset(sbi);
kfree(sbi);
}
}
static const struct fs_context_operations exfat_context_ops = {