[PATCH] fat: Mark dirty just before updating metadata

2021-03-14 Thread Tetsuhiro Kohada
Instead of marking dirty-state on mount, do it just before updating
the metadata.
Therefore, if no write operation is performed, the boot sector will
not be updated.
This eliminates unnecessary dirty mark / unmark and reduces the risk
of boot sector corruption.
Also, keep boot-sec bh in sb to suppress errors when updating dirty.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/fat/dir.c |   2 +
 fs/fat/fat.h |   4 ++
 fs/fat/fatent.c  |   8 +++-
 fs/fat/file.c|   1 +
 fs/fat/inode.c   | 100 +++
 fs/fat/misc.c|   1 +
 fs/fat/namei_msdos.c |   1 +
 fs/fat/namei_vfat.c  |   1 +
 8 files changed, 70 insertions(+), 48 deletions(-)

diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index c4a274285858..d0236908dfc5 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1033,6 +1033,7 @@ int fat_remove_entries(struct inode *dir, struct 
fat_slot_info *sinfo)
struct buffer_head *bh;
int err = 0, nr_slots;
 
+   fat_set_state(sb, true);
/*
 * First stage: Remove the shortname. By this, the directory
 * entry is removed.
@@ -1327,6 +1328,7 @@ int fat_add_entries(struct inode *dir, void *slots, int 
nr_slots,
}
 
 found:
+   fat_set_state(sb, true);
err = 0;
pos -= free_slots * sizeof(*de);
nr_slots -= free_slots;
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 922a0c6ba46c..870369603746 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -76,6 +76,7 @@ struct msdos_sb_info {
struct mutex fat_lock;
struct mutex nfs_build_inode_lock;
struct mutex s_lock;
+   struct mutex bootsec_lock;
unsigned int prev_free;  /* previously allocated cluster number */
unsigned int free_clusters;  /* -1 if undefined */
unsigned int free_clus_valid; /* is free_clusters valid? */
@@ -101,6 +102,8 @@ struct msdos_sb_info {
struct hlist_head dir_hashtable[FAT_HASH_SIZE];
 
unsigned int dirty;   /* fs state before mount */
+   u8 state;   /* current fs state */
+   struct buffer_head *boot_bh;
struct rcu_head rcu;
 };
 
@@ -423,6 +426,7 @@ static inline unsigned long fat_dir_hash(int logstart)
return hash_32(logstart, FAT_HASH_BITS);
 }
 extern int fat_add_cluster(struct inode *inode);
+extern void fat_set_state(struct super_block *sb, bool dirty);
 
 /* fat/misc.c */
 extern __printf(3, 4) __cold
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index f7e3304b7802..5c4cebfdf337 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -472,6 +472,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, 
int nr_cluster)
 
BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2));/* fixed limit */
 
+   fat_set_state(sb, true);
lock_fat(sbi);
if (sbi->free_clusters != -1 && sbi->free_clus_valid &&
sbi->free_clusters < nr_cluster) {
@@ -559,6 +560,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
int i, err, nr_bhs;
int first_cl = cluster, dirty_fsinfo = 0;
 
+   fat_set_state(sb, true);
nr_bhs = 0;
fatent_init();
lock_fat(sbi);
@@ -741,9 +743,11 @@ int fat_count_free_clusters(struct super_block *sb)
} while (fat_ent_next(sbi, ));
cond_resched();
}
-   sbi->free_clusters = free;
sbi->free_clus_valid = 1;
-   mark_fsinfo_dirty(sb);
+   if (sbi->free_clusters != free) {
+   sbi->free_clusters = free;
+   mark_fsinfo_dirty(sb);
+   }
fatent_brelse();
 out:
unlock_fat(sbi);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index f9ee27cf4d7c..04af56276cda 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -314,6 +314,7 @@ static int fat_free(struct inode *inode, int skip)
if (MSDOS_I(inode)->i_start == 0)
return 0;
 
+   fat_set_state(sb, true);
fat_cache_inval_inode(inode);
 
wait = IS_DIRSYNC(inode);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index bab9b202b496..cb7b50746b9b 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -662,52 +662,45 @@ static void fat_evict_inode(struct inode *inode)
fat_detach(inode);
 }
 
-static void fat_set_state(struct super_block *sb,
-   unsigned int set, unsigned int force)
+void fat_set_state(struct super_block *sb, bool dirty)
 {
-   struct buffer_head *bh;
struct fat_boot_sector *b;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
+   u8 newstate;
 
/* do not change any thing if mounted read only */
-   if (sb_rdonly(sb) && !force)
+   if (sb_rdonly(sb))
return;
 
/* do not change state if fs was dirty */
-   if (sbi->dirty) {
-   /* warn only on set (mount). */
-   if (set)
-   fat_msg(sb, KERN_WARNING, "Volume was not properly "
-

[PATCH 1/2] exfat: commonize getting information from dir-entries

2020-10-21 Thread Tetsuhiro Kohada
Move 'getting dir-entries information' from exfat_find() to
exfat_find_dir_entry(), and make it common in exfat_readdir().
And, remove unused parameter in exfat_find_dir_entry().

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 75 +
 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/namei.c| 51 +++---
 3 files changed, 53 insertions(+), 75 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 916797077aad..ff809239a540 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -58,6 +58,37 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
exfat_free_dentry_set(es, false);
 }
 
+static void exfat_get_dentry_info(struct exfat_sb_info *sbi, struct 
exfat_dentry de[2],
+   struct exfat_dir_entry *info)
+{
+   info->attr = le16_to_cpu(de[0].dentry.file.attr);
+   info->type = (info->attr & ATTR_SUBDIR) ? TYPE_DIR : TYPE_FILE;
+
+   exfat_get_entry_time(sbi, >crtime,
+de[0].dentry.file.create_tz,
+de[0].dentry.file.create_time,
+de[0].dentry.file.create_date,
+de[0].dentry.file.create_time_cs);
+   exfat_get_entry_time(sbi, >mtime,
+de[0].dentry.file.modify_tz,
+de[0].dentry.file.modify_time,
+de[0].dentry.file.modify_date,
+de[0].dentry.file.modify_time_cs);
+   exfat_get_entry_time(sbi, >atime,
+de[0].dentry.file.access_tz,
+de[0].dentry.file.access_time,
+de[0].dentry.file.access_date,
+0);
+
+   info->flags = de[1].dentry.stream.flags;
+   info->start_clu = le32_to_cpu(de[1].dentry.stream.start_clu);
+   info->size = le64_to_cpu(de[1].dentry.stream.valid_size);
+   if ((info->type == TYPE_FILE) && (info->size == 0)) {
+   info->flags = ALLOC_NO_FAT_CHAIN;
+   info->start_clu = EXFAT_EOF_CLUSTER;
+   }
+}
+
 /* read a directory entry from the opened directory */
 static int exfat_readdir(struct inode *inode, loff_t *cpos, struct 
exfat_dir_entry *dir_entry)
 {
@@ -66,7 +97,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, 
struct exfat_dir_ent
sector_t sector;
struct exfat_chain dir, clu;
struct exfat_uni_name uni_name;
-   struct exfat_dentry *ep;
+   struct exfat_dentry *ep, de[2];
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
@@ -128,22 +159,7 @@ static int exfat_readdir(struct inode *inode, loff_t 
*cpos, struct exfat_dir_ent
}
 
num_ext = ep->dentry.file.num_ext;
-   dir_entry->attr = le16_to_cpu(ep->dentry.file.attr);
-   exfat_get_entry_time(sbi, _entry->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, _entry->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, _entry->atime,
-   ep->dentry.file.access_tz,
-   ep->dentry.file.access_time,
-   ep->dentry.file.access_date,
-   0);
+   de[0] = *ep;
 
*uni_name.name = 0x0;
exfat_get_uniname_from_ext_entry(sb, , dentry,
@@ -156,10 +172,10 @@ static int exfat_readdir(struct inode *inode, loff_t 
*cpos, struct exfat_dir_ent
ep = exfat_get_dentry(sb, , i + 1, , NULL);
if (!ep)
return -EIO;
-   dir_entry->size =
-   le64_to_cpu(ep->dentry.stream.valid_size);
+   de[1] = *ep;
dir_entry->entry = dentry;
brelse(bh);
+   exfat_get_dentry_info(sbi, de, dir_entry);
 
ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
ei->hint_bmap.clu = clu.dir;
@@ -913,7 +929,7 @@ enum {

[PATCH 2/2] exfat: simplify exfat_hint_femp structure

2020-10-21 Thread Tetsuhiro Kohada
The hint provided by exfat_hint_femp is that the cluster number is enough,
so replace exfat_chain with the cluster number.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  |  3 +--
 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/namei.c| 19 ++-
 3 files changed, 8 insertions(+), 16 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index ff809239a540..2e68796750b0 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -978,8 +978,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
num_empty++;
if (candi_empty.eidx == EXFAT_HINT_NONE &&
num_empty == 1) {
-   exfat_chain_set(_empty.cur,
-   clu.dir, clu.size, clu.flags);
+   candi_empty.clu = clu.dir;
}
 
if (candi_empty.eidx == EXFAT_HINT_NONE &&
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index f1402fed3302..28330804f9c9 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -153,7 +153,7 @@ struct exfat_hint_femp {
/* count of continuous empty entry */
int count;
/* the cluster that first empty slot exists in */
-   struct exfat_chain cur;
+   unsigned int clu;
 };
 
 /* hint structure */
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 54f54624d7e5..cfe11b368122 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -202,10 +202,10 @@ static int exfat_search_empty_slot(struct super_block *sb,
struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir,
int num_entries)
 {
-   int i, dentry, num_empty = 0;
+   int i, dentry = 0, num_empty = 0;
int dentries_per_clu;
unsigned int type;
-   struct exfat_chain clu;
+   struct exfat_chain clu = *p_dir;
struct exfat_dentry *ep;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct buffer_head *bh;
@@ -218,11 +218,8 @@ static int exfat_search_empty_slot(struct super_block *sb,
hint_femp->eidx = EXFAT_HINT_NONE;
return dentry;
}
-
-   exfat_chain_dup(, _femp->cur);
-   } else {
-   exfat_chain_dup(, p_dir);
-   dentry = 0;
+   clu.dir = hint_femp->clu;
+   clu.size -= EXFAT_B_TO_CLU(dentry * DENTRY_SIZE, sbi);
}
 
while (clu.dir != EXFAT_EOF_CLUSTER) {
@@ -240,8 +237,7 @@ static int exfat_search_empty_slot(struct super_block *sb,
if (hint_femp->eidx == EXFAT_HINT_NONE) {
hint_femp->eidx = dentry;
hint_femp->count = CNT_UNUSED_NOHIT;
-   exfat_chain_set(_femp->cur,
-   clu.dir, clu.size, clu.flags);
+   hint_femp->clu = clu.dir;
}
 
if (type == TYPE_UNUSED &&
@@ -354,7 +350,6 @@ static int exfat_find_empty_entry(struct inode *inode,
 */
exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size);
p_dir->flags = ALLOC_FAT_CHAIN;
-   hint_femp.cur.flags = ALLOC_FAT_CHAIN;
}
 
if (clu.flags == ALLOC_FAT_CHAIN)
@@ -367,10 +362,8 @@ static int exfat_find_empty_entry(struct inode *inode,
 */
hint_femp.eidx = EXFAT_B_TO_DEN_IDX(p_dir->size, sbi);
hint_femp.count = sbi->dentries_per_clu;
-
-   exfat_chain_set(_femp.cur, clu.dir, 0, clu.flags);
+   hint_femp.clu = clu.dir;
}
-   hint_femp.cur.size++;
p_dir->size++;
size = EXFAT_CLU_TO_B(p_dir->size, sbi);
 
-- 
2.25.1



[PATCH v4 1/2] exfat: add exfat_update_inode()

2020-10-18 Thread Tetsuhiro Kohada
Integrate exfat_sync_inode() and mark_inode_dirty() as exfat_update_inode()
Also, return the result of _exfat_write_inode () when sync is specified.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v4
 - no change
Changes in v3
 - no change
Changes in v2
 - no change

 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/file.c |  5 +
 fs/exfat/inode.c|  9 +++--
 fs/exfat/namei.c| 35 +++
 4 files changed, 16 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index b8f0e829ecbd..ec0ee516aee2 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -466,7 +466,7 @@ int exfat_count_dir_entries(struct super_block *sb, struct 
exfat_chain *p_dir);
 
 /* inode.c */
 extern const struct inode_operations exfat_file_inode_operations;
-void exfat_sync_inode(struct inode *inode);
+int exfat_update_inode(struct inode *inode);
 struct inode *exfat_build_inode(struct super_block *sb,
struct exfat_dir_entry *info, loff_t i_pos);
 void exfat_hash_inode(struct inode *inode, loff_t i_pos);
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index a92478eabfa4..e510b95dbf77 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -245,10 +245,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
goto write_size;
 
inode->i_ctime = inode->i_mtime = current_time(inode);
-   if (IS_DIRSYNC(inode))
-   exfat_sync_inode(inode);
-   else
-   mark_inode_dirty(inode);
+   exfat_update_inode(inode);
 
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 730373e0965a..5a55303e1f65 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -91,10 +91,15 @@ int exfat_write_inode(struct inode *inode, struct 
writeback_control *wbc)
return ret;
 }
 
-void exfat_sync_inode(struct inode *inode)
+int exfat_update_inode(struct inode *inode)
 {
lockdep_assert_held(_SB(inode->i_sb)->s_lock);
-   __exfat_write_inode(inode, 1);
+
+   if (IS_DIRSYNC(inode))
+   return __exfat_write_inode(inode, 1);
+
+   mark_inode_dirty(inode);
+   return 0;
 }
 
 /*
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 2932b23a3b6c..1f5f72eb5baf 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -561,10 +561,7 @@ static int exfat_create(struct inode *dir, struct dentry 
*dentry, umode_t mode,
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , i_pos);
@@ -812,10 +809,7 @@ static int exfat_unlink(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
clear_nlink(inode);
inode->i_mtime = inode->i_atime = current_time(inode);
@@ -846,10 +840,7 @@ static int exfat_mkdir(struct inode *dir, struct dentry 
*dentry, umode_t mode)
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
inc_nlink(dir);
 
i_pos = exfat_make_i_pos();
@@ -976,10 +967,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
drop_nlink(dir);
 
clear_nlink(inode);
@@ -1347,19 +1335,13 @@ static int exfat_rename(struct inode *old_dir, struct 
dentry *old_dentry,
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
exfat_truncate_atime(_dir->i_atime);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(new_dir);
-   else
-   mark_inode_dirty(new_dir);
+   exfat_update_inode(new_dir);
 
i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) |
(EXFAT_I(old_inode)->entry & 0x);
exfat_unhash_inode(old_inode);
exfat_hash_inode(old_inode, i_pos);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(old_inode);
-   else
-   

[PATCH v4 2/2] exfat: aggregate dir-entry updates into __exfat_write_inode().

2020-10-18 Thread Tetsuhiro Kohada
The following function writes the updated inode information as dir-entry
by themselves.
 - __exfat_truncate()
 - exfat_map_cluster()
 - exfat_find_empty_entry()
Aggregate these writes into __exfat_write_inode().

In exfat_map_cluster(), the value obtained from i_size_read() is set to
stream.valid_size and stream.size.
However, in the context of get_block(), inode->i_size has not been set yet,
so the same value as current will be set, which is a meaningless update.
Furthermore, if it is called with previous size=0, the newly allocated
cluster number will be set to stream.start_clu, and stream.valid_size/size
will be 0, which is illegal.
Update stream.valid_size/size and stream.start_clu when __exfat_write_inode
is called after i_size is set, to prevent meaningless/illegal updates.

Others:
 - Remove double inode-update in __exfat_truncate() and exfat_truncate().
 - In __exfat_write_inode(), rename 'on_disk_size' to 'filesize' and
   add adjustment when filesize is 0.

Reported-by: kernel test robot 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v4
 - Remove debug message
Changes in v3
 - Remove update_inode() in exfat_map_cluster()/exfat_truncate()
 - Update commit-message
Changes in v2
 - Fix endian issue

 fs/exfat/file.c  | 52 +---
 fs/exfat/inode.c | 42 +++---
 fs/exfat/namei.c | 26 +---
 3 files changed, 17 insertions(+), 103 deletions(-)

diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index e510b95dbf77..211fb947747a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -100,7 +100,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
-   int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
+   int ret;
 
/* check if the given file ID is opened */
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
@@ -150,49 +150,10 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ei->attr |= ATTR_ARCHIVE;
 
/* 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);
-   if (!es)
-   return -EIO;
-   ep = exfat_get_dentry_cached(es, 0);
-   ep2 = exfat_get_dentry_cached(es, 1);
-
-   ts = current_time(inode);
-   exfat_set_entry_time(sbi, ,
-   >dentry.file.modify_tz,
-   >dentry.file.modify_time,
-   >dentry.file.modify_date,
-   >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) {
-   ep2->dentry.stream.valid_size = 0;
-   ep2->dentry.stream.size = 0;
-   } else {
-   ep2->dentry.stream.valid_size = cpu_to_le64(new_size);
-   ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
-   }
-
-   if (new_size == 0) {
-   /* Any directory can not be truncated to zero */
-   WARN_ON(ei->type != TYPE_FILE);
-
-   ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
-   ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
-   }
-
-   exfat_update_dir_chksum_with_entry_set(es);
-   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
-   if (err)
-   return err;
-   }
+   inode->i_ctime = inode->i_mtime = current_time(inode);
+   ret = exfat_update_inode(inode);
+   if (ret)
+   return ret;
 
/* cut off from the FAT chain */
if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER &&
@@ -244,9 +205,6 @@ void exfat_truncate(struct inode *inode, loff_t size)
if (err)
goto write_size;
 
-   inode->i_ctime = inode->i_mtime = current_time(inode);
-   exfat_update_inode(inode);
-
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
 write_size:
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 5a55303e1f65..3870f5a1d8cd 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -19,7 +19,7 @@
 
 static int __exfat_writ

Re: [PATCH v3 2/2] exfat: aggregate dir-entry updates into __exfat_write_inode().

2020-10-18 Thread Tetsuhiro Kohada

@@ -184,6 +185,11 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
return -EIO;
}

+   exfat_warn(sb, "alloc[%lu]@map: %lld (%d - %08x)",
+  inode->i_ino, i_size_read(inode),
+  (clu_offset << sbi->sect_per_clus_bits) * 512,
+  last_clu);

Is this leftover print from debugging?


Oops!
Yes, just as you said.
I will post V4 soon.
Is there any other problem?


BR
---
Tetsuhiro Kohada 


Re: [PATCH v3 1/2] exfat: add exfat_update_inode()

2020-10-07 Thread Tetsuhiro Kohada

Thank you for your reply.


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(_dir->i_atime);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(new_dir);
-   else
-   mark_inode_dirty(new_dir);
+   exfat_update_inode(new_dir);

i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) |
(EXFAT_I(old_inode)->entry & 0x);
exfat_unhash_inode(old_inode);
exfat_hash_inode(old_inode, i_pos);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(old_inode);
-   else
-   mark_inode_dirty(old_inode);
+   exfat_update_inode(old_inode);

This is checking if old_inode is IS_DIRSYNC, not new_dir.
Is there any reason ?


To eliminate meaningless usage and simplify it.

Th exfat does not have an attribute that indicates whether each file/dir should 
be synced(such as ext4).
Therefore, sync necessity cannot be set for each inode, so sync necessity of the 
whole FS setting(sb-> s_flags) is inherited.
As a result, the following values ​​are all the same.
 IS_DIRSYNC (new_dir)
 IS_DIRSYNC (old_dir)
 IS_DIRSYNC (old_inode)
 sb-> s_flags & SB_SYNCHRONOUS | SB_DIRSYNC

In exfat, IS_DIRSYNC only works as a shortcut to sb->s_flags.

Even if S_SYNC or S_DIRSYNC were set to inode->i_flags, the current 
implementation is inappropriate.
Whether to sync or not should be determined by 
"IS_DIRSYNC(new_dir)||IS_DIRSYNC(old_dir)", I think.
(Syncing only old_dir is a high risk of losing file)

Whatever, no one sets S_SYNC and S_DIRSYNC in exfat, so the behavior is no 
different.

***
Please tell me your opinion about "aggregate dir-entry updates into 
__exfat_write_inode()"

BR
---
Tetsuhiro Kohada 


[PATCH v3 1/2] exfat: add exfat_update_inode()

2020-10-02 Thread Tetsuhiro Kohada
Integrate exfat_sync_inode() and mark_inode_dirty() as exfat_update_inode()
Also, return the result of _exfat_write_inode () when sync is specified.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v3
 - no change
Changes in v2
 - no change

 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/file.c |  5 +
 fs/exfat/inode.c|  9 +++--
 fs/exfat/namei.c| 35 +++
 4 files changed, 16 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index b8f0e829ecbd..ec0ee516aee2 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -466,7 +466,7 @@ int exfat_count_dir_entries(struct super_block *sb, struct 
exfat_chain *p_dir);
 
 /* inode.c */
 extern const struct inode_operations exfat_file_inode_operations;
-void exfat_sync_inode(struct inode *inode);
+int exfat_update_inode(struct inode *inode);
 struct inode *exfat_build_inode(struct super_block *sb,
struct exfat_dir_entry *info, loff_t i_pos);
 void exfat_hash_inode(struct inode *inode, loff_t i_pos);
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index a92478eabfa4..e510b95dbf77 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -245,10 +245,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
goto write_size;
 
inode->i_ctime = inode->i_mtime = current_time(inode);
-   if (IS_DIRSYNC(inode))
-   exfat_sync_inode(inode);
-   else
-   mark_inode_dirty(inode);
+   exfat_update_inode(inode);
 
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 730373e0965a..5a55303e1f65 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -91,10 +91,15 @@ int exfat_write_inode(struct inode *inode, struct 
writeback_control *wbc)
return ret;
 }
 
-void exfat_sync_inode(struct inode *inode)
+int exfat_update_inode(struct inode *inode)
 {
lockdep_assert_held(_SB(inode->i_sb)->s_lock);
-   __exfat_write_inode(inode, 1);
+
+   if (IS_DIRSYNC(inode))
+   return __exfat_write_inode(inode, 1);
+
+   mark_inode_dirty(inode);
+   return 0;
 }
 
 /*
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 676094f2abe2..4eb7cb528e97 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -561,10 +561,7 @@ static int exfat_create(struct inode *dir, struct dentry 
*dentry, umode_t mode,
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , i_pos);
@@ -812,10 +809,7 @@ static int exfat_unlink(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
clear_nlink(inode);
inode->i_mtime = inode->i_atime = current_time(inode);
@@ -846,10 +840,7 @@ static int exfat_mkdir(struct inode *dir, struct dentry 
*dentry, umode_t mode)
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
inc_nlink(dir);
 
i_pos = exfat_make_i_pos();
@@ -976,10 +967,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
drop_nlink(dir);
 
clear_nlink(inode);
@@ -1352,19 +1340,13 @@ static int exfat_rename(struct inode *old_dir, struct 
dentry *old_dentry,
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
exfat_truncate_atime(_dir->i_atime);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(new_dir);
-   else
-   mark_inode_dirty(new_dir);
+   exfat_update_inode(new_dir);
 
i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) |
(EXFAT_I(old_inode)->entry & 0x);
exfat_unhash_inode(old_inode);
exfat_hash_inode(old_inode, i_pos);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(old_inode);
-   else
-   mark_i

[PATCH v3 2/2] exfat: aggregate dir-entry updates into __exfat_write_inode().

2020-10-02 Thread Tetsuhiro Kohada
The following function writes the updated inode information as dir-entry
by themselves.
 - __exfat_truncate()
 - exfat_map_cluster()
 - exfat_find_empty_entry()
Aggregate these writes into __exfat_write_inode().

In exfat_map_cluster(), the value obtained from i_size_read() is set to
stream.valid_size and stream.size.
However, in the context of get_block(), inode->i_size has not been set yet,
so the same value as current will be set, which is a meaningless update.
Furthermore, if it is called with previous size=0, the newly allocated
cluster number will be set to stream.start_clu, and stream.valid_size/size
will be 0, which is illegal.
Update stream.valid_size/size and stream.start_clu when __exfat_write_inode
is called after i_size is set, to prevent meaningless/illegal updates.

Others:
 - Remove double inode-update in __exfat_truncate() and exfat_truncate().
 - In __exfat_write_inode(), rename 'on_disk_size' to 'filesize' and
   add adjustment when filesize is 0.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v3
 - Remove update_inode() in exfat_map_cluster()/exfat_truncate()
 - Update commit-message
Changes in v2
 - Fix endian issue

 fs/exfat/file.c  | 52 +---
 fs/exfat/inode.c | 47 +++
 fs/exfat/namei.c | 26 +---
 3 files changed, 22 insertions(+), 103 deletions(-)

diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index e510b95dbf77..211fb947747a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -100,7 +100,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
-   int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
+   int ret;
 
/* check if the given file ID is opened */
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
@@ -150,49 +150,10 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ei->attr |= ATTR_ARCHIVE;
 
/* 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);
-   if (!es)
-   return -EIO;
-   ep = exfat_get_dentry_cached(es, 0);
-   ep2 = exfat_get_dentry_cached(es, 1);
-
-   ts = current_time(inode);
-   exfat_set_entry_time(sbi, ,
-   >dentry.file.modify_tz,
-   >dentry.file.modify_time,
-   >dentry.file.modify_date,
-   >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) {
-   ep2->dentry.stream.valid_size = 0;
-   ep2->dentry.stream.size = 0;
-   } else {
-   ep2->dentry.stream.valid_size = cpu_to_le64(new_size);
-   ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
-   }
-
-   if (new_size == 0) {
-   /* Any directory can not be truncated to zero */
-   WARN_ON(ei->type != TYPE_FILE);
-
-   ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
-   ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
-   }
-
-   exfat_update_dir_chksum_with_entry_set(es);
-   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
-   if (err)
-   return err;
-   }
+   inode->i_ctime = inode->i_mtime = current_time(inode);
+   ret = exfat_update_inode(inode);
+   if (ret)
+   return ret;
 
/* cut off from the FAT chain */
if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER &&
@@ -244,9 +205,6 @@ void exfat_truncate(struct inode *inode, loff_t size)
if (err)
goto write_size;
 
-   inode->i_ctime = inode->i_mtime = current_time(inode);
-   exfat_update_inode(inode);
-
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
 write_size:
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 5a55303e1f65..cf29b14ce7f9 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -19,7 +19,7 @@
 
 static int __exfat_write_inode(struct inode *inode, int sync)
 {
-   unsigned long l

Re: [PATCH 2/3] exfat: remove useless check in exfat_move_file()

2020-09-30 Thread Tetsuhiro Kohada




BTW
Are you busy now?

I'm sorry, I'm so busy for my full time work :( Anyway, I'm trying to review 
serious bug patches or
bug reports first.
Other patches, such as clean-up or code refactoring, may take some time to 
review.


I am waiting for your reply about "integrates dir-entry getting and
validation" patch.

As I know, your patch is being under review by Namjae.

I already gave comments and a patch, but you said you can't do it.
I'm sorry, But I can't accept an incomplete patch. I will directly fix it later.


Of course, I understand that you can't accept a under-discussed patch.

I think you know what I'm trying to do, with previous patches.
Unfortunately, I couldn't implement it properly using the patch you provided.
But I don't think the checksum and name-lenth issues should be left unresolved.
(How do you think?)
So I want you to think with me.

I still feel we haven't discussed this enough.
I still don't understand what you think is the problem with the patch.
Where and what kind of problems do you think the patch has?
- performance?
- wrong behavior?
- readability?
- runtime cost?
- style?
- other?

I think I explained the reason for each implementation.
If it's not enough, I'd like to explain it in more detail.


BR
---
Tetsuhiro Kohada 


Re: [PATCH 2/3] exfat: remove useless check in exfat_move_file()

2020-09-30 Thread Tetsuhiro Kohada

Are you busy now?

I'm sorry, I'm so busy for my full time work :(
Anyway, I'm trying to review serious bug patches or bug reports first.
Other patches, such as clean-up or code refactoring, may take some time to 
review.


I'll try to reduce your burden as much as possible.


I am waiting for your reply about "integrates dir-entry getting and
validation" patch.

As I know, your patch is being under review by Namjae.


OK.
I'll discuss it with him.
If possible, please let me know your opinion.

BR
---
Tetsuhiro Kohada 


Re: [PATCH 2/3] exfat: remove useless check in exfat_move_file()

2020-09-29 Thread Tetsuhiro Kohada

It might check if the cluster numbers are same between source entry
and target directory.


This checks if newdir is the move target itself.
Example:
mv /mnt/dir0 /mnt/dir0/foo

However, this check is not enough.
We need to check newdir and all ancestors.
Example:
mv /mnt/dir0 /mnt/dir0/dir1/foo
mv /mnt/dir0 /mnt/dir0/dir1/dir2/foo
...

This is probably a taboo for all layered filesystems.



Could you let me know what code you mentioned?
Or do you mean the codes on vfs?


You can find in do_renameat2(). --- around 'fs/namei.c:4440'
If the destination ancestors are itself, our driver will not be called.


I think, of course, vfs has been doing that.
So that code is unnecessary in normal situations.

That code comes from the old exfat implementation.


It could be a remnant of another system.
Once upon a time, I moved the dir to a descendant dir without implementing this 
check
and it disappeared forever.
linux-VFS fixed this issue immediately, but some systems still need to be 
checked by
the driver itself. (ex.Windows-IFS)



And as far as I understand, it seems to check once more "the cluster number"
even though it comes through vfs so that it tries detecting abnormal of on-disk.

Anyway, I agonized if it is really needed.
In conclusion, old code could be eliminated and your patch looks reasonable.


It's easy to add, but it's really hard to remove the ancient code.


BTW
I have a question for you.
Now, I'm trying to optimize exfat_get_dentry().
However, exfat_get_dentry() is used a lot, so the patch is also large.
In such a case
-Replace old implementation with new ones with a single patch.
-Devide multiple patches in which old functions and new functions (ex. 
exfat_get_dentry2) coexist temporarily. And finally clean up.

I understand that a small patch is desirable, but the latter has "two similar 
functions".
Which is better for you to review the patch?


BR
---
Tetsuhiro Kohada 
 


[PATCH v2 1/2] exfat: add exfat_update_inode()

2020-09-19 Thread Tetsuhiro Kohada
Integrate exfat_sync_inode() and mark_inode_dirty() as exfat_update_inode()
Also, return the result of _exfat_write_inode () when sync is specified.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - no change

fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/file.c |  5 +
 fs/exfat/inode.c|  9 +++--
 fs/exfat/namei.c| 35 +++
 4 files changed, 16 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 44dc04520175..3152c01e47ed 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -467,7 +467,7 @@ int exfat_count_dir_entries(struct super_block *sb, struct 
exfat_chain *p_dir);
 
 /* inode.c */
 extern const struct inode_operations exfat_file_inode_operations;
-void exfat_sync_inode(struct inode *inode);
+int exfat_update_inode(struct inode *inode);
 struct inode *exfat_build_inode(struct super_block *sb,
struct exfat_dir_entry *info, loff_t i_pos);
 void exfat_hash_inode(struct inode *inode, loff_t i_pos);
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 4831a39632a1..dcc99349b816 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -247,10 +247,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
goto write_size;
 
inode->i_ctime = inode->i_mtime = current_time(inode);
-   if (IS_DIRSYNC(inode))
-   exfat_sync_inode(inode);
-   else
-   mark_inode_dirty(inode);
+   exfat_update_inode(inode);
 
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 7f90204adef5..f307019afe88 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -91,10 +91,15 @@ int exfat_write_inode(struct inode *inode, struct 
writeback_control *wbc)
return ret;
 }
 
-void exfat_sync_inode(struct inode *inode)
+int exfat_update_inode(struct inode *inode)
 {
lockdep_assert_held(_SB(inode->i_sb)->s_lock);
-   __exfat_write_inode(inode, 1);
+
+   if (IS_DIRSYNC(inode))
+   return __exfat_write_inode(inode, 1);
+
+   mark_inode_dirty(inode);
+   return 0;
 }
 
 /*
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index b966b9120c9c..4febff3541a9 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -571,10 +571,7 @@ static int exfat_create(struct inode *dir, struct dentry 
*dentry, umode_t mode,
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , i_pos);
@@ -822,10 +819,7 @@ static int exfat_unlink(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
clear_nlink(inode);
inode->i_mtime = inode->i_atime = current_time(inode);
@@ -856,10 +850,7 @@ static int exfat_mkdir(struct inode *dir, struct dentry 
*dentry, umode_t mode)
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
inc_nlink(dir);
 
i_pos = exfat_make_i_pos();
@@ -986,10 +977,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
drop_nlink(dir);
 
clear_nlink(inode);
@@ -1362,19 +1350,13 @@ static int exfat_rename(struct inode *old_dir, struct 
dentry *old_dentry,
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
exfat_truncate_atime(_dir->i_atime);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(new_dir);
-   else
-   mark_inode_dirty(new_dir);
+   exfat_update_inode(new_dir);
 
i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) |
(EXFAT_I(old_inode)->entry & 0x);
exfat_unhash_inode(old_inode);
exfat_hash_inode(old_inode, i_pos);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(old_inode);
-   else
-   mark_inode_dirty(old_inode);

[PATCH v2 2/2] exfat: aggregate dir-entry updates into __exfat_write_inode().

2020-09-19 Thread Tetsuhiro Kohada
The following function writes the updated inode information as dir-entry
by themselves.
 - __exfat_truncate()
 - exfat_map_cluster()
 - exfat_find_empty_entry()
Aggregate these writes into __exfat_write_inode().

Also, in __exfat_write_inode(), rename 'on_disk_size' to 'filesize' and
add adjustment when filesize is 0.

Reported-by: kernel test robot 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Fix endian issue

 fs/exfat/file.c  | 49 +---
 fs/exfat/inode.c | 42 +++--
 fs/exfat/namei.c | 26 +
 3 files changed, 21 insertions(+), 96 deletions(-)

diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index dcc99349b816..d5b026183387 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -100,7 +100,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
-   int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
+   int ret;
 
/* check if the given file ID is opened */
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
@@ -150,49 +150,10 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ei->attr |= ATTR_ARCHIVE;
 
/* 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);
-   if (!es)
-   return -EIO;
-   ep = exfat_get_dentry_cached(es, 0);
-   ep2 = exfat_get_dentry_cached(es, 1);
-
-   ts = current_time(inode);
-   exfat_set_entry_time(sbi, ,
-   >dentry.file.modify_tz,
-   >dentry.file.modify_time,
-   >dentry.file.modify_date,
-   >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) {
-   ep2->dentry.stream.valid_size = 0;
-   ep2->dentry.stream.size = 0;
-   } else {
-   ep2->dentry.stream.valid_size = cpu_to_le64(new_size);
-   ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
-   }
-
-   if (new_size == 0) {
-   /* Any directory can not be truncated to zero */
-   WARN_ON(ei->type != TYPE_FILE);
-
-   ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
-   ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
-   }
-
-   exfat_update_dir_chksum_with_entry_set(es);
-   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
-   if (err)
-   return err;
-   }
+   inode->i_mtime = current_time(inode);
+   ret = exfat_update_inode(inode);
+   if (ret)
+   return ret;
 
/* cut off from the FAT chain */
if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER &&
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index f307019afe88..74e105e95942 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -19,7 +19,7 @@
 
 static int __exfat_write_inode(struct inode *inode, int sync)
 {
-   unsigned long long on_disk_size;
+   unsigned long long filesize;
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es = NULL;
struct super_block *sb = inode->i_sb;
@@ -68,13 +68,14 @@ static int __exfat_write_inode(struct inode *inode, int 
sync)
NULL);
 
/* File size should be zero if there is no cluster allocated */
-   on_disk_size = i_size_read(inode);
-
+   filesize = i_size_read(inode);
if (ei->start_clu == EXFAT_EOF_CLUSTER)
-   on_disk_size = 0;
+   filesize = 0;
 
-   ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
+   ep2->dentry.stream.valid_size = cpu_to_le64(filesize);
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
+   ep2->dentry.stream.flags = filesize ? ei->flags : ALLOC_FAT_CHAIN;
+   ep2->dentry.stream.start_clu = cpu_to_le32(filesize ? ei->start_clu : 
EXFAT_FREE_CLUSTER);
 
exfat_update_dir_chksum_with_entry_set(es);
return exfat_free_dentry_set(es, sync);
@@ -223,32 +224,11 @@ static int exfat_map_cluster(struct inode *inode, 
unsigned int clu_

[PATCH 2/2] exfat: aggregate dir-entry updates into __exfat_write_inode().

2020-09-17 Thread Tetsuhiro Kohada
The following function writes the updated inode information as dir-entry
by themselves.
 - __exfat_truncate()
 - exfat_map_cluster()
 - exfat_find_empty_entry()
Aggregate these writes into __exfat_write_inode().

Also, in __exfat_write_inode(), rename 'on_disk_size' to 'filesize' and
add adjustment when filesize is 0.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/file.c  | 49 +---
 fs/exfat/inode.c | 42 +++--
 fs/exfat/namei.c | 26 +
 3 files changed, 21 insertions(+), 96 deletions(-)

diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index dcc99349b816..d5b026183387 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -100,7 +100,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
-   int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
+   int ret;
 
/* check if the given file ID is opened */
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
@@ -150,49 +150,10 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ei->attr |= ATTR_ARCHIVE;
 
/* 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);
-   if (!es)
-   return -EIO;
-   ep = exfat_get_dentry_cached(es, 0);
-   ep2 = exfat_get_dentry_cached(es, 1);
-
-   ts = current_time(inode);
-   exfat_set_entry_time(sbi, ,
-   >dentry.file.modify_tz,
-   >dentry.file.modify_time,
-   >dentry.file.modify_date,
-   >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) {
-   ep2->dentry.stream.valid_size = 0;
-   ep2->dentry.stream.size = 0;
-   } else {
-   ep2->dentry.stream.valid_size = cpu_to_le64(new_size);
-   ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
-   }
-
-   if (new_size == 0) {
-   /* Any directory can not be truncated to zero */
-   WARN_ON(ei->type != TYPE_FILE);
-
-   ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
-   ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
-   }
-
-   exfat_update_dir_chksum_with_entry_set(es);
-   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
-   if (err)
-   return err;
-   }
+   inode->i_mtime = current_time(inode);
+   ret = exfat_update_inode(inode);
+   if (ret)
+   return ret;
 
