summaryrefslogtreecommitdiffstats
path: root/src/fs/namei.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/namei.c')
-rw-r--r--src/fs/namei.c778
1 files changed, 778 insertions, 0 deletions
diff --git a/src/fs/namei.c b/src/fs/namei.c
new file mode 100644
index 0000000..97d0de4
--- /dev/null
+++ b/src/fs/namei.c
@@ -0,0 +1,778 @@
1/*
2 * linux/fs/namei.c
3 *
4 * (C) 1991 Linus Torvalds
5 */
6
7/*
8 * Some corrections by tytso.
9 */
10
11#include <linux/sched.h>
12#include <linux/kernel.h>
13#include <asm/segment.h>
14
15#include <string.h>
16#include <fcntl.h>
17#include <errno.h>
18#include <const.h>
19#include <sys/stat.h>
20
21#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
22
23/*
24 * comment out this line if you want names > NAME_LEN chars to be
25 * truncated. Else they will be disallowed.
26 */
27/* #define NO_TRUNCATE */
28
29#define MAY_EXEC 1
30#define MAY_WRITE 2
31#define MAY_READ 4
32
33/*
34 * permission()
35 *
36 * is used to check for read/write/execute permissions on a file.
37 * I don't know if we should look at just the euid or both euid and
38 * uid, but that should be easily changed.
39 */
40static int permission(struct m_inode * inode,int mask)
41{
42 int mode = inode->i_mode;
43
44/* special case: not even root can read/write a deleted file */
45 if (inode->i_dev && !inode->i_nlinks)
46 return 0;
47 else if (current->euid==inode->i_uid)
48 mode >>= 6;
49 else if (current->egid==inode->i_gid)
50 mode >>= 3;
51 if (((mode & mask & 0007) == mask) || suser())
52 return 1;
53 return 0;
54}
55
56/*
57 * ok, we cannot use strncmp, as the name is not in our data space.
58 * Thus we'll have to use match. No big problem. Match also makes
59 * some sanity tests.
60 *
61 * NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
62 */
63static int match(int len,const char * name,struct dir_entry * de)
64{
65 register int same ;
66
67 if (!de || !de->inode || len > NAME_LEN)
68 return 0;
69 if (len < NAME_LEN && de->name[len])
70 return 0;
71 __asm__("cld\n\t"
72 "fs ; repe ; cmpsb\n\t"
73 "setz %%al"
74 :"=a" (same)
75 :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
76 );
77 return same;
78}
79
80/*
81 * find_entry()
82 *
83 * finds an entry in the specified directory with the wanted name. It
84 * returns the cache buffer in which the entry was found, and the entry
85 * itself (as a parameter - res_dir). It does NOT read the inode of the
86 * entry - you'll have to do that yourself if you want to.
87 *
88 * This also takes care of the few special cases due to '..'-traversal
89 * over a pseudo-root and a mount point.
90 */
91static struct buffer_head * find_entry(struct m_inode ** dir,
92 const char * name, int namelen, struct dir_entry ** res_dir)
93{
94 int entries;
95 int block,i;
96 struct buffer_head * bh;
97 struct dir_entry * de;
98 struct super_block * sb;
99
100#ifdef NO_TRUNCATE
101 if (namelen > NAME_LEN)
102 return NULL;
103#else
104 if (namelen > NAME_LEN)
105 namelen = NAME_LEN;
106#endif
107 entries = (*dir)->i_size / (sizeof (struct dir_entry));
108 *res_dir = NULL;
109 if (!namelen)
110 return NULL;
111/* check for '..', as we might have to do some "magic" for it */
112 if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
113/* '..' in a pseudo-root results in a faked '.' (just change namelen) */
114 if ((*dir) == current->root)
115 namelen=1;
116 else if ((*dir)->i_num == ROOT_INO) {
117/* '..' over a mount-point results in 'dir' being exchanged for the mounted
118 directory-inode. NOTE! We set mounted, so that we can iput the new dir */
119 sb=get_super((*dir)->i_dev);
120 if (sb->s_imount) {
121 iput(*dir);
122 (*dir)=sb->s_imount;
123 (*dir)->i_count++;
124 }
125 }
126 }
127 if (!(block = (*dir)->i_zone[0]))
128 return NULL;
129 if (!(bh = bread((*dir)->i_dev,block)))
130 return NULL;
131 i = 0;
132 de = (struct dir_entry *) bh->b_data;
133 while (i < entries) {
134 if ((char *)de >= BLOCK_SIZE+bh->b_data) {
135 brelse(bh);
136 bh = NULL;
137 if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
138 !(bh = bread((*dir)->i_dev,block))) {
139 i += DIR_ENTRIES_PER_BLOCK;
140 continue;
141 }
142 de = (struct dir_entry *) bh->b_data;
143 }
144 if (match(namelen,name,de)) {
145 *res_dir = de;
146 return bh;
147 }
148 de++;
149 i++;
150 }
151 brelse(bh);
152 return NULL;
153}
154
155/*
156 * add_entry()
157 *
158 * adds a file entry to the specified directory, using the same
159 * semantics as find_entry(). It returns NULL if it failed.
160 *
161 * NOTE!! The inode part of 'de' is left at 0 - which means you
162 * may not sleep between calling this and putting something into
163 * the entry, as someone else might have used it while you slept.
164 */
165static struct buffer_head * add_entry(struct m_inode * dir,
166 const char * name, int namelen, struct dir_entry ** res_dir)
167{
168 int block,i;
169 struct buffer_head * bh;
170 struct dir_entry * de;
171
172 *res_dir = NULL;
173#ifdef NO_TRUNCATE
174 if (namelen > NAME_LEN)
175 return NULL;
176#else
177 if (namelen > NAME_LEN)
178 namelen = NAME_LEN;
179#endif
180 if (!namelen)
181 return NULL;
182 if (!(block = dir->i_zone[0]))
183 return NULL;
184 if (!(bh = bread(dir->i_dev,block)))
185 return NULL;
186 i = 0;
187 de = (struct dir_entry *) bh->b_data;
188 while (1) {
189 if ((char *)de >= BLOCK_SIZE+bh->b_data) {
190 brelse(bh);
191 bh = NULL;
192 block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
193 if (!block)
194 return NULL;
195 if (!(bh = bread(dir->i_dev,block))) {
196 i += DIR_ENTRIES_PER_BLOCK;
197 continue;
198 }
199 de = (struct dir_entry *) bh->b_data;
200 }
201 if (i*sizeof(struct dir_entry) >= dir->i_size) {
202 de->inode=0;
203 dir->i_size = (i+1)*sizeof(struct dir_entry);
204 dir->i_dirt = 1;
205 dir->i_ctime = CURRENT_TIME;
206 }
207 if (!de->inode) {
208 dir->i_mtime = CURRENT_TIME;
209 for (i=0; i < NAME_LEN ; i++)
210 de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
211 bh->b_dirt = 1;
212 *res_dir = de;
213 return bh;
214 }
215 de++;
216 i++;
217 }
218 brelse(bh);
219 return NULL;
220}
221
222/*
223 * get_dir()
224 *
225 * Getdir traverses the pathname until it hits the topmost directory.
226 * It returns NULL on failure.
227 */
228static struct m_inode * get_dir(const char * pathname)
229{
230 char c;
231 const char * thisname;
232 struct m_inode * inode;
233 struct buffer_head * bh;
234 int namelen,inr,idev;
235 struct dir_entry * de;
236
237 if (!current->root || !current->root->i_count)
238 panic("No root inode");
239 if (!current->pwd || !current->pwd->i_count)
240 panic("No cwd inode");
241 if ((c=get_fs_byte(pathname))=='/') {
242 inode = current->root;
243 pathname++;
244 } else if (c)
245 inode = current->pwd;
246 else
247 return NULL; /* empty name is bad */
248 inode->i_count++;
249 while (1) {
250 thisname = pathname;
251 if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
252 iput(inode);
253 return NULL;
254 }
255 for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
256 /* nothing */ ;
257 if (!c)
258 return inode;
259 if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
260 iput(inode);
261 return NULL;
262 }
263 inr = de->inode;
264 idev = inode->i_dev;
265 brelse(bh);
266 iput(inode);
267 if (!(inode = iget(idev,inr)))
268 return NULL;
269 }
270}
271
272/*
273 * dir_namei()
274 *
275 * dir_namei() returns the inode of the directory of the
276 * specified name, and the name within that directory.
277 */
278static struct m_inode * dir_namei(const char * pathname,
279 int * namelen, const char ** name)
280{
281 char c;
282 const char * basename;
283 struct m_inode * dir;
284
285 if (!(dir = get_dir(pathname)))
286 return NULL;
287 basename = pathname;
288 while ((c=get_fs_byte(pathname++)))
289 if (c=='/')
290 basename=pathname;
291 *namelen = pathname-basename-1;
292 *name = basename;
293 return dir;
294}
295
296/*
297 * namei()
298 *
299 * is used by most simple commands to get the inode of a specified name.
300 * Open, link etc use their own routines, but this is enough for things
301 * like 'chmod' etc.
302 */
303struct m_inode * namei(const char * pathname)
304{
305 const char * basename;
306 int inr,dev,namelen;
307 struct m_inode * dir;
308 struct buffer_head * bh;
309 struct dir_entry * de;
310
311 if (!(dir = dir_namei(pathname,&namelen,&basename)))
312 return NULL;
313 if (!namelen) /* special case: '/usr/' etc */
314 return dir;
315 bh = find_entry(&dir,basename,namelen,&de);
316 if (!bh) {
317 iput(dir);
318 return NULL;
319 }
320 inr = de->inode;
321 dev = dir->i_dev;
322 brelse(bh);
323 iput(dir);
324 dir=iget(dev,inr);
325 if (dir) {
326 dir->i_atime=CURRENT_TIME;
327 dir->i_dirt=1;
328 }
329 return dir;
330}
331
332/*
333 * open_namei()
334 *
335 * namei for open - this is in fact almost the whole open-routine.
336 */
337int open_namei(const char * pathname, int flag, int mode,
338 struct m_inode ** res_inode)
339{
340 const char * basename;
341 int inr,dev,namelen;
342 struct m_inode * dir, *inode;
343 struct buffer_head * bh;
344 struct dir_entry * de;
345
346 if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
347 flag |= O_WRONLY;
348 mode &= 0777 & ~current->umask;
349 mode |= I_REGULAR;
350 if (!(dir = dir_namei(pathname,&namelen,&basename)))
351 return -ENOENT;
352 if (!namelen) { /* special case: '/usr/' etc */
353 if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
354 *res_inode=dir;
355 return 0;
356 }
357 iput(dir);
358 return -EISDIR;
359 }
360 bh = find_entry(&dir,basename,namelen,&de);
361 if (!bh) {
362 if (!(flag & O_CREAT)) {
363 iput(dir);
364 return -ENOENT;
365 }
366 if (!permission(dir,MAY_WRITE)) {
367 iput(dir);
368 return -EACCES;
369 }
370 inode = new_inode(dir->i_dev);
371 if (!inode) {
372 iput(dir);
373 return -ENOSPC;
374 }
375 inode->i_uid = current->euid;
376 inode->i_mode = mode;
377 inode->i_dirt = 1;
378 bh = add_entry(dir,basename,namelen,&de);
379 if (!bh) {
380 inode->i_nlinks--;
381 iput(inode);
382 iput(dir);
383 return -ENOSPC;
384 }
385 de->inode = inode->i_num;
386 bh->b_dirt = 1;
387 brelse(bh);
388 iput(dir);
389 *res_inode = inode;
390 return 0;
391 }
392 inr = de->inode;
393 dev = dir->i_dev;
394 brelse(bh);
395 iput(dir);
396 if (flag & O_EXCL)
397 return -EEXIST;
398 if (!(inode=iget(dev,inr)))
399 return -EACCES;
400 if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
401 !permission(inode,ACC_MODE(flag))) {
402 iput(inode);
403 return -EPERM;
404 }
405 inode->i_atime = CURRENT_TIME;
406 if (flag & O_TRUNC)
407 truncate(inode);
408 *res_inode = inode;
409 return 0;
410}
411
412int sys_mknod(const char * filename, int mode, int dev)
413{
414 const char * basename;
415 int namelen;
416 struct m_inode * dir, * inode;
417 struct buffer_head * bh;
418 struct dir_entry * de;
419
420 if (!suser())
421 return -EPERM;
422 if (!(dir = dir_namei(filename,&namelen,&basename)))
423 return -ENOENT;
424 if (!namelen) {
425 iput(dir);
426 return -ENOENT;
427 }
428 if (!permission(dir,MAY_WRITE)) {
429 iput(dir);
430 return -EPERM;
431 }
432 bh = find_entry(&dir,basename,namelen,&de);
433 if (bh) {
434 brelse(bh);
435 iput(dir);
436 return -EEXIST;
437 }
438 inode = new_inode(dir->i_dev);
439 if (!inode) {
440 iput(dir);
441 return -ENOSPC;
442 }
443 inode->i_mode = mode;
444 if (S_ISBLK(mode) || S_ISCHR(mode))
445 inode->i_zone[0] = dev;
446 inode->i_mtime = inode->i_atime = CURRENT_TIME;
447 inode->i_dirt = 1;
448 bh = add_entry(dir,basename,namelen,&de);
449 if (!bh) {
450 iput(dir);
451 inode->i_nlinks=0;
452 iput(inode);
453 return -ENOSPC;
454 }
455 de->inode = inode->i_num;
456 bh->b_dirt = 1;
457 iput(dir);
458 iput(inode);
459 brelse(bh);
460 return 0;
461}
462
463int sys_mkdir(const char * pathname, int mode)
464{
465 const char * basename;
466 int namelen;
467 struct m_inode * dir, * inode;
468 struct buffer_head * bh, *dir_block;
469 struct dir_entry * de;
470
471 if (!suser())
472 return -EPERM;
473 if (!(dir = dir_namei(pathname,&namelen,&basename)))
474 return -ENOENT;
475 if (!namelen) {
476 iput(dir);
477 return -ENOENT;
478 }
479 if (!permission(dir,MAY_WRITE)) {
480 iput(dir);
481 return -EPERM;
482 }
483 bh = find_entry(&dir,basename,namelen,&de);
484 if (bh) {
485 brelse(bh);
486 iput(dir);
487 return -EEXIST;
488 }
489 inode = new_inode(dir->i_dev);
490 if (!inode) {
491 iput(dir);
492 return -ENOSPC;
493 }
494 inode->i_size = 32;
495 inode->i_dirt = 1;
496 inode->i_mtime = inode->i_atime = CURRENT_TIME;
497 if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
498 iput(dir);
499 inode->i_nlinks--;
500 iput(inode);
501 return -ENOSPC;
502 }
503 inode->i_dirt = 1;
504 if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
505 iput(dir);
506 free_block(inode->i_dev,inode->i_zone[0]);
507 inode->i_nlinks--;
508 iput(inode);
509 return -ERROR;
510 }
511 de = (struct dir_entry *) dir_block->b_data;
512 de->inode=inode->i_num;
513 strcpy(de->name,".");
514 de++;
515 de->inode = dir->i_num;
516 strcpy(de->name,"..");
517 inode->i_nlinks = 2;
518 dir_block->b_dirt = 1;
519 brelse(dir_block);
520 inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
521 inode->i_dirt = 1;
522 bh = add_entry(dir,basename,namelen,&de);
523 if (!bh) {
524 iput(dir);
525 free_block(inode->i_dev,inode->i_zone[0]);
526 inode->i_nlinks=0;
527 iput(inode);
528 return -ENOSPC;
529 }
530 de->inode = inode->i_num;
531 bh->b_dirt = 1;
532 dir->i_nlinks++;
533 dir->i_dirt = 1;
534 iput(dir);
535 iput(inode);
536 brelse(bh);
537 return 0;
538}
539
540/*
541 * routine to check that the specified directory is empty (for rmdir)
542 */
543static int empty_dir(struct m_inode * inode)
544{
545 int nr,block;
546 int len;
547 struct buffer_head * bh;
548 struct dir_entry * de;
549
550 len = inode->i_size / sizeof (struct dir_entry);
551 if (len<2 || !inode->i_zone[0] ||
552 !(bh=bread(inode->i_dev,inode->i_zone[0]))) {
553 printk("warning - bad directory on dev %04x\n",inode->i_dev);
554 return 0;
555 }
556 de = (struct dir_entry *) bh->b_data;
557 if (de[0].inode != inode->i_num || !de[1].inode ||
558 strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
559 printk("warning - bad directory on dev %04x\n",inode->i_dev);
560 return 0;
561 }
562 nr = 2;
563 de += 2;
564 while (nr<len) {
565 if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
566 brelse(bh);
567 block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
568 if (!block) {
569 nr += DIR_ENTRIES_PER_BLOCK;
570 continue;
571 }
572 if (!(bh=bread(inode->i_dev,block)))
573 return 0;
574 de = (struct dir_entry *) bh->b_data;
575 }
576 if (de->inode) {
577 brelse(bh);
578 return 0;
579 }
580 de++;
581 nr++;
582 }
583 brelse(bh);
584 return 1;
585}
586
587int sys_rmdir(const char * name)
588{
589 const char * basename;
590 int namelen;
591 struct m_inode * dir, * inode;
592 struct buffer_head * bh;
593 struct dir_entry * de;
594
595 if (!suser())
596 return -EPERM;
597 if (!(dir = dir_namei(name,&namelen,&basename)))
598 return -ENOENT;
599 if (!namelen) {
600 iput(dir);
601 return -ENOENT;
602 }
603 if (!permission(dir,MAY_WRITE)) {
604 iput(dir);
605 return -EPERM;
606 }
607 bh = find_entry(&dir,basename,namelen,&de);
608 if (!bh) {
609 iput(dir);
610 return -ENOENT;
611 }
612 if (!(inode = iget(dir->i_dev, de->inode))) {
613 iput(dir);
614 brelse(bh);
615 return -EPERM;
616 }
617 if ((dir->i_mode & S_ISVTX) && current->euid &&
618 inode->i_uid != current->euid) {
619 iput(dir);
620 iput(inode);
621 brelse(bh);
622 return -EPERM;
623 }
624 if (inode->i_dev != dir->i_dev || inode->i_count>1) {
625 iput(dir);
626 iput(inode);
627 brelse(bh);
628 return -EPERM;
629 }
630 if (inode == dir) { /* we may not delete ".", but "../dir" is ok */
631 iput(inode);
632 iput(dir);
633 brelse(bh);
634 return -EPERM;
635 }
636 if (!S_ISDIR(inode->i_mode)) {
637 iput(inode);
638 iput(dir);
639 brelse(bh);
640 return -ENOTDIR;
641 }
642 if (!empty_dir(inode)) {
643 iput(inode);
644 iput(dir);
645 brelse(bh);
646 return -ENOTEMPTY;
647 }
648 if (inode->i_nlinks != 2)
649 printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
650 de->inode = 0;
651 bh->b_dirt = 1;
652 brelse(bh);
653 inode->i_nlinks=0;
654 inode->i_dirt=1;
655 dir->i_nlinks--;
656 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
657 dir->i_dirt=1;
658 iput(dir);
659 iput(inode);
660 return 0;
661}
662
663int sys_unlink(const char * name)
664{
665 const char * basename;
666 int namelen;
667 struct m_inode * dir, * inode;
668 struct buffer_head * bh;
669 struct dir_entry * de;
670
671 if (!(dir = dir_namei(name,&namelen,&basename)))
672 return -ENOENT;
673 if (!namelen) {
674 iput(dir);
675 return -ENOENT;
676 }
677 if (!permission(dir,MAY_WRITE)) {
678 iput(dir);
679 return -EPERM;
680 }
681 bh = find_entry(&dir,basename,namelen,&de);
682 if (!bh) {
683 iput(dir);
684 return -ENOENT;
685 }
686 if (!(inode = iget(dir->i_dev, de->inode))) {
687 iput(dir);
688 brelse(bh);
689 return -ENOENT;
690 }
691 if ((dir->i_mode & S_ISVTX) && !suser() &&
692 current->euid != inode->i_uid &&
693 current->euid != dir->i_uid) {
694 iput(dir);
695 iput(inode);
696 brelse(bh);
697 return -EPERM;
698 }
699 if (S_ISDIR(inode->i_mode)) {
700 iput(inode);
701 iput(dir);
702 brelse(bh);
703 return -EPERM;
704 }
705 if (!inode->i_nlinks) {
706 printk("Deleting nonexistent file (%04x:%d), %d\n",
707 inode->i_dev,inode->i_num,inode->i_nlinks);
708 inode->i_nlinks=1;
709 }
710 de->inode = 0;
711 bh->b_dirt = 1;
712 brelse(bh);
713 inode->i_nlinks--;
714 inode->i_dirt = 1;
715 inode->i_ctime = CURRENT_TIME;
716 iput(inode);
717 iput(dir);
718 return 0;
719}
720
721int sys_link(const char * oldname, const char * newname)
722{
723 struct dir_entry * de;
724 struct m_inode * oldinode, * dir;
725 struct buffer_head * bh;
726 const char * basename;
727 int namelen;
728
729 oldinode=namei(oldname);
730 if (!oldinode)
731 return -ENOENT;
732 if (S_ISDIR(oldinode->i_mode)) {
733 iput(oldinode);
734 return -EPERM;
735 }
736 dir = dir_namei(newname,&namelen,&basename);
737 if (!dir) {
738 iput(oldinode);
739 return -EACCES;
740 }
741 if (!namelen) {
742 iput(oldinode);
743 iput(dir);
744 return -EPERM;
745 }
746 if (dir->i_dev != oldinode->i_dev) {
747 iput(dir);
748 iput(oldinode);
749 return -EXDEV;
750 }
751 if (!permission(dir,MAY_WRITE)) {
752 iput(dir);
753 iput(oldinode);
754 return -EACCES;
755 }
756 bh = find_entry(&dir,basename,namelen,&de);
757 if (bh) {
758 brelse(bh);
759 iput(dir);
760 iput(oldinode);
761 return -EEXIST;
762 }
763 bh = add_entry(dir,basename,namelen,&de);
764 if (!bh) {
765 iput(dir);
766 iput(oldinode);
767 return -ENOSPC;
768 }
769 de->inode = oldinode->i_num;
770 bh->b_dirt = 1;
771 brelse(bh);
772 iput(dir);
773 oldinode->i_nlinks++;
774 oldinode->i_ctime = CURRENT_TIME;
775 oldinode->i_dirt = 1;
776 iput(oldinode);
777 return 0;
778}