Import 2.3.7pre9
[davej-history.git] / fs / affs / namei.c
blobaad5b8f149b4ff18c9acee5bd465cc66a1e699e9
1 /*
2 * linux/fs/affs/namei.c
4 * (c) 1996 Hans-Joachim Widmaier - Rewritten
6 * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
8 * (C) 1991 Linus Torvalds - minix filesystem
9 */
11 #define DEBUG 0
12 #include <linux/sched.h>
13 #include <linux/affs_fs.h>
14 #include <linux/kernel.h>
15 #include <linux/string.h>
16 #include <linux/stat.h>
17 #include <linux/fcntl.h>
18 #include <linux/locks.h>
19 #include <linux/amigaffs.h>
20 #include <asm/uaccess.h>
22 #include <linux/errno.h>
24 /* Simple toupper() for DOS\1 */
26 static unsigned int
27 affs_toupper(unsigned int ch)
29 return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
32 /* International toupper() for DOS\3 ("international") */
34 static unsigned int
35 affs_intl_toupper(unsigned int ch)
37 return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
38 && ch <= 0xFE && ch != 0xF7) ?
39 ch - ('a' - 'A') : ch;
42 static int affs_hash_dentry(struct dentry *, struct qstr *);
43 static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
44 struct dentry_operations affs_dentry_operations = {
45 NULL, /* d_validate */
46 affs_hash_dentry, /* d_hash */
47 affs_compare_dentry, /* d_compare */
48 NULL /* d_delete */
52 * Note: the dentry argument is the parent dentry.
54 static int
55 affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
57 unsigned int (*toupper)(unsigned int) = affs_toupper;
58 unsigned long hash;
59 int i;
61 if ((i = affs_check_name(qstr->name,qstr->len)))
62 return i;
64 /* Check whether to use the international 'toupper' routine */
65 if (AFFS_I2FSTYPE(dentry->d_inode))
66 toupper = affs_intl_toupper;
67 hash = init_name_hash();
68 for (i = 0; i < qstr->len && i < 30; i++)
69 hash = partial_name_hash(toupper(qstr->name[i]), hash);
70 qstr->hash = end_name_hash(hash);
72 return 0;
75 static int
76 affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
78 unsigned int (*toupper)(unsigned int) = affs_toupper;
79 int alen = a->len;
80 int blen = b->len;
81 int i;
83 /* 'a' is the qstr of an already existing dentry, so the name
84 * must be valid. 'b' must be validated first.
87 if (affs_check_name(b->name,b->len))
88 return 1;
90 /* If the names are longer than the allowed 30 chars,
91 * the excess is ignored, so their length may differ.
93 if (alen > 30)
94 alen = 30;
95 if (blen > 30)
96 blen = 30;
97 if (alen != blen)
98 return 1;
100 /* Check whether to use the international 'toupper' routine */
101 if (AFFS_I2FSTYPE(dentry->d_inode))
102 toupper = affs_intl_toupper;
104 for (i = 0; i < alen; i++)
105 if (toupper(a->name[i]) != toupper(b->name[i]))
106 return 1;
108 return 0;
112 * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
115 static int
116 affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
118 unsigned int (*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
119 int i;
121 if (!compare)
122 return 0;
124 if (len > 30)
125 len = 30;
126 if (dlen > 30)
127 dlen = 30;
129 /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
130 if (!len && dlen == 1 && compare[0] == '.')
131 return 1;
132 if (dlen != len)
133 return 0;
134 for (i = 0; i < len; i++)
135 if (toupper(name[i]) != toupper(compare[i]))
136 return 0;
137 return 1;
141 affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
143 unsigned int i, x;
145 if (len > 30)
146 len = 30;
148 x = len;
149 for (i = 0; i < len; i++)
150 if (intl)
151 x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
152 else
153 x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
155 return x % hashsize;
158 static struct buffer_head *
159 affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
161 struct buffer_head *bh;
162 int intl = AFFS_I2FSTYPE(dir);
163 s32 key;
164 const char *name = dentry->d_name.name;
165 int namelen = dentry->d_name.len;
167 pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
169 bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
170 if (!bh)
171 return NULL;
173 if (namelen == 1 && name[0] == '.') {
174 *ino = dir->i_ino;
175 return bh;
177 if (namelen == 2 && name[0] == '.' && name[1] == '.') {
178 *ino = affs_parent_ino(dir);
179 return bh;
182 key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
184 for (;;) {
185 unsigned char *cname;
186 int cnamelen;
188 affs_brelse(bh);
189 bh = NULL;
190 if (key == 0)
191 break;
192 bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
193 if (!bh)
194 break;
195 cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
196 if (affs_match(name,namelen,cname,cnamelen,intl))
197 break;
198 key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
200 *ino = key;
201 return bh;
204 struct dentry *
205 affs_lookup(struct inode *dir, struct dentry *dentry)
207 unsigned long ino;
208 struct buffer_head *bh;
209 struct inode *inode;
211 pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
213 inode = NULL;
214 bh = affs_find_entry(dir,dentry,&ino);
215 if (bh) {
216 if (FILE_END(bh->b_data,dir)->original)
217 ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
218 affs_brelse(bh);
219 inode = iget(dir->i_sb,ino);
220 if (!inode)
221 return ERR_PTR(-EACCES);
223 dentry->d_op = &affs_dentry_operations;
224 d_add(dentry,inode);
225 return NULL;
229 affs_unlink(struct inode *dir, struct dentry *dentry)
231 int retval;
232 struct buffer_head *bh;
233 unsigned long ino;
234 struct inode *inode;
236 pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
237 (int)dentry->d_name.len,dentry->d_name.name);
239 retval = -ENOENT;
240 if (!(bh = affs_find_entry(dir,dentry,&ino)))
241 goto unlink_done;
243 inode = dentry->d_inode;
245 if ((retval = affs_remove_header(bh,inode)) < 0)
246 goto unlink_done;
248 inode->i_nlink = retval;
249 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
250 dir->i_version = ++event;
251 mark_inode_dirty(inode);
252 d_delete(dentry);
253 mark_inode_dirty(dir);
254 retval = 0;
256 unlink_done:
257 affs_brelse(bh);
258 return retval;
262 affs_create(struct inode *dir, struct dentry *dentry, int mode)
264 struct inode *inode;
265 int error;
267 pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
268 dentry->d_name.name,mode);
270 error = -ENOSPC;
271 inode = affs_new_inode(dir);
272 if (!inode)
273 goto out;
275 pr_debug("AFFS: ino=%lu\n",inode->i_ino);
276 if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
277 inode->i_op = &affs_file_inode_operations_ofs;
278 else
279 inode->i_op = &affs_file_inode_operations;
281 error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
282 if (error)
283 goto out_iput;
284 inode->i_mode = mode;
285 inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
286 d_instantiate(dentry,inode);
287 mark_inode_dirty(inode);
288 dir->i_version = ++event;
289 mark_inode_dirty(dir);
290 out:
291 return error;
293 out_iput:
294 inode->i_nlink = 0;
295 mark_inode_dirty(inode);
296 iput(inode);
297 goto out;
301 affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
303 struct inode *inode;
304 int error;
306 pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
307 (int)dentry->d_name.len,dentry->d_name.name,mode);
309 error = -ENOSPC;
310 inode = affs_new_inode(dir);
311 if (!inode)
312 goto out;
314 inode->i_op = &affs_dir_inode_operations;
315 error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
316 if (error)
317 goto out_iput;
318 inode->i_mode = S_IFDIR | S_ISVTX | mode;
319 inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
320 d_instantiate(dentry,inode);
321 mark_inode_dirty(inode);
322 dir->i_version = ++event;
323 mark_inode_dirty(dir);
324 out:
325 return error;
327 out_iput:
328 inode->i_nlink = 0;
329 mark_inode_dirty(inode);
330 iput(inode);
331 goto out;
334 static int
335 empty_dir(struct buffer_head *bh, int hashsize)
337 while (--hashsize >= 0) {
338 if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
339 return 0;
341 return 1;
345 affs_rmdir(struct inode *dir, struct dentry *dentry)
347 struct inode *inode = dentry->d_inode;
348 int retval;
349 unsigned long ino;
350 struct buffer_head *bh;
352 pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
353 (int)dentry->d_name.len,dentry->d_name.name);
355 retval = -ENOENT;
356 if (!(bh = affs_find_entry(dir,dentry,&ino)))
357 goto rmdir_done;
360 * Make sure the directory is empty and the dentry isn't busy.
362 retval = -ENOTEMPTY;
363 if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
364 goto rmdir_done;
365 retval = -EBUSY;
366 if (!list_empty(&dentry->d_hash))
367 goto rmdir_done;
369 if ((retval = affs_remove_header(bh,inode)) < 0)
370 goto rmdir_done;
372 inode->i_nlink = retval;
373 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
374 retval = 0;
375 dir->i_version = ++event;
376 mark_inode_dirty(dir);
377 mark_inode_dirty(inode);
378 d_delete(dentry);
380 rmdir_done:
381 affs_brelse(bh);
382 return retval;
386 affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
388 struct buffer_head *bh;
389 struct inode *inode;
390 char *p;
391 unsigned long tmp;
392 int i, maxlen, error;
393 char c, lc;
395 pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
396 (int)dentry->d_name.len,dentry->d_name.name,symname);
398 maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
399 error = -ENOSPC;
400 inode = affs_new_inode(dir);
401 if (!inode)
402 goto out;
404 inode->i_op = &affs_symlink_inode_operations;
405 inode->i_mode = S_IFLNK | 0777;
406 inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
407 error = -EIO;
408 bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
409 if (!bh)
410 goto out_iput;
411 i = 0;
412 p = ((struct slink_front *)bh->b_data)->symname;
413 lc = '/';
414 if (*symname == '/') {
415 while (*symname == '/')
416 symname++;
417 while (inode->i_sb->u.affs_sb.s_volume[i]) /* Cannot overflow */
418 *p++ = inode->i_sb->u.affs_sb.s_volume[i++];
420 while (i < maxlen && (c = *symname++)) {
421 if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
422 *p++ = '/';
423 i++;
424 symname += 2;
425 lc = '/';
426 } else if (c == '.' && lc == '/' && *symname == '/') {
427 symname++;
428 lc = '/';
429 } else {
430 *p++ = c;
431 lc = c;
432 i++;
434 if (lc == '/')
435 while (*symname == '/')
436 symname++;
438 *p = 0;
439 mark_buffer_dirty(bh,1);
440 affs_brelse(bh);
441 mark_inode_dirty(inode);
443 /* N.B. This test shouldn't be necessary ... dentry must be negative */
444 error = -EEXIST;
445 bh = affs_find_entry(dir,dentry,&tmp);
446 if (bh)
447 goto out_release;
448 /* N.B. Shouldn't we add the entry before dirtying the buffer? */
449 error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
450 if (error)
451 goto out_release;
452 d_instantiate(dentry,inode);
453 dir->i_version = ++event;
454 mark_inode_dirty(dir);
456 out:
457 return error;
459 out_release:
460 affs_brelse(bh);
461 out_iput:
462 inode->i_nlink = 0;
463 mark_inode_dirty(inode);
464 iput(inode);
465 goto out;
469 affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
471 struct inode *oldinode = old_dentry->d_inode;
472 struct inode *inode;
473 struct buffer_head *bh;
474 unsigned long i;
475 int error;
477 pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
478 (int)dentry->d_name.len,dentry->d_name.name);
480 /* N.B. Do we need this test? The dentry must be negative ... */
481 bh = affs_find_entry(dir,dentry,&i);
482 if (bh) {
483 affs_brelse(bh);
484 return -EEXIST;
486 if (oldinode->u.affs_i.i_hlink) { /* Cannot happen */
487 affs_warning(dir->i_sb,"link","Impossible link to link");
488 return -EINVAL;
490 error = -ENOSPC;
491 if (!(inode = affs_new_inode(dir)))
492 goto out;
494 inode->i_op = oldinode->i_op;
495 inode->u.affs_i.i_protect = mode_to_prot(oldinode->i_mode);
496 inode->u.affs_i.i_original = oldinode->i_ino;
497 inode->u.affs_i.i_hlink = 1;
498 inode->i_mtime = oldinode->i_mtime;
500 if (S_ISDIR(oldinode->i_mode))
501 error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
502 else
503 error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
504 if (error)
505 inode->i_nlink = 0;
506 else {
507 dir->i_version = ++event;
508 mark_inode_dirty(dir);
509 mark_inode_dirty(oldinode);
510 oldinode->i_count++;
511 d_instantiate(dentry,oldinode);
513 mark_inode_dirty(inode);
514 iput(inode);
516 out:
517 return error;
521 affs_rename(struct inode *old_dir, struct dentry *old_dentry,
522 struct inode *new_dir, struct dentry *new_dentry)
524 struct inode *old_inode = old_dentry->d_inode;
525 struct inode *new_inode = new_dentry->d_inode;
526 struct buffer_head *old_bh;
527 struct buffer_head *new_bh;
528 unsigned long old_ino;
529 unsigned long new_ino;
530 int retval;
532 pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p))\n",
533 old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode,
534 new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
536 if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
537 goto out;
539 new_bh = NULL;
540 retval = -ENOENT;
541 old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
542 if (!old_bh)
543 goto end_rename;
545 new_bh = affs_find_entry(new_dir,new_dentry,&new_ino);
546 if (new_bh && !new_inode) {
547 affs_error(old_inode->i_sb,"affs_rename",
548 "No inode for entry found (key=%lu)\n",new_ino);
549 goto end_rename;
551 if (S_ISDIR(old_inode->i_mode)) {
552 if (new_inode) {
553 retval = -ENOTEMPTY;
554 if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
555 goto end_rename;
558 retval = -ENOENT;
559 if (affs_parent_ino(old_inode) != old_dir->i_ino)
560 goto end_rename;
562 /* Unlink destination if it already exists */
563 if (new_inode) {
564 if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
565 goto end_rename;
566 new_inode->i_nlink = retval;
567 mark_inode_dirty(new_inode);
568 if (new_inode->i_ino == new_ino)
569 new_inode->i_nlink = 0;
571 /* Remove header from its parent directory. */
572 if ((retval = affs_remove_hash(old_bh,old_dir)))
573 goto end_rename;
574 /* And insert it into the new directory with the new name. */
575 affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name);
576 if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
577 goto end_rename;
578 affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
580 new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime
581 = old_dir->i_mtime = CURRENT_TIME;
582 new_dir->i_version = ++event;
583 old_dir->i_version = ++event;
584 retval = 0;
585 mark_inode_dirty(new_dir);
586 mark_inode_dirty(old_dir);
587 mark_buffer_dirty(old_bh,1);
589 end_rename:
590 affs_brelse(old_bh);
591 affs_brelse(new_bh);
592 out:
593 return retval;