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