mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
Merge "Merge remote-tracking branch 'remotes/origin/tmp-5030b0a' into msm-5.4"
This commit is contained in:
commit
7ffa7cf024
12 changed files with 688 additions and 746 deletions
|
|
@ -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]);
|
||||
|
||||
|
|
@ -162,7 +158,7 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu)
|
|||
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
|
||||
|
||||
set_bit_le(b, sbi->vol_amap[i]->b_data);
|
||||
exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode));
|
||||
exfat_update_bh(sbi->vol_amap[i], IS_DIRSYNC(inode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +180,7 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu)
|
|||
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
|
||||
|
||||
clear_bit_le(b, sbi->vol_amap[i]->b_data);
|
||||
exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode));
|
||||
exfat_update_bh(sbi->vol_amap[i], IS_DIRSYNC(inode));
|
||||
|
||||
if (opts->discard) {
|
||||
int ret_discard;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
||||
#define EXFAT_CACHE_VALID 0
|
||||
#define EXFAT_MAX_CACHE 16
|
||||
|
||||
struct exfat_cache {
|
||||
|
|
@ -61,16 +60,6 @@ void exfat_cache_shutdown(void)
|
|||
kmem_cache_destroy(exfat_cachep);
|
||||
}
|
||||
|
||||
void exfat_cache_init_inode(struct inode *inode)
|
||||
{
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
|
||||
spin_lock_init(&ei->cache_lru_lock);
|
||||
ei->nr_caches = 0;
|
||||
ei->cache_valid_id = EXFAT_CACHE_VALID + 1;
|
||||
INIT_LIST_HEAD(&ei->cache_lru);
|
||||
}
|
||||
|
||||
static inline struct exfat_cache *exfat_cache_alloc(void)
|
||||
{
|
||||
return kmem_cache_alloc(exfat_cachep, GFP_NOFS);
|
||||
|
|
|
|||
283
fs/exfat/dir.c
283
fs/exfat/dir.c
|
|
@ -32,41 +32,36 @@ 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 */
|
||||
static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry)
|
||||
static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
|
||||
{
|
||||
int i, dentries_per_clu, dentries_per_clu_bits = 0;
|
||||
int i, dentries_per_clu, dentries_per_clu_bits = 0, num_ext;
|
||||
unsigned int type, clu_offset;
|
||||
sector_t sector;
|
||||
struct exfat_chain dir, clu;
|
||||
|
|
@ -75,7 +70,7 @@ static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry)
|
|||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
unsigned int dentry = ei->rwoffset & 0xFFFFFFFF;
|
||||
unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0xFFFFFFFF;
|
||||
struct buffer_head *bh;
|
||||
|
||||
/* check if the given file ID is opened */
|
||||
|
|
@ -132,17 +127,18 @@ static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry)
|
|||
continue;
|
||||
}
|
||||
|
||||
num_ext = ep->dentry.file.num_ext;
|
||||
dir_entry->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
exfat_get_entry_time(sbi, &dir_entry->crtime,
|
||||
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,
|
||||
|
|
@ -162,12 +158,13 @@ static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry)
|
|||
return -EIO;
|
||||
dir_entry->size =
|
||||
le64_to_cpu(ep->dentry.stream.valid_size);
|
||||
dir_entry->entry = dentry;
|
||||
brelse(bh);
|
||||
|
||||
ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
|
||||
ei->hint_bmap.clu = clu.dir;
|
||||
|
||||
ei->rwoffset = ++dentry;
|
||||
*cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +180,7 @@ static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry)
|
|||
}
|
||||
|
||||
dir_entry->namebuf.lfn[0] = '\0';
|
||||
ei->rwoffset = dentry;
|
||||
*cpos = EXFAT_DEN_TO_B(dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -247,12 +244,10 @@ static int exfat_iterate(struct file *filp, struct dir_context *ctx)
|
|||
if (err)
|
||||
goto unlock;
|
||||
get_new:
|
||||
ei->rwoffset = EXFAT_B_TO_DEN(cpos);
|
||||
|
||||
if (cpos >= i_size_read(inode))
|
||||
goto end_of_dir;
|
||||
|
||||
err = exfat_readdir(inode, &de);
|
||||
err = exfat_readdir(inode, &cpos, &de);
|
||||
if (err) {
|
||||
/*
|
||||
* At least we tried to read a sector. Move cpos to next sector
|
||||
|
|
@ -267,13 +262,10 @@ get_new:
|
|||
goto end_of_dir;
|
||||
}
|
||||
|
||||
cpos = EXFAT_DEN_TO_B(ei->rwoffset);
|
||||
|
||||
if (!nb->lfn[0])
|
||||
goto end_of_dir;
|
||||
|
||||
i_pos = ((loff_t)ei->start_clu << 32) |
|
||||
((ei->rwoffset - 1) & 0xffffffff);
|
||||
i_pos = ((loff_t)ei->start_clu << 32) | (de.entry & 0xffffffff);
|
||||
tmp = exfat_iget(sb, i_pos);
|
||||
if (tmp) {
|
||||
inum = tmp->i_ino;
|
||||
|
|
@ -314,7 +306,7 @@ const struct file_operations exfat_dir_operations = {
|
|||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate = exfat_iterate,
|
||||
.fsync = generic_file_fsync,
|
||||
.fsync = exfat_file_fsync,
|
||||
};
|
||||
|
||||
int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu)
|
||||
|
|
@ -430,10 +422,12 @@ static void exfat_init_name_entry(struct exfat_dentry *ep,
|
|||
ep->dentry.name.flags = 0x0;
|
||||
|
||||
for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
|
||||
ep->dentry.name.unicode_0_14[i] = cpu_to_le16(*uniname);
|
||||
if (*uniname == 0x0)
|
||||
break;
|
||||
uniname++;
|
||||
if (*uniname != 0x0) {
|
||||
ep->dentry.name.unicode_0_14[i] = cpu_to_le16(*uniname);
|
||||
uniname++;
|
||||
} else {
|
||||
ep->dentry.name.unicode_0_14[i] = 0x0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -461,19 +455,19 @@ 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,
|
||||
&ep->dentry.file.access_date,
|
||||
NULL);
|
||||
|
||||
exfat_update_bh(sb, bh, IS_DIRSYNC(inode));
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
|
||||
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, §or);
|
||||
|
|
@ -483,7 +477,7 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
|
|||
exfat_init_stream_entry(ep,
|
||||
(type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN,
|
||||
start_clu, size);
|
||||
exfat_update_bh(sb, bh, IS_DIRSYNC(inode));
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
|
||||
return 0;
|
||||
|
|
@ -496,7 +490,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 +499,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,13 +507,13 @@ 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);
|
||||
}
|
||||
|
||||
fep->dentry.file.checksum = cpu_to_le16(chksum);
|
||||
exfat_update_bh(sb, fbh, IS_DIRSYNC(inode));
|
||||
exfat_update_bh(fbh, IS_DIRSYNC(inode));
|
||||
release_fbh:
|
||||
brelse(fbh);
|
||||
return ret;
|
||||
|
|
@ -541,7 +535,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
|||
return -EIO;
|
||||
|
||||
ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);
|
||||
exfat_update_bh(sb, bh, sync);
|
||||
exfat_update_bh(bh, sync);
|
||||
brelse(bh);
|
||||
|
||||
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, §or);
|
||||
|
|
@ -550,7 +544,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
|||
|
||||
ep->dentry.stream.name_len = p_uniname->name_len;
|
||||
ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash);
|
||||
exfat_update_bh(sb, bh, sync);
|
||||
exfat_update_bh(bh, sync);
|
||||
brelse(bh);
|
||||
|
||||
for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
|
||||
|
|
@ -559,7 +553,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
|||
return -EIO;
|
||||
|
||||
exfat_init_name_entry(ep, uniname);
|
||||
exfat_update_bh(sb, bh, sync);
|
||||
exfat_update_bh(bh, sync);
|
||||
brelse(bh);
|
||||
uniname += EXFAT_FILE_NAME_LEN;
|
||||
}
|
||||
|
|
@ -583,69 +577,44 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
|
|||
return -EIO;
|
||||
|
||||
exfat_set_entry_type(ep, TYPE_DELETED);
|
||||
exfat_update_bh(sb, bh, IS_DIRSYNC(inode));
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
}
|
||||
|
||||
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);
|
||||
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
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 (es->modified)
|
||||
err = exfat_update_bhs(es->bh, es->num_bh, sync);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_out:
|
||||
return -EIO;
|
||||
for (i = 0; i < es->num_bh; i++)
|
||||
if (err)
|
||||
bforget(es->bh[i]);
|
||||
else
|
||||
brelse(es->bh[i]);
|
||||
kfree(es);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int exfat_walk_fat_chain(struct super_block *sb,
|
||||
|
|
@ -720,9 +689,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 +718,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 +789,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 +836,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 +855,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;
|
||||
}
|
||||
|
||||
|
|
@ -953,7 +908,6 @@ enum {
|
|||
/*
|
||||
* return values:
|
||||
* >= 0 : return dir entiry position with the name in dir
|
||||
* -EEXIST : (root dir, ".") it is the root dir itself
|
||||
* -ENOENT : entry with the name does not exist
|
||||
* -EIO : I/O error
|
||||
*/
|
||||
|
|
@ -1021,11 +975,8 @@ rewind:
|
|||
if (ei->hint_femp.eidx ==
|
||||
EXFAT_HINT_NONE ||
|
||||
candi_empty.eidx <=
|
||||
ei->hint_femp.eidx) {
|
||||
memcpy(&ei->hint_femp,
|
||||
&candi_empty,
|
||||
sizeof(candi_empty));
|
||||
}
|
||||
ei->hint_femp.eidx)
|
||||
ei->hint_femp = candi_empty;
|
||||
}
|
||||
|
||||
brelse(bh);
|
||||
|
|
@ -1048,7 +999,7 @@ rewind:
|
|||
}
|
||||
|
||||
if (entry_type == TYPE_STREAM) {
|
||||
unsigned short name_hash;
|
||||
u16 name_hash;
|
||||
|
||||
if (step != DIRENT_STEP_STRM) {
|
||||
step = DIRENT_STEP_FILE;
|
||||
|
|
@ -1158,7 +1109,7 @@ found:
|
|||
ret = exfat_get_next_cluster(sb, &clu.dir);
|
||||
}
|
||||
|
||||
if (ret || clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
if (ret || clu.dir == EXFAT_EOF_CLUSTER) {
|
||||
/* just initialized hint_stat */
|
||||
hint_stat->clu = p_dir->dir;
|
||||
hint_stat->eidx = 0;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
#define EXFAT_SUPER_MAGIC 0x2011BAB0UL
|
||||
#define EXFAT_ROOT_INO 1
|
||||
|
||||
#define EXFAT_SB_DIRTY 0
|
||||
|
||||
#define EXFAT_CLUSTERS_UNTRACKED (~0u)
|
||||
|
||||
/*
|
||||
|
|
@ -71,10 +69,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
|
||||
|
|
@ -132,14 +128,14 @@ enum {
|
|||
|
||||
struct exfat_dentry_namebuf {
|
||||
char *lfn;
|
||||
int lfnbuf_len; /* usally MAX_UNINAME_BUF_SIZE */
|
||||
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
|
||||
};
|
||||
|
||||
/* unicode name structure */
|
||||
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 +166,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 {
|
||||
|
|
@ -230,8 +224,9 @@ struct exfat_sb_info {
|
|||
unsigned int num_FAT_sectors; /* num of FAT sectors */
|
||||
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 */
|
||||
unsigned int vol_flags; /* volume flags */
|
||||
unsigned int vol_flags_persistent; /* volume flags to retain */
|
||||
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 */
|
||||
|
|
@ -242,7 +237,6 @@ struct exfat_sb_info {
|
|||
unsigned int clu_srch_ptr; /* cluster search pointer */
|
||||
unsigned int used_clusters; /* number of used clusters */
|
||||
|
||||
unsigned long s_state;
|
||||
struct mutex s_lock; /* superblock lock */
|
||||
struct exfat_mount_options options;
|
||||
struct nls_table *nls_io; /* Charset used for input and display */
|
||||
|
|
@ -254,6 +248,8 @@ struct exfat_sb_info {
|
|||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
#define EXFAT_CACHE_VALID 0
|
||||
|
||||
/*
|
||||
* EXFAT file system inode in-memory data
|
||||
*/
|
||||
|
|
@ -269,8 +265,6 @@ struct exfat_inode_info {
|
|||
* the validation of hint_stat.
|
||||
*/
|
||||
unsigned int version;
|
||||
/* file offset or dentry index for readdir */
|
||||
loff_t rwoffset;
|
||||
|
||||
/* hint for cluster last accessed */
|
||||
struct exfat_hint hint_bmap;
|
||||
|
|
@ -375,7 +369,7 @@ static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
|
|||
static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi,
|
||||
unsigned int clus)
|
||||
{
|
||||
return ((clus - EXFAT_RESERVED_CLUSTERS) << sbi->sect_per_clus_bits) +
|
||||
return ((sector_t)(clus - EXFAT_RESERVED_CLUSTERS) << sbi->sect_per_clus_bits) +
|
||||
sbi->data_start_sector;
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +381,8 @@ static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
|||
}
|
||||
|
||||
/* super.c */
|
||||
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag);
|
||||
int exfat_set_volume_dirty(struct super_block *sb);
|
||||
int exfat_clear_volume_dirty(struct super_block *sb);
|
||||
|
||||
/* fatent.c */
|
||||
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
|
||||
|
|
@ -424,6 +419,7 @@ void exfat_truncate(struct inode *inode, loff_t size);
|
|||
int exfat_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
int exfat_getattr(const struct path *path, struct kstat *stat,
|
||||
unsigned int request_mask, unsigned int query_flags);
|
||||
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
|
||||
/* namei.c */
|
||||
extern const struct dentry_operations exfat_dentry_ops;
|
||||
|
|
@ -432,7 +428,6 @@ extern const struct dentry_operations exfat_utf8_dentry_ops;
|
|||
/* cache.c */
|
||||
int exfat_cache_init(void);
|
||||
void exfat_cache_shutdown(void);
|
||||
void exfat_cache_init_inode(struct inode *inode);
|
||||
void exfat_cache_inval_inode(struct inode *inode);
|
||||
int exfat_get_cluster(struct inode *inode, unsigned int cluster,
|
||||
unsigned int *fclus, unsigned int *dclus,
|
||||
|
|
@ -451,8 +446,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 +457,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);
|
||||
int 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 +488,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 +499,22 @@ 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);
|
||||
void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
|
||||
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 buffer_head *bh, int sync);
|
||||
int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync);
|
||||
void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
|
||||
unsigned int size, unsigned char flags);
|
||||
void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
|
||||
#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 VOLUME_DIRTY 0x0002
|
||||
#define MEDIA_FAILURE 0x0004
|
||||
|
||||
#define EXFAT_EOF_CLUSTER 0xFFFFFFFFu
|
||||
#define EXFAT_BAD_CLUSTER 0xFFFFFFF7u
|
||||
|
|
@ -55,7 +57,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 +71,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 +116,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;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
|
|||
|
||||
fat_entry = (__le32 *)&(bh->b_data[off]);
|
||||
*fat_entry = cpu_to_le32(content);
|
||||
exfat_update_bh(sb, bh, sb->s_flags & SB_SYNCHRONOUS);
|
||||
exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS);
|
||||
exfat_mirror_bh(sb, sec, bh);
|
||||
brelse(bh);
|
||||
return 0;
|
||||
|
|
@ -169,13 +169,11 @@ 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;
|
||||
}
|
||||
|
||||
set_bit(EXFAT_SB_DIRTY, &sbi->s_state);
|
||||
clu = p_chain->dir;
|
||||
|
||||
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
|
|
@ -231,21 +229,6 @@ int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int exfat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < nr_bhs; i++)
|
||||
write_dirty_buffer(bhs[i], 0);
|
||||
|
||||
for (i = 0; i < nr_bhs; i++) {
|
||||
wait_on_buffer(bhs[i]);
|
||||
if (!err && !buffer_uptodate(bhs[i]))
|
||||
err = -EIO;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
|
|
@ -267,46 +250,27 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
|
|||
}
|
||||
|
||||
/* Zeroing the unused blocks on this cluster */
|
||||
n = 0;
|
||||
while (blknr < last_blknr) {
|
||||
bhs[n] = sb_getblk(sb, blknr);
|
||||
if (!bhs[n]) {
|
||||
err = -ENOMEM;
|
||||
goto release_bhs;
|
||||
}
|
||||
memset(bhs[n]->b_data, 0, sb->s_blocksize);
|
||||
exfat_update_bh(sb, bhs[n], 0);
|
||||
|
||||
n++;
|
||||
blknr++;
|
||||
|
||||
if (n == nr_bhs) {
|
||||
if (IS_DIRSYNC(dir)) {
|
||||
err = exfat_sync_bhs(bhs, n);
|
||||
if (err)
|
||||
goto release_bhs;
|
||||
for (n = 0; n < nr_bhs && blknr < last_blknr; n++, blknr++) {
|
||||
bhs[n] = sb_getblk(sb, blknr);
|
||||
if (!bhs[n]) {
|
||||
err = -ENOMEM;
|
||||
goto release_bhs;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
brelse(bhs[i]);
|
||||
n = 0;
|
||||
memset(bhs[n]->b_data, 0, sb->s_blocksize);
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_DIRSYNC(dir)) {
|
||||
err = exfat_sync_bhs(bhs, n);
|
||||
err = exfat_update_bhs(bhs, n, IS_DIRSYNC(dir));
|
||||
if (err)
|
||||
goto release_bhs;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
brelse(bhs[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
brelse(bhs[i]);
|
||||
|
||||
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 +301,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 +312,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) {
|
||||
|
|
@ -361,8 +324,6 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
}
|
||||
}
|
||||
|
||||
set_bit(EXFAT_SB_DIRTY, &sbi->s_state);
|
||||
|
||||
p_chain->dir = EXFAT_EOF_CLUSTER;
|
||||
|
||||
while ((new_clu = exfat_find_free_bitmap(sb, hint_clu)) !=
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
|
@ -96,18 +97,16 @@ 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 */
|
||||
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
|
||||
return -EPERM;
|
||||
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
|
||||
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
|
||||
num_clusters_phys =
|
||||
|
|
@ -153,28 +152,32 @@ 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;
|
||||
int err;
|
||||
|
||||
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 = ep2->dentry.stream.valid_size;
|
||||
}
|
||||
|
||||
if (new_size == 0) {
|
||||
|
|
@ -185,10 +188,10 @@ 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);
|
||||
err = exfat_free_dentry_set(es, inode_needs_sync(inode));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* cut off from the FAT chain */
|
||||
|
|
@ -205,8 +208,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
|||
/* hint information */
|
||||
ei->hint_bmap.off = EXFAT_EOF_CLUSTER;
|
||||
ei->hint_bmap.clu = EXFAT_EOF_CLUSTER;
|
||||
if (ei->rwoffset > new_size)
|
||||
ei->rwoffset = new_size;
|
||||
|
||||
/* hint_stat will be used if this is directory. */
|
||||
ei->hint_stat.eidx = 0;
|
||||
|
|
@ -217,7 +218,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
|||
if (exfat_free_cluster(inode, &clu))
|
||||
return -EIO;
|
||||
|
||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
||||
exfat_clear_volume_dirty(sb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -226,7 +227,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
|
|||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
unsigned int blocksize = 1 << inode->i_blkbits;
|
||||
unsigned int blocksize = i_blocksize(inode);
|
||||
loff_t aligned_size;
|
||||
int err;
|
||||
|
||||
|
|
@ -273,6 +274,7 @@ int exfat_getattr(const struct path *path, struct kstat *stat,
|
|||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
exfat_truncate_atime(&stat->atime);
|
||||
stat->result_mask |= STATX_BTIME;
|
||||
stat->btime.tv_sec = ei->i_crtime.tv_sec;
|
||||
stat->btime.tv_nsec = ei->i_crtime.tv_nsec;
|
||||
|
|
@ -339,19 +341,37 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
}
|
||||
|
||||
setattr_copy(inode, attr);
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
int err;
|
||||
|
||||
err = __generic_file_fsync(filp, start, end, datasync);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sync_blockdev(inode->i_sb->s_bdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
|
||||
}
|
||||
|
||||
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 = exfat_file_fsync,
|
||||
.splice_read = generic_file_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
};
|
||||
|
||||
const struct inode_operations exfat_file_inode_operations = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -40,14 +39,14 @@ static int __exfat_write_inode(struct inode *inode, int sync)
|
|||
if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1)
|
||||
return 0;
|
||||
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
|
||||
/* 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,8 @@ 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);
|
||||
return exfat_free_dentry_set(es, sync);
|
||||
}
|
||||
|
||||
int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
|
|
@ -110,16 +108,12 @@ 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);
|
||||
unsigned int local_clu_offset = clu_offset;
|
||||
unsigned int num_to_be_allocated = 0, num_clusters = 0;
|
||||
|
||||
ei->rwoffset = EXFAT_CLU_TO_B(clu_offset, sbi);
|
||||
|
||||
if (EXFAT_I(inode)->i_size_ondisk > 0)
|
||||
num_clusters =
|
||||
EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk,
|
||||
|
|
@ -171,7 +165,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
|||
}
|
||||
|
||||
if (*clu == EXFAT_EOF_CLUSTER) {
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
|
||||
new_clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
|
||||
EXFAT_EOF_CLUSTER : last_clu + 1;
|
||||
|
|
@ -222,35 +216,31 @@ 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;
|
||||
int err;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
exfat_update_dir_chksum_with_entry_set(es);
|
||||
err = exfat_free_dentry_set(es, inode_needs_sync(inode));
|
||||
if (err)
|
||||
return err;
|
||||
} /* end of if != DIR_DELETED */
|
||||
|
||||
inode->i_blocks +=
|
||||
|
|
@ -565,7 +555,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
|||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
loff_t size = info->size;
|
||||
|
||||
memcpy(&ei->dir, &info->dir, sizeof(struct exfat_chain));
|
||||
ei->dir = info->dir;
|
||||
ei->entry = info->entry;
|
||||
ei->attr = info->attr;
|
||||
ei->start_clu = info->start_clu;
|
||||
|
|
@ -576,7 +566,6 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
|||
ei->hint_stat.eidx = 0;
|
||||
ei->hint_stat.clu = info->start_clu;
|
||||
ei->hint_femp.eidx = EXFAT_HINT_NONE;
|
||||
ei->rwoffset = 0;
|
||||
ei->hint_bmap.off = EXFAT_EOF_CLUSTER;
|
||||
ei->i_pos = 0;
|
||||
|
||||
|
|
@ -620,8 +609,6 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
|||
ei->i_crtime = info->crtime;
|
||||
inode->i_atime = info->atime;
|
||||
|
||||
exfat_cache_init_inode(inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,11 +84,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. */
|
||||
|
|
@ -100,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;
|
||||
|
|
@ -112,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);
|
||||
|
||||
/*
|
||||
|
|
@ -124,24 +125,46 @@ 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)
|
||||
/*
|
||||
* The timestamp for access_time has double seconds granularity.
|
||||
* (There is no 10msIncrement field for access_time unlike create/modify_time)
|
||||
* atime also 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;
|
||||
}
|
||||
|
||||
void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
|
||||
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;
|
||||
}
|
||||
|
||||
void exfat_update_bh(struct buffer_head *bh, int sync)
|
||||
{
|
||||
set_bit(EXFAT_SB_DIRTY, &EXFAT_SB(sb)->s_state);
|
||||
set_buffer_uptodate(bh);
|
||||
mark_buffer_dirty(bh);
|
||||
|
||||
|
|
@ -149,6 +172,25 @@ void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
|
|||
sync_dirty_buffer(bh);
|
||||
}
|
||||
|
||||
int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < nr_bhs; i++) {
|
||||
set_buffer_uptodate(bhs[i]);
|
||||
mark_buffer_dirty(bhs[i]);
|
||||
if (sync)
|
||||
write_dirty_buffer(bhs[i], 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_bhs && sync; i++) {
|
||||
wait_on_buffer(bhs[i]);
|
||||
if (!buffer_uptodate(bhs[i]))
|
||||
err = -EIO;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
|
||||
unsigned int size, unsigned char flags)
|
||||
{
|
||||
|
|
|
|||
257
fs/exfat/namei.c
257
fs/exfat/namei.c
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -301,7 +290,7 @@ static int exfat_check_max_dentries(struct inode *inode)
|
|||
{
|
||||
if (EXFAT_B_TO_DEN(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) {
|
||||
/*
|
||||
* exFAT spec allows a dir to grow upto 8388608(256MB)
|
||||
* exFAT spec allows a dir to grow up to 8388608(256MB)
|
||||
* dentries
|
||||
*/
|
||||
return -ENOSPC;
|
||||
|
|
@ -329,8 +318,7 @@ static int exfat_find_empty_entry(struct inode *inode,
|
|||
hint_femp.eidx = EXFAT_HINT_NONE;
|
||||
|
||||
if (ei->hint_femp.eidx != EXFAT_HINT_NONE) {
|
||||
memcpy(&hint_femp, &ei->hint_femp,
|
||||
sizeof(struct exfat_hint_femp));
|
||||
hint_femp = ei->hint_femp;
|
||||
ei->hint_femp.eidx = EXFAT_HINT_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -398,7 +386,7 @@ static int exfat_find_empty_entry(struct inode *inode,
|
|||
ep->dentry.stream.valid_size = cpu_to_le64(size);
|
||||
ep->dentry.stream.size = ep->dentry.stream.valid_size;
|
||||
ep->dentry.stream.flags = p_dir->flags;
|
||||
exfat_update_bh(sb, bh, IS_DIRSYNC(inode));
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
if (exfat_update_dir_chksum(inode, &(ei->dir),
|
||||
ei->entry))
|
||||
|
|
@ -530,7 +518,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
memcpy(&info->dir, p_dir, sizeof(struct exfat_chain));
|
||||
info->dir = *p_dir;
|
||||
info->entry = dentry;
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->type = type;
|
||||
|
|
@ -541,19 +529,10 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
|||
info->size = 0;
|
||||
info->num_subdirs = 0;
|
||||
} else {
|
||||
int count;
|
||||
struct exfat_chain cdir;
|
||||
|
||||
info->attr = ATTR_SUBDIR;
|
||||
info->start_clu = start_clu;
|
||||
info->size = clu_size;
|
||||
|
||||
exfat_chain_set(&cdir, info->start_clu,
|
||||
EXFAT_B_TO_CLU(info->size, sbi), info->flags);
|
||||
count = exfat_count_dir_entries(sb, &cdir);
|
||||
if (count < 0)
|
||||
return -EIO;
|
||||
info->num_subdirs = count + EXFAT_MIN_SUBDIR;
|
||||
info->num_subdirs = EXFAT_MIN_SUBDIR;
|
||||
}
|
||||
memset(&info->crtime, 0, sizeof(info->crtime));
|
||||
memset(&info->mtime, 0, sizeof(info->mtime));
|
||||
|
|
@ -573,10 +552,10 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
int err;
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
|
||||
&info);
|
||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
||||
exfat_clear_volume_dirty(sb);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -589,12 +568,14 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
|
||||
i_pos = exfat_make_i_pos(&info);
|
||||
inode = exfat_build_inode(sb, &info, i_pos);
|
||||
if (IS_ERR(inode))
|
||||
err = PTR_ERR_OR_ZERO(inode);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
inode_inc_iversion(inode);
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = current_time(inode);
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
|
|
@ -610,11 +591,11 @@ 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);
|
||||
struct exfat_dentry *ep, *ep2;
|
||||
struct exfat_entry_set_cache *es;
|
||||
|
||||
if (qname->len == 0)
|
||||
return -ENOENT;
|
||||
|
|
@ -640,86 +621,63 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
|||
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name,
|
||||
num_entries, TYPE_ALL);
|
||||
|
||||
if ((dentry < 0) && (dentry != -EEXIST))
|
||||
if (dentry < 0)
|
||||
return dentry; /* -error value */
|
||||
|
||||
memcpy(&info->dir, &cdir.dir, sizeof(struct exfat_chain));
|
||||
info->dir = cdir;
|
||||
info->entry = dentry;
|
||||
info->num_subdirs = 0;
|
||||
|
||||
/* root directory itself */
|
||||
if (unlikely(dentry == -EEXIST)) {
|
||||
int num_clu = 0;
|
||||
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES);
|
||||
if (!es)
|
||||
return -EIO;
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep2 = exfat_get_dentry_cached(es, 1);
|
||||
|
||||
info->type = TYPE_DIR;
|
||||
info->attr = ATTR_SUBDIR;
|
||||
info->flags = ALLOC_FAT_CHAIN;
|
||||
info->start_clu = sbi->root_dir;
|
||||
memset(&info->crtime, 0, sizeof(info->crtime));
|
||||
memset(&info->mtime, 0, sizeof(info->mtime));
|
||||
memset(&info->atime, 0, sizeof(info->atime));
|
||||
info->type = exfat_get_entry_type(ep);
|
||||
info->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
|
||||
if ((info->type == TYPE_FILE) && (info->size == 0)) {
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
} else {
|
||||
info->flags = ep2->dentry.stream.flags;
|
||||
info->start_clu =
|
||||
le32_to_cpu(ep2->dentry.stream.start_clu);
|
||||
}
|
||||
|
||||
exfat_chain_set(&cdir, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
|
||||
if (exfat_count_num_clusters(sb, &cdir, &num_clu))
|
||||
return -EIO;
|
||||
info->size = num_clu << sbi->cluster_size_bits;
|
||||
exfat_get_entry_time(sbi, &info->crtime,
|
||||
ep->dentry.file.create_tz,
|
||||
ep->dentry.file.create_time,
|
||||
ep->dentry.file.create_date,
|
||||
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_cs);
|
||||
exfat_get_entry_time(sbi, &info->atime,
|
||||
ep->dentry.file.access_tz,
|
||||
ep->dentry.file.access_time,
|
||||
ep->dentry.file.access_date,
|
||||
0);
|
||||
exfat_free_dentry_set(es, false);
|
||||
|
||||
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
|
||||
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);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (info->type == TYPE_DIR) {
|
||||
exfat_chain_set(&cdir, info->start_clu,
|
||||
EXFAT_B_TO_CLU(info->size, sbi), info->flags);
|
||||
count = exfat_count_dir_entries(sb, &cdir);
|
||||
if (count < 0)
|
||||
return -EIO;
|
||||
|
||||
info->num_subdirs = count;
|
||||
} else {
|
||||
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES, &ep);
|
||||
if (!es)
|
||||
return -EIO;
|
||||
ep2 = ep + 1;
|
||||
|
||||
info->type = exfat_get_entry_type(ep);
|
||||
info->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
|
||||
if ((info->type == TYPE_FILE) && (info->size == 0)) {
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
} else {
|
||||
info->flags = ep2->dentry.stream.flags;
|
||||
info->start_clu =
|
||||
le32_to_cpu(ep2->dentry.stream.start_clu);
|
||||
}
|
||||
|
||||
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
|
||||
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);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
exfat_get_entry_time(sbi, &info->crtime,
|
||||
ep->dentry.file.create_tz,
|
||||
ep->dentry.file.create_time,
|
||||
ep->dentry.file.create_date,
|
||||
ep->dentry.file.create_time_ms);
|
||||
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);
|
||||
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);
|
||||
|
||||
if (info->type == TYPE_DIR) {
|
||||
exfat_chain_set(&cdir, info->start_clu,
|
||||
EXFAT_B_TO_CLU(info->size, sbi), info->flags);
|
||||
count = exfat_count_dir_entries(sb, &cdir);
|
||||
if (count < 0)
|
||||
return -EIO;
|
||||
|
||||
info->num_subdirs = count + EXFAT_MIN_SUBDIR;
|
||||
}
|
||||
info->num_subdirs = count + EXFAT_MIN_SUBDIR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -752,10 +710,9 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
i_pos = exfat_make_i_pos(&info);
|
||||
inode = exfat_build_inode(sb, &info, i_pos);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
err = PTR_ERR_OR_ZERO(inode);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
i_mode = inode->i_mode;
|
||||
alias = d_find_alias(inode);
|
||||
|
|
@ -776,8 +733,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)) {
|
||||
|
|
@ -822,7 +779,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;
|
||||
}
|
||||
|
|
@ -841,7 +798,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
|||
num_entries++;
|
||||
brelse(bh);
|
||||
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
/* update the directory entry */
|
||||
if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
|
||||
err = -EIO;
|
||||
|
|
@ -850,10 +807,11 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
|||
|
||||
/* This doesn't modify ei */
|
||||
ei->dir.dir = DIR_DELETED;
|
||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
||||
exfat_clear_volume_dirty(sb);
|
||||
|
||||
inode_inc_iversion(dir);
|
||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||
exfat_truncate_atime(&dir->i_atime);
|
||||
if (IS_DIRSYNC(dir))
|
||||
exfat_sync_inode(dir);
|
||||
else
|
||||
|
|
@ -861,6 +819,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
|||
|
||||
clear_nlink(inode);
|
||||
inode->i_mtime = inode->i_atime = current_time(inode);
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
exfat_unhash_inode(inode);
|
||||
exfat_d_version_set(dentry, inode_query_iversion(dir));
|
||||
unlock:
|
||||
|
|
@ -878,10 +837,10 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
int err;
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
|
||||
&info);
|
||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
||||
exfat_clear_volume_dirty(sb);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -895,14 +854,14 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
|
||||
i_pos = exfat_make_i_pos(&info);
|
||||
inode = exfat_build_inode(sb, &info, i_pos);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
err = PTR_ERR_OR_ZERO(inode);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
inode_inc_iversion(inode);
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = current_time(inode);
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
|
|
@ -974,21 +933,19 @@ 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;
|
||||
}
|
||||
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_chain_set(&clu_to_free, ei->start_clu,
|
||||
EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi), ei->flags);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1007,18 +964,18 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
num_entries++;
|
||||
brelse(bh);
|
||||
|
||||
exfat_set_volume_dirty(sb);
|
||||
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;
|
||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
||||
exfat_clear_volume_dirty(sb);
|
||||
|
||||
inode_inc_iversion(dir);
|
||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||
exfat_truncate_atime(&dir->i_atime);
|
||||
if (IS_DIRSYNC(dir))
|
||||
exfat_sync_inode(dir);
|
||||
else
|
||||
|
|
@ -1027,6 +984,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
|
||||
clear_nlink(inode);
|
||||
inode->i_mtime = inode->i_atime = current_time(inode);
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
exfat_unhash_inode(inode);
|
||||
exfat_d_version_set(dentry, inode_query_iversion(dir));
|
||||
unlock:
|
||||
|
|
@ -1071,24 +1029,28 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
|||
if (!epnew)
|
||||
return -EIO;
|
||||
|
||||
memcpy(epnew, epold, DENTRY_SIZE);
|
||||
*epnew = *epold;
|
||||
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
|
||||
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(sb, new_bh, sync);
|
||||
exfat_update_bh(new_bh, sync);
|
||||
brelse(old_bh);
|
||||
brelse(new_bh);
|
||||
|
||||
epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh,
|
||||
§or_old);
|
||||
if (!epold)
|
||||
return -EIO;
|
||||
epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh,
|
||||
§or_new);
|
||||
if (!epold || !epnew)
|
||||
if (!epnew) {
|
||||
brelse(old_bh);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(epnew, epold, DENTRY_SIZE);
|
||||
exfat_update_bh(sb, new_bh, sync);
|
||||
*epnew = *epold;
|
||||
exfat_update_bh(new_bh, sync);
|
||||
brelse(old_bh);
|
||||
brelse(new_bh);
|
||||
|
||||
|
|
@ -1105,7 +1067,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
|||
epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(sb, old_bh, sync);
|
||||
exfat_update_bh(old_bh, sync);
|
||||
brelse(old_bh);
|
||||
ret = exfat_init_ext_entry(inode, p_dir, oldentry,
|
||||
num_new_entries, p_uniname);
|
||||
|
|
@ -1132,11 +1094,6 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
|
|||
if (!epmov)
|
||||
return -EIO;
|
||||
|
||||
/* check if the source and target directory is the same */
|
||||
if (exfat_get_entry_type(epmov) == TYPE_DIR &&
|
||||
le32_to_cpu(epmov->dentry.stream.start_clu) == p_newdir->dir)
|
||||
return -EINVAL;
|
||||
|
||||
num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry,
|
||||
epmov);
|
||||
if (num_old_entries < 0)
|
||||
|
|
@ -1155,24 +1112,28 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
|
|||
if (!epnew)
|
||||
return -EIO;
|
||||
|
||||
memcpy(epnew, epmov, DENTRY_SIZE);
|
||||
*epnew = *epmov;
|
||||
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
|
||||
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(sb, new_bh, IS_DIRSYNC(inode));
|
||||
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
|
||||
brelse(mov_bh);
|
||||
brelse(new_bh);
|
||||
|
||||
epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh,
|
||||
§or_mov);
|
||||
if (!epmov)
|
||||
return -EIO;
|
||||
epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh,
|
||||
§or_new);
|
||||
if (!epmov || !epnew)
|
||||
if (!epnew) {
|
||||
brelse(mov_bh);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(epnew, epmov, DENTRY_SIZE);
|
||||
exfat_update_bh(sb, new_bh, IS_DIRSYNC(inode));
|
||||
*epnew = *epmov;
|
||||
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
|
||||
brelse(mov_bh);
|
||||
brelse(new_bh);
|
||||
|
||||
|
|
@ -1238,8 +1199,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;
|
||||
}
|
||||
|
||||
|
|
@ -1261,8 +1221,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;
|
||||
}
|
||||
|
||||
|
|
@ -1299,7 +1258,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
||||
exfat_set_volume_dirty(sb);
|
||||
|
||||
if (olddir.dir == newdir.dir)
|
||||
ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
|
||||
|
|
@ -1354,7 +1313,7 @@ del_out:
|
|||
*/
|
||||
new_ei->dir.dir = DIR_DELETED;
|
||||
}
|
||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
||||
exfat_clear_volume_dirty(sb);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1387,6 +1346,7 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
inode_inc_iversion(new_dir);
|
||||
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
|
||||
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
|
||||
exfat_truncate_atime(&new_dir->i_atime);
|
||||
if (IS_DIRSYNC(new_dir))
|
||||
exfat_sync_inode(new_dir);
|
||||
else
|
||||
|
|
@ -1423,8 +1383,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 =
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
||||
/* Upcase tabel macro */
|
||||
/* Upcase table macro */
|
||||
#define EXFAT_NUM_UPCASE (2918)
|
||||
#define UTBL_COUNT (0x10000)
|
||||
|
||||
|
|
@ -495,7 +495,7 @@ static int exfat_utf8_to_utf16(struct super_block *sb,
|
|||
struct exfat_uni_name *p_uniname, int *p_lossy)
|
||||
{
|
||||
int i, unilen, lossy = NLS_NAME_NO_LOSSY;
|
||||
unsigned short upname[MAX_NAME_LENGTH + 1];
|
||||
__le16 upname[MAX_NAME_LENGTH + 1];
|
||||
unsigned short *uniname = p_uniname->name;
|
||||
|
||||
WARN_ON(!len);
|
||||
|
|
@ -503,33 +503,29 @@ 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))
|
||||
lossy |= NLS_NAME_LOSSY;
|
||||
|
||||
upname[i] = exfat_toupper(sb, *uniname);
|
||||
upname[i] = cpu_to_le16(exfat_toupper(sb, *uniname));
|
||||
uniname++;
|
||||
}
|
||||
|
||||
*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,
|
||||
|
|
@ -614,7 +597,7 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
|
|||
struct exfat_uni_name *p_uniname, int *p_lossy)
|
||||
{
|
||||
int i = 0, unilen = 0, lossy = NLS_NAME_NO_LOSSY;
|
||||
unsigned short upname[MAX_NAME_LENGTH + 1];
|
||||
__le16 upname[MAX_NAME_LENGTH + 1];
|
||||
unsigned short *uniname = p_uniname->name;
|
||||
struct nls_table *nls = EXFAT_SB(sb)->nls_io;
|
||||
|
||||
|
|
@ -628,7 +611,7 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
|
|||
exfat_wstrchr(bad_uni_chars, *uniname))
|
||||
lossy |= NLS_NAME_LOSSY;
|
||||
|
||||
upname[unilen] = exfat_toupper(sb, *uniname);
|
||||
upname[unilen] = cpu_to_le16(exfat_toupper(sb, *uniname));
|
||||
uniname++;
|
||||
unilen++;
|
||||
}
|
||||
|
|
@ -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,12 +653,13 @@ 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;
|
||||
|
||||
upcase_table = kcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
|
||||
upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
|
||||
if (!upcase_table)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -739,7 +715,7 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
|
|||
unsigned short uni = 0, *upcase_table;
|
||||
unsigned int index = 0;
|
||||
|
||||
upcase_table = kcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
|
||||
upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
|
||||
if (!upcase_table)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -827,5 +803,5 @@ load_default:
|
|||
|
||||
void exfat_free_upcase_table(struct exfat_sb_info *sbi)
|
||||
{
|
||||
kfree(sbi->vol_utbl);
|
||||
}
|
||||
kvfree(sbi->vol_utbl);
|
||||
}
|
||||
|
|
|
|||
343
fs/exfat/super.c
343
fs/exfat/super.c
|
|
@ -45,10 +45,8 @@ static void exfat_put_super(struct super_block *sb)
|
|||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
mutex_lock(&sbi->s_lock);
|
||||
if (test_and_clear_bit(EXFAT_SB_DIRTY, &sbi->s_state))
|
||||
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);
|
||||
|
|
@ -59,13 +57,14 @@ static int exfat_sync_fs(struct super_block *sb, int wait)
|
|||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int err = 0;
|
||||
|
||||
if (!wait)
|
||||
return 0;
|
||||
|
||||
/* If there are some dirty buffers in the bdev inode */
|
||||
mutex_lock(&sbi->s_lock);
|
||||
if (test_and_clear_bit(EXFAT_SB_DIRTY, &sbi->s_state)) {
|
||||
sync_blockdev(sb->s_bdev);
|
||||
if (exfat_set_vol_flags(sb, VOL_CLEAN))
|
||||
err = -EIO;
|
||||
}
|
||||
sync_blockdev(sb->s_bdev);
|
||||
if (exfat_clear_volume_dirty(sb))
|
||||
err = -EIO;
|
||||
mutex_unlock(&sbi->s_lock);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -97,17 +96,20 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
|
||||
static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
|
||||
{
|
||||
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;
|
||||
|
||||
/* retain persistent-flags */
|
||||
new_flags |= sbi->vol_flags_persistent;
|
||||
|
||||
/* flags are not changed */
|
||||
if (sbi->vol_flag == new_flag)
|
||||
if (sbi->vol_flags == new_flags)
|
||||
return 0;
|
||||
|
||||
sbi->vol_flag = new_flag;
|
||||
sbi->vol_flags = new_flags;
|
||||
|
||||
/* skip updating volume dirty flag,
|
||||
* if this volume has been mounted with read-only
|
||||
|
|
@ -115,30 +117,35 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
|
|||
if (sb_rdonly(sb))
|
||||
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_flags);
|
||||
|
||||
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_flags & VOLUME_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;
|
||||
}
|
||||
|
||||
int exfat_set_volume_dirty(struct super_block *sb)
|
||||
{
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
return exfat_set_vol_flags(sb, sbi->vol_flags | VOLUME_DIRTY);
|
||||
}
|
||||
|
||||
int exfat_clear_volume_dirty(struct super_block *sb)
|
||||
{
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
return exfat_set_vol_flags(sb, sbi->vol_flags & ~VOLUME_DIRTY);
|
||||
}
|
||||
|
||||
static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
||||
{
|
||||
struct super_block *sb = root->d_sb;
|
||||
|
|
@ -159,7 +166,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)
|
||||
|
|
@ -274,9 +280,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;
|
||||
|
|
@ -324,7 +329,6 @@ static int exfat_read_root(struct inode *inode)
|
|||
ei->flags = ALLOC_FAT_CHAIN;
|
||||
ei->type = TYPE_DIR;
|
||||
ei->version = 0;
|
||||
ei->rwoffset = 0;
|
||||
ei->hint_bmap.off = EXFAT_EOF_CLUSTER;
|
||||
ei->hint_stat.eidx = 0;
|
||||
ei->hint_stat.clu = sbi->root_dir;
|
||||
|
|
@ -357,159 +361,213 @@ static int exfat_read_root(struct inode *inode)
|
|||
exfat_save_attr(inode, ATTR_SUBDIR);
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
|
||||
current_time(inode);
|
||||
exfat_cache_init_inode(inode);
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
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_flags = le16_to_cpu(p_boot->vol_flags);
|
||||
sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE);
|
||||
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_flags & VOLUME_DIRTY)
|
||||
exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
|
||||
if (sbi->vol_flags & MEDIA_FAILURE)
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -520,7 +578,7 @@ free_alloc_bitmap:
|
|||
free_upcase_table:
|
||||
exfat_free_upcase_table(sbi);
|
||||
free_bh:
|
||||
brelse(bh);
|
||||
brelse(sbi->boot_bh);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -537,23 +595,23 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sb->s_flags |= SB_NODIRATIME;
|
||||
sb->s_magic = EXFAT_SUPER_MAGIC;
|
||||
sb->s_op = &exfat_sops;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -565,8 +623,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;
|
||||
}
|
||||
|
|
@ -579,7 +637,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;
|
||||
}
|
||||
|
|
@ -588,7 +646,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;
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +655,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;
|
||||
}
|
||||
|
|
@ -611,6 +669,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);
|
||||
|
|
@ -627,13 +686,28 @@ 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 int exfat_reconfigure(struct fs_context *fc)
|
||||
{
|
||||
fc->sb_flags |= SB_NODIRATIME;
|
||||
|
||||
/* volume flag will be updated in exfat_sync_fs */
|
||||
sync_filesystem(fc->root->d_sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fs_context_operations exfat_context_ops = {
|
||||
.parse_param = exfat_parse_param,
|
||||
.get_tree = exfat_get_tree,
|
||||
.free = exfat_free,
|
||||
.reconfigure = exfat_reconfigure,
|
||||
};
|
||||
|
||||
static int exfat_init_fs_context(struct fs_context *fc)
|
||||
|
|
@ -674,6 +748,10 @@ static void exfat_inode_init_once(void *foo)
|
|||
{
|
||||
struct exfat_inode_info *ei = (struct exfat_inode_info *)foo;
|
||||
|
||||
spin_lock_init(&ei->cache_lru_lock);
|
||||
ei->nr_caches = 0;
|
||||
ei->cache_valid_id = EXFAT_CACHE_VALID + 1;
|
||||
INIT_LIST_HEAD(&ei->cache_lru);
|
||||
INIT_HLIST_NODE(&ei->i_hash_fat);
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
|
|
@ -723,6 +801,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.");
|
||||
MODULE_AUTHOR("Samsung Electronics Co., Ltd.");
|
||||
|
|
|
|||
Loading…
Reference in a new issue