xref: /openbmc/linux/fs/adfs/dir.c (revision 25e5d4df)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/adfs/dir.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1999-2000 Russell King
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Common directory handling for ADFS
81da177e4SLinus Torvalds  */
91dd9f5baSRussell King #include <linux/slab.h>
101da177e4SLinus Torvalds #include "adfs.h"
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds  * For future.  This should probably be per-directory.
141da177e4SLinus Torvalds  */
15deed1bfdSRussell King static DECLARE_RWSEM(adfs_dir_rwsem);
161da177e4SLinus Torvalds 
adfs_dir_copyfrom(void * dst,struct adfs_dir * dir,unsigned int offset,size_t len)17a317120bSRussell King int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
18a317120bSRussell King 		      size_t len)
19a317120bSRussell King {
20a317120bSRussell King 	struct super_block *sb = dir->sb;
21a317120bSRussell King 	unsigned int index, remain;
22a317120bSRussell King 
23a317120bSRussell King 	index = offset >> sb->s_blocksize_bits;
24a317120bSRussell King 	offset &= sb->s_blocksize - 1;
25a317120bSRussell King 	remain = sb->s_blocksize - offset;
26a317120bSRussell King 	if (index + (remain < len) >= dir->nr_buffers)
27a317120bSRussell King 		return -EINVAL;
28a317120bSRussell King 
29a317120bSRussell King 	if (remain < len) {
30a317120bSRussell King 		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
31a317120bSRussell King 		dst += remain;
32a317120bSRussell King 		len -= remain;
33a317120bSRussell King 		index += 1;
34a317120bSRussell King 		offset = 0;
35a317120bSRussell King 	}
36a317120bSRussell King 
37a317120bSRussell King 	memcpy(dst, dir->bhs[index]->b_data + offset, len);
38a317120bSRussell King 
39a317120bSRussell King 	return 0;
40a317120bSRussell King }
41a317120bSRussell King 
adfs_dir_copyto(struct adfs_dir * dir,unsigned int offset,const void * src,size_t len)42a317120bSRussell King int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
43a317120bSRussell King 		    size_t len)
44a317120bSRussell King {
45a317120bSRussell King 	struct super_block *sb = dir->sb;
46a317120bSRussell King 	unsigned int index, remain;
47a317120bSRussell King 
48a317120bSRussell King 	index = offset >> sb->s_blocksize_bits;
49a317120bSRussell King 	offset &= sb->s_blocksize - 1;
50a317120bSRussell King 	remain = sb->s_blocksize - offset;
51a317120bSRussell King 	if (index + (remain < len) >= dir->nr_buffers)
52a317120bSRussell King 		return -EINVAL;
53a317120bSRussell King 
54a317120bSRussell King 	if (remain < len) {
55a317120bSRussell King 		memcpy(dir->bhs[index]->b_data + offset, src, remain);
56a317120bSRussell King 		src += remain;
57a317120bSRussell King 		len -= remain;
58a317120bSRussell King 		index += 1;
59a317120bSRussell King 		offset = 0;
60a317120bSRussell King 	}
61a317120bSRussell King 
62a317120bSRussell King 	memcpy(dir->bhs[index]->b_data + offset, src, len);
63a317120bSRussell King 
64a317120bSRussell King 	return 0;
65a317120bSRussell King }
66a317120bSRussell King 
__adfs_dir_cleanup(struct adfs_dir * dir)67f6075c79SRussell King static void __adfs_dir_cleanup(struct adfs_dir *dir)
681dd9f5baSRussell King {
691dd9f5baSRussell King 	dir->nr_buffers = 0;
701dd9f5baSRussell King 
711dd9f5baSRussell King 	if (dir->bhs != dir->bh)
721dd9f5baSRussell King 		kfree(dir->bhs);
731dd9f5baSRussell King 	dir->bhs = NULL;
741dd9f5baSRussell King 	dir->sb = NULL;
751dd9f5baSRussell King }
761dd9f5baSRussell King 
adfs_dir_relse(struct adfs_dir * dir)77f6075c79SRussell King void adfs_dir_relse(struct adfs_dir *dir)
78f6075c79SRussell King {
79f6075c79SRussell King 	unsigned int i;
80f6075c79SRussell King 
81f6075c79SRussell King 	for (i = 0; i < dir->nr_buffers; i++)
82f6075c79SRussell King 		brelse(dir->bhs[i]);
83f6075c79SRussell King 
84f6075c79SRussell King 	__adfs_dir_cleanup(dir);
85f6075c79SRussell King }
86f6075c79SRussell King 
adfs_dir_forget(struct adfs_dir * dir)87f6075c79SRussell King static void adfs_dir_forget(struct adfs_dir *dir)
88f6075c79SRussell King {
89f6075c79SRussell King 	unsigned int i;
90f6075c79SRussell King 
91f6075c79SRussell King 	for (i = 0; i < dir->nr_buffers; i++)
92f6075c79SRussell King 		bforget(dir->bhs[i]);
93f6075c79SRussell King 
94f6075c79SRussell King 	__adfs_dir_cleanup(dir);
95f6075c79SRussell King }
96f6075c79SRussell King 
adfs_dir_read_buffers(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)97419a6e5eSRussell King int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
98419a6e5eSRussell King 			  unsigned int size, struct adfs_dir *dir)
99419a6e5eSRussell King {
100419a6e5eSRussell King 	struct buffer_head **bhs;
101419a6e5eSRussell King 	unsigned int i, num;
102419a6e5eSRussell King 	int block;
103419a6e5eSRussell King 
104419a6e5eSRussell King 	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
105419a6e5eSRussell King 	if (num > ARRAY_SIZE(dir->bh)) {
106419a6e5eSRussell King 		/* We only allow one extension */
107419a6e5eSRussell King 		if (dir->bhs != dir->bh)
108419a6e5eSRussell King 			return -EINVAL;
109419a6e5eSRussell King 
110419a6e5eSRussell King 		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
111419a6e5eSRussell King 		if (!bhs)
112419a6e5eSRussell King 			return -ENOMEM;
113419a6e5eSRussell King 
114419a6e5eSRussell King 		if (dir->nr_buffers)
115419a6e5eSRussell King 			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
116419a6e5eSRussell King 
117419a6e5eSRussell King 		dir->bhs = bhs;
118419a6e5eSRussell King 	}
119419a6e5eSRussell King 
120419a6e5eSRussell King 	for (i = dir->nr_buffers; i < num; i++) {
121419a6e5eSRussell King 		block = __adfs_block_map(sb, indaddr, i);
122419a6e5eSRussell King 		if (!block) {
123419a6e5eSRussell King 			adfs_error(sb, "dir %06x has a hole at offset %u",
124419a6e5eSRussell King 				   indaddr, i);
125419a6e5eSRussell King 			goto error;
126419a6e5eSRussell King 		}
127419a6e5eSRussell King 
128419a6e5eSRussell King 		dir->bhs[i] = sb_bread(sb, block);
129419a6e5eSRussell King 		if (!dir->bhs[i]) {
130419a6e5eSRussell King 			adfs_error(sb,
131419a6e5eSRussell King 				   "dir %06x failed read at offset %u, mapped block 0x%08x",
132419a6e5eSRussell King 				   indaddr, i, block);
133419a6e5eSRussell King 			goto error;
134419a6e5eSRussell King 		}
135419a6e5eSRussell King 
136419a6e5eSRussell King 		dir->nr_buffers++;
137419a6e5eSRussell King 	}
138419a6e5eSRussell King 	return 0;
139419a6e5eSRussell King 
140419a6e5eSRussell King error:
141419a6e5eSRussell King 	adfs_dir_relse(dir);
142419a6e5eSRussell King 
143419a6e5eSRussell King 	return -EIO;
144419a6e5eSRussell King }
145419a6e5eSRussell King 
adfs_dir_read(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)14695fbadbbSRussell King static int adfs_dir_read(struct super_block *sb, u32 indaddr,
14795fbadbbSRussell King 			 unsigned int size, struct adfs_dir *dir)
14895fbadbbSRussell King {
14995fbadbbSRussell King 	dir->sb = sb;
15095fbadbbSRussell King 	dir->bhs = dir->bh;
15195fbadbbSRussell King 	dir->nr_buffers = 0;
15295fbadbbSRussell King 
15395fbadbbSRussell King 	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
15495fbadbbSRussell King }
15595fbadbbSRussell King 
adfs_dir_read_inode(struct super_block * sb,struct inode * inode,struct adfs_dir * dir)15690011c7aSRussell King static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
15790011c7aSRussell King 			       struct adfs_dir *dir)
15890011c7aSRussell King {
15990011c7aSRussell King 	int ret;
16090011c7aSRussell King 
16125e5d4dfSRussell King 	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
16290011c7aSRussell King 	if (ret)
16390011c7aSRussell King 		return ret;
16490011c7aSRussell King 
16590011c7aSRussell King 	if (ADFS_I(inode)->parent_id != dir->parent_id) {
16690011c7aSRussell King 		adfs_error(sb,
16790011c7aSRussell King 			   "parent directory id changed under me! (%06x but got %06x)\n",
16890011c7aSRussell King 			   ADFS_I(inode)->parent_id, dir->parent_id);
16990011c7aSRussell King 		adfs_dir_relse(dir);
17090011c7aSRussell King 		ret = -EIO;
17190011c7aSRussell King 	}
17290011c7aSRussell King 
17390011c7aSRussell King 	return ret;
17490011c7aSRussell King }
17590011c7aSRussell King 
adfs_dir_mark_dirty(struct adfs_dir * dir)176c3c8149bSRussell King static void adfs_dir_mark_dirty(struct adfs_dir *dir)
177c3c8149bSRussell King {
178c3c8149bSRussell King 	unsigned int i;
179c3c8149bSRussell King 
180c3c8149bSRussell King 	/* Mark the buffers dirty */
181c3c8149bSRussell King 	for (i = 0; i < dir->nr_buffers; i++)
182c3c8149bSRussell King 		mark_buffer_dirty(dir->bhs[i]);
183c3c8149bSRussell King }
184c3c8149bSRussell King 
adfs_dir_sync(struct adfs_dir * dir)185acf5f0beSRussell King static int adfs_dir_sync(struct adfs_dir *dir)
186acf5f0beSRussell King {
187acf5f0beSRussell King 	int err = 0;
188acf5f0beSRussell King 	int i;
189acf5f0beSRussell King 
190acf5f0beSRussell King 	for (i = dir->nr_buffers - 1; i >= 0; i--) {
191acf5f0beSRussell King 		struct buffer_head *bh = dir->bhs[i];
192acf5f0beSRussell King 		sync_dirty_buffer(bh);
193acf5f0beSRussell King 		if (buffer_req(bh) && !buffer_uptodate(bh))
194acf5f0beSRussell King 			err = -EIO;
195acf5f0beSRussell King 	}
196acf5f0beSRussell King 
197acf5f0beSRussell King 	return err;
198acf5f0beSRussell King }
199acf5f0beSRussell King 
adfs_object_fixup(struct adfs_dir * dir,struct object_info * obj)200411c49bcSRussell King void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
201411c49bcSRussell King {
202fc722a04SRussell King 	unsigned int dots, i;
203adb514a4SRussell King 
204adb514a4SRussell King 	/*
205adb514a4SRussell King 	 * RISC OS allows the use of '/' in directory entry names, so we need
206adb514a4SRussell King 	 * to fix these up.  '/' is typically used for FAT compatibility to
207adb514a4SRussell King 	 * represent '.', so do the same conversion here.  In any case, '.'
208adb514a4SRussell King 	 * will never be in a RISC OS name since it is used as the pathname
209fc722a04SRussell King 	 * separator.  Handle the case where we may generate a '.' or '..'
210fc722a04SRussell King 	 * name, replacing the first character with '^' (the RISC OS "parent
211fc722a04SRussell King 	 * directory" character.)
212adb514a4SRussell King 	 */
213fc722a04SRussell King 	for (i = dots = 0; i < obj->name_len; i++)
214fc722a04SRussell King 		if (obj->name[i] == '/') {
215adb514a4SRussell King 			obj->name[i] = '.';
216fc722a04SRussell King 			dots++;
217fc722a04SRussell King 		}
218fc722a04SRussell King 
219fc722a04SRussell King 	if (obj->name_len <= 2 && dots == obj->name_len)
220fc722a04SRussell King 		obj->name[0] = '^';
221adb514a4SRussell King 
222411c49bcSRussell King 	/*
223b4ed8f75SRussell King 	 * If the object is a file, and the user requested the ,xyz hex
224b4ed8f75SRussell King 	 * filetype suffix to the name, check the filetype and append.
225411c49bcSRussell King 	 */
226b4ed8f75SRussell King 	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
227b4ed8f75SRussell King 		u16 filetype = adfs_filetype(obj->loadaddr);
228411c49bcSRussell King 
229b4ed8f75SRussell King 		if (filetype != ADFS_FILETYPE_NONE) {
2305f8de487SRussell King 			obj->name[obj->name_len++] = ',';
2315f8de487SRussell King 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
2325f8de487SRussell King 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
2335f8de487SRussell King 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
2345f8de487SRussell King 		}
235411c49bcSRussell King 	}
236411c49bcSRussell King }
237411c49bcSRussell King 
adfs_iterate(struct file * file,struct dir_context * ctx)238cdc46e99SRussell King static int adfs_iterate(struct file *file, struct dir_context *ctx)
2391da177e4SLinus Torvalds {
2402638ffbaSAl Viro 	struct inode *inode = file_inode(file);
2411da177e4SLinus Torvalds 	struct super_block *sb = inode->i_sb;
2420125f504SJulia Lawall 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
2431da177e4SLinus Torvalds 	struct adfs_dir dir;
2444287e4deSRussell King 	int ret;
2451da177e4SLinus Torvalds 
246deed1bfdSRussell King 	down_read(&adfs_dir_rwsem);
24790011c7aSRussell King 	ret = adfs_dir_read_inode(sb, inode, &dir);
2481da177e4SLinus Torvalds 	if (ret)
249deed1bfdSRussell King 		goto unlock;
2501da177e4SLinus Torvalds 
2512638ffbaSAl Viro 	if (ctx->pos == 0) {
2522638ffbaSAl Viro 		if (!dir_emit_dot(file, ctx))
253deed1bfdSRussell King 			goto unlock_relse;
2542638ffbaSAl Viro 		ctx->pos = 1;
2552638ffbaSAl Viro 	}
2562638ffbaSAl Viro 	if (ctx->pos == 1) {
2572638ffbaSAl Viro 		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
258deed1bfdSRussell King 			goto unlock_relse;
2592638ffbaSAl Viro 		ctx->pos = 2;
2601da177e4SLinus Torvalds 	}
2611da177e4SLinus Torvalds 
2624287e4deSRussell King 	ret = ops->iterate(&dir, ctx);
2631da177e4SLinus Torvalds 
264deed1bfdSRussell King unlock_relse:
265deed1bfdSRussell King 	up_read(&adfs_dir_rwsem);
2661dd9f5baSRussell King 	adfs_dir_relse(&dir);
2671da177e4SLinus Torvalds 	return ret;
268deed1bfdSRussell King 
269deed1bfdSRussell King unlock:
270deed1bfdSRussell King 	up_read(&adfs_dir_rwsem);
271deed1bfdSRussell King 	return ret;
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds int
adfs_dir_update(struct super_block * sb,struct object_info * obj,int wait)275ffdc9064SAl Viro adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
2761da177e4SLinus Torvalds {
2770125f504SJulia Lawall 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
2781da177e4SLinus Torvalds 	struct adfs_dir dir;
2794a0a88b6SRussell King 	int ret;
2801da177e4SLinus Torvalds 
2814a0a88b6SRussell King 	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
2824a0a88b6SRussell King 		return -EINVAL;
2834a0a88b6SRussell King 
284acf5f0beSRussell King 	if (!ops->update)
285acf5f0beSRussell King 		return -EINVAL;
2861da177e4SLinus Torvalds 
287deed1bfdSRussell King 	down_write(&adfs_dir_rwsem);
28895fbadbbSRussell King 	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
2891da177e4SLinus Torvalds 	if (ret)
290deed1bfdSRussell King 		goto unlock;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	ret = ops->update(&dir, obj);
293f6075c79SRussell King 	if (ret)
294f6075c79SRussell King 		goto forget;
295aacc954cSRussell King 
296aacc954cSRussell King 	ret = ops->commit(&dir);
297aacc954cSRussell King 	if (ret)
298aacc954cSRussell King 		goto forget;
299deed1bfdSRussell King 	up_write(&adfs_dir_rwsem);
3001da177e4SLinus Torvalds 
301c3c8149bSRussell King 	adfs_dir_mark_dirty(&dir);
302c3c8149bSRussell King 
303f6075c79SRussell King 	if (wait)
304f6075c79SRussell King 		ret = adfs_dir_sync(&dir);
305ffdc9064SAl Viro 
3061dd9f5baSRussell King 	adfs_dir_relse(&dir);
307deed1bfdSRussell King 	return ret;
308deed1bfdSRussell King 
309f6075c79SRussell King 	/*
310f6075c79SRussell King 	 * If the updated failed because the entry wasn't found, we can
311f6075c79SRussell King 	 * just release the buffers. If it was any other error, forget
312f6075c79SRussell King 	 * the dirtied buffers so they aren't written back to the media.
313f6075c79SRussell King 	 */
314f6075c79SRussell King forget:
315f6075c79SRussell King 	if (ret == -ENOENT)
316f6075c79SRussell King 		adfs_dir_relse(&dir);
317f6075c79SRussell King 	else
318f6075c79SRussell King 		adfs_dir_forget(&dir);
319deed1bfdSRussell King unlock:
320deed1bfdSRussell King 	up_write(&adfs_dir_rwsem);
3214a0a88b6SRussell King 
3221da177e4SLinus Torvalds 	return ret;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
adfs_tolower(unsigned char c)325525715d0SRussell King static unsigned char adfs_tolower(unsigned char c)
326525715d0SRussell King {
327525715d0SRussell King 	if (c >= 'A' && c <= 'Z')
328525715d0SRussell King 		c += 'a' - 'A';
329525715d0SRussell King 	return c;
330525715d0SRussell King }
331525715d0SRussell King 
__adfs_compare(const unsigned char * qstr,u32 qlen,const char * str,u32 len)3321e504cf8SRussell King static int __adfs_compare(const unsigned char *qstr, u32 qlen,
3331e504cf8SRussell King 			  const char *str, u32 len)
3341da177e4SLinus Torvalds {
3351e504cf8SRussell King 	u32 i;
3361da177e4SLinus Torvalds 
3371e504cf8SRussell King 	if (qlen != len)
3381e504cf8SRussell King 		return 1;
3391da177e4SLinus Torvalds 
340525715d0SRussell King 	for (i = 0; i < qlen; i++)
341525715d0SRussell King 		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
3421da177e4SLinus Torvalds 			return 1;
343525715d0SRussell King 
3441e504cf8SRussell King 	return 0;
3451e504cf8SRussell King }
3461da177e4SLinus Torvalds 
adfs_dir_lookup_byname(struct inode * inode,const struct qstr * qstr,struct object_info * obj)3471e504cf8SRussell King static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
3481e504cf8SRussell King 				  struct object_info *obj)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds 	struct super_block *sb = inode->i_sb;
3510125f504SJulia Lawall 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
3521e504cf8SRussell King 	const unsigned char *name;
3531da177e4SLinus Torvalds 	struct adfs_dir dir;
3541e504cf8SRussell King 	u32 name_len;
3551da177e4SLinus Torvalds 	int ret;
3561da177e4SLinus Torvalds 
357deed1bfdSRussell King 	down_read(&adfs_dir_rwsem);
35890011c7aSRussell King 	ret = adfs_dir_read_inode(sb, inode, &dir);
3591da177e4SLinus Torvalds 	if (ret)
360deed1bfdSRussell King 		goto unlock;
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	ret = ops->setpos(&dir, 0);
3631da177e4SLinus Torvalds 	if (ret)
364deed1bfdSRussell King 		goto unlock_relse;
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 	ret = -ENOENT;
3671e504cf8SRussell King 	name = qstr->name;
3681e504cf8SRussell King 	name_len = qstr->len;
3691da177e4SLinus Torvalds 	while (ops->getnext(&dir, obj) == 0) {
3701e504cf8SRussell King 		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
3711da177e4SLinus Torvalds 			ret = 0;
3721da177e4SLinus Torvalds 			break;
3731da177e4SLinus Torvalds 		}
3741da177e4SLinus Torvalds 	}
37525e5d4dfSRussell King 	obj->parent_id = ADFS_I(inode)->indaddr;
3761da177e4SLinus Torvalds 
377deed1bfdSRussell King unlock_relse:
378deed1bfdSRussell King 	up_read(&adfs_dir_rwsem);
3791dd9f5baSRussell King 	adfs_dir_relse(&dir);
380deed1bfdSRussell King 	return ret;
381deed1bfdSRussell King 
382deed1bfdSRussell King unlock:
383deed1bfdSRussell King 	up_read(&adfs_dir_rwsem);
3841da177e4SLinus Torvalds 	return ret;
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds 
3874b6f5d20SArjan van de Ven const struct file_operations adfs_dir_operations = {
3881da177e4SLinus Torvalds 	.read		= generic_read_dir,
38959af1584SAl Viro 	.llseek		= generic_file_llseek,
390cdc46e99SRussell King 	.iterate_shared	= adfs_iterate,
3911b061d92SChristoph Hellwig 	.fsync		= generic_file_fsync,
3921da177e4SLinus Torvalds };
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds static int
adfs_hash(const struct dentry * parent,struct qstr * qstr)395da53be12SLinus Torvalds adfs_hash(const struct dentry *parent, struct qstr *qstr)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds 	const unsigned char *name;
3981da177e4SLinus Torvalds 	unsigned long hash;
3992eb0684fSRussell King 	u32 len;
4001da177e4SLinus Torvalds 
4012eb0684fSRussell King 	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
4022eb0684fSRussell King 		return -ENAMETOOLONG;
4031da177e4SLinus Torvalds 
4042eb0684fSRussell King 	len = qstr->len;
4051da177e4SLinus Torvalds 	name = qstr->name;
4068387ff25SLinus Torvalds 	hash = init_name_hash(parent);
4072eb0684fSRussell King 	while (len--)
408525715d0SRussell King 		hash = partial_name_hash(adfs_tolower(*name++), hash);
4091da177e4SLinus Torvalds 	qstr->hash = end_name_hash(hash);
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	return 0;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds /*
4151da177e4SLinus Torvalds  * Compare two names, taking note of the name length
4161da177e4SLinus Torvalds  * requirements of the underlying filesystem.
4171da177e4SLinus Torvalds  */
adfs_compare(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * qstr)4181e504cf8SRussell King static int adfs_compare(const struct dentry *dentry, unsigned int len,
4191e504cf8SRussell King 			const char *str, const struct qstr *qstr)
4201da177e4SLinus Torvalds {
4211e504cf8SRussell King 	return __adfs_compare(qstr->name, qstr->len, str, len);
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds 
424e16404edSAl Viro const struct dentry_operations adfs_dentry_operations = {
4251da177e4SLinus Torvalds 	.d_hash		= adfs_hash,
4261da177e4SLinus Torvalds 	.d_compare	= adfs_compare,
4271da177e4SLinus Torvalds };
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds static struct dentry *
adfs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)43000cd8dd3SAl Viro adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
4311da177e4SLinus Torvalds {
4321da177e4SLinus Torvalds 	struct inode *inode = NULL;
4331da177e4SLinus Torvalds 	struct object_info obj;
4341da177e4SLinus Torvalds 	int error;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
4371da177e4SLinus Torvalds 	if (error == 0) {
4381da177e4SLinus Torvalds 		/*
4391da177e4SLinus Torvalds 		 * This only returns NULL if get_empty_inode
4401da177e4SLinus Torvalds 		 * fails.
4411da177e4SLinus Torvalds 		 */
4421da177e4SLinus Torvalds 		inode = adfs_iget(dir->i_sb, &obj);
4439a7dddcaSAl Viro 		if (!inode)
4449a7dddcaSAl Viro 			inode = ERR_PTR(-EACCES);
4459a7dddcaSAl Viro 	} else if (error != -ENOENT) {
4469a7dddcaSAl Viro 		inode = ERR_PTR(error);
4471da177e4SLinus Torvalds 	}
4489a7dddcaSAl Viro 	return d_splice_alias(inode, dentry);
4491da177e4SLinus Torvalds }
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds /*
4521da177e4SLinus Torvalds  * directories can handle most operations...
4531da177e4SLinus Torvalds  */
454754661f1SArjan van de Ven const struct inode_operations adfs_dir_inode_operations = {
4551da177e4SLinus Torvalds 	.lookup		= adfs_lookup,
4561da177e4SLinus Torvalds 	.setattr	= adfs_notify_change,
4571da177e4SLinus Torvalds };
458