/* cut off from the FAT chain */
if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER &&
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index f307019afe88..3df80740529d 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -19,7 +19,7 @@
 
 static int __exfat_write_inode(struct inode *inode, int sync)
 {
-   unsigned long long on_disk_size;
+   unsigned long long filesize;
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es = NULL;
struct super_block *sb = inode->i_sb;
@@ -68,13 +68,14 @@ static int __exfat_write_inode(struct inode *inode, int 
sync)
NULL);
 
/* File size should be zero if there is no cluster allocated */
-   on_disk_size = i_size_read(inode);
-
+   filesize = i_size_read(inode);
if (ei->start_clu == EXFAT_EOF_CLUSTER)
-   on_disk_size = 0;
+   filesize = 0;
 
-   ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
+   ep2->dentry.stream.valid_size = cpu_to_le64(filesize);
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
+   ep2->dentry.stream.flags = filesize ? ei->flags : ALLOC_FAT_CHAIN;
+   ep2->dentry.stream.start_clu = filesize ? ei->start_clu : 
EXFAT_FREE_CLUSTER;
 
exfat_update_dir_chksum_with_entry_set(es);
return exfat_free_dentry_set(es, sync);
@@ -223,32 +224,11 @@ static int exfat_map_cluster(struct inode *inode, 
unsigned int clu_offset,
num_clusters += num_to_be_allocated;
*

[PATCH 1/2] exfat: add exfat_update_inode()

2020-09-17 Thread Tetsuhiro Kohada
Integrate exfat_sync_inode() and mark_inode_dirty() as exfat_update_inode()
Also, return the result of _exfat_write_inode () when sync is specified.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/file.c |  5 +
 fs/exfat/inode.c|  9 +++--
 fs/exfat/namei.c| 35 +++
 4 files changed, 16 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 44dc04520175..3152c01e47ed 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -467,7 +467,7 @@ int exfat_count_dir_entries(struct super_block *sb, struct 
exfat_chain *p_dir);
 
 /* inode.c */
 extern const struct inode_operations exfat_file_inode_operations;
-void exfat_sync_inode(struct inode *inode);
+int exfat_update_inode(struct inode *inode);
 struct inode *exfat_build_inode(struct super_block *sb,
struct exfat_dir_entry *info, loff_t i_pos);
 void exfat_hash_inode(struct inode *inode, loff_t i_pos);
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 4831a39632a1..dcc99349b816 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -247,10 +247,7 @@ void exfat_truncate(struct inode *inode, loff_t size)
goto write_size;
 
inode->i_ctime = inode->i_mtime = current_time(inode);
-   if (IS_DIRSYNC(inode))
-   exfat_sync_inode(inode);
-   else
-   mark_inode_dirty(inode);
+   exfat_update_inode(inode);
 
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 7f90204adef5..f307019afe88 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -91,10 +91,15 @@ int exfat_write_inode(struct inode *inode, struct 
writeback_control *wbc)
return ret;
 }
 
-void exfat_sync_inode(struct inode *inode)
+int exfat_update_inode(struct inode *inode)
 {
lockdep_assert_held(_SB(inode->i_sb)->s_lock);
-   __exfat_write_inode(inode, 1);
+
+   if (IS_DIRSYNC(inode))
+   return __exfat_write_inode(inode, 1);
+
+   mark_inode_dirty(inode);
+   return 0;
 }
 
 /*
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index b966b9120c9c..4febff3541a9 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -571,10 +571,7 @@ static int exfat_create(struct inode *dir, struct dentry 
*dentry, umode_t mode,
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , i_pos);
@@ -822,10 +819,7 @@ static int exfat_unlink(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
 
clear_nlink(inode);
inode->i_mtime = inode->i_atime = current_time(inode);
@@ -856,10 +850,7 @@ static int exfat_mkdir(struct inode *dir, struct dentry 
*dentry, umode_t mode)
 
inode_inc_iversion(dir);
dir->i_ctime = dir->i_mtime = current_time(dir);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
inc_nlink(dir);
 
i_pos = exfat_make_i_pos();
@@ -986,10 +977,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry 
*dentry)
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = current_time(dir);
exfat_truncate_atime(>i_atime);
-   if (IS_DIRSYNC(dir))
-   exfat_sync_inode(dir);
-   else
-   mark_inode_dirty(dir);
+   exfat_update_inode(dir);
drop_nlink(dir);
 
clear_nlink(inode);
@@ -1362,19 +1350,13 @@ static int exfat_rename(struct inode *old_dir, struct 
dentry *old_dentry,
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
exfat_truncate_atime(_dir->i_atime);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(new_dir);
-   else
-   mark_inode_dirty(new_dir);
+   exfat_update_inode(new_dir);
 
i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) |
(EXFAT_I(old_inode)->entry & 0x);
exfat_unhash_inode(old_inode);
exfat_hash_inode(old_inode, i_pos);
-   if (IS_DIRSYNC(new_dir))
-   exfat_sync_inode(old_inode);
-   else
-   mark_inode_dirty(old_inode);
+   exfat_update_inode(old_inode);

[PATCH v2] exfat: remove 'rwoffset' in exfat_inode_info

2020-09-16 Thread Tetsuhiro Kohada
Remove 'rwoffset' in exfat_inode_info and replace it with the parameter of
exfat_readdir().
Since rwoffset is referenced only by exfat_readdir(), it is not necessary
a exfat_inode_info's member.
Also, change cpos to point to the next of entry-set, and return the index
of dir-entry via dir_entry->entry.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - 'cpos' point to the next of entry-set
 - return the index of dir-entry via dir_entry->entry
 - fix commit-message

 fs/exfat/dir.c  | 21 +
 fs/exfat/exfat_fs.h |  2 --
 fs/exfat/file.c |  2 --
 fs/exfat/inode.c|  3 ---
 fs/exfat/super.c|  1 -
 5 files changed, 9 insertions(+), 20 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index a9b13ae3f325..82bee625549d 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -59,9 +59,9 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
 }
 
 /* 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;
@@ -70,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 & 0x;
+   unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0x;
struct buffer_head *bh;
 
/* check if the given file ID is opened */
@@ -127,6 +127,7 @@ 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, _entry->crtime,
ep->dentry.file.create_tz,
@@ -157,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;
}
 
@@ -178,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;
 }
 
@@ -242,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, );
+   err = exfat_readdir(inode, , );
if (err) {
/*
 * At least we tried to read a sector.  Move cpos to next sector
@@ -262,13 +262,10 @@ static int exfat_iterate(struct file *filp, struct 
dir_context *ctx)
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) & 0x);
+   i_pos = ((loff_t)ei->start_clu << 32) | (de.entry & 0x);
tmp = exfat_iget(sb, i_pos);
if (tmp) {
inum = tmp->i_ino;
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 44dc04520175..e586daf5a2e7 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -263,8 +263,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;
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 4831a39632a1..a92478eabfa4 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -208,8 +208,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
/* hint information */
ei->hint_bmap.off = EXFAT_EOF

Re: [PATCH 2/3] exfat: remove useless check in exfat_move_file()

2020-09-16 Thread Tetsuhiro Kohada

--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -1095,11 +1095,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;
-


It might check if the cluster numbers are same between source entry and
target directory.


This checks if newdir is the move target itself.
Example:
  mv /mnt/dir0 /mnt/dir0/foo

However, this check is not enough.
We need to check newdir and all ancestors.
Example:
  mv /mnt/dir0 /mnt/dir0/dir1/foo
  mv /mnt/dir0 /mnt/dir0/dir1/dir2/foo
  ...

This is probably a taboo for all layered filesystems.



Could you let me know what code you mentioned?
Or do you mean the codes on vfs?


You can find in do_renameat2(). --- around 'fs/namei.c:4440'
If the destination ancestors are itself, our driver will not be called.


BTW
Are you busy now?
I am waiting for your reply about "integrates dir-entry getting and validation" 
patch.

BR
---
Tetsuhiro Kohada 


Re: [PATCH] exfat: remove 'rwoffset' in exfat_inode_info

2020-09-15 Thread Tetsuhiro Kohada




On 2020/09/12 14:01, Sungjong Seo wrote:

Remove 'rwoffset' in exfat_inode_info and replace it with the
parameter(cpos) of exfat_readdir.
Since rwoffset of  is referenced only by exfat_readdir, it is not
necessary a exfat_inode_info's member.

Signed-off-by: Tetsuhiro Kohada 
---
  fs/exfat/dir.c  | 16 ++--
  fs/exfat/exfat_fs.h |  2 --
  fs/exfat/file.c |  2 --
  fs/exfat/inode.c|  3 ---
  fs/exfat/super.c|  1 -
  5 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index
a9b13ae3f325..fa5bb72aa295 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c

[snip]

sector @@ -262,13 +260,11 @@ static int exfat_iterate(struct file *filp,
struct dir_context *ctx)
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) & 0x);
+   (EXFAT_B_TO_DEN(cpos-1) & 0x);


Need to fix the above line to be:
(EXFAT_B_TO_DEN(cpos)-1)) & 0x);



Here, we simply converted so that the calculation results would be the same.
But after reading it carefully again, I noticed.
 - Why use the previous entry?
 - Why does cpos point to stream dir-entry in entry-set?

For the former, there is no need to "++dentry" in exfat_readdir().
For the latter, I think cpos should point to the next to current entry-set.

I'll make V2 considering these.
How do you think?

BR
---
Tetsuhiro Kohada 



[PATCH 2/3] exfat: remove useless check in exfat_move_file()

2020-09-10 Thread Tetsuhiro Kohada
In exfat_move_file(), the identity of source and target directory has been
checked by the caller.
Also, it gets stream.start_clu from file dir-entry, which is an invalid
determination.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/namei.c | 5 -
 1 file changed, 5 deletions(-)

diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 803748946ddb..1c433491f771 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -1095,11 +1095,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)
-- 
2.25.1



[PATCH 3/3] exfat: replace memcpy with structure assignment

2020-09-10 Thread Tetsuhiro Kohada
Use structure assignment instead of memcpy.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c   |  7 ++-
 fs/exfat/inode.c |  2 +-
 fs/exfat/namei.c | 15 +++
 3 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index fa5bb72aa295..8520decd120c 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -974,11 +974,8 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
if (ei->hint_femp.eidx ==
EXFAT_HINT_NONE ||
candi_empty.eidx <=
-ei->hint_femp.eidx) {
-   memcpy(>hint_femp,
-   _empty,
-   sizeof(candi_empty));
-   }
+ei->hint_femp.eidx)
+   ei->hint_femp = candi_empty;
}
 
brelse(bh);
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 70a33d4807c3..687f77653187 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -554,7 +554,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(>dir, >dir, sizeof(struct exfat_chain));
+   ei->dir = info->dir;
ei->entry = info->entry;
ei->attr = info->attr;
ei->start_clu = info->start_clu;
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 1c433491f771..2932b23a3b6c 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -318,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(_femp, >hint_femp,
-   sizeof(struct exfat_hint_femp));
+   hint_femp = ei->hint_femp;
ei->hint_femp.eidx = EXFAT_HINT_NONE;
}
 
@@ -519,7 +518,7 @@ static int exfat_add_entry(struct inode *inode, const char 
*path,
if (ret)
goto out;
 
-   memcpy(>dir, p_dir, sizeof(struct exfat_chain));
+   info->dir = *p_dir;
info->entry = dentry;
info->flags = ALLOC_NO_FAT_CHAIN;
info->type = type;
@@ -625,7 +624,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
if (dentry < 0)
return dentry; /* -error value */
 
-   memcpy(>dir, , sizeof(struct exfat_chain));
+   info->dir = cdir;
info->entry = dentry;
info->num_subdirs = 0;
 
@@ -1030,7 +1029,7 @@ 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;
@@ -1050,7 +1049,7 @@ static int exfat_rename_file(struct inode *inode, struct 
exfat_chain *p_dir,
return -EIO;
}
 
-   memcpy(epnew, epold, DENTRY_SIZE);
+   *epnew = *epold;
exfat_update_bh(new_bh, sync);
brelse(old_bh);
brelse(new_bh);
@@ -1113,7 +1112,7 @@ 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;
@@ -1133,7 +1132,7 @@ static int exfat_move_file(struct inode *inode, struct 
exfat_chain *p_olddir,
return -EIO;
}
 
-   memcpy(epnew, epmov, DENTRY_SIZE);
+   *epnew = *epmov;
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
brelse(mov_bh);
brelse(new_bh);
-- 
2.25.1



[PATCH 1/3] exfat: remove useless directory scan in exfat_add_entry()

2020-09-10 Thread Tetsuhiro Kohada
There is nothing in directory just created, so there is no need to scan.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/namei.c | 11 +--
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index b966b9120c9c..803748946ddb 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -530,19 +530,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(, info->start_clu,
-   EXFAT_B_TO_CLU(info->size, sbi), info->flags);
-   count = exfat_count_dir_entries(sb, );
-   if (count < 0)
-   return -EIO;
-   info->num_subdirs = count + EXFAT_MIN_SUBDIR;
+   info->num_subdirs = EXFAT_MIN_SUBDIR;
}
memset(>crtime, 0, sizeof(info->crtime));
memset(>mtime, 0, sizeof(info->mtime));
-- 
2.25.1



[PATCH] exfat: remove 'rwoffset' in exfat_inode_info

2020-09-09 Thread Tetsuhiro Kohada
Remove 'rwoffset' in exfat_inode_info and replace it with the parameter(cpos) 
of exfat_readdir.
Since rwoffset of  is referenced only by exfat_readdir, it is not necessary a 
exfat_inode_info's member.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 16 ++--
 fs/exfat/exfat_fs.h |  2 --
 fs/exfat/file.c |  2 --
 fs/exfat/inode.c|  3 ---
 fs/exfat/super.c|  1 -
 5 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index a9b13ae3f325..fa5bb72aa295 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -59,7 +59,7 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
 }
 
 /* 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;
unsigned int type, clu_offset;
@@ -70,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 & 0x;
+   unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0x;
struct buffer_head *bh;
 
/* check if the given file ID is opened */
@@ -162,7 +162,7 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
ei->hint_bmap.clu = clu.dir;
 
-   ei->rwoffset = ++dentry;
+   *cpos = EXFAT_DEN_TO_B(++dentry);
return 0;
}
 
@@ -178,7 +178,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;
 }
 
