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