// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
#include "exfat_raw.h"
#include "exfat_fs.h"
static int exfat_extract_uni_name(struct exfat_dentry *ep,
unsigned short *uniname)
{
int i, len = 0;
for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) {
*uniname = le16_to_cpu(ep->dentry.name.unicode_0_14[i]);
if (*uniname == 0x0)
return len;
uniname++;
len++;
}
*uniname = 0x0;
return len;
}
static int exfat_get_uniname_from_ext_entry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, unsigned short *uniname)
{
int i, err;
struct exfat_entry_set_cache es;
unsigned int uni_len = 0, len;
err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES);
if (err)
return err;
/*
* First entry : file entry
* Second entry : stream-extension entry
* Third entry : first file-name entry
* So, the index of first file-name dentry should start from 2.
*/
for (i = ES_IDX_FIRST_FILENAME; i < es.num_entries; i++) {
struct exfat_dentry *ep = exfat_get_dentry_cached(&es, i);
/* end of name entry */
if (exfat_get_entry_type(ep) != TYPE_EXTEND)
break;
len = exfat_extract_uni_name(ep, uniname);
uni_len += len;
if (len != EXFAT_FILE_NAME_LEN || uni_len >= MAX_NAME_LENGTH)
break;
uniname += EXFAT_FILE_NAME_LEN;
}
exfat_put_dentry_set(&es, false);
return 0;
}
/* read a directory entry from the opened directory */
static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
{
int i, dentries_per_clu, num_ext, err;
unsigned int type, clu_offset, max_dentries;
struct exfat_chain dir, clu;
struct exfat_uni_name uni_name;
struct exfat_dentry *ep;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0xFFFFFFFF;
struct buffer_head *bh;
/* check if the given file ID is opened */
if (ei->type != TYPE_DIR)
return -EPERM;
if (ei->entry == -1)
exfat_chain_set(&dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
else
exfat_chain_set(&dir, ei->start_clu,
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
dentries_per_clu = sbi->dentries_per_clu;
max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
(u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi));
clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
exfat_chain_dup(&clu, &dir);
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
clu.dir += clu_offset;
clu.size -= clu_offset;
} else {