* [PATCH 6.6.y 2/6] exfat: remove unnecessary read entry in __exfat_rename()
2026-07-21 11:47 ` [PATCH 6.6.y 1/6] exfat: move free cluster out of exfat_init_ext_entry() Sasha Levin
@ 2026-07-21 11:47 ` Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 3/6] exfat: rename argument name for exfat_move_file and exfat_rename_file Sasha Levin
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-21 11:47 UTC (permalink / raw)
To: stable
Cc: Yuezhang Mo, Aoyama Wataru, Daniel Palmer, Sungjong Seo,
Namjae Jeon, Sasha Levin
From: Yuezhang Mo <Yuezhang.Mo@sony.com>
[ Upstream commit 30ef0e0d7ff5b6dceda19d18a85d9d72a4909784 ]
To determine whether it is a directory, there is no need to read its
directory entry, just use S_ISDIR(inode->i_mode).
Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: Aoyama Wataru <wataru.aoyama@sony.com>
Reviewed-by: Daniel Palmer <daniel.palmer@sony.com>
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Stable-dep-of: 942296784b2a ("exfat: preserve benign secondary entries during rename and move")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/exfat/namei.c | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 53fc0c26865deb..7d8c3d481f641d 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -1177,18 +1177,13 @@ static int __exfat_rename(struct inode *old_parent_inode,
int ret;
int dentry;
struct exfat_chain olddir, newdir;
- struct exfat_chain *p_dir = NULL;
struct exfat_uni_name uni_name;
- struct exfat_dentry *ep;
struct super_block *sb = old_parent_inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
const unsigned char *new_path = new_dentry->d_name.name;
struct inode *new_inode = new_dentry->d_inode;
int num_entries;
struct exfat_inode_info *new_ei = NULL;
- unsigned int new_entry_type = TYPE_UNUSED;
- int new_entry = 0;
- struct buffer_head *new_bh = NULL;
/* check the validity of pointer parameters */
if (new_path == NULL || strlen(new_path) == 0)
@@ -1214,17 +1209,8 @@ static int __exfat_rename(struct inode *old_parent_inode,
goto out;
}
- p_dir = &(new_ei->dir);
- new_entry = new_ei->entry;
- ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
- if (!ep)
- goto out;
-
- new_entry_type = exfat_get_entry_type(ep);
- brelse(new_bh);
-
/* if new_inode exists, update ei */
- if (new_entry_type == TYPE_DIR) {
+ if (S_ISDIR(new_inode->i_mode)) {
struct exfat_chain new_clu;
new_clu.dir = new_ei->start_clu;
@@ -1255,6 +1241,11 @@ static int __exfat_rename(struct inode *old_parent_inode,
&newdir, &uni_name, ei);
if (!ret && new_inode) {
+ struct exfat_chain *p_dir = &(new_ei->dir);
+ int new_entry = new_ei->entry;
+ struct exfat_dentry *ep;
+ struct buffer_head *new_bh = NULL;
+
/* delete entries of new_dir */
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
if (!ep) {
@@ -1276,7 +1267,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
}
/* Free the clusters if new_inode is a dir(as if exfat_rmdir) */
- if (new_entry_type == TYPE_DIR &&
+ if (S_ISDIR(new_inode->i_mode) &&
new_ei->start_clu != EXFAT_EOF_CLUSTER) {
/* new_ei, new_clu_to_free */
struct exfat_chain new_clu_to_free;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 6.6.y 3/6] exfat: rename argument name for exfat_move_file and exfat_rename_file
2026-07-21 11:47 ` [PATCH 6.6.y 1/6] exfat: move free cluster out of exfat_init_ext_entry() Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 2/6] exfat: remove unnecessary read entry in __exfat_rename() Sasha Levin
@ 2026-07-21 11:47 ` Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 4/6] exfat: add exfat_get_dentry_set_by_ei() helper Sasha Levin
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-21 11:47 UTC (permalink / raw)
To: stable; +Cc: Yuezhang Mo, Sungjong Seo, Namjae Jeon, Sasha Levin
From: Yuezhang Mo <Yuezhang.Mo@sony.com>
[ Upstream commit 06a2b0b3b490a6103376652c01c3ac6e8e22e654 ]
In this exfat implementation, the relationship between inode and ei
is ei=EXFAT_I(inode). However, in the arguments of exfat_move_file()
and exfat_rename_file(), argument 'inode' indicates the parent
directory, but argument 'ei' indicates the target file to be renamed.
They do not have the above relationship, which is not friendly to code
readers.
So this commit renames 'inode' to 'parent_inode', making the argument
name match its role.
Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Stable-dep-of: 942296784b2a ("exfat: preserve benign secondary entries during rename and move")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/exfat/namei.c | 36 +++++++++++++++++++-----------------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 7d8c3d481f641d..7c7fe9448475cf 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -1015,15 +1015,15 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
return err;
}
-static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
+static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_dir,
int oldentry, struct exfat_uni_name *p_uniname,
struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
struct exfat_dentry *epold, *epnew;
- struct super_block *sb = inode->i_sb;
+ struct super_block *sb = parent_inode->i_sb;
struct buffer_head *new_bh, *old_bh;
- int sync = IS_DIRSYNC(inode);
+ int sync = IS_DIRSYNC(parent_inode);
epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh);
if (!epold)
@@ -1041,8 +1041,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
if (num_old_entries < num_new_entries) {
int newentry;
- newentry =
- exfat_find_empty_entry(inode, p_dir, num_new_entries);
+ newentry = exfat_find_empty_entry(parent_inode, p_dir,
+ num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
@@ -1073,12 +1073,12 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
brelse(old_bh);
brelse(new_bh);
- ret = exfat_init_ext_entry(inode, p_dir, newentry,
+ ret = exfat_init_ext_entry(parent_inode, p_dir, newentry,
num_new_entries, p_uniname);
if (ret)
return ret;
- exfat_remove_entries(inode, p_dir, oldentry, 0,
+ exfat_remove_entries(parent_inode, p_dir, oldentry, 0,
num_old_entries);
ei->dir = *p_dir;
ei->entry = newentry;
@@ -1090,10 +1090,10 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
exfat_update_bh(old_bh, sync);
brelse(old_bh);
- exfat_remove_entries(inode, p_dir, oldentry,
+ exfat_remove_entries(parent_inode, p_dir, oldentry,
ES_IDX_FIRST_FILENAME + 1, num_old_entries);
- ret = exfat_init_ext_entry(inode, p_dir, oldentry,
+ ret = exfat_init_ext_entry(parent_inode, p_dir, oldentry,
num_new_entries, p_uniname);
if (ret)
return ret;
@@ -1101,13 +1101,13 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
return 0;
}
-static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
+static int exfat_move_file(struct inode *parent_inode, struct exfat_chain *p_olddir,
int oldentry, struct exfat_chain *p_newdir,
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, newentry, num_new_entries, num_old_entries;
struct exfat_dentry *epmov, *epnew;
- struct super_block *sb = inode->i_sb;
+ struct super_block *sb = parent_inode->i_sb;
struct buffer_head *mov_bh, *new_bh;
epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh);
@@ -1124,7 +1124,8 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
if (num_new_entries < 0)
return num_new_entries;
- newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries);
+ newentry = exfat_find_empty_entry(parent_inode, p_newdir,
+ num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
@@ -1137,7 +1138,7 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
}
- exfat_update_bh(new_bh, IS_DIRSYNC(inode));
+ exfat_update_bh(new_bh, IS_DIRSYNC(parent_inode));
brelse(mov_bh);
brelse(new_bh);
@@ -1151,16 +1152,17 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
}
*epnew = *epmov;
- exfat_update_bh(new_bh, IS_DIRSYNC(inode));
+ exfat_update_bh(new_bh, IS_DIRSYNC(parent_inode));
brelse(mov_bh);
brelse(new_bh);
- ret = exfat_init_ext_entry(inode, p_newdir, newentry, num_new_entries,
- p_uniname);
+ ret = exfat_init_ext_entry(parent_inode, p_newdir, newentry,
+ num_new_entries, p_uniname);
if (ret)
return ret;
- exfat_remove_entries(inode, p_olddir, oldentry, 0, num_old_entries);
+ exfat_remove_entries(parent_inode, p_olddir, oldentry, 0,
+ num_old_entries);
exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size,
p_newdir->flags);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 6.6.y 4/6] exfat: add exfat_get_dentry_set_by_ei() helper
2026-07-21 11:47 ` [PATCH 6.6.y 1/6] exfat: move free cluster out of exfat_init_ext_entry() Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 2/6] exfat: remove unnecessary read entry in __exfat_rename() Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 3/6] exfat: rename argument name for exfat_move_file and exfat_rename_file Sasha Levin
@ 2026-07-21 11:47 ` Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 5/6] exfat: move exfat_chain_set() out of __exfat_resolve_path() Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 6/6] exfat: preserve benign secondary entries during rename and move Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-21 11:47 UTC (permalink / raw)
To: stable
Cc: Yuezhang Mo, Aoyama Wataru, Daniel Palmer, Sungjong Seo,
Namjae Jeon, Sasha Levin
From: Yuezhang Mo <Yuezhang.Mo@sony.com>
[ Upstream commit ac844e91364a03c35838fd488437605fbe56f8c3 ]
This helper gets the directory entry set of the file for the exfat
inode which has been created.
It's used to remove all the instances of the pattern it replaces
making the code cleaner, it's also a preparation for changing ->dir
to record the cluster where the directory entry set is located and
changing ->entry to record the index of the directory entry within
the cluster.
Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: Aoyama Wataru <wataru.aoyama@sony.com>
Reviewed-by: Daniel Palmer <daniel.palmer@sony.com>
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Stable-dep-of: 942296784b2a ("exfat: preserve benign secondary entries during rename and move")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/exfat/exfat_fs.h | 2 ++
fs/exfat/inode.c | 2 +-
fs/exfat/namei.c | 82 +++++++++++++++++++--------------------------
3 files changed, 38 insertions(+), 48 deletions(-)
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 19245ddd885fc3..e154284be63b6f 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -498,6 +498,8 @@ struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir, int entry,
unsigned int type);
+#define exfat_get_dentry_set_by_ei(es, sb, ei) \
+ exfat_get_dentry_set(es, sb, &(ei)->dir, (ei)->entry, ES_ALL_ENTRIES)
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 13329baeafbcfb..81019484296de4 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -42,7 +42,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
exfat_set_volume_dirty(sb);
/* get the directory entry of given file or directory */
- if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES))
+ if (exfat_get_dentry_set_by_ei(&es, sb, ei))
return -EIO;
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 7c7fe9448475cf..f1c34d8e6ff59b 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -786,29 +786,26 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
/* remove an entry, BUT don't truncate */
static int exfat_unlink(struct inode *dir, struct dentry *dentry)
{
- struct exfat_chain cdir;
struct exfat_dentry *ep;
struct super_block *sb = dir->i_sb;
struct inode *inode = dentry->d_inode;
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
- int num_entries, entry, err = 0;
+ int num_entries, err = 0;
mutex_lock(&EXFAT_SB(sb)->s_lock);
- exfat_chain_dup(&cdir, &ei->dir);
- entry = ei->entry;
if (ei->dir.dir == DIR_DELETED) {
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
goto unlock;
}
- ep = exfat_get_dentry(sb, &cdir, entry, &bh);
+ ep = exfat_get_dentry(sb, &ei->dir, ei->entry, &bh);
if (!ep) {
err = -EIO;
goto unlock;
}
- num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep);
+ num_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry, ep);
if (num_entries < 0) {
err = -EIO;
brelse(bh);
@@ -819,7 +816,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
exfat_set_volume_dirty(sb);
/* update the directory entry */
- if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
+ if (exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries)) {
err = -EIO;
goto unlock;
}
@@ -944,18 +941,15 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct exfat_dentry *ep;
- struct exfat_chain cdir, clu_to_free;
+ struct exfat_chain clu_to_free;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
- int num_entries, entry, err;
+ int num_entries, err;
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
- exfat_chain_dup(&cdir, &ei->dir);
- entry = ei->entry;
-
if (ei->dir.dir == DIR_DELETED) {
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
@@ -973,13 +967,13 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
goto unlock;
}
- ep = exfat_get_dentry(sb, &cdir, entry, &bh);
+ ep = exfat_get_dentry(sb, &ei->dir, ei->entry, &bh);
if (!ep) {
err = -EIO;
goto unlock;
}
- num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep);
+ num_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry, ep);
if (num_entries < 0) {
err = -EIO;
brelse(bh);
@@ -989,7 +983,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
brelse(bh);
exfat_set_volume_dirty(sb);
- err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
+ err = exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries);
if (err) {
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
goto unlock;
@@ -1015,8 +1009,8 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
return err;
}
-static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_dir,
- int oldentry, struct exfat_uni_name *p_uniname,
+static int exfat_rename_file(struct inode *parent_inode,
+ struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
@@ -1025,11 +1019,12 @@ static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_d
struct buffer_head *new_bh, *old_bh;
int sync = IS_DIRSYNC(parent_inode);
- epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh);
+ epold = exfat_get_dentry(sb, &ei->dir, ei->entry, &old_bh);
if (!epold)
return -EIO;
- num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry, epold);
+ num_old_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry,
+ epold);
if (num_old_entries < 0)
return -EIO;
num_old_entries++;
@@ -1059,7 +1054,7 @@ static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_d
brelse(old_bh);
brelse(new_bh);
- epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh);
+ epold = exfat_get_dentry(sb, &ei->dir, ei->entry + 1, &old_bh);
if (!epold)
return -EIO;
epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh);
@@ -1078,7 +1073,7 @@ static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_d
if (ret)
return ret;
- exfat_remove_entries(parent_inode, p_dir, oldentry, 0,
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
num_old_entries);
ei->dir = *p_dir;
ei->entry = newentry;
@@ -1090,10 +1085,10 @@ static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_d
exfat_update_bh(old_bh, sync);
brelse(old_bh);
- exfat_remove_entries(parent_inode, p_dir, oldentry,
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry,
ES_IDX_FIRST_FILENAME + 1, num_old_entries);
- ret = exfat_init_ext_entry(parent_inode, p_dir, oldentry,
+ ret = exfat_init_ext_entry(parent_inode, &ei->dir, ei->entry,
num_new_entries, p_uniname);
if (ret)
return ret;
@@ -1101,20 +1096,20 @@ static int exfat_rename_file(struct inode *parent_inode, struct exfat_chain *p_d
return 0;
}
-static int exfat_move_file(struct inode *parent_inode, struct exfat_chain *p_olddir,
- int oldentry, struct exfat_chain *p_newdir,
- struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
+static int exfat_move_file(struct inode *parent_inode,
+ struct exfat_chain *p_newdir, struct exfat_uni_name *p_uniname,
+ struct exfat_inode_info *ei)
{
int ret, newentry, num_new_entries, num_old_entries;
struct exfat_dentry *epmov, *epnew;
struct super_block *sb = parent_inode->i_sb;
struct buffer_head *mov_bh, *new_bh;
- epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh);
+ epmov = exfat_get_dentry(sb, &ei->dir, ei->entry, &mov_bh);
if (!epmov)
return -EIO;
- num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry,
+ num_old_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry,
epmov);
if (num_old_entries < 0)
return -EIO;
@@ -1142,7 +1137,7 @@ static int exfat_move_file(struct inode *parent_inode, struct exfat_chain *p_old
brelse(mov_bh);
brelse(new_bh);
- epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh);
+ epmov = exfat_get_dentry(sb, &ei->dir, ei->entry + 1, &mov_bh);
if (!epmov)
return -EIO;
epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh);
@@ -1161,7 +1156,7 @@ static int exfat_move_file(struct inode *parent_inode, struct exfat_chain *p_old
if (ret)
return ret;
- exfat_remove_entries(parent_inode, p_olddir, oldentry, 0,
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
num_old_entries);
exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size,
@@ -1177,8 +1172,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
struct dentry *new_dentry)
{
int ret;
- int dentry;
- struct exfat_chain olddir, newdir;
+ struct exfat_chain newdir;
struct exfat_uni_name uni_name;
struct super_block *sb = old_parent_inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
@@ -1196,11 +1190,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
return -ENOENT;
}
- exfat_chain_set(&olddir, EXFAT_I(old_parent_inode)->start_clu,
- EXFAT_B_TO_CLU_ROUND_UP(i_size_read(old_parent_inode), sbi),
- EXFAT_I(old_parent_inode)->flags);
- dentry = ei->entry;
-
/* check whether new dir is existing directory and empty */
if (new_inode) {
ret = -EIO;
@@ -1235,35 +1224,34 @@ static int __exfat_rename(struct inode *old_parent_inode,
exfat_set_volume_dirty(sb);
- if (olddir.dir == newdir.dir)
- ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
+ if (new_parent_inode == old_parent_inode)
+ ret = exfat_rename_file(new_parent_inode, &newdir,
&uni_name, ei);
else
- ret = exfat_move_file(new_parent_inode, &olddir, dentry,
- &newdir, &uni_name, ei);
+ ret = exfat_move_file(new_parent_inode, &newdir,
+ &uni_name, ei);
if (!ret && new_inode) {
- struct exfat_chain *p_dir = &(new_ei->dir);
- int new_entry = new_ei->entry;
struct exfat_dentry *ep;
struct buffer_head *new_bh = NULL;
/* delete entries of new_dir */
- ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
+ ep = exfat_get_dentry(sb, &new_ei->dir, new_ei->entry, &new_bh);
if (!ep) {
ret = -EIO;
goto del_out;
}
- num_entries = exfat_count_ext_entries(sb, p_dir, new_entry, ep);
+ num_entries = exfat_count_ext_entries(sb, &new_ei->dir,
+ new_ei->entry, ep);
if (num_entries < 0) {
ret = -EIO;
goto del_out;
}
brelse(new_bh);
- if (exfat_remove_entries(new_inode, p_dir, new_entry, 0,
- num_entries + 1)) {
+ if (exfat_remove_entries(new_inode, &new_ei->dir, new_ei->entry,
+ 0, num_entries + 1)) {
ret = -EIO;
goto del_out;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 6.6.y 5/6] exfat: move exfat_chain_set() out of __exfat_resolve_path()
2026-07-21 11:47 ` [PATCH 6.6.y 1/6] exfat: move free cluster out of exfat_init_ext_entry() Sasha Levin
` (2 preceding siblings ...)
2026-07-21 11:47 ` [PATCH 6.6.y 4/6] exfat: add exfat_get_dentry_set_by_ei() helper Sasha Levin
@ 2026-07-21 11:47 ` Sasha Levin
2026-07-21 11:47 ` [PATCH 6.6.y 6/6] exfat: preserve benign secondary entries during rename and move Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-21 11:47 UTC (permalink / raw)
To: stable
Cc: Yuezhang Mo, Aoyama Wataru, Daniel Palmer, Sungjong Seo,
Namjae Jeon, Sasha Levin
From: Yuezhang Mo <Yuezhang.Mo@sony.com>
[ Upstream commit 0891c7313d87a1b6baf7162bc2f0d755ce70383f ]
__exfat_resolve_path() mixes two functions. The first one is to
resolve and check if the path is valid. The second one is to output
the cluster assigned to the directory.
The second one is only needed when need to traverse the directory
entries, and calling exfat_chain_set() so early causes p_dir to be
passed as an argument multiple times, increasing the complexity of
the code.
This commit moves the call to exfat_chain_set() before traversing
directory entries.
Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: Aoyama Wataru <wataru.aoyama@sony.com>
Reviewed-by: Daniel Palmer <daniel.palmer@sony.com>
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Stable-dep-of: 942296784b2a ("exfat: preserve benign secondary entries during rename and move")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/exfat/namei.c | 68 +++++++++++++++++++++---------------------------
1 file changed, 30 insertions(+), 38 deletions(-)
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index f1c34d8e6ff59b..f0856f7d9d5910 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -343,6 +343,9 @@ static int exfat_find_empty_entry(struct inode *inode,
ei->hint_femp.eidx = EXFAT_HINT_NONE;
}
+ exfat_chain_set(p_dir, ei->start_clu,
+ EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
+
while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir,
num_entries)) < 0) {
if (dentry == -EIO)
@@ -419,14 +422,11 @@ static int exfat_find_empty_entry(struct inode *inode,
* Zero if it was successful; otherwise nonzero.
*/
static int __exfat_resolve_path(struct inode *inode, const unsigned char *path,
- struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
- int lookup)
+ struct exfat_uni_name *p_uniname, int lookup)
{
int namelen;
int lossy = NLS_NAME_NO_LOSSY;
struct super_block *sb = inode->i_sb;
- struct exfat_sb_info *sbi = EXFAT_SB(sb);
- struct exfat_inode_info *ei = EXFAT_I(inode);
int pathlen = strlen(path);
/*
@@ -465,24 +465,19 @@ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path,
if ((lossy && !lookup) || !namelen)
return (lossy & NLS_NAME_OVERLEN) ? -ENAMETOOLONG : -EINVAL;
- exfat_chain_set(p_dir, ei->start_clu,
- EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
-
return 0;
}
static inline int exfat_resolve_path(struct inode *inode,
- const unsigned char *path, struct exfat_chain *dir,
- struct exfat_uni_name *uni)
+ const unsigned char *path, struct exfat_uni_name *uni)
{
- return __exfat_resolve_path(inode, path, dir, uni, 0);
+ return __exfat_resolve_path(inode, path, uni, 0);
}
static inline int exfat_resolve_path_for_lookup(struct inode *inode,
- const unsigned char *path, struct exfat_chain *dir,
- struct exfat_uni_name *uni)
+ const unsigned char *path, struct exfat_uni_name *uni)
{
- return __exfat_resolve_path(inode, path, dir, uni, 1);
+ return __exfat_resolve_path(inode, path, uni, 1);
}
static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info)
@@ -502,7 +497,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
int clu_size = 0;
unsigned int start_clu = EXFAT_FREE_CLUSTER;
- ret = exfat_resolve_path(inode, path, p_dir, &uniname);
+ ret = exfat_resolve_path(inode, path, &uniname);
if (ret)
goto out;
@@ -623,10 +618,13 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
return -ENOENT;
/* check the validity of directory name in the given pathname */
- ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name);
+ ret = exfat_resolve_path_for_lookup(dir, qname->name, &uni_name);
if (ret)
return ret;
+ exfat_chain_set(&cdir, ei->start_clu,
+ EXFAT_B_TO_CLU(i_size_read(dir), sbi), ei->flags);
+
/* check the validation of hint_stat and initialize it if required */
if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) {
ei->hint_stat.clu = cdir.dir;
@@ -1010,8 +1008,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
}
static int exfat_rename_file(struct inode *parent_inode,
- struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
- struct exfat_inode_info *ei)
+ struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
struct exfat_dentry *epold, *epnew;
@@ -1035,13 +1032,14 @@ static int exfat_rename_file(struct inode *parent_inode,
if (num_old_entries < num_new_entries) {
int newentry;
+ struct exfat_chain dir;
- newentry = exfat_find_empty_entry(parent_inode, p_dir,
+ newentry = exfat_find_empty_entry(parent_inode, &dir,
num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
- epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh);
+ epnew = exfat_get_dentry(sb, &dir, newentry, &new_bh);
if (!epnew)
return -EIO;
@@ -1057,7 +1055,7 @@ static int exfat_rename_file(struct inode *parent_inode,
epold = exfat_get_dentry(sb, &ei->dir, ei->entry + 1, &old_bh);
if (!epold)
return -EIO;
- epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh);
+ epnew = exfat_get_dentry(sb, &dir, newentry + 1, &new_bh);
if (!epnew) {
brelse(old_bh);
return -EIO;
@@ -1068,14 +1066,14 @@ static int exfat_rename_file(struct inode *parent_inode,
brelse(old_bh);
brelse(new_bh);
- ret = exfat_init_ext_entry(parent_inode, p_dir, newentry,
+ ret = exfat_init_ext_entry(parent_inode, &dir, newentry,
num_new_entries, p_uniname);
if (ret)
return ret;
exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
num_old_entries);
- ei->dir = *p_dir;
+ ei->dir = dir;
ei->entry = newentry;
} else {
if (exfat_get_entry_type(epold) == TYPE_FILE) {
@@ -1097,13 +1095,13 @@ static int exfat_rename_file(struct inode *parent_inode,
}
static int exfat_move_file(struct inode *parent_inode,
- struct exfat_chain *p_newdir, struct exfat_uni_name *p_uniname,
- struct exfat_inode_info *ei)
+ struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, newentry, num_new_entries, num_old_entries;
struct exfat_dentry *epmov, *epnew;
struct super_block *sb = parent_inode->i_sb;
struct buffer_head *mov_bh, *new_bh;
+ struct exfat_chain newdir;
epmov = exfat_get_dentry(sb, &ei->dir, ei->entry, &mov_bh);
if (!epmov)
@@ -1119,12 +1117,12 @@ static int exfat_move_file(struct inode *parent_inode,
if (num_new_entries < 0)
return num_new_entries;
- newentry = exfat_find_empty_entry(parent_inode, p_newdir,
+ newentry = exfat_find_empty_entry(parent_inode, &newdir,
num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
- epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh);
+ epnew = exfat_get_dentry(sb, &newdir, newentry, &new_bh);
if (!epnew)
return -EIO;
@@ -1140,7 +1138,7 @@ static int exfat_move_file(struct inode *parent_inode,
epmov = exfat_get_dentry(sb, &ei->dir, ei->entry + 1, &mov_bh);
if (!epmov)
return -EIO;
- epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh);
+ epnew = exfat_get_dentry(sb, &newdir, newentry + 1, &new_bh);
if (!epnew) {
brelse(mov_bh);
return -EIO;
@@ -1151,7 +1149,7 @@ static int exfat_move_file(struct inode *parent_inode,
brelse(mov_bh);
brelse(new_bh);
- ret = exfat_init_ext_entry(parent_inode, p_newdir, newentry,
+ ret = exfat_init_ext_entry(parent_inode, &newdir, newentry,
num_new_entries, p_uniname);
if (ret)
return ret;
@@ -1159,9 +1157,7 @@ static int exfat_move_file(struct inode *parent_inode,
exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
num_old_entries);
- exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size,
- p_newdir->flags);
-
+ ei->dir = newdir;
ei->entry = newentry;
return 0;
}
@@ -1172,7 +1168,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
struct dentry *new_dentry)
{
int ret;
- struct exfat_chain newdir;
struct exfat_uni_name uni_name;
struct super_block *sb = old_parent_inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
@@ -1217,19 +1212,16 @@ static int __exfat_rename(struct inode *old_parent_inode,
}
/* check the validity of directory name in the given new pathname */
- ret = exfat_resolve_path(new_parent_inode, new_path, &newdir,
- &uni_name);
+ ret = exfat_resolve_path(new_parent_inode, new_path, &uni_name);
if (ret)
goto out;
exfat_set_volume_dirty(sb);
if (new_parent_inode == old_parent_inode)
- ret = exfat_rename_file(new_parent_inode, &newdir,
- &uni_name, ei);
+ ret = exfat_rename_file(new_parent_inode, &uni_name, ei);
else
- ret = exfat_move_file(new_parent_inode, &newdir,
- &uni_name, ei);
+ ret = exfat_move_file(new_parent_inode, &uni_name, ei);
if (!ret && new_inode) {
struct exfat_dentry *ep;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 6.6.y 6/6] exfat: preserve benign secondary entries during rename and move
2026-07-21 11:47 ` [PATCH 6.6.y 1/6] exfat: move free cluster out of exfat_init_ext_entry() Sasha Levin
` (3 preceding siblings ...)
2026-07-21 11:47 ` [PATCH 6.6.y 5/6] exfat: move exfat_chain_set() out of __exfat_resolve_path() Sasha Levin
@ 2026-07-21 11:47 ` Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-21 11:47 UTC (permalink / raw)
To: stable; +Cc: Rochan Avlur, Yuezhang Mo, Namjae Jeon, Sasha Levin
From: Rochan Avlur <rochan.avlur@gmail.com>
[ Upstream commit 942296784b2a9439651750c42f540bf2579b330f ]
Commit 8258ef28001a ("exfat: handle unreconized benign secondary
entries") added cluster freeing for benign secondary entries inside
exfat_remove_entries(). However, exfat_remove_entries() is also called
from the rename and move paths (exfat_rename_file and exfat_move_file),
where the old entry set is being relocated rather than deleted. This
causes benign secondary entries such as vendor extension entries to be
silently destroyed on rename or cross-directory move, violating the
exFAT spec requirement (section 8.2) that implementations preserve
unrecognized benign secondary entries.
Fix this by adding a free_benign parameter to exfat_remove_entries()
so callers can suppress cluster freeing during relocation, and
extending exfat_init_ext_entry() to copy trailing benign secondary
entries from the old entry set into the new one internally. Also
clean up the error paths to delete newly allocated entries on failure.
Fixes: 8258ef28001a ("exfat: handle unreconized benign secondary entries")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/linux-fsdevel/CAG7tbBV--waov7XVu2FHQEc6paR92dufS=em9DW5Kzsrpu3iQg@mail.gmail.com/
Signed-off-by: Rochan Avlur <rochan.avlur@gmail.com>
Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/exfat/dir.c | 45 +++++++++--
fs/exfat/exfat_fs.h | 5 +-
fs/exfat/namei.c | 191 +++++++++++++++++++++++++++++---------------
3 files changed, 169 insertions(+), 72 deletions(-)
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 6c714b110f089b..2c4afa6675f55a 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -552,21 +552,43 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode,
exfat_free_cluster(inode, &dir);
}
+/*
+ * exfat_init_ext_entry - initialize extension entries of a dentry set
+ * @inode: parent directory inode
+ * @p_dir: directory the dentry set is in
+ * @entry: index of the first entry of the dentry set
+ * @num_entries: number of entries excluding benign secondary entries
+ * @p_uniname: filename to store
+ * @old_es: optional source entry set with benign secondary entries, or NULL
+ * @num_extra: number of benign secondary entries to copy from @old_es
+ *
+ * Set up the file, stream extension, and filename entries, optionally
+ * preserving @num_extra benign secondary entries from @old_es. @old_es
+ * may refer to the same dentry set that is being initialized.
+ */
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int num_entries, struct exfat_uni_name *p_uniname)
+ int entry, int num_entries, struct exfat_uni_name *p_uniname,
+ struct exfat_entry_set_cache *old_es, int num_extra)
{
struct super_block *sb = inode->i_sb;
- int i;
+ int i, src_start = 0;
unsigned short *uniname = p_uniname->name;
struct exfat_dentry *ep;
struct buffer_head *bh;
int sync = IS_DIRSYNC(inode);
+ if (WARN_ON(num_extra < 0 || (num_extra && (!old_es ||
+ old_es->num_entries < ES_IDX_FIRST_FILENAME + num_extra))))
+ num_extra = 0;
+
+ if (old_es && num_extra > 0)
+ src_start = old_es->num_entries - num_extra;
+
ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ep)
return -EIO;
- ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);
+ ep->dentry.file.num_ext = (unsigned char)(num_entries - 1 + num_extra);
exfat_update_bh(bh, sync);
brelse(bh);
@@ -579,6 +601,19 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
exfat_update_bh(bh, sync);
brelse(bh);
+ if (old_es && num_extra > 0) {
+ for (i = 0; i < num_extra; i++) {
+ ep = exfat_get_dentry(sb, p_dir,
+ entry + num_entries + i, &bh);
+ if (!ep)
+ return -EIO;
+
+ *ep = *exfat_get_dentry_cached(old_es, src_start + i);
+ exfat_update_bh(bh, sync);
+ brelse(bh);
+ }
+ }
+
for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep)
@@ -595,7 +630,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
}
int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int order, int num_entries)
+ int entry, int order, int num_entries, bool free_benign)
{
struct super_block *sb = inode->i_sb;
int i;
@@ -607,7 +642,7 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
if (!ep)
return -EIO;
- if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
+ if (free_benign && (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC))
exfat_free_benign_secondary_clusters(inode, ep);
exfat_set_entry_type(ep, TYPE_DELETED);
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index e154284be63b6f..96b1faa25aaa9a 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -480,9 +480,10 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
int entry, unsigned int type, unsigned int start_clu,
unsigned long long size);
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int num_entries, struct exfat_uni_name *p_uniname);
+ int entry, int num_entries, struct exfat_uni_name *p_uniname,
+ struct exfat_entry_set_cache *old_es, int num_extra);
int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int order, int num_entries);
+ int entry, int order, int num_entries, bool free_benign);
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);
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index f0856f7d9d5910..83a146d3c684ce 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -531,7 +531,8 @@ static int exfat_add_entry(struct inode *inode, const char *path,
if (ret)
goto out;
- ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname);
+ ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname,
+ NULL, 0);
if (ret)
goto out;
@@ -814,7 +815,8 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
exfat_set_volume_dirty(sb);
/* update the directory entry */
- if (exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries)) {
+ if (exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries,
+ true)) {
err = -EIO;
goto unlock;
}
@@ -981,7 +983,8 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
brelse(bh);
exfat_set_volume_dirty(sb);
- err = exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries);
+ err = exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries,
+ true);
if (err) {
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
goto unlock;
@@ -1007,41 +1010,67 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
return err;
}
+/*
+ * Count benign secondary entries beyond the filename entries.
+ * Returns the count, or -EIO if the entry set is inconsistent.
+ */
+static int exfat_count_extra_entries(struct exfat_entry_set_cache *es)
+{
+ struct exfat_dentry *stream;
+ unsigned int name_entries;
+ int extra;
+
+ stream = exfat_get_dentry_cached(es, ES_IDX_STREAM);
+ name_entries = EXFAT_FILENAME_ENTRY_NUM(stream->dentry.stream.name_len);
+ extra = es->num_entries - (ES_IDX_FIRST_FILENAME + name_entries);
+
+ return extra >= 0 ? extra : -EIO;
+}
+
static int exfat_rename_file(struct inode *parent_inode,
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
struct exfat_dentry *epold, *epnew;
struct super_block *sb = parent_inode->i_sb;
+ struct exfat_entry_set_cache old_es;
struct buffer_head *new_bh, *old_bh;
int sync = IS_DIRSYNC(parent_inode);
-
- epold = exfat_get_dentry(sb, &ei->dir, ei->entry, &old_bh);
- if (!epold)
- return -EIO;
-
- num_old_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry,
- epold);
- if (num_old_entries < 0)
- return -EIO;
- num_old_entries++;
+ unsigned int num_extra_entries, num_total_entries;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;
- if (num_old_entries < num_new_entries) {
+ ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei);
+ if (ret)
+ return -EIO;
+
+ num_old_entries = old_es.num_entries;
+
+ ret = exfat_count_extra_entries(&old_es);
+ if (ret < 0)
+ goto put_old_es;
+ num_extra_entries = ret;
+ num_total_entries = num_new_entries + num_extra_entries;
+
+ if (num_old_entries < num_total_entries) {
int newentry;
struct exfat_chain dir;
newentry = exfat_find_empty_entry(parent_inode, &dir,
- num_new_entries);
- if (newentry < 0)
- return newentry; /* -EIO or -ENOSPC */
+ num_total_entries);
+ if (newentry < 0) {
+ ret = newentry; /* -EIO or -ENOSPC */
+ goto put_old_es;
+ }
+ epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE);
epnew = exfat_get_dentry(sb, &dir, newentry, &new_bh);
- if (!epnew)
- return -EIO;
+ if (!epnew) {
+ ret = -EIO;
+ goto put_old_es;
+ }
*epnew = *epold;
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
@@ -1049,33 +1078,43 @@ static int exfat_rename_file(struct inode *parent_inode,
ei->attr |= ATTR_ARCHIVE;
}
exfat_update_bh(new_bh, sync);
- brelse(old_bh);
brelse(new_bh);
- epold = exfat_get_dentry(sb, &ei->dir, ei->entry + 1, &old_bh);
- if (!epold)
- return -EIO;
+ epold = exfat_get_dentry_cached(&old_es, ES_IDX_STREAM);
epnew = exfat_get_dentry(sb, &dir, newentry + 1, &new_bh);
if (!epnew) {
- brelse(old_bh);
- return -EIO;
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &dir, newentry, 0,
+ num_total_entries, false);
+ ret = -EIO;
+ goto put_old_es;
}
*epnew = *epold;
exfat_update_bh(new_bh, sync);
- brelse(old_bh);
brelse(new_bh);
ret = exfat_init_ext_entry(parent_inode, &dir, newentry,
- num_new_entries, p_uniname);
- if (ret)
- return ret;
+ num_new_entries, p_uniname, &old_es,
+ num_extra_entries);
+ if (ret) {
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &dir, newentry, 0,
+ num_total_entries, false);
+ goto put_old_es;
+ }
exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
- num_old_entries);
+ num_old_entries, false);
ei->dir = dir;
ei->entry = newentry;
} else {
+ epold = exfat_get_dentry(sb, &ei->dir, ei->entry, &old_bh);
+ if (!epold) {
+ ret = -EIO;
+ goto put_old_es;
+ }
+
if (exfat_get_entry_type(epold) == TYPE_FILE) {
epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
@@ -1083,15 +1122,21 @@ static int exfat_rename_file(struct inode *parent_inode,
exfat_update_bh(old_bh, sync);
brelse(old_bh);
- exfat_remove_entries(parent_inode, &ei->dir, ei->entry,
- ES_IDX_FIRST_FILENAME + 1, num_old_entries);
-
ret = exfat_init_ext_entry(parent_inode, &ei->dir, ei->entry,
- num_new_entries, p_uniname);
+ num_new_entries, p_uniname, &old_es,
+ num_extra_entries);
if (ret)
- return ret;
+ goto put_old_es;
+
+ /* Mark excess old entries as deleted (in-place shrink) */
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry,
+ num_total_entries, num_old_entries, false);
}
- return 0;
+ ret = 0;
+
+put_old_es:
+ exfat_put_dentry_set(&old_es, false);
+ return ret;
}
static int exfat_move_file(struct inode *parent_inode,
@@ -1100,31 +1145,40 @@ static int exfat_move_file(struct inode *parent_inode,
int ret, newentry, num_new_entries, num_old_entries;
struct exfat_dentry *epmov, *epnew;
struct super_block *sb = parent_inode->i_sb;
- struct buffer_head *mov_bh, *new_bh;
+ struct exfat_entry_set_cache mov_es;
+ struct buffer_head *new_bh;
struct exfat_chain newdir;
-
- epmov = exfat_get_dentry(sb, &ei->dir, ei->entry, &mov_bh);
- if (!epmov)
- return -EIO;
-
- num_old_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry,
- epmov);
- if (num_old_entries < 0)
- return -EIO;
- num_old_entries++;
+ unsigned int num_extra_entries, num_total_entries;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;
+ ret = exfat_get_dentry_set_by_ei(&mov_es, sb, ei);
+ if (ret)
+ return -EIO;
+
+ num_old_entries = mov_es.num_entries;
+
+ ret = exfat_count_extra_entries(&mov_es);
+ if (ret < 0)
+ goto put_mov_es;
+ num_extra_entries = ret;
+ num_total_entries = num_new_entries + num_extra_entries;
+
newentry = exfat_find_empty_entry(parent_inode, &newdir,
- num_new_entries);
- if (newentry < 0)
- return newentry; /* -EIO or -ENOSPC */
+ num_total_entries);
+ if (newentry < 0) {
+ ret = newentry; /* -EIO or -ENOSPC */
+ goto put_mov_es;
+ }
+ epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_FILE);
epnew = exfat_get_dentry(sb, &newdir, newentry, &new_bh);
- if (!epnew)
- return -EIO;
+ if (!epnew) {
+ ret = -EIO;
+ goto put_mov_es;
+ }
*epnew = *epmov;
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
@@ -1132,34 +1186,41 @@ static int exfat_move_file(struct inode *parent_inode,
ei->attr |= ATTR_ARCHIVE;
}
exfat_update_bh(new_bh, IS_DIRSYNC(parent_inode));
- brelse(mov_bh);
brelse(new_bh);
- epmov = exfat_get_dentry(sb, &ei->dir, ei->entry + 1, &mov_bh);
- if (!epmov)
- return -EIO;
+ epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_STREAM);
epnew = exfat_get_dentry(sb, &newdir, newentry + 1, &new_bh);
if (!epnew) {
- brelse(mov_bh);
- return -EIO;
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &newdir, newentry, 0,
+ num_total_entries, false);
+ ret = -EIO;
+ goto put_mov_es;
}
*epnew = *epmov;
exfat_update_bh(new_bh, IS_DIRSYNC(parent_inode));
- brelse(mov_bh);
brelse(new_bh);
ret = exfat_init_ext_entry(parent_inode, &newdir, newentry,
- num_new_entries, p_uniname);
- if (ret)
- return ret;
+ num_new_entries, p_uniname, &mov_es, num_extra_entries);
+ if (ret) {
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &newdir, newentry, 0,
+ num_total_entries, false);
+ goto put_mov_es;
+ }
exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
- num_old_entries);
+ num_old_entries, false);
ei->dir = newdir;
ei->entry = newentry;
- return 0;
+ ret = 0;
+
+put_mov_es:
+ exfat_put_dentry_set(&mov_es, false);
+ return ret;
}
/* rename or move a old file into a new file */
@@ -1243,7 +1304,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
brelse(new_bh);
if (exfat_remove_entries(new_inode, &new_ei->dir, new_ei->entry,
- 0, num_entries + 1)) {
+ 0, num_entries + 1, true)) {
ret = -EIO;
goto del_out;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread