fs/adfs: remove truncated filename hashing
[linux-block.git] / fs / adfs / dir.c
CommitLineData
1da177e4
LT
1/*
2 * linux/fs/adfs/dir.c
3 *
4 * Copyright (C) 1999-2000 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Common directory handling for ADFS
11 */
1da177e4
LT
12#include "adfs.h"
13
14/*
15 * For future. This should probably be per-directory.
16 */
17static DEFINE_RWLOCK(adfs_dir_lock);
18
411c49bc
RK
19void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
20{
adb514a4
RK
21 unsigned int i;
22
23 /*
24 * RISC OS allows the use of '/' in directory entry names, so we need
25 * to fix these up. '/' is typically used for FAT compatibility to
26 * represent '.', so do the same conversion here. In any case, '.'
27 * will never be in a RISC OS name since it is used as the pathname
28 * separator.
29 */
30 for (i = 0; i < obj->name_len; i++)
31 if (obj->name[i] == '/')
32 obj->name[i] = '.';
33
411c49bc
RK
34 obj->filetype = -1;
35
36 /*
37 * object is a file and is filetyped and timestamped?
38 * RISC OS 12-bit filetype is stored in load_address[19:8]
39 */
40 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
41 (0xfff00000 == (0xfff00000 & obj->loadaddr))) {
42 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);
43
44 /* optionally append the ,xyz hex filetype suffix */
45 if (ADFS_SB(dir->sb)->s_ftsuffix)
46 obj->name_len +=
47 append_filetype_suffix(
48 &obj->name[obj->name_len],
49 obj->filetype);
50 }
51}
52
1da177e4 53static int
2638ffba 54adfs_readdir(struct file *file, struct dir_context *ctx)
1da177e4 55{
2638ffba 56 struct inode *inode = file_inode(file);
1da177e4 57 struct super_block *sb = inode->i_sb;
0125f504 58 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1da177e4
LT
59 struct object_info obj;
60 struct adfs_dir dir;
61 int ret = 0;
62
2638ffba
AV
63 if (ctx->pos >> 32)
64 return 0;
1da177e4
LT
65
66 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
67 if (ret)
2638ffba 68 return ret;
1da177e4 69
2638ffba
AV
70 if (ctx->pos == 0) {
71 if (!dir_emit_dot(file, ctx))
1da177e4 72 goto free_out;
2638ffba
AV
73 ctx->pos = 1;
74 }
75 if (ctx->pos == 1) {
76 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
1da177e4 77 goto free_out;
2638ffba 78 ctx->pos = 2;
1da177e4
LT
79 }
80
81 read_lock(&adfs_dir_lock);
82
2638ffba 83 ret = ops->setpos(&dir, ctx->pos - 2);
1da177e4
LT
84 if (ret)
85 goto unlock_out;
86 while (ops->getnext(&dir, &obj) == 0) {
2638ffba
AV
87 if (!dir_emit(ctx, obj.name, obj.name_len,
88 obj.file_id, DT_UNKNOWN))
89 break;
90 ctx->pos++;
1da177e4
LT
91 }
92
93unlock_out:
94 read_unlock(&adfs_dir_lock);
95
96free_out:
97 ops->free(&dir);
1da177e4
LT
98 return ret;
99}
100
101int
ffdc9064 102adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
1da177e4
LT
103{
104 int ret = -EINVAL;
105#ifdef CONFIG_ADFS_FS_RW
0125f504 106 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1da177e4
LT
107 struct adfs_dir dir;
108
109 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
110 obj->file_id, obj->parent_id);
111
112 if (!ops->update) {
113 ret = -EINVAL;
114 goto out;
115 }
116
117 ret = ops->read(sb, obj->parent_id, 0, &dir);
118 if (ret)
119 goto out;
120
121 write_lock(&adfs_dir_lock);
122 ret = ops->update(&dir, obj);
123 write_unlock(&adfs_dir_lock);
124
ffdc9064
AV
125 if (wait) {
126 int err = ops->sync(&dir);
127 if (!ret)
128 ret = err;
129 }
130
1da177e4
LT
131 ops->free(&dir);
132out:
133#endif
134 return ret;
135}
136
525715d0
RK
137static unsigned char adfs_tolower(unsigned char c)
138{
139 if (c >= 'A' && c <= 'Z')
140 c += 'a' - 'A';
141 return c;
142}
143
1e504cf8
RK
144static int __adfs_compare(const unsigned char *qstr, u32 qlen,
145 const char *str, u32 len)
1da177e4 146{
1e504cf8 147 u32 i;
1da177e4 148
1e504cf8
RK
149 if (qlen != len)
150 return 1;
1da177e4 151
525715d0
RK
152 for (i = 0; i < qlen; i++)
153 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
1e504cf8 154 return 1;
525715d0 155
1e504cf8 156 return 0;
1da177e4
LT
157}
158
1e504cf8
RK
159static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
160 struct object_info *obj)
1da177e4
LT
161{
162 struct super_block *sb = inode->i_sb;
0125f504 163 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1e504cf8 164 const unsigned char *name;
1da177e4 165 struct adfs_dir dir;
1e504cf8 166 u32 name_len;
1da177e4
LT
167 int ret;
168
169 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
170 if (ret)
171 goto out;
172
173 if (ADFS_I(inode)->parent_id != dir.parent_id) {
19bdd41a 174 adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n",
1da177e4
LT
175 ADFS_I(inode)->parent_id, dir.parent_id);
176 ret = -EIO;
177 goto free_out;
178 }
179
180 obj->parent_id = inode->i_ino;
181
1da177e4
LT
182 read_lock(&adfs_dir_lock);
183
184 ret = ops->setpos(&dir, 0);
185 if (ret)
186 goto unlock_out;
187
188 ret = -ENOENT;
1e504cf8
RK
189 name = qstr->name;
190 name_len = qstr->len;
1da177e4 191 while (ops->getnext(&dir, obj) == 0) {
1e504cf8 192 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
1da177e4
LT
193 ret = 0;
194 break;
195 }
196 }
197
198unlock_out:
199 read_unlock(&adfs_dir_lock);
200
201free_out:
202 ops->free(&dir);
203out:
204 return ret;
205}
206
4b6f5d20 207const struct file_operations adfs_dir_operations = {
1da177e4 208 .read = generic_read_dir,
59af1584 209 .llseek = generic_file_llseek,
2638ffba 210 .iterate = adfs_readdir,
1b061d92 211 .fsync = generic_file_fsync,
1da177e4
LT
212};
213
214static int
da53be12 215adfs_hash(const struct dentry *parent, struct qstr *qstr)
1da177e4 216{
1da177e4
LT
217 const unsigned char *name;
218 unsigned long hash;
2eb0684f 219 u32 len;
1da177e4 220
2eb0684f
RK
221 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
222 return -ENAMETOOLONG;
1da177e4 223
2eb0684f 224 len = qstr->len;
1da177e4 225 name = qstr->name;
8387ff25 226 hash = init_name_hash(parent);
2eb0684f 227 while (len--)
525715d0 228 hash = partial_name_hash(adfs_tolower(*name++), hash);
1da177e4
LT
229 qstr->hash = end_name_hash(hash);
230
231 return 0;
232}
233
234/*
235 * Compare two names, taking note of the name length
236 * requirements of the underlying filesystem.
237 */
1e504cf8
RK
238static int adfs_compare(const struct dentry *dentry, unsigned int len,
239 const char *str, const struct qstr *qstr)
1da177e4 240{
1e504cf8 241 return __adfs_compare(qstr->name, qstr->len, str, len);
1da177e4
LT
242}
243
e16404ed 244const struct dentry_operations adfs_dentry_operations = {
1da177e4
LT
245 .d_hash = adfs_hash,
246 .d_compare = adfs_compare,
247};
248
249static struct dentry *
00cd8dd3 250adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
1da177e4
LT
251{
252 struct inode *inode = NULL;
253 struct object_info obj;
254 int error;
255
1da177e4
LT
256 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
257 if (error == 0) {
1da177e4
LT
258 /*
259 * This only returns NULL if get_empty_inode
260 * fails.
261 */
262 inode = adfs_iget(dir->i_sb, &obj);
9a7dddca
AV
263 if (!inode)
264 inode = ERR_PTR(-EACCES);
265 } else if (error != -ENOENT) {
266 inode = ERR_PTR(error);
1da177e4 267 }
9a7dddca 268 return d_splice_alias(inode, dentry);
1da177e4
LT
269}
270
271/*
272 * directories can handle most operations...
273 */
754661f1 274const struct inode_operations adfs_dir_inode_operations = {
1da177e4
LT
275 .lookup = adfs_lookup,
276 .setattr = adfs_notify_change,
277};