|
|
1.1.1.2 root 1: /*
2: * linux/fs/namei.c
3: *
4: * (C) 1991 Linus Torvalds
5: */
6:
1.1.1.3 ! root 7: /*
! 8: * Some corrections by tytso.
! 9: */
! 10:
1.1 root 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: */
40: static 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;
1.1.1.2 root 47: else if (current->euid==inode->i_uid)
1.1 root 48: mode >>= 6;
1.1.1.2 root 49: else if (current->egid==inode->i_gid)
1.1 root 50: mode >>= 3;
1.1.1.2 root 51: if (((mode & mask & 0007) == mask) || suser())
52: return 1;
53: return 0;
1.1 root 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: */
63: static int match(int len,const char * name,struct dir_entry * de)
64: {
65: register int same __asm__("ax");
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: :"cx","di","si");
77: return same;
78: }
79:
80: /*
81: * find_entry()
82: *
1.1.1.3 ! root 83: * finds an entry in the specified directory with the wanted name. It
1.1 root 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.
1.1.1.2 root 87: *
88: * This also takes care of the few special cases due to '..'-traversal
89: * over a pseudo-root and a mount point.
1.1 root 90: */
1.1.1.2 root 91: static struct buffer_head * find_entry(struct m_inode ** dir,
1.1 root 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;
1.1.1.2 root 98: struct super_block * sb;
1.1 root 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
1.1.1.2 root 107: entries = (*dir)->i_size / (sizeof (struct dir_entry));
1.1 root 108: *res_dir = NULL;
109: if (!namelen)
110: return NULL;
1.1.1.2 root 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]))
1.1 root 128: return NULL;
1.1.1.2 root 129: if (!(bh = bread((*dir)->i_dev,block)))
1.1 root 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;
1.1.1.2 root 137: if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
138: !(bh = bread((*dir)->i_dev,block))) {
1.1 root 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: */
165: static 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: */
228: static 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;
1.1.1.2 root 259: if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
1.1 root 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: */
278: static 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: */
303: struct 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;
1.1.1.2 root 315: bh = find_entry(&dir,basename,namelen,&de);
1.1 root 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: */
337: int 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: }
1.1.1.2 root 360: bh = find_entry(&dir,basename,namelen,&de);
1.1 root 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: }
1.1.1.2 root 375: inode->i_uid = current->euid;
1.1 root 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)) ||
1.1.1.2 root 401: !permission(inode,ACC_MODE(flag))) {
1.1 root 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:
1.1.1.2 root 412: int 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:
1.1 root 463: int 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:
1.1.1.2 root 471: if (!suser())
1.1 root 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: }
1.1.1.2 root 483: bh = find_entry(&dir,basename,namelen,&de);
1.1 root 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: */
543: static 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:
587: int 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:
1.1.1.2 root 595: if (!suser())
1.1 root 596: return -EPERM;
597: if (!(dir = dir_namei(name,&namelen,&basename)))
598: return -ENOENT;
599: if (!namelen) {
600: iput(dir);
601: return -ENOENT;
602: }
1.1.1.3 ! root 603: if (!permission(dir,MAY_WRITE)) {
! 604: iput(dir);
! 605: return -EPERM;
! 606: }
1.1.1.2 root 607: bh = find_entry(&dir,basename,namelen,&de);
1.1 root 608: if (!bh) {
609: iput(dir);
610: return -ENOENT;
611: }
1.1.1.3 ! root 612: if (!(inode = iget(dir->i_dev, de->inode))) {
1.1 root 613: iput(dir);
614: brelse(bh);
615: return -EPERM;
616: }
1.1.1.3 ! root 617: if ((dir->i_mode & S_ISVTX) && current->euid &&
! 618: inode->i_uid != current->euid) {
1.1 root 619: iput(dir);
1.1.1.3 ! root 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);
1.1 root 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:
663: int 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: }
1.1.1.2 root 681: bh = find_entry(&dir,basename,namelen,&de);
1.1 root 682: if (!bh) {
683: iput(dir);
684: return -ENOENT;
685: }
1.1.1.3 ! root 686: if (!(inode = iget(dir->i_dev, de->inode))) {
1.1 root 687: iput(dir);
688: brelse(bh);
689: return -ENOENT;
690: }
1.1.1.3 ! root 691: if ((dir->i_mode & S_ISVTX) && !suser() &&
! 692: current->euid != inode->i_uid &&
! 693: current->euid != dir->i_uid) {
1.1 root 694: iput(dir);
1.1.1.3 ! root 695: iput(inode);
1.1 root 696: brelse(bh);
697: return -EPERM;
698: }
1.1.1.3 ! root 699: if (S_ISDIR(inode->i_mode)) {
1.1.1.2 root 700: iput(inode);
701: iput(dir);
702: brelse(bh);
703: return -EPERM;
1.1.1.3 ! root 704: }
1.1 root 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:
721: int 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;
1.1.1.2 root 732: if (S_ISDIR(oldinode->i_mode)) {
1.1 root 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: }
1.1.1.2 root 756: bh = find_entry(&dir,basename,namelen,&de);
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.