@@ -242,12 +242,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, );
+   err = exfat_readdir(inode, , );
if (err) {
/*
 * At least we tried to read a sector.  Move cpos to next sector
@@ -262,13 +260,11 @@ static int exfat_iterate(struct file *filp, struct 
dir_context *ctx)
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) & 0x);
+   (EXFAT_B_TO_DEN(cpos-1) & 0x);
tmp = exfat_iget(sb, i_pos);
if (tmp) {
inum = tmp->i_ino;
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 44dc04520175..e586daf5a2e7 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -263,8 +263,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;
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 4831a39632a1..a92478eabfa4 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -208,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;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 7f90204adef5..70a33d4807c3 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -114,8 +114,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
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,
@@ -567,7 +565,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;

[PATCH] exfat: eliminate dead code in exfat_find()

2020-09-02 Thread Tetsuhiro Kohada
The exfat_find_dir_entry() called by exfat_find() doesn't return -EEXIST.
Therefore, the root-dir information setting is never executed.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c   |   1 -
 fs/exfat/namei.c | 120 +++
 2 files changed, 47 insertions(+), 74 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 573659bfbc55..a9b13ae3f325 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -911,7 +911,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
  */
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 0b12033e1577..b966b9120c9c 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -604,6 +604,8 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
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;
@@ -629,91 +631,63 @@ static int exfat_find(struct inode *dir, struct qstr 
*qname,
dentry = exfat_find_dir_entry(sb, ei, , _name,
num_entries, TYPE_ALL);
 
-   if ((dentry < 0) && (dentry != -EEXIST))
+   if (dentry < 0)
return dentry; /* -error value */
 
memcpy(>dir, , sizeof(struct exfat_chain));
info->entry = dentry;
info->num_subdirs = 0;
 
-   /* root directory itself */
-   if (unlikely(dentry == -EEXIST)) {
-   int num_clu = 0;
+   es = exfat_get_dentry_set(sb, , dentry, ES_2_ENTRIES);
+   if (!es)
+   return -EIO;
+   ep = exfat_get_dentry_cached(es, 0);
+   ep2 = exfat_get_dentry_cached(es, 1);
+
+   info->type = exfat_get_entry_type(ep);
+   info->attr = le16_to_cpu(ep->dentry.file.attr);
+   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);
+   }
 
-   info->type = TYPE_DIR;
-   info->attr = ATTR_SUBDIR;
-   info->flags = ALLOC_FAT_CHAIN;
-   info->start_clu = sbi->root_dir;
-   memset(>crtime, 0, sizeof(info->crtime));
-   memset(>mtime, 0, sizeof(info->mtime));
-   memset(>atime, 0, sizeof(info->atime));
-
-   exfat_chain_set(, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
-   if (exfat_count_num_clusters(sb, , _clu))
-   return -EIO;
-   info->size = num_clu << sbi->cluster_size_bits;
+   exfat_get_entry_time(sbi, >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, >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, >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(, info->start_clu,
+   EXFAT_B_TO_CLU(info->size, sbi), info->flags);
count = exfat_count_dir_entries(sb, );
if (count < 0)
return -EIO;
 
-   info->num_subdirs = count;
-   } else {
-   struct exfat_dentry *ep, *ep2;
-   struct exfat_entry_set_cache *es;
-
-   es = exfat_get_dentry_set(sb, , dentry, ES_2_ENTRIES);
-   if (!es)
-

Re: [PATCH v4 1/5] exfat: integrates dir-entry getting and validation

2020-08-27 Thread Tetsuhiro Kohada

Thank you for your quick review.

On 2020/08/27 12:19, Namjae Jeon wrote:

+   i = ES_INDEX_NAME;
+   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {

Please find the way to access name entries like ep_file, ep_stream
without calling exfat_get_validated_dentry().


Hmm, it's a hard order.
I can't separate length/type validation and extraction.
Sorry, I have no good idea.



@@ -590,17 +587,16 @@ int exfat_remove_entries(struct inode *inode, struct 
exfat_chain *p_dir,  void
exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)  {
int chksum_type = CS_DIR_ENTRY, i;
-   unsigned short chksum = 0;
+   u16 chksum = 0;
struct exfat_dentry *ep;

for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_dentry_cached(es, i);
+   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);

Ditto, You do not need to repeatedly call exfat_get_validated_dentry() for the 
entries
which got from exfat_get_dentry_set().


Even if I could do that, it would be very difficult to implement a checksum 
patch.
It is also difficult to use for rename, move, delete.
(these also have no verification of neme-length and set-checksum)



/* 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), ))
-   goto free_es;
+   es->ep_stream = exfat_get_validated_dentry(es, ES_INDEX_STREAM, 
TYPE_STREAM);
+   if (!es->ep_stream)
+   goto free_es;
+
+   if (max_entries == ES_ALL_ENTRIES) {
+   for (i = 0; i < ES_FILE(es).num_ext; i++)
+   if (!exfat_get_validated_dentry(es, ES_INDEX_STREAM + 
i, TYPE_SECONDARY))
+   goto free_es;
+   for (i = 0; i * EXFAT_FILE_NAME_LEN < ES_STREAM(es).name_len; 
i++)
+   if (!exfat_get_validated_dentry(es, ES_INDEX_NAME + i, 
TYPE_NAME))
+   goto free_es;

Why do you unnecessarily check entries with two loops?
Please refer to the patch I sent.


This order is possible.
However, TYPE_SECONDARY loop will be back as checksum loop.

In the next patch, I can fix the 'TYPE_SECONDARY loop' order.
do you need it?


BR
---
Tetsuhiro Kohada 




Re: [PATCH v3] exfat: integrates dir-entry getting and validation

2020-08-27 Thread Tetsuhiro Kohada




-   /* 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), ))
+   ep = exfat_get_dentry_cached(es, ENTRY_STREAM);
+   if (!ep || ep->type != EXFAT_STREAM)
+   goto free_es;
+   es->de[ENTRY_STREAM] = ep;


The value contained in stream-ext dir-entry should not be used
before validating the EntrySet

checksum.

So I would insert EntrySet checksum validation here.
In that case, the checksum verification loop would be followed by
the TYPE_NAME verification loop, can you acceptable?

Yes. That would be great.


OK.
I'll add TYPE_NAME verification after checksum verification, in next patch.
However, I think it is enough to validate TYPE_NAME when extracting name.
Could you please tell me why you think you need TYPE_NAME validation here?

I've told you on previous mail. This function should return validated
dentry set after checking
file->stream->name in sequence.


Yes. I understand that the current implementation checks in that order.
Sorry, my question was unclear.
Why do you think you should leave the TYPE_NAME validation in this function?
What kind of problem are you worried about if this function does not validate 
TYPE_NAME?
(for preserve the current behavior?)

We have not checked the problem when it is removed because it was implemented
according to the specification from the beginning.


I understand that the main reason to validate TYPE_NAME here is "according to the 
specification".
(No one knows the actual problem)

First, we should validate as 'dir-entry set' by SecondaryCount and SetChecksum 
described
in "6.3 Generic Primary DirectoryEntry Template".

Next, description about validity of 'File dir-entry set' is ...
7.4 File Directory Entry:
... For a File directory entry to be valid, exactly one Stream Extension 
directory entry and at least
one File Name directory entry must immediately follow the File directory entry.
7.7 File Name Directory Entry:
... File Name directory entries are valid only if they immediately follow the 
Stream Extension
directory entry as a consecutive series.

It is possible to validate the above correctly, with either 
exfat_get_dentry_set() or
exfat_get_uniname_from_name_entries().
Is this wrong?


And your v3 patch are
already checking the name entries as TYPE_SECONDARY. And it check them with
TYPE_NAME again in exfat_get_uniname_from_ext_entry(). 


This is according to "6.3 Generic Primary DirectoryEntry Template".
"6.3 Generic Primary DirectoryEntry Template" only required TYPE_SECONDARY.
In v3, there is no checksum validation yet.


If you check TYPE_NAME
with stream->name_len, We don't need to perform the loop for extracting
filename from the name entries if stream->name_len or name entry is invalid.


Don't worry, it's a rare case.
(Do you care about the run-time costs?)


And I request to prove why we do not need to validate name entries in this
function calling from somewhere. 


If you need, it's okey to validate in both.
However, name-length and type validation and name-extraction should not be 
separated.
These are closely related, so these should be placed physically and temporally 
close.

Well, why it's unnecessary.
Both can be validate correctly, as I wrote before.
And, I don't really trust the verification with TYPE_NAME.
(reliability of validation as 'file dir-entry set' by checksum is much higher)


So as I suggested earlier, You can make it
with an argument flags so that we skip the validation.


No need skip the validation, I think.
The run-time costs for validation are pretty low.
The reason I want to remove the validation is because I want to keep the code 
simple.
(KISS principle)


BR
---
Tetsuhiro Kohada 



[PATCH v4 4/5] exfat: add dir-entry set checksum validation

2020-08-26 Thread Tetsuhiro Kohada
Add checksum validation for dir-entry set when getting it.
exfat_calc_entry_set_chksum_with() also validates entry-type.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add error log if checksum mismatch
Changes in v3:
 - Nothing
Changes in v4:
 - Into patch series '[PATCH v4] exfat: integrates dir-entry getting and 
validation'

 fs/exfat/dir.c | 34 +++---
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index cd37091844fa..d4beea796708 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -565,18 +565,26 @@ int exfat_remove_entries(struct inode *inode, struct 
exfat_chain *p_dir,
return 0;
 }
 
-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+static int exfat_calc_entry_set_chksum(struct exfat_entry_set_cache *es, u16 
*chksum)
 {
-   int chksum_type = CS_DIR_ENTRY, i;
-   u16 chksum = 0;
struct exfat_dentry *ep;
+   int i;
 
-   for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
-   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
-chksum_type);
-   chksum_type = CS_DEFAULT;
+   *chksum = exfat_calc_chksum16(es->ep_file, DENTRY_SIZE, 0, 
CS_DIR_ENTRY);
+   for (i = 0; i < ES_FILE(es).num_ext; i++) {
+   ep = exfat_get_validated_dentry(es, 1 + i, TYPE_SECONDARY);
+   if (!ep)
+   return -EIO;
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, *chksum, 
CS_DEFAULT);
}
+   return 0;
+}
+
+void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+{
+   u16 chksum;
+
+   exfat_calc_entry_set_chksum(es, );
ES_FILE(es).checksum = cpu_to_le16(chksum);
es->modified = true;
 }
@@ -776,6 +784,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_entry_set_cache *es;
struct buffer_head *bh;
+   u16 chksum;
 
if (p_dir->dir == DIR_DELETED) {
exfat_err(sb, "access to deleted dentry");
@@ -839,9 +848,12 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
goto free_es;
 
if (max_entries == ES_ALL_ENTRIES) {
-   for (i = 0; i < ES_FILE(es).num_ext; i++)
-   if (!exfat_get_validated_dentry(es, ES_INDEX_STREAM + 
i, TYPE_SECONDARY))
-   goto free_es;
+   if (exfat_calc_entry_set_chksum(es, ) ||
+  chksum != le16_to_cpu(ES_FILE(es).checksum)) {
+   exfat_err(sb, "invalid entry-set checksum (entry : 
0x%08x, set-checksum : 0x%04x, checksum : 0x%04x)",
+ entry, le16_to_cpu(ES_FILE(es).checksum), 
chksum);
+   goto free_es;
+   }
for (i = 0; i * EXFAT_FILE_NAME_LEN < ES_STREAM(es).name_len; 
i++)
if (!exfat_get_validated_dentry(es, ES_INDEX_NAME + i, 
TYPE_NAME))
goto free_es;
-- 
2.25.1



[PATCH v4 5/5] exfat: write only modified part of dir-entry set

2020-08-26 Thread Tetsuhiro Kohada
Currently exfat_free_dentry_set() writes all of dir-entry set.
Change it to write only the modified part of dir-entry set.
And, Integrate exfat_free_dentry_set() and
exfat_update_dir_chksum_with_entry_set() as exfat_put_dentry_set().

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Based on v2 'name-length' patches
Changes in v3:
 - Nothing
Changes in v4:
 - Into patch series '[PATCH v4] exfat: integrates dir-entry getting and 
validation'

 fs/exfat/dir.c  | 31 +++
 fs/exfat/exfat_fs.h |  4 +---
 fs/exfat/file.c |  3 +--
 fs/exfat/inode.c|  6 ++
 fs/exfat/namei.c|  4 ++--
 5 files changed, 21 insertions(+), 27 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index d4beea796708..78539d91d3c5 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -129,7 +129,7 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
dir_entry->size = le64_to_cpu(ES_STREAM(es).valid_size);
 
err = exfat_get_uniname_from_name_entries(es, 
_name);
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
if (err)
return err;
 
@@ -580,21 +580,21 @@ static int exfat_calc_entry_set_chksum(struct 
exfat_entry_set_cache *es, u16 *ch
return 0;
 }
 
-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
-{
-   u16 chksum;
-
-   exfat_calc_entry_set_chksum(es, );
-   ES_FILE(es).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 modified, int 
sync)
 {
int i, err = 0;
 
-   if (es->modified)
-   err = exfat_update_bhs(es->bh, es->num_bh, sync);
+   if (modified) {
+   int off = es->start_off + (modified - 1) * DENTRY_SIZE;
+   int modified_bh = min(EXFAT_B_TO_BLK(off, es->sb) + 1, 
es->num_bh);
+   u16 chksum;
+
+   err = exfat_calc_entry_set_chksum(es, );
+   if (!err) {
+   ES_FILE(es).checksum = cpu_to_le16(chksum);
+   err = exfat_update_bhs(es->bh, modified_bh, sync);
+   }
+   }
 
for (i = 0; i < es->num_bh; i++)
if (err)
@@ -800,7 +800,6 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
if (!es)
return NULL;
es->sb = sb;
-   es->modified = false;
es->num_entries = 1;
 
/* byte offset in cluster */
@@ -861,7 +860,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
return es;
 
 free_es:
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
return NULL;
 }
 
@@ -973,7 +972,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
!exfat_get_uniname_from_name_entries(es, 
_name) &&
!exfat_uniname_ncmp(sb, p_uniname->name, 
uni_name.name, name_len);
 
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
 
if (found) {
/* set the last used position as hint */
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 7057e64b405d..3354512629ac 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -170,7 +170,6 @@ 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];
@@ -454,7 +453,6 @@ 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);
-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,
@@ -469,7 +467,7 @@ struct exfat_dentry *exfat_get_validated_dentry(struct 
exfat_entry_set_cache *es
int num, unsigned int type);
 struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
struct exfat_chain *p_dir, int entry, int max_entries);
-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 modified, int 
sync);
 int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
 
 /* inode.c */

[PATCH v4 3/5] exfat: unify name extraction

2020-08-26 Thread Tetsuhiro Kohada
Name extraction in exfat_find_dir_entry() also doesn't care NameLength,
so the name may be incorrect.
Replace the name extraction in exfat_find_dir_entry() with using
exfat_entry_set_cache and exfat_get_uniname_from_name_entries(),
like exfat_readdir().
Replace the name extraction with using exfat_entry_set_cache and
exfat_get_uniname_from_name_entries(), like exfat_readdir().
And, remove unused functions/parameters.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add error check when extracting name
 - Remove temporary exfat_get_dentry_set() with ES_2_ENTRIES
 - Remove duplicate parts in commit message
Changes in v3:
 - Nothing
Changes in v4:
 - Into patch series '[PATCH v4] exfat: integrates dir-entry getting and 
validation'

 fs/exfat/dir.c  | 156 +---
 fs/exfat/exfat_fs.h |   2 +-
 fs/exfat/namei.c|   4 +-
 3 files changed, 32 insertions(+), 130 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 99d9e6d119d6..cd37091844fa 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -10,24 +10,6 @@
 #include "exfat_raw.h"
 #include "exfat_fs.h"
 
-static int exfat_extract_uni_name(struct exfat_dentry *ep,
-   unsigned short *uniname)
-{
-   int i, len = 0;
-
-   for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
-   *uniname = le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
-   if (*uniname == 0x0)
-   return len;
-   uniname++;
-   len++;
-   }
-
-   *uniname = 0x0;
-   return len;
-
-}
-
 static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
struct exfat_uni_name *uniname)
 {
@@ -871,13 +853,6 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
return NULL;
 }
 
-enum {
-   DIRENT_STEP_FILE,
-   DIRENT_STEP_STRM,
-   DIRENT_STEP_NAME,
-   DIRENT_STEP_SECD,
-};
-
 /*
  * return values:
  *   >= 0  : return dir entiry position with the name in dir
@@ -887,13 +862,12 @@ 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)
+   int num_entries)
 {
-   int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
-   int order, step, name_len = 0;
+   int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0;
+   int name_len = 0;
int dentries_per_clu, num_empty = 0;
unsigned int entry_type;
-   unsigned short *uniname = NULL;
struct exfat_chain clu;
struct exfat_hint *hint_stat = >hint_stat;
struct exfat_hint_femp candi_empty;
@@ -911,27 +885,34 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
 
candi_empty.eidx = EXFAT_HINT_NONE;
 rewind:
-   order = 0;
-   step = DIRENT_STEP_FILE;
while (clu.dir != EXFAT_EOF_CLUSTER) {
i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) {
struct exfat_dentry *ep;
struct buffer_head *bh;
+   struct exfat_entry_set_cache *es;
+   struct exfat_uni_name uni_name;
+   u16 name_hash;
+   bool found;
 
if (rewind && dentry == end_eidx)
goto not_found;
 
+   /* skip secondary dir-entries in previous dir-entry set 
*/
+   if (num_ext) {
+   num_ext--;
+   continue;
+   }
+
ep = exfat_get_dentry(sb, , i, , NULL);
if (!ep)
return -EIO;
 
entry_type = exfat_get_entry_type(ep);
+   brelse(bh);
 
if (entry_type == TYPE_UNUSED ||
entry_type == TYPE_DELETED) {
-   step = DIRENT_STEP_FILE;
-
num_empty++;
if (candi_empty.eidx == EXFAT_HINT_NONE &&
num_empty == 1) {
@@ -956,7 +937,6 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
}
}
 
-   brelse(bh);
if (entry_type == TYPE_UNUSED)
goto not_found;
continue;
@@ -965,80 +945,30 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
num_empty = 0;
candi_empty.eidx = EX

[PATCH v4 2/5] exfat: add NameLength check when extracting name

2020-08-26 Thread Tetsuhiro Kohada
The current implementation doesn't care NameLength when extracting
the name from Name dir-entries, so the name may be incorrect.
(Without null-termination, Insufficient Name dir-entry, etc)
Add a NameLength check when extracting the name from Name dir-entries
to extract correct name.
And, change to get the information of file/stream-ext dir-entries
via the member variable of exfat_entry_set_cache.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add error check when extracting name
 - Change error from EIO to EINVAL when the name length is invalid
 - Correct the spelling in commit messages
Changes in v3:
 - Nothing
Changes in v4:
 - Into patch series '[PATCH v4] exfat: integrates dir-entry getting and 
validation'

 fs/exfat/dir.c | 87 +-
 1 file changed, 43 insertions(+), 44 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index bb3c20bac422..99d9e6d119d6 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -28,16 +28,15 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep,
 
 }
 
-static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
-   struct exfat_chain *p_dir, int entry, unsigned short *uniname)
+static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
+   struct exfat_uni_name *uniname)
 {
-   int i;
-   struct exfat_entry_set_cache *es;
+   int n, l, i;
struct exfat_dentry *ep;
 
-   es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
-   if (!es)
-   return;
+   uniname->name_len = ES_STREAM(es).name_len;
+   if (uniname->name_len == 0)
+   return -EINVAL;
 
/*
 * First entry  : file entry
@@ -45,24 +44,26 @@ 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.
 */
-
-   i = ES_INDEX_NAME;
-   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
-   exfat_extract_uni_name(ep, uniname);
-   uniname += EXFAT_FILE_NAME_LEN;
+   for (l = 0, n = ES_INDEX_NAME; l < uniname->name_len; n++) {
+   ep = exfat_get_validated_dentry(es, n, TYPE_NAME);
+   if (!ep)
+   return -EIO;
+   for (i = 0; l < uniname->name_len && i < EXFAT_FILE_NAME_LEN; 
i++, l++)
+   uniname->name[l] = 
le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
}
-
-   exfat_free_dentry_set(es, false);
+   uniname->name[l] = 0;
+   return 0;
 }
 
 /* read a directory entry from the opened directory */
 static int exfat_readdir(struct inode *inode, 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, err;
unsigned int type, clu_offset;
sector_t sector;
struct exfat_chain dir, clu;
struct exfat_uni_name uni_name;
+   struct exfat_entry_set_cache *es;
struct exfat_dentry *ep;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
@@ -114,47 +115,45 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
return -EIO;
 
type = exfat_get_entry_type(ep);
-   if (type == TYPE_UNUSED) {
-   brelse(bh);
+   brelse(bh);
+
+   if (type == TYPE_UNUSED)
break;
-   }
 
-   if (type != TYPE_FILE && type != TYPE_DIR) {
-   brelse(bh);
+   if (type != TYPE_FILE && type != TYPE_DIR)
continue;
-   }
 
-   dir_entry->attr = le16_to_cpu(ep->dentry.file.attr);
+   es = exfat_get_dentry_set(sb, , dentry, 
ES_ALL_ENTRIES);
+   if (!es)
+   return -EIO;
+
+   dir_entry->attr = le16_to_cpu(ES_FILE(es).attr);
exfat_get_entry_time(sbi, _entry->crtime,
-   ep->dentry.file.create_tz,
-   ep->dentry.file.create_time,
-   ep->dentry.file.create_date,
-   ep->dentry.file.create_time_cs);
+   ES_FILE(es).create_tz,
+   ES_FILE(es).create_time,
+   ES_FILE(es).create_date,
+   ES_FILE(es).create_time_cs);
exfat_get_entry_time(sbi,

[PATCH v4 1/5] exfat: integrates dir-entry getting and validation

2020-08-26 Thread Tetsuhiro Kohada
Add validation for num, bh and type on getting dir-entry.
Renamed exfat_get_dentry_cached() to exfat_get_validated_dentry() due to
a change in functionality.

Integrate type-validation with simplified.
This will also recognize a dir-entry set that contains 'benign secondary'
dir-entries.

Pre-Validated 'file' and 'stream-ext' dir-entries are provided via
ES_FILE/ES_STREAM macros.

And, rename TYPE_EXTEND to TYPE_NAME.

Suggested-by: Sungjong Seo 
Suggested-by: Namjae Jeon 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Change verification order
 - Verification loop start with index 2
Changes in v3
 - Fix indent 
 - Fix comment of exfat_get_dentry_set()
 - Add de_file/de_stream in exfat_entry_set_cache
 - Add srtuct tag name for each dir-entry type in exfat_dentry
 - Add description about de_file/de_stream to commit-log
Changes in v4
 - Replace de_file/de_stream with ep_file/ep_stream in exfat_entry_set_cache
 - Remove srtuct tag names added in v3
 - Add ES_INDEX_XXX macro definitions
 - Add ES_FILE/ES_STREAM macro definitions
 - Replace some EXFAT_FIRST_CLUSTER with ES_INDEX_NAME
 - Modify commit-log

 fs/exfat/dir.c   | 155 ++-
 fs/exfat/exfat_fs.h  |  19 --
 fs/exfat/exfat_raw.h |  14 ++--
 fs/exfat/file.c  |  25 +++
 fs/exfat/inode.c |  49 ++
 fs/exfat/namei.c |  36 +-
 6 files changed, 132 insertions(+), 166 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 573659bfbc55..bb3c20bac422 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -33,6 +33,7 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
 {
int i;
struct exfat_entry_set_cache *es;
+   struct exfat_dentry *ep;
 
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
if (!es)
@@ -44,13 +45,9 @@ 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);
-
-   /* end of name entry */
-   if (exfat_get_entry_type(ep) != TYPE_EXTEND)
-   break;
 
+   i = ES_INDEX_NAME;
+   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
exfat_extract_uni_name(ep, uniname);
uniname += EXFAT_FILE_NAME_LEN;
}
@@ -372,7 +369,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
if (ep->type == EXFAT_STREAM)
return TYPE_STREAM;
if (ep->type == EXFAT_NAME)
-   return TYPE_EXTEND;
+   return TYPE_NAME;
if (ep->type == EXFAT_ACL)
return TYPE_ACL;
return TYPE_CRITICAL_SEC;
@@ -388,7 +385,7 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, 
unsigned int type)
ep->type &= EXFAT_DELETE;
} else if (type == TYPE_STREAM) {
ep->type = EXFAT_STREAM;
-   } else if (type == TYPE_EXTEND) {
+   } else if (type == TYPE_NAME) {
ep->type = EXFAT_NAME;
} else if (type == TYPE_BITMAP) {
ep->type = EXFAT_BITMAP;
@@ -421,7 +418,7 @@ static void exfat_init_name_entry(struct exfat_dentry *ep,
 {
int i;
 
-   exfat_set_entry_type(ep, TYPE_EXTEND);
+   exfat_set_entry_type(ep, TYPE_NAME);
ep->dentry.name.flags = 0x0;
 
for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
@@ -550,7 +547,7 @@ int exfat_init_ext_entry(struct inode *inode, struct 
exfat_chain *p_dir,
exfat_update_bh(bh, sync);
brelse(bh);
 
-   for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
+   for (i = ES_INDEX_NAME; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, , );
if (!ep)
return -EIO;
@@ -590,17 +587,16 @@ int exfat_remove_entries(struct inode *inode, struct 
exfat_chain *p_dir,
 void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
 {
int chksum_type = CS_DIR_ENTRY, i;
-   unsigned short chksum = 0;
+   u16 chksum = 0;
struct exfat_dentry *ep;
 
for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_dentry_cached(es, i);
+   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
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_FILE(es).checksum = cpu_to_le16(chksum);
es->modified = true;
 }
 
@@ -741,92 +737,63 @@ struct

Re: [PATCH v3] exfat: integrates dir-entry getting and validation

2020-08-26 Thread Tetsuhiro Kohada

Thank you for quick reply!

On 2020/08/26 13:19, Namjae Jeon wrote:

On 2020/08/26 10:03, Namjae Jeon wrote:

Second: Range validation and type validation should not be separated.
When I started making this patch, I intended to add only range validation.
However, after the caller gets the ep, the type validation follows.
Get ep, null check of ep (= range verification), type verification is a series 
of procedures.
There would be no reason to keep them independent anymore.
Range and type validation is enforced when the caller uses ep.

You can add a validate flags as argument of exfat_get_dentry_set(), e.g. none, 
basic and strict.
none : only range validation.
basic : range + type validation.
strict : range + type + checksum and name length, etc.


Currently, various types of verification will not be needed.
Let's add it when we need it.



-   /* 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), ))
+   ep = exfat_get_dentry_cached(es, ENTRY_STREAM);
+   if (!ep || ep->type != EXFAT_STREAM)
+   goto free_es;
+   es->de[ENTRY_STREAM] = ep;


The value contained in stream-ext dir-entry should not be used before 
validating the EntrySet

checksum.

So I would insert EntrySet checksum validation here.
In that case, the checksum verification loop would be followed by the
TYPE_NAME verification loop, can you acceptable?

Yes. That would be great.


OK.
I'll add TYPE_NAME verification after checksum verification, in next patch.
However, I think it is enough to validate TYPE_NAME when extracting name.
Could you please tell me why you think you need TYPE_NAME validation here?

I've told you on previous mail. This function should return validated dentry 
set after checking
file->stream->name in sequence.


Yes. I understand that the current implementation checks in that order.
Sorry, my question was unclear.
Why do you think you should leave the TYPE_NAME validation in this function?
What kind of problem are you worried about if this function does not validate 
TYPE_NAME?
(for preserve the current behavior?)

Don't worry, I will add TYPE_NAME verification to the v4 patch.
I will post it later today.

BR
---
Tetsuhiro Kohada 


Re: [PATCH v3] exfat: integrates dir-entry getting and validation

2020-08-25 Thread Tetsuhiro Kohada

On 2020/08/26 10:03, Namjae Jeon wrote:

Second: Range validation and type validation should not be separated.
When I started making this patch, I intended to add only range validation.
However, after the caller gets the ep, the type validation follows.
Get ep, null check of ep (= range verification), type verification is a series 
of procedures.
There would be no reason to keep them independent anymore.
Range and type validation is enforced when the caller uses ep.

You can add a validate flags as argument of exfat_get_dentry_set(), e.g. none, 
basic and strict.
none : only range validation.
basic : range + type validation.
strict : range + type + checksum and name length, etc.


Currently, various types of verification will not be needed.
Let's add it when we need it.
  

-   /* 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), ))
+   ep = exfat_get_dentry_cached(es, ENTRY_STREAM);
+   if (!ep || ep->type != EXFAT_STREAM)
+   goto free_es;
+   es->de[ENTRY_STREAM] = ep;


The value contained in stream-ext dir-entry should not be used before 
validating the EntrySet checksum.
So I would insert EntrySet checksum validation here.
In that case, the checksum verification loop would be followed by the TYPE_NAME 
verification loop, can
you acceptable?

Yes. That would be great.


OK.
I'll add TYPE_NAME verification after checksum verification, in next patch.
However, I think it is enough to validate TYPE_NAME when extracting name.
Could you please tell me why you think you need TYPE_NAME validation here?


BR
---
Tetsuhiro Kohada 




[PATCH] exfat: fix pointer error checking

2020-08-25 Thread Tetsuhiro Kohada
Fix missing result check of exfat_build_inode().
And use PTR_ERR_OR_ZERO instead of PTR_ERR.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/namei.c | 13 ++---
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 2aff6605fecc..0b12033e1577 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -578,7 +578,8 @@ static int exfat_create(struct inode *dir, struct dentry 
*dentry, umode_t mode,
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , i_pos);
-   if (IS_ERR(inode))
+   err = PTR_ERR_OR_ZERO(inode);
+   if (err)
goto unlock;
 
inode_inc_iversion(inode);
@@ -745,10 +746,9 @@ static struct dentry *exfat_lookup(struct inode *dir, 
struct dentry *dentry,
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , 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);
@@ -890,10 +890,9 @@ static int exfat_mkdir(struct inode *dir, struct dentry 
*dentry, umode_t mode)
 
i_pos = exfat_make_i_pos();
inode = exfat_build_inode(sb, , 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 =
-- 
2.25.1



Re: [PATCH 2/2] exfat: unify name extraction

2020-08-25 Thread Tetsuhiro Kohada




+   exfat_free_dentry_set(es, false);
+
+   if (!exfat_uniname_ncmp(sb,
+   p_uniname->name,
+   uni_name.name,
+   name_len)) {
+   /* set the last used position as hint */
+   hint_stat->clu = clu.dir;
+   hint_stat->eidx = dentry;


eidx and clu of hint_stat should have one for the next entry we'll
start looking for.
Did you intentionally change the concept?


Yes, this is intentional.
Essentially, the "Hint" concept is to reduce the next seek cost with
minimal cost.
There is a difference in the position of the hint, but the concept is the
same.
As you can see, the patched code strategy doesn't move from current
position.
Basically, the original code strategy is advancing only one dentry.(It's
the "minimum cost") However, when it reaches the cluster boundary, it gets
the next cluster and error handling.


I didn't get exactly what "original code" is.
Do you mean whole code lines for exfat_find_dir_entry()?
Or just only for handling the hint in it?


My intention is the latter.



The strategy of original code for hint is advancing not one dentry but one 
dentry_set.


That's the strategy as a whole code.
But all it does to get a hint after "found" is to advance one entry.
In the original code, the 'dentry' variable points to the end of the EntrySet when 
"found",
so it can point to the next EntrySet by simply advancing one entry.
(However, it may need to scan the cluster chain)



If a hint position is not moved to next like the patched code,
caller have to start at old dentry_set that could be already loaded on dentry 
cache.

Let's think the case of searching through all files sequentially.
The patched code should check twice per a file.


This is the case when all requests find the specified file, right?

Sure, the request will evaluate the same EntrySet as before found.
However, the cost to spend is different from the last time.
The current request looks for a different name than the last request.
In most cases, length and hash are different from the last EntrySet.
Therefore, the last EntrySet just skips dir-entries by num_ext.
There is no string comparison with ignores cases. <- This cost is high
The cost of skipping dir-entries is much less than the string comparison.


No better than the original policy.


In this patch, when "found", the 'dentry' variable still points to the 
beginning of the EntrySet.
In this case, I thought "stay here" was a very efficient hint at a minimal cost.
As a whole, I think that the cost has been reduced...



Getting the next cluster The error handling already exists at the end of
the while loop, so the code is duplicated.
These costs should be paid next time and are no longer the "minimum cost".


I agree with your words, "These costs should be paid next time".
If so, how about moving the cluster handling for a hint dentry to
the beginning of the function while keeping the original policy?


My first idea was
hint_stat->eidx = dentry + 1 + num_ext;

However, in the current hint, offset ((hint_stat->eidx) and cluster number 
(hint_stat->clu) in the directory are paired.
It was difficult to change only one of values.
So I'm trying to make a 'new hint' where the offset and cluster number aren't 
linked.



BTW, this patch is not related to the hint code.
I think it would be better to keep the original code in this patch and improve 
it with a separate patch.


I think so, too.
I'll try another patch.


BR
---
Tetsuhiro Kohada 




Re: [PATCH v3] exfat: integrates dir-entry getting and validation

2020-08-25 Thread Tetsuhiro Kohada
   info->attr = le16_to_cpu(ES_FILE(es).attr);
+   info->size = le64_to_cpu(ES_STREAM(es).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);
+   info->flags = ES_STREAM(es).flags;
+   info->start_clu = le32_to_cpu(ES_STREAM(es).start_clu);
}
  
  		if (ei->start_clu == EXFAT_FREE_CLUSTER) {

@@ -688,19 +684,19 @@ static int exfat_find(struct inode *dir, struct qstr 
*qname,
}
  
  		exfat_get_entry_time(sbi, >crtime,

-   ep->dentry.file.create_tz,
-   ep->dentry.file.create_time,
-   ep->dentry.file.create_date,
-   ep->dentry.file.create_time_cs);
+   ES_FILE(es).create_tz,
+   ES_FILE(es).create_time,
+   ES_FILE(es).create_date,
+   ES_FILE(es).create_time_cs);
exfat_get_entry_time(sbi, >mtime,
-   ep->dentry.file.modify_tz,
-   ep->dentry.file.modify_time,
-   ep->dentry.file.modify_date,
-   ep->dentry.file.modify_time_cs);
+   ES_FILE(es).modify_tz,
+   ES_FILE(es).modify_time,
+   ES_FILE(es).modify_date,
+       ES_FILE(es).modify_time_cs);
exfat_get_entry_time(sbi, >atime,
-   ep->dentry.file.access_tz,
-   ep->dentry.file.access_time,
-   ep->dentry.file.access_date,
+   ES_FILE(es).access_tz,
+   ES_FILE(es).access_time,
+   ES_FILE(es).access_date,
0);
exfat_free_dentry_set(es, false);
  



BR
---
Tetsuhiro Kohada 


[PATCH v2 2/2] exfat: write only modified part of dir-entry set

2020-08-20 Thread Tetsuhiro Kohada
Currently exfat_free_dentry_set() writes all of dir-entry set.
Change it to write only the modified part of dir-entry set.
And, Integrate exfat_free_dentry_set() and
exfat_update_dir_chksum_with_entry_set() as exfat_put_dentry_set().

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'
  '[PATCH v2] exfat: add NameLength check when extracting name'
  '[PATCH v2] exfat: unify name extraction'

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Based on v2 'name-length' patches

 fs/exfat/dir.c  | 31 +++
 fs/exfat/exfat_fs.h |  4 +---
 fs/exfat/file.c |  3 +--
 fs/exfat/inode.c|  6 ++
 fs/exfat/namei.c|  4 ++--
 5 files changed, 21 insertions(+), 27 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 6f9de364a919..ad6fef854c30 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -129,7 +129,7 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
dir_entry->size = 
le64_to_cpu(es->de_stream->valid_size);
 
err = exfat_get_uniname_from_name_entries(es, 
_name);
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
if (err)
return err;
 
@@ -581,21 +581,21 @@ static int exfat_calc_entry_set_chksum(struct 
exfat_entry_set_cache *es, u16 *ch
return 0;
 }
 
-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
-{
-   u16 chksum;
-
-   exfat_calc_entry_set_chksum(es, );
-   es->de_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 modified, int 
sync)
 {
int i, err = 0;
 
-   if (es->modified)
-   err = exfat_update_bhs(es->bh, es->num_bh, sync);
+   if (modified) {
+   int off = es->start_off + (modified - 1) * DENTRY_SIZE;
+   int modified_bh = min(EXFAT_B_TO_BLK(off, es->sb) + 1, 
es->num_bh);
+   u16 chksum;
+
+   err = exfat_calc_entry_set_chksum(es, );
+   if (!err) {
+   es->de_file->checksum = cpu_to_le16(chksum);
+   err = exfat_update_bhs(es->bh, modified_bh, sync);
+   }
+   }
 
for (i = 0; i < es->num_bh; i++)
if (err)
@@ -802,7 +802,6 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
if (!es)
return NULL;
es->sb = sb;
-   es->modified = false;
es->num_entries = 1;
 
/* byte offset in cluster */
@@ -861,7 +860,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
return es;
 
 free_es:
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
return NULL;
 }
 
@@ -973,7 +972,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
!exfat_get_uniname_from_name_entries(es, 
_name) &&
!exfat_uniname_ncmp(sb, p_uniname->name, 
uni_name.name, name_len);
 
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
 
if (found) {
/* set the last used position as hint */
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 489881bf9bde..4dc5ce857d1f 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -170,7 +170,6 @@ 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];
@@ -452,7 +451,6 @@ 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);
-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,
@@ -467,7 +465,7 @@ struct exfat_dentry *exfat_get_validated_dentry(struct 
exfat_entry_set_cache *es
int num, unsigned int type);
 struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
struct exfat_chain *p_dir, int entry, int max_entries);
-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 modified, int 
sync);

[PATCH v2 1/2] exfat: add dir-entry set checksum validation

2020-08-20 Thread Tetsuhiro Kohada
Add checksum validation for dir-entry set when getting it.
exfat_calc_entry_set_chksum_with() also validates entry-type.

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add error log if checksum mismatch

 fs/exfat/dir.c | 36 
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 0b42544e6340..6f9de364a919 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -565,18 +565,27 @@ int exfat_remove_entries(struct inode *inode, struct 
exfat_chain *p_dir,
return 0;
 }
 
-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+static int exfat_calc_entry_set_chksum(struct exfat_entry_set_cache *es, u16 
*chksum)
 {
-   int chksum_type = CS_DIR_ENTRY, i;
-   unsigned short chksum = 0;
struct exfat_dentry *ep;
+   int i;
 
-   for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
-   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
-chksum_type);
-   chksum_type = CS_DEFAULT;
+   ep = container_of(es->de_file, struct exfat_dentry, dentry.file);
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+   for (i = 0; i < es->de_file->num_ext; i++) {
+   ep = exfat_get_validated_dentry(es, 1 + i, TYPE_SECONDARY);
+   if (!ep)
+   return -EIO;
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, *chksum, 
CS_DEFAULT);
}
+   return 0;
+}
+
+void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+{
+   u16 chksum;
+
+   exfat_calc_entry_set_chksum(es, );
es->de_file->checksum = cpu_to_le16(chksum);
es->modified = true;
 }
