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