xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 7441)
1*7441Sroot /*	vfs_lookup.c	4.17	82/07/15	*/
230Sbill 
36571Smckusic /* merged into kernel:	@(#)nami.c 2.3 4/8/82 */
46571Smckusic 
530Sbill #include "../h/param.h"
630Sbill #include "../h/systm.h"
730Sbill #include "../h/inode.h"
86571Smckusic #include "../h/fs.h"
930Sbill #include "../h/mount.h"
1030Sbill #include "../h/dir.h"
1130Sbill #include "../h/user.h"
1230Sbill #include "../h/buf.h"
132275Swnj #include "../h/conf.h"
1430Sbill 
15*7441Sroot #ifdef	EFS
16*7441Sroot extern	int	efs_major;
17*7441Sroot #endif
18*7441Sroot 
1930Sbill /*
2030Sbill  * Convert a pathname into a pointer to
215972Swnj  * a locked inode.
2230Sbill  *
2330Sbill  * func = function called to get next char of name
2430Sbill  *	&uchar if name is in user space
2530Sbill  *	&schar if name is in system space
2630Sbill  * flag = 0 if name is sought
2730Sbill  *	1 if name is to be created
2830Sbill  *	2 if name is to be deleted
295972Swnj  * follow = 1 if links are to be followed at the end of the name
3030Sbill  */
3130Sbill struct inode *
325972Swnj namei(func, flag, follow)
335972Swnj 	int (*func)(), flag, follow;
3430Sbill {
3530Sbill 	register struct inode *dp;
3630Sbill 	register char *cp;
375972Swnj 	register struct buf *bp, *nbp;
38194Sbill 	register struct direct *ep;
396571Smckusic 	register struct fs *fs;
405990Swnj 	struct inode *pdp;
416571Smckusic 	enum {NONE, COMPACT, FOUND} slot;
426571Smckusic 	int entryfree, entrysize;
436571Smckusic 	int spccnt, size, newsize;
446571Smckusic 	int loc, prevoff, curoff;
456571Smckusic 	int i, nlink, bsize;
466571Smckusic 	unsigned pathlen;
476571Smckusic 	daddr_t lbn, bn;
4830Sbill 	dev_t d;
4930Sbill 
5030Sbill 	/*
515972Swnj 	 * allocate name buffer; copy name
525972Swnj 	 */
536571Smckusic 	nbp = geteblk(MAXPATHLEN);
545972Swnj 	nlink = 0;
556571Smckusic 	for (i = 0, cp = nbp->b_un.b_addr; *cp = (*func)(); i++) {
566571Smckusic 		if ((*cp & 0377) == ('/'|0200)) {
576066Sroot 			u.u_error = EPERM;
586066Sroot 			break;
596066Sroot 		}
606066Sroot #ifdef notdef
616571Smckusic 		if (*cp++ & 0200 && flag == 1 ||
626571Smckusic 		    cp >= nbp->b_un.b_addr + MAXPATHLEN) {
636066Sroot #else
646066Sroot 		cp++;
656571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
666066Sroot #endif
675972Swnj 			u.u_error = ENOENT;
685972Swnj 			break;
695972Swnj 		}
705972Swnj 	}
715972Swnj 	if (u.u_error) {
726571Smckusic 		brelse(nbp);
736571Smckusic 		return (NULL);
745972Swnj 	}
755972Swnj 	cp = nbp->b_un.b_addr;
765972Swnj 	/*
7730Sbill 	 * If name starts with '/' start from
7830Sbill 	 * root; otherwise start from current dir.
7930Sbill 	 */
8030Sbill 	dp = u.u_cdir;
815972Swnj 	if (*cp == '/') {
825972Swnj 		while (*cp == '/')
835972Swnj 			cp++;
8430Sbill 		if ((dp = u.u_rdir) == NULL)
8530Sbill 			dp = rootdir;
8630Sbill 	}
875972Swnj 	ilock(dp);
885972Swnj 	dp->i_count++;
896571Smckusic 	fs = dp->i_fs;
906571Smckusic 	newsize = 0;
916571Smckusic dirloop:
9230Sbill 	/*
9330Sbill 	 * dp must be a directory and
9430Sbill 	 * must have X permission.
955972Swnj 	 * cp is a path name relative to that directory.
9630Sbill 	 */
97*7441Sroot #ifdef	EFS
98*7441Sroot 	/*
99*7441Sroot 	 * But first, if the last component was a character special file
100*7441Sroot 	 * and the major device is the extended file system device
101*7441Sroot 	 * then return even if more pathname exists.
102*7441Sroot 	 */
103*7441Sroot 	if ((dp->i_mode & IFMT) == IFCHR && major(dp->i_rdev) == efs_major) {
104*7441Sroot 		brelse(nbp);
105*7441Sroot 		return(dp);
106*7441Sroot 	}
107*7441Sroot #endif
1084823Swnj 	if ((dp->i_mode&IFMT) != IFDIR)
10930Sbill 		u.u_error = ENOTDIR;
110194Sbill 	(void) access(dp, IEXEC);
1116384Swnj dirloop2:
1126571Smckusic 	for (i = 0; *cp != '\0' && *cp != '/'; cp++) {
1136066Sroot #ifdef notdef
1146571Smckusic 		if (i >= MAXNAMLEN) {
1155972Swnj 			u.u_error = ENOENT;
1165972Swnj 			break;
1175972Swnj 		}
1186571Smckusic 		u.u_dent.d_name[i] = *cp;
1196066Sroot #else
1206571Smckusic 		if (i < MAXNAMLEN) {
1216571Smckusic 			u.u_dent.d_name[i] = *cp;
1226571Smckusic 			i++;
1236571Smckusic 		}
1246066Sroot #endif
1255972Swnj 	}
1266571Smckusic 	if (u.u_error) {
1276571Smckusic 		iput(dp);
1286571Smckusic 		brelse(nbp);
1296571Smckusic 		return (NULL);
1306571Smckusic 	}
1316571Smckusic 	u.u_dent.d_namlen = i;
1326571Smckusic 	u.u_dent.d_name[i] = '\0';
1336571Smckusic 	newsize = DIRSIZ(&u.u_dent);
1345990Swnj 	u.u_pdir = dp;
1356571Smckusic 	if (u.u_dent.d_name[0] == '\0') {	/* null name, e.g. "/" or "" */
1366571Smckusic 		if (flag != 0) {
1375972Swnj 			u.u_error = ENOENT;
1386571Smckusic 			iput(dp);
1396571Smckusic 			dp = NULL;
1405972Swnj 		}
1416571Smckusic 		u.u_offset = 0;
1426571Smckusic 		u.u_count = newsize;
1436571Smckusic 		brelse(nbp);
1446571Smckusic 		return (dp);
1455972Swnj 	}
1466571Smckusic 	/*
1476571Smckusic 	 * set up to search a directory
1486571Smckusic 	 */
1496571Smckusic 	if (flag == 1)
1506571Smckusic 		slot = NONE;
1516571Smckusic 	else
1526571Smckusic 		slot = FOUND;
1536571Smckusic 	u.u_offset = 0;
15430Sbill 	u.u_segflg = 1;
15530Sbill 	bp = NULL;
1566571Smckusic 	spccnt = 0;
1576571Smckusic 	loc = 0;
1586571Smckusic 	while (u.u_offset < dp->i_size) {
1595972Swnj 		/*
1606571Smckusic 		 * check to see if enough space has been accumulated to make
1616571Smckusic 		 * an entry by compaction. Reset the free space counter each
1626571Smckusic 		 * time a directory block is crossed.
1636571Smckusic 		 */
1646571Smckusic 		if (slot == NONE) {
1656571Smckusic 			if (spccnt >= newsize) {
1666571Smckusic 				slot = COMPACT;
1676571Smckusic 				entrysize = u.u_offset - entryfree;
1686571Smckusic 			} else if (loc % DIRBLKSIZ == 0) {
1696571Smckusic 				entryfree = NULL;
1706571Smckusic 				spccnt = 0;
1716571Smckusic 			}
1726571Smckusic 		}
1736571Smckusic 		/*
1745972Swnj 		 * If offset is on a block boundary,
1755972Swnj 		 * read the next directory block.
1765972Swnj 		 * Release previous if it exists.
1775972Swnj 		 */
1786571Smckusic 		if (blkoff(fs, u.u_offset) == 0) {
1795972Swnj 			if (bp != NULL)
1805972Swnj 				brelse(bp);
1816571Smckusic 			lbn = (daddr_t)lblkno(fs, u.u_offset);
1826571Smckusic 			bsize = blksize(fs, dp, lbn);
1836571Smckusic 			if ((bn = bmap(dp, lbn, B_READ)) < 0) {
1846571Smckusic 				printf("hole in dir: %s i = %d\n",
1856571Smckusic 				    fs->fs_fsmnt, dp->i_number);
1866571Smckusic 				if (fs->fs_ronly != 0 ||
1876571Smckusic 				    (bn = bmap(dp, lbn, B_WRITE, bsize)) < 0) {
1886571Smckusic 					u.u_offset += bsize;
1896571Smckusic 					bp = NULL;
1906571Smckusic 					continue;
1916571Smckusic 				}
1926571Smckusic 			}
1936571Smckusic 			bp = bread(dp->i_dev, fsbtodb(fs, bn), bsize);
1945972Swnj 			if (bp->b_flags & B_ERROR) {
1955972Swnj 				brelse(bp);
1966571Smckusic 				iput(dp);
1976571Smckusic 				brelse(nbp);
1986571Smckusic 				return (NULL);
1995972Swnj 			}
2006571Smckusic 			loc = 0;
2016571Smckusic 		} else {
2026571Smckusic 			loc += ep->d_reclen;
2035972Swnj 		}
2045972Swnj 		/*
2056571Smckusic 		 * calculate the next directory entry and run
2066571Smckusic 		 * some rudimentary bounds checks to make sure
2076571Smckusic 		 * that it is reasonable. If the check fails
2086571Smckusic 		 * resync at the beginning of the next directory
2096571Smckusic 		 * block.
2106571Smckusic 		 */
2116571Smckusic 		ep = (struct direct *)(bp->b_un.b_addr + loc);
2126571Smckusic 		i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
2136571Smckusic 		if (ep->d_reclen <= 0 || ep->d_reclen > i) {
2146571Smckusic 			loc += i;
2156571Smckusic 			u.u_offset += i;
2166571Smckusic 			continue;
2176571Smckusic 		}
2186571Smckusic 		/*
2196571Smckusic 		 * If an appropriate sized hole has not yet been found,
2206571Smckusic 		 * check to see if one is available. Also accumulate space
2216571Smckusic 		 * in the current block so that we can determine if
2226571Smckusic 		 * compaction is viable.
2236571Smckusic 		 */
2246571Smckusic 		if (slot != FOUND) {
2256571Smckusic 			size = ep->d_reclen;
2266571Smckusic 			if (ep->d_ino != 0)
2276571Smckusic 				size -= DIRSIZ(ep);
2286571Smckusic 			if (size > 0) {
2296571Smckusic 				if (size >= newsize) {
2306571Smckusic 					slot = FOUND;
2316571Smckusic 					entryfree = u.u_offset;
2326571Smckusic 					entrysize = DIRSIZ(ep) + newsize;
2336571Smckusic 				}
2346571Smckusic 				if (entryfree == NULL)
2356571Smckusic 					entryfree = u.u_offset;
2366571Smckusic 				spccnt += size;
2376571Smckusic 			}
2386571Smckusic 		}
2396571Smckusic 		/*
2405972Swnj 		 * String compare the directory entry
2415972Swnj 		 * and the current component.
2426571Smckusic 		 * If they do not match, continue to the next entry.
2435972Swnj 		 */
2446571Smckusic 		prevoff = curoff;
2456571Smckusic 		curoff = u.u_offset;
2466571Smckusic 		u.u_offset += ep->d_reclen;
2476571Smckusic 		if (ep->d_ino == 0)
2485972Swnj 			continue;
2496571Smckusic 		if (ep->d_namlen != u.u_dent.d_namlen)
2505972Swnj 			continue;
2516571Smckusic 		if (bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
2526571Smckusic 			continue;
2535972Swnj 		/*
2545972Swnj 		 * Here a component matched in a directory.
2555972Swnj 		 * If there is more pathname, go back to
2565972Swnj 		 * dirloop, otherwise return.
2575972Swnj 		 */
2586571Smckusic 		bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
2595972Swnj 		brelse(bp);
2606571Smckusic 		if (flag == 2 && *cp == '\0') {
2616571Smckusic 			brelse(nbp);
2626571Smckusic 			if (access(dp, IWRITE)) {
2636571Smckusic 				iput(dp);
2646571Smckusic 				return (NULL);
2656571Smckusic 			}
2666571Smckusic 			if (curoff % DIRBLKSIZ == 0) {
2676571Smckusic 				u.u_offset = curoff;
2686571Smckusic 				u.u_count = 0;
2696571Smckusic 				return (dp);
2706571Smckusic 			}
2716571Smckusic 			u.u_offset = prevoff;
2726571Smckusic 			u.u_count = DIRSIZ((struct direct *)
2736571Smckusic 			    (bp->b_un.b_addr + blkoff(fs, prevoff)));
2746571Smckusic 			return (dp);
27530Sbill 		}
2765972Swnj 		/*
2775972Swnj 		 * Special handling for ".."
2785972Swnj 		 */
2796571Smckusic 		if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
2806571Smckusic 		    u.u_dent.d_name[2] == '\0') {
2815972Swnj 			if (dp == u.u_rdir)
2825972Swnj 				u.u_dent.d_ino = dp->i_number;
2836571Smckusic 			else if (u.u_dent.d_ino == ROOTINO &&
2845972Swnj 			   dp->i_number == ROOTINO) {
2856571Smckusic 				for (i = 1; i < NMOUNT; i++)
2865972Swnj 					if (mount[i].m_bufp != NULL &&
2875972Swnj 					   mount[i].m_dev == dp->i_dev) {
2885972Swnj 						iput(dp);
2895972Swnj 						dp = mount[i].m_inodp;
2905972Swnj 						ilock(dp);
2915972Swnj 						dp->i_count++;
2926571Smckusic 						fs = dp->i_fs;
2935972Swnj 						cp -= 2;     /* back over .. */
2946384Swnj 						goto dirloop2;
2955972Swnj 					}
2965972Swnj 			}
2975972Swnj 		}
2985972Swnj 		d = dp->i_dev;
2995990Swnj 		pdp = dp;
3007119Smckusick 		iunlock(pdp);
3016571Smckusic 		dp = iget(d, fs, u.u_dent.d_ino);
3025990Swnj 		if (dp == NULL)  {
3037119Smckusick 			irele(pdp);
3046571Smckusic 			brelse(nbp);
3056571Smckusic 			return (NULL);
3065990Swnj 		}
3076571Smckusic 		fs = dp->i_fs;
3085972Swnj 		/*
3095972Swnj 		 * Check for symbolic link
3105972Swnj 		 */
3116571Smckusic 		if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
3126571Smckusic 			pathlen = strlen(cp) + 1;
3136571Smckusic 			if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
3146571Smckusic 			    ++nlink > MAXSYMLINKS) {
3155972Swnj 				u.u_error = ELOOP;
3167119Smckusick 				irele(pdp);
3176571Smckusic 				iput(dp);
3186571Smckusic 				brelse(nbp);
3196571Smckusic 				return (NULL);
3205972Swnj 			}
3216571Smckusic 			bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
3226571Smckusic 			bn =  bmap(dp, (daddr_t)0, B_READ);
3236571Smckusic 			if (bn < 0) {
3246571Smckusic 				printf("hole in symlink: %s i = %d\n",
3256571Smckusic 				    fs->fs_fsmnt, dp->i_number);
3267119Smckusick 				irele(pdp);
3276571Smckusic 				iput(dp);
3286571Smckusic 				brelse(nbp);
3296571Smckusic 				return (NULL);
3306571Smckusic 			}
3316571Smckusic 			bp = bread(dp->i_dev, fsbtodb(fs, bn),
3326571Smckusic 				   (int)blksize(fs, dp, (daddr_t)0));
3335972Swnj 			if (bp->b_flags & B_ERROR) {
3345972Swnj 				brelse(bp);
3357119Smckusick 				irele(pdp);
3366571Smckusic 				iput(dp);
3376571Smckusic 				brelse(nbp);
3386571Smckusic 				return (NULL);
3395972Swnj 			}
3406164Ssam 			bcopy(bp->b_un.b_addr, nbp->b_un.b_addr,
3416164Ssam 			  (unsigned)dp->i_size);
34230Sbill 			brelse(bp);
3435972Swnj 			cp = nbp->b_un.b_addr;
3445972Swnj 			iput(dp);
3455972Swnj 			if (*cp == '/') {
3467119Smckusick 				irele(pdp);
3475972Swnj 				while (*cp == '/')
3485972Swnj 					cp++;
3495972Swnj 				if ((dp = u.u_rdir) == NULL)
3505972Swnj 					dp = rootdir;
3515972Swnj 				ilock(dp);
3525972Swnj 				dp->i_count++;
3535972Swnj 			} else {
3545990Swnj 				dp = pdp;
3555990Swnj 				ilock(dp);
3565972Swnj 			}
3576571Smckusic 			fs = dp->i_fs;
3585972Swnj 			goto dirloop;
35930Sbill 		}
3607119Smckusick 		irele(pdp);
3615972Swnj 		if (*cp == '/') {
3625972Swnj 			while (*cp == '/')
3635972Swnj 				cp++;
3645972Swnj 			goto dirloop;
3655972Swnj 		}
3666571Smckusic 		/*
3676571Smckusic 		 * End of path, so return name matched.
3686571Smckusic 		 */
3696571Smckusic 		u.u_offset -= ep->d_reclen;
3706571Smckusic 		u.u_count = newsize;
3716571Smckusic 		brelse(nbp);
3726571Smckusic 		return (dp);
37330Sbill 	}
37430Sbill 	/*
3755972Swnj 	 * Search failed.
3766571Smckusic 	 * Report what is appropriate as per flag.
37730Sbill 	 */
3784823Swnj 	if (bp != NULL)
37930Sbill 		brelse(bp);
3806571Smckusic 	if (flag == 1 && *cp == '\0' && dp->i_nlink != 0) {
3816571Smckusic 		brelse(nbp);
3826571Smckusic 		if (access(dp, IWRITE)) {
3836571Smckusic 			iput(dp);
3846571Smckusic 			return (NULL);
3856571Smckusic 		}
3866571Smckusic 		if (slot == NONE) {
3876571Smckusic 			u.u_count = 0;
3886571Smckusic 		} else {
3896571Smckusic 			u.u_offset = entryfree;
3906571Smckusic 			u.u_count = entrysize;
3916571Smckusic 		}
3925972Swnj 		dp->i_flag |= IUPD|ICHG;
3936571Smckusic 		return (NULL);
39430Sbill 	}
3955972Swnj 	u.u_error = ENOENT;
39630Sbill 	iput(dp);
3975972Swnj 	brelse(nbp);
3986571Smckusic 	return (NULL);
39930Sbill }
40030Sbill 
40130Sbill /*
40230Sbill  * Return the next character from the
40330Sbill  * kernel string pointed at by dirp.
40430Sbill  */
40530Sbill schar()
40630Sbill {
40730Sbill 
4085972Swnj 	return (*u.u_dirp++ & 0377);
40930Sbill }
41030Sbill 
41130Sbill /*
41230Sbill  * Return the next character from the
41330Sbill  * user string pointed at by dirp.
41430Sbill  */
41530Sbill uchar()
41630Sbill {
41730Sbill 	register c;
41830Sbill 
41930Sbill 	c = fubyte(u.u_dirp++);
4205972Swnj 	if (c == -1) {
42130Sbill 		u.u_error = EFAULT;
4225972Swnj 		c = 0;
4235972Swnj 	}
4245972Swnj 	return (c);
42530Sbill }
4265972Swnj 
4275972Swnj #ifndef vax
4286571Smckusic bcmp(s1, s2, len)
4295972Swnj 	register char *s1, *s2;
4306571Smckusic 	register int len;
4315972Swnj {
4325972Swnj 
4336571Smckusic 	while (--len)
4346571Smckusic 		if (*s1++ != *s2++)
4355972Swnj 			return (1);
4365972Swnj 	return (0);
4375972Swnj }
4386571Smckusic 
4396571Smckusic strlen(s1)
4406571Smckusic 	register char *s1;
4416571Smckusic {
4426571Smckusic 	register int len;
4436571Smckusic 
4446571Smckusic 	for (len = 0; *s1++ != '\0'; len++)
4456571Smckusic 		/* void */;
4466571Smckusic 	return (len);
4476571Smckusic }
4485972Swnj #endif
449