@@ -777,6 +786,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
struct exfat_entry_set_cache *es;
struct exfat_dentry *ep;
struct buffer_head *bh;
+   u16 chksum;
 
if (p_dir->dir == DIR_DELETED) {
exfat_err(sb, "access to deleted dentry");
@@ -841,11 +851,13 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
goto free_es;
es->de_stream = >dentry.stream;
 
-   for (i = 2; i < es->num_entries; i++) {
-   if (!exfat_get_validated_dentry(es, i, TYPE_SECONDARY))
-   goto free_es;
+   if (max_entries == ES_ALL_ENTRIES &&
+   ((exfat_calc_entry_set_chksum(es, ) ||
+ chksum != le16_to_cpu(es->de_file->checksum {
+   exfat_err(sb, "invalid entry-set checksum (entry : 0x%08x, 
set-checksum : 0x%04x, checksum : 0x%04x)",
+ entry, le16_to_cpu(es->de_file->checksum), chksum);
+   goto free_es;
}
-
return es;
 
 free_es:
-- 
2.25.1



[PATCH v2 1/2] exfat: add NameLength check when extracting name

2020-08-19 Thread Tetsuhiro Kohada
The current implementation doesn't care NameLength when extracting
the name from Name dir-entries, so the name may be incorrect.
(Without null-termination, Insufficient Name dir-entries, etc)
Add a NameLength check when extracting the name from Name dir-entries
to extract correct name.
And, change to get the information of file/stream-ext dir-entries
via the member variable in exfat_entry_set_cache.

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add error check when extracting name
 - Change error from EIO to EINVAL when the name length is invalid
 - Correct the spelling in commit messages

 fs/exfat/dir.c | 87 +-
 1 file changed, 43 insertions(+), 44 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 91cdbede0fd1..08ebfcdd66a0 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -28,16 +28,15 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep,
 
 }
 
-static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
-   struct exfat_chain *p_dir, int entry, unsigned short *uniname)
+static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
+   struct exfat_uni_name *uniname)
 {
-   int i;
-   struct exfat_entry_set_cache *es;
+   int n, l, i;
struct exfat_dentry *ep;
 
-   es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
-   if (!es)
-   return;
+   uniname->name_len = es->de_stream->name_len;
+   if (uniname->name_len == 0)
+   return -EINVAL;
 
/*
 * First entry  : file entry
@@ -45,24 +44,26 @@ 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.
 */
-
-   i = 2;
-   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
-   exfat_extract_uni_name(ep, uniname);
-   uniname += EXFAT_FILE_NAME_LEN;
+   for (l = 0, n = 2; l < uniname->name_len; n++) {
+   ep = exfat_get_validated_dentry(es, n, TYPE_NAME);
+   if (!ep)
+   return -EIO;
+   for (i = 0; l < uniname->name_len && i < EXFAT_FILE_NAME_LEN; 
i++, l++)
+   uniname->name[l] = 
le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
}
-
-   exfat_free_dentry_set(es, false);
+   uniname->name[l] = 0;
+   return 0;
 }
 
 /* read a directory entry from the opened directory */
 static int exfat_readdir(struct inode *inode, 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, err;
unsigned int type, clu_offset;
sector_t sector;
struct exfat_chain dir, clu;
struct exfat_uni_name uni_name;
+   struct exfat_entry_set_cache *es;
struct exfat_dentry *ep;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
@@ -114,47 +115,45 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
return -EIO;
 
type = exfat_get_entry_type(ep);
-   if (type == TYPE_UNUSED) {
-   brelse(bh);
+   brelse(bh);
+
+   if (type == TYPE_UNUSED)
break;
-   }
 
-   if (type != TYPE_FILE && type != TYPE_DIR) {
-   brelse(bh);
+   if (type != TYPE_FILE && type != TYPE_DIR)
continue;
-   }
 
-   dir_entry->attr = le16_to_cpu(ep->dentry.file.attr);
+   es = exfat_get_dentry_set(sb, , dentry, 
ES_ALL_ENTRIES);
+   if (!es)
+   return -EIO;
+
+   dir_entry->attr = le16_to_cpu(es->de_file->attr);
exfat_get_entry_time(sbi, _entry->crtime,
-   ep->dentry.file.create_tz,
-   ep->dentry.file.create_time,
-   ep->dentry.file.create_date,
-   ep->dentry.file.create_time_cs);
+   es->de_file->create_tz,
+   es->de_file->create_time,
+   es->de_file->create_date,
+   es->de_file->create_time_cs);
 

[PATCH v2 2/2] exfat: unify name extraction

2020-08-19 Thread Tetsuhiro Kohada
Name extraction in exfat_find_dir_entry() also doesn't care NameLength,
so the name may be incorrect.
Replace the name extraction in exfat_find_dir_entry() with using
exfat_entry_set_cache and exfat_get_uniname_from_name_entries(),
like exfat_readdir().
And, remove unused functions/parameters.

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add error check when extracting name
 - Remove temporary exfat_get_dentry_set() with ES_2_ENTRIES
 - Remove duplicate parts in commit message

 fs/exfat/dir.c  | 156 +---
 fs/exfat/exfat_fs.h |   2 +-
 fs/exfat/namei.c|   4 +-
 3 files changed, 32 insertions(+), 130 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 08ebfcdd66a0..0b42544e6340 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -10,24 +10,6 @@
 #include "exfat_raw.h"
 #include "exfat_fs.h"
 
-static int exfat_extract_uni_name(struct exfat_dentry *ep,
-   unsigned short *uniname)
-{
-   int i, len = 0;
-
-   for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
-   *uniname = le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
-   if (*uniname == 0x0)
-   return len;
-   uniname++;
-   len++;
-   }
-
-   *uniname = 0x0;
-   return len;
-
-}
-
 static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
struct exfat_uni_name *uniname)
 {
@@ -871,13 +853,6 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
return NULL;
 }
 
-enum {
-   DIRENT_STEP_FILE,
-   DIRENT_STEP_STRM,
-   DIRENT_STEP_NAME,
-   DIRENT_STEP_SECD,
-};
-
 /*
  * return values:
  *   >= 0  : return dir entiry position with the name in dir
@@ -887,13 +862,12 @@ 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)
+   int num_entries)
 {
-   int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
-   int order, step, name_len = 0;
+   int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0;
+   int name_len = 0;
int dentries_per_clu, num_empty = 0;
unsigned int entry_type;
-   unsigned short *uniname = NULL;
struct exfat_chain clu;
struct exfat_hint *hint_stat = >hint_stat;
struct exfat_hint_femp candi_empty;
@@ -911,27 +885,34 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
 
candi_empty.eidx = EXFAT_HINT_NONE;
 rewind:
-   order = 0;
-   step = DIRENT_STEP_FILE;
while (clu.dir != EXFAT_EOF_CLUSTER) {
i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) {
struct exfat_dentry *ep;
struct buffer_head *bh;
+   struct exfat_entry_set_cache *es;
+   struct exfat_uni_name uni_name;
+   u16 name_hash;
+   bool found;
 
if (rewind && dentry == end_eidx)
goto not_found;
 
+   /* skip secondary dir-entries in previous dir-entry set 
*/
+   if (num_ext) {
+   num_ext--;
+   continue;
+   }
+
ep = exfat_get_dentry(sb, , i, , NULL);
if (!ep)
return -EIO;
 
entry_type = exfat_get_entry_type(ep);
+   brelse(bh);
 
if (entry_type == TYPE_UNUSED ||
entry_type == TYPE_DELETED) {
-   step = DIRENT_STEP_FILE;
-
num_empty++;
if (candi_empty.eidx == EXFAT_HINT_NONE &&
num_empty == 1) {
@@ -956,7 +937,6 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
}
}
 
-   brelse(bh);
if (entry_type == TYPE_UNUSED)
goto not_found;
continue;
@@ -965,80 +945,30 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
num_empty = 0;
candi_empty.eidx = EXFAT_HINT_NONE;
 
-   if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
-   step = DIRENT_STEP_FILE;
-

Re: [PATCH v3] exfat: remove EXFAT_SB_DIRTY flag

2020-08-17 Thread Tetsuhiro Kohada

Thank you for your reply.


Most of the NAND flash devices and HDDs have wear leveling and bad sector 
replacement algorithms

applied.

So I think that the life of the boot sector will not be exhausted first.


I'm not too worried about the life of the boot-sector.
I'm worried about write failures caused by external factors.
(power failure/system down/vibration/etc. during writing) They rarely occur on 
SD cards, but occur on
many HDDs, some SSDs and USB storages by 0.1% or more.

Hard disk and SSD do not guarantee atomic write of a sector unit?


In the case of SD, the sector-data will be either new or old when unexpected 
write interruption occurs.
Almost HDD, the sector-data will be either new, old, or unreadable.
And, some SSD products have similar problem.


Especially with AFT-HDD, not only boot-sector but also the following multiple 
sectors become
unreadable.

Other file systems will also be unstable on a such HW.


A well-designed FileSystems never rewrite critical regions.


It is not possible to completely solve this problem, as long as writing to the 
boot-sector.
(I think it's a exFAT's specification defect) The only effective way to reduce 
this problem is to
reduce writes to the boot-sector.

exFAT's specification defect... Well..
Even though the boot sector is corrupted, It can be recovered using the backup 
boot sector
through fsck.


Exactly.
However, in order to execute fsck, it is necessary to recognize the 
partition/volume with broken boot-sector as exfat.
Can linux(or fsck) correctly recognize the FileSystem even if the boot-sector 
cannot be read?
(I don't yet know how linux recognizes FileSystem)
In fact, a certain system recognize it as 'Unknown format'.
Nowadays, exfat is often used for removable storage.
This problem is not only for linux.

BR
---
etsuhiro Kohada 


Re: [PATCH 1/2] exfat: add dir-entry set checksum validation

2020-08-12 Thread Tetsuhiro Kohada

Thnak you for your reply.


-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+static int exfat_calc_dir_chksum_with_entry_set(struct
+exfat_entry_set_cache *es, u16 *chksum)
  {
-   int chksum_type = CS_DIR_ENTRY, i;
-   unsigned short chksum = 0;
struct exfat_dentry *ep;
+   int i;

-   for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
-   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
-chksum_type);
-   chksum_type = CS_DEFAULT;
+   ep = container_of(es->de_file, struct exfat_dentry, dentry.file);
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+   for (i = 0; i < es->de_file->num_ext; i++) {
+   ep = exfat_get_validated_dentry(es, 1 + i, TYPE_SECONDARY);
+   if (!ep)
+   return -EIO;
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, *chksum, 
CS_DEFAULT);
}
+   return 0;

We can return checksum after removing u16 *chksum argument.


I want to do that too!
How should I return the error?

Right now, I found that the type of chksum is not 'u16'.
I'll fix in v2.



+}
+
+void exfat_update_dir_chksum_with_entry_set(struct
+exfat_entry_set_cache *es) {
+   u16 chksum;
+
+   exfat_calc_dir_chksum_with_entry_set(es, );
es->de_file->checksum = cpu_to_le16(chksum);
es->modified = true;
  }
@@ -775,6 +784,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
struct exfat_entry_set_cache *es;
struct exfat_dentry *ep;
struct buffer_head *bh;
+   u16 chksum;

