Description for this pull request:
- simplify and remove some redundant codes handing directory entries - optimize the size of exfat_entry_set_cache and its allocation policy - improve the performance for creating files and directories -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmObqEYWHGxpbmtpbmpl b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCOOuEACuu267tIhg/QNZosz3hXsb3rEV 0sLLlx9IlzQTQlZcKclUko2ow04SdmvcNA5pc500S3GJBiZXHF5Tu+Xqh5NcnNNw J+4xCX7g1hdz5fclTWM2lwRXcYPM+qwEwmyQSDwOw6vmK116fXWGbMag9Obd0tCf YtCrLfO3XvOOR/WnPeGWuQGkB2ygDi2+fAjC9ULn9iBmoXKims46YQUZo0OyNbuO yEXQN8oN3+dQOqzUm6ZDt1c90eDhyWbewdKz6J/0tS3D7poQNmqRjKAAUgn0M/Aj le3213oMTHP/lnW50BDrG3BuIGMENRbZFsWg+BG2w4lw5FGtQVaP5/U6HcSyNJNG 7I0ca8XJsAuU63Fb53j8uVjKuyVpc/kPMc+dd5PWoliKfokQu5ZFk6pCPnkLlsIR qrs1JUD1Vna4RZhQ9VyvVP7ghAhX+pOwwofh/TdQGdDPWBnBmdsHqOjWot+X2Sed OVbjcgN5EX0ba/VDdyFhz6JR+TyoC7YWLD0sTXtr2AlFmTSoA0oBYXuaWaSs6ZgU e4Fm8F2WlWwFaUv1pea0xn1yBBjkOdrS2xhni2JVp7nEXg7qFKID732/BcCFVTVo 2YgNLqE7teEu3RAvKQl6EwDqVXBA760jIQldQZBsX98q3ShlZ6MjV2Bt0AAHvQsN VvJ/PCKV8h2IP7LH8w== =sCw6 -----END PGP SIGNATURE----- Merge tag 'exfat-for-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat Pull exfat update from Namjae Jeon: - simplify and remove some redundant directory entry code - optimize the size of exfat_entry_set_cache and its allocation policy - improve the performance for creating files and directories * tag 'exfat-for-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: reuse exfat_find_location() to simplify exfat_get_dentry_set() exfat: fix overflow in sector and cluster conversion exfat: remove i_size_write() from __exfat_truncate() exfat: remove argument 'size' from exfat_truncate() exfat: remove unnecessary arguments from exfat_find_dir_entry() exfat: remove unneeded codes from __exfat_rename() exfat: remove call ilog2() from exfat_readdir() exfat: replace magic numbers with Macros exfat: rename exfat_free_dentry_set() to exfat_put_dentry_set() exfat: move exfat_entry_set_cache from heap to stack exfat: support dynamic allocate bh for exfat_entry_set_cache exfat: reduce the size of exfat_entry_set_cache exfat: hint the empty entry which at the end of cluster chain exfat: simplify empty entry hint
This commit is contained in:
commit
84e57d2922
184
fs/exfat/dir.c
184
fs/exfat/dir.c
|
@ -33,10 +33,9 @@ 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_entry_set_cache *es;
|
||||
struct exfat_entry_set_cache es;
|
||||
|
||||
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
|
||||
if (!es)
|
||||
if (exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -45,8 +44,8 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
|||
* 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++) {
|
||||
struct exfat_dentry *ep = exfat_get_dentry_cached(es, i);
|
||||
for (i = ES_IDX_FIRST_FILENAME; 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)
|
||||
|
@ -56,13 +55,13 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
|||
uniname += EXFAT_FILE_NAME_LEN;
|
||||
}
|
||||
|
||||
exfat_free_dentry_set(es, false);
|
||||
exfat_put_dentry_set(&es, false);
|
||||
}
|
||||
|
||||
/* read a directory entry from the opened directory */
|
||||
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, num_ext;
|
||||
int i, dentries_per_clu, num_ext;
|
||||
unsigned int type, clu_offset, max_dentries;
|
||||
struct exfat_chain dir, clu;
|
||||
struct exfat_uni_name uni_name;
|
||||
|
@ -84,11 +83,10 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
|||
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
dentries_per_clu_bits = ilog2(dentries_per_clu);
|
||||
max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
|
||||
(u64)sbi->num_clusters << dentries_per_clu_bits);
|
||||
(u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi));
|
||||
|
||||
clu_offset = dentry >> dentries_per_clu_bits;
|
||||
clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
|
||||
exfat_chain_dup(&clu, &dir);
|
||||
|
||||
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
|
||||
|
@ -163,7 +161,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
|||
dir_entry->entry = dentry;
|
||||
brelse(bh);
|
||||
|
||||
ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
|
||||
ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi);
|
||||
ei->hint_bmap.clu = clu.dir;
|
||||
|
||||
*cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext);
|
||||
|
@ -337,7 +335,7 @@ int exfat_calc_num_entries(struct exfat_uni_name *p_uniname)
|
|||
return -EINVAL;
|
||||
|
||||
/* 1 file entry + 1 stream entry + name entries */
|
||||
return ((len - 1) / EXFAT_FILE_NAME_LEN + 3);
|
||||
return ES_ENTRY_NUM(len);
|
||||
}
|
||||
|
||||
unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
|
||||
|
@ -592,18 +590,18 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
|
|||
unsigned short chksum = 0;
|
||||
struct exfat_dentry *ep;
|
||||
|
||||
for (i = 0; i < es->num_entries; i++) {
|
||||
for (i = ES_IDX_FILE; 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 = exfat_get_dentry_cached(es, ES_IDX_FILE);
|
||||
ep->dentry.file.checksum = cpu_to_le16(chksum);
|
||||
es->modified = true;
|
||||
}
|
||||
|
||||
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
||||
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
|
@ -615,7 +613,10 @@ int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
|||
bforget(es->bh[i]);
|
||||
else
|
||||
brelse(es->bh[i]);
|
||||
kfree(es);
|
||||
|
||||
if (IS_DYNAMIC_ES(es))
|
||||
kfree(es->bh);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -812,14 +813,14 @@ struct exfat_dentry *exfat_get_dentry_cached(
|
|||
* 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)
|
||||
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
|
||||
struct super_block *sb, struct exfat_chain *p_dir, int entry,
|
||||
unsigned int type)
|
||||
{
|
||||
int ret, i, num_bh;
|
||||
unsigned int off, byte_offset, clu = 0;
|
||||
unsigned int off;
|
||||
sector_t sec;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_entry_set_cache *es;
|
||||
struct exfat_dentry *ep;
|
||||
int num_entries;
|
||||
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
|
||||
|
@ -827,52 +828,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
|
|||
|
||||
if (p_dir->dir == DIR_DELETED) {
|
||||
exfat_err(sb, "access to deleted dentry");
|
||||
return NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
byte_offset = EXFAT_DEN_TO_B(entry);
|
||||
ret = exfat_walk_fat_chain(sb, p_dir, byte_offset, &clu);
|
||||
ret = exfat_find_location(sb, p_dir, entry, &sec, &off);
|
||||
if (ret)
|
||||
return NULL;
|
||||
return ret;
|
||||
|
||||
es = kzalloc(sizeof(*es), GFP_KERNEL);
|
||||
if (!es)
|
||||
return NULL;
|
||||
memset(es, 0, sizeof(*es));
|
||||
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);
|
||||
sec += exfat_cluster_to_sector(sbi, clu);
|
||||
es->bh = es->__bh;
|
||||
|
||||
bh = sb_bread(sb, sec);
|
||||
if (!bh)
|
||||
goto free_es;
|
||||
return -EIO;
|
||||
es->bh[es->num_bh++] = bh;
|
||||
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
|
||||
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
|
||||
num_entries = type == ES_ALL_ENTRIES ?
|
||||
ep->dentry.file.num_ext + 1 : type;
|
||||
es->num_entries = num_entries;
|
||||
|
||||
num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
|
||||
if (num_bh > ARRAY_SIZE(es->__bh)) {
|
||||
es->bh = kmalloc_array(num_bh, sizeof(*es->bh), GFP_KERNEL);
|
||||
if (!es->bh) {
|
||||
brelse(bh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
es->bh[0] = bh;
|
||||
}
|
||||
|
||||
for (i = 1; i < num_bh; i++) {
|
||||
/* get the next sector */
|
||||
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
|
||||
unsigned int clu = exfat_sector_to_cluster(sbi, sec);
|
||||
|
||||
if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
|
||||
clu++;
|
||||
else if (exfat_get_next_cluster(sb, &clu))
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
sec = exfat_cluster_to_sector(sbi, clu);
|
||||
} else {
|
||||
sec++;
|
||||
|
@ -880,21 +880,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
|
|||
|
||||
bh = sb_bread(sb, sec);
|
||||
if (!bh)
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
es->bh[es->num_bh++] = bh;
|
||||
}
|
||||
|
||||
/* validate cached dentries */
|
||||
for (i = 1; i < num_entries; i++) {
|
||||
for (i = ES_IDX_STREAM; i < num_entries; i++) {
|
||||
ep = exfat_get_dentry_cached(es, i);
|
||||
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
}
|
||||
return es;
|
||||
return 0;
|
||||
|
||||
free_es:
|
||||
exfat_free_dentry_set(es, false);
|
||||
return NULL;
|
||||
put_es:
|
||||
exfat_put_dentry_set(es, false);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static inline void exfat_reset_empty_hint(struct exfat_hint_femp *hint_femp)
|
||||
{
|
||||
hint_femp->eidx = EXFAT_HINT_NONE;
|
||||
hint_femp->count = 0;
|
||||
}
|
||||
|
||||
static inline void exfat_set_empty_hint(struct exfat_inode_info *ei,
|
||||
struct exfat_hint_femp *candi_empty, struct exfat_chain *clu,
|
||||
int dentry, int num_entries, int entry_type)
|
||||
{
|
||||
if (ei->hint_femp.eidx == EXFAT_HINT_NONE ||
|
||||
ei->hint_femp.eidx > dentry) {
|
||||
int total_entries = EXFAT_B_TO_DEN(i_size_read(&ei->vfs_inode));
|
||||
|
||||
if (candi_empty->count == 0) {
|
||||
candi_empty->cur = *clu;
|
||||
candi_empty->eidx = dentry;
|
||||
}
|
||||
|
||||
if (entry_type == TYPE_UNUSED)
|
||||
candi_empty->count += total_entries - dentry;
|
||||
else
|
||||
candi_empty->count++;
|
||||
|
||||
if (candi_empty->count == num_entries ||
|
||||
candi_empty->count + candi_empty->eidx == total_entries)
|
||||
ei->hint_femp = *candi_empty;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
|
@ -917,17 +947,21 @@ enum {
|
|||
*/
|
||||
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,
|
||||
int num_entries, unsigned int type, struct exfat_hint *hint_opt)
|
||||
struct exfat_hint *hint_opt)
|
||||
{
|
||||
int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
|
||||
int order, step, name_len = 0;
|
||||
int dentries_per_clu, num_empty = 0;
|
||||
int dentries_per_clu;
|
||||
unsigned int entry_type;
|
||||
unsigned short *uniname = NULL;
|
||||
struct exfat_chain clu;
|
||||
struct exfat_hint *hint_stat = &ei->hint_stat;
|
||||
struct exfat_hint_femp candi_empty;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int num_entries = exfat_calc_num_entries(p_uniname);
|
||||
|
||||
if (num_entries < 0)
|
||||
return num_entries;
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
|
||||
|
@ -939,10 +973,13 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
|
|||
end_eidx = dentry;
|
||||
}
|
||||
|
||||
candi_empty.eidx = EXFAT_HINT_NONE;
|
||||
exfat_reset_empty_hint(&ei->hint_femp);
|
||||
|
||||
rewind:
|
||||
order = 0;
|
||||
step = DIRENT_STEP_FILE;
|
||||
exfat_reset_empty_hint(&candi_empty);
|
||||
|
||||
while (clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
i = dentry & (dentries_per_clu - 1);
|
||||
for (; i < dentries_per_clu; i++, dentry++) {
|
||||
|
@ -962,26 +999,9 @@ rewind:
|
|||
entry_type == TYPE_DELETED) {
|
||||
step = DIRENT_STEP_FILE;
|
||||
|
||||
num_empty++;
|
||||
if (candi_empty.eidx == EXFAT_HINT_NONE &&
|
||||
num_empty == 1) {
|
||||
exfat_chain_set(&candi_empty.cur,
|
||||
clu.dir, clu.size, clu.flags);
|
||||
}
|
||||
|
||||
if (candi_empty.eidx == EXFAT_HINT_NONE &&
|
||||
num_empty >= num_entries) {
|
||||
candi_empty.eidx =
|
||||
dentry - (num_empty - 1);
|
||||
WARN_ON(candi_empty.eidx < 0);
|
||||
candi_empty.count = num_empty;
|
||||
|
||||
if (ei->hint_femp.eidx ==
|
||||
EXFAT_HINT_NONE ||
|
||||
candi_empty.eidx <=
|
||||
ei->hint_femp.eidx)
|
||||
ei->hint_femp = candi_empty;
|
||||
}
|
||||
exfat_set_empty_hint(ei, &candi_empty, &clu,
|
||||
dentry, num_entries,
|
||||
entry_type);
|
||||
|
||||
brelse(bh);
|
||||
if (entry_type == TYPE_UNUSED)
|
||||
|
@ -989,17 +1009,14 @@ rewind:
|
|||
continue;
|
||||
}
|
||||
|
||||
num_empty = 0;
|
||||
candi_empty.eidx = EXFAT_HINT_NONE;
|
||||
exfat_reset_empty_hint(&candi_empty);
|
||||
|
||||
if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
|
||||
step = DIRENT_STEP_FILE;
|
||||
hint_opt->clu = clu.dir;
|
||||
hint_opt->eidx = i;
|
||||
if (type == TYPE_ALL || type == entry_type) {
|
||||
num_ext = ep->dentry.file.num_ext;
|
||||
step = DIRENT_STEP_STRM;
|
||||
}
|
||||
num_ext = ep->dentry.file.num_ext;
|
||||
step = DIRENT_STEP_STRM;
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
|
@ -1090,12 +1107,19 @@ not_found:
|
|||
rewind = 1;
|
||||
dentry = 0;
|
||||
clu.dir = p_dir->dir;
|
||||
/* reset empty hint */
|
||||
num_empty = 0;
|
||||
candi_empty.eidx = EXFAT_HINT_NONE;
|
||||
goto rewind;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the EXFAT_EOF_CLUSTER flag to avoid search
|
||||
* from the beginning again when allocated a new cluster
|
||||
*/
|
||||
if (ei->hint_femp.eidx == EXFAT_HINT_NONE) {
|
||||
ei->hint_femp.cur.dir = EXFAT_EOF_CLUSTER;
|
||||
ei->hint_femp.eidx = p_dir->size * dentries_per_clu;
|
||||
ei->hint_femp.count = 0;
|
||||
}
|
||||
|
||||
/* initialized hint_stat */
|
||||
hint_stat->clu = p_dir->dir;
|
||||
hint_stat->eidx = 0;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#define EXFAT_ROOT_INO 1
|
||||
|
||||
|
@ -41,6 +42,14 @@ enum {
|
|||
#define ES_2_ENTRIES 2
|
||||
#define ES_ALL_ENTRIES 0
|
||||
|
||||
#define ES_IDX_FILE 0
|
||||
#define ES_IDX_STREAM 1
|
||||
#define ES_IDX_FIRST_FILENAME 2
|
||||
#define EXFAT_FILENAME_ENTRY_NUM(name_len) \
|
||||
DIV_ROUND_UP(name_len, EXFAT_FILE_NAME_LEN)
|
||||
#define ES_IDX_LAST_FILENAME(name_len) \
|
||||
(ES_IDX_FIRST_FILENAME + EXFAT_FILENAME_ENTRY_NUM(name_len) - 1)
|
||||
|
||||
#define DIR_DELETED 0xFFFF0321
|
||||
|
||||
/* type values */
|
||||
|
@ -62,15 +71,11 @@ enum {
|
|||
#define TYPE_PADDING 0x0402
|
||||
#define TYPE_ACLTAB 0x0403
|
||||
#define TYPE_BENIGN_SEC 0x0800
|
||||
#define TYPE_ALL 0x0FFF
|
||||
|
||||
#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */
|
||||
#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
|
||||
#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
|
||||
|
||||
/* 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
|
||||
|
||||
|
@ -95,11 +100,17 @@ enum {
|
|||
/*
|
||||
* helpers for block size to dentry size conversion.
|
||||
*/
|
||||
#define EXFAT_B_TO_DEN_IDX(b, sbi) \
|
||||
((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
|
||||
#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS)
|
||||
#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS)
|
||||
|
||||
/*
|
||||
* helpers for cluster size to dentry size conversion.
|
||||
*/
|
||||
#define EXFAT_CLU_TO_DEN(clu, sbi) \
|
||||
((clu) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
|
||||
#define EXFAT_DEN_TO_CLU(dentry, sbi) \
|
||||
((dentry) >> ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
|
||||
|
||||
/*
|
||||
* helpers for fat entry.
|
||||
*/
|
||||
|
@ -125,6 +136,17 @@ enum {
|
|||
#define BITS_PER_BYTE_MASK 0x7
|
||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
|
||||
|
||||
#define ES_ENTRY_NUM(name_len) (ES_IDX_LAST_FILENAME(name_len) + 1)
|
||||
/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
|
||||
#define ES_MAX_ENTRY_NUM ES_ENTRY_NUM(MAX_NAME_LENGTH)
|
||||
|
||||
/*
|
||||
* 19 entries x 32 bytes/entry = 608 bytes.
|
||||
* The 608 bytes are in 3 sectors at most (even 512 Byte sector).
|
||||
*/
|
||||
#define DIR_CACHE_SIZE \
|
||||
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
|
||||
|
||||
struct exfat_dentry_namebuf {
|
||||
char *lfn;
|
||||
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
|
||||
|
@ -166,13 +188,16 @@ struct exfat_hint {
|
|||
|
||||
struct exfat_entry_set_cache {
|
||||
struct super_block *sb;
|
||||
bool modified;
|
||||
unsigned int start_off;
|
||||
int num_bh;
|
||||
struct buffer_head *bh[DIR_CACHE_SIZE];
|
||||
struct buffer_head *__bh[DIR_CACHE_SIZE];
|
||||
struct buffer_head **bh;
|
||||
unsigned int num_entries;
|
||||
bool modified;
|
||||
};
|
||||
|
||||
#define IS_DYNAMIC_ES(es) ((es)->__bh != (es)->bh)
|
||||
|
||||
struct exfat_dir_entry {
|
||||
struct exfat_chain dir;
|
||||
int entry;
|
||||
|
@ -375,7 +400,7 @@ static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi,
|
|||
sbi->data_start_sector;
|
||||
}
|
||||
|
||||
static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
||||
static inline unsigned int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
||||
sector_t sec)
|
||||
{
|
||||
return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) +
|
||||
|
@ -423,8 +448,8 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
|
|||
|
||||
/* file.c */
|
||||
extern const struct file_operations exfat_file_operations;
|
||||
int __exfat_truncate(struct inode *inode, loff_t new_size);
|
||||
void exfat_truncate(struct inode *inode, loff_t size);
|
||||
int __exfat_truncate(struct inode *inode);
|
||||
void exfat_truncate(struct inode *inode);
|
||||
int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
struct iattr *attr);
|
||||
int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path,
|
||||
|
@ -464,15 +489,16 @@ 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,
|
||||
int num_entries, unsigned int type, struct exfat_hint *hint_opt);
|
||||
struct exfat_hint *hint_opt);
|
||||
int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
|
||||
struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
|
||||
struct exfat_chain *p_dir, int entry, struct buffer_head **bh);
|
||||
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);
|
||||
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
|
||||
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
|
||||
struct super_block *sb, struct exfat_chain *p_dir, int entry,
|
||||
unsigned int type);
|
||||
int exfat_put_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 */
|
||||
|
|
|
@ -93,7 +93,7 @@ static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
|
|||
}
|
||||
|
||||
/* resize the file length */
|
||||
int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||
int __exfat_truncate(struct inode *inode)
|
||||
{
|
||||
unsigned int num_clusters_new, num_clusters_phys;
|
||||
unsigned int last_clu = EXFAT_FREE_CLUSTER;
|
||||
|
@ -113,7 +113,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
|||
|
||||
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
|
||||
|
||||
if (new_size > 0) {
|
||||
if (i_size_read(inode) > 0) {
|
||||
/*
|
||||
* Truncate FAT chain num_clusters after the first cluster
|
||||
* num_clusters = min(new, phys);
|
||||
|
@ -143,8 +143,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
|||
ei->start_clu = EXFAT_EOF_CLUSTER;
|
||||
}
|
||||
|
||||
i_size_write(inode, new_size);
|
||||
|
||||
if (ei->type == TYPE_FILE)
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
|
||||
|
@ -189,7 +187,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void exfat_truncate(struct inode *inode, loff_t size)
|
||||
void exfat_truncate(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
@ -207,7 +205,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
|
|||
goto write_size;
|
||||
}
|
||||
|
||||
err = __exfat_truncate(inode, i_size_read(inode));
|
||||
err = __exfat_truncate(inode);
|
||||
if (err)
|
||||
goto write_size;
|
||||
|
||||
|
@ -310,7 +308,7 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
|||
* __exfat_write_inode() is called from exfat_truncate(), inode
|
||||
* is already written by it, so mark_inode_dirty() is unneeded.
|
||||
*/
|
||||
exfat_truncate(inode, attr->ia_size);
|
||||
exfat_truncate(inode);
|
||||
up_write(&EXFAT_I(inode)->truncate_lock);
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
|
|
|
@ -21,7 +21,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
|||
{
|
||||
unsigned long long on_disk_size;
|
||||
struct exfat_dentry *ep, *ep2;
|
||||
struct exfat_entry_set_cache *es = NULL;
|
||||
struct exfat_entry_set_cache es;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
|
@ -42,11 +42,10 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
|||
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);
|
||||
if (!es)
|
||||
if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES))
|
||||
return -EIO;
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep2 = exfat_get_dentry_cached(es, 1);
|
||||
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
|
||||
ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
|
||||
|
||||
ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode));
|
||||
|
||||
|
@ -83,8 +82,8 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
|||
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
|
||||
}
|
||||
|
||||
exfat_update_dir_chksum_with_entry_set(es);
|
||||
return exfat_free_dentry_set(es, sync);
|
||||
exfat_update_dir_chksum_with_entry_set(&es);
|
||||
return exfat_put_dentry_set(&es, sync);
|
||||
}
|
||||
|
||||
int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
|
@ -358,7 +357,7 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
|
|||
if (to > i_size_read(inode)) {
|
||||
truncate_pagecache(inode, i_size_read(inode));
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned);
|
||||
exfat_truncate(inode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -622,7 +621,7 @@ void exfat_evict_inode(struct inode *inode)
|
|||
if (!inode->i_nlink) {
|
||||
i_size_write(inode, 0);
|
||||
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
__exfat_truncate(inode, 0);
|
||||
__exfat_truncate(inode);
|
||||
mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -224,11 +224,18 @@ static int exfat_search_empty_slot(struct super_block *sb,
|
|||
|
||||
if (hint_femp->eidx != EXFAT_HINT_NONE) {
|
||||
dentry = hint_femp->eidx;
|
||||
if (num_entries <= hint_femp->count) {
|
||||
hint_femp->eidx = EXFAT_HINT_NONE;
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/*
|
||||
* If hint_femp->count is enough, it is needed to check if
|
||||
* there are actual empty entries.
|
||||
* Otherwise, and if "dentry + hint_famp->count" is also equal
|
||||
* to "p_dir->size * dentries_per_clu", it means ENOSPC.
|
||||
*/
|
||||
if (dentry + hint_femp->count == p_dir->size * dentries_per_clu &&
|
||||
num_entries > hint_femp->count)
|
||||
return -ENOSPC;
|
||||
|
||||
hint_femp->eidx = EXFAT_HINT_NONE;
|
||||
exfat_chain_dup(&clu, &hint_femp->cur);
|
||||
} else {
|
||||
exfat_chain_dup(&clu, p_dir);
|
||||
|
@ -293,6 +300,12 @@ static int exfat_search_empty_slot(struct super_block *sb,
|
|||
}
|
||||
}
|
||||
|
||||
hint_femp->eidx = p_dir->size * dentries_per_clu - num_empty;
|
||||
hint_femp->count = num_empty;
|
||||
if (num_empty == 0)
|
||||
exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0,
|
||||
clu.flags);
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
|
@ -369,15 +382,11 @@ static int exfat_find_empty_entry(struct inode *inode,
|
|||
if (exfat_ent_set(sb, last_clu, clu.dir))
|
||||
return -EIO;
|
||||
|
||||
if (hint_femp.eidx == EXFAT_HINT_NONE) {
|
||||
/* the special case that new dentry
|
||||
* should be allocated from the start of new cluster
|
||||
*/
|
||||
hint_femp.eidx = EXFAT_B_TO_DEN_IDX(p_dir->size, sbi);
|
||||
hint_femp.count = sbi->dentries_per_clu;
|
||||
|
||||
if (hint_femp.cur.dir == EXFAT_EOF_CLUSTER)
|
||||
exfat_chain_set(&hint_femp.cur, clu.dir, 0, clu.flags);
|
||||
}
|
||||
|
||||
hint_femp.count += sbi->dentries_per_clu;
|
||||
|
||||
hint_femp.cur.size++;
|
||||
p_dir->size++;
|
||||
size = EXFAT_CLU_TO_B(p_dir->size, sbi);
|
||||
|
@ -588,14 +597,14 @@ unlock:
|
|||
static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
struct exfat_dir_entry *info)
|
||||
{
|
||||
int ret, dentry, num_entries, count;
|
||||
int ret, dentry, count;
|
||||
struct exfat_chain cdir;
|
||||
struct exfat_uni_name uni_name;
|
||||
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;
|
||||
struct exfat_entry_set_cache es;
|
||||
/* for optimized dir & entry to prevent long traverse of cluster chain */
|
||||
struct exfat_hint hint_opt;
|
||||
|
||||
|
@ -607,10 +616,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_entries = exfat_calc_num_entries(&uni_name);
|
||||
if (num_entries < 0)
|
||||
return num_entries;
|
||||
|
||||
/* check the validation of hint_stat and initialize it if required */
|
||||
if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) {
|
||||
ei->hint_stat.clu = cdir.dir;
|
||||
|
@ -620,9 +625,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
|||
}
|
||||
|
||||
/* search the file name for directories */
|
||||
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name,
|
||||
num_entries, TYPE_ALL, &hint_opt);
|
||||
|
||||
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name, &hint_opt);
|
||||
if (dentry < 0)
|
||||
return dentry; /* -error value */
|
||||
|
||||
|
@ -635,11 +638,10 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
|||
if (cdir.flags & ALLOC_NO_FAT_CHAIN)
|
||||
cdir.size -= dentry / sbi->dentries_per_clu;
|
||||
dentry = hint_opt.eidx;
|
||||
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES);
|
||||
if (!es)
|
||||
if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES))
|
||||
return -EIO;
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep2 = exfat_get_dentry_cached(es, 1);
|
||||
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
|
||||
ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
|
||||
|
||||
info->type = exfat_get_entry_type(ep);
|
||||
info->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
|
@ -668,7 +670,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
|||
ep->dentry.file.access_time,
|
||||
ep->dentry.file.access_date,
|
||||
0);
|
||||
exfat_free_dentry_set(es, false);
|
||||
exfat_put_dentry_set(&es, false);
|
||||
|
||||
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
|
||||
exfat_fs_error(sb,
|
||||
|
@ -1167,7 +1169,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
|||
struct exfat_inode_info *new_ei = NULL;
|
||||
unsigned int new_entry_type = TYPE_UNUSED;
|
||||
int new_entry = 0;
|
||||
struct buffer_head *old_bh, *new_bh = NULL;
|
||||
struct buffer_head *new_bh = NULL;
|
||||
|
||||
/* check the validity of pointer parameters */
|
||||
if (new_path == NULL || strlen(new_path) == 0)
|
||||
|
@ -1183,13 +1185,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
|||
EXFAT_I(old_parent_inode)->flags);
|
||||
dentry = ei->entry;
|
||||
|
||||
ep = exfat_get_dentry(sb, &olddir, dentry, &old_bh);
|
||||
if (!ep) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
brelse(old_bh);
|
||||
|
||||
/* check whether new dir is existing directory and empty */
|
||||
if (new_inode) {
|
||||
ret = -EIO;
|
||||
|
|
Loading…
Reference in New Issue