Root/
1 | /* |
2 | * linux/fs/affs/namei.c |
3 | * |
4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten |
5 | * |
6 | * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. |
7 | * |
8 | * (C) 1991 Linus Torvalds - minix filesystem |
9 | */ |
10 | |
11 | #include "affs.h" |
12 | |
13 | typedef int (*toupper_t)(int); |
14 | |
15 | static int affs_toupper(int ch); |
16 | static int affs_hash_dentry(struct dentry *, struct qstr *); |
17 | static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); |
18 | static int affs_intl_toupper(int ch); |
19 | static int affs_intl_hash_dentry(struct dentry *, struct qstr *); |
20 | static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *); |
21 | |
22 | const struct dentry_operations affs_dentry_operations = { |
23 | .d_hash = affs_hash_dentry, |
24 | .d_compare = affs_compare_dentry, |
25 | }; |
26 | |
27 | static const struct dentry_operations affs_intl_dentry_operations = { |
28 | .d_hash = affs_intl_hash_dentry, |
29 | .d_compare = affs_intl_compare_dentry, |
30 | }; |
31 | |
32 | |
33 | /* Simple toupper() for DOS\1 */ |
34 | |
35 | static int |
36 | affs_toupper(int ch) |
37 | { |
38 | return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch; |
39 | } |
40 | |
41 | /* International toupper() for DOS\3 ("international") */ |
42 | |
43 | static int |
44 | affs_intl_toupper(int ch) |
45 | { |
46 | return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0 |
47 | && ch <= 0xFE && ch != 0xF7) ? |
48 | ch - ('a' - 'A') : ch; |
49 | } |
50 | |
51 | static inline toupper_t |
52 | affs_get_toupper(struct super_block *sb) |
53 | { |
54 | return AFFS_SB(sb)->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper; |
55 | } |
56 | |
57 | /* |
58 | * Note: the dentry argument is the parent dentry. |
59 | */ |
60 | static inline int |
61 | __affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper) |
62 | { |
63 | const u8 *name = qstr->name; |
64 | unsigned long hash; |
65 | int i; |
66 | |
67 | i = affs_check_name(qstr->name,qstr->len); |
68 | if (i) |
69 | return i; |
70 | |
71 | hash = init_name_hash(); |
72 | i = min(qstr->len, 30u); |
73 | for (; i > 0; name++, i--) |
74 | hash = partial_name_hash(toupper(*name), hash); |
75 | qstr->hash = end_name_hash(hash); |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | static int |
81 | affs_hash_dentry(struct dentry *dentry, struct qstr *qstr) |
82 | { |
83 | return __affs_hash_dentry(dentry, qstr, affs_toupper); |
84 | } |
85 | static int |
86 | affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr) |
87 | { |
88 | return __affs_hash_dentry(dentry, qstr, affs_intl_toupper); |
89 | } |
90 | |
91 | static inline int |
92 | __affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper) |
93 | { |
94 | const u8 *aname = a->name; |
95 | const u8 *bname = b->name; |
96 | int len; |
97 | |
98 | /* 'a' is the qstr of an already existing dentry, so the name |
99 | * must be valid. 'b' must be validated first. |
100 | */ |
101 | |
102 | if (affs_check_name(b->name,b->len)) |
103 | return 1; |
104 | |
105 | /* If the names are longer than the allowed 30 chars, |
106 | * the excess is ignored, so their length may differ. |
107 | */ |
108 | len = a->len; |
109 | if (len >= 30) { |
110 | if (b->len < 30) |
111 | return 1; |
112 | len = 30; |
113 | } else if (len != b->len) |
114 | return 1; |
115 | |
116 | for (; len > 0; len--) |
117 | if (toupper(*aname++) != toupper(*bname++)) |
118 | return 1; |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static int |
124 | affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) |
125 | { |
126 | return __affs_compare_dentry(dentry, a, b, affs_toupper); |
127 | } |
128 | static int |
129 | affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) |
130 | { |
131 | return __affs_compare_dentry(dentry, a, b, affs_intl_toupper); |
132 | } |
133 | |
134 | /* |
135 | * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure. |
136 | */ |
137 | |
138 | static inline int |
139 | affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) |
140 | { |
141 | const u8 *name = dentry->d_name.name; |
142 | int len = dentry->d_name.len; |
143 | |
144 | if (len >= 30) { |
145 | if (*name2 < 30) |
146 | return 0; |
147 | len = 30; |
148 | } else if (len != *name2) |
149 | return 0; |
150 | |
151 | for (name2++; len > 0; len--) |
152 | if (toupper(*name++) != toupper(*name2++)) |
153 | return 0; |
154 | return 1; |
155 | } |
156 | |
157 | int |
158 | affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) |
159 | { |
160 | toupper_t toupper = affs_get_toupper(sb); |
161 | int hash; |
162 | |
163 | hash = len = min(len, 30u); |
164 | for (; len > 0; len--) |
165 | hash = (hash * 13 + toupper(*name++)) & 0x7ff; |
166 | |
167 | return hash % AFFS_SB(sb)->s_hashsize; |
168 | } |
169 | |
170 | static struct buffer_head * |
171 | affs_find_entry(struct inode *dir, struct dentry *dentry) |
172 | { |
173 | struct super_block *sb = dir->i_sb; |
174 | struct buffer_head *bh; |
175 | toupper_t toupper = affs_get_toupper(sb); |
176 | u32 key; |
177 | |
178 | pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name); |
179 | |
180 | bh = affs_bread(sb, dir->i_ino); |
181 | if (!bh) |
182 | return ERR_PTR(-EIO); |
183 | |
184 | key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]); |
185 | |
186 | for (;;) { |
187 | affs_brelse(bh); |
188 | if (key == 0) |
189 | return NULL; |
190 | bh = affs_bread(sb, key); |
191 | if (!bh) |
192 | return ERR_PTR(-EIO); |
193 | if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper)) |
194 | return bh; |
195 | key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); |
196 | } |
197 | } |
198 | |
199 | struct dentry * |
200 | affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) |
201 | { |
202 | struct super_block *sb = dir->i_sb; |
203 | struct buffer_head *bh; |
204 | struct inode *inode = NULL; |
205 | |
206 | pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name); |
207 | |
208 | affs_lock_dir(dir); |
209 | bh = affs_find_entry(dir, dentry); |
210 | affs_unlock_dir(dir); |
211 | if (IS_ERR(bh)) |
212 | return ERR_CAST(bh); |
213 | if (bh) { |
214 | u32 ino = bh->b_blocknr; |
215 | |
216 | /* store the real header ino in d_fsdata for faster lookups */ |
217 | dentry->d_fsdata = (void *)(long)ino; |
218 | switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { |
219 | //link to dirs disabled |
220 | //case ST_LINKDIR: |
221 | case ST_LINKFILE: |
222 | ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); |
223 | } |
224 | affs_brelse(bh); |
225 | inode = affs_iget(sb, ino); |
226 | if (IS_ERR(inode)) |
227 | return ERR_PTR(PTR_ERR(inode)); |
228 | } |
229 | dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations; |
230 | d_add(dentry, inode); |
231 | return NULL; |
232 | } |
233 | |
234 | int |
235 | affs_unlink(struct inode *dir, struct dentry *dentry) |
236 | { |
237 | pr_debug("AFFS: unlink(dir=%d, %lu \"%.*s\")\n", (u32)dir->i_ino, |
238 | dentry->d_inode->i_ino, |
239 | (int)dentry->d_name.len, dentry->d_name.name); |
240 | |
241 | return affs_remove_header(dentry); |
242 | } |
243 | |
244 | int |
245 | affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) |
246 | { |
247 | struct super_block *sb = dir->i_sb; |
248 | struct inode *inode; |
249 | int error; |
250 | |
251 | pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len, |
252 | dentry->d_name.name,mode); |
253 | |
254 | inode = affs_new_inode(dir); |
255 | if (!inode) |
256 | return -ENOSPC; |
257 | |
258 | inode->i_mode = mode; |
259 | mode_to_prot(inode); |
260 | mark_inode_dirty(inode); |
261 | |
262 | inode->i_op = &affs_file_inode_operations; |
263 | inode->i_fop = &affs_file_operations; |
264 | inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; |
265 | error = affs_add_entry(dir, inode, dentry, ST_FILE); |
266 | if (error) { |
267 | inode->i_nlink = 0; |
268 | iput(inode); |
269 | return error; |
270 | } |
271 | return 0; |
272 | } |
273 | |
274 | int |
275 | affs_mkdir(struct inode *dir, struct dentry *dentry, int mode) |
276 | { |
277 | struct inode *inode; |
278 | int error; |
279 | |
280 | pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino, |
281 | (int)dentry->d_name.len,dentry->d_name.name,mode); |
282 | |
283 | inode = affs_new_inode(dir); |
284 | if (!inode) |
285 | return -ENOSPC; |
286 | |
287 | inode->i_mode = S_IFDIR | mode; |
288 | mode_to_prot(inode); |
289 | |
290 | inode->i_op = &affs_dir_inode_operations; |
291 | inode->i_fop = &affs_dir_operations; |
292 | |
293 | error = affs_add_entry(dir, inode, dentry, ST_USERDIR); |
294 | if (error) { |
295 | inode->i_nlink = 0; |
296 | mark_inode_dirty(inode); |
297 | iput(inode); |
298 | return error; |
299 | } |
300 | return 0; |
301 | } |
302 | |
303 | int |
304 | affs_rmdir(struct inode *dir, struct dentry *dentry) |
305 | { |
306 | pr_debug("AFFS: rmdir(dir=%u, %lu \"%.*s\")\n", (u32)dir->i_ino, |
307 | dentry->d_inode->i_ino, |
308 | (int)dentry->d_name.len, dentry->d_name.name); |
309 | |
310 | return affs_remove_header(dentry); |
311 | } |
312 | |
313 | int |
314 | affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) |
315 | { |
316 | struct super_block *sb = dir->i_sb; |
317 | struct buffer_head *bh; |
318 | struct inode *inode; |
319 | char *p; |
320 | int i, maxlen, error; |
321 | char c, lc; |
322 | |
323 | pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, |
324 | (int)dentry->d_name.len,dentry->d_name.name,symname); |
325 | |
326 | maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; |
327 | inode = affs_new_inode(dir); |
328 | if (!inode) |
329 | return -ENOSPC; |
330 | |
331 | inode->i_op = &affs_symlink_inode_operations; |
332 | inode->i_data.a_ops = &affs_symlink_aops; |
333 | inode->i_mode = S_IFLNK | 0777; |
334 | mode_to_prot(inode); |
335 | |
336 | error = -EIO; |
337 | bh = affs_bread(sb, inode->i_ino); |
338 | if (!bh) |
339 | goto err; |
340 | i = 0; |
341 | p = (char *)AFFS_HEAD(bh)->table; |
342 | lc = '/'; |
343 | if (*symname == '/') { |
344 | while (*symname == '/') |
345 | symname++; |
346 | while (AFFS_SB(sb)->s_volume[i]) /* Cannot overflow */ |
347 | *p++ = AFFS_SB(sb)->s_volume[i++]; |
348 | } |
349 | while (i < maxlen && (c = *symname++)) { |
350 | if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { |
351 | *p++ = '/'; |
352 | i++; |
353 | symname += 2; |
354 | lc = '/'; |
355 | } else if (c == '.' && lc == '/' && *symname == '/') { |
356 | symname++; |
357 | lc = '/'; |
358 | } else { |
359 | *p++ = c; |
360 | lc = c; |
361 | i++; |
362 | } |
363 | if (lc == '/') |
364 | while (*symname == '/') |
365 | symname++; |
366 | } |
367 | *p = 0; |
368 | mark_buffer_dirty_inode(bh, inode); |
369 | affs_brelse(bh); |
370 | mark_inode_dirty(inode); |
371 | |
372 | error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); |
373 | if (error) |
374 | goto err; |
375 | |
376 | return 0; |
377 | |
378 | err: |
379 | inode->i_nlink = 0; |
380 | mark_inode_dirty(inode); |
381 | iput(inode); |
382 | return error; |
383 | } |
384 | |
385 | int |
386 | affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) |
387 | { |
388 | struct inode *inode = old_dentry->d_inode; |
389 | |
390 | pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino, |
391 | (int)dentry->d_name.len,dentry->d_name.name); |
392 | |
393 | return affs_add_entry(dir, inode, dentry, ST_LINKFILE); |
394 | } |
395 | |
396 | int |
397 | affs_rename(struct inode *old_dir, struct dentry *old_dentry, |
398 | struct inode *new_dir, struct dentry *new_dentry) |
399 | { |
400 | struct super_block *sb = old_dir->i_sb; |
401 | struct buffer_head *bh = NULL; |
402 | int retval; |
403 | |
404 | pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n", |
405 | (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, |
406 | (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); |
407 | |
408 | retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len); |
409 | if (retval) |
410 | return retval; |
411 | |
412 | /* Unlink destination if it already exists */ |
413 | if (new_dentry->d_inode) { |
414 | retval = affs_remove_header(new_dentry); |
415 | if (retval) |
416 | return retval; |
417 | } |
418 | |
419 | bh = affs_bread(sb, old_dentry->d_inode->i_ino); |
420 | if (!bh) |
421 | return -EIO; |
422 | |
423 | /* Remove header from its parent directory. */ |
424 | affs_lock_dir(old_dir); |
425 | retval = affs_remove_hash(old_dir, bh); |
426 | affs_unlock_dir(old_dir); |
427 | if (retval) |
428 | goto done; |
429 | |
430 | /* And insert it into the new directory with the new name. */ |
431 | affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); |
432 | affs_fix_checksum(sb, bh); |
433 | affs_lock_dir(new_dir); |
434 | retval = affs_insert_hash(new_dir, bh); |
435 | affs_unlock_dir(new_dir); |
436 | /* TODO: move it back to old_dir, if error? */ |
437 | |
438 | done: |
439 | mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); |
440 | affs_brelse(bh); |
441 | return retval; |
442 | } |
443 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9