mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-01 10:43:43 +00:00
Description for this pull request:
- Handle it as the empty directory if the start cluster of stream entry is invalid. - Valid size of steam entry cannot be greater than data size. If valid_size is invalid, Deal with data_size. - Move Direct-IO alignment check to before extending the valid size. - Fix uninit-value issue reported by syzbot. - Optimize to find directory entry-set in write_inode, rename, unlink. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmdEMg0WHGxpbmtpbmpl b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCKdjD/9PyXz3KQk101fMWYeW0y0Tb9WB StQyRr+8p8VIakb1ktatl/TxGAco7dDMdI9ITo2DvGKpRHAeLfztWPogrsRJOmgN bqSfGSZMTkzSTIVHuPJ+203G1jpYAwKL3yVeyDfWrUv7H3iGju/eKWUB2Mm4ICDB 1nOfK/tCzDlX3tJ1QrSrUYNVZ58bh4bbBUkSMqMIZgZc8AgvTkZR0jCzZMh8nYn4 B4Se9uA+SE5flYQYhs7CTM20/EBB9sg69o0mcnzCgLNQV+3DAk6u+EsignSRPYnG qCPVIs1LG7WePOnyaQA/5iN8ZasvZ5w/Of8RXagDnj4+OpZEVOJEPPr8uvMI4RLv STvBwixj6KZquMQEfroxRTaLv6rRoaAZcZISSsIcDJ9E/oGlWJQiyY4idfDpaXEI 2vfd+qmKQASinIs/N7E4xh2ofL5dkMmRH5T7IJxhq5aZ76k/Jpc0Wnk3cEYVhib8 gLs155acllOEVxpkZYh9BHfI7+KrvPnYQ5eO0a+QjHK3aVkWPvQVZ2mRGI5mcsOy m+Ku5/1juAtpr4wSZOytGEl1EjDM1GZ3UKB+HcwOsDxuo6g/E5bHVWnM79D0ada2 DIxOKXDrVNWPI5LDVMAL8NaS6jjj1GgmmUqBf28RbQ2qcbTuQ/Gdn1YxNq5kER0z dEq3Csk6ht0KAwN+AQ== =1uEv -----END PGP SIGNATURE----- Merge tag 'exfat-for-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat Pull exfat updates from Namjae Jeon: - If the start cluster of stream entry is invalid, treat it as the empty directory - Valid size of steam entry cannot be greater than data size. If valid_size is invalid, use data_size - Move Direct-IO alignment check to before extending the valid size - Fix uninit-value issue reported by syzbot - Optimize finding directory entry-set in write_inode, rename, unlink * tag 'exfat-for-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: reduce FAT chain traversal exfat: code cleanup for exfat_readdir() exfat: remove argument 'p_dir' from exfat_add_entry() exfat: move exfat_chain_set() out of __exfat_resolve_path() exfat: add exfat_get_dentry_set_by_ei() helper exfat: rename argument name for exfat_move_file and exfat_rename_file exfat: remove unnecessary read entry in __exfat_rename() exfat: fix file being changed by unaligned direct write exfat: fix uninit-value in __exfat_get_dentry_set exfat: fix out-of-bounds access of directory entries
This commit is contained in:
commit
8170a99c0b
@ -82,11 +82,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
||||
if (ei->type != TYPE_DIR)
|
||||
return -EPERM;
|
||||
|
||||
if (ei->entry == -1)
|
||||
exfat_chain_set(&dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
|
||||
else
|
||||
exfat_chain_set(&dir, ei->start_clu,
|
||||
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
|
||||
exfat_chain_set(&dir, ei->start_clu,
|
||||
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
|
||||
@ -135,21 +132,6 @@ 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, &dir_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, &dir_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, &dir_entry->atime,
|
||||
ep->dentry.file.access_tz,
|
||||
ep->dentry.file.access_time,
|
||||
ep->dentry.file.access_date,
|
||||
0);
|
||||
|
||||
*uni_name.name = 0x0;
|
||||
err = exfat_get_uniname_from_ext_entry(sb, &clu, i,
|
||||
@ -166,9 +148,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
||||
ep = exfat_get_dentry(sb, &clu, i + 1, &bh);
|
||||
if (!ep)
|
||||
return -EIO;
|
||||
dir_entry->size =
|
||||
le64_to_cpu(ep->dentry.stream.valid_size);
|
||||
dir_entry->entry = dentry;
|
||||
dir_entry->entry = i;
|
||||
dir_entry->dir = clu;
|
||||
brelse(bh);
|
||||
|
||||
ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi);
|
||||
@ -276,7 +257,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
|
||||
if (!nb->lfn[0])
|
||||
goto end_of_dir;
|
||||
|
||||
i_pos = ((loff_t)ei->start_clu << 32) | (de.entry & 0xffffffff);
|
||||
i_pos = ((loff_t)de.dir.dir << 32) | (de.entry & 0xffffffff);
|
||||
tmp = exfat_iget(sb, i_pos);
|
||||
if (tmp) {
|
||||
inum = tmp->i_ino;
|
||||
|
@ -204,7 +204,9 @@ struct exfat_entry_set_cache {
|
||||
#define IS_DYNAMIC_ES(es) ((es)->__bh != (es)->bh)
|
||||
|
||||
struct exfat_dir_entry {
|
||||
/* the cluster where file dentry is located */
|
||||
struct exfat_chain dir;
|
||||
/* the index of file dentry in ->dir */
|
||||
int entry;
|
||||
unsigned int type;
|
||||
unsigned int start_clu;
|
||||
@ -290,7 +292,9 @@ struct exfat_sb_info {
|
||||
* EXFAT file system inode in-memory data
|
||||
*/
|
||||
struct exfat_inode_info {
|
||||
/* the cluster where file dentry is located */
|
||||
struct exfat_chain dir;
|
||||
/* the index of file dentry in ->dir */
|
||||
int entry;
|
||||
unsigned int type;
|
||||
unsigned short attr;
|
||||
@ -508,6 +512,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 num_entries);
|
||||
#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_get_empty_dentry_set(struct exfat_entry_set_cache *es,
|
||||
struct super_block *sb, struct exfat_chain *p_dir, int entry,
|
||||
unsigned int num_entries);
|
||||
|
@ -584,6 +584,16 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
if (iocb->ki_flags & IOCB_DIRECT) {
|
||||
unsigned long align = pos | iov_iter_alignment(iter);
|
||||
|
||||
if (!IS_ALIGNED(align, i_blocksize(inode)) &&
|
||||
!IS_ALIGNED(align, bdev_logical_block_size(inode->i_sb->s_bdev))) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos > valid_size) {
|
||||
ret = exfat_extend_valid_size(file, pos);
|
||||
if (ret < 0 && ret != -ENOSPC) {
|
||||
|
@ -43,7 +43,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);
|
||||
|
194
fs/exfat/namei.c
194
fs/exfat/namei.c
@ -288,8 +288,22 @@ static int exfat_check_max_dentries(struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find empty directory entry.
|
||||
* if there isn't any empty slot, expand cluster chain.
|
||||
/*
|
||||
* Find an empty directory entry set.
|
||||
*
|
||||
* If there isn't any empty slot, expand cluster chain.
|
||||
*
|
||||
* in:
|
||||
* inode: inode of the parent directory
|
||||
* num_entries: specifies how many dentries in the empty directory entry set
|
||||
*
|
||||
* out:
|
||||
* p_dir: the cluster where the empty directory entry set is located
|
||||
* es: The found empty directory entry set
|
||||
*
|
||||
* return:
|
||||
* the directory entry index in p_dir is returned on succeeds
|
||||
* -error code is returned on failure
|
||||
*/
|
||||
static int exfat_find_empty_entry(struct inode *inode,
|
||||
struct exfat_chain *p_dir, int num_entries,
|
||||
@ -311,6 +325,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, es)) < 0) {
|
||||
if (dentry == -EIO)
|
||||
@ -345,6 +362,7 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
if (ei->start_clu == EXFAT_EOF_CLUSTER) {
|
||||
ei->start_clu = clu.dir;
|
||||
p_dir->dir = clu.dir;
|
||||
hint_femp.eidx = 0;
|
||||
}
|
||||
|
||||
/* append to the FAT chain */
|
||||
@ -377,7 +395,10 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
inode->i_blocks += sbi->cluster_size >> 9;
|
||||
}
|
||||
|
||||
return dentry;
|
||||
p_dir->dir = exfat_sector_to_cluster(sbi, es->bh[0]->b_blocknr);
|
||||
p_dir->size -= dentry / sbi->dentries_per_clu;
|
||||
|
||||
return dentry & (sbi->dentries_per_clu - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -385,14 +406,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);
|
||||
|
||||
/*
|
||||
@ -431,24 +449,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)
|
||||
@ -457,8 +470,7 @@ static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info)
|
||||
}
|
||||
|
||||
static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
struct exfat_chain *p_dir, unsigned int type,
|
||||
struct exfat_dir_entry *info)
|
||||
unsigned int type, struct exfat_dir_entry *info)
|
||||
{
|
||||
int ret, dentry, num_entries;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@ -470,7 +482,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;
|
||||
|
||||
@ -481,7 +493,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
}
|
||||
|
||||
/* exfat_find_empty_entry must be called before alloc_cluster() */
|
||||
dentry = exfat_find_empty_entry(inode, p_dir, num_entries, &es);
|
||||
dentry = exfat_find_empty_entry(inode, &info->dir, num_entries, &es);
|
||||
if (dentry < 0) {
|
||||
ret = dentry; /* -EIO or -ENOSPC */
|
||||
goto out;
|
||||
@ -508,7 +520,6 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
info->dir = *p_dir;
|
||||
info->entry = dentry;
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->type = type;
|
||||
@ -541,7 +552,6 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode;
|
||||
struct exfat_chain cdir;
|
||||
struct exfat_dir_entry info;
|
||||
loff_t i_pos;
|
||||
int err;
|
||||
@ -552,8 +562,7 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_set_volume_dirty(sb);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
|
||||
&info);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, TYPE_FILE, &info);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
@ -601,10 +610,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;
|
||||
@ -618,15 +630,16 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
if (dentry < 0)
|
||||
return dentry; /* -error value */
|
||||
|
||||
info->dir = cdir;
|
||||
info->entry = dentry;
|
||||
info->num_subdirs = 0;
|
||||
|
||||
/* adjust cdir to the optimized value */
|
||||
cdir.dir = hint_opt.clu;
|
||||
if (cdir.flags & ALLOC_NO_FAT_CHAIN)
|
||||
cdir.size -= dentry / sbi->dentries_per_clu;
|
||||
dentry = hint_opt.eidx;
|
||||
|
||||
info->dir = cdir;
|
||||
info->entry = dentry;
|
||||
info->num_subdirs = 0;
|
||||
|
||||
if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES))
|
||||
return -EIO;
|
||||
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
|
||||
@ -637,14 +650,26 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
|
||||
info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size);
|
||||
info->size = le64_to_cpu(ep2->dentry.stream.size);
|
||||
|
||||
info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu);
|
||||
if (!is_valid_cluster(sbi, info->start_clu) && info->size) {
|
||||
exfat_warn(sb, "start_clu is invalid cluster(0x%x)",
|
||||
info->start_clu);
|
||||
info->size = 0;
|
||||
info->valid_size = 0;
|
||||
}
|
||||
|
||||
if (info->valid_size > info->size) {
|
||||
exfat_warn(sb, "valid_size(%lld) is greater than size(%lld)",
|
||||
info->valid_size, info->size);
|
||||
info->valid_size = info->size;
|
||||
}
|
||||
|
||||
if (info->size == 0) {
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
} else {
|
||||
} else
|
||||
info->flags = ep2->dentry.stream.flags;
|
||||
info->start_clu =
|
||||
le32_to_cpu(ep2->dentry.stream.start_clu);
|
||||
}
|
||||
|
||||
exfat_get_entry_time(sbi, &info->crtime,
|
||||
ep->dentry.file.create_tz,
|
||||
@ -766,26 +791,23 @@ 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 super_block *sb = dir->i_sb;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
struct exfat_entry_set_cache es;
|
||||
int entry, err = 0;
|
||||
int err = 0;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
err = exfat_get_dentry_set(&es, sb, &cdir, entry, ES_ALL_ENTRIES);
|
||||
err = exfat_get_dentry_set_by_ei(&es, sb, ei);
|
||||
if (err) {
|
||||
err = -EIO;
|
||||
goto unlock;
|
||||
@ -824,7 +846,6 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode;
|
||||
struct exfat_dir_entry info;
|
||||
struct exfat_chain cdir;
|
||||
loff_t i_pos;
|
||||
int err;
|
||||
loff_t size = i_size_read(dir);
|
||||
@ -834,8 +855,7 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_set_volume_dirty(sb);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
|
||||
&info);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, TYPE_DIR, &info);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
@ -915,21 +935,18 @@ static int exfat_check_dir_empty(struct super_block *sb,
|
||||
static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
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 exfat_entry_set_cache es;
|
||||
int entry, err;
|
||||
int err;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
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;
|
||||
@ -947,7 +964,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = exfat_get_dentry_set(&es, sb, &cdir, entry, ES_ALL_ENTRIES);
|
||||
err = exfat_get_dentry_set_by_ei(&es, sb, ei);
|
||||
if (err) {
|
||||
err = -EIO;
|
||||
goto unlock;
|
||||
@ -982,15 +999,14 @@ 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,
|
||||
int oldentry, struct exfat_uni_name *p_uniname,
|
||||
struct exfat_inode_info *ei)
|
||||
static int exfat_rename_file(struct inode *parent_inode,
|
||||
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
|
||||
{
|
||||
int ret, num_new_entries;
|
||||
struct exfat_dentry *epold, *epnew;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct super_block *sb = parent_inode->i_sb;
|
||||
struct exfat_entry_set_cache old_es, new_es;
|
||||
int sync = IS_DIRSYNC(inode);
|
||||
int sync = IS_DIRSYNC(parent_inode);
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
@ -999,7 +1015,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
if (num_new_entries < 0)
|
||||
return num_new_entries;
|
||||
|
||||
ret = exfat_get_dentry_set(&old_es, sb, p_dir, oldentry, ES_ALL_ENTRIES);
|
||||
ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
return ret;
|
||||
@ -1009,9 +1025,10 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
|
||||
if (old_es.num_entries < num_new_entries) {
|
||||
int newentry;
|
||||
struct exfat_chain dir;
|
||||
|
||||
newentry = exfat_find_empty_entry(inode, p_dir, num_new_entries,
|
||||
&new_es);
|
||||
newentry = exfat_find_empty_entry(parent_inode, &dir,
|
||||
num_new_entries, &new_es);
|
||||
if (newentry < 0) {
|
||||
ret = newentry; /* -EIO or -ENOSPC */
|
||||
goto put_old_es;
|
||||
@ -1034,8 +1051,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
if (ret)
|
||||
goto put_old_es;
|
||||
|
||||
exfat_remove_entries(inode, &old_es, ES_IDX_FILE);
|
||||
ei->dir = *p_dir;
|
||||
exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE);
|
||||
ei->dir = dir;
|
||||
ei->entry = newentry;
|
||||
} else {
|
||||
if (exfat_get_entry_type(epold) == TYPE_FILE) {
|
||||
@ -1043,7 +1060,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
|
||||
exfat_remove_entries(inode, &old_es, ES_IDX_FIRST_FILENAME + 1);
|
||||
exfat_remove_entries(parent_inode, &old_es, ES_IDX_FIRST_FILENAME + 1);
|
||||
exfat_init_ext_entry(&old_es, num_new_entries, p_uniname);
|
||||
}
|
||||
return exfat_put_dentry_set(&old_es, sync);
|
||||
@ -1053,26 +1070,24 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
|
||||
int oldentry, struct exfat_chain *p_newdir,
|
||||
static int exfat_move_file(struct inode *parent_inode,
|
||||
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
|
||||
{
|
||||
int ret, newentry, num_new_entries;
|
||||
struct exfat_dentry *epmov, *epnew;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_entry_set_cache mov_es, new_es;
|
||||
struct exfat_chain newdir;
|
||||
|
||||
num_new_entries = exfat_calc_num_entries(p_uniname);
|
||||
if (num_new_entries < 0)
|
||||
return num_new_entries;
|
||||
|
||||
ret = exfat_get_dentry_set(&mov_es, sb, p_olddir, oldentry,
|
||||
ES_ALL_ENTRIES);
|
||||
ret = exfat_get_dentry_set_by_ei(&mov_es, parent_inode->i_sb, ei);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
|
||||
newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries,
|
||||
&new_es);
|
||||
newentry = exfat_find_empty_entry(parent_inode, &newdir,
|
||||
num_new_entries, &new_es);
|
||||
if (newentry < 0) {
|
||||
ret = newentry; /* -EIO or -ENOSPC */
|
||||
goto put_mov_es;
|
||||
@ -1091,18 +1106,16 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
|
||||
*epnew = *epmov;
|
||||
|
||||
exfat_init_ext_entry(&new_es, num_new_entries, p_uniname);
|
||||
exfat_remove_entries(inode, &mov_es, ES_IDX_FILE);
|
||||
|
||||
exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size,
|
||||
p_newdir->flags);
|
||||
exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE);
|
||||
|
||||
ei->dir = newdir;
|
||||
ei->entry = newentry;
|
||||
|
||||
ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(inode));
|
||||
ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode));
|
||||
if (ret)
|
||||
goto put_mov_es;
|
||||
|
||||
return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(inode));
|
||||
return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode));
|
||||
|
||||
put_mov_es:
|
||||
exfat_put_dentry_set(&mov_es, false);
|
||||
@ -1116,19 +1129,12 @@ 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 *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;
|
||||
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)
|
||||
@ -1139,11 +1145,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;
|
||||
@ -1154,17 +1155,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;
|
||||
@ -1180,26 +1172,22 @@ 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 (olddir.dir == newdir.dir)
|
||||
ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
|
||||
&uni_name, ei);
|
||||
if (new_parent_inode == old_parent_inode)
|
||||
ret = exfat_rename_file(new_parent_inode, &uni_name, ei);
|
||||
else
|
||||
ret = exfat_move_file(new_parent_inode, &olddir, dentry,
|
||||
&newdir, &uni_name, ei);
|
||||
ret = exfat_move_file(new_parent_inode, &uni_name, ei);
|
||||
|
||||
if (!ret && new_inode) {
|
||||
struct exfat_entry_set_cache es;
|
||||
|
||||
/* delete entries of new_dir */
|
||||
ret = exfat_get_dentry_set(&es, sb, p_dir, new_entry,
|
||||
ES_ALL_ENTRIES);
|
||||
ret = exfat_get_dentry_set_by_ei(&es, sb, new_ei);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto del_out;
|
||||
@ -1212,7 +1200,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||
goto del_out;
|
||||
|
||||
/* 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;
|
||||
|
Loading…
Reference in New Issue
Block a user