]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | struct dentry_operations affs_dentry_operations = { | |
23 | .d_hash = affs_hash_dentry, | |
24 | .d_compare = affs_compare_dentry, | |
25 | }; | |
26 | ||
27 | static 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); | |
210f8559 | 211 | if (IS_ERR(bh)) |
e231c2ee | 212 | return ERR_CAST(bh); |
1da177e4 LT |
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); | |
210f8559 DH |
225 | inode = affs_iget(sb, ino); |
226 | if (IS_ERR(inode)) | |
227 | return ERR_PTR(PTR_ERR(inode)); | |
1da177e4 LT |
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, \"%.*s\")\n", (u32)dir->i_ino, | |
238 | (int)dentry->d_name.len, dentry->d_name.name); | |
239 | ||
240 | return affs_remove_header(dentry); | |
241 | } | |
242 | ||
243 | int | |
244 | affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) | |
245 | { | |
246 | struct super_block *sb = dir->i_sb; | |
247 | struct inode *inode; | |
248 | int error; | |
249 | ||
250 | pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len, | |
251 | dentry->d_name.name,mode); | |
252 | ||
253 | inode = affs_new_inode(dir); | |
254 | if (!inode) | |
255 | return -ENOSPC; | |
256 | ||
257 | inode->i_mode = mode; | |
258 | mode_to_prot(inode); | |
259 | mark_inode_dirty(inode); | |
260 | ||
261 | inode->i_op = &affs_file_inode_operations; | |
262 | inode->i_fop = &affs_file_operations; | |
263 | inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; | |
264 | error = affs_add_entry(dir, inode, dentry, ST_FILE); | |
265 | if (error) { | |
266 | inode->i_nlink = 0; | |
267 | iput(inode); | |
268 | return error; | |
269 | } | |
270 | return 0; | |
271 | } | |
272 | ||
273 | int | |
274 | affs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |
275 | { | |
276 | struct inode *inode; | |
277 | int error; | |
278 | ||
279 | pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino, | |
280 | (int)dentry->d_name.len,dentry->d_name.name,mode); | |
281 | ||
282 | inode = affs_new_inode(dir); | |
283 | if (!inode) | |
284 | return -ENOSPC; | |
285 | ||
286 | inode->i_mode = S_IFDIR | mode; | |
287 | mode_to_prot(inode); | |
288 | ||
289 | inode->i_op = &affs_dir_inode_operations; | |
290 | inode->i_fop = &affs_dir_operations; | |
291 | ||
292 | error = affs_add_entry(dir, inode, dentry, ST_USERDIR); | |
293 | if (error) { | |
294 | inode->i_nlink = 0; | |
295 | mark_inode_dirty(inode); | |
296 | iput(inode); | |
297 | return error; | |
298 | } | |
299 | return 0; | |
300 | } | |
301 | ||
302 | int | |
303 | affs_rmdir(struct inode *dir, struct dentry *dentry) | |
304 | { | |
305 | pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino, | |
306 | (int)dentry->d_name.len, dentry->d_name.name); | |
307 | ||
308 | return affs_remove_header(dentry); | |
309 | } | |
310 | ||
311 | int | |
312 | affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | |
313 | { | |
314 | struct super_block *sb = dir->i_sb; | |
315 | struct buffer_head *bh; | |
316 | struct inode *inode; | |
317 | char *p; | |
318 | int i, maxlen, error; | |
319 | char c, lc; | |
320 | ||
321 | pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, | |
322 | (int)dentry->d_name.len,dentry->d_name.name,symname); | |
323 | ||
324 | maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; | |
325 | inode = affs_new_inode(dir); | |
326 | if (!inode) | |
327 | return -ENOSPC; | |
328 | ||
329 | inode->i_op = &affs_symlink_inode_operations; | |
330 | inode->i_data.a_ops = &affs_symlink_aops; | |
331 | inode->i_mode = S_IFLNK | 0777; | |
332 | mode_to_prot(inode); | |
333 | ||
334 | error = -EIO; | |
335 | bh = affs_bread(sb, inode->i_ino); | |
336 | if (!bh) | |
337 | goto err; | |
338 | i = 0; | |
339 | p = (char *)AFFS_HEAD(bh)->table; | |
340 | lc = '/'; | |
341 | if (*symname == '/') { | |
342 | while (*symname == '/') | |
343 | symname++; | |
344 | while (AFFS_SB(sb)->s_volume[i]) /* Cannot overflow */ | |
345 | *p++ = AFFS_SB(sb)->s_volume[i++]; | |
346 | } | |
347 | while (i < maxlen && (c = *symname++)) { | |
348 | if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { | |
349 | *p++ = '/'; | |
350 | i++; | |
351 | symname += 2; | |
352 | lc = '/'; | |
353 | } else if (c == '.' && lc == '/' && *symname == '/') { | |
354 | symname++; | |
355 | lc = '/'; | |
356 | } else { | |
357 | *p++ = c; | |
358 | lc = c; | |
359 | i++; | |
360 | } | |
361 | if (lc == '/') | |
362 | while (*symname == '/') | |
363 | symname++; | |
364 | } | |
365 | *p = 0; | |
366 | mark_buffer_dirty_inode(bh, inode); | |
367 | affs_brelse(bh); | |
368 | mark_inode_dirty(inode); | |
369 | ||
370 | error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); | |
371 | if (error) | |
372 | goto err; | |
373 | ||
374 | return 0; | |
375 | ||
376 | err: | |
377 | inode->i_nlink = 0; | |
378 | mark_inode_dirty(inode); | |
379 | iput(inode); | |
380 | return error; | |
381 | } | |
382 | ||
383 | int | |
384 | affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) | |
385 | { | |
386 | struct inode *inode = old_dentry->d_inode; | |
387 | ||
388 | pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino, | |
389 | (int)dentry->d_name.len,dentry->d_name.name); | |
390 | ||
391 | return affs_add_entry(dir, inode, dentry, ST_LINKFILE); | |
392 | } | |
393 | ||
394 | int | |
395 | affs_rename(struct inode *old_dir, struct dentry *old_dentry, | |
396 | struct inode *new_dir, struct dentry *new_dentry) | |
397 | { | |
398 | struct super_block *sb = old_dir->i_sb; | |
399 | struct buffer_head *bh = NULL; | |
400 | int retval; | |
401 | ||
402 | pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n", | |
403 | (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, | |
404 | (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); | |
405 | ||
406 | retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len); | |
407 | if (retval) | |
408 | return retval; | |
409 | ||
410 | /* Unlink destination if it already exists */ | |
411 | if (new_dentry->d_inode) { | |
412 | retval = affs_remove_header(new_dentry); | |
413 | if (retval) | |
414 | return retval; | |
415 | } | |
416 | ||
1da177e4 LT |
417 | bh = affs_bread(sb, old_dentry->d_inode->i_ino); |
418 | if (!bh) | |
3ac81413 | 419 | return -EIO; |
1da177e4 LT |
420 | |
421 | /* Remove header from its parent directory. */ | |
422 | affs_lock_dir(old_dir); | |
423 | retval = affs_remove_hash(old_dir, bh); | |
424 | affs_unlock_dir(old_dir); | |
425 | if (retval) | |
426 | goto done; | |
427 | ||
428 | /* And insert it into the new directory with the new name. */ | |
429 | affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); | |
430 | affs_fix_checksum(sb, bh); | |
431 | affs_lock_dir(new_dir); | |
432 | retval = affs_insert_hash(new_dir, bh); | |
433 | affs_unlock_dir(new_dir); | |
434 | /* TODO: move it back to old_dir, if error? */ | |
435 | ||
436 | done: | |
437 | mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); | |
438 | affs_brelse(bh); | |
439 | return retval; | |
440 | } |