162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/compat.h>
862306a36Sopenharmony_ci#include <linux/bio.h>
962306a36Sopenharmony_ci#include <linux/buffer_head.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "exfat_raw.h"
1262306a36Sopenharmony_ci#include "exfat_fs.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int exfat_extract_uni_name(struct exfat_dentry *ep,
1562306a36Sopenharmony_ci		unsigned short *uniname)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	int i, len = 0;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
2062306a36Sopenharmony_ci		*uniname = le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
2162306a36Sopenharmony_ci		if (*uniname == 0x0)
2262306a36Sopenharmony_ci			return len;
2362306a36Sopenharmony_ci		uniname++;
2462306a36Sopenharmony_ci		len++;
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	*uniname = 0x0;
2862306a36Sopenharmony_ci	return len;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int exfat_get_uniname_from_ext_entry(struct super_block *sb,
3362306a36Sopenharmony_ci		struct exfat_chain *p_dir, int entry, unsigned short *uniname)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int i, err;
3662306a36Sopenharmony_ci	struct exfat_entry_set_cache es;
3762306a36Sopenharmony_ci	unsigned int uni_len = 0, len;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES);
4062306a36Sopenharmony_ci	if (err)
4162306a36Sopenharmony_ci		return err;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/*
4462306a36Sopenharmony_ci	 * First entry  : file entry
4562306a36Sopenharmony_ci	 * Second entry : stream-extension entry
4662306a36Sopenharmony_ci	 * Third entry  : first file-name entry
4762306a36Sopenharmony_ci	 * So, the index of first file-name dentry should start from 2.
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	for (i = ES_IDX_FIRST_FILENAME; i < es.num_entries; i++) {
5062306a36Sopenharmony_ci		struct exfat_dentry *ep = exfat_get_dentry_cached(&es, i);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		/* end of name entry */
5362306a36Sopenharmony_ci		if (exfat_get_entry_type(ep) != TYPE_EXTEND)
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		len = exfat_extract_uni_name(ep, uniname);
5762306a36Sopenharmony_ci		uni_len += len;
5862306a36Sopenharmony_ci		if (len != EXFAT_FILE_NAME_LEN || uni_len >= MAX_NAME_LENGTH)
5962306a36Sopenharmony_ci			break;
6062306a36Sopenharmony_ci		uniname += EXFAT_FILE_NAME_LEN;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	exfat_put_dentry_set(&es, false);
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* read a directory entry from the opened directory */
6862306a36Sopenharmony_cistatic int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int i, dentries_per_clu, num_ext, err;
7162306a36Sopenharmony_ci	unsigned int type, clu_offset, max_dentries;
7262306a36Sopenharmony_ci	struct exfat_chain dir, clu;
7362306a36Sopenharmony_ci	struct exfat_uni_name uni_name;
7462306a36Sopenharmony_ci	struct exfat_dentry *ep;
7562306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
7662306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
7762306a36Sopenharmony_ci	struct exfat_inode_info *ei = EXFAT_I(inode);
7862306a36Sopenharmony_ci	unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0xFFFFFFFF;
7962306a36Sopenharmony_ci	struct buffer_head *bh;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* check if the given file ID is opened */
8262306a36Sopenharmony_ci	if (ei->type != TYPE_DIR)
8362306a36Sopenharmony_ci		return -EPERM;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (ei->entry == -1)
8662306a36Sopenharmony_ci		exfat_chain_set(&dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
8762306a36Sopenharmony_ci	else
8862306a36Sopenharmony_ci		exfat_chain_set(&dir, ei->start_clu,
8962306a36Sopenharmony_ci			EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	dentries_per_clu = sbi->dentries_per_clu;
9262306a36Sopenharmony_ci	max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
9362306a36Sopenharmony_ci				(u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi));
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
9662306a36Sopenharmony_ci	exfat_chain_dup(&clu, &dir);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (clu.flags == ALLOC_NO_FAT_CHAIN) {
9962306a36Sopenharmony_ci		clu.dir += clu_offset;
10062306a36Sopenharmony_ci		clu.size -= clu_offset;
10162306a36Sopenharmony_ci	} else {
10262306a36Sopenharmony_ci		/* hint_information */
10362306a36Sopenharmony_ci		if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER &&
10462306a36Sopenharmony_ci		    ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) {
10562306a36Sopenharmony_ci			clu_offset -= ei->hint_bmap.off;
10662306a36Sopenharmony_ci			clu.dir = ei->hint_bmap.clu;
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) {
11062306a36Sopenharmony_ci			if (exfat_get_next_cluster(sb, &(clu.dir)))
11162306a36Sopenharmony_ci				return -EIO;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci			clu_offset--;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) {
11862306a36Sopenharmony_ci		i = dentry & (dentries_per_clu - 1);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		for ( ; i < dentries_per_clu; i++, dentry++) {
12162306a36Sopenharmony_ci			ep = exfat_get_dentry(sb, &clu, i, &bh);
12262306a36Sopenharmony_ci			if (!ep)
12362306a36Sopenharmony_ci				return -EIO;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci			type = exfat_get_entry_type(ep);
12662306a36Sopenharmony_ci			if (type == TYPE_UNUSED) {
12762306a36Sopenharmony_ci				brelse(bh);
12862306a36Sopenharmony_ci				break;
12962306a36Sopenharmony_ci			}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci			if (type != TYPE_FILE && type != TYPE_DIR) {
13262306a36Sopenharmony_ci				brelse(bh);
13362306a36Sopenharmony_ci				continue;
13462306a36Sopenharmony_ci			}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci			num_ext = ep->dentry.file.num_ext;
13762306a36Sopenharmony_ci			dir_entry->attr = le16_to_cpu(ep->dentry.file.attr);
13862306a36Sopenharmony_ci			exfat_get_entry_time(sbi, &dir_entry->crtime,
13962306a36Sopenharmony_ci					ep->dentry.file.create_tz,
14062306a36Sopenharmony_ci					ep->dentry.file.create_time,
14162306a36Sopenharmony_ci					ep->dentry.file.create_date,
14262306a36Sopenharmony_ci					ep->dentry.file.create_time_cs);
14362306a36Sopenharmony_ci			exfat_get_entry_time(sbi, &dir_entry->mtime,
14462306a36Sopenharmony_ci					ep->dentry.file.modify_tz,
14562306a36Sopenharmony_ci					ep->dentry.file.modify_time,
14662306a36Sopenharmony_ci					ep->dentry.file.modify_date,
14762306a36Sopenharmony_ci					ep->dentry.file.modify_time_cs);
14862306a36Sopenharmony_ci			exfat_get_entry_time(sbi, &dir_entry->atime,
14962306a36Sopenharmony_ci					ep->dentry.file.access_tz,
15062306a36Sopenharmony_ci					ep->dentry.file.access_time,
15162306a36Sopenharmony_ci					ep->dentry.file.access_date,
15262306a36Sopenharmony_ci					0);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			*uni_name.name = 0x0;
15562306a36Sopenharmony_ci			err = exfat_get_uniname_from_ext_entry(sb, &clu, i,
15662306a36Sopenharmony_ci				uni_name.name);
15762306a36Sopenharmony_ci			if (err) {
15862306a36Sopenharmony_ci				brelse(bh);
15962306a36Sopenharmony_ci				continue;
16062306a36Sopenharmony_ci			}
16162306a36Sopenharmony_ci			exfat_utf16_to_nls(sb, &uni_name,
16262306a36Sopenharmony_ci				dir_entry->namebuf.lfn,
16362306a36Sopenharmony_ci				dir_entry->namebuf.lfnbuf_len);
16462306a36Sopenharmony_ci			brelse(bh);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			ep = exfat_get_dentry(sb, &clu, i + 1, &bh);
16762306a36Sopenharmony_ci			if (!ep)
16862306a36Sopenharmony_ci				return -EIO;
16962306a36Sopenharmony_ci			dir_entry->size =
17062306a36Sopenharmony_ci				le64_to_cpu(ep->dentry.stream.valid_size);
17162306a36Sopenharmony_ci			dir_entry->entry = dentry;
17262306a36Sopenharmony_ci			brelse(bh);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci			ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi);
17562306a36Sopenharmony_ci			ei->hint_bmap.clu = clu.dir;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			*cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext);
17862306a36Sopenharmony_ci			return 0;
17962306a36Sopenharmony_ci		}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
18262306a36Sopenharmony_ci			if (--clu.size > 0)
18362306a36Sopenharmony_ci				clu.dir++;
18462306a36Sopenharmony_ci			else
18562306a36Sopenharmony_ci				clu.dir = EXFAT_EOF_CLUSTER;
18662306a36Sopenharmony_ci		} else {
18762306a36Sopenharmony_ci			if (exfat_get_next_cluster(sb, &(clu.dir)))
18862306a36Sopenharmony_ci				return -EIO;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	dir_entry->namebuf.lfn[0] = '\0';
19362306a36Sopenharmony_ci	*cpos = EXFAT_DEN_TO_B(dentry);
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void exfat_init_namebuf(struct exfat_dentry_namebuf *nb)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	nb->lfn = NULL;
20062306a36Sopenharmony_ci	nb->lfnbuf_len = 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int exfat_alloc_namebuf(struct exfat_dentry_namebuf *nb)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	nb->lfn = __getname();
20662306a36Sopenharmony_ci	if (!nb->lfn)
20762306a36Sopenharmony_ci		return -ENOMEM;
20862306a36Sopenharmony_ci	nb->lfnbuf_len = MAX_VFSNAME_BUF_SIZE;
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void exfat_free_namebuf(struct exfat_dentry_namebuf *nb)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	if (!nb->lfn)
21562306a36Sopenharmony_ci		return;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	__putname(nb->lfn);
21862306a36Sopenharmony_ci	exfat_init_namebuf(nb);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/*
22262306a36Sopenharmony_ci * Before calling dir_emit*(), sbi->s_lock should be released
22362306a36Sopenharmony_ci * because page fault can occur in dir_emit*().
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_ci#define ITER_POS_FILLED_DOTS    (2)
22662306a36Sopenharmony_cistatic int exfat_iterate(struct file *file, struct dir_context *ctx)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
22962306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
23062306a36Sopenharmony_ci	struct inode *tmp;
23162306a36Sopenharmony_ci	struct exfat_dir_entry de;
23262306a36Sopenharmony_ci	struct exfat_dentry_namebuf *nb = &(de.namebuf);
23362306a36Sopenharmony_ci	struct exfat_inode_info *ei = EXFAT_I(inode);
23462306a36Sopenharmony_ci	unsigned long inum;
23562306a36Sopenharmony_ci	loff_t cpos, i_pos;
23662306a36Sopenharmony_ci	int err = 0, fake_offset = 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	exfat_init_namebuf(nb);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	cpos = ctx->pos;
24162306a36Sopenharmony_ci	if (!dir_emit_dots(file, ctx))
24262306a36Sopenharmony_ci		goto out;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (ctx->pos == ITER_POS_FILLED_DOTS) {
24562306a36Sopenharmony_ci		cpos = 0;
24662306a36Sopenharmony_ci		fake_offset = 1;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	cpos = round_up(cpos, DENTRY_SIZE);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* name buffer should be allocated before use */
25262306a36Sopenharmony_ci	err = exfat_alloc_namebuf(nb);
25362306a36Sopenharmony_ci	if (err)
25462306a36Sopenharmony_ci		goto out;
25562306a36Sopenharmony_ciget_new:
25662306a36Sopenharmony_ci	mutex_lock(&EXFAT_SB(sb)->s_lock);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode))
25962306a36Sopenharmony_ci		goto end_of_dir;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	err = exfat_readdir(inode, &cpos, &de);
26262306a36Sopenharmony_ci	if (err) {
26362306a36Sopenharmony_ci		/*
26462306a36Sopenharmony_ci		 * At least we tried to read a sector.
26562306a36Sopenharmony_ci		 * Move cpos to next sector position (should be aligned).
26662306a36Sopenharmony_ci		 */
26762306a36Sopenharmony_ci		if (err == -EIO) {
26862306a36Sopenharmony_ci			cpos += 1 << (sb->s_blocksize_bits);
26962306a36Sopenharmony_ci			cpos &= ~(sb->s_blocksize - 1);
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		err = -EIO;
27362306a36Sopenharmony_ci		goto end_of_dir;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (!nb->lfn[0])
27762306a36Sopenharmony_ci		goto end_of_dir;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	i_pos = ((loff_t)ei->start_clu << 32) |	(de.entry & 0xffffffff);
28062306a36Sopenharmony_ci	tmp = exfat_iget(sb, i_pos);
28162306a36Sopenharmony_ci	if (tmp) {
28262306a36Sopenharmony_ci		inum = tmp->i_ino;
28362306a36Sopenharmony_ci		iput(tmp);
28462306a36Sopenharmony_ci	} else {
28562306a36Sopenharmony_ci		inum = iunique(sb, EXFAT_ROOT_INO);
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	mutex_unlock(&EXFAT_SB(sb)->s_lock);
28962306a36Sopenharmony_ci	if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
29062306a36Sopenharmony_ci			(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
29162306a36Sopenharmony_ci		goto out;
29262306a36Sopenharmony_ci	ctx->pos = cpos;
29362306a36Sopenharmony_ci	goto get_new;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciend_of_dir:
29662306a36Sopenharmony_ci	if (!cpos && fake_offset)
29762306a36Sopenharmony_ci		cpos = ITER_POS_FILLED_DOTS;
29862306a36Sopenharmony_ci	ctx->pos = cpos;
29962306a36Sopenharmony_ci	mutex_unlock(&EXFAT_SB(sb)->s_lock);
30062306a36Sopenharmony_ciout:
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * To improve performance, free namebuf after unlock sb_lock.
30362306a36Sopenharmony_ci	 * If namebuf is not allocated, this function do nothing
30462306a36Sopenharmony_ci	 */
30562306a36Sopenharmony_ci	exfat_free_namebuf(nb);
30662306a36Sopenharmony_ci	return err;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ciWRAP_DIR_ITER(exfat_iterate) // FIXME!
31062306a36Sopenharmony_ciconst struct file_operations exfat_dir_operations = {
31162306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
31262306a36Sopenharmony_ci	.read		= generic_read_dir,
31362306a36Sopenharmony_ci	.iterate_shared	= shared_exfat_iterate,
31462306a36Sopenharmony_ci	.unlocked_ioctl = exfat_ioctl,
31562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
31662306a36Sopenharmony_ci	.compat_ioctl = exfat_compat_ioctl,
31762306a36Sopenharmony_ci#endif
31862306a36Sopenharmony_ci	.fsync		= exfat_file_fsync,
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ciint exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	exfat_chain_set(clu, EXFAT_EOF_CLUSTER, 0, ALLOC_NO_FAT_CHAIN);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode));
32862306a36Sopenharmony_ci	if (ret)
32962306a36Sopenharmony_ci		return ret;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return exfat_zeroed_cluster(inode, clu->dir);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ciint exfat_calc_num_entries(struct exfat_uni_name *p_uniname)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int len;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	len = p_uniname->name_len;
33962306a36Sopenharmony_ci	if (len == 0)
34062306a36Sopenharmony_ci		return -EINVAL;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* 1 file entry + 1 stream entry + name entries */
34362306a36Sopenharmony_ci	return ES_ENTRY_NUM(len);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ciunsigned int exfat_get_entry_type(struct exfat_dentry *ep)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	if (ep->type == EXFAT_UNUSED)
34962306a36Sopenharmony_ci		return TYPE_UNUSED;
35062306a36Sopenharmony_ci	if (IS_EXFAT_DELETED(ep->type))
35162306a36Sopenharmony_ci		return TYPE_DELETED;
35262306a36Sopenharmony_ci	if (ep->type == EXFAT_INVAL)
35362306a36Sopenharmony_ci		return TYPE_INVALID;
35462306a36Sopenharmony_ci	if (IS_EXFAT_CRITICAL_PRI(ep->type)) {
35562306a36Sopenharmony_ci		if (ep->type == EXFAT_BITMAP)
35662306a36Sopenharmony_ci			return TYPE_BITMAP;
35762306a36Sopenharmony_ci		if (ep->type == EXFAT_UPCASE)
35862306a36Sopenharmony_ci			return TYPE_UPCASE;
35962306a36Sopenharmony_ci		if (ep->type == EXFAT_VOLUME)
36062306a36Sopenharmony_ci			return TYPE_VOLUME;
36162306a36Sopenharmony_ci		if (ep->type == EXFAT_FILE) {
36262306a36Sopenharmony_ci			if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR)
36362306a36Sopenharmony_ci				return TYPE_DIR;
36462306a36Sopenharmony_ci			return TYPE_FILE;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci		return TYPE_CRITICAL_PRI;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	if (IS_EXFAT_BENIGN_PRI(ep->type)) {
36962306a36Sopenharmony_ci		if (ep->type == EXFAT_GUID)
37062306a36Sopenharmony_ci			return TYPE_GUID;
37162306a36Sopenharmony_ci		if (ep->type == EXFAT_PADDING)
37262306a36Sopenharmony_ci			return TYPE_PADDING;
37362306a36Sopenharmony_ci		if (ep->type == EXFAT_ACLTAB)
37462306a36Sopenharmony_ci			return TYPE_ACLTAB;
37562306a36Sopenharmony_ci		return TYPE_BENIGN_PRI;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	if (IS_EXFAT_CRITICAL_SEC(ep->type)) {
37862306a36Sopenharmony_ci		if (ep->type == EXFAT_STREAM)
37962306a36Sopenharmony_ci			return TYPE_STREAM;
38062306a36Sopenharmony_ci		if (ep->type == EXFAT_NAME)
38162306a36Sopenharmony_ci			return TYPE_EXTEND;
38262306a36Sopenharmony_ci		if (ep->type == EXFAT_ACL)
38362306a36Sopenharmony_ci			return TYPE_ACL;
38462306a36Sopenharmony_ci		return TYPE_CRITICAL_SEC;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (ep->type == EXFAT_VENDOR_EXT)
38862306a36Sopenharmony_ci		return TYPE_VENDOR_EXT;
38962306a36Sopenharmony_ci	if (ep->type == EXFAT_VENDOR_ALLOC)
39062306a36Sopenharmony_ci		return TYPE_VENDOR_ALLOC;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return TYPE_BENIGN_SEC;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	if (type == TYPE_UNUSED) {
39862306a36Sopenharmony_ci		ep->type = EXFAT_UNUSED;
39962306a36Sopenharmony_ci	} else if (type == TYPE_DELETED) {
40062306a36Sopenharmony_ci		ep->type &= EXFAT_DELETE;
40162306a36Sopenharmony_ci	} else if (type == TYPE_STREAM) {
40262306a36Sopenharmony_ci		ep->type = EXFAT_STREAM;
40362306a36Sopenharmony_ci	} else if (type == TYPE_EXTEND) {
40462306a36Sopenharmony_ci		ep->type = EXFAT_NAME;
40562306a36Sopenharmony_ci	} else if (type == TYPE_BITMAP) {
40662306a36Sopenharmony_ci		ep->type = EXFAT_BITMAP;
40762306a36Sopenharmony_ci	} else if (type == TYPE_UPCASE) {
40862306a36Sopenharmony_ci		ep->type = EXFAT_UPCASE;
40962306a36Sopenharmony_ci	} else if (type == TYPE_VOLUME) {
41062306a36Sopenharmony_ci		ep->type = EXFAT_VOLUME;
41162306a36Sopenharmony_ci	} else if (type == TYPE_DIR) {
41262306a36Sopenharmony_ci		ep->type = EXFAT_FILE;
41362306a36Sopenharmony_ci		ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR);
41462306a36Sopenharmony_ci	} else if (type == TYPE_FILE) {
41562306a36Sopenharmony_ci		ep->type = EXFAT_FILE;
41662306a36Sopenharmony_ci		ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE);
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void exfat_init_stream_entry(struct exfat_dentry *ep,
42162306a36Sopenharmony_ci		unsigned char flags, unsigned int start_clu,
42262306a36Sopenharmony_ci		unsigned long long size)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	exfat_set_entry_type(ep, TYPE_STREAM);
42562306a36Sopenharmony_ci	ep->dentry.stream.flags = flags;
42662306a36Sopenharmony_ci	ep->dentry.stream.start_clu = cpu_to_le32(start_clu);
42762306a36Sopenharmony_ci	ep->dentry.stream.valid_size = cpu_to_le64(size);
42862306a36Sopenharmony_ci	ep->dentry.stream.size = cpu_to_le64(size);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic void exfat_init_name_entry(struct exfat_dentry *ep,
43262306a36Sopenharmony_ci		unsigned short *uniname)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	int i;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	exfat_set_entry_type(ep, TYPE_EXTEND);
43762306a36Sopenharmony_ci	ep->dentry.name.flags = 0x0;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
44062306a36Sopenharmony_ci		if (*uniname != 0x0) {
44162306a36Sopenharmony_ci			ep->dentry.name.unicode_0_14[i] = cpu_to_le16(*uniname);
44262306a36Sopenharmony_ci			uniname++;
44362306a36Sopenharmony_ci		} else {
44462306a36Sopenharmony_ci			ep->dentry.name.unicode_0_14[i] = 0x0;
44562306a36Sopenharmony_ci		}
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ciint exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
45062306a36Sopenharmony_ci		int entry, unsigned int type, unsigned int start_clu,
45162306a36Sopenharmony_ci		unsigned long long size)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
45462306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
45562306a36Sopenharmony_ci	struct timespec64 ts = current_time(inode);
45662306a36Sopenharmony_ci	struct exfat_dentry *ep;
45762306a36Sopenharmony_ci	struct buffer_head *bh;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * We cannot use exfat_get_dentry_set here because file ep is not
46162306a36Sopenharmony_ci	 * initialized yet.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	ep = exfat_get_dentry(sb, p_dir, entry, &bh);
46462306a36Sopenharmony_ci	if (!ep)
46562306a36Sopenharmony_ci		return -EIO;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	exfat_set_entry_type(ep, type);
46862306a36Sopenharmony_ci	exfat_set_entry_time(sbi, &ts,
46962306a36Sopenharmony_ci			&ep->dentry.file.create_tz,
47062306a36Sopenharmony_ci			&ep->dentry.file.create_time,
47162306a36Sopenharmony_ci			&ep->dentry.file.create_date,
47262306a36Sopenharmony_ci			&ep->dentry.file.create_time_cs);
47362306a36Sopenharmony_ci	exfat_set_entry_time(sbi, &ts,
47462306a36Sopenharmony_ci			&ep->dentry.file.modify_tz,
47562306a36Sopenharmony_ci			&ep->dentry.file.modify_time,
47662306a36Sopenharmony_ci			&ep->dentry.file.modify_date,
47762306a36Sopenharmony_ci			&ep->dentry.file.modify_time_cs);
47862306a36Sopenharmony_ci	exfat_set_entry_time(sbi, &ts,
47962306a36Sopenharmony_ci			&ep->dentry.file.access_tz,
48062306a36Sopenharmony_ci			&ep->dentry.file.access_time,
48162306a36Sopenharmony_ci			&ep->dentry.file.access_date,
48262306a36Sopenharmony_ci			NULL);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	exfat_update_bh(bh, IS_DIRSYNC(inode));
48562306a36Sopenharmony_ci	brelse(bh);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh);
48862306a36Sopenharmony_ci	if (!ep)
48962306a36Sopenharmony_ci		return -EIO;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	exfat_init_stream_entry(ep,
49262306a36Sopenharmony_ci		(type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN,
49362306a36Sopenharmony_ci		start_clu, size);
49462306a36Sopenharmony_ci	exfat_update_bh(bh, IS_DIRSYNC(inode));
49562306a36Sopenharmony_ci	brelse(bh);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ciint exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
50162306a36Sopenharmony_ci		int entry)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
50462306a36Sopenharmony_ci	int ret = 0;
50562306a36Sopenharmony_ci	int i, num_entries;
50662306a36Sopenharmony_ci	u16 chksum;
50762306a36Sopenharmony_ci	struct exfat_dentry *ep, *fep;
50862306a36Sopenharmony_ci	struct buffer_head *fbh, *bh;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	fep = exfat_get_dentry(sb, p_dir, entry, &fbh);
51162306a36Sopenharmony_ci	if (!fep)
51262306a36Sopenharmony_ci		return -EIO;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	num_entries = fep->dentry.file.num_ext + 1;
51562306a36Sopenharmony_ci	chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	for (i = 1; i < num_entries; i++) {
51862306a36Sopenharmony_ci		ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
51962306a36Sopenharmony_ci		if (!ep) {
52062306a36Sopenharmony_ci			ret = -EIO;
52162306a36Sopenharmony_ci			goto release_fbh;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci		chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
52462306a36Sopenharmony_ci				CS_DEFAULT);
52562306a36Sopenharmony_ci		brelse(bh);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	fep->dentry.file.checksum = cpu_to_le16(chksum);
52962306a36Sopenharmony_ci	exfat_update_bh(fbh, IS_DIRSYNC(inode));
53062306a36Sopenharmony_cirelease_fbh:
53162306a36Sopenharmony_ci	brelse(fbh);
53262306a36Sopenharmony_ci	return ret;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void exfat_free_benign_secondary_clusters(struct inode *inode,
53662306a36Sopenharmony_ci		struct exfat_dentry *ep)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
53962306a36Sopenharmony_ci	struct exfat_chain dir;
54062306a36Sopenharmony_ci	unsigned int start_clu =
54162306a36Sopenharmony_ci		le32_to_cpu(ep->dentry.generic_secondary.start_clu);
54262306a36Sopenharmony_ci	u64 size = le64_to_cpu(ep->dentry.generic_secondary.size);
54362306a36Sopenharmony_ci	unsigned char flags = ep->dentry.generic_secondary.flags;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (!(flags & ALLOC_POSSIBLE) || !start_clu || !size)
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	exfat_chain_set(&dir, start_clu,
54962306a36Sopenharmony_ci			EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)),
55062306a36Sopenharmony_ci			flags);
55162306a36Sopenharmony_ci	exfat_free_cluster(inode, &dir);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ciint exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
55562306a36Sopenharmony_ci		int entry, int num_entries, struct exfat_uni_name *p_uniname)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
55862306a36Sopenharmony_ci	int i;
55962306a36Sopenharmony_ci	unsigned short *uniname = p_uniname->name;
56062306a36Sopenharmony_ci	struct exfat_dentry *ep;
56162306a36Sopenharmony_ci	struct buffer_head *bh;
56262306a36Sopenharmony_ci	int sync = IS_DIRSYNC(inode);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	ep = exfat_get_dentry(sb, p_dir, entry, &bh);
56562306a36Sopenharmony_ci	if (!ep)
56662306a36Sopenharmony_ci		return -EIO;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);
56962306a36Sopenharmony_ci	exfat_update_bh(bh, sync);
57062306a36Sopenharmony_ci	brelse(bh);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh);
57362306a36Sopenharmony_ci	if (!ep)
57462306a36Sopenharmony_ci		return -EIO;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ep->dentry.stream.name_len = p_uniname->name_len;
57762306a36Sopenharmony_ci	ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash);
57862306a36Sopenharmony_ci	exfat_update_bh(bh, sync);
57962306a36Sopenharmony_ci	brelse(bh);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
58262306a36Sopenharmony_ci		ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
58362306a36Sopenharmony_ci		if (!ep)
58462306a36Sopenharmony_ci			return -EIO;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
58762306a36Sopenharmony_ci			exfat_free_benign_secondary_clusters(inode, ep);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		exfat_init_name_entry(ep, uniname);
59062306a36Sopenharmony_ci		exfat_update_bh(bh, sync);
59162306a36Sopenharmony_ci		brelse(bh);
59262306a36Sopenharmony_ci		uniname += EXFAT_FILE_NAME_LEN;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	exfat_update_dir_chksum(inode, p_dir, entry);
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ciint exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
60062306a36Sopenharmony_ci		int entry, int order, int num_entries)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
60362306a36Sopenharmony_ci	int i;
60462306a36Sopenharmony_ci	struct exfat_dentry *ep;
60562306a36Sopenharmony_ci	struct buffer_head *bh;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	for (i = order; i < num_entries; i++) {
60862306a36Sopenharmony_ci		ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
60962306a36Sopenharmony_ci		if (!ep)
61062306a36Sopenharmony_ci			return -EIO;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
61362306a36Sopenharmony_ci			exfat_free_benign_secondary_clusters(inode, ep);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		exfat_set_entry_type(ep, TYPE_DELETED);
61662306a36Sopenharmony_ci		exfat_update_bh(bh, IS_DIRSYNC(inode));
61762306a36Sopenharmony_ci		brelse(bh);
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_civoid exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	int chksum_type = CS_DIR_ENTRY, i;
62662306a36Sopenharmony_ci	unsigned short chksum = 0;
62762306a36Sopenharmony_ci	struct exfat_dentry *ep;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	for (i = ES_IDX_FILE; i < es->num_entries; i++) {
63062306a36Sopenharmony_ci		ep = exfat_get_dentry_cached(es, i);
63162306a36Sopenharmony_ci		chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
63262306a36Sopenharmony_ci					     chksum_type);
63362306a36Sopenharmony_ci		chksum_type = CS_DEFAULT;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci	ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
63662306a36Sopenharmony_ci	ep->dentry.file.checksum = cpu_to_le16(chksum);
63762306a36Sopenharmony_ci	es->modified = true;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ciint exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	int i, err = 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (es->modified)
64562306a36Sopenharmony_ci		err = exfat_update_bhs(es->bh, es->num_bh, sync);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	for (i = 0; i < es->num_bh; i++)
64862306a36Sopenharmony_ci		if (err)
64962306a36Sopenharmony_ci			bforget(es->bh[i]);
65062306a36Sopenharmony_ci		else
65162306a36Sopenharmony_ci			brelse(es->bh[i]);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (IS_DYNAMIC_ES(es))
65462306a36Sopenharmony_ci		kfree(es->bh);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	return err;
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int exfat_walk_fat_chain(struct super_block *sb,
66062306a36Sopenharmony_ci		struct exfat_chain *p_dir, unsigned int byte_offset,
66162306a36Sopenharmony_ci		unsigned int *clu)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
66462306a36Sopenharmony_ci	unsigned int clu_offset;
66562306a36Sopenharmony_ci	unsigned int cur_clu;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	clu_offset = EXFAT_B_TO_CLU(byte_offset, sbi);
66862306a36Sopenharmony_ci	cur_clu = p_dir->dir;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (p_dir->flags == ALLOC_NO_FAT_CHAIN) {
67162306a36Sopenharmony_ci		cur_clu += clu_offset;
67262306a36Sopenharmony_ci	} else {
67362306a36Sopenharmony_ci		while (clu_offset > 0) {
67462306a36Sopenharmony_ci			if (exfat_get_next_cluster(sb, &cur_clu))
67562306a36Sopenharmony_ci				return -EIO;
67662306a36Sopenharmony_ci			if (cur_clu == EXFAT_EOF_CLUSTER) {
67762306a36Sopenharmony_ci				exfat_fs_error(sb,
67862306a36Sopenharmony_ci					"invalid dentry access beyond EOF (clu : %u, eidx : %d)",
67962306a36Sopenharmony_ci					p_dir->dir,
68062306a36Sopenharmony_ci					EXFAT_B_TO_DEN(byte_offset));
68162306a36Sopenharmony_ci				return -EIO;
68262306a36Sopenharmony_ci			}
68362306a36Sopenharmony_ci			clu_offset--;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	*clu = cur_clu;
68862306a36Sopenharmony_ci	return 0;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
69262306a36Sopenharmony_ci			       int entry, sector_t *sector, int *offset)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	int ret;
69562306a36Sopenharmony_ci	unsigned int off, clu = 0;
69662306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	off = EXFAT_DEN_TO_B(entry);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	ret = exfat_walk_fat_chain(sb, p_dir, off, &clu);
70162306a36Sopenharmony_ci	if (ret)
70262306a36Sopenharmony_ci		return ret;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* byte offset in cluster */
70562306a36Sopenharmony_ci	off = EXFAT_CLU_OFFSET(off, sbi);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* byte offset in sector    */
70862306a36Sopenharmony_ci	*offset = EXFAT_BLK_OFFSET(off, sb);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* sector offset in cluster */
71162306a36Sopenharmony_ci	*sector = EXFAT_B_TO_BLK(off, sb);
71262306a36Sopenharmony_ci	*sector += exfat_cluster_to_sector(sbi, clu);
71362306a36Sopenharmony_ci	return 0;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci#define EXFAT_MAX_RA_SIZE     (128*1024)
71762306a36Sopenharmony_cistatic int exfat_dir_readahead(struct super_block *sb, sector_t sec)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
72062306a36Sopenharmony_ci	struct buffer_head *bh;
72162306a36Sopenharmony_ci	unsigned int max_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits;
72262306a36Sopenharmony_ci	unsigned int page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits;
72362306a36Sopenharmony_ci	unsigned int adj_ra_count = max(sbi->sect_per_clus, page_ra_count);
72462306a36Sopenharmony_ci	unsigned int ra_count = min(adj_ra_count, max_ra_count);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Read-ahead is not required */
72762306a36Sopenharmony_ci	if (sbi->sect_per_clus == 1)
72862306a36Sopenharmony_ci		return 0;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (sec < sbi->data_start_sector) {
73162306a36Sopenharmony_ci		exfat_err(sb, "requested sector is invalid(sect:%llu, root:%llu)",
73262306a36Sopenharmony_ci			  (unsigned long long)sec, sbi->data_start_sector);
73362306a36Sopenharmony_ci		return -EIO;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* Not sector aligned with ra_count, resize ra_count to page size */
73762306a36Sopenharmony_ci	if ((sec - sbi->data_start_sector) & (ra_count - 1))
73862306a36Sopenharmony_ci		ra_count = page_ra_count;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	bh = sb_find_get_block(sb, sec);
74162306a36Sopenharmony_ci	if (!bh || !buffer_uptodate(bh)) {
74262306a36Sopenharmony_ci		unsigned int i;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		for (i = 0; i < ra_count; i++)
74562306a36Sopenharmony_ci			sb_breadahead(sb, (sector_t)(sec + i));
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci	brelse(bh);
74862306a36Sopenharmony_ci	return 0;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistruct exfat_dentry *exfat_get_dentry(struct super_block *sb,
75262306a36Sopenharmony_ci		struct exfat_chain *p_dir, int entry, struct buffer_head **bh)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE);
75562306a36Sopenharmony_ci	int off;
75662306a36Sopenharmony_ci	sector_t sec;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (p_dir->dir == DIR_DELETED) {
75962306a36Sopenharmony_ci		exfat_err(sb, "abnormal access to deleted dentry");
76062306a36Sopenharmony_ci		return NULL;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (exfat_find_location(sb, p_dir, entry, &sec, &off))
76462306a36Sopenharmony_ci		return NULL;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (p_dir->dir != EXFAT_FREE_CLUSTER &&
76762306a36Sopenharmony_ci			!(entry & (dentries_per_page - 1)))
76862306a36Sopenharmony_ci		exfat_dir_readahead(sb, sec);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	*bh = sb_bread(sb, sec);
77162306a36Sopenharmony_ci	if (!*bh)
77262306a36Sopenharmony_ci		return NULL;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return (struct exfat_dentry *)((*bh)->b_data + off);
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cienum exfat_validate_dentry_mode {
77862306a36Sopenharmony_ci	ES_MODE_STARTED,
77962306a36Sopenharmony_ci	ES_MODE_GET_FILE_ENTRY,
78062306a36Sopenharmony_ci	ES_MODE_GET_STRM_ENTRY,
78162306a36Sopenharmony_ci	ES_MODE_GET_NAME_ENTRY,
78262306a36Sopenharmony_ci	ES_MODE_GET_CRITICAL_SEC_ENTRY,
78362306a36Sopenharmony_ci	ES_MODE_GET_BENIGN_SEC_ENTRY,
78462306a36Sopenharmony_ci};
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic bool exfat_validate_entry(unsigned int type,
78762306a36Sopenharmony_ci		enum exfat_validate_dentry_mode *mode)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	if (type == TYPE_UNUSED || type == TYPE_DELETED)
79062306a36Sopenharmony_ci		return false;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	switch (*mode) {
79362306a36Sopenharmony_ci	case ES_MODE_STARTED:
79462306a36Sopenharmony_ci		if  (type != TYPE_FILE && type != TYPE_DIR)
79562306a36Sopenharmony_ci			return false;
79662306a36Sopenharmony_ci		*mode = ES_MODE_GET_FILE_ENTRY;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	case ES_MODE_GET_FILE_ENTRY:
79962306a36Sopenharmony_ci		if (type != TYPE_STREAM)
80062306a36Sopenharmony_ci			return false;
80162306a36Sopenharmony_ci		*mode = ES_MODE_GET_STRM_ENTRY;
80262306a36Sopenharmony_ci		break;
80362306a36Sopenharmony_ci	case ES_MODE_GET_STRM_ENTRY:
80462306a36Sopenharmony_ci		if (type != TYPE_EXTEND)
80562306a36Sopenharmony_ci			return false;
80662306a36Sopenharmony_ci		*mode = ES_MODE_GET_NAME_ENTRY;
80762306a36Sopenharmony_ci		break;
80862306a36Sopenharmony_ci	case ES_MODE_GET_NAME_ENTRY:
80962306a36Sopenharmony_ci		if (type & TYPE_BENIGN_SEC)
81062306a36Sopenharmony_ci			*mode = ES_MODE_GET_BENIGN_SEC_ENTRY;
81162306a36Sopenharmony_ci		else if (type != TYPE_EXTEND)
81262306a36Sopenharmony_ci			return false;
81362306a36Sopenharmony_ci		break;
81462306a36Sopenharmony_ci	case ES_MODE_GET_BENIGN_SEC_ENTRY:
81562306a36Sopenharmony_ci		/* Assume unreconized benign secondary entry */
81662306a36Sopenharmony_ci		if (!(type & TYPE_BENIGN_SEC))
81762306a36Sopenharmony_ci			return false;
81862306a36Sopenharmony_ci		break;
81962306a36Sopenharmony_ci	default:
82062306a36Sopenharmony_ci		return false;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return true;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistruct exfat_dentry *exfat_get_dentry_cached(
82762306a36Sopenharmony_ci	struct exfat_entry_set_cache *es, int num)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	int off = es->start_off + num * DENTRY_SIZE;
83062306a36Sopenharmony_ci	struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)];
83162306a36Sopenharmony_ci	char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return (struct exfat_dentry *)p;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci/*
83762306a36Sopenharmony_ci * Returns a set of dentries for a file or dir.
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * Note It provides a direct pointer to bh->data via exfat_get_dentry_cached().
84062306a36Sopenharmony_ci * User should call exfat_get_dentry_set() after setting 'modified' to apply
84162306a36Sopenharmony_ci * changes made in this entry set to the real device.
84262306a36Sopenharmony_ci *
84362306a36Sopenharmony_ci * in:
84462306a36Sopenharmony_ci *   sb+p_dir+entry: indicates a file/dir
84562306a36Sopenharmony_ci *   type:  specifies how many dentries should be included.
84662306a36Sopenharmony_ci * return:
84762306a36Sopenharmony_ci *   pointer of entry set on success,
84862306a36Sopenharmony_ci *   NULL on failure.
84962306a36Sopenharmony_ci */
85062306a36Sopenharmony_ciint exfat_get_dentry_set(struct exfat_entry_set_cache *es,
85162306a36Sopenharmony_ci		struct super_block *sb, struct exfat_chain *p_dir, int entry,
85262306a36Sopenharmony_ci		unsigned int type)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	int ret, i, num_bh;
85562306a36Sopenharmony_ci	unsigned int off;
85662306a36Sopenharmony_ci	sector_t sec;
85762306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
85862306a36Sopenharmony_ci	struct exfat_dentry *ep;
85962306a36Sopenharmony_ci	int num_entries;
86062306a36Sopenharmony_ci	enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
86162306a36Sopenharmony_ci	struct buffer_head *bh;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (p_dir->dir == DIR_DELETED) {
86462306a36Sopenharmony_ci		exfat_err(sb, "access to deleted dentry");
86562306a36Sopenharmony_ci		return -EIO;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	ret = exfat_find_location(sb, p_dir, entry, &sec, &off);
86962306a36Sopenharmony_ci	if (ret)
87062306a36Sopenharmony_ci		return ret;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	memset(es, 0, sizeof(*es));
87362306a36Sopenharmony_ci	es->sb = sb;
87462306a36Sopenharmony_ci	es->modified = false;
87562306a36Sopenharmony_ci	es->start_off = off;
87662306a36Sopenharmony_ci	es->bh = es->__bh;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	bh = sb_bread(sb, sec);
87962306a36Sopenharmony_ci	if (!bh)
88062306a36Sopenharmony_ci		return -EIO;
88162306a36Sopenharmony_ci	es->bh[es->num_bh++] = bh;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
88462306a36Sopenharmony_ci	if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
88562306a36Sopenharmony_ci		goto put_es;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	num_entries = type == ES_ALL_ENTRIES ?
88862306a36Sopenharmony_ci		ep->dentry.file.num_ext + 1 : type;
88962306a36Sopenharmony_ci	es->num_entries = num_entries;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
89262306a36Sopenharmony_ci	if (num_bh > ARRAY_SIZE(es->__bh)) {
89362306a36Sopenharmony_ci		es->bh = kmalloc_array(num_bh, sizeof(*es->bh), GFP_KERNEL);
89462306a36Sopenharmony_ci		if (!es->bh) {
89562306a36Sopenharmony_ci			brelse(bh);
89662306a36Sopenharmony_ci			return -ENOMEM;
89762306a36Sopenharmony_ci		}
89862306a36Sopenharmony_ci		es->bh[0] = bh;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	for (i = 1; i < num_bh; i++) {
90262306a36Sopenharmony_ci		/* get the next sector */
90362306a36Sopenharmony_ci		if (exfat_is_last_sector_in_cluster(sbi, sec)) {
90462306a36Sopenharmony_ci			unsigned int clu = exfat_sector_to_cluster(sbi, sec);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci			if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
90762306a36Sopenharmony_ci				clu++;
90862306a36Sopenharmony_ci			else if (exfat_get_next_cluster(sb, &clu))
90962306a36Sopenharmony_ci				goto put_es;
91062306a36Sopenharmony_ci			sec = exfat_cluster_to_sector(sbi, clu);
91162306a36Sopenharmony_ci		} else {
91262306a36Sopenharmony_ci			sec++;
91362306a36Sopenharmony_ci		}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		bh = sb_bread(sb, sec);
91662306a36Sopenharmony_ci		if (!bh)
91762306a36Sopenharmony_ci			goto put_es;
91862306a36Sopenharmony_ci		es->bh[es->num_bh++] = bh;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* validate cached dentries */
92262306a36Sopenharmony_ci	for (i = ES_IDX_STREAM; i < num_entries; i++) {
92362306a36Sopenharmony_ci		ep = exfat_get_dentry_cached(es, i);
92462306a36Sopenharmony_ci		if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
92562306a36Sopenharmony_ci			goto put_es;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci	return 0;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ciput_es:
93062306a36Sopenharmony_ci	exfat_put_dentry_set(es, false);
93162306a36Sopenharmony_ci	return -EIO;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic inline void exfat_reset_empty_hint(struct exfat_hint_femp *hint_femp)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	hint_femp->eidx = EXFAT_HINT_NONE;
93762306a36Sopenharmony_ci	hint_femp->count = 0;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic inline void exfat_set_empty_hint(struct exfat_inode_info *ei,
94162306a36Sopenharmony_ci		struct exfat_hint_femp *candi_empty, struct exfat_chain *clu,
94262306a36Sopenharmony_ci		int dentry, int num_entries, int entry_type)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	if (ei->hint_femp.eidx == EXFAT_HINT_NONE ||
94562306a36Sopenharmony_ci	    ei->hint_femp.eidx > dentry) {
94662306a36Sopenharmony_ci		int total_entries = EXFAT_B_TO_DEN(i_size_read(&ei->vfs_inode));
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		if (candi_empty->count == 0) {
94962306a36Sopenharmony_ci			candi_empty->cur = *clu;
95062306a36Sopenharmony_ci			candi_empty->eidx = dentry;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		if (entry_type == TYPE_UNUSED)
95462306a36Sopenharmony_ci			candi_empty->count += total_entries - dentry;
95562306a36Sopenharmony_ci		else
95662306a36Sopenharmony_ci			candi_empty->count++;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		if (candi_empty->count == num_entries ||
95962306a36Sopenharmony_ci		    candi_empty->count + candi_empty->eidx == total_entries)
96062306a36Sopenharmony_ci			ei->hint_femp = *candi_empty;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cienum {
96562306a36Sopenharmony_ci	DIRENT_STEP_FILE,
96662306a36Sopenharmony_ci	DIRENT_STEP_STRM,
96762306a36Sopenharmony_ci	DIRENT_STEP_NAME,
96862306a36Sopenharmony_ci	DIRENT_STEP_SECD,
96962306a36Sopenharmony_ci};
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci/*
97262306a36Sopenharmony_ci * @ei:         inode info of parent directory
97362306a36Sopenharmony_ci * @p_dir:      directory structure of parent directory
97462306a36Sopenharmony_ci * @num_entries:entry size of p_uniname
97562306a36Sopenharmony_ci * @hint_opt:   If p_uniname is found, filled with optimized dir/entry
97662306a36Sopenharmony_ci *              for traversing cluster chain.
97762306a36Sopenharmony_ci * @return:
97862306a36Sopenharmony_ci *   >= 0:      file directory entry position where the name exists
97962306a36Sopenharmony_ci *   -ENOENT:   entry with the name does not exist
98062306a36Sopenharmony_ci *   -EIO:      I/O error
98162306a36Sopenharmony_ci */
98262306a36Sopenharmony_ciint exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
98362306a36Sopenharmony_ci		struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
98462306a36Sopenharmony_ci		struct exfat_hint *hint_opt)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
98762306a36Sopenharmony_ci	int order, step, name_len = 0;
98862306a36Sopenharmony_ci	int dentries_per_clu;
98962306a36Sopenharmony_ci	unsigned int entry_type;
99062306a36Sopenharmony_ci	unsigned short *uniname = NULL;
99162306a36Sopenharmony_ci	struct exfat_chain clu;
99262306a36Sopenharmony_ci	struct exfat_hint *hint_stat = &ei->hint_stat;
99362306a36Sopenharmony_ci	struct exfat_hint_femp candi_empty;
99462306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
99562306a36Sopenharmony_ci	int num_entries = exfat_calc_num_entries(p_uniname);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (num_entries < 0)
99862306a36Sopenharmony_ci		return num_entries;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	dentries_per_clu = sbi->dentries_per_clu;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	exfat_chain_dup(&clu, p_dir);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (hint_stat->eidx) {
100562306a36Sopenharmony_ci		clu.dir = hint_stat->clu;
100662306a36Sopenharmony_ci		dentry = hint_stat->eidx;
100762306a36Sopenharmony_ci		end_eidx = dentry;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	exfat_reset_empty_hint(&ei->hint_femp);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cirewind:
101362306a36Sopenharmony_ci	order = 0;
101462306a36Sopenharmony_ci	step = DIRENT_STEP_FILE;
101562306a36Sopenharmony_ci	exfat_reset_empty_hint(&candi_empty);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	while (clu.dir != EXFAT_EOF_CLUSTER) {
101862306a36Sopenharmony_ci		i = dentry & (dentries_per_clu - 1);
101962306a36Sopenharmony_ci		for (; i < dentries_per_clu; i++, dentry++) {
102062306a36Sopenharmony_ci			struct exfat_dentry *ep;
102162306a36Sopenharmony_ci			struct buffer_head *bh;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci			if (rewind && dentry == end_eidx)
102462306a36Sopenharmony_ci				goto not_found;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci			ep = exfat_get_dentry(sb, &clu, i, &bh);
102762306a36Sopenharmony_ci			if (!ep)
102862306a36Sopenharmony_ci				return -EIO;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci			entry_type = exfat_get_entry_type(ep);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci			if (entry_type == TYPE_UNUSED ||
103362306a36Sopenharmony_ci			    entry_type == TYPE_DELETED) {
103462306a36Sopenharmony_ci				step = DIRENT_STEP_FILE;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci				exfat_set_empty_hint(ei, &candi_empty, &clu,
103762306a36Sopenharmony_ci						dentry, num_entries,
103862306a36Sopenharmony_ci						entry_type);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci				brelse(bh);
104162306a36Sopenharmony_ci				if (entry_type == TYPE_UNUSED)
104262306a36Sopenharmony_ci					goto not_found;
104362306a36Sopenharmony_ci				continue;
104462306a36Sopenharmony_ci			}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci			exfat_reset_empty_hint(&candi_empty);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci			if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
104962306a36Sopenharmony_ci				step = DIRENT_STEP_FILE;
105062306a36Sopenharmony_ci				hint_opt->clu = clu.dir;
105162306a36Sopenharmony_ci				hint_opt->eidx = i;
105262306a36Sopenharmony_ci				num_ext = ep->dentry.file.num_ext;
105362306a36Sopenharmony_ci				step = DIRENT_STEP_STRM;
105462306a36Sopenharmony_ci				brelse(bh);
105562306a36Sopenharmony_ci				continue;
105662306a36Sopenharmony_ci			}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci			if (entry_type == TYPE_STREAM) {
105962306a36Sopenharmony_ci				u16 name_hash;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci				if (step != DIRENT_STEP_STRM) {
106262306a36Sopenharmony_ci					step = DIRENT_STEP_FILE;
106362306a36Sopenharmony_ci					brelse(bh);
106462306a36Sopenharmony_ci					continue;
106562306a36Sopenharmony_ci				}
106662306a36Sopenharmony_ci				step = DIRENT_STEP_FILE;
106762306a36Sopenharmony_ci				name_hash = le16_to_cpu(
106862306a36Sopenharmony_ci						ep->dentry.stream.name_hash);
106962306a36Sopenharmony_ci				if (p_uniname->name_hash == name_hash &&
107062306a36Sopenharmony_ci				    p_uniname->name_len ==
107162306a36Sopenharmony_ci						ep->dentry.stream.name_len) {
107262306a36Sopenharmony_ci					step = DIRENT_STEP_NAME;
107362306a36Sopenharmony_ci					order = 1;
107462306a36Sopenharmony_ci					name_len = 0;
107562306a36Sopenharmony_ci				}
107662306a36Sopenharmony_ci				brelse(bh);
107762306a36Sopenharmony_ci				continue;
107862306a36Sopenharmony_ci			}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci			brelse(bh);
108162306a36Sopenharmony_ci			if (entry_type == TYPE_EXTEND) {
108262306a36Sopenharmony_ci				unsigned short entry_uniname[16], unichar;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci				if (step != DIRENT_STEP_NAME ||
108562306a36Sopenharmony_ci				    name_len >= MAX_NAME_LENGTH) {
108662306a36Sopenharmony_ci					step = DIRENT_STEP_FILE;
108762306a36Sopenharmony_ci					continue;
108862306a36Sopenharmony_ci				}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci				if (++order == 2)
109162306a36Sopenharmony_ci					uniname = p_uniname->name;
109262306a36Sopenharmony_ci				else
109362306a36Sopenharmony_ci					uniname += EXFAT_FILE_NAME_LEN;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci				len = exfat_extract_uni_name(ep, entry_uniname);
109662306a36Sopenharmony_ci				name_len += len;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci				unichar = *(uniname+len);
109962306a36Sopenharmony_ci				*(uniname+len) = 0x0;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci				if (exfat_uniname_ncmp(sb, uniname,
110262306a36Sopenharmony_ci					entry_uniname, len)) {
110362306a36Sopenharmony_ci					step = DIRENT_STEP_FILE;
110462306a36Sopenharmony_ci				} else if (p_uniname->name_len == name_len) {
110562306a36Sopenharmony_ci					if (order == num_ext)
110662306a36Sopenharmony_ci						goto found;
110762306a36Sopenharmony_ci					step = DIRENT_STEP_SECD;
110862306a36Sopenharmony_ci				}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci				*(uniname+len) = unichar;
111162306a36Sopenharmony_ci				continue;
111262306a36Sopenharmony_ci			}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci			if (entry_type &
111562306a36Sopenharmony_ci					(TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) {
111662306a36Sopenharmony_ci				if (step == DIRENT_STEP_SECD) {
111762306a36Sopenharmony_ci					if (++order == num_ext)
111862306a36Sopenharmony_ci						goto found;
111962306a36Sopenharmony_ci					continue;
112062306a36Sopenharmony_ci				}
112162306a36Sopenharmony_ci			}
112262306a36Sopenharmony_ci			step = DIRENT_STEP_FILE;
112362306a36Sopenharmony_ci		}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
112662306a36Sopenharmony_ci			if (--clu.size > 0)
112762306a36Sopenharmony_ci				clu.dir++;
112862306a36Sopenharmony_ci			else
112962306a36Sopenharmony_ci				clu.dir = EXFAT_EOF_CLUSTER;
113062306a36Sopenharmony_ci		} else {
113162306a36Sopenharmony_ci			if (exfat_get_next_cluster(sb, &clu.dir))
113262306a36Sopenharmony_ci				return -EIO;
113362306a36Sopenharmony_ci		}
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cinot_found:
113762306a36Sopenharmony_ci	/*
113862306a36Sopenharmony_ci	 * We started at not 0 index,so we should try to find target
113962306a36Sopenharmony_ci	 * from 0 index to the index we started at.
114062306a36Sopenharmony_ci	 */
114162306a36Sopenharmony_ci	if (!rewind && end_eidx) {
114262306a36Sopenharmony_ci		rewind = 1;
114362306a36Sopenharmony_ci		dentry = 0;
114462306a36Sopenharmony_ci		clu.dir = p_dir->dir;
114562306a36Sopenharmony_ci		goto rewind;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	/*
114962306a36Sopenharmony_ci	 * set the EXFAT_EOF_CLUSTER flag to avoid search
115062306a36Sopenharmony_ci	 * from the beginning again when allocated a new cluster
115162306a36Sopenharmony_ci	 */
115262306a36Sopenharmony_ci	if (ei->hint_femp.eidx == EXFAT_HINT_NONE) {
115362306a36Sopenharmony_ci		ei->hint_femp.cur.dir = EXFAT_EOF_CLUSTER;
115462306a36Sopenharmony_ci		ei->hint_femp.eidx = p_dir->size * dentries_per_clu;
115562306a36Sopenharmony_ci		ei->hint_femp.count = 0;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* initialized hint_stat */
115962306a36Sopenharmony_ci	hint_stat->clu = p_dir->dir;
116062306a36Sopenharmony_ci	hint_stat->eidx = 0;
116162306a36Sopenharmony_ci	return -ENOENT;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cifound:
116462306a36Sopenharmony_ci	/* next dentry we'll find is out of this cluster */
116562306a36Sopenharmony_ci	if (!((dentry + 1) & (dentries_per_clu - 1))) {
116662306a36Sopenharmony_ci		int ret = 0;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
116962306a36Sopenharmony_ci			if (--clu.size > 0)
117062306a36Sopenharmony_ci				clu.dir++;
117162306a36Sopenharmony_ci			else
117262306a36Sopenharmony_ci				clu.dir = EXFAT_EOF_CLUSTER;
117362306a36Sopenharmony_ci		} else {
117462306a36Sopenharmony_ci			ret = exfat_get_next_cluster(sb, &clu.dir);
117562306a36Sopenharmony_ci		}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		if (ret || clu.dir == EXFAT_EOF_CLUSTER) {
117862306a36Sopenharmony_ci			/* just initialized hint_stat */
117962306a36Sopenharmony_ci			hint_stat->clu = p_dir->dir;
118062306a36Sopenharmony_ci			hint_stat->eidx = 0;
118162306a36Sopenharmony_ci			return (dentry - num_ext);
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	hint_stat->clu = clu.dir;
118662306a36Sopenharmony_ci	hint_stat->eidx = dentry + 1;
118762306a36Sopenharmony_ci	return dentry - num_ext;
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ciint exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
119162306a36Sopenharmony_ci		int entry, struct exfat_dentry *ep)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	int i, count = 0;
119462306a36Sopenharmony_ci	unsigned int type;
119562306a36Sopenharmony_ci	struct exfat_dentry *ext_ep;
119662306a36Sopenharmony_ci	struct buffer_head *bh;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	for (i = 0, entry++; i < ep->dentry.file.num_ext; i++, entry++) {
119962306a36Sopenharmony_ci		ext_ep = exfat_get_dentry(sb, p_dir, entry, &bh);
120062306a36Sopenharmony_ci		if (!ext_ep)
120162306a36Sopenharmony_ci			return -EIO;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci		type = exfat_get_entry_type(ext_ep);
120462306a36Sopenharmony_ci		brelse(bh);
120562306a36Sopenharmony_ci		if (type & TYPE_CRITICAL_SEC || type & TYPE_BENIGN_SEC)
120662306a36Sopenharmony_ci			count++;
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci	return count;
120962306a36Sopenharmony_ci}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ciint exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	int i, count = 0;
121462306a36Sopenharmony_ci	int dentries_per_clu;
121562306a36Sopenharmony_ci	unsigned int entry_type;
121662306a36Sopenharmony_ci	struct exfat_chain clu;
121762306a36Sopenharmony_ci	struct exfat_dentry *ep;
121862306a36Sopenharmony_ci	struct exfat_sb_info *sbi = EXFAT_SB(sb);
121962306a36Sopenharmony_ci	struct buffer_head *bh;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	dentries_per_clu = sbi->dentries_per_clu;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	exfat_chain_dup(&clu, p_dir);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	while (clu.dir != EXFAT_EOF_CLUSTER) {
122662306a36Sopenharmony_ci		for (i = 0; i < dentries_per_clu; i++) {
122762306a36Sopenharmony_ci			ep = exfat_get_dentry(sb, &clu, i, &bh);
122862306a36Sopenharmony_ci			if (!ep)
122962306a36Sopenharmony_ci				return -EIO;
123062306a36Sopenharmony_ci			entry_type = exfat_get_entry_type(ep);
123162306a36Sopenharmony_ci			brelse(bh);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci			if (entry_type == TYPE_UNUSED)
123462306a36Sopenharmony_ci				return count;
123562306a36Sopenharmony_ci			if (entry_type != TYPE_DIR)
123662306a36Sopenharmony_ci				continue;
123762306a36Sopenharmony_ci			count++;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
124162306a36Sopenharmony_ci			if (--clu.size > 0)
124262306a36Sopenharmony_ci				clu.dir++;
124362306a36Sopenharmony_ci			else
124462306a36Sopenharmony_ci				clu.dir = EXFAT_EOF_CLUSTER;
124562306a36Sopenharmony_ci		} else {
124662306a36Sopenharmony_ci			if (exfat_get_next_cluster(sb, &(clu.dir)))
124762306a36Sopenharmony_ci				return -EIO;
124862306a36Sopenharmony_ci		}
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	return count;
125262306a36Sopenharmony_ci}
1253