18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/adfs/dir.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1999-2000 Russell King
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Common directory handling for ADFS
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include "adfs.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci * For future.  This should probably be per-directory.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(adfs_dir_rwsem);
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciint adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
188c2ecf20Sopenharmony_ci		      size_t len)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct super_block *sb = dir->sb;
218c2ecf20Sopenharmony_ci	unsigned int index, remain;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	index = offset >> sb->s_blocksize_bits;
248c2ecf20Sopenharmony_ci	offset &= sb->s_blocksize - 1;
258c2ecf20Sopenharmony_ci	remain = sb->s_blocksize - offset;
268c2ecf20Sopenharmony_ci	if (index + (remain < len) >= dir->nr_buffers)
278c2ecf20Sopenharmony_ci		return -EINVAL;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	if (remain < len) {
308c2ecf20Sopenharmony_ci		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
318c2ecf20Sopenharmony_ci		dst += remain;
328c2ecf20Sopenharmony_ci		len -= remain;
338c2ecf20Sopenharmony_ci		index += 1;
348c2ecf20Sopenharmony_ci		offset = 0;
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	memcpy(dst, dir->bhs[index]->b_data + offset, len);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return 0;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ciint adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
438c2ecf20Sopenharmony_ci		    size_t len)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct super_block *sb = dir->sb;
468c2ecf20Sopenharmony_ci	unsigned int index, remain;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	index = offset >> sb->s_blocksize_bits;
498c2ecf20Sopenharmony_ci	offset &= sb->s_blocksize - 1;
508c2ecf20Sopenharmony_ci	remain = sb->s_blocksize - offset;
518c2ecf20Sopenharmony_ci	if (index + (remain < len) >= dir->nr_buffers)
528c2ecf20Sopenharmony_ci		return -EINVAL;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (remain < len) {
558c2ecf20Sopenharmony_ci		memcpy(dir->bhs[index]->b_data + offset, src, remain);
568c2ecf20Sopenharmony_ci		src += remain;
578c2ecf20Sopenharmony_ci		len -= remain;
588c2ecf20Sopenharmony_ci		index += 1;
598c2ecf20Sopenharmony_ci		offset = 0;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	memcpy(dir->bhs[index]->b_data + offset, src, len);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void __adfs_dir_cleanup(struct adfs_dir *dir)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	dir->nr_buffers = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (dir->bhs != dir->bh)
728c2ecf20Sopenharmony_ci		kfree(dir->bhs);
738c2ecf20Sopenharmony_ci	dir->bhs = NULL;
748c2ecf20Sopenharmony_ci	dir->sb = NULL;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid adfs_dir_relse(struct adfs_dir *dir)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	unsigned int i;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for (i = 0; i < dir->nr_buffers; i++)
828c2ecf20Sopenharmony_ci		brelse(dir->bhs[i]);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	__adfs_dir_cleanup(dir);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void adfs_dir_forget(struct adfs_dir *dir)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	unsigned int i;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	for (i = 0; i < dir->nr_buffers; i++)
928c2ecf20Sopenharmony_ci		bforget(dir->bhs[i]);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	__adfs_dir_cleanup(dir);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
988c2ecf20Sopenharmony_ci			  unsigned int size, struct adfs_dir *dir)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct buffer_head **bhs;
1018c2ecf20Sopenharmony_ci	unsigned int i, num;
1028c2ecf20Sopenharmony_ci	int block;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
1058c2ecf20Sopenharmony_ci	if (num > ARRAY_SIZE(dir->bh)) {
1068c2ecf20Sopenharmony_ci		/* We only allow one extension */
1078c2ecf20Sopenharmony_ci		if (dir->bhs != dir->bh)
1088c2ecf20Sopenharmony_ci			return -EINVAL;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
1118c2ecf20Sopenharmony_ci		if (!bhs)
1128c2ecf20Sopenharmony_ci			return -ENOMEM;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (dir->nr_buffers)
1158c2ecf20Sopenharmony_ci			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		dir->bhs = bhs;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	for (i = dir->nr_buffers; i < num; i++) {
1218c2ecf20Sopenharmony_ci		block = __adfs_block_map(sb, indaddr, i);
1228c2ecf20Sopenharmony_ci		if (!block) {
1238c2ecf20Sopenharmony_ci			adfs_error(sb, "dir %06x has a hole at offset %u",
1248c2ecf20Sopenharmony_ci				   indaddr, i);
1258c2ecf20Sopenharmony_ci			goto error;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		dir->bhs[i] = sb_bread(sb, block);
1298c2ecf20Sopenharmony_ci		if (!dir->bhs[i]) {
1308c2ecf20Sopenharmony_ci			adfs_error(sb,
1318c2ecf20Sopenharmony_ci				   "dir %06x failed read at offset %u, mapped block 0x%08x",
1328c2ecf20Sopenharmony_ci				   indaddr, i, block);
1338c2ecf20Sopenharmony_ci			goto error;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		dir->nr_buffers++;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cierror:
1418c2ecf20Sopenharmony_ci	adfs_dir_relse(dir);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return -EIO;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int adfs_dir_read(struct super_block *sb, u32 indaddr,
1478c2ecf20Sopenharmony_ci			 unsigned int size, struct adfs_dir *dir)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	dir->sb = sb;
1508c2ecf20Sopenharmony_ci	dir->bhs = dir->bh;
1518c2ecf20Sopenharmony_ci	dir->nr_buffers = 0;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
1578c2ecf20Sopenharmony_ci			       struct adfs_dir *dir)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	int ret;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
1628c2ecf20Sopenharmony_ci	if (ret)
1638c2ecf20Sopenharmony_ci		return ret;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (ADFS_I(inode)->parent_id != dir->parent_id) {
1668c2ecf20Sopenharmony_ci		adfs_error(sb,
1678c2ecf20Sopenharmony_ci			   "parent directory id changed under me! (%06x but got %06x)\n",
1688c2ecf20Sopenharmony_ci			   ADFS_I(inode)->parent_id, dir->parent_id);
1698c2ecf20Sopenharmony_ci		adfs_dir_relse(dir);
1708c2ecf20Sopenharmony_ci		ret = -EIO;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return ret;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void adfs_dir_mark_dirty(struct adfs_dir *dir)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	unsigned int i;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Mark the buffers dirty */
1818c2ecf20Sopenharmony_ci	for (i = 0; i < dir->nr_buffers; i++)
1828c2ecf20Sopenharmony_ci		mark_buffer_dirty(dir->bhs[i]);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int adfs_dir_sync(struct adfs_dir *dir)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int err = 0;
1888c2ecf20Sopenharmony_ci	int i;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	for (i = dir->nr_buffers - 1; i >= 0; i--) {
1918c2ecf20Sopenharmony_ci		struct buffer_head *bh = dir->bhs[i];
1928c2ecf20Sopenharmony_ci		sync_dirty_buffer(bh);
1938c2ecf20Sopenharmony_ci		if (buffer_req(bh) && !buffer_uptodate(bh))
1948c2ecf20Sopenharmony_ci			err = -EIO;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return err;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_civoid adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	unsigned int dots, i;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/*
2058c2ecf20Sopenharmony_ci	 * RISC OS allows the use of '/' in directory entry names, so we need
2068c2ecf20Sopenharmony_ci	 * to fix these up.  '/' is typically used for FAT compatibility to
2078c2ecf20Sopenharmony_ci	 * represent '.', so do the same conversion here.  In any case, '.'
2088c2ecf20Sopenharmony_ci	 * will never be in a RISC OS name since it is used as the pathname
2098c2ecf20Sopenharmony_ci	 * separator.  Handle the case where we may generate a '.' or '..'
2108c2ecf20Sopenharmony_ci	 * name, replacing the first character with '^' (the RISC OS "parent
2118c2ecf20Sopenharmony_ci	 * directory" character.)
2128c2ecf20Sopenharmony_ci	 */
2138c2ecf20Sopenharmony_ci	for (i = dots = 0; i < obj->name_len; i++)
2148c2ecf20Sopenharmony_ci		if (obj->name[i] == '/') {
2158c2ecf20Sopenharmony_ci			obj->name[i] = '.';
2168c2ecf20Sopenharmony_ci			dots++;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (obj->name_len <= 2 && dots == obj->name_len)
2208c2ecf20Sopenharmony_ci		obj->name[0] = '^';
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/*
2238c2ecf20Sopenharmony_ci	 * If the object is a file, and the user requested the ,xyz hex
2248c2ecf20Sopenharmony_ci	 * filetype suffix to the name, check the filetype and append.
2258c2ecf20Sopenharmony_ci	 */
2268c2ecf20Sopenharmony_ci	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
2278c2ecf20Sopenharmony_ci		u16 filetype = adfs_filetype(obj->loadaddr);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		if (filetype != ADFS_FILETYPE_NONE) {
2308c2ecf20Sopenharmony_ci			obj->name[obj->name_len++] = ',';
2318c2ecf20Sopenharmony_ci			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
2328c2ecf20Sopenharmony_ci			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
2338c2ecf20Sopenharmony_ci			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int adfs_iterate(struct file *file, struct dir_context *ctx)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
2418c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
2428c2ecf20Sopenharmony_ci	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
2438c2ecf20Sopenharmony_ci	struct adfs_dir dir;
2448c2ecf20Sopenharmony_ci	int ret;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	down_read(&adfs_dir_rwsem);
2478c2ecf20Sopenharmony_ci	ret = adfs_dir_read_inode(sb, inode, &dir);
2488c2ecf20Sopenharmony_ci	if (ret)
2498c2ecf20Sopenharmony_ci		goto unlock;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (ctx->pos == 0) {
2528c2ecf20Sopenharmony_ci		if (!dir_emit_dot(file, ctx))
2538c2ecf20Sopenharmony_ci			goto unlock_relse;
2548c2ecf20Sopenharmony_ci		ctx->pos = 1;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci	if (ctx->pos == 1) {
2578c2ecf20Sopenharmony_ci		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
2588c2ecf20Sopenharmony_ci			goto unlock_relse;
2598c2ecf20Sopenharmony_ci		ctx->pos = 2;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	ret = ops->iterate(&dir, ctx);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciunlock_relse:
2658c2ecf20Sopenharmony_ci	up_read(&adfs_dir_rwsem);
2668c2ecf20Sopenharmony_ci	adfs_dir_relse(&dir);
2678c2ecf20Sopenharmony_ci	return ret;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ciunlock:
2708c2ecf20Sopenharmony_ci	up_read(&adfs_dir_rwsem);
2718c2ecf20Sopenharmony_ci	return ret;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ciint
2758c2ecf20Sopenharmony_ciadfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
2788c2ecf20Sopenharmony_ci	struct adfs_dir dir;
2798c2ecf20Sopenharmony_ci	int ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
2828c2ecf20Sopenharmony_ci		return -EINVAL;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (!ops->update)
2858c2ecf20Sopenharmony_ci		return -EINVAL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	down_write(&adfs_dir_rwsem);
2888c2ecf20Sopenharmony_ci	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
2898c2ecf20Sopenharmony_ci	if (ret)
2908c2ecf20Sopenharmony_ci		goto unlock;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ret = ops->update(&dir, obj);
2938c2ecf20Sopenharmony_ci	if (ret)
2948c2ecf20Sopenharmony_ci		goto forget;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ret = ops->commit(&dir);
2978c2ecf20Sopenharmony_ci	if (ret)
2988c2ecf20Sopenharmony_ci		goto forget;
2998c2ecf20Sopenharmony_ci	up_write(&adfs_dir_rwsem);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	adfs_dir_mark_dirty(&dir);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (wait)
3048c2ecf20Sopenharmony_ci		ret = adfs_dir_sync(&dir);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	adfs_dir_relse(&dir);
3078c2ecf20Sopenharmony_ci	return ret;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/*
3108c2ecf20Sopenharmony_ci	 * If the updated failed because the entry wasn't found, we can
3118c2ecf20Sopenharmony_ci	 * just release the buffers. If it was any other error, forget
3128c2ecf20Sopenharmony_ci	 * the dirtied buffers so they aren't written back to the media.
3138c2ecf20Sopenharmony_ci	 */
3148c2ecf20Sopenharmony_ciforget:
3158c2ecf20Sopenharmony_ci	if (ret == -ENOENT)
3168c2ecf20Sopenharmony_ci		adfs_dir_relse(&dir);
3178c2ecf20Sopenharmony_ci	else
3188c2ecf20Sopenharmony_ci		adfs_dir_forget(&dir);
3198c2ecf20Sopenharmony_ciunlock:
3208c2ecf20Sopenharmony_ci	up_write(&adfs_dir_rwsem);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return ret;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic unsigned char adfs_tolower(unsigned char c)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	if (c >= 'A' && c <= 'Z')
3288c2ecf20Sopenharmony_ci		c += 'a' - 'A';
3298c2ecf20Sopenharmony_ci	return c;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int __adfs_compare(const unsigned char *qstr, u32 qlen,
3338c2ecf20Sopenharmony_ci			  const char *str, u32 len)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	u32 i;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (qlen != len)
3388c2ecf20Sopenharmony_ci		return 1;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	for (i = 0; i < qlen; i++)
3418c2ecf20Sopenharmony_ci		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
3428c2ecf20Sopenharmony_ci			return 1;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return 0;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
3488c2ecf20Sopenharmony_ci				  struct object_info *obj)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
3518c2ecf20Sopenharmony_ci	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
3528c2ecf20Sopenharmony_ci	const unsigned char *name;
3538c2ecf20Sopenharmony_ci	struct adfs_dir dir;
3548c2ecf20Sopenharmony_ci	u32 name_len;
3558c2ecf20Sopenharmony_ci	int ret;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	down_read(&adfs_dir_rwsem);
3588c2ecf20Sopenharmony_ci	ret = adfs_dir_read_inode(sb, inode, &dir);
3598c2ecf20Sopenharmony_ci	if (ret)
3608c2ecf20Sopenharmony_ci		goto unlock;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ret = ops->setpos(&dir, 0);
3638c2ecf20Sopenharmony_ci	if (ret)
3648c2ecf20Sopenharmony_ci		goto unlock_relse;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	ret = -ENOENT;
3678c2ecf20Sopenharmony_ci	name = qstr->name;
3688c2ecf20Sopenharmony_ci	name_len = qstr->len;
3698c2ecf20Sopenharmony_ci	while (ops->getnext(&dir, obj) == 0) {
3708c2ecf20Sopenharmony_ci		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
3718c2ecf20Sopenharmony_ci			ret = 0;
3728c2ecf20Sopenharmony_ci			break;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci	obj->parent_id = ADFS_I(inode)->indaddr;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ciunlock_relse:
3788c2ecf20Sopenharmony_ci	up_read(&adfs_dir_rwsem);
3798c2ecf20Sopenharmony_ci	adfs_dir_relse(&dir);
3808c2ecf20Sopenharmony_ci	return ret;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ciunlock:
3838c2ecf20Sopenharmony_ci	up_read(&adfs_dir_rwsem);
3848c2ecf20Sopenharmony_ci	return ret;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ciconst struct file_operations adfs_dir_operations = {
3888c2ecf20Sopenharmony_ci	.read		= generic_read_dir,
3898c2ecf20Sopenharmony_ci	.llseek		= generic_file_llseek,
3908c2ecf20Sopenharmony_ci	.iterate_shared	= adfs_iterate,
3918c2ecf20Sopenharmony_ci	.fsync		= generic_file_fsync,
3928c2ecf20Sopenharmony_ci};
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int
3958c2ecf20Sopenharmony_ciadfs_hash(const struct dentry *parent, struct qstr *qstr)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	const unsigned char *name;
3988c2ecf20Sopenharmony_ci	unsigned long hash;
3998c2ecf20Sopenharmony_ci	u32 len;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
4028c2ecf20Sopenharmony_ci		return -ENAMETOOLONG;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	len = qstr->len;
4058c2ecf20Sopenharmony_ci	name = qstr->name;
4068c2ecf20Sopenharmony_ci	hash = init_name_hash(parent);
4078c2ecf20Sopenharmony_ci	while (len--)
4088c2ecf20Sopenharmony_ci		hash = partial_name_hash(adfs_tolower(*name++), hash);
4098c2ecf20Sopenharmony_ci	qstr->hash = end_name_hash(hash);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/*
4158c2ecf20Sopenharmony_ci * Compare two names, taking note of the name length
4168c2ecf20Sopenharmony_ci * requirements of the underlying filesystem.
4178c2ecf20Sopenharmony_ci */
4188c2ecf20Sopenharmony_cistatic int adfs_compare(const struct dentry *dentry, unsigned int len,
4198c2ecf20Sopenharmony_ci			const char *str, const struct qstr *qstr)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	return __adfs_compare(qstr->name, qstr->len, str, len);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ciconst struct dentry_operations adfs_dentry_operations = {
4258c2ecf20Sopenharmony_ci	.d_hash		= adfs_hash,
4268c2ecf20Sopenharmony_ci	.d_compare	= adfs_compare,
4278c2ecf20Sopenharmony_ci};
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic struct dentry *
4308c2ecf20Sopenharmony_ciadfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct inode *inode = NULL;
4338c2ecf20Sopenharmony_ci	struct object_info obj;
4348c2ecf20Sopenharmony_ci	int error;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
4378c2ecf20Sopenharmony_ci	if (error == 0) {
4388c2ecf20Sopenharmony_ci		/*
4398c2ecf20Sopenharmony_ci		 * This only returns NULL if get_empty_inode
4408c2ecf20Sopenharmony_ci		 * fails.
4418c2ecf20Sopenharmony_ci		 */
4428c2ecf20Sopenharmony_ci		inode = adfs_iget(dir->i_sb, &obj);
4438c2ecf20Sopenharmony_ci		if (!inode)
4448c2ecf20Sopenharmony_ci			inode = ERR_PTR(-EACCES);
4458c2ecf20Sopenharmony_ci	} else if (error != -ENOENT) {
4468c2ecf20Sopenharmony_ci		inode = ERR_PTR(error);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	return d_splice_alias(inode, dentry);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/*
4528c2ecf20Sopenharmony_ci * directories can handle most operations...
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_ciconst struct inode_operations adfs_dir_inode_operations = {
4558c2ecf20Sopenharmony_ci	.lookup		= adfs_lookup,
4568c2ecf20Sopenharmony_ci	.setattr	= adfs_notify_change,
4578c2ecf20Sopenharmony_ci};
458