Description for this pull request:
- Handle vendor extension and allocation entries as unrecognized benign secondary entries. - Fix wrong ->i_blocks on devices with non-512 byte sector. - Add the check to avoid returning -EIO from exfat_readdir() at current position exceeding the directory size. - Fix a bug that reach the end of the directory stream at a position not aligned with the dentry size. - Redefine DIR_DELETED as 0xFFFFFFF7, the bad cluster number. - Two cleanup fixes and fix cluster leakage in error handling. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmP/MaMWHGxpbmtpbmpl b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCI8WEADJpjyqM7eZ4FI2+16Ws7C8DaIG Kk77yXhI9c79+fSLLCcP7vneZYDPAsu4ELuFaHukT6yQ90t87RyWDJOBO/qJhibn jf89EbaO8U7L5AwU8ETsSsQ46MKi6ZdU5EUzqvcm4Tz5ZBCbMd6o+4uwMJ6VD/e/ uj+gyZi5XSNWxbEq/sj4oGugFjz7hfDapvlseE9gqHpX4lZftwt7qpGlX3WRWiGS QJpqi4C4QwP6+62LI107fWFAGF6SWmzPYUPhsWZmjeIoKTwFWOD3KPfOxlTBh73M RI8QxJHF9g5slDHjLDW0NmeXPCDTUxCkMXRFljXBtmsiejIutEpdJS24RbBpxNGX 69W6k/kg+EQRyzMBqLKAqrITZZTUSEDeneHfOLBly1sRIOhwxHa9JZ4Y/9TH9Eaj /e9KN1jbKzQDovDLaBdVJXb5zU1QC1S+D0ZEMX3BUNSR+lgnfE8jJVoQm3fXCYAo 9GqkRFeItD5vJW4zYf05yIRFS/4TpNytGCetogBbfB2XGq4X66bwOuDi9QvUidwK MKuEL7Y22cMxe7tLPeVndUF3r1FuAprrOyrzNuqQDINitEEzwq4hNwCYkL2WClmT 5ZfVWW5vMI+hn4XbzRIN20shBAPV1pXzpC4/2kbNQ64xXGAL/97JUjntVfhWgGkr TDRr0bNuYLIyBiWQaQ== =AbjH -----END PGP SIGNATURE----- Merge tag 'exfat-for-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat Pull exfat updates from Namjae Jeon: - Handle vendor extension and allocation entries as unrecognized benign secondary entries - Fix wrong ->i_blocks on devices with non-512 byte sector - Add the check to avoid returning -EIO from exfat_readdir() at current position exceeding the directory size - Fix a bug that reach the end of the directory stream at a position not aligned with the dentry size - Redefine DIR_DELETED as 0xFFFFFFF7, the bad cluster number - Two cleanup fixes and fix cluster leakage in error handling * tag 'exfat-for-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: fix the newly allocated clusters are not freed in error handling exfat: don't print error log in normal case exfat: remove unneeded code from exfat_alloc_cluster() exfat: handle unreconized benign secondary entries exfat: fix inode->i_blocks for non-512 byte sector size device exfat: redefine DIR_DELETED as the bad cluster number exfat: fix reporting fs error when reading dir beyond EOF exfat: fix unexpected EOF while reading dir
This commit is contained in:
commit
e103ecedce
|
@ -29,14 +29,15 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep,
|
|||
|
||||
}
|
||||
|
||||
static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
||||
static int exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
||||
struct exfat_chain *p_dir, int entry, unsigned short *uniname)
|
||||
{
|
||||
int i;
|
||||
int i, err;
|
||||
struct exfat_entry_set_cache es;
|
||||
|
||||
if (exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES))
|
||||
return;
|
||||
err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* First entry : file entry
|
||||
|
@ -56,12 +57,13 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
|||
}
|
||||
|
||||
exfat_put_dentry_set(&es, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read a directory entry from the opened directory */
|
||||
static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
|
||||
{
|
||||
int i, dentries_per_clu, num_ext;
|
||||
int i, dentries_per_clu, num_ext, err;
|
||||
unsigned int type, clu_offset, max_dentries;
|
||||
struct exfat_chain dir, clu;
|
||||
struct exfat_uni_name uni_name;
|
||||
|
@ -100,7 +102,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
|||
clu.dir = ei->hint_bmap.clu;
|
||||
}
|
||||
|
||||
while (clu_offset > 0) {
|
||||
while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
if (exfat_get_next_cluster(sb, &(clu.dir)))
|
||||
return -EIO;
|
||||
|
||||
|
@ -146,8 +148,12 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
|||
0);
|
||||
|
||||
*uni_name.name = 0x0;
|
||||
exfat_get_uniname_from_ext_entry(sb, &clu, i,
|
||||
err = exfat_get_uniname_from_ext_entry(sb, &clu, i,
|
||||
uni_name.name);
|
||||
if (err) {
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
exfat_utf16_to_nls(sb, &uni_name,
|
||||
dir_entry->namebuf.lfn,
|
||||
dir_entry->namebuf.lfnbuf_len);
|
||||
|
@ -234,10 +240,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
|
|||
fake_offset = 1;
|
||||
}
|
||||
|
||||
if (cpos & (DENTRY_SIZE - 1)) {
|
||||
err = -ENOENT;
|
||||
goto unlock;
|
||||
}
|
||||
cpos = round_up(cpos, DENTRY_SIZE);
|
||||
|
||||
/* name buffer should be allocated before use */
|
||||
err = exfat_alloc_namebuf(nb);
|
||||
|
@ -378,6 +381,12 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
|
|||
return TYPE_ACL;
|
||||
return TYPE_CRITICAL_SEC;
|
||||
}
|
||||
|
||||
if (ep->type == EXFAT_VENDOR_EXT)
|
||||
return TYPE_VENDOR_EXT;
|
||||
if (ep->type == EXFAT_VENDOR_ALLOC)
|
||||
return TYPE_VENDOR_ALLOC;
|
||||
|
||||
return TYPE_BENIGN_SEC;
|
||||
}
|
||||
|
||||
|
@ -521,6 +530,25 @@ release_fbh:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void exfat_free_benign_secondary_clusters(struct inode *inode,
|
||||
struct exfat_dentry *ep)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_chain dir;
|
||||
unsigned int start_clu =
|
||||
le32_to_cpu(ep->dentry.generic_secondary.start_clu);
|
||||
u64 size = le64_to_cpu(ep->dentry.generic_secondary.size);
|
||||
unsigned char flags = ep->dentry.generic_secondary.flags;
|
||||
|
||||
if (!(flags & ALLOC_POSSIBLE) || !start_clu || !size)
|
||||
return;
|
||||
|
||||
exfat_chain_set(&dir, start_clu,
|
||||
EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)),
|
||||
flags);
|
||||
exfat_free_cluster(inode, &dir);
|
||||
}
|
||||
|
||||
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
||||
int entry, int num_entries, struct exfat_uni_name *p_uniname)
|
||||
{
|
||||
|
@ -553,6 +581,9 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
|||
if (!ep)
|
||||
return -EIO;
|
||||
|
||||
if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
|
||||
exfat_free_benign_secondary_clusters(inode, ep);
|
||||
|
||||
exfat_init_name_entry(ep, uniname);
|
||||
exfat_update_bh(bh, sync);
|
||||
brelse(bh);
|
||||
|
@ -576,6 +607,9 @@ 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)
|
||||
exfat_free_benign_secondary_clusters(inode, ep);
|
||||
|
||||
exfat_set_entry_type(ep, TYPE_DELETED);
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
|
@ -744,6 +778,7 @@ enum exfat_validate_dentry_mode {
|
|||
ES_MODE_GET_STRM_ENTRY,
|
||||
ES_MODE_GET_NAME_ENTRY,
|
||||
ES_MODE_GET_CRITICAL_SEC_ENTRY,
|
||||
ES_MODE_GET_BENIGN_SEC_ENTRY,
|
||||
};
|
||||
|
||||
static bool exfat_validate_entry(unsigned int type,
|
||||
|
@ -757,36 +792,33 @@ static bool exfat_validate_entry(unsigned int type,
|
|||
if (type != TYPE_FILE && type != TYPE_DIR)
|
||||
return false;
|
||||
*mode = ES_MODE_GET_FILE_ENTRY;
|
||||
return true;
|
||||
break;
|
||||
case ES_MODE_GET_FILE_ENTRY:
|
||||
if (type != TYPE_STREAM)
|
||||
return false;
|
||||
*mode = ES_MODE_GET_STRM_ENTRY;
|
||||
return true;
|
||||
break;
|
||||
case ES_MODE_GET_STRM_ENTRY:
|
||||
if (type != TYPE_EXTEND)
|
||||
return false;
|
||||
*mode = ES_MODE_GET_NAME_ENTRY;
|
||||
return true;
|
||||
break;
|
||||
case ES_MODE_GET_NAME_ENTRY:
|
||||
if (type == TYPE_STREAM)
|
||||
if (type & TYPE_BENIGN_SEC)
|
||||
*mode = ES_MODE_GET_BENIGN_SEC_ENTRY;
|
||||
else if (type != TYPE_EXTEND)
|
||||
return false;
|
||||
if (type != TYPE_EXTEND) {
|
||||
if (!(type & TYPE_CRITICAL_SEC))
|
||||
return false;
|
||||
*mode = ES_MODE_GET_CRITICAL_SEC_ENTRY;
|
||||
}
|
||||
return true;
|
||||
case ES_MODE_GET_CRITICAL_SEC_ENTRY:
|
||||
if (type == TYPE_EXTEND || type == TYPE_STREAM)
|
||||
break;
|
||||
case ES_MODE_GET_BENIGN_SEC_ENTRY:
|
||||
/* Assume unreconized benign secondary entry */
|
||||
if (!(type & TYPE_BENIGN_SEC))
|
||||
return false;
|
||||
if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC)
|
||||
return false;
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct exfat_dentry *exfat_get_dentry_cached(
|
||||
|
@ -1167,10 +1199,8 @@ int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
|
|||
|
||||
type = exfat_get_entry_type(ext_ep);
|
||||
brelse(bh);
|
||||
if (type == TYPE_EXTEND || type == TYPE_STREAM)
|
||||
if (type & TYPE_CRITICAL_SEC || type & TYPE_BENIGN_SEC)
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ enum {
|
|||
#define ES_IDX_LAST_FILENAME(name_len) \
|
||||
(ES_IDX_FIRST_FILENAME + EXFAT_FILENAME_ENTRY_NUM(name_len) - 1)
|
||||
|
||||
#define DIR_DELETED 0xFFFF0321
|
||||
#define DIR_DELETED 0xFFFFFFF7
|
||||
|
||||
/* type values */
|
||||
#define TYPE_UNUSED 0x0000
|
||||
|
@ -71,6 +71,8 @@ enum {
|
|||
#define TYPE_PADDING 0x0402
|
||||
#define TYPE_ACLTAB 0x0403
|
||||
#define TYPE_BENIGN_SEC 0x0800
|
||||
#define TYPE_VENDOR_EXT 0x0801
|
||||
#define TYPE_VENDOR_ALLOC 0x0802
|
||||
|
||||
#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */
|
||||
#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
((sbi)->num_clusters - EXFAT_RESERVED_CLUSTERS)
|
||||
|
||||
/* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */
|
||||
#define ALLOC_POSSIBLE 0x01
|
||||
#define ALLOC_FAT_CHAIN 0x01
|
||||
#define ALLOC_NO_FAT_CHAIN 0x03
|
||||
|
||||
|
@ -50,6 +51,8 @@
|
|||
#define EXFAT_STREAM 0xC0 /* stream entry */
|
||||
#define EXFAT_NAME 0xC1 /* file name entry */
|
||||
#define EXFAT_ACL 0xC2 /* stream entry */
|
||||
#define EXFAT_VENDOR_EXT 0xE0 /* vendor extension entry */
|
||||
#define EXFAT_VENDOR_ALLOC 0xE1 /* vendor allocation entry */
|
||||
|
||||
#define IS_EXFAT_CRITICAL_PRI(x) (x < 0xA0)
|
||||
#define IS_EXFAT_BENIGN_PRI(x) (x < 0xC0)
|
||||
|
@ -155,6 +158,24 @@ struct exfat_dentry {
|
|||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __packed upcase; /* up-case table directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 vendor_guid[16];
|
||||
__u8 vendor_defined[14];
|
||||
} __packed vendor_ext; /* vendor extension directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 vendor_guid[16];
|
||||
__u8 vendor_defined[2];
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __packed vendor_alloc; /* vendor allocation directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 custom_defined[18];
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __packed generic_secondary; /* generic secondary directory entry */
|
||||
} __packed dentry;
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -307,7 +307,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
struct exfat_chain *p_chain, bool sync_bmap)
|
||||
{
|
||||
int ret = -ENOSPC;
|
||||
unsigned int num_clusters = 0, total_cnt;
|
||||
unsigned int total_cnt;
|
||||
unsigned int hint_clu, new_clu, last_clu = EXFAT_EOF_CLUSTER;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
@ -344,17 +344,11 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
|
||||
/* check cluster validation */
|
||||
if (!is_valid_cluster(sbi, hint_clu)) {
|
||||
exfat_err(sb, "hint_cluster is invalid (%u)",
|
||||
hint_clu);
|
||||
if (hint_clu != sbi->num_clusters)
|
||||
exfat_err(sb, "hint_cluster is invalid (%u), rewind to the first cluster",
|
||||
hint_clu);
|
||||
hint_clu = EXFAT_FIRST_CLUSTER;
|
||||
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
||||
num_clusters)) {
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
p_chain->flags = ALLOC_FAT_CHAIN;
|
||||
}
|
||||
p_chain->flags = ALLOC_FAT_CHAIN;
|
||||
}
|
||||
|
||||
p_chain->dir = EXFAT_EOF_CLUSTER;
|
||||
|
@ -364,7 +358,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
if (new_clu != hint_clu &&
|
||||
p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
||||
num_clusters)) {
|
||||
p_chain->size)) {
|
||||
ret = -EIO;
|
||||
goto free_cluster;
|
||||
}
|
||||
|
@ -377,8 +371,6 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
goto free_cluster;
|
||||
}
|
||||
|
||||
num_clusters++;
|
||||
|
||||
/* update FAT table */
|
||||
if (p_chain->flags == ALLOC_FAT_CHAIN) {
|
||||
if (exfat_ent_set(sb, new_clu, EXFAT_EOF_CLUSTER)) {
|
||||
|
@ -395,13 +387,14 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
goto free_cluster;
|
||||
}
|
||||
}
|
||||
p_chain->size++;
|
||||
|
||||
last_clu = new_clu;
|
||||
|
||||
if (--num_alloc == 0) {
|
||||
if (p_chain->size == num_alloc) {
|
||||
sbi->clu_srch_ptr = hint_clu;
|
||||
sbi->used_clusters += num_clusters;
|
||||
sbi->used_clusters += num_alloc;
|
||||
|
||||
p_chain->size += num_clusters;
|
||||
mutex_unlock(&sbi->bitmap_lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -412,7 +405,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
|
||||
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
||||
num_clusters)) {
|
||||
p_chain->size)) {
|
||||
ret = -EIO;
|
||||
goto free_cluster;
|
||||
}
|
||||
|
@ -421,8 +414,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
|||
}
|
||||
}
|
||||
free_cluster:
|
||||
if (num_clusters)
|
||||
__exfat_free_cluster(inode, p_chain);
|
||||
__exfat_free_cluster(inode, p_chain);
|
||||
unlock:
|
||||
mutex_unlock(&sbi->bitmap_lock);
|
||||
return ret;
|
||||
|
|
|
@ -209,8 +209,7 @@ void exfat_truncate(struct inode *inode)
|
|||
if (err)
|
||||
goto write_size;
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >>
|
||||
inode->i_blkbits;
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
write_size:
|
||||
aligned_size = i_size_read(inode);
|
||||
if (aligned_size & (blocksize - 1)) {
|
||||
|
|
|
@ -220,8 +220,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
|||
num_clusters += num_to_be_allocated;
|
||||
*clu = new_clu.dir;
|
||||
|
||||
inode->i_blocks +=
|
||||
num_to_be_allocated << sbi->sect_per_clus_bits;
|
||||
inode->i_blocks += EXFAT_CLU_TO_B(num_to_be_allocated, sbi) >> 9;
|
||||
|
||||
/*
|
||||
* Move *clu pointer along FAT chains (hole care) because the
|
||||
|
@ -576,8 +575,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
|||
|
||||
exfat_save_attr(inode, info->attr);
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >>
|
||||
inode->i_blkbits;
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
inode->i_mtime = info->mtime;
|
||||
inode->i_ctime = info->mtime;
|
||||
ei->i_crtime = info->crtime;
|
||||
|
|
|
@ -396,7 +396,7 @@ static int exfat_find_empty_entry(struct inode *inode,
|
|||
ei->i_size_ondisk += sbi->cluster_size;
|
||||
ei->i_size_aligned += sbi->cluster_size;
|
||||
ei->flags = p_dir->flags;
|
||||
inode->i_blocks += 1 << sbi->sect_per_clus_bits;
|
||||
inode->i_blocks += sbi->cluster_size >> 9;
|
||||
}
|
||||
|
||||
return dentry;
|
||||
|
|
|
@ -373,8 +373,7 @@ static int exfat_read_root(struct inode *inode)
|
|||
inode->i_op = &exfat_dir_inode_operations;
|
||||
inode->i_fop = &exfat_dir_operations;
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >>
|
||||
inode->i_blkbits;
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
ei->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff;
|
||||
ei->i_size_aligned = i_size_read(inode);
|
||||
ei->i_size_ondisk = i_size_read(inode);
|
||||
|
|
Loading…
Reference in New Issue