xref: /OK3568_Linux_fs/kernel/fs/adfs/dir.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  linux/fs/adfs/dir.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 1999-2000 Russell King
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  Common directory handling for ADFS
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun #include <linux/slab.h>
10*4882a593Smuzhiyun #include "adfs.h"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun /*
13*4882a593Smuzhiyun  * For future.  This should probably be per-directory.
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun static DECLARE_RWSEM(adfs_dir_rwsem);
16*4882a593Smuzhiyun 
adfs_dir_copyfrom(void * dst,struct adfs_dir * dir,unsigned int offset,size_t len)17*4882a593Smuzhiyun int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
18*4882a593Smuzhiyun 		      size_t len)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun 	struct super_block *sb = dir->sb;
21*4882a593Smuzhiyun 	unsigned int index, remain;
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun 	index = offset >> sb->s_blocksize_bits;
24*4882a593Smuzhiyun 	offset &= sb->s_blocksize - 1;
25*4882a593Smuzhiyun 	remain = sb->s_blocksize - offset;
26*4882a593Smuzhiyun 	if (index + (remain < len) >= dir->nr_buffers)
27*4882a593Smuzhiyun 		return -EINVAL;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	if (remain < len) {
30*4882a593Smuzhiyun 		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
31*4882a593Smuzhiyun 		dst += remain;
32*4882a593Smuzhiyun 		len -= remain;
33*4882a593Smuzhiyun 		index += 1;
34*4882a593Smuzhiyun 		offset = 0;
35*4882a593Smuzhiyun 	}
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	memcpy(dst, dir->bhs[index]->b_data + offset, len);
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	return 0;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun 
adfs_dir_copyto(struct adfs_dir * dir,unsigned int offset,const void * src,size_t len)42*4882a593Smuzhiyun int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
43*4882a593Smuzhiyun 		    size_t len)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	struct super_block *sb = dir->sb;
46*4882a593Smuzhiyun 	unsigned int index, remain;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	index = offset >> sb->s_blocksize_bits;
49*4882a593Smuzhiyun 	offset &= sb->s_blocksize - 1;
50*4882a593Smuzhiyun 	remain = sb->s_blocksize - offset;
51*4882a593Smuzhiyun 	if (index + (remain < len) >= dir->nr_buffers)
52*4882a593Smuzhiyun 		return -EINVAL;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	if (remain < len) {
55*4882a593Smuzhiyun 		memcpy(dir->bhs[index]->b_data + offset, src, remain);
56*4882a593Smuzhiyun 		src += remain;
57*4882a593Smuzhiyun 		len -= remain;
58*4882a593Smuzhiyun 		index += 1;
59*4882a593Smuzhiyun 		offset = 0;
60*4882a593Smuzhiyun 	}
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	memcpy(dir->bhs[index]->b_data + offset, src, len);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	return 0;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
__adfs_dir_cleanup(struct adfs_dir * dir)67*4882a593Smuzhiyun static void __adfs_dir_cleanup(struct adfs_dir *dir)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	dir->nr_buffers = 0;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	if (dir->bhs != dir->bh)
72*4882a593Smuzhiyun 		kfree(dir->bhs);
73*4882a593Smuzhiyun 	dir->bhs = NULL;
74*4882a593Smuzhiyun 	dir->sb = NULL;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
adfs_dir_relse(struct adfs_dir * dir)77*4882a593Smuzhiyun void adfs_dir_relse(struct adfs_dir *dir)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	unsigned int i;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	for (i = 0; i < dir->nr_buffers; i++)
82*4882a593Smuzhiyun 		brelse(dir->bhs[i]);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	__adfs_dir_cleanup(dir);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
adfs_dir_forget(struct adfs_dir * dir)87*4882a593Smuzhiyun static void adfs_dir_forget(struct adfs_dir *dir)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	unsigned int i;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	for (i = 0; i < dir->nr_buffers; i++)
92*4882a593Smuzhiyun 		bforget(dir->bhs[i]);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	__adfs_dir_cleanup(dir);
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
adfs_dir_read_buffers(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)97*4882a593Smuzhiyun int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
98*4882a593Smuzhiyun 			  unsigned int size, struct adfs_dir *dir)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	struct buffer_head **bhs;
101*4882a593Smuzhiyun 	unsigned int i, num;
102*4882a593Smuzhiyun 	int block;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
105*4882a593Smuzhiyun 	if (num > ARRAY_SIZE(dir->bh)) {
106*4882a593Smuzhiyun 		/* We only allow one extension */
107*4882a593Smuzhiyun 		if (dir->bhs != dir->bh)
108*4882a593Smuzhiyun 			return -EINVAL;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
111*4882a593Smuzhiyun 		if (!bhs)
112*4882a593Smuzhiyun 			return -ENOMEM;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 		if (dir->nr_buffers)
115*4882a593Smuzhiyun 			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 		dir->bhs = bhs;
118*4882a593Smuzhiyun 	}
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	for (i = dir->nr_buffers; i < num; i++) {
121*4882a593Smuzhiyun 		block = __adfs_block_map(sb, indaddr, i);
122*4882a593Smuzhiyun 		if (!block) {
123*4882a593Smuzhiyun 			adfs_error(sb, "dir %06x has a hole at offset %u",
124*4882a593Smuzhiyun 				   indaddr, i);
125*4882a593Smuzhiyun 			goto error;
126*4882a593Smuzhiyun 		}
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 		dir->bhs[i] = sb_bread(sb, block);
129*4882a593Smuzhiyun 		if (!dir->bhs[i]) {
130*4882a593Smuzhiyun 			adfs_error(sb,
131*4882a593Smuzhiyun 				   "dir %06x failed read at offset %u, mapped block 0x%08x",
132*4882a593Smuzhiyun 				   indaddr, i, block);
133*4882a593Smuzhiyun 			goto error;
134*4882a593Smuzhiyun 		}
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 		dir->nr_buffers++;
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun 	return 0;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun error:
141*4882a593Smuzhiyun 	adfs_dir_relse(dir);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	return -EIO;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
adfs_dir_read(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)146*4882a593Smuzhiyun static int adfs_dir_read(struct super_block *sb, u32 indaddr,
147*4882a593Smuzhiyun 			 unsigned int size, struct adfs_dir *dir)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	dir->sb = sb;
150*4882a593Smuzhiyun 	dir->bhs = dir->bh;
151*4882a593Smuzhiyun 	dir->nr_buffers = 0;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
adfs_dir_read_inode(struct super_block * sb,struct inode * inode,struct adfs_dir * dir)156*4882a593Smuzhiyun static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
157*4882a593Smuzhiyun 			       struct adfs_dir *dir)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 	int ret;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
162*4882a593Smuzhiyun 	if (ret)
163*4882a593Smuzhiyun 		return ret;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	if (ADFS_I(inode)->parent_id != dir->parent_id) {
166*4882a593Smuzhiyun 		adfs_error(sb,
167*4882a593Smuzhiyun 			   "parent directory id changed under me! (%06x but got %06x)\n",
168*4882a593Smuzhiyun 			   ADFS_I(inode)->parent_id, dir->parent_id);
169*4882a593Smuzhiyun 		adfs_dir_relse(dir);
170*4882a593Smuzhiyun 		ret = -EIO;
171*4882a593Smuzhiyun 	}
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	return ret;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
adfs_dir_mark_dirty(struct adfs_dir * dir)176*4882a593Smuzhiyun static void adfs_dir_mark_dirty(struct adfs_dir *dir)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	unsigned int i;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	/* Mark the buffers dirty */
181*4882a593Smuzhiyun 	for (i = 0; i < dir->nr_buffers; i++)
182*4882a593Smuzhiyun 		mark_buffer_dirty(dir->bhs[i]);
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
adfs_dir_sync(struct adfs_dir * dir)185*4882a593Smuzhiyun static int adfs_dir_sync(struct adfs_dir *dir)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	int err = 0;
188*4882a593Smuzhiyun 	int i;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	for (i = dir->nr_buffers - 1; i >= 0; i--) {
191*4882a593Smuzhiyun 		struct buffer_head *bh = dir->bhs[i];
192*4882a593Smuzhiyun 		sync_dirty_buffer(bh);
193*4882a593Smuzhiyun 		if (buffer_req(bh) && !buffer_uptodate(bh))
194*4882a593Smuzhiyun 			err = -EIO;
195*4882a593Smuzhiyun 	}
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	return err;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun 
adfs_object_fixup(struct adfs_dir * dir,struct object_info * obj)200*4882a593Smuzhiyun void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun 	unsigned int dots, i;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	/*
205*4882a593Smuzhiyun 	 * RISC OS allows the use of '/' in directory entry names, so we need
206*4882a593Smuzhiyun 	 * to fix these up.  '/' is typically used for FAT compatibility to
207*4882a593Smuzhiyun 	 * represent '.', so do the same conversion here.  In any case, '.'
208*4882a593Smuzhiyun 	 * will never be in a RISC OS name since it is used as the pathname
209*4882a593Smuzhiyun 	 * separator.  Handle the case where we may generate a '.' or '..'
210*4882a593Smuzhiyun 	 * name, replacing the first character with '^' (the RISC OS "parent
211*4882a593Smuzhiyun 	 * directory" character.)
212*4882a593Smuzhiyun 	 */
213*4882a593Smuzhiyun 	for (i = dots = 0; i < obj->name_len; i++)
214*4882a593Smuzhiyun 		if (obj->name[i] == '/') {
215*4882a593Smuzhiyun 			obj->name[i] = '.';
216*4882a593Smuzhiyun 			dots++;
217*4882a593Smuzhiyun 		}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	if (obj->name_len <= 2 && dots == obj->name_len)
220*4882a593Smuzhiyun 		obj->name[0] = '^';
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	/*
223*4882a593Smuzhiyun 	 * If the object is a file, and the user requested the ,xyz hex
224*4882a593Smuzhiyun 	 * filetype suffix to the name, check the filetype and append.
225*4882a593Smuzhiyun 	 */
226*4882a593Smuzhiyun 	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
227*4882a593Smuzhiyun 		u16 filetype = adfs_filetype(obj->loadaddr);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 		if (filetype != ADFS_FILETYPE_NONE) {
230*4882a593Smuzhiyun 			obj->name[obj->name_len++] = ',';
231*4882a593Smuzhiyun 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
232*4882a593Smuzhiyun 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
233*4882a593Smuzhiyun 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
234*4882a593Smuzhiyun 		}
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
adfs_iterate(struct file * file,struct dir_context * ctx)238*4882a593Smuzhiyun static int adfs_iterate(struct file *file, struct dir_context *ctx)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	struct inode *inode = file_inode(file);
241*4882a593Smuzhiyun 	struct super_block *sb = inode->i_sb;
242*4882a593Smuzhiyun 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
243*4882a593Smuzhiyun 	struct adfs_dir dir;
244*4882a593Smuzhiyun 	int ret;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	down_read(&adfs_dir_rwsem);
247*4882a593Smuzhiyun 	ret = adfs_dir_read_inode(sb, inode, &dir);
248*4882a593Smuzhiyun 	if (ret)
249*4882a593Smuzhiyun 		goto unlock;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	if (ctx->pos == 0) {
252*4882a593Smuzhiyun 		if (!dir_emit_dot(file, ctx))
253*4882a593Smuzhiyun 			goto unlock_relse;
254*4882a593Smuzhiyun 		ctx->pos = 1;
255*4882a593Smuzhiyun 	}
256*4882a593Smuzhiyun 	if (ctx->pos == 1) {
257*4882a593Smuzhiyun 		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
258*4882a593Smuzhiyun 			goto unlock_relse;
259*4882a593Smuzhiyun 		ctx->pos = 2;
260*4882a593Smuzhiyun 	}
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	ret = ops->iterate(&dir, ctx);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun unlock_relse:
265*4882a593Smuzhiyun 	up_read(&adfs_dir_rwsem);
266*4882a593Smuzhiyun 	adfs_dir_relse(&dir);
267*4882a593Smuzhiyun 	return ret;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun unlock:
270*4882a593Smuzhiyun 	up_read(&adfs_dir_rwsem);
271*4882a593Smuzhiyun 	return ret;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun int
adfs_dir_update(struct super_block * sb,struct object_info * obj,int wait)275*4882a593Smuzhiyun adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
278*4882a593Smuzhiyun 	struct adfs_dir dir;
279*4882a593Smuzhiyun 	int ret;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
282*4882a593Smuzhiyun 		return -EINVAL;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	if (!ops->update)
285*4882a593Smuzhiyun 		return -EINVAL;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	down_write(&adfs_dir_rwsem);
288*4882a593Smuzhiyun 	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
289*4882a593Smuzhiyun 	if (ret)
290*4882a593Smuzhiyun 		goto unlock;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	ret = ops->update(&dir, obj);
293*4882a593Smuzhiyun 	if (ret)
294*4882a593Smuzhiyun 		goto forget;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	ret = ops->commit(&dir);
297*4882a593Smuzhiyun 	if (ret)
298*4882a593Smuzhiyun 		goto forget;
299*4882a593Smuzhiyun 	up_write(&adfs_dir_rwsem);
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	adfs_dir_mark_dirty(&dir);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	if (wait)
304*4882a593Smuzhiyun 		ret = adfs_dir_sync(&dir);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	adfs_dir_relse(&dir);
307*4882a593Smuzhiyun 	return ret;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	/*
310*4882a593Smuzhiyun 	 * If the updated failed because the entry wasn't found, we can
311*4882a593Smuzhiyun 	 * just release the buffers. If it was any other error, forget
312*4882a593Smuzhiyun 	 * the dirtied buffers so they aren't written back to the media.
313*4882a593Smuzhiyun 	 */
314*4882a593Smuzhiyun forget:
315*4882a593Smuzhiyun 	if (ret == -ENOENT)
316*4882a593Smuzhiyun 		adfs_dir_relse(&dir);
317*4882a593Smuzhiyun 	else
318*4882a593Smuzhiyun 		adfs_dir_forget(&dir);
319*4882a593Smuzhiyun unlock:
320*4882a593Smuzhiyun 	up_write(&adfs_dir_rwsem);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return ret;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
adfs_tolower(unsigned char c)325*4882a593Smuzhiyun static unsigned char adfs_tolower(unsigned char c)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	if (c >= 'A' && c <= 'Z')
328*4882a593Smuzhiyun 		c += 'a' - 'A';
329*4882a593Smuzhiyun 	return c;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
__adfs_compare(const unsigned char * qstr,u32 qlen,const char * str,u32 len)332*4882a593Smuzhiyun static int __adfs_compare(const unsigned char *qstr, u32 qlen,
333*4882a593Smuzhiyun 			  const char *str, u32 len)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	u32 i;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (qlen != len)
338*4882a593Smuzhiyun 		return 1;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	for (i = 0; i < qlen; i++)
341*4882a593Smuzhiyun 		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
342*4882a593Smuzhiyun 			return 1;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	return 0;
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
adfs_dir_lookup_byname(struct inode * inode,const struct qstr * qstr,struct object_info * obj)347*4882a593Smuzhiyun static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
348*4882a593Smuzhiyun 				  struct object_info *obj)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun 	struct super_block *sb = inode->i_sb;
351*4882a593Smuzhiyun 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
352*4882a593Smuzhiyun 	const unsigned char *name;
353*4882a593Smuzhiyun 	struct adfs_dir dir;
354*4882a593Smuzhiyun 	u32 name_len;
355*4882a593Smuzhiyun 	int ret;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	down_read(&adfs_dir_rwsem);
358*4882a593Smuzhiyun 	ret = adfs_dir_read_inode(sb, inode, &dir);
359*4882a593Smuzhiyun 	if (ret)
360*4882a593Smuzhiyun 		goto unlock;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	ret = ops->setpos(&dir, 0);
363*4882a593Smuzhiyun 	if (ret)
364*4882a593Smuzhiyun 		goto unlock_relse;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	ret = -ENOENT;
367*4882a593Smuzhiyun 	name = qstr->name;
368*4882a593Smuzhiyun 	name_len = qstr->len;
369*4882a593Smuzhiyun 	while (ops->getnext(&dir, obj) == 0) {
370*4882a593Smuzhiyun 		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
371*4882a593Smuzhiyun 			ret = 0;
372*4882a593Smuzhiyun 			break;
373*4882a593Smuzhiyun 		}
374*4882a593Smuzhiyun 	}
375*4882a593Smuzhiyun 	obj->parent_id = ADFS_I(inode)->indaddr;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun unlock_relse:
378*4882a593Smuzhiyun 	up_read(&adfs_dir_rwsem);
379*4882a593Smuzhiyun 	adfs_dir_relse(&dir);
380*4882a593Smuzhiyun 	return ret;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun unlock:
383*4882a593Smuzhiyun 	up_read(&adfs_dir_rwsem);
384*4882a593Smuzhiyun 	return ret;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun const struct file_operations adfs_dir_operations = {
388*4882a593Smuzhiyun 	.read		= generic_read_dir,
389*4882a593Smuzhiyun 	.llseek		= generic_file_llseek,
390*4882a593Smuzhiyun 	.iterate_shared	= adfs_iterate,
391*4882a593Smuzhiyun 	.fsync		= generic_file_fsync,
392*4882a593Smuzhiyun };
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun static int
adfs_hash(const struct dentry * parent,struct qstr * qstr)395*4882a593Smuzhiyun adfs_hash(const struct dentry *parent, struct qstr *qstr)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	const unsigned char *name;
398*4882a593Smuzhiyun 	unsigned long hash;
399*4882a593Smuzhiyun 	u32 len;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
402*4882a593Smuzhiyun 		return -ENAMETOOLONG;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	len = qstr->len;
405*4882a593Smuzhiyun 	name = qstr->name;
406*4882a593Smuzhiyun 	hash = init_name_hash(parent);
407*4882a593Smuzhiyun 	while (len--)
408*4882a593Smuzhiyun 		hash = partial_name_hash(adfs_tolower(*name++), hash);
409*4882a593Smuzhiyun 	qstr->hash = end_name_hash(hash);
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	return 0;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun /*
415*4882a593Smuzhiyun  * Compare two names, taking note of the name length
416*4882a593Smuzhiyun  * requirements of the underlying filesystem.
417*4882a593Smuzhiyun  */
adfs_compare(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * qstr)418*4882a593Smuzhiyun static int adfs_compare(const struct dentry *dentry, unsigned int len,
419*4882a593Smuzhiyun 			const char *str, const struct qstr *qstr)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	return __adfs_compare(qstr->name, qstr->len, str, len);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun const struct dentry_operations adfs_dentry_operations = {
425*4882a593Smuzhiyun 	.d_hash		= adfs_hash,
426*4882a593Smuzhiyun 	.d_compare	= adfs_compare,
427*4882a593Smuzhiyun };
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun static struct dentry *
adfs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)430*4882a593Smuzhiyun adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun 	struct inode *inode = NULL;
433*4882a593Smuzhiyun 	struct object_info obj;
434*4882a593Smuzhiyun 	int error;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
437*4882a593Smuzhiyun 	if (error == 0) {
438*4882a593Smuzhiyun 		/*
439*4882a593Smuzhiyun 		 * This only returns NULL if get_empty_inode
440*4882a593Smuzhiyun 		 * fails.
441*4882a593Smuzhiyun 		 */
442*4882a593Smuzhiyun 		inode = adfs_iget(dir->i_sb, &obj);
443*4882a593Smuzhiyun 		if (!inode)
444*4882a593Smuzhiyun 			inode = ERR_PTR(-EACCES);
445*4882a593Smuzhiyun 	} else if (error != -ENOENT) {
446*4882a593Smuzhiyun 		inode = ERR_PTR(error);
447*4882a593Smuzhiyun 	}
448*4882a593Smuzhiyun 	return d_splice_alias(inode, dentry);
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun /*
452*4882a593Smuzhiyun  * directories can handle most operations...
453*4882a593Smuzhiyun  */
454*4882a593Smuzhiyun const struct inode_operations adfs_dir_inode_operations = {
455*4882a593Smuzhiyun 	.lookup		= adfs_lookup,
456*4882a593Smuzhiyun 	.setattr	= adfs_notify_change,
457*4882a593Smuzhiyun };
458