162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/adfs/dir.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1999-2000 Russell King
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Common directory handling for ADFS
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include "adfs.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * For future.  This should probably be per-directory.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic DECLARE_RWSEM(adfs_dir_rwsem);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciint adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
1862306a36Sopenharmony_ci		      size_t len)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct super_block *sb = dir->sb;
2162306a36Sopenharmony_ci	unsigned int index, remain;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	index = offset >> sb->s_blocksize_bits;
2462306a36Sopenharmony_ci	offset &= sb->s_blocksize - 1;
2562306a36Sopenharmony_ci	remain = sb->s_blocksize - offset;
2662306a36Sopenharmony_ci	if (index + (remain < len) >= dir->nr_buffers)
2762306a36Sopenharmony_ci		return -EINVAL;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (remain < len) {
3062306a36Sopenharmony_ci		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
3162306a36Sopenharmony_ci		dst += remain;
3262306a36Sopenharmony_ci		len -= remain;
3362306a36Sopenharmony_ci		index += 1;
3462306a36Sopenharmony_ci		offset = 0;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	memcpy(dst, dir->bhs[index]->b_data + offset, len);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciint adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
4362306a36Sopenharmony_ci		    size_t len)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct super_block *sb = dir->sb;
4662306a36Sopenharmony_ci	unsigned int index, remain;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	index = offset >> sb->s_blocksize_bits;
4962306a36Sopenharmony_ci	offset &= sb->s_blocksize - 1;
5062306a36Sopenharmony_ci	remain = sb->s_blocksize - offset;
5162306a36Sopenharmony_ci	if (index + (remain < len) >= dir->nr_buffers)
5262306a36Sopenharmony_ci		return -EINVAL;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (remain < len) {
5562306a36Sopenharmony_ci		memcpy(dir->bhs[index]->b_data + offset, src, remain);
5662306a36Sopenharmony_ci		src += remain;
5762306a36Sopenharmony_ci		len -= remain;
5862306a36Sopenharmony_ci		index += 1;
5962306a36Sopenharmony_ci		offset = 0;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	memcpy(dir->bhs[index]->b_data + offset, src, len);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void __adfs_dir_cleanup(struct adfs_dir *dir)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	dir->nr_buffers = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (dir->bhs != dir->bh)
7262306a36Sopenharmony_ci		kfree(dir->bhs);
7362306a36Sopenharmony_ci	dir->bhs = NULL;
7462306a36Sopenharmony_ci	dir->sb = NULL;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_civoid adfs_dir_relse(struct adfs_dir *dir)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	unsigned int i;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < dir->nr_buffers; i++)
8262306a36Sopenharmony_ci		brelse(dir->bhs[i]);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	__adfs_dir_cleanup(dir);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void adfs_dir_forget(struct adfs_dir *dir)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	unsigned int i;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	for (i = 0; i < dir->nr_buffers; i++)
9262306a36Sopenharmony_ci		bforget(dir->bhs[i]);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	__adfs_dir_cleanup(dir);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
9862306a36Sopenharmony_ci			  unsigned int size, struct adfs_dir *dir)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct buffer_head **bhs;
10162306a36Sopenharmony_ci	unsigned int i, num;
10262306a36Sopenharmony_ci	int block;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
10562306a36Sopenharmony_ci	if (num > ARRAY_SIZE(dir->bh)) {
10662306a36Sopenharmony_ci		/* We only allow one extension */
10762306a36Sopenharmony_ci		if (dir->bhs != dir->bh)
10862306a36Sopenharmony_ci			return -EINVAL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
11162306a36Sopenharmony_ci		if (!bhs)
11262306a36Sopenharmony_ci			return -ENOMEM;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (dir->nr_buffers)
11562306a36Sopenharmony_ci			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		dir->bhs = bhs;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	for (i = dir->nr_buffers; i < num; i++) {
12162306a36Sopenharmony_ci		block = __adfs_block_map(sb, indaddr, i);
12262306a36Sopenharmony_ci		if (!block) {
12362306a36Sopenharmony_ci			adfs_error(sb, "dir %06x has a hole at offset %u",
12462306a36Sopenharmony_ci				   indaddr, i);
12562306a36Sopenharmony_ci			goto error;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		dir->bhs[i] = sb_bread(sb, block);
12962306a36Sopenharmony_ci		if (!dir->bhs[i]) {
13062306a36Sopenharmony_ci			adfs_error(sb,
13162306a36Sopenharmony_ci				   "dir %06x failed read at offset %u, mapped block 0x%08x",
13262306a36Sopenharmony_ci				   indaddr, i, block);
13362306a36Sopenharmony_ci			goto error;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		dir->nr_buffers++;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cierror:
14162306a36Sopenharmony_ci	adfs_dir_relse(dir);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return -EIO;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int adfs_dir_read(struct super_block *sb, u32 indaddr,
14762306a36Sopenharmony_ci			 unsigned int size, struct adfs_dir *dir)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	dir->sb = sb;
15062306a36Sopenharmony_ci	dir->bhs = dir->bh;
15162306a36Sopenharmony_ci	dir->nr_buffers = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
15762306a36Sopenharmony_ci			       struct adfs_dir *dir)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	int ret;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
16262306a36Sopenharmony_ci	if (ret)
16362306a36Sopenharmony_ci		return ret;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (ADFS_I(inode)->parent_id != dir->parent_id) {
16662306a36Sopenharmony_ci		adfs_error(sb,
16762306a36Sopenharmony_ci			   "parent directory id changed under me! (%06x but got %06x)\n",
16862306a36Sopenharmony_ci			   ADFS_I(inode)->parent_id, dir->parent_id);
16962306a36Sopenharmony_ci		adfs_dir_relse(dir);
17062306a36Sopenharmony_ci		ret = -EIO;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return ret;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic void adfs_dir_mark_dirty(struct adfs_dir *dir)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	unsigned int i;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Mark the buffers dirty */
18162306a36Sopenharmony_ci	for (i = 0; i < dir->nr_buffers; i++)
18262306a36Sopenharmony_ci		mark_buffer_dirty(dir->bhs[i]);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int adfs_dir_sync(struct adfs_dir *dir)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	int err = 0;
18862306a36Sopenharmony_ci	int i;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	for (i = dir->nr_buffers - 1; i >= 0; i--) {
19162306a36Sopenharmony_ci		struct buffer_head *bh = dir->bhs[i];
19262306a36Sopenharmony_ci		sync_dirty_buffer(bh);
19362306a36Sopenharmony_ci		if (buffer_req(bh) && !buffer_uptodate(bh))
19462306a36Sopenharmony_ci			err = -EIO;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return err;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_civoid adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	unsigned int dots, i;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/*
20562306a36Sopenharmony_ci	 * RISC OS allows the use of '/' in directory entry names, so we need
20662306a36Sopenharmony_ci	 * to fix these up.  '/' is typically used for FAT compatibility to
20762306a36Sopenharmony_ci	 * represent '.', so do the same conversion here.  In any case, '.'
20862306a36Sopenharmony_ci	 * will never be in a RISC OS name since it is used as the pathname
20962306a36Sopenharmony_ci	 * separator.  Handle the case where we may generate a '.' or '..'
21062306a36Sopenharmony_ci	 * name, replacing the first character with '^' (the RISC OS "parent
21162306a36Sopenharmony_ci	 * directory" character.)
21262306a36Sopenharmony_ci	 */
21362306a36Sopenharmony_ci	for (i = dots = 0; i < obj->name_len; i++)
21462306a36Sopenharmony_ci		if (obj->name[i] == '/') {
21562306a36Sopenharmony_ci			obj->name[i] = '.';
21662306a36Sopenharmony_ci			dots++;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (obj->name_len <= 2 && dots == obj->name_len)
22062306a36Sopenharmony_ci		obj->name[0] = '^';
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/*
22362306a36Sopenharmony_ci	 * If the object is a file, and the user requested the ,xyz hex
22462306a36Sopenharmony_ci	 * filetype suffix to the name, check the filetype and append.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
22762306a36Sopenharmony_ci		u16 filetype = adfs_filetype(obj->loadaddr);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if (filetype != ADFS_FILETYPE_NONE) {
23062306a36Sopenharmony_ci			obj->name[obj->name_len++] = ',';
23162306a36Sopenharmony_ci			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
23262306a36Sopenharmony_ci			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
23362306a36Sopenharmony_ci			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int adfs_iterate(struct file *file, struct dir_context *ctx)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
24162306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
24262306a36Sopenharmony_ci	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
24362306a36Sopenharmony_ci	struct adfs_dir dir;
24462306a36Sopenharmony_ci	int ret;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	down_read(&adfs_dir_rwsem);
24762306a36Sopenharmony_ci	ret = adfs_dir_read_inode(sb, inode, &dir);
24862306a36Sopenharmony_ci	if (ret)
24962306a36Sopenharmony_ci		goto unlock;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (ctx->pos == 0) {
25262306a36Sopenharmony_ci		if (!dir_emit_dot(file, ctx))
25362306a36Sopenharmony_ci			goto unlock_relse;
25462306a36Sopenharmony_ci		ctx->pos = 1;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	if (ctx->pos == 1) {
25762306a36Sopenharmony_ci		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
25862306a36Sopenharmony_ci			goto unlock_relse;
25962306a36Sopenharmony_ci		ctx->pos = 2;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = ops->iterate(&dir, ctx);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ciunlock_relse:
26562306a36Sopenharmony_ci	up_read(&adfs_dir_rwsem);
26662306a36Sopenharmony_ci	adfs_dir_relse(&dir);
26762306a36Sopenharmony_ci	return ret;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ciunlock:
27062306a36Sopenharmony_ci	up_read(&adfs_dir_rwsem);
27162306a36Sopenharmony_ci	return ret;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ciint
27562306a36Sopenharmony_ciadfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
27862306a36Sopenharmony_ci	struct adfs_dir dir;
27962306a36Sopenharmony_ci	int ret;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (!ops->update)
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	down_write(&adfs_dir_rwsem);
28862306a36Sopenharmony_ci	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
28962306a36Sopenharmony_ci	if (ret)
29062306a36Sopenharmony_ci		goto unlock;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ret = ops->update(&dir, obj);
29362306a36Sopenharmony_ci	if (ret)
29462306a36Sopenharmony_ci		goto forget;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = ops->commit(&dir);
29762306a36Sopenharmony_ci	if (ret)
29862306a36Sopenharmony_ci		goto forget;
29962306a36Sopenharmony_ci	up_write(&adfs_dir_rwsem);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	adfs_dir_mark_dirty(&dir);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (wait)
30462306a36Sopenharmony_ci		ret = adfs_dir_sync(&dir);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	adfs_dir_relse(&dir);
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * If the updated failed because the entry wasn't found, we can
31162306a36Sopenharmony_ci	 * just release the buffers. If it was any other error, forget
31262306a36Sopenharmony_ci	 * the dirtied buffers so they aren't written back to the media.
31362306a36Sopenharmony_ci	 */
31462306a36Sopenharmony_ciforget:
31562306a36Sopenharmony_ci	if (ret == -ENOENT)
31662306a36Sopenharmony_ci		adfs_dir_relse(&dir);
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		adfs_dir_forget(&dir);
31962306a36Sopenharmony_ciunlock:
32062306a36Sopenharmony_ci	up_write(&adfs_dir_rwsem);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return ret;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic unsigned char adfs_tolower(unsigned char c)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	if (c >= 'A' && c <= 'Z')
32862306a36Sopenharmony_ci		c += 'a' - 'A';
32962306a36Sopenharmony_ci	return c;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int __adfs_compare(const unsigned char *qstr, u32 qlen,
33362306a36Sopenharmony_ci			  const char *str, u32 len)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	u32 i;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (qlen != len)
33862306a36Sopenharmony_ci		return 1;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0; i < qlen; i++)
34162306a36Sopenharmony_ci		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
34262306a36Sopenharmony_ci			return 1;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
34862306a36Sopenharmony_ci				  struct object_info *obj)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
35162306a36Sopenharmony_ci	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
35262306a36Sopenharmony_ci	const unsigned char *name;
35362306a36Sopenharmony_ci	struct adfs_dir dir;
35462306a36Sopenharmony_ci	u32 name_len;
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	down_read(&adfs_dir_rwsem);
35862306a36Sopenharmony_ci	ret = adfs_dir_read_inode(sb, inode, &dir);
35962306a36Sopenharmony_ci	if (ret)
36062306a36Sopenharmony_ci		goto unlock;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ret = ops->setpos(&dir, 0);
36362306a36Sopenharmony_ci	if (ret)
36462306a36Sopenharmony_ci		goto unlock_relse;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	ret = -ENOENT;
36762306a36Sopenharmony_ci	name = qstr->name;
36862306a36Sopenharmony_ci	name_len = qstr->len;
36962306a36Sopenharmony_ci	while (ops->getnext(&dir, obj) == 0) {
37062306a36Sopenharmony_ci		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
37162306a36Sopenharmony_ci			ret = 0;
37262306a36Sopenharmony_ci			break;
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	obj->parent_id = ADFS_I(inode)->indaddr;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ciunlock_relse:
37862306a36Sopenharmony_ci	up_read(&adfs_dir_rwsem);
37962306a36Sopenharmony_ci	adfs_dir_relse(&dir);
38062306a36Sopenharmony_ci	return ret;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciunlock:
38362306a36Sopenharmony_ci	up_read(&adfs_dir_rwsem);
38462306a36Sopenharmony_ci	return ret;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ciconst struct file_operations adfs_dir_operations = {
38862306a36Sopenharmony_ci	.read		= generic_read_dir,
38962306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
39062306a36Sopenharmony_ci	.iterate_shared	= adfs_iterate,
39162306a36Sopenharmony_ci	.fsync		= generic_file_fsync,
39262306a36Sopenharmony_ci};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int
39562306a36Sopenharmony_ciadfs_hash(const struct dentry *parent, struct qstr *qstr)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	const unsigned char *name;
39862306a36Sopenharmony_ci	unsigned long hash;
39962306a36Sopenharmony_ci	u32 len;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
40262306a36Sopenharmony_ci		return -ENAMETOOLONG;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	len = qstr->len;
40562306a36Sopenharmony_ci	name = qstr->name;
40662306a36Sopenharmony_ci	hash = init_name_hash(parent);
40762306a36Sopenharmony_ci	while (len--)
40862306a36Sopenharmony_ci		hash = partial_name_hash(adfs_tolower(*name++), hash);
40962306a36Sopenharmony_ci	qstr->hash = end_name_hash(hash);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return 0;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/*
41562306a36Sopenharmony_ci * Compare two names, taking note of the name length
41662306a36Sopenharmony_ci * requirements of the underlying filesystem.
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_cistatic int adfs_compare(const struct dentry *dentry, unsigned int len,
41962306a36Sopenharmony_ci			const char *str, const struct qstr *qstr)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	return __adfs_compare(qstr->name, qstr->len, str, len);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ciconst struct dentry_operations adfs_dentry_operations = {
42562306a36Sopenharmony_ci	.d_hash		= adfs_hash,
42662306a36Sopenharmony_ci	.d_compare	= adfs_compare,
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic struct dentry *
43062306a36Sopenharmony_ciadfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct inode *inode = NULL;
43362306a36Sopenharmony_ci	struct object_info obj;
43462306a36Sopenharmony_ci	int error;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
43762306a36Sopenharmony_ci	if (error == 0) {
43862306a36Sopenharmony_ci		/*
43962306a36Sopenharmony_ci		 * This only returns NULL if get_empty_inode
44062306a36Sopenharmony_ci		 * fails.
44162306a36Sopenharmony_ci		 */
44262306a36Sopenharmony_ci		inode = adfs_iget(dir->i_sb, &obj);
44362306a36Sopenharmony_ci		if (!inode)
44462306a36Sopenharmony_ci			inode = ERR_PTR(-EACCES);
44562306a36Sopenharmony_ci	} else if (error != -ENOENT) {
44662306a36Sopenharmony_ci		inode = ERR_PTR(error);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	return d_splice_alias(inode, dentry);
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci/*
45262306a36Sopenharmony_ci * directories can handle most operations...
45362306a36Sopenharmony_ci */
45462306a36Sopenharmony_ciconst struct inode_operations adfs_dir_inode_operations = {
45562306a36Sopenharmony_ci	.lookup		= adfs_lookup,
45662306a36Sopenharmony_ci	.setattr	= adfs_notify_change,
45762306a36Sopenharmony_ci};
458