Commit e103eced authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents c0927a7a d5c514b6
Loading
Loading
Loading
Loading
+60 −30
Original line number Diff line number Diff line
@@ -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 @@ int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
	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)
			return false;
		if (type != TYPE_EXTEND) {
			if (!(type & TYPE_CRITICAL_SEC))
		if (type & TYPE_BENIGN_SEC)
			*mode = ES_MODE_GET_BENIGN_SEC_ENTRY;
		else if (type != TYPE_EXTEND)
			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)
			return false;
		if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC)
		break;
	case ES_MODE_GET_BENIGN_SEC_ENTRY:
		/* Assume unreconized benign secondary entry */
		if (!(type & TYPE_BENIGN_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;
}
+3 −1
Original line number Diff line number Diff line
@@ -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 */
+21 −0
Original line number Diff line number Diff line
@@ -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;

+12 −20
Original line number Diff line number Diff line
@@ -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,18 +344,12 @@ 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)",
		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->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,7 +414,6 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
		}
	}
free_cluster:
	if (num_clusters)
	__exfat_free_cluster(inode, p_chain);
unlock:
	mutex_unlock(&sbi->bitmap_lock);
+1 −2
Original line number Diff line number Diff line
@@ -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)) {
Loading