if (p_dir->dir == DIR_DELETED) {
exfat_err(sb, "access to deleted dentry"); @@ -839,10 +849,10 
@@ struct
exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
goto free_es;
es->de_stream = >dentry.stream;

-   for (i = 2; i < es->num_entries; i++) {
-   if (!exfat_get_validated_dentry(es, i, TYPE_SECONDARY))
-   goto free_es;
-   }
+   if (max_entries == ES_ALL_ENTRIES &&
+   ((exfat_calc_dir_chksum_with_entry_set(es, ) ||
+ chksum != le16_to_cpu(es->de_file->checksum

Please add error print log if checksum mismatch error happen.


OK.
I'll add in v2.


BR
---
Tetsuhiro Kohada 


Re: [PATCH 1/2] exfat: add NameLength check when extracting name

2020-08-12 Thread Tetsuhiro Kohada

Thank you for your reply.


-static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
-   struct exfat_chain *p_dir, int entry, unsigned short *uniname)
+static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
+   struct exfat_uni_name *uniname)
  {
-   int i;
-   struct exfat_entry_set_cache *es;
+   int n, l, i;
struct exfat_dentry *ep;

-   es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
-   if (!es)
-   return;
+   uniname->name_len = es->de_stream->name_len;
+   if (uniname->name_len == 0)
+   return -EIO;

Can we validate ->name_len and name entry ->type in exfat_get_dentry_set() ?


Yes.
As I wrote in a previous email, entry type validation, name-length validation, 
and name
extraction should not be separated, so implement all of these in 
exfat_get_dentry_set().
It can be easily implemented by adding uniname to exfat_entry_set_cache and 
calling
exfat_get_uniname_from_name_entries() from exfat_get_dentry_set().

However, that would be over-implementation.
Not all callers of exfat_get_dentry_set() need a name.
It is enough to validate the name when it is needed.
This is a file-system driver, not fsck.
Validation is possible in exfat_get_dentry_set(), but unnecessary.

Why do you want to validate the name in exfat_get_dentry_set()?


BR
---
Tetsuhiro Kohada 


Re: [PATCH v3] exfat: integrates dir-entry getting and validation

2020-08-12 Thread Tetsuhiro Kohada

Thank you for your reply.


@@ -171,7 +174,9 @@ struct exfat_entry_set_cache {
unsigned int start_off;
int num_bh;
struct buffer_head *bh[DIR_CACHE_SIZE];
-   unsigned int num_entries;
+   int num_entries;
+   struct exfat_de_file *de_file;
+   struct exfat_de_stream *de_stream;

I prefer to assign validated entries to **de and use it using enum value.
struct exfat_dentry **de;


I've tried several implementations that add a struct exfat_dentry type.(*de0 & 
*de1;  *de[2]; etc...)
The problem with the struct exfat_dentry type is that it is too flexible for 
type.
This means weak typing.
Therefore, when using them,
de[XXX_FILE]->dentry.file.zzz ...
It is necessary to re-specify the type. (against the DRY principle)
Strong typing prevents use with wrong type, at compiling.

I think the approach of using de_file/de_stream could be strongly typed.
I don't think we need excessive flexibility.


BR
---
Tetsuhiro Kohada 


Re: [PATCH v3] exfat: remove EXFAT_SB_DIRTY flag

2020-08-12 Thread Tetsuhiro Kohada


When should VOL_DIRTY be cleared?

The current behavior is ...

Case of  mkdir, rmdir, rename:
- set VOL_DIRTY before operation
- set VOL_CLEAN after operating.
In async mode, it is actually written to the media after 30 seconds.

Case of  cp, touch:
- set VOL_DIRTY before operation
- however, VOL_CLEAN is not called in this context.
VOL_CLEAN will call by sync_fs or unmount.

I added VOL_CLEAN in last of __exfat_write_inode() and exfat_map_cluster().
As a result, VOL_DIRTY is cleared with cp and touch.
However, when copying a many files ...
   - Async mode: VOL_DIRTY is written to the media twice every 30 seconds.
   - Sync mode: Of course,  VOL_DIRTY and VOL_CLEAN to the media for each
file.

Frequent writing VOL_DIRTY and VOL_CLEAN  increases the risk of boot-
sector curruption.
If the boot-sector corrupted, it causes the following serious problems  on
some OSs.
   - misjudge as unformatted
   - can't judge as exfat
   - can't repair

I want to minimize boot sector writes, to reduce these risk.

I looked vfat/udf implementation, which manages similar dirty information
on linux, and found that they ware mark-dirty at mount and cleared at
unmount.

Here are some ways to clear VOL_DIRTY.

(A) VOL_CLEAN after every write operation.
:-) Ejectable at any time after a write operation.
:-( Many times write to Boot-sector.

(B) dirty at mount, clear at unmount (same as vfat/udf)
:-) Write to boot-sector twice.
:-( It remains dirty unless unmounted.
:-( Write to boot-sector even if there is no write operation.

(C) dirty on first write operation, clear on unmount
:-) Writing to boot-sector is minimal.
:-) Will not write to the boot-sector if there is no write operation.
:-( It remains dirty unless unmounted.

(D) dirty on first write operation,  clear on sync-fs/unmount
   :-) Writing to boot-sector can be reduced.
   :-) Will not write to the boot-sector if there is no write operation.
   :-) sync-fs makes it clean and ejectable immidiately.
   :-( It remains dirty unless sync-fs or unmount.
   :-( Frequent sync-fs will  increases writes to boot-sector.

I think it should be (C) or(D).
What do you think?



First of all, I'm sorry for the late reply.
And thank you for the suggestion.


Thanks for thinking on this complicated issue.



Most of the NAND flash devices and HDDs have wear leveling and bad sector 
replacement algorithms applied.
So I think that the life of the boot sector will not be exhausted first.


I'm not too worried about the life of the boot-sector.
I'm worried about write failures caused by external factors.
(power failure/system down/vibration/etc. during writing)
They rarely occur on SD cards, but occur on many HDDs, some SSDs and USB 
storages by 0.1% or more.
Especially with AFT-HDD, not only boot-sector but also the following multiple 
sectors become unreadable.
It is not possible to completely solve this problem, as long as writing to the 
boot-sector.
(I think it's a exFAT's specification defect)
The only effective way to reduce this problem is to reduce writes to the 
boot-sector.



Currently the volume dirty/clean policy of exfat-fs is not perfect,


Thank you for sharing the problem with you.



but I think it behaves similarly to the policy of MS Windows.


On Windows10, the dirty flag is cleared after more than 15 seconds after all 
write operations are completed.
(dirty-flag is never updated during the write operation continues)



Therefore,
I think code improvements should be made to reduce volume flag records while 
maintaining the current policy.


Current policy is inconsistent.
As I wrote last mail, the problem with the current implementation is that the 
dirty-flag may not be cleared
after the write operation.(even if sync is enabled or disabled)
Because, some write operations clear the dirty-flag but some don't clear.
Unmount or sync command is the only way to ensure that the dirty-flag is 
cleared.
This has no effect on clearing the dirty-flag after a write operations, it only 
increases risk of destroying
the boot-sector.
 - Clear the dirty-flag after every write operation.
 - Never clear the dirty-flag after every write operation.
Unless unified to either one,  I think that sync policy cannot be consistent.

How do you think?


BR
---
etsuhiro Kohada 



Re: [PATCH 2/2] exfat: unify name extraction

2020-08-12 Thread Tetsuhiro Kohada

Thanks for your reply.

On 2020/08/09 2:19, Sungjong Seo wrote:

[snip]

@@ -963,80 +942,38 @@ int exfat_find_dir_entry(struct super_block *sb,
struct exfat_inode_info *ei,
num_empty = 0;
candi_empty.eidx = EXFAT_HINT_NONE;


[snip]


-   if (entry_type &
-   (TYPE_CRITICAL_SEC |

TYPE_BENIGN_SEC)) {

-   if (step == DIRENT_STEP_SECD) {
-   if (++order == num_ext)
-   goto found;
-   continue;
-   }
+   exfat_get_uniname_from_name_entries(es, _name);


It is needed to check a return value.


I'll fix it in v2.



+   exfat_free_dentry_set(es, false);
+
+   if (!exfat_uniname_ncmp(sb,
+   p_uniname->name,
+   uni_name.name,
+   name_len)) {
+   /* set the last used position as hint */
+   hint_stat->clu = clu.dir;
+   hint_stat->eidx = dentry;


eidx and clu of hint_stat should have one for the next entry we'll start
looking for.
Did you intentionally change the concept?


Yes, this is intentional.
Essentially, the "Hint" concept is to reduce the next seek cost with minimal 
cost.
There is a difference in the position of the hint, but the concept is the same.
As you can see, the patched code strategy doesn't move from current position.
Basically, the original code strategy is advancing only one dentry.(It's the 
"minimum cost")
However, when it reaches the cluster boundary, it gets the next cluster and 
error handling.
Getting the next cluster The error handling already exists at the end of the 
while loop,
so the code is duplicated.
These costs should be paid next time and are no longer the "minimum cost".

Should I add this to the commit-message?


BR
---
Tetsuhiro Kohada 


Re: [PATCH 1/2] exfat: add NameLength check when extracting name

2020-08-11 Thread Tetsuhiro Kohada

Thanks for your reply.

On 2020/08/09 1:54, Sungjong Seo wrote:

The current implementation doesn't care NameLength when extracting the
name from Name dir-entries, so the name may be incorrect.
(Without null-termination, Insufficient Name dir-entry, etc) Add a
NameLength check when extracting the name from Name dir-entries to extract
correct name.
And, change to get the information of file/stream-ext dir-entries via the
member variable of exfat_entry_set_cache.

** This patch depends on:
   '[PATCH v3] exfat: integrates dir-entry getting and validation'.

Signed-off-by: Tetsuhiro Kohada 
---
  fs/exfat/dir.c | 81 --
  1 file changed, 39 insertions(+), 42 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index
91cdbede0fd1..545bb73b95e9 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -28,16 +28,15 @@ static int exfat_extract_uni_name(struct exfat_dentry
*ep,

  }

-static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
-   struct exfat_chain *p_dir, int entry, unsigned short
*uniname)
+static int exfat_get_uniname_from_name_entries(struct
exfat_entry_set_cache *es,
+   struct exfat_uni_name *uniname)
  {
-   int i;
-   struct exfat_entry_set_cache *es;
+   int n, l, i;
struct exfat_dentry *ep;

-   es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
-   if (!es)
-   return;
+   uniname->name_len = es->de_stream->name_len;
+   if (uniname->name_len == 0)
+   return -EIO;


-EINVAL looks better.


OK.
I'll change in v2.


/*
 * First entry  : file entry
@@ -45,14 +44,15 @@ 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.
 */
-
-   i = 2;
-   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
-   exfat_extract_uni_name(ep, uniname);
-   uniname += EXFAT_FILE_NAME_LEN;
+   for (l = 0, n = 2; l < uniname->name_len; n++) {
+   ep = exfat_get_validated_dentry(es, n, TYPE_NAME);
+   if (!ep)
+   return -EIO;
+   for (i = 0; l < uniname->name_len && i <

EXFAT_FILE_NAME_LEN;

i++, l++)
+   uniname->name[l] = le16_to_cpu(ep-

dentry.name.unicode_0_14[i]);


Looks good.


}
-
-   exfat_free_dentry_set(es, false);
+   uniname->name[l] = 0;
+   return 0;
  }

  /* read a directory entry from the opened directory */ @@ -63,6 +63,7 @@
static int exfat_readdir(struct inode *inode, struct exfat_dir_entry
*dir_entry)

[snip]

-   *uni_name.name = 0x0;
-   exfat_get_uniname_from_ext_entry(sb, , dentry,
-   uni_name.name);
+   dir_entry->size = le64_to_cpu(es->de_stream-

valid_size);

+
+   exfat_get_uniname_from_name_entries(es, _name);


Modified function has a return value.
It would be better to check the return value.


Oops!
I'll fix it in v2.



exfat_utf16_to_nls(sb, _name,
dir_entry->namebuf.lfn,
dir_entry->namebuf.lfnbuf_len);
-   brelse(bh);

-   ep = exfat_get_dentry(sb, , i + 1, , NULL);
-   if (!ep)
-   return -EIO;
-   dir_entry->size =
-   le64_to_cpu(ep->dentry.stream.valid_size);
-   brelse(bh);
+   exfat_free_dentry_set(es, false);

ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
ei->hint_bmap.clu = clu.dir;
--
2.25.1





[PATCH 2/2] exfat: write only modified part of dir-entry set

2020-08-07 Thread Tetsuhiro Kohada
Currently exfat_free_dentry_set() writes all of dir-entry set.
Change it to write only the modified part of dir-entry set.
And, Integrate exfat_free_dentry_set() and
exfat_update_dir_chksum_with_entry_set() as exfat_put_dentry_set().

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'
  '[PATCH] exfat: add NameLength check when extracting name'
  '[PATCH] exfat: unify name extraction'

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 33 -
 fs/exfat/exfat_fs.h |  4 +---
 fs/exfat/file.c |  3 +--
 fs/exfat/inode.c|  6 ++
 fs/exfat/namei.c|  4 ++--
 5 files changed, 22 insertions(+), 28 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 2e79ac464f5f..5071a05f1150 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -133,7 +133,7 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
dir_entry->namebuf.lfn,
dir_entry->namebuf.lfnbuf_len);
 
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
 
ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
ei->hint_bmap.clu = clu.dir;
@@ -579,21 +579,21 @@ static int exfat_calc_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es
return 0;
 }
 
-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
-{
-   u16 chksum;
-
-   exfat_calc_dir_chksum_with_entry_set(es, );
-   es->de_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 modified, int 
sync)
 {
int i, err = 0;
 
-   if (es->modified)
-   err = exfat_update_bhs(es->bh, es->num_bh, sync);
+   if (modified) {
+   int off = es->start_off + (modified - 1) * DENTRY_SIZE;
+   int modified_bh = min(EXFAT_B_TO_BLK(off, es->sb) + 1, 
es->num_bh);
+   u16 chksum;
+
+   err = exfat_calc_dir_chksum_with_entry_set(es, );
+   if (!err) {
+   es->de_file->checksum = cpu_to_le16(chksum);
+   err = exfat_update_bhs(es->bh, modified_bh, sync);
+   }
+   }
 
for (i = 0; i < es->num_bh; i++)
if (err)
@@ -800,7 +800,6 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
if (!es)
return NULL;
es->sb = sb;
-   es->modified = false;
es->num_entries = 1;
 
/* byte offset in cluster */
@@ -857,7 +856,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
return es;
 
 free_es:
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
return NULL;
 }
 
@@ -962,7 +961,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
num_ext = es->de_file->num_ext;
name_hash = le16_to_cpu(es->de_stream->name_hash);
name_len = es->de_stream->name_len;
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
 
if (p_uniname->name_hash != name_hash ||
p_uniname->name_len != name_len)
@@ -973,7 +972,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
continue;
 
exfat_get_uniname_from_name_entries(es, _name);
-   exfat_free_dentry_set(es, false);
+   exfat_put_dentry_set(es, 0, false);
 
if (!exfat_uniname_ncmp(sb,
p_uniname->name,
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 62a4768a4f6e..6fac1cec3e7f 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -170,7 +170,6 @@ 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];
@@ -452,7 +451,6 @@ 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);
-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

[PATCH 1/2] exfat: add dir-entry set checksum validation

2020-08-07 Thread Tetsuhiro Kohada
Add checksum validation for dir-entry set when getting it.
exfat_calc_dir_chksum_with_entry_set() also validates entry-type.

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c | 34 ++
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index c9715c7a55a1..2e79ac464f5f 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -563,18 +563,27 @@ int exfat_remove_entries(struct inode *inode, struct 
exfat_chain *p_dir,
return 0;
 }
 
-void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+static int exfat_calc_dir_chksum_with_entry_set(struct exfat_entry_set_cache 
*es, u16 *chksum)
 {
-   int chksum_type = CS_DIR_ENTRY, i;
-   unsigned short chksum = 0;
struct exfat_dentry *ep;
+   int i;
 
-   for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
-   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
-chksum_type);
-   chksum_type = CS_DEFAULT;
+   ep = container_of(es->de_file, struct exfat_dentry, dentry.file);
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+   for (i = 0; i < es->de_file->num_ext; i++) {
+   ep = exfat_get_validated_dentry(es, 1 + i, TYPE_SECONDARY);
+   if (!ep)
+   return -EIO;
+   *chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, *chksum, 
CS_DEFAULT);
}
+   return 0;
+}
+
+void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
+{
+   u16 chksum;
+
+   exfat_calc_dir_chksum_with_entry_set(es, );
es->de_file->checksum = cpu_to_le16(chksum);
es->modified = true;
 }
@@ -775,6 +784,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
struct exfat_entry_set_cache *es;
struct exfat_dentry *ep;
struct buffer_head *bh;
+   u16 chksum;
 
if (p_dir->dir == DIR_DELETED) {
exfat_err(sb, "access to deleted dentry");
@@ -839,10 +849,10 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
goto free_es;
es->de_stream = >dentry.stream;
 
-   for (i = 2; i < es->num_entries; i++) {
-   if (!exfat_get_validated_dentry(es, i, TYPE_SECONDARY))
-   goto free_es;
-   }
+   if (max_entries == ES_ALL_ENTRIES &&
+   ((exfat_calc_dir_chksum_with_entry_set(es, ) ||
+ chksum != le16_to_cpu(es->de_file->checksum
+   goto free_es;
 
return es;
 
-- 
2.25.1



[PATCH 1/2] exfat: add NameLength check when extracting name

2020-08-05 Thread Tetsuhiro Kohada
The current implementation doesn't care NameLength when extracting
the name from Name dir-entries, so the name may be incorrect.
(Without null-termination, Insufficient Name dir-entry, etc)
Add a NameLength check when extracting the name from Name dir-entries
to extract correct name.
And, change to get the information of file/stream-ext dir-entries
via the member variable of exfat_entry_set_cache.

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c | 81 --
 1 file changed, 39 insertions(+), 42 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 91cdbede0fd1..545bb73b95e9 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -28,16 +28,15 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep,
 
 }
 
-static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
-   struct exfat_chain *p_dir, int entry, unsigned short *uniname)
+static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
+   struct exfat_uni_name *uniname)
 {
-   int i;
-   struct exfat_entry_set_cache *es;
+   int n, l, i;
struct exfat_dentry *ep;
 
-   es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
-   if (!es)
-   return;
+   uniname->name_len = es->de_stream->name_len;
+   if (uniname->name_len == 0)
+   return -EIO;
 
/*
 * First entry  : file entry
@@ -45,14 +44,15 @@ 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.
 */
-
-   i = 2;
-   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
-   exfat_extract_uni_name(ep, uniname);
-   uniname += EXFAT_FILE_NAME_LEN;
+   for (l = 0, n = 2; l < uniname->name_len; n++) {
+   ep = exfat_get_validated_dentry(es, n, TYPE_NAME);
+   if (!ep)
+   return -EIO;
+   for (i = 0; l < uniname->name_len && i < EXFAT_FILE_NAME_LEN; 
i++, l++)
+   uniname->name[l] = 
le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
}
-
-   exfat_free_dentry_set(es, false);
+   uniname->name[l] = 0;
+   return 0;
 }
 
 /* read a directory entry from the opened directory */
@@ -63,6 +63,7 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
sector_t sector;
struct exfat_chain dir, clu;
struct exfat_uni_name uni_name;
+   struct exfat_entry_set_cache *es;
struct exfat_dentry *ep;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
@@ -114,47 +115,43 @@ static int exfat_readdir(struct inode *inode, struct 
exfat_dir_entry *dir_entry)
return -EIO;
 
type = exfat_get_entry_type(ep);
-   if (type == TYPE_UNUSED) {
-   brelse(bh);
+   brelse(bh);
+
+   if (type == TYPE_UNUSED)
break;
-   }
 
-   if (type != TYPE_FILE && type != TYPE_DIR) {
-   brelse(bh);
+   if (type != TYPE_FILE && type != TYPE_DIR)
continue;
-   }
 
-   dir_entry->attr = le16_to_cpu(ep->dentry.file.attr);
+   es = exfat_get_dentry_set(sb, , dentry, 
ES_ALL_ENTRIES);
+   if (!es)
+   return -EIO;
+
+   dir_entry->attr = le16_to_cpu(es->de_file->attr);
exfat_get_entry_time(sbi, _entry->crtime,
-   ep->dentry.file.create_tz,
-   ep->dentry.file.create_time,
-   ep->dentry.file.create_date,
-   ep->dentry.file.create_time_cs);
+   es->de_file->create_tz,
+   es->de_file->create_time,
+   es->de_file->create_date,
+   es->de_file->create_time_cs);
exfat_get_entry_time(sbi, _entry->mtime,
-   ep->dentry.file.modify_tz,
-   ep->dentry.file.modify_time,
-   ep->dentry.file.modify_date,
-   ep->dentry.file.modify_time_c

[PATCH 2/2] exfat: unify name extraction

2020-08-05 Thread Tetsuhiro Kohada
Name extraction in exfat_find_dir_entry() also doesn't care NameLength,
so the name may be incorrect.
Replace the name extraction in exfat_find_dir_entry() with using
exfat_entry_set_cache and exfat_get_uniname_from_name_entries(),
like exfat_readdir().
Replace the name extraction with using exfat_entry_set_cache and
exfat_get_uniname_from_name_entries(), like exfat_readdir().
And, remove unused functions/parameters.

** This patch depends on:
  '[PATCH v3] exfat: integrates dir-entry getting and validation'.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 161 ++--
 fs/exfat/exfat_fs.h |   2 +-
 fs/exfat/namei.c|   4 +-
 3 files changed, 38 insertions(+), 129 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 545bb73b95e9..c9715c7a55a1 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -10,24 +10,6 @@
 #include "exfat_raw.h"
 #include "exfat_fs.h"
 
-static int exfat_extract_uni_name(struct exfat_dentry *ep,
-   unsigned short *uniname)
-{
-   int i, len = 0;
-
-   for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
-   *uniname = le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
-   if (*uniname == 0x0)
-   return len;
-   uniname++;
-   len++;
-   }
-
-   *uniname = 0x0;
-   return len;
-
-}
-
 static int exfat_get_uniname_from_name_entries(struct exfat_entry_set_cache 
*es,
struct exfat_uni_name *uniname)
 {
@@ -869,13 +851,6 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct 
super_block *sb,
return NULL;
 }
 
-enum {
-   DIRENT_STEP_FILE,
-   DIRENT_STEP_STRM,
-   DIRENT_STEP_NAME,
-   DIRENT_STEP_SECD,
-};
-
 /*
  * return values:
  *   >= 0  : return dir entiry position with the name in dir
@@ -885,13 +860,12 @@ 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)
+   int num_entries)
 {
-   int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
-   int order, step, name_len = 0;
+   int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0;
+   int name_len = 0;
int dentries_per_clu, num_empty = 0;
unsigned int entry_type;
-   unsigned short *uniname = NULL;
struct exfat_chain clu;
struct exfat_hint *hint_stat = >hint_stat;
struct exfat_hint_femp candi_empty;
@@ -909,27 +883,33 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
 
candi_empty.eidx = EXFAT_HINT_NONE;
 rewind:
-   order = 0;
-   step = DIRENT_STEP_FILE;
while (clu.dir != EXFAT_EOF_CLUSTER) {
i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) {
struct exfat_dentry *ep;
struct buffer_head *bh;
+   struct exfat_entry_set_cache *es;
+   struct exfat_uni_name uni_name;
+   u16 name_hash;
 
if (rewind && dentry == end_eidx)
goto not_found;
 
+   /* skip secondary dir-entries in previous dir-entry set 
*/
+   if (num_ext) {
+   num_ext--;
+   continue;
+   }
+
ep = exfat_get_dentry(sb, , i, , NULL);
if (!ep)
return -EIO;
 
entry_type = exfat_get_entry_type(ep);
+   brelse(bh);
 
if (entry_type == TYPE_UNUSED ||
entry_type == TYPE_DELETED) {
-   step = DIRENT_STEP_FILE;
-
num_empty++;
if (candi_empty.eidx == EXFAT_HINT_NONE &&
num_empty == 1) {
@@ -954,7 +934,6 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
}
}
 
-   brelse(bh);
if (entry_type == TYPE_UNUSED)
goto not_found;
continue;
@@ -963,80 +942,38 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
num_empty = 0;
candi_empty.eidx = EXFAT_HINT_NONE;
 
-   if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
-   step = DIRENT_STEP_FILE;
-   if (type == TYPE_ALL || type == entry_type)

[PATCH v3] exfat: integrates dir-entry getting and validation

2020-08-05 Thread Tetsuhiro Kohada
Add validation for num, bh and type on getting dir-entry.
Renamed exfat_get_dentry_cached() to exfat_get_validated_dentry() due to
a change in functionality.

Integrate type-validation with simplified.
This will also recognize a dir-entry set that contains 'benign secondary'
dir-entries.

Pre-Validated 'file' and 'stream-ext' dir-entries are provided as member
variables of exfat_entry_set_cache.

And, rename TYPE_EXTEND to TYPE_NAME.

Suggested-by: Sungjong Seo 
Suggested-by: Namjae Jeon 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Change verification order
 - Verification loop start with index 2
Changes in v3
 - Fix indent 
 - Fix comment of exfat_get_dentry_set()
 - Add de_file/de_stream in exfat_entry_set_cache
 - Add srtuct tag name for each dir-entry type in exfat_dentry
 - Add description about de_file/de_stream to commit-log

 fs/exfat/dir.c   | 147 +--
 fs/exfat/exfat_fs.h  |  17 +++--
 fs/exfat/exfat_raw.h |  10 +--
 fs/exfat/file.c  |  25 
 fs/exfat/inode.c |  49 ++-
 fs/exfat/namei.c |  36 +--
 6 files changed, 122 insertions(+), 162 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 573659bfbc55..91cdbede0fd1 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -33,6 +33,7 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
 {
int i;
struct exfat_entry_set_cache *es;
+   struct exfat_dentry *ep;
 
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
if (!es)
@@ -44,13 +45,9 @@ 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);
-
-   /* end of name entry */
-   if (exfat_get_entry_type(ep) != TYPE_EXTEND)
-   break;
 
+   i = 2;
+   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
exfat_extract_uni_name(ep, uniname);
uniname += EXFAT_FILE_NAME_LEN;
}
@@ -372,7 +369,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
if (ep->type == EXFAT_STREAM)
return TYPE_STREAM;
if (ep->type == EXFAT_NAME)
-   return TYPE_EXTEND;
+   return TYPE_NAME;
if (ep->type == EXFAT_ACL)
return TYPE_ACL;
return TYPE_CRITICAL_SEC;
@@ -388,7 +385,7 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, 
unsigned int type)
ep->type &= EXFAT_DELETE;
} else if (type == TYPE_STREAM) {
ep->type = EXFAT_STREAM;
-   } else if (type == TYPE_EXTEND) {
+   } else if (type == TYPE_NAME) {
ep->type = EXFAT_NAME;
} else if (type == TYPE_BITMAP) {
ep->type = EXFAT_BITMAP;
@@ -421,7 +418,7 @@ static void exfat_init_name_entry(struct exfat_dentry *ep,
 {
int i;
 
-   exfat_set_entry_type(ep, TYPE_EXTEND);
+   exfat_set_entry_type(ep, TYPE_NAME);
ep->dentry.name.flags = 0x0;
 
for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
@@ -594,13 +591,12 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
struct exfat_dentry *ep;
 
for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_dentry_cached(es, i);
+   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
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->de_file->checksum = cpu_to_le16(chksum);
es->modified = true;
 }
 
@@ -741,92 +737,64 @@ struct exfat_dentry *exfat_get_dentry(struct super_block 
*sb,
return (struct exfat_dentry *)((*bh)->b_data + off);
 }
 
-enum exfat_validate_dentry_mode {
-   ES_MODE_STARTED,
-   ES_MODE_GET_FILE_ENTRY,
-   ES_MODE_GET_STRM_ENTRY,
-   ES_MODE_GET_NAME_ENTRY,
-   ES_MODE_GET_CRITICAL_SEC_ENTRY,
-};
-
-static bool exfat_validate_entry(unsigned int type,
-   enum exfat_validate_dentry_mode *mode)
-{
-   if (type == TYPE_UNUSED || type == TYPE_DELETED)
-   return false;
-
-   switch (*mode) {
-   case ES_MODE_STARTED:
-   if  (type != TYPE_FILE && type != TYPE_DIR)
-   return false;
-   *mode = ES_MODE_GET_FILE_ENTRY;
-   return true;
-   case ES_MODE_GET_FILE_ENTRY:
-   if (type != TYPE_STREAM)
- 

Re: [PATCH v2] exfat: integrates dir-entry getting and validation

2020-08-04 Thread Tetsuhiro Kohada




+   i = 2;
+   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {

As Sungjong said, I think that TYPE_NAME seems right to be validated in 
exfat_get_dentry_set().


First, it is possible to correctly determine that "Immediately follow the 
Stream Extension directory
entry as a consecutive series"
whether the TYPE_NAME check is implemented here or exfat_get_dentry_set().
It's functionally same, so it is also right to validate in either.

Second, the current implementation does not care for NameLength field, as I 
replied to Sungjong.
If name is not terminated with zero, the name will be incorrect.(With or 
without my patch) I think
TYPE_NAME and NameLength validation should not be separated from the name 
extraction.
If validate TYPE_NAME in exfat_get_dentry_set(), NameLength validation and name 
extraction should also
be implemented there.
(Otherwise, a duplication check with exfat_get_dentry_set() and here.) I will 
add NameLength
validation here.

Okay.


Thank you for your understanding.


Therefore, TYPE_NAME validation here should not be omitted.

Third, getting dentry and entry-type validation should be integrated.
These no longer have to be primitive.
The integration simplifies caller error checking.





diff --git a/fs/exfat/file.c b/fs/exfat/file.c index
6707f3eb09b5..b6b458e6f5e3 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -160,8 +160,8 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ES_ALL_ENTRIES);
if (!es)
return -EIO;
-   ep = exfat_get_dentry_cached(es, 0);
-   ep2 = exfat_get_dentry_cached(es, 1);
+   ep = exfat_get_validated_dentry(es, 0, TYPE_FILE);
+   ep2 = exfat_get_validated_dentry(es, 1, TYPE_STREAM);

TYPE_FILE and TYPE_STREAM was already validated in exfat_get_dentry_set().
Isn't it unnecessary duplication check ?


No, as you say.
Although TYPE is specified, it is not good not to check the null of ep/ep2.
However, with TYPE_ALL, it becomes difficult to understand what purpose ep/ep2 
is used for.
Therefore, I proposed adding ep_file/ep_stream to es, and here
ep = es->ep_file;
ep2 = es->ep_stream;

How about this?

You can factor out exfat_get_dentry_cached() from exfat_get_validated_dentry() 
and use it here.


I actually implemented and use it, but I feel it is not so good.
- Since there are two functions to get from es, so it's a bit confusing which 
one is better for use.
- There was the same anxiety as using exfat_get_validated_dentry() in that 
there is no NULL check of
  ep got with exfat_get_dentry_cached().

Whichever function I use, there are places where I check the return value and 
where I don't.
This will cause  missing entry-type validation or missing return value check,in 
the future.
I think it's easier to use by including it as a validated object in the member 
of exfat_entry_set_cache.


And then, You can rename ep and ep2 to ep_file and ep_stream.


I propose a slightly different approach than last.
Add members to exfat_entry_set_cache as below.
struct exfat_de_file *de_file;
struct exfat_de_stream *de_stream;
And, use these as below.
es->de_file->attr = cpu_to_le16(exfat_make_attr(inode));
es->de_stream->valid_size = cpu_to_le64(on_disk_size);

exfat_de_file/exfat_de_stream corresponds to the file dir-entry/stream dir-enty 
structure in the exfat_dentry union.
We can use the validated valid values ​​directly.
Furthermore, these are strongly typed.



Or is it better to specify TYPE_ALL?


BTW
It's been about a month since I posted this patch.
In the meantime, I created a NameLength check and a checksum validation based 
on this patch.
Can you review those as well?

Let me see the patches.


Thanks a lot.
For now, I will create and post a V3 patch with this proposal.
After that, I will recreate the NameLength check and a checksum validation 
patches based on the V3 patch and post them.
(Should I post these as an RFC?)

BR
---
Kohada Tetsuhiro 


[PATCH v2] exfat: retain 'VolumeFlags' properly

2020-07-30 Thread Tetsuhiro Kohada
Retain ActiveFat, MediaFailure and ClearToZero fields.
And, never clear VolumeDirty, if it is dirty at mount.

In '3.1.13.3 Media Failure Field' of exfat specification says ...
 If, upon mounting a volume, the value of this field is 1, implementations
 which scan the entire volume for media failures and record all failures as
 "bad" clusters in the FAT (or otherwise resolve media failures) may clear
 the value of this field to 0.
Therefore, should not clear MediaFailure without scanning volume.

In '8.1 Recommended Write Ordering' of exfat specification says ...
 Clear the value of the VolumeDirty field to 0, if its value prior to the
 first step was 0
Therefore, should not clear VolumeDirty when mounted.

Also, rename ERR_MEDIUM to MEDIA_FAILURE.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Add exfat_set_volume_dirty() & exfat_clear_volume_dirty()
 - Rename vol_flags_noclear to vol_flags_persistent
 - Rename MED_FAILURE to MEDIA_FAILURE
 - Rename VOL_DIRTY to VOLUME_DIRTY
 - Remove VOL_CLEAN

 fs/exfat/exfat_fs.h  |  6 --
 fs/exfat/exfat_raw.h |  5 ++---
 fs/exfat/file.c  |  4 ++--
 fs/exfat/inode.c |  4 ++--
 fs/exfat/namei.c | 20 ++--
 fs/exfat/super.c | 36 +++-
 6 files changed, 47 insertions(+), 28 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index cb51d6e83199..95d717f8620c 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -224,7 +224,8 @@ 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 */
+   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 */
@@ -380,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)
diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index 350ce59cc324..6aec6288e1f2 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -14,9 +14,8 @@
 
 #define EXFAT_MAX_FILE_LEN 255
 
-#define VOL_CLEAN  0x
-#define VOL_DIRTY  0x0002
-#define ERR_MEDIUM 0x0004
+#define VOLUME_DIRTY   0x0002
+#define MEDIA_FAILURE  0x0004
 
 #define EXFAT_EOF_CLUSTER  0xu
 #define EXFAT_BAD_CLUSTER  0xFFF7u
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 6bdabfd4b134..f41f523a58ad 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -106,7 +106,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
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 =
@@ -220,7 +220,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
if (exfat_free_cluster(inode, ))
return -EIO;
 
-   exfat_set_vol_flags(sb, VOL_CLEAN);
+   exfat_clear_volume_dirty(sb);
 
return 0;
 }
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index f0160a7892a8..7f90204adef5 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -39,7 +39,7 @@ 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);
@@ -167,7 +167,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;
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 126ed3ba8f47..e73f20f66cb2 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -562,10 +562,10 @@ static int exfat_create(struct inode *dir, struct dentry 
*dentry, umode_t mode,
int err;
 
mutex_lock(_SB(sb)->s_lock);
-   exfat_set_vol_flags(sb, VOL_DIRTY);
+   exfat_set_volume_dirty(sb);
 

Re: [PATCH] exfat: retain 'VolumeFlags' properly

2020-07-30 Thread Tetsuhiro Kohada

On 2020/07/30 15:59, Namjae Jeon wrote:

Ping..

Hi Tetsuhiro,


Thank you for your reply.



On 2020/07/15 19:06, Tetsuhiro Kohada wrote:

It looks complicated. It would be better to simply set/clear VOLUME DIRTY bit.


I think exfat_set_vol_flags() gets a little complicated, because it
needs the followings (with bit operation)
   a) Set/Clear VOLUME_DIRTY.
   b) Set MEDIA_FAILUR.


How about splitting these into separate functions  as below?


exfat_set_volume_dirty()
exfat_set_vol_flags(sb, sbi->vol_flag | VOLUME_DIRTY);

exfat_clear_volume_dirty()
exfat_set_vol_flags(sb, sbi->vol_flag & ~VOLUME_DIRTY);

Looks good.



exfat_set_media_failure()
exfat_set_vol_flags(sb, sbi->vol_flag | MEDIA_FAILURE);

Where will this function be called? We don't need to create unused functions in 
advance...



Sorry. I ran ahead without explaining.
I would like to set MEDIA_FAILURE when a format error is detected so that fsck 
will be run on the next mount.
This patch is the basis for setting MEDIA_FAILURE.

But, I have no reason to implement this right now, as you say.
I'll add it in a patch that sets MEDIA_FAILURE.


BTW
I would like your opinion on the timing of clearing VOLUME_DIRTY.
https://lore.kernel.org/linux-fsdevel/c635e965-6b78-436a-3959-e4777e173...@gmail.com/#t

BR
---
Tetsuhiro Kohada 




Re: [PATCH] exfat: retain 'VolumeFlags' properly

2020-07-30 Thread Tetsuhiro Kohada

Ping..

On 2020/07/15 19:06, Tetsuhiro Kohada wrote:

It looks complicated. It would be better to simply set/clear VOLUME DIRTY bit.


I think exfat_set_vol_flags() gets a little complicated,
because it needs the followings (with bit operation)
  a) Set/Clear VOLUME_DIRTY.
  b) Set MEDIA_FAILUR.


How about splitting these into separate functions  as below?


exfat_set_volume_dirty()
exfat_set_vol_flags(sb, sbi->vol_flag | VOLUME_DIRTY);

exfat_clear_volume_dirty()
exfat_set_vol_flags(sb, sbi->vol_flag & ~VOLUME_DIRTY);

exfat_set_media_failure()
exfat_set_vol_flags(sb, sbi->vol_flag | MEDIA_FAILURE);


The implementation is essentially the same for exfat_set_vol_flags(),
but I think the intention of the operation will be easier to understand.


BR
---
Tetsuhiro Kohada 


Re: [PATCH v2] exfat: integrates dir-entry getting and validation

2020-07-29 Thread Tetsuhiro Kohada

On 2020/07/15 10:22, Tetsuhiro Kohada wrote:

Add validation for num, bh and type on getting dir-entry.
('file' and 'stream-ext' dir-entries are pre-validated to ensure success)
Renamed exfat_get_dentry_cached() to exfat_get_validated_dentry() due to
a change in functionality.

Integrate type-validation with simplified.
This will also recognize a dir-entry set that contains 'benign secondary'
dir-entries.

And, rename TYPE_EXTEND to TYPE_NAME.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
  - Change verification order
  - Verification loop start with index 2


Ping.
Is there anything I should do?


BR
---
Tetsuhiro Kohada 


Re: [PATCH] exfat: change exfat_set_vol_flags() return type void.

2020-07-17 Thread Tetsuhiro Kohada

On 2020/07/15 22:50, youngjun wrote:

exfat_set_vol_flags() always return 0.
So, change function return type as void.


On the contrary, I think it should be fixed to return an appropriate error.



@@ -114,7 +113,7 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned 
short new_flag)
 * if this volume has been mounted with read-only
 */
if (sb_rdonly(sb))
-   return 0;
+   return;


Some other FileSystems return -EROFS.
exfat-fs may also need to return it.
(If so, the caller will also need to be modified)



p_boot->vol_flags = cpu_to_le16(new_flag);
  
@@ -128,7 +127,7 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
  
  	if (sync)

sync_dirty_buffer(sbi->boot_bh);
-   return 0;
+   return;


Shouldn't the execution result be returned when sync_dirty_buffer() is executed?


BR
---
Tetsuhiro Kohada 


Re: [PATCH] exfat: retain 'VolumeFlags' properly

2020-07-15 Thread Tetsuhiro Kohada




Also, rename ERR_MEDIUM to MED_FAILURE.

I think that MEDIA_FAILURE looks better.


I think so too.
If so, should I change VOL_DIRTY to VOLUME_DIRTY?

Yes, maybe.


OK.
I'll rename both in v2.



-   p_boot->vol_flags = cpu_to_le16(new_flag);
+   p_boot->vol_flags = cpu_to_le16(new_flags);

How about set or clear only dirty bit to on-disk volume flags instead
of using
sbi->vol_flags_noclear ?
if (set)
p_boot->vol_flags |= cpu_to_le16(VOL_DIRTY);
else
p_boot->vol_flags &= cpu_to_le16(~VOL_DIRTY);


Please let me know about this code.
Does this code assume that 'sbi->vol_flags'(last vol_flag value) is not used?

If you use sbi->vol_flags, I think the original idea is fine.

sbi-> vol_flags = new_flag;
p_boot->vol_flags = cpu_to_le16(new_flag);



In this way, the initial  VOL_DIRTY cannot be retained.
The purpose of this patch is to avoid losing the initial VOL_DIRTY and 
MED_FAILURE.
Furthermore, I'm also thinking of setting MED_FAILURE.

I know what you do. I mean this function does not need to be called if volume 
dirty
Is already set on on-disk volume flags as I said earlier.


Hmm?
Does it mean the caller check that volume was dirty at mount, and caller 
determine
whether to call exfat_set_vol_flags() or not?
If so, MEDIA_FAILUR needs to be set independently of the volume-dirty state.



However, the formula for 'new_flags' may be a bit complicated.
Is it better to change to the following?

if (new_flags == VOL_CLEAN)
new_flags = sbi->vol_flags & ~VOL_DIRTY
else
new_flags |= sbi->vol_flags;

new_flags |= sbi->vol_flags_noclear;


one more point,
Is there a better name than 'vol_flags_noclear'?
(I can't come up with a good name anymore)

It looks complicated. It would be better to simply set/clear VOLUME DIRTY bit.


I think exfat_set_vol_flags() gets a little complicated,
because it needs the followings (with bit operation)
 a) Set/Clear VOLUME_DIRTY.
 b) Set MEDIA_FAILUR.
 c) Do not change other flags.
 d) Retain VOLUME_DIRTY/MEDIA_FAILUR as it is when mounted.

'vol_flags_noclear' is used for d).

Bit-by-bit operation makes the code redundant.
I think it's not a bad way to handle multiple bits.

What do you think?


BR
---
Tetsuhiro Kohada 


[PATCH v2] exfat: integrates dir-entry getting and validation

2020-07-14 Thread Tetsuhiro Kohada
Add validation for num, bh and type on getting dir-entry.
('file' and 'stream-ext' dir-entries are pre-validated to ensure success)
Renamed exfat_get_dentry_cached() to exfat_get_validated_dentry() due to
a change in functionality.

Integrate type-validation with simplified.
This will also recognize a dir-entry set that contains 'benign secondary'
dir-entries.

And, rename TYPE_EXTEND to TYPE_NAME.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Change verification order
 - Verification loop start with index 2

 fs/exfat/dir.c  | 144 ++--
 fs/exfat/exfat_fs.h |  15 +++--
 fs/exfat/file.c |   4 +-
 fs/exfat/inode.c|   6 +-
 fs/exfat/namei.c|   4 +-
 5 files changed, 73 insertions(+), 100 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 573659bfbc55..09b85746e760 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -33,6 +33,7 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
 {
int i;
struct exfat_entry_set_cache *es;
+   struct exfat_dentry *ep;
 
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
if (!es)
@@ -44,13 +45,9 @@ 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);
-
-   /* end of name entry */
-   if (exfat_get_entry_type(ep) != TYPE_EXTEND)
-   break;
 
+   i = 2;
+   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
exfat_extract_uni_name(ep, uniname);
uniname += EXFAT_FILE_NAME_LEN;
}
@@ -372,7 +369,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
if (ep->type == EXFAT_STREAM)
return TYPE_STREAM;
if (ep->type == EXFAT_NAME)
-   return TYPE_EXTEND;
+   return TYPE_NAME;
if (ep->type == EXFAT_ACL)
return TYPE_ACL;
return TYPE_CRITICAL_SEC;
@@ -388,7 +385,7 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, 
unsigned int type)
ep->type &= EXFAT_DELETE;
} else if (type == TYPE_STREAM) {
ep->type = EXFAT_STREAM;
-   } else if (type == TYPE_EXTEND) {
+   } else if (type == TYPE_NAME) {
ep->type = EXFAT_NAME;
} else if (type == TYPE_BITMAP) {
ep->type = EXFAT_BITMAP;
@@ -421,7 +418,7 @@ static void exfat_init_name_entry(struct exfat_dentry *ep,
 {
int i;
 
-   exfat_set_entry_type(ep, TYPE_EXTEND);
+   exfat_set_entry_type(ep, TYPE_NAME);
ep->dentry.name.flags = 0x0;
 
for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
@@ -594,12 +591,12 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
struct exfat_dentry *ep;
 
for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_dentry_cached(es, i);
+   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
 chksum_type);
chksum_type = CS_DEFAULT;
}
-   ep = exfat_get_dentry_cached(es, 0);
+   ep = exfat_get_validated_dentry(es, 0, TYPE_FILE);
ep->dentry.file.checksum = cpu_to_le16(chksum);
es->modified = true;
 }
@@ -741,92 +738,66 @@ struct exfat_dentry *exfat_get_dentry(struct super_block 
*sb,
return (struct exfat_dentry *)((*bh)->b_data + off);
 }
 
-enum exfat_validate_dentry_mode {
-   ES_MODE_STARTED,
-   ES_MODE_GET_FILE_ENTRY,
-   ES_MODE_GET_STRM_ENTRY,
-   ES_MODE_GET_NAME_ENTRY,
-   ES_MODE_GET_CRITICAL_SEC_ENTRY,
-};
-
-static bool exfat_validate_entry(unsigned int type,
-   enum exfat_validate_dentry_mode *mode)
-{
-   if (type == TYPE_UNUSED || type == TYPE_DELETED)
-   return false;
-
-   switch (*mode) {
-   case ES_MODE_STARTED:
-   if  (type != TYPE_FILE && type != TYPE_DIR)
-   return false;
-   *mode = ES_MODE_GET_FILE_ENTRY;
-   return true;
-   case ES_MODE_GET_FILE_ENTRY:
-   if (type != TYPE_STREAM)
-   return false;
-   *mode = ES_MODE_GET_STRM_ENTRY;
-   return true;
-   case ES_MODE_GET_STRM_ENTRY:
-   if (type != TYPE_EXTEND)
-   return false;
-   *mode = ES_MODE_GET_NAME_ENTRY;
-   return true;
-   case ES_MODE_GET_NAME_ENTRY:
-   if (type == TYPE_STREAM)
- 

Re: [PATCH v3] exfat: remove EXFAT_SB_DIRTY flag

2020-07-10 Thread Tetsuhiro Kohada



On 2020/06/18 22:11, Sungjong Seo wrote:

BTW
Even with this patch applied,  VOL_DIRTY remains until synced in the above
case.
It's not  easy to reproduce as rmdir, but I'll try to fix it in the future.


I think it's not a problem not to clear VOL_DIRTY under real errors,
because VOL_DIRTY is just like a hint to note that write was not finished 
clearly.

If you mean there are more situation like ENOTEMPTY you mentioned,
please make new patch to fix them.



When should VOL_DIRTY be cleared?

The current behavior is ...

Case of  mkdir, rmdir, rename:
  - set VOL_DIRTY before operation
  - set VOL_CLEAN after operating.
In async mode, it is actually written to the media after 30 seconds.

Case of  cp, touch:
  - set VOL_DIRTY before operation
  - however, VOL_CLEAN is not called in this context.
VOL_CLEAN will call by sync_fs or unmount.

I added VOL_CLEAN in last of __exfat_write_inode() and exfat_map_cluster().
As a result, VOL_DIRTY is cleared with cp and touch.
However, when copying a many files ...
 - Async mode: VOL_DIRTY is written to the media twice every 30 seconds.
 - Sync mode: Of course,  VOL_DIRTY and VOL_CLEAN to the media for each file.

Frequent writing VOL_DIRTY and VOL_CLEAN  increases the risk of boot-sector 
curruption.
If the boot-sector corrupted, it causes the following serious problems  on some 
OSs.
 - misjudge as unformatted
 - can't judge as exfat
 - can't repair

I want to minimize boot sector writes, to reduce these risk.

I looked vfat/udf implementation, which manages similar dirty information on 
linux,
and found that they ware mark-dirty at mount and cleared at unmount.

Here are some ways to clear VOL_DIRTY.

(A) VOL_CLEAN after every write operation.
  :-) Ejectable at any time after a write operation.
  :-( Many times write to Boot-sector.

(B) dirty at mount, clear at unmount (same as vfat/udf)
  :-) Write to boot-sector twice.
  :-( It remains dirty unless unmounted.
  :-( Write to boot-sector even if there is no write operation. 

(C) dirty on first write operation, clear on unmount
  :-) Writing to boot-sector is minimal.
  :-) Will not write to the boot-sector if there is no write operation.
  :-( It remains dirty unless unmounted.

(D) dirty on first write operation,  clear on sync-fs/unmount
 :-) Writing to boot-sector can be reduced.
 :-) Will not write to the boot-sector if there is no write operation.
 :-) sync-fs makes it clean and ejectable immidiately.
 :-( It remains dirty unless sync-fs or unmount.
 :-( Frequent sync-fs will  increases writes to boot-sector.

I think it should be (C) or(D).
What do you think?



BR
---
Tetsuhiro Kohada 


[PATCH] exfat: retain 'VolumeFlags' properly

2020-07-08 Thread Tetsuhiro Kohada
Retain ActiveFat, MediaFailure and ClearToZero fields.
And, never clear VolumeDirty, if it is dirty at mount.

In '3.1.13.3 Media Failure Field' of exfat specification says ...
 If, upon mounting a volume, the value of this field is 1, implementations
 which scan the entire volume for media failures and record all failures as
 "bad" clusters in the FAT (or otherwise resolve media failures) may clear
 the value of this field to 0.
Therefore, should not clear MediaFailure without scanning volume.

In '8.1 Recommended Write Ordering' of exfat specification says ...
 Clear the value of the VolumeDirty field to 0, if its value prior to the
 first step was 0
Therefore, should not clear VolumeDirty when mounted.

Also, rename ERR_MEDIUM to MED_FAILURE.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/exfat_fs.h  |  5 +++--
 fs/exfat/exfat_raw.h |  2 +-
 fs/exfat/super.c | 22 ++
 3 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index cb51d6e83199..3f8dc4ca8109 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -224,7 +224,8 @@ 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 */
+   unsigned int vol_flags; /* volume flags */
+   unsigned int vol_flags_noclear; /* volume flags to retain */
struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
 
unsigned int map_clu; /* allocation bitmap start cluster */
@@ -380,7 +381,7 @@ 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_vol_flags(struct super_block *sb, unsigned short new_flags);
 
 /* fatent.c */
 #define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index 350ce59cc324..d86a8a6b0601 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -16,7 +16,7 @@
 
 #define VOL_CLEAN  0x
 #define VOL_DIRTY  0x0002
-#define ERR_MEDIUM 0x0004
+#define MED_FAILURE0x0004
 
 #define EXFAT_EOF_CLUSTER  0xu
 #define EXFAT_BAD_CLUSTER  0xFFF7u
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index b5bf6dedbe11..c26b0f5a0875 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -96,17 +96,22 @@ 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)
+int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
 {
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
bool sync;
 
+   if (new_flags == VOL_CLEAN)
+   new_flags = (sbi->vol_flags & ~VOL_DIRTY) | 
sbi->vol_flags_noclear;
+   else
+   new_flags |= sbi->vol_flags;
+
/* 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
@@ -114,9 +119,9 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned 
short new_flag)
if (sb_rdonly(sb))
return 0;
 
-   p_boot->vol_flags = cpu_to_le16(new_flag);
+   p_boot->vol_flags = cpu_to_le16(new_flags);
 
-   if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->boot_bh))
+   if ((new_flags & VOL_DIRTY) && !buffer_dirty(sbi->boot_bh))
sync = true;
else
sync = false;
@@ -457,7 +462,8 @@ static int exfat_read_boot_sector(struct super_block *sb)
sbi->dentries_per_clu = 1 <<
(sbi->cluster_size_bits - DENTRY_SIZE_BITS);
 
-   sbi->vol_flag = le16_to_cpu(p_boot->vol_flags);
+   sbi->vol_flags = le16_to_cpu(p_boot->vol_flags);
+   sbi->vol_flags_noclear = sbi->vol_flags & (VOL_DIRTY | MED_FAILURE);
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED;
 
@@ -472,9 +478,9 @@ static int exfat_read_boot_sector(struct super_block *sb)
exfat_err(sb, "bogus data start sector");
return -EINVAL;
}
-   if (sbi->vol_flag & VOL_DIRTY)
+   if (sbi->vol_flags & VOL_DIRTY)
exfat_warn(sb, "Volume was not properly unmounted. Some data 
may be corrupt. Please run fsck.");
-   if (sbi->vol_flag & ERR_MEDIUM)
+   if (sbi-

[RFC]PATCH] exfat: integrates dir-entry getting and validation

2020-06-26 Thread Tetsuhiro Kohada
Add validation for num, bh and type on getting dir-entry.
('file' and 'stream-ext' dir-entries are pre-validated to ensure success)
Renamed exfat_get_dentry_cached() to exfat_get_validated_dentry() due to
a change in functionality.

Integrate type-validation with simplified.
This will also recognize a dir-entry set that contains 'benign secondary'
dir-entries.

And, rename TYPE_EXTEND to TYPE_NAME.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 144 ++--
 fs/exfat/exfat_fs.h |  15 +++--
 fs/exfat/file.c |   4 +-
 fs/exfat/inode.c|   6 +-
 fs/exfat/namei.c|   4 +-
 5 files changed, 73 insertions(+), 100 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index f4cea9a7fd02..e029e0986edc 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -33,6 +33,7 @@ static void exfat_get_uniname_from_ext_entry(struct 
super_block *sb,
 {
int i;
struct exfat_entry_set_cache *es;
+   struct exfat_dentry *ep;
 
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
if (!es)
@@ -44,13 +45,9 @@ 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);
-
-   /* end of name entry */
-   if (exfat_get_entry_type(ep) != TYPE_EXTEND)
-   break;
 
+   i = 2;
+   while ((ep = exfat_get_validated_dentry(es, i++, TYPE_NAME))) {
exfat_extract_uni_name(ep, uniname);
uniname += EXFAT_FILE_NAME_LEN;
}
@@ -372,7 +369,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
if (ep->type == EXFAT_STREAM)
return TYPE_STREAM;
if (ep->type == EXFAT_NAME)
-   return TYPE_EXTEND;
+   return TYPE_NAME;
if (ep->type == EXFAT_ACL)
return TYPE_ACL;
return TYPE_CRITICAL_SEC;
@@ -388,7 +385,7 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, 
unsigned int type)
ep->type &= EXFAT_DELETE;
} else if (type == TYPE_STREAM) {
ep->type = EXFAT_STREAM;
-   } else if (type == TYPE_EXTEND) {
+   } else if (type == TYPE_NAME) {
ep->type = EXFAT_NAME;
} else if (type == TYPE_BITMAP) {
ep->type = EXFAT_BITMAP;
@@ -421,7 +418,7 @@ static void exfat_init_name_entry(struct exfat_dentry *ep,
 {
int i;
 
-   exfat_set_entry_type(ep, TYPE_EXTEND);
+   exfat_set_entry_type(ep, TYPE_NAME);
ep->dentry.name.flags = 0x0;
 
for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
@@ -594,12 +591,12 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
struct exfat_dentry *ep;
 
for (i = 0; i < es->num_entries; i++) {
-   ep = exfat_get_dentry_cached(es, i);
+   ep = exfat_get_validated_dentry(es, i, TYPE_ALL);
chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
 chksum_type);
chksum_type = CS_DEFAULT;
}
-   ep = exfat_get_dentry_cached(es, 0);
+   ep = exfat_get_validated_dentry(es, 0, TYPE_FILE);
ep->dentry.file.checksum = cpu_to_le16(chksum);
es->modified = true;
 }
@@ -741,92 +738,66 @@ struct exfat_dentry *exfat_get_dentry(struct super_block 
*sb,
return (struct exfat_dentry *)((*bh)->b_data + off);
 }
 
-enum exfat_validate_dentry_mode {
-   ES_MODE_STARTED,
-   ES_MODE_GET_FILE_ENTRY,
-   ES_MODE_GET_STRM_ENTRY,
-   ES_MODE_GET_NAME_ENTRY,
-   ES_MODE_GET_CRITICAL_SEC_ENTRY,
-};
-
-static bool exfat_validate_entry(unsigned int type,
-   enum exfat_validate_dentry_mode *mode)
-{
-   if (type == TYPE_UNUSED || type == TYPE_DELETED)
-   return false;
-
-   switch (*mode) {
-   case ES_MODE_STARTED:
-   if  (type != TYPE_FILE && type != TYPE_DIR)
-   return false;
-   *mode = ES_MODE_GET_FILE_ENTRY;
-   return true;
-   case ES_MODE_GET_FILE_ENTRY:
-   if (type != TYPE_STREAM)
-   return false;
-   *mode = ES_MODE_GET_STRM_ENTRY;
-   return true;
-   case ES_MODE_GET_STRM_ENTRY:
-   if (type != TYPE_EXTEND)
-   return false;
-   *mode = ES_MODE_GET_NAME_ENTRY;
-   return true;
-   case ES_MODE_GET_NAME_ENTRY:
-   if (type == TYPE_STREAM)
-   return false;
-   if (type != TYPE_EXTE

[PATCH v2] exfat: optimize exfat_zeroed_cluster()

2020-06-23 Thread Tetsuhiro Kohada
Replace part of exfat_zeroed_cluster() with exfat_update_bhs().
And remove exfat_sync_bhs().

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2
 - Rebase to latest exfat-dev

 fs/exfat/fatent.c | 53 +--
 1 file changed, 10 insertions(+), 43 deletions(-)

diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
index 82ee8246c080..c3c9afee7418 100644
--- a/fs/exfat/fatent.c
+++ b/fs/exfat/fatent.c
@@ -229,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;
@@ -265,41 +250,23 @@ 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(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:
-- 
2.25.1



[PATCH 1/2 v5] exfat: write multiple sectors at once

2020-06-23 Thread Tetsuhiro Kohada
Write multiple sectors at once when updating dir-entries.
Add exfat_update_bhs() for that. It wait for write completion once
instead of sector by sector.
It's only effective if sync enabled.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'
Changes in v3
 - Rebase to latest exfat-dev
Changes in v4
 - Use if/else instead of conditional operator
Changes in v5
 - Remove Reviewed-by tag

 fs/exfat/dir.c  | 15 +--
 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 19 +++
 3 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 02acbb6ddf02..7c2e29632634 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -606,13 +606,16 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
 
 void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
-   int i;
+   int i, err = 0;
 
-   for (i = 0; i < es->num_bh; i++) {
-   if (es->modified)
-   exfat_update_bh(es->bh[i], sync);
-   brelse(es->bh[i]);
-   }
+   if (es->modified)
+   err = exfat_update_bhs(es->bh, es->num_bh, sync);
+
+   for (i = 0; i < es->num_bh; i++)
+   if (err)
+   bforget(es->bh[i]);
+   else
+   brelse(es->bh[i]);
kfree(es);
 }
 
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 84664024e51e..cbb00ee97183 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -512,6 +512,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 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);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index 8a3dde59052b..564718747fb2 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -172,6 +172,25 @@ void exfat_update_bh(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)
 {
-- 
2.25.1



[PATCH 1/2 v4] exfat: write multiple sectors at once

2020-06-19 Thread Tetsuhiro Kohada
Write multiple sectors at once when updating dir-entries.
Add exfat_update_bhs() for that. It wait for write completion once
instead of sector by sector.
It's only effective if sync enabled.

Reviewed-by: Christoph Hellwig 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'
Changes in v3
 - Rebase to latest exfat-dev
Changes in v4
 - Use if/else instead of conditional operator

 fs/exfat/dir.c  | 15 +--
 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 19 +++
 3 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 02acbb6ddf02..7c2e29632634 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -606,13 +606,16 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
 
 void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
-   int i;
+   int i, err = 0;
 
-   for (i = 0; i < es->num_bh; i++) {
-   if (es->modified)
-   exfat_update_bh(es->bh[i], sync);
-   brelse(es->bh[i]);
-   }
+   if (es->modified)
+   err = exfat_update_bhs(es->bh, es->num_bh, sync);
+
+   for (i = 0; i < es->num_bh; i++)
+   if (err)
+   bforget(es->bh[i]);
+   else
+   brelse(es->bh[i]);
kfree(es);
 }
 
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 84664024e51e..cbb00ee97183 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -512,6 +512,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 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);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index 8a3dde59052b..564718747fb2 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -172,6 +172,25 @@ void exfat_update_bh(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)
 {
-- 
2.25.1



[PATCH 2/2 v4] exfat: add error check when updating dir-entries

2020-06-19 Thread Tetsuhiro Kohada
Add error check when synchronously updating dir-entries.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'
Changes in v3
 - Rebase to latest exfat-dev
Changes in v4
 - Based on 'exfat: write multiple sectors at once' v4

 fs/exfat/dir.c  | 3 ++-
 fs/exfat/exfat_fs.h | 2 +-
 fs/exfat/file.c | 5 -
 fs/exfat/inode.c| 8 +---
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 7c2e29632634..2dc6fe695cb6 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -604,7 +604,7 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
es->modified = true;
 }
 
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
+int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
int i, err = 0;
 
@@ -617,6 +617,7 @@ void exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
else
brelse(es->bh[i]);
kfree(es);
+   return err;
 }
 
 static int exfat_walk_fat_chain(struct super_block *sb,
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index cbb00ee97183..da677c85314f 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -459,7 +459,7 @@ 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);
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
+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 */
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index fce03f318787..37c8f04c1f8a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -153,6 +153,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
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);
@@ -187,7 +188,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
}
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
+   if (err)
+   return err;
}
 
/* cut off from the FAT chain */
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index cf9ca6c4d046..1e851f172e0c 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -77,8 +77,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, sync);
-   return 0;
+   return exfat_free_dentry_set(es, sync);
 }
 
 int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -222,6 +221,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
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);
@@ -240,7 +240,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
ep->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, 
inode_needs_sync(inode));
+   if (err)
+   return err;
 
} /* end of if != DIR_DELETED */
 
-- 
2.25.1



Re: [PATCH v3] exfat: remove EXFAT_SB_DIRTY flag

2020-06-18 Thread Tetsuhiro Kohada

I mentioned rmdir as an example.
However, this problem is not only with rmdirs.
VOL_DIRTY remains when some functions abort with an error.
In original, VOL_DIRTY is not cleared even if performe 'sync'.
With this patch, it ensures that VOL_DIRTY will be cleared by 'sync'.

Is my description insufficient?


I understood what you said. However, it is a natural result
when deleting the related code with EXFAT_SB_DIRTY flag.

So I thought it would be better to separate it into new problems
related to VOL_DIRTY-set under not real errors.


I see.
It seems that it is better to consider separately when consistency is corrupted 
and when it is kept.


BTW
Even with this patch applied,  VOL_DIRTY remains until synced in the above
case.
It's not  easy to reproduce as rmdir, but I'll try to fix it in the future.


I think it's not a problem not to clear VOL_DIRTY under real errors,
because VOL_DIRTY is just like a hint to note that write was not finished 
clearly.

If you mean there are more situation like ENOTEMPTY you mentioned,
please make new patch to fix them.


Hmm.
VOL_DIRTY is easily cleared by another write operation.
For that purpose, I think MediaFailure is more appropriate.

BR
---
Tetsuhiro Kohada 


Re: [PATCH v3] exfat: remove EXFAT_SB_DIRTY flag

2020-06-18 Thread Tetsuhiro Kohada

Thank you for your comment.

On 2020/06/17 16:20, Sungjong Seo wrote:

remove EXFAT_SB_DIRTY flag and related codes.

This flag is set/reset in exfat_put_super()/exfat_sync_fs() to avoid
sync_blockdev().
However ...
- exfat_put_super():
Before calling this, the VFS has already called sync_filesystem(), so sync
is never performed here.
- exfat_sync_fs():
After calling this, the VFS calls sync_blockdev(), so, it is meaningless
to check EXFAT_SB_DIRTY or to bypass sync_blockdev() here.
Not only that, but in some cases can't clear VOL_DIRTY.
ex:
VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.



Since this patch does not resolve 'VOL_DIRTY in ENOTEMPTY' problem you
mentioned,
it would be better to remove the description above for that and to make new
patch.


I mentioned rmdir as an example.
However, this problem is not only with rmdirs.
VOL_DIRTY remains when some functions abort with an error.
In original, VOL_DIRTY is not cleared even if performe 'sync'.
With this patch, it ensures that VOL_DIRTY will be cleared by 'sync'.

Is my description insufficient?


BTW
Even with this patch applied,  VOL_DIRTY remains until synced in the above case.
It's not  easy to reproduce as rmdir, but I'll try to fix it in the future.


BR
---
Tetsuhiro Kohada 





[PATCH 1/2 v3] exfat: write multiple sectors at once

2020-06-17 Thread Tetsuhiro Kohada
Write multiple sectors at once when updating dir-entries.
Add exfat_update_bhs() for that. It wait for write completion once
instead of sector by sector.
It's only effective if sync enabled.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'
Changes in v3
 - Rebase to latest exfat-dev

 fs/exfat/dir.c  | 12 ++--
 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 19 +++
 3 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 02acbb6ddf02..a3364df6339c 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -606,13 +606,13 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
 
 void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
-   int i;
+   int i, err = 0;
 
-   for (i = 0; i < es->num_bh; i++) {
-   if (es->modified)
-   exfat_update_bh(es->bh[i], sync);
-   brelse(es->bh[i]);
-   }
+   if (es->modified)
+   err = exfat_update_bhs(es->bh, es->num_bh, sync);
+
+   for (i = 0; i < es->num_bh; i++)
+   err ? bforget(es->bh[i]):brelse(es->bh[i]);
kfree(es);
 }
 
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 84664024e51e..cbb00ee97183 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -512,6 +512,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 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);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index 8a3dde59052b..564718747fb2 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -172,6 +172,25 @@ void exfat_update_bh(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)
 {
-- 
2.25.1



[PATCH 2/2 v3] exfat: add error check when updating dir-entries

2020-06-17 Thread Tetsuhiro Kohada
Add error check when synchronously updating dir-entries.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'
Changes in v3
 - Rebase to latest exfat-dev

 fs/exfat/dir.c  | 3 ++-
 fs/exfat/exfat_fs.h | 2 +-
 fs/exfat/file.c | 5 -
 fs/exfat/inode.c| 8 +---
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index a3364df6339c..89d216d5ac89 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -604,7 +604,7 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
es->modified = true;
 }
 
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
+int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
int i, err = 0;
 
@@ -614,6 +614,7 @@ void exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
for (i = 0; i < es->num_bh; i++)
err ? bforget(es->bh[i]):brelse(es->bh[i]);
kfree(es);
+   return err;
 }
 
 static int exfat_walk_fat_chain(struct super_block *sb,
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index cbb00ee97183..da677c85314f 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -459,7 +459,7 @@ 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);
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
+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 */
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index fce03f318787..37c8f04c1f8a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -153,6 +153,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
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);
@@ -187,7 +188,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
}
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
+   if (err)
+   return err;
}
 
/* cut off from the FAT chain */
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index cf9ca6c4d046..1e851f172e0c 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -77,8 +77,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, sync);
-   return 0;
+   return exfat_free_dentry_set(es, sync);
 }
 
 int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -222,6 +221,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
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);
@@ -240,7 +240,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
ep->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, 
inode_needs_sync(inode));
+   if (err)
+   return err;
 
} /* end of if != DIR_DELETED */
 
-- 
2.25.1



Re: [PATCH v2] exfat: remove EXFAT_SB_DIRTY flag

2020-06-15 Thread Tetsuhiro Kohada

On 2020/06/16 11:03, Sungjong Seo wrote:

remove EXFAT_SB_DIRTY flag and related codes.

This flag is set/reset in exfat_put_super()/exfat_sync_fs() to avoid
sync_blockdev().
However ...
- exfat_put_super():
Before calling this, the VFS has already called sync_filesystem(), so sync
is never performed here.
- exfat_sync_fs():
After calling this, the VFS calls sync_blockdev(), so, it is meaningless
to check EXFAT_SB_DIRTY or to bypass sync_blockdev() here.
Not only that, but in some cases can't clear VOL_DIRTY.
ex:
VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.

Remove the EXFAT_SB_DIRTY check to ensure synchronization.
And, remove the code related to the flag.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
  - exfat_sync_fs() avoids synchronous processing when wait=0

  fs/exfat/balloc.c   |  4 ++--
  fs/exfat/dir.c  | 16 
  fs/exfat/exfat_fs.h |  5 +
  fs/exfat/fatent.c   |  7 ++-
  fs/exfat/misc.c |  3 +--
  fs/exfat/namei.c| 12 ++--
  fs/exfat/super.c| 14 ++
  7 files changed, 26 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index
4055eb00ea9b..a987919686c0 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -158,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;
  }

@@ -180,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;
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index
8e775bd5d523..02acbb6ddf02 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -470,7 +470,7 @@ int exfat_init_dir_entry(struct inode *inode, struct
exfat_chain *p_dir,
>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, , ); @@ -
480,7 +480,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;
@@ -516,7 +516,7 @@ int exfat_update_dir_chksum(struct inode *inode,
struct exfat_chain *p_dir,
}

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;
@@ -538,7 +538,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, , ); @@ -
547,7 +547,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++) { @@ -556,7
+556,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;
}
@@ -580,7 +580,7 @@ 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);
}

@@ -610,7 +610,7 @@ void exfat_free_dentry_set(struct
exfat_entry_set_cache *es, int sync)

for (i = 0; i < es->num_bh; i++) {
if (es->modified)
-   exfat_update_bh(es->s

[PATCH v3] exfat: remove EXFAT_SB_DIRTY flag

2020-06-15 Thread Tetsuhiro Kohada
remove EXFAT_SB_DIRTY flag and related codes.

This flag is set/reset in exfat_put_super()/exfat_sync_fs()
to avoid sync_blockdev().
However ...
- exfat_put_super():
Before calling this, the VFS has already called sync_filesystem(),
so sync is never performed here.
- exfat_sync_fs():
After calling this, the VFS calls sync_blockdev(), so, it is meaningless
to check EXFAT_SB_DIRTY or to bypass sync_blockdev() here.
Not only that, but in some cases can't clear VOL_DIRTY.
ex:
VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.

Remove the EXFAT_SB_DIRTY check to ensure synchronization.
And, remove the code related to the flag.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - exfat_sync_fs() avoids synchronous processing when wait=0
Changes in v3:
 - fix return value in exfat_sync_fs()

 fs/exfat/balloc.c   |  4 ++--
 fs/exfat/dir.c  | 16 
 fs/exfat/exfat_fs.h |  5 +
 fs/exfat/fatent.c   |  7 ++-
 fs/exfat/misc.c |  3 +--
 fs/exfat/namei.c| 12 ++--
 fs/exfat/super.c| 14 ++
 7 files changed, 26 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index 4055eb00ea9b..a987919686c0 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -158,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;
 }
 
@@ -180,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;
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 8e775bd5d523..02acbb6ddf02 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -470,7 +470,7 @@ int exfat_init_dir_entry(struct inode *inode, struct 
exfat_chain *p_dir,
>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, , );
@@ -480,7 +480,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;
@@ -516,7 +516,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
}
 
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;
@@ -538,7 +538,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, , );
@@ -547,7 +547,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++) {
@@ -556,7 +556,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;
}
@@ -580,7 +580,7 @@ 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);
}
 
@@ -610,7 +610,7 @@ void exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
 
for (i = 0; i < es->num_bh; i++) {
if (es->modified)
-   exfat_update_bh(es->sb, e

[PATCH v2] exfat: remove EXFAT_SB_DIRTY flag

2020-06-15 Thread Tetsuhiro Kohada
remove EXFAT_SB_DIRTY flag and related codes.

This flag is set/reset in exfat_put_super()/exfat_sync_fs()
to avoid sync_blockdev().
However ...
- exfat_put_super():
Before calling this, the VFS has already called sync_filesystem(),
so sync is never performed here.
- exfat_sync_fs():
After calling this, the VFS calls sync_blockdev(), so, it is meaningless
to check EXFAT_SB_DIRTY or to bypass sync_blockdev() here.
Not only that, but in some cases can't clear VOL_DIRTY.
ex:
VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.

Remove the EXFAT_SB_DIRTY check to ensure synchronization.
And, remove the code related to the flag.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - exfat_sync_fs() avoids synchronous processing when wait=0

 fs/exfat/balloc.c   |  4 ++--
 fs/exfat/dir.c  | 16 
 fs/exfat/exfat_fs.h |  5 +
 fs/exfat/fatent.c   |  7 ++-
 fs/exfat/misc.c |  3 +--
 fs/exfat/namei.c| 12 ++--
 fs/exfat/super.c| 14 ++
 7 files changed, 26 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index 4055eb00ea9b..a987919686c0 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -158,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;
 }
 
@@ -180,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;
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 8e775bd5d523..02acbb6ddf02 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -470,7 +470,7 @@ int exfat_init_dir_entry(struct inode *inode, struct 
exfat_chain *p_dir,
>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, , );
@@ -480,7 +480,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;
@@ -516,7 +516,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
}
 
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;
@@ -538,7 +538,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, , );
@@ -547,7 +547,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++) {
@@ -556,7 +556,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;
}
@@ -580,7 +580,7 @@ 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);
}
 
@@ -610,7 +610,7 @@ void exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
 
for (i = 0; i < es->num_bh; i++) {
if (es->modified)
-   exfat_update_bh(es->sb, es->bh[

Re: [PATCH] exfat: remove EXFAT_SB_DIRTY flag

2020-06-12 Thread Tetsuhiro Kohada

On 2020/06/12 17:34, Sungjong Seo wrote:

remove EXFAT_SB_DIRTY flag and related codes.

This flag is set/reset in exfat_put_super()/exfat_sync_fs() to avoid
sync_blockdev().
However ...
- exfat_put_super():
Before calling this, the VFS has already called sync_filesystem(), so sync
is never performed here.
- exfat_sync_fs():
After calling this, the VFS calls sync_blockdev(), so, it is meaningless
to check EXFAT_SB_DIRTY or to bypass sync_blockdev() here.
Not only that, but in some cases can't clear VOL_DIRTY.
ex:
VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.

Remove the EXFAT_SB_DIRTY check to ensure synchronization.
And, remove the code related to the flag.

Signed-off-by: Tetsuhiro Kohada 
---
  fs/exfat/balloc.c   |  4 ++--
  fs/exfat/dir.c  | 16 
  fs/exfat/exfat_fs.h |  5 +
  fs/exfat/fatent.c   |  7 ++-
  fs/exfat/misc.c |  3 +--
  fs/exfat/namei.c| 12 ++--
  fs/exfat/super.c| 11 +++
  7 files changed, 23 insertions(+), 35 deletions(-)


[snip]


@@ -62,11 +59,9 @@ static int exfat_sync_fs(struct super_block *sb, int
wait)

/* If there are some dirty buffers in the bdev inode */
mutex_lock(>s_lock);
-   if (test_and_clear_bit(EXFAT_SB_DIRTY, >s_state)) {
-   sync_blockdev(sb->s_bdev);
-   if (exfat_set_vol_flags(sb, VOL_CLEAN))
-   err = -EIO;
-   }


I looked through most codes related to EXFAT_SB_DIRTY and VOL_DIRTY.
And your approach looks good because all of them seem to be protected by
s_lock.

BTW, as you know, sync_filesystem() calls sync_fs() with 'nowait' first,
and then calls it again with 'wait' twice. No need to sync with lock twice.
If so, isn't it okay to do nothing when wait is 0?


I also think  ‘do nothing when wait is 0’ as you say, but I'm still not sure.

Some other Filesystems do nothing with nowait and just return.
However, a few Filesystems always perform sync.

sync_blockdev() waits for completion, so it may be inappropriate to call with  
nowait. (But it was called in the original code)

I'm still not sure, so I excluded it in this patch.
Is it okay to include it?



+   sync_blockdev(sb->s_bdev);
+   if (exfat_set_vol_flags(sb, VOL_CLEAN))
+   err = -EIO;
mutex_unlock(>s_lock);
return err;
  }
--
2.25.1





BR
---
Tetsuhiro Kohada 



[PATCH] exfat: remove EXFAT_SB_DIRTY flag

2020-06-11 Thread Tetsuhiro Kohada
remove EXFAT_SB_DIRTY flag and related codes.

This flag is set/reset in exfat_put_super()/exfat_sync_fs()
to avoid sync_blockdev().
However ...
- exfat_put_super():
Before calling this, the VFS has already called sync_filesystem(),
so sync is never performed here.
- exfat_sync_fs():
After calling this, the VFS calls sync_blockdev(), so, it is meaningless
to check EXFAT_SB_DIRTY or to bypass sync_blockdev() here.
Not only that, but in some cases can't clear VOL_DIRTY.
ex:
VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.

Remove the EXFAT_SB_DIRTY check to ensure synchronization.
And, remove the code related to the flag.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/balloc.c   |  4 ++--
 fs/exfat/dir.c  | 16 
 fs/exfat/exfat_fs.h |  5 +
 fs/exfat/fatent.c   |  7 ++-
 fs/exfat/misc.c |  3 +--
 fs/exfat/namei.c| 12 ++--
 fs/exfat/super.c| 11 +++
 7 files changed, 23 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index 4055eb00ea9b..a987919686c0 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -158,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;
 }
 
@@ -180,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;
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 8e775bd5d523..02acbb6ddf02 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -470,7 +470,7 @@ int exfat_init_dir_entry(struct inode *inode, struct 
exfat_chain *p_dir,
>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, , );
@@ -480,7 +480,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;
@@ -516,7 +516,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
}
 
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;
@@ -538,7 +538,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, , );
@@ -547,7 +547,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++) {
@@ -556,7 +556,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;
}
@@ -580,7 +580,7 @@ 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);
}
 
@@ -610,7 +610,7 @@ void exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
 
for (i = 0; i < es->num_bh; i++) {
if (es->modified)
-   exfat_update_bh(es->sb, es->bh[i], sync);
+   exfat_update_bh(es->bh[i], sync);
brelse(es->bh[i]);
}
 

Re: [PATCH 1/3] exfat: add error check when updating dir-entries

2020-06-09 Thread Tetsuhiro Kohada

Add error check when synchronously updating dir-entries.
Furthermore, add exfat_update_bhs(). It wait for write completion once
instead of sector by sector.

This patch can be split into two also ?


I sent a patch split into 'write multiple sectors at once'
and 'add error check when updating dir-entries'.

The other two patches(2nd & 3rd) are no-changed, so have not been sent.
If you need the other two patches, I will send them.
In that case, please tell me how to write the subject and change-log.

BR
---
Tetsuhiro Kohada 


[PATCH 2/2] exfat: add error check when updating dir-entries

2020-06-09 Thread Tetsuhiro Kohada
Add error check when synchronously updating dir-entries.

Suggested-by: Namjae Jeon 
Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'

 fs/exfat/dir.c  | 3 ++-
 fs/exfat/exfat_fs.h | 2 +-
 fs/exfat/file.c | 5 -
 fs/exfat/inode.c| 8 +---
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 495884ccb352..3eb8386fb5f2 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -602,7 +602,7 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
es->modified = true;
 }
 
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
+int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
int i, err = 0;
 
@@ -614,6 +614,7 @@ void exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
for (i = 0; i < es->num_bh; i++)
err ? bforget(es->bh[i]):brelse(es->bh[i]);
kfree(es);
+   return err;
 }
 
 static int exfat_walk_fat_chain(struct super_block *sb,
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 935954da2e54..f4fa0e833486 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -462,7 +462,7 @@ 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);
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
+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 */
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index fce03f318787..37c8f04c1f8a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -153,6 +153,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
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);
@@ -187,7 +188,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
}
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
+   if (err)
+   return err;
}
 
/* cut off from the FAT chain */
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index ef7cf7a6d187..c0bfd1a586aa 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -77,8 +77,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, sync);
-   return 0;
+   return exfat_free_dentry_set(es, sync);
 }
 
 int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -222,6 +221,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
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);
@@ -240,7 +240,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
ep->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, 
inode_needs_sync(inode));
+   if (err)
+   return err;
 
} /* end of if != DIR_DELETED */
 
-- 
2.25.1



[PATCH 1/2 v2] exfat: write multiple sectors at once

2020-06-09 Thread Tetsuhiro Kohada
Write multiple sectors at once when updating dir-entries.
Add exfat_update_bhs() for that. It wait for write completion once
instead of sector by sector.
It's only effective if sync enabled.

Suggested-by: Namjae Jeon 
Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - Split into 'write multiple sectors at once'
   and 'add error check when updating dir-entries'

 fs/exfat/dir.c  | 12 +++-
 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 19 +++
 3 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index de43534aa299..495884ccb352 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -604,13 +604,15 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
 
 void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
-   int i;
+   int i, err = 0;
 
-   for (i = 0; i < es->num_bh; i++) {
-   if (es->modified)
-   exfat_update_bh(es->sb, es->bh[i], sync);
-   brelse(es->bh[i]);
+   if (es->modified) {
+   set_bit(EXFAT_SB_DIRTY, _SB(es->sb)->s_state);
+   err = exfat_update_bhs(es->bh, es->num_bh, sync);
}
+
+   for (i = 0; i < es->num_bh; i++)
+   err ? bforget(es->bh[i]):brelse(es->bh[i]);
kfree(es);
 }
 
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 595f3117f492..935954da2e54 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -515,6 +515,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
 u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
+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);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index 17d41f3d3709..dc34968e99d3 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -173,6 +173,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)
 {
-- 
2.25.1



[PATCH 2/3] exfat: optimize exfat_zeroed_cluster()

2020-06-04 Thread Tetsuhiro Kohada
Replace part of exfat_zeroed_cluster() with exfat_update_bhs().
And remove exfat_sync_bhs().

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/fatent.c | 54 ++-
 1 file changed, 11 insertions(+), 43 deletions(-)

diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
index 267e5e09eb13..5d11bc2f1b68 100644
--- a/fs/exfat/fatent.c
+++ b/fs/exfat/fatent.c
@@ -230,21 +230,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;
@@ -266,41 +251,24 @@ 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);
+   set_bit(EXFAT_SB_DIRTY, >s_state);
+   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:
-- 
2.25.1



[PATCH 1/3] exfat: add error check when updating dir-entries

2020-06-04 Thread Tetsuhiro Kohada
Add error check when synchronously updating dir-entries.
Furthermore, add exfat_update_bhs(). It wait for write completion once
instead of sector by sector.

Suggested-by: Sungjong Seo 
Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 15 +--
 fs/exfat/exfat_fs.h |  3 ++-
 fs/exfat/file.c |  5 -
 fs/exfat/inode.c|  8 +---
 fs/exfat/misc.c | 19 +++
 5 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index de43534aa299..3eb8386fb5f2 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -602,16 +602,19 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
es->modified = true;
 }
 
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
+int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
 {
-   int i;
+   int i, err = 0;
 
-   for (i = 0; i < es->num_bh; i++) {
-   if (es->modified)
-   exfat_update_bh(es->sb, es->bh[i], sync);
-   brelse(es->bh[i]);
+   if (es->modified) {
+   set_bit(EXFAT_SB_DIRTY, _SB(es->sb)->s_state);
+   err = exfat_update_bhs(es->bh, es->num_bh, sync);
}
+
+   for (i = 0; i < es->num_bh; i++)
+   err ? bforget(es->bh[i]):brelse(es->bh[i]);
kfree(es);
+   return err;
 }
 
 static int exfat_walk_fat_chain(struct super_block *sb,
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 595f3117f492..f4fa0e833486 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -462,7 +462,7 @@ 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);
-void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
+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 */
@@ -515,6 +515,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
 u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
+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);
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index fce03f318787..37c8f04c1f8a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -153,6 +153,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
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);
@@ -187,7 +188,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
}
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   err = exfat_free_dentry_set(es, inode_needs_sync(inode));
+   if (err)
+   return err;
}
 
/* cut off from the FAT chain */
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index ef7cf7a6d187..c0bfd1a586aa 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -77,8 +77,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, sync);
-   return 0;
+   return exfat_free_dentry_set(es, sync);
 }
 
 int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -222,6 +221,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
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);
@@ -240,7 +240,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned 
int clu_offset,
ep->dentry.stream.valid_size;
 
exfat_update_dir_chksum_with_entry_set(es);
-   exfat_free_dentry_set(es, inode_needs_sync(inode));
+   er

[PATCH 3/3] exfat: set EXFAT_SB_DIRTY and VOL_DIRTY at the same timing

2020-06-04 Thread Tetsuhiro Kohada
Set EXFAT_SB_DIRTY flag in exfat_put_super().

In some cases, can't clear VOL_DIRTY with 'sync'.
ex:

VOL_DIRTY is set when rmdir starts, but when non-empty-dir is detected,
return error without setting EXFAT_SB_DIRTY.
If performe 'sync' in this state, VOL_DIRTY will not be cleared.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/balloc.c   |  4 ++--
 fs/exfat/dir.c  | 18 --
 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/fatent.c   |  6 +-
 fs/exfat/misc.c |  3 +--
 fs/exfat/namei.c| 12 ++--
 fs/exfat/super.c|  3 +++
 7 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index 4055eb00ea9b..a987919686c0 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -158,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;
 }
 
@@ -180,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;
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 3eb8386fb5f2..96c9a817d928 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -468,7 +468,7 @@ int exfat_init_dir_entry(struct inode *inode, struct 
exfat_chain *p_dir,
>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, , );
@@ -478,7 +478,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;
@@ -514,7 +514,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
}
 
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;
@@ -536,7 +536,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, , );
@@ -545,7 +545,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++) {
@@ -554,7 +554,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;
}
@@ -578,7 +578,7 @@ 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);
}
 
@@ -606,10 +606,8 @@ int exfat_free_dentry_set(struct exfat_entry_set_cache 
*es, int sync)
 {
int i, err = 0;
 
-   if (es->modified) {
-   set_bit(EXFAT_SB_DIRTY, _SB(es->sb)->s_state);
+   if (es->modified)
err = exfat_update_bhs(es->bh, es->num_bh, sync);
-   }
 
for (i = 0; i < es->num_bh; i++)
err ? bforget(es->bh[i]):brelse(es->bh[i]);
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index f4fa0e833486..0e094d186612 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -514,7 +514,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
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(voi

[PATCH 3/4 v4] exfat: add boot region verification

2020-05-31 Thread Tetsuhiro Kohada
Add Boot-Regions verification specified in exFAT specification.
Note that the checksum type is strongly related to the raw structure,
so the'u32 'type is used to clarify the number of bits.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied
 - just print a warning when invalid exboot-signature detected
 - print additional information when invalid boot-checksum detected
Changes in v3:
 - based on '[PATCH 2/4 v3] exfat: separate the boot sector analysis'
Changes in v4:
 - fix type of p_sig/p_chksum to __le32

 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 14 +
 fs/exfat/super.c| 50 +
 3 files changed, 65 insertions(+)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 9673e2d31045..eebbe5a84b2b 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -514,6 +514,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type);
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
unsigned int size, unsigned char flags);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index ab7f88b1f6d3..b82d2dd5bd7c 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -151,6 +151,20 @@ unsigned short exfat_calc_chksum_2byte(void *data, int len,
return chksum;
 }
 
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
+{
+   int i;
+   u8 *c = (u8 *)data;
+
+   for (i = 0; i < len; i++, c++) {
+   if (unlikely(type == CS_BOOT_SECTOR &&
+(i == 106 || i == 107 || i == 112)))
+   continue;
+   chksum = ((chksum << 31) | (chksum >> 1)) + *c;
+   }
+   return chksum;
+}
+
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
 {
set_bit(EXFAT_SB_DIRTY, _SB(sb)->s_state);
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 6a1330be5a9a..405717e4e3ea 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -491,6 +491,50 @@ static int exfat_read_boot_sector(struct super_block *sb)
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 *)>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 *)>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)
 {
@@ -503,6 +547,12 @@ static int __exfat_fill_super(struct super_block *sb)
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_err(sb, "failed to load upcase table");
-- 
2.25.1



[PATCH 3/4 v3] exfat: add boot region verification

2020-05-29 Thread Tetsuhiro Kohada
Add Boot-Regions verification specified in exFAT specification.
Note that the checksum type is strongly related to the raw structure,
so the'u32 'type is used to clarify the number of bits.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied
 - just print a warning when invalid exboot-signature detected
 - print additional information when invalid boot-checksum detected
Changes in v3:
 - based on '[PATCH 2/4 v3] exfat: separate the boot sector analysis'

 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 14 +
 fs/exfat/super.c| 49 +
 3 files changed, 64 insertions(+)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 9673e2d31045..eebbe5a84b2b 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -514,6 +514,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type);
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
unsigned int size, unsigned char flags);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index ab7f88b1f6d3..b82d2dd5bd7c 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -151,6 +151,20 @@ unsigned short exfat_calc_chksum_2byte(void *data, int len,
return chksum;
 }
 
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
+{
+   int i;
+   u8 *c = (u8 *)data;
+
+   for (i = 0; i < len; i++, c++) {
+   if (unlikely(type == CS_BOOT_SECTOR &&
+(i == 106 || i == 107 || i == 112)))
+   continue;
+   chksum = ((chksum << 31) | (chksum >> 1)) + *c;
+   }
+   return chksum;
+}
+
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
 {
set_bit(EXFAT_SB_DIRTY, _SB(sb)->s_state);
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 6a1330be5a9a..c9bc6ad0aade 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -491,6 +491,49 @@ static int exfat_read_boot_sector(struct super_block *sb)
return 0;
 }
 
+static int exfat_verify_boot_region(struct super_block *sb)
+{
+   struct buffer_head *bh = NULL;
+   u32 chksum = 0, *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 = (u32 *)>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 = (u32 *)>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)
 {
@@ -503,6 +546,12 @@ static int __exfat_fill_super(struct super_block *sb)
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_err(sb, "failed to load upcase table");
-- 
2.25.1



[PATCH 2/4 v3] exfat: separate the boot sector analysis

2020-05-29 Thread Tetsuhiro Kohada
Separate the boot sector analysis to read_boot_sector().
And add a check for the fs_name field.
Furthermore, add a strict consistency check, because overlapping areas
can cause serious corruption.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied
Changes in v3:
 - add a check for the fs_name field

 fs/exfat/exfat_raw.h |  2 +
 fs/exfat/super.c | 97 
 2 files changed, 56 insertions(+), 43 deletions(-)

diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index 07f74190df44..350ce59cc324 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -10,11 +10,13 @@
 
 #define BOOT_SIGNATURE 0xAA55
 #define EXBOOT_SIGNATURE   0xAA55
+#define STR_EXFAT  "EXFAT   "  /* size should be 8 */
 
 #define EXFAT_MAX_FILE_LEN 255
 
 #define VOL_CLEAN  0x
 #define VOL_DIRTY  0x0002
+#define ERR_MEDIUM 0x0004
 
 #define EXFAT_EOF_CLUSTER  0xu
 #define EXFAT_BAD_CLUSTER  0xFFF7u
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index e60d28e73ff0..6a1330be5a9a 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -366,25 +366,20 @@ static int exfat_read_root(struct inode *inode)
return 0;
 }
 
-static struct boot_sector *exfat_read_boot_with_logical_sector(
-   struct super_block *sb)
+static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect)
 {
struct exfat_sb_info *sbi = EXFAT_SB(sb);
-   struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
-   unsigned short logical_sect = 0;
-
-   logical_sect = 1 << p_boot->sect_size_bits;
 
if (!is_power_of_2(logical_sect) ||
logical_sect < 512 || logical_sect > 4096) {
exfat_err(sb, "bogus logical sector size %u", logical_sect);
-   return NULL;
+   return -EIO;
}
 
if (logical_sect < sb->s_blocksize) {
exfat_err(sb, "logical sector size too small for device 
(logical sector size = %u)",
  logical_sect);
-   return NULL;
+   return -EIO;
}
 
if (logical_sect > sb->s_blocksize) {
@@ -394,24 +389,20 @@ static struct boot_sector 
*exfat_read_boot_with_logical_sector(
if (!sb_set_blocksize(sb, logical_sect)) {
exfat_err(sb, "unable to set blocksize %u",
  logical_sect);
-   return NULL;
+   return -EIO;
}
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 NULL;
+   return -EIO;
}
-
-   p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
}
-   return p_boot;
+   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 boot_sector *p_boot;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
@@ -424,51 +415,41 @@ static int __exfat_fill_super(struct super_block *sb)
exfat_err(sb, "unable to read boot sector");
return -EIO;
}
-
-   /* PRB is read */
p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
 
/* check the validity of BOOT */
if (le16_to_cpu((p_boot->signature)) != BOOT_SIGNATURE) {
exfat_err(sb, "invalid boot record signature");
-   ret = -EINVAL;
-   goto free_bh;
+   return -EINVAL;
}
 
-
-   /* check logical sector size */
-   p_boot = exfat_read_boot_with_logical_sector(sb);
-   if (!p_boot) {
-   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_boot->must_be_zero, 0,
-   sizeof(p_boot->must_be_zero))) {
-   ret = -EINVAL;
-   goto free_bh;
-   }
+   if (memchr_inv(p_boot->must_be_zero, 0, sizeof(p_boot->must_be_zero)))
+   return -EINVAL;
 
-   p_boot = (struct boot_sector *)p_boot;
-   if (!p_boot->num_fats) {

[PATCH 4/4 v3] exfat: standardize checksum calculation

2020-05-29 Thread Tetsuhiro Kohada
To clarify that it is a 16-bit checksum, the parts related to the 16-bit
checksum are renamed and change type to u16.
Furthermore, replace checksum calculation in exfat_load_upcase_table()
with exfat_calc_checksum32().

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied
Changes in v3:
 - based on '[PATCH 2/4 v3] exfat: separate the boot sector analysis'

 fs/exfat/dir.c  | 12 ++--
 fs/exfat/exfat_fs.h |  5 ++---
 fs/exfat/misc.c | 10 --
 fs/exfat/nls.c  | 19 +++
 4 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 2902d285bf20..de43534aa299 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -491,7 +491,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
int ret = 0;
int i, num_entries;
sector_t sector;
-   unsigned short chksum;
+   u16 chksum;
struct exfat_dentry *ep, *fep;
struct buffer_head *fbh, *bh;
 
@@ -500,7 +500,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
return -EIO;
 
num_entries = fep->dentry.file.num_ext + 1;
-   chksum = exfat_calc_chksum_2byte(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+   chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
 
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, , NULL);
@@ -508,7 +508,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
ret = -EIO;
goto release_fbh;
}
-   chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
+   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
CS_DEFAULT);
brelse(bh);
}
@@ -593,8 +593,8 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
 
for (i = 0; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
-   chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
-chksum_type);
+   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
+chksum_type);
chksum_type = CS_DEFAULT;
}
ep = exfat_get_dentry_cached(es, 0);
@@ -1000,7 +1000,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
}
 
if (entry_type == TYPE_STREAM) {
-   unsigned short name_hash;
+   u16 name_hash;
 
if (step != DIRENT_STEP_STRM) {
step = DIRENT_STEP_FILE;
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index eebbe5a84b2b..9188985694f0 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -137,7 +137,7 @@ struct exfat_dentry_namebuf {
 struct exfat_uni_name {
/* +3 for null and for converting */
unsigned short name[MAX_NAME_LENGTH + 3];
-   unsigned short name_hash;
+   u16 name_hash;
unsigned char name_len;
 };
 
@@ -512,8 +512,7 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 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_cs);
-unsigned short exfat_calc_chksum_2byte(void *data, int len,
-   unsigned short chksum, int type);
+u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
 u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index b82d2dd5bd7c..17d41f3d3709 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -136,17 +136,15 @@ void exfat_truncate_atime(struct timespec64 *ts)
ts->tv_nsec = 0;
 }
 
-unsigned short exfat_calc_chksum_2byte(void *data, int len,
-   unsigned short chksum, int type)
+u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
 {
int i;
-   unsigned char *c = (unsigned char *)data;
+   u8 *c = (u8 *)data;
 
for (i = 0; i < len; i++, c++) {
-   if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY))
+   if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3)))
continue;
-   chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) +
-   (unsigned short)*c;
+   chksum = ((chksum << 15) | (chksum >> 1)) + *c;
}
re

[PATCH 1/4 v3] exfat: redefine PBR as boot_sector

2020-05-29 Thread Tetsuhiro Kohada
Aggregate PBR related definitions and redefine as "boot_sector" to comply
with the exFAT specification.
And, rename variable names including 'pbr'.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied
Changes in v3:
 - rename BOOTSEC_OEM_NAME_LEN to BOOTSEC_FS_NAME_LEN
 - rename oem_name to fs_name in struct boot_sector

 fs/exfat/exfat_fs.h  |  2 +-
 fs/exfat/exfat_raw.h | 79 +++--
 fs/exfat/super.c | 84 ++--
 3 files changed, 72 insertions(+), 93 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 5caad1380818..9673e2d31045 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -227,7 +227,7 @@ struct exfat_sb_info {
unsigned int root_dir; /* root dir cluster */
unsigned int dentries_per_clu; /* num of dentries per cluster */
unsigned int vol_flag; /* volume dirty flag */
-   struct buffer_head *pbr_bh; /* buffer_head of PBR sector */
+   struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
 
unsigned int map_clu; /* allocation bitmap start cluster */
unsigned int map_sectors; /* num of allocation bitmap sectors */
diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index 8d6c64a7546d..07f74190df44 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -8,7 +8,8 @@
 
 #include 
 
-#define PBR_SIGNATURE  0xAA55
+#define BOOT_SIGNATURE 0xAA55
+#define EXBOOT_SIGNATURE   0xAA55
 
 #define EXFAT_MAX_FILE_LEN 255
 
@@ -55,7 +56,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 +70,35 @@
 #define ATTR_RWMASK(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
 ATTR_SUBDIR | ATTR_ARCHIVE)
 
-#define PBR64_JUMP_BOOT_LEN3
-#define PBR64_OEM_NAME_LEN 8
-#define PBR64_RESERVED_LEN 53
+#define BOOTSEC_JUMP_BOOT_LEN  3
+#define BOOTSEC_FS_NAME_LEN8
+#define BOOTSEC_OLDBPB_LEN 53
 
 #define EXFAT_FILE_NAME_LEN15
 
-/* 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 {
+   __u8jmp_boot[BOOTSEC_JUMP_BOOT_LEN];
+   __u8fs_name[BOOTSEC_FS_NAME_LEN];
+   __u8must_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;
+   __u8fs_revision[2];
+   __le16  vol_flags;
+   __u8sect_size_bits;
+   __u8sect_per_clus_bits;
+   __u8num_fats;
+   __u8drv_sel;
+   __u8percent_in_use;
+   __u8reserved[7];
+   __u8boot_code[390];
+   __le16  signature;
 } __packed;
 
 struct exfat_dentry {
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index c1f47f4071a8..e60d28e73ff0 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -49,7 +49,7 @@ static void exfat_put_super(struct super_block *sb)
sync_blockdev(sb->s_bdev);
exfat_set_vol_flags(sb, VOL_CLEAN);
exfat_free_bitmap(sbi);
-   brelse(sbi->pbr_bh);
+   brelse(sbi->boot_bh);
mutex_unlock(>s_lock);
 
call_rcu(>rcu, exfat_delayed_free);
@@ -101,7 +101,7 @@ static int exfat_statfs(struct dentry *dentry, struct 
kstatfs *buf)
 int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
 {
struct exfat_sb_info *sbi = EXFAT_SB(sb);
-   struct pbr64 *bpb = (struct pbr64 *)sbi->pbr_bh->b_data;
+   struct boot_secto

Re: [PATCH 1/4] exfat: redefine PBR as boot_sector

2020-05-29 Thread Tetsuhiro Kohada

I'll make another small patch, OK?


No, It make sense to make v3, because you have renamed the variables in
boot_sector on this patch.


OK.



BTW
I have a concern about fs_name.
The exfat specification says that this field is "EXFAT".

I think it's a important field for determining the filesystem.
However, in this patch, I gave up checking this field.
Because there is no similar check in FATFS.
Do you know why Linux FATFS does not check this filed?
And, what do you think of checking this field?


FATFS has the same field named "oem_name" and whatever is okay for its value.
However, in case of exFAT, it is an important field to determine filesystem.

I think it would be better to check this field for exFAT-fs.
Would you like to contribute new patch for checking it?


I already have the code, so I'll add it to [PATCH 2/4 v3].

BR


Re: [PATCH 1/4] exfat: redefine PBR as boot_sector

2020-05-28 Thread Tetsuhiro Kohada

[snip]

+/* EXFAT: Main and Backup Boot Sector (512 bytes) */ struct boot_sector
+{
+   __u8jmp_boot[BOOTSEC_JUMP_BOOT_LEN];
+   __u8oem_name[BOOTSEC_OEM_NAME_LEN];


According to the exFAT specification, fs_name and BOOTSEC_FS_NAME_LEN look
better.


Oops.
I sent v2 patches, before I noticed this comment,

I'll make another small patch, OK?

BTW
I have a concern about fs_name.
The exfat specification says that this field is "EXFAT".

I think it's a important field for determining the filesystem.
However, in this patch, I gave up checking this field.
Because there is no similar check in FATFS.
Do you know why Linux FATFS does not check this filed?
And, what do you think of checking this field?

BR


Re: [PATCH 4/4] exfat: standardize checksum calculation

2020-05-28 Thread Tetsuhiro Kohada

I'll repost the patch, based on the dir-cache patched dev-tree.
If dir-cache patch will merge into dev-tree, should I wait until then?

I will apply them after testing at once if you send updated 5 patches again.


I resend patches for boot_sector.
However, the dir-cache patch hasn't changed, so I haven't reposted it.

BR


[PATCH 4/4 v2] exfat: standardize checksum calculation

2020-05-28 Thread Tetsuhiro Kohada
To clarify that it is a 16-bit checksum, the parts related to the 16-bit
checksum are renamed and change type to u16.
Furthermore, replace checksum calculation in exfat_load_upcase_table()
with exfat_calc_checksum32().

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied

 fs/exfat/dir.c  | 12 ++--
 fs/exfat/exfat_fs.h |  5 ++---
 fs/exfat/misc.c | 10 --
 fs/exfat/nls.c  | 19 +++
 4 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 2902d285bf20..de43534aa299 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -491,7 +491,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
int ret = 0;
int i, num_entries;
sector_t sector;
-   unsigned short chksum;
+   u16 chksum;
struct exfat_dentry *ep, *fep;
struct buffer_head *fbh, *bh;
 
@@ -500,7 +500,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
return -EIO;
 
num_entries = fep->dentry.file.num_ext + 1;
-   chksum = exfat_calc_chksum_2byte(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+   chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
 
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, , NULL);
@@ -508,7 +508,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
ret = -EIO;
goto release_fbh;
}
-   chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
+   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
CS_DEFAULT);
brelse(bh);
}
@@ -593,8 +593,8 @@ void exfat_update_dir_chksum_with_entry_set(struct 
exfat_entry_set_cache *es)
 
for (i = 0; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
-   chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
-chksum_type);
+   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
+chksum_type);
chksum_type = CS_DEFAULT;
}
ep = exfat_get_dentry_cached(es, 0);
@@ -1000,7 +1000,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
}
 
if (entry_type == TYPE_STREAM) {
-   unsigned short name_hash;
+   u16 name_hash;
 
if (step != DIRENT_STEP_STRM) {
step = DIRENT_STEP_FILE;
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index eebbe5a84b2b..9188985694f0 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -137,7 +137,7 @@ struct exfat_dentry_namebuf {
 struct exfat_uni_name {
/* +3 for null and for converting */
unsigned short name[MAX_NAME_LENGTH + 3];
-   unsigned short name_hash;
+   u16 name_hash;
unsigned char name_len;
 };
 
@@ -512,8 +512,7 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 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_cs);
-unsigned short exfat_calc_chksum_2byte(void *data, int len,
-   unsigned short chksum, int type);
+u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
 u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index b82d2dd5bd7c..17d41f3d3709 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -136,17 +136,15 @@ void exfat_truncate_atime(struct timespec64 *ts)
ts->tv_nsec = 0;
 }
 
-unsigned short exfat_calc_chksum_2byte(void *data, int len,
-   unsigned short chksum, int type)
+u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
 {
int i;
-   unsigned char *c = (unsigned char *)data;
+   u8 *c = (u8 *)data;
 
for (i = 0; i < len; i++, c++) {
-   if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY))
+   if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3)))
continue;
-   chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) +
-   (unsigned short)*c;
+   chksum = ((chksum << 15) | (chksum >> 1)) + *c;
}
return chksum;
 }
diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
index 1ebda90cbdd7..19321773dd07

[PATCH 3/4 v2] exfat: add boot region verification

2020-05-28 Thread Tetsuhiro Kohada
Add Boot-Regions verification specified in exFAT specification.
Note that the checksum type is strongly related to the raw structure,
so the'u32 'type is used to clarify the number of bits.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied
 - just print a warning when invalid exboot-signature detected
 - print additional information when invalid boot-checksum detected

 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 14 +
 fs/exfat/super.c| 49 +
 3 files changed, 64 insertions(+)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 9673e2d31045..eebbe5a84b2b 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -514,6 +514,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type);
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
unsigned int size, unsigned char flags);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index ab7f88b1f6d3..b82d2dd5bd7c 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -151,6 +151,20 @@ unsigned short exfat_calc_chksum_2byte(void *data, int len,
return chksum;
 }
 
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
+{
+   int i;
+   u8 *c = (u8 *)data;
+
+   for (i = 0; i < len; i++, c++) {
+   if (unlikely(type == CS_BOOT_SECTOR &&
+(i == 106 || i == 107 || i == 112)))
+   continue;
+   chksum = ((chksum << 31) | (chksum >> 1)) + *c;
+   }
+   return chksum;
+}
+
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
 {
set_bit(EXFAT_SB_DIRTY, _SB(sb)->s_state);
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 95909b4d5e75..381f4394f976 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -486,6 +486,49 @@ static int exfat_read_boot_sector(struct super_block *sb)
return 0;
 }
 
+static int exfat_verify_boot_region(struct super_block *sb)
+{
+   struct buffer_head *bh = NULL;
+   u32 chksum = 0, *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 = (u32 *)>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 = (u32 *)>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)
 {
@@ -498,6 +541,12 @@ static int __exfat_fill_super(struct super_block *sb)
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_err(sb, "failed to load upcase table");
-- 
2.25.1



[PATCH 1/4 v2] exfat: redefine PBR as boot_sector

2020-05-28 Thread Tetsuhiro Kohada
Aggregate PBR related definitions and redefine as "boot_sector" to comply
with the exFAT specification.
And, rename variable names including 'pbr'.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied

 fs/exfat/exfat_fs.h  |  2 +-
 fs/exfat/exfat_raw.h | 79 +++--
 fs/exfat/super.c | 84 ++--
 3 files changed, 72 insertions(+), 93 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 5caad1380818..9673e2d31045 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -227,7 +227,7 @@ struct exfat_sb_info {
unsigned int root_dir; /* root dir cluster */
unsigned int dentries_per_clu; /* num of dentries per cluster */
unsigned int vol_flag; /* volume dirty flag */
-   struct buffer_head *pbr_bh; /* buffer_head of PBR sector */
+   struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
 
unsigned int map_clu; /* allocation bitmap start cluster */
unsigned int map_sectors; /* num of allocation bitmap sectors */
diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index 8d6c64a7546d..b373dc4e099f 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -8,7 +8,8 @@
 
 #include 
 
-#define PBR_SIGNATURE  0xAA55
+#define BOOT_SIGNATURE 0xAA55
+#define EXBOOT_SIGNATURE   0xAA55
 
 #define EXFAT_MAX_FILE_LEN 255
 
@@ -55,7 +56,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 +70,35 @@
 #define ATTR_RWMASK(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
 ATTR_SUBDIR | ATTR_ARCHIVE)
 
-#define PBR64_JUMP_BOOT_LEN3
-#define PBR64_OEM_NAME_LEN 8
-#define PBR64_RESERVED_LEN 53
+#define BOOTSEC_JUMP_BOOT_LEN  3
+#define BOOTSEC_OEM_NAME_LEN   8
+#define BOOTSEC_OLDBPB_LEN 53
 
 #define EXFAT_FILE_NAME_LEN15
 
-/* 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 {
+   __u8jmp_boot[BOOTSEC_JUMP_BOOT_LEN];
+   __u8oem_name[BOOTSEC_OEM_NAME_LEN];
+   __u8must_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;
+   __u8fs_revision[2];
+   __le16  vol_flags;
+   __u8sect_size_bits;
+   __u8sect_per_clus_bits;
+   __u8num_fats;
+   __u8drv_sel;
+   __u8percent_in_use;
+   __u8reserved[7];
+   __u8boot_code[390];
+   __le16  signature;
 } __packed;
 
 struct exfat_dentry {
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index c1f47f4071a8..e60d28e73ff0 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -49,7 +49,7 @@ static void exfat_put_super(struct super_block *sb)
sync_blockdev(sb->s_bdev);
exfat_set_vol_flags(sb, VOL_CLEAN);
exfat_free_bitmap(sbi);
-   brelse(sbi->pbr_bh);
+   brelse(sbi->boot_bh);
mutex_unlock(>s_lock);
 
call_rcu(>rcu, exfat_delayed_free);
@@ -101,7 +101,7 @@ static int exfat_statfs(struct dentry *dentry, struct 
kstatfs *buf)
 int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
 {
struct exfat_sb_info *sbi = EXFAT_SB(sb);
-   struct pbr64 *bpb = (struct pbr64 *)sbi->pbr_bh->b_data;
+   struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
bool sync;
 
/* flags are not

[PATCH 2/4 v2] exfat: separate the boot sector analysis

2020-05-28 Thread Tetsuhiro Kohada
Separate the boot sector analysis to read_boot_sector().
Furthermore, add a strict consistency check, because overlapping areas
can cause serious corruption.

Signed-off-by: Tetsuhiro Kohada 
---
Changes in v2:
 - rebase with patch 'optimize dir-cache' applied

 fs/exfat/exfat_raw.h |  1 +
 fs/exfat/super.c | 96 +++-
 2 files changed, 52 insertions(+), 45 deletions(-)

diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index b373dc4e099f..65f884785192 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -15,6 +15,7 @@
 
 #define VOL_CLEAN  0x
 #define VOL_DIRTY  0x0002
+#define ERR_MEDIUM 0x0004
 
 #define EXFAT_EOF_CLUSTER  0xu
 #define EXFAT_BAD_CLUSTER  0xFFF7u
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index e60d28e73ff0..95909b4d5e75 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -366,25 +366,20 @@ static int exfat_read_root(struct inode *inode)
return 0;
 }
 
-static struct boot_sector *exfat_read_boot_with_logical_sector(
-   struct super_block *sb)
+static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect)
 {
struct exfat_sb_info *sbi = EXFAT_SB(sb);
-   struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
-   unsigned short logical_sect = 0;
-
-   logical_sect = 1 << p_boot->sect_size_bits;
 
if (!is_power_of_2(logical_sect) ||
logical_sect < 512 || logical_sect > 4096) {
exfat_err(sb, "bogus logical sector size %u", logical_sect);
-   return NULL;
+   return -EIO;
}
 
if (logical_sect < sb->s_blocksize) {
exfat_err(sb, "logical sector size too small for device 
(logical sector size = %u)",
  logical_sect);
-   return NULL;
+   return -EIO;
}
 
if (logical_sect > sb->s_blocksize) {
@@ -394,24 +389,20 @@ static struct boot_sector 
*exfat_read_boot_with_logical_sector(
if (!sb_set_blocksize(sb, logical_sect)) {
exfat_err(sb, "unable to set blocksize %u",
  logical_sect);
-   return NULL;
+   return -EIO;
}
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 NULL;
+   return -EIO;
}
-
-   p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
}
-   return p_boot;
+   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 boot_sector *p_boot;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
@@ -424,51 +415,36 @@ static int __exfat_fill_super(struct super_block *sb)
exfat_err(sb, "unable to read boot sector");
return -EIO;
}
-
-   /* PRB is read */
p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
 
/* check the validity of BOOT */
if (le16_to_cpu((p_boot->signature)) != BOOT_SIGNATURE) {
exfat_err(sb, "invalid boot record signature");
-   ret = -EINVAL;
-   goto free_bh;
-   }
-
-
-   /* check logical sector size */
-   p_boot = exfat_read_boot_with_logical_sector(sb);
-   if (!p_boot) {
-   ret = -EIO;
-   goto free_bh;
+   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_boot->must_be_zero, 0,
-   sizeof(p_boot->must_be_zero))) {
-   ret = -EINVAL;
-   goto free_bh;
-   }
+   if (memchr_inv(p_boot->must_be_zero, 0, sizeof(p_boot->must_be_zero)))
+   return -EINVAL;
 
-   p_boot = (struct boot_sector *)p_boot;
-   if (!p_boot->num_fats) {
+   if (p_boot->num_fats != 1 && p_boot->num_fats != 2) {
exfat_err(sb, "bogus number of FAT structure");
-   ret = -EINVAL;
-   goto free_bh;
+   return -EINVAL;
}
 
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 = sbi->sect_per_clus_bits + sb->s_blocksize_bits;
+   sbi->cl

Re: [PATCH 4/4] exfat: standardize checksum calculation

2020-05-27 Thread Tetsuhiro Kohada

II tried applying patch to dev-tree (4c4dbb6ad8e8).
-The .patch file I sent
-mbox file downloaded from archive
But I can't reproduce the error. (Both succeed)
How do you reproduce the error?

I tried to appy your patches in the following order.
1. [PATCH] exfat: optimize dir-cache
2. [PATCH 1/4] exfat: redefine PBR as boot_sector
3. [PATCH 2/4] exfat: separate the boot sector analysis
4. [PATCH 3/4] exfat: add boot region verification
5. [PATCH 4/4] exfat: standardize checksum calculation


I was able to reproduce it.

The dir-cache patch was created based on the HEAD of dev-tree.
The 4 patches for boot_sector were also created based on the HEAD of dev-tree.
(at physically separated place)

I'm sorry I didn't check any conflicts with these patches.

I'll repost the patch, based on the dir-cache patched dev-tree.
If dir-cache patch will merge into dev-tree, should I wait until then?

BR


Re: [PATCH] exfat: optimize dir-cache

2020-05-27 Thread Tetsuhiro Kohada

  > In order to prevent illegal accesses to bh and dentries, it would
be better to check validation for num and bh.

  There is no new error checking for same reason as above.

  I'll try to add error checking to this v2 patch.
  Or is it better to add error checking in another patch?

The latter:)
Thanks!


Yes, the latter looks better.


I will do so.

I will post additional patches for error checking, after this patch is merged 
into tree.
OK?





Re: [PATCH 4/4] exfat: standardize checksum calculation

2020-05-27 Thread Tetsuhiro Kohada

Thank you for your comment.


I can not apply this patch to exfat dev tree. Could you please check it ?
patching file fs/exfat/dir.c
Hunk #1 succeeded at 491 (offset -5 lines).
Hunk #2 succeeded at 500 (offset -5 lines).
Hunk #3 succeeded at 508 (offset -5 lines).
Hunk #4 FAILED at 600.
Hunk #5 succeeded at 1000 (offset -47 lines).
1 out of 5 hunks FAILED -- saving rejects to file fs/exfat/dir.c.rej
patching file fs/exfat/exfat_fs.h
Hunk #1 succeeded at 137 (offset -2 lines).
Hunk #2 succeeded at 512 (offset -3 lines).
patching file fs/exfat/misc.c
patching file fs/exfat/nls.c


II tried applying patch to dev-tree (4c4dbb6ad8e8).
-The .patch file I sent
-mbox file downloaded from archive
But I can't reproduce the error. (Both succeed)
How do you reproduce the error?

BR


[PATCH 3/4] exfat: add boot region verification

2020-05-25 Thread Tetsuhiro Kohada
Add Boot-Regions verification specified in exFAT specification.
Note that the checksum type is strongly related to the raw structure,
so the'u32 'type is used to clarify the number of bits.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/misc.c | 14 +
 fs/exfat/super.c| 50 +
 3 files changed, 65 insertions(+)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index b0e5b9afc56c..15817281b3c8 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -517,6 +517,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
unsigned short chksum, int type);
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
unsigned int size, unsigned char flags);
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index ab7f88b1f6d3..b82d2dd5bd7c 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -151,6 +151,20 @@ unsigned short exfat_calc_chksum_2byte(void *data, int len,
return chksum;
 }
 
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
+{
+   int i;
+   u8 *c = (u8 *)data;
+
+   for (i = 0; i < len; i++, c++) {
+   if (unlikely(type == CS_BOOT_SECTOR &&
+(i == 106 || i == 107 || i == 112)))
+   continue;
+   chksum = ((chksum << 31) | (chksum >> 1)) + *c;
+   }
+   return chksum;
+}
+
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
 {
set_bit(EXFAT_SB_DIRTY, _SB(sb)->s_state);
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 95909b4d5e75..42b3bd3df020 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -486,6 +486,50 @@ static int exfat_read_boot_sector(struct super_block *sb)
return 0;
 }
 
+static int exfat_verify_boot_region(struct super_block *sb)
+{
+   struct buffer_head *bh = NULL;
+   u32 chksum = 0, *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 = (u32 *)>b_data[sb->s_blocksize - 4];
+   if (le32_to_cpu(*p_sig) != EXBOOT_SIGNATURE) {
+   exfat_err(sb, "no exboot-signature");
+   brelse(bh);
+   return -EINVAL;
+   }
+   }
+
+   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 = (u32 *)>b_data[i];
+   if (le32_to_cpu(*p_chksum) != chksum) {
+   exfat_err(sb, "mismatch checksum");
+   brelse(bh);
+   return -EINVAL;
+   }
+   }
+   brelse(bh);
+   return 0;
+}
+
 /* mount the file system volume */
 static int __exfat_fill_super(struct super_block *sb)
 {
@@ -498,6 +542,12 @@ static int __exfat_fill_super(struct super_block *sb)
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_err(sb, "failed to load upcase table");
-- 
2.25.1



[PATCH 2/4] exfat: separate the boot sector analysis

2020-05-25 Thread Tetsuhiro Kohada
Separate the boot sector analysis to read_boot_sector().
Furthermore, add a strict consistency check, because overlapping areas
can cause serious corruption.

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/exfat_raw.h |  1 +
 fs/exfat/super.c | 96 +++-
 2 files changed, 52 insertions(+), 45 deletions(-)

diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index b373dc4e099f..65f884785192 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -15,6 +15,7 @@
 
 #define VOL_CLEAN  0x
 #define VOL_DIRTY  0x0002
+#define ERR_MEDIUM 0x0004
 
 #define EXFAT_EOF_CLUSTER  0xu
 #define EXFAT_BAD_CLUSTER  0xFFF7u
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index e60d28e73ff0..95909b4d5e75 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -366,25 +366,20 @@ static int exfat_read_root(struct inode *inode)
return 0;
 }
 
-static struct boot_sector *exfat_read_boot_with_logical_sector(
-   struct super_block *sb)
+static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect)
 {
struct exfat_sb_info *sbi = EXFAT_SB(sb);
-   struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
-   unsigned short logical_sect = 0;
-
-   logical_sect = 1 << p_boot->sect_size_bits;
 
if (!is_power_of_2(logical_sect) ||
logical_sect < 512 || logical_sect > 4096) {
exfat_err(sb, "bogus logical sector size %u", logical_sect);
-   return NULL;
+   return -EIO;
}
 
if (logical_sect < sb->s_blocksize) {
exfat_err(sb, "logical sector size too small for device 
(logical sector size = %u)",
  logical_sect);
-   return NULL;
+   return -EIO;
}
 
if (logical_sect > sb->s_blocksize) {
@@ -394,24 +389,20 @@ static struct boot_sector 
*exfat_read_boot_with_logical_sector(
if (!sb_set_blocksize(sb, logical_sect)) {
exfat_err(sb, "unable to set blocksize %u",
  logical_sect);
-   return NULL;
+   return -EIO;
}
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 NULL;
+   return -EIO;
}
-
-   p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
}
-   return p_boot;
+   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 boot_sector *p_boot;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
@@ -424,51 +415,36 @@ static int __exfat_fill_super(struct super_block *sb)
exfat_err(sb, "unable to read boot sector");
return -EIO;
}
-
-   /* PRB is read */
p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
 
/* check the validity of BOOT */
if (le16_to_cpu((p_boot->signature)) != BOOT_SIGNATURE) {
exfat_err(sb, "invalid boot record signature");
-   ret = -EINVAL;
-   goto free_bh;
-   }
-
-
-   /* check logical sector size */
-   p_boot = exfat_read_boot_with_logical_sector(sb);
-   if (!p_boot) {
-   ret = -EIO;
-   goto free_bh;
+   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_boot->must_be_zero, 0,
-   sizeof(p_boot->must_be_zero))) {
-   ret = -EINVAL;
-   goto free_bh;
-   }
+   if (memchr_inv(p_boot->must_be_zero, 0, sizeof(p_boot->must_be_zero)))
+   return -EINVAL;
 
-   p_boot = (struct boot_sector *)p_boot;
-   if (!p_boot->num_fats) {
+   if (p_boot->num_fats != 1 && p_boot->num_fats != 2) {
exfat_err(sb, "bogus number of FAT structure");
-   ret = -EINVAL;
-   goto free_bh;
+   return -EINVAL;
}
 
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 = sbi->sect_per_clus_bits + sb->s_blocksize_bits;
+   sbi->cluster_size_bits = p_boot->sect_per_clus_bits +
+   

[PATCH 4/4] exfat: standardize checksum calculation

2020-05-25 Thread Tetsuhiro Kohada
To clarify that it is a 16-bit checksum, the parts related to the 16-bit
checksum are renamed and change type to u16.
Furthermore, replace checksum calculation in exfat_load_upcase_table()
with exfat_calc_checksum32().

Signed-off-by: Tetsuhiro Kohada 
---
 fs/exfat/dir.c  | 12 ++--
 fs/exfat/exfat_fs.h |  5 ++---
 fs/exfat/misc.c | 10 --
 fs/exfat/nls.c  | 19 +++
 4 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index b5a237c33d50..b673362a895c 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -496,7 +496,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
int ret = 0;
int i, num_entries;
sector_t sector;
-   unsigned short chksum;
+   u16 chksum;
struct exfat_dentry *ep, *fep;
struct buffer_head *fbh, *bh;
 
@@ -505,7 +505,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
return -EIO;
 
num_entries = fep->dentry.file.num_ext + 1;
-   chksum = exfat_calc_chksum_2byte(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+   chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
 
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, , NULL);
@@ -513,7 +513,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct 
exfat_chain *p_dir,
ret = -EIO;
goto release_fbh;
}
-   chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
+   chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
CS_DEFAULT);
brelse(bh);
}
@@ -600,10 +600,10 @@ int exfat_update_dir_chksum_with_entry_set(struct 
super_block *sb,
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;
-   unsigned short chksum = 0;
+   u16 chksum = 0;
 
for (i = 0; i < num_entries; i++) {
-   chksum = exfat_calc_chksum_2byte(>entries[i], DENTRY_SIZE,
+   chksum = exfat_calc_chksum16(>entries[i], DENTRY_SIZE,
chksum, chksum_type);
chksum_type = CS_DEFAULT;
}
@@ -1047,7 +1047,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct 
exfat_inode_info *ei,
}
 
if (entry_type == TYPE_STREAM) {
-   unsigned short name_hash;
+   u16 name_hash;
 
if (step != DIRENT_STEP_STRM) {
step = DIRENT_STEP_FILE;
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 15817281b3c8..993d13bbebec 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -139,7 +139,7 @@ struct exfat_dentry_namebuf {
 struct exfat_uni_name {
/* +3 for null and for converting */
unsigned short name[MAX_NAME_LENGTH + 3];
-   unsigned short name_hash;
+   u16 name_hash;
unsigned char name_len;
 };
 
@@ -515,8 +515,7 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct 
timespec64 *ts,
 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_cs);
-unsigned short exfat_calc_chksum_2byte(void *data, int len,
-   unsigned short chksum, int type);
+u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
 u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c
index b82d2dd5bd7c..17d41f3d3709 100644
--- a/fs/exfat/misc.c
+++ b/fs/exfat/misc.c
@@ -136,17 +136,15 @@ void exfat_truncate_atime(struct timespec64 *ts)
ts->tv_nsec = 0;
 }
 
-unsigned short exfat_calc_chksum_2byte(void *data, int len,
-   unsigned short chksum, int type)
+u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
 {
int i;
-   unsigned char *c = (unsigned char *)data;
+   u8 *c = (u8 *)data;
 
for (i = 0; i < len; i++, c++) {
-   if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY))
+   if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3)))
continue;
-   chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) +
-   (unsigned short)*c;
+   chksum = ((chksum << 15) | (chksum >> 1)) + *c;
}
return chksum;
 }
diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
index 1ebda90cbdd7..193

  1   2   >