xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 6571)
1*6571Smckusic /*	vfs_lookup.c	4.15	82/04/19	*/
230Sbill 
3*6571Smckusic /* merged into kernel:	@(#)nami.c 2.3 4/8/82 */
4*6571Smckusic 
530Sbill #include "../h/param.h"
630Sbill #include "../h/systm.h"
730Sbill #include "../h/inode.h"
8*6571Smckusic #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 
1530Sbill /*
1630Sbill  * Convert a pathname into a pointer to
175972Swnj  * a locked inode.
1830Sbill  *
1930Sbill  * func = function called to get next char of name
2030Sbill  *	&uchar if name is in user space
2130Sbill  *	&schar if name is in system space
2230Sbill  * flag = 0 if name is sought
2330Sbill  *	1 if name is to be created
2430Sbill  *	2 if name is to be deleted
255972Swnj  * follow = 1 if links are to be followed at the end of the name
2630Sbill  */
2730Sbill struct inode *
285972Swnj namei(func, flag, follow)
295972Swnj 	int (*func)(), flag, follow;
3030Sbill {
3130Sbill 	register struct inode *dp;
3230Sbill 	register char *cp;
335972Swnj 	register struct buf *bp, *nbp;
34194Sbill 	register struct direct *ep;
35*6571Smckusic 	register struct fs *fs;
365990Swnj 	struct inode *pdp;
37*6571Smckusic 	enum {NONE, COMPACT, FOUND} slot;
38*6571Smckusic 	int entryfree, entrysize;
39*6571Smckusic 	int spccnt, size, newsize;
40*6571Smckusic 	int loc, prevoff, curoff;
41*6571Smckusic 	int i, nlink, bsize;
42*6571Smckusic 	unsigned pathlen;
43*6571Smckusic 	daddr_t lbn, bn;
4430Sbill 	dev_t d;
4530Sbill 
4630Sbill 	/*
475972Swnj 	 * allocate name buffer; copy name
485972Swnj 	 */
49*6571Smckusic 	nbp = geteblk(MAXPATHLEN);
505972Swnj 	nlink = 0;
51*6571Smckusic 	for (i = 0, cp = nbp->b_un.b_addr; *cp = (*func)(); i++) {
52*6571Smckusic 		if ((*cp & 0377) == ('/'|0200)) {
536066Sroot 			u.u_error = EPERM;
546066Sroot 			break;
556066Sroot 		}
566066Sroot #ifdef notdef
57*6571Smckusic 		if (*cp++ & 0200 && flag == 1 ||
58*6571Smckusic 		    cp >= nbp->b_un.b_addr + MAXPATHLEN) {
596066Sroot #else
606066Sroot 		cp++;
61*6571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
626066Sroot #endif
635972Swnj 			u.u_error = ENOENT;
645972Swnj 			break;
655972Swnj 		}
665972Swnj 	}
675972Swnj 	if (u.u_error) {
68*6571Smckusic 		brelse(nbp);
69*6571Smckusic 		return (NULL);
705972Swnj 	}
715972Swnj 	cp = nbp->b_un.b_addr;
725972Swnj 	/*
7330Sbill 	 * If name starts with '/' start from
7430Sbill 	 * root; otherwise start from current dir.
7530Sbill 	 */
7630Sbill 	dp = u.u_cdir;
775972Swnj 	if (*cp == '/') {
785972Swnj 		while (*cp == '/')
795972Swnj 			cp++;
8030Sbill 		if ((dp = u.u_rdir) == NULL)
8130Sbill 			dp = rootdir;
8230Sbill 	}
835972Swnj 	ilock(dp);
845972Swnj 	dp->i_count++;
85*6571Smckusic 	fs = dp->i_fs;
86*6571Smckusic 	newsize = 0;
87*6571Smckusic dirloop:
8830Sbill 	/*
8930Sbill 	 * dp must be a directory and
9030Sbill 	 * must have X permission.
915972Swnj 	 * cp is a path name relative to that directory.
9230Sbill 	 */
934823Swnj 	if ((dp->i_mode&IFMT) != IFDIR)
9430Sbill 		u.u_error = ENOTDIR;
95194Sbill 	(void) access(dp, IEXEC);
966384Swnj dirloop2:
97*6571Smckusic 	for (i = 0; *cp != '\0' && *cp != '/'; cp++) {
986066Sroot #ifdef notdef
99*6571Smckusic 		if (i >= MAXNAMLEN) {
1005972Swnj 			u.u_error = ENOENT;
1015972Swnj 			break;
1025972Swnj 		}
103*6571Smckusic 		u.u_dent.d_name[i] = *cp;
1046066Sroot #else
105*6571Smckusic 		if (i < MAXNAMLEN) {
106*6571Smckusic 			u.u_dent.d_name[i] = *cp;
107*6571Smckusic 			i++;
108*6571Smckusic 		}
1096066Sroot #endif
1105972Swnj 	}
111*6571Smckusic 	if (u.u_error) {
112*6571Smckusic 		iput(dp);
113*6571Smckusic 		brelse(nbp);
114*6571Smckusic 		return (NULL);
115*6571Smckusic 	}
116*6571Smckusic 	u.u_dent.d_namlen = i;
117*6571Smckusic 	u.u_dent.d_name[i] = '\0';
118*6571Smckusic 	newsize = DIRSIZ(&u.u_dent);
1195990Swnj 	u.u_pdir = dp;
120*6571Smckusic 	if (u.u_dent.d_name[0] == '\0') {	/* null name, e.g. "/" or "" */
121*6571Smckusic 		if (flag != 0) {
1225972Swnj 			u.u_error = ENOENT;
123*6571Smckusic 			iput(dp);
124*6571Smckusic 			dp = NULL;
1255972Swnj 		}
126*6571Smckusic 		u.u_offset = 0;
127*6571Smckusic 		u.u_count = newsize;
128*6571Smckusic 		brelse(nbp);
129*6571Smckusic 		return (dp);
1305972Swnj 	}
131*6571Smckusic 	/*
132*6571Smckusic 	 * set up to search a directory
133*6571Smckusic 	 */
134*6571Smckusic 	if (flag == 1)
135*6571Smckusic 		slot = NONE;
136*6571Smckusic 	else
137*6571Smckusic 		slot = FOUND;
138*6571Smckusic 	u.u_offset = 0;
13930Sbill 	u.u_segflg = 1;
14030Sbill 	bp = NULL;
141*6571Smckusic 	spccnt = 0;
142*6571Smckusic 	loc = 0;
143*6571Smckusic 	while (u.u_offset < dp->i_size) {
1445972Swnj 		/*
145*6571Smckusic 		 * check to see if enough space has been accumulated to make
146*6571Smckusic 		 * an entry by compaction. Reset the free space counter each
147*6571Smckusic 		 * time a directory block is crossed.
148*6571Smckusic 		 */
149*6571Smckusic 		if (slot == NONE) {
150*6571Smckusic 			if (spccnt >= newsize) {
151*6571Smckusic 				slot = COMPACT;
152*6571Smckusic 				entrysize = u.u_offset - entryfree;
153*6571Smckusic 			} else if (loc % DIRBLKSIZ == 0) {
154*6571Smckusic 				entryfree = NULL;
155*6571Smckusic 				spccnt = 0;
156*6571Smckusic 			}
157*6571Smckusic 		}
158*6571Smckusic 		/*
1595972Swnj 		 * If offset is on a block boundary,
1605972Swnj 		 * read the next directory block.
1615972Swnj 		 * Release previous if it exists.
1625972Swnj 		 */
163*6571Smckusic 		if (blkoff(fs, u.u_offset) == 0) {
1645972Swnj 			if (bp != NULL)
1655972Swnj 				brelse(bp);
166*6571Smckusic 			lbn = (daddr_t)lblkno(fs, u.u_offset);
167*6571Smckusic 			bsize = blksize(fs, dp, lbn);
168*6571Smckusic 			if ((bn = bmap(dp, lbn, B_READ)) < 0) {
169*6571Smckusic 				printf("hole in dir: %s i = %d\n",
170*6571Smckusic 				    fs->fs_fsmnt, dp->i_number);
171*6571Smckusic 				if (fs->fs_ronly != 0 ||
172*6571Smckusic 				    (bn = bmap(dp, lbn, B_WRITE, bsize)) < 0) {
173*6571Smckusic 					u.u_offset += bsize;
174*6571Smckusic 					bp = NULL;
175*6571Smckusic 					continue;
176*6571Smckusic 				}
177*6571Smckusic 			}
178*6571Smckusic 			bp = bread(dp->i_dev, fsbtodb(fs, bn), bsize);
1795972Swnj 			if (bp->b_flags & B_ERROR) {
1805972Swnj 				brelse(bp);
181*6571Smckusic 				iput(dp);
182*6571Smckusic 				brelse(nbp);
183*6571Smckusic 				return (NULL);
1845972Swnj 			}
185*6571Smckusic 			loc = 0;
186*6571Smckusic 		} else {
187*6571Smckusic 			loc += ep->d_reclen;
1885972Swnj 		}
1895972Swnj 		/*
190*6571Smckusic 		 * calculate the next directory entry and run
191*6571Smckusic 		 * some rudimentary bounds checks to make sure
192*6571Smckusic 		 * that it is reasonable. If the check fails
193*6571Smckusic 		 * resync at the beginning of the next directory
194*6571Smckusic 		 * block.
195*6571Smckusic 		 */
196*6571Smckusic 		ep = (struct direct *)(bp->b_un.b_addr + loc);
197*6571Smckusic 		i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
198*6571Smckusic 		if (ep->d_reclen <= 0 || ep->d_reclen > i) {
199*6571Smckusic 			loc += i;
200*6571Smckusic 			u.u_offset += i;
201*6571Smckusic 			continue;
202*6571Smckusic 		}
203*6571Smckusic 		/*
204*6571Smckusic 		 * If an appropriate sized hole has not yet been found,
205*6571Smckusic 		 * check to see if one is available. Also accumulate space
206*6571Smckusic 		 * in the current block so that we can determine if
207*6571Smckusic 		 * compaction is viable.
208*6571Smckusic 		 */
209*6571Smckusic 		if (slot != FOUND) {
210*6571Smckusic 			size = ep->d_reclen;
211*6571Smckusic 			if (ep->d_ino != 0)
212*6571Smckusic 				size -= DIRSIZ(ep);
213*6571Smckusic 			if (size > 0) {
214*6571Smckusic 				if (size >= newsize) {
215*6571Smckusic 					slot = FOUND;
216*6571Smckusic 					entryfree = u.u_offset;
217*6571Smckusic 					entrysize = DIRSIZ(ep) + newsize;
218*6571Smckusic 				}
219*6571Smckusic 				if (entryfree == NULL)
220*6571Smckusic 					entryfree = u.u_offset;
221*6571Smckusic 				spccnt += size;
222*6571Smckusic 			}
223*6571Smckusic 		}
224*6571Smckusic 		/*
2255972Swnj 		 * String compare the directory entry
2265972Swnj 		 * and the current component.
227*6571Smckusic 		 * If they do not match, continue to the next entry.
2285972Swnj 		 */
229*6571Smckusic 		prevoff = curoff;
230*6571Smckusic 		curoff = u.u_offset;
231*6571Smckusic 		u.u_offset += ep->d_reclen;
232*6571Smckusic 		if (ep->d_ino == 0)
2335972Swnj 			continue;
234*6571Smckusic 		if (ep->d_namlen != u.u_dent.d_namlen)
2355972Swnj 			continue;
236*6571Smckusic 		if (bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
237*6571Smckusic 			continue;
2385972Swnj 		/*
2395972Swnj 		 * Here a component matched in a directory.
2405972Swnj 		 * If there is more pathname, go back to
2415972Swnj 		 * dirloop, otherwise return.
2425972Swnj 		 */
243*6571Smckusic 		bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
2445972Swnj 		brelse(bp);
245*6571Smckusic 		if (flag == 2 && *cp == '\0') {
246*6571Smckusic 			brelse(nbp);
247*6571Smckusic 			if (access(dp, IWRITE)) {
248*6571Smckusic 				iput(dp);
249*6571Smckusic 				return (NULL);
250*6571Smckusic 			}
251*6571Smckusic 			if (curoff % DIRBLKSIZ == 0) {
252*6571Smckusic 				u.u_offset = curoff;
253*6571Smckusic 				u.u_count = 0;
254*6571Smckusic 				return (dp);
255*6571Smckusic 			}
256*6571Smckusic 			u.u_offset = prevoff;
257*6571Smckusic 			u.u_count = DIRSIZ((struct direct *)
258*6571Smckusic 			    (bp->b_un.b_addr + blkoff(fs, prevoff)));
259*6571Smckusic 			return (dp);
26030Sbill 		}
2615972Swnj 		/*
2625972Swnj 		 * Special handling for ".."
2635972Swnj 		 */
264*6571Smckusic 		if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
265*6571Smckusic 		    u.u_dent.d_name[2] == '\0') {
2665972Swnj 			if (dp == u.u_rdir)
2675972Swnj 				u.u_dent.d_ino = dp->i_number;
268*6571Smckusic 			else if (u.u_dent.d_ino == ROOTINO &&
2695972Swnj 			   dp->i_number == ROOTINO) {
270*6571Smckusic 				for (i = 1; i < NMOUNT; i++)
2715972Swnj 					if (mount[i].m_bufp != NULL &&
2725972Swnj 					   mount[i].m_dev == dp->i_dev) {
2735972Swnj 						iput(dp);
2745972Swnj 						dp = mount[i].m_inodp;
2755972Swnj 						ilock(dp);
2765972Swnj 						dp->i_count++;
277*6571Smckusic 						fs = dp->i_fs;
2785972Swnj 						cp -= 2;     /* back over .. */
2796384Swnj 						goto dirloop2;
2805972Swnj 					}
2815972Swnj 			}
2825972Swnj 		}
2835972Swnj 		d = dp->i_dev;
2845990Swnj 		irele(dp);
2855990Swnj 		pdp = dp;
286*6571Smckusic 		dp = iget(d, fs, u.u_dent.d_ino);
2875990Swnj 		if (dp == NULL)  {
2885990Swnj 			iput(pdp);
289*6571Smckusic 			brelse(nbp);
290*6571Smckusic 			return (NULL);
2915990Swnj 		}
292*6571Smckusic 		fs = dp->i_fs;
2935972Swnj 		/*
2945972Swnj 		 * Check for symbolic link
2955972Swnj 		 */
296*6571Smckusic 		if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
297*6571Smckusic 			pathlen = strlen(cp) + 1;
298*6571Smckusic 			if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
299*6571Smckusic 			    ++nlink > MAXSYMLINKS) {
3005972Swnj 				u.u_error = ELOOP;
3015990Swnj 				iput(pdp);
302*6571Smckusic 				iput(dp);
303*6571Smckusic 				brelse(nbp);
304*6571Smckusic 				return (NULL);
3055972Swnj 			}
306*6571Smckusic 			bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
307*6571Smckusic 			bn =  bmap(dp, (daddr_t)0, B_READ);
308*6571Smckusic 			if (bn < 0) {
309*6571Smckusic 				printf("hole in symlink: %s i = %d\n",
310*6571Smckusic 				    fs->fs_fsmnt, dp->i_number);
311*6571Smckusic 				iput(pdp);
312*6571Smckusic 				iput(dp);
313*6571Smckusic 				brelse(nbp);
314*6571Smckusic 				return (NULL);
315*6571Smckusic 			}
316*6571Smckusic 			bp = bread(dp->i_dev, fsbtodb(fs, bn),
317*6571Smckusic 				   (int)blksize(fs, dp, (daddr_t)0));
3185972Swnj 			if (bp->b_flags & B_ERROR) {
3195972Swnj 				brelse(bp);
3205990Swnj 				iput(pdp);
321*6571Smckusic 				iput(dp);
322*6571Smckusic 				brelse(nbp);
323*6571Smckusic 				return (NULL);
3245972Swnj 			}
3256164Ssam 			bcopy(bp->b_un.b_addr, nbp->b_un.b_addr,
3266164Ssam 			  (unsigned)dp->i_size);
32730Sbill 			brelse(bp);
3285972Swnj 			cp = nbp->b_un.b_addr;
3295972Swnj 			iput(dp);
3305972Swnj 			if (*cp == '/') {
3315990Swnj 				iput(pdp);
3325972Swnj 				while (*cp == '/')
3335972Swnj 					cp++;
3345972Swnj 				if ((dp = u.u_rdir) == NULL)
3355972Swnj 					dp = rootdir;
3365972Swnj 				ilock(dp);
3375972Swnj 				dp->i_count++;
3385972Swnj 			} else {
3395990Swnj 				dp = pdp;
3405990Swnj 				ilock(dp);
3415972Swnj 			}
342*6571Smckusic 			fs = dp->i_fs;
3435972Swnj 			goto dirloop;
34430Sbill 		}
3455990Swnj 		iput(pdp);
3465972Swnj 		if (*cp == '/') {
3475972Swnj 			while (*cp == '/')
3485972Swnj 				cp++;
3495972Swnj 			goto dirloop;
3505972Swnj 		}
351*6571Smckusic 		/*
352*6571Smckusic 		 * End of path, so return name matched.
353*6571Smckusic 		 */
354*6571Smckusic 		u.u_offset -= ep->d_reclen;
355*6571Smckusic 		u.u_count = newsize;
356*6571Smckusic 		brelse(nbp);
357*6571Smckusic 		return (dp);
35830Sbill 	}
35930Sbill 	/*
3605972Swnj 	 * Search failed.
361*6571Smckusic 	 * Report what is appropriate as per flag.
36230Sbill 	 */
3634823Swnj 	if (bp != NULL)
36430Sbill 		brelse(bp);
365*6571Smckusic 	if (flag == 1 && *cp == '\0' && dp->i_nlink != 0) {
366*6571Smckusic 		brelse(nbp);
367*6571Smckusic 		if (access(dp, IWRITE)) {
368*6571Smckusic 			iput(dp);
369*6571Smckusic 			return (NULL);
370*6571Smckusic 		}
371*6571Smckusic 		if (slot == NONE) {
372*6571Smckusic 			u.u_count = 0;
373*6571Smckusic 		} else {
374*6571Smckusic 			u.u_offset = entryfree;
375*6571Smckusic 			u.u_count = entrysize;
376*6571Smckusic 		}
3775972Swnj 		dp->i_flag |= IUPD|ICHG;
378*6571Smckusic 		return (NULL);
37930Sbill 	}
3805972Swnj 	u.u_error = ENOENT;
38130Sbill 	iput(dp);
3825972Swnj 	brelse(nbp);
383*6571Smckusic 	return (NULL);
38430Sbill }
38530Sbill 
38630Sbill /*
38730Sbill  * Return the next character from the
38830Sbill  * kernel string pointed at by dirp.
38930Sbill  */
39030Sbill schar()
39130Sbill {
39230Sbill 
3935972Swnj 	return (*u.u_dirp++ & 0377);
39430Sbill }
39530Sbill 
39630Sbill /*
39730Sbill  * Return the next character from the
39830Sbill  * user string pointed at by dirp.
39930Sbill  */
40030Sbill uchar()
40130Sbill {
40230Sbill 	register c;
40330Sbill 
40430Sbill 	c = fubyte(u.u_dirp++);
4055972Swnj 	if (c == -1) {
40630Sbill 		u.u_error = EFAULT;
4075972Swnj 		c = 0;
4085972Swnj 	}
4095972Swnj 	return (c);
41030Sbill }
4115972Swnj 
4125972Swnj #ifndef vax
413*6571Smckusic bcmp(s1, s2, len)
4145972Swnj 	register char *s1, *s2;
415*6571Smckusic 	register int len;
4165972Swnj {
4175972Swnj 
418*6571Smckusic 	while (--len)
419*6571Smckusic 		if (*s1++ != *s2++)
4205972Swnj 			return (1);
4215972Swnj 	return (0);
4225972Swnj }
423*6571Smckusic 
424*6571Smckusic strlen(s1)
425*6571Smckusic 	register char *s1;
426*6571Smckusic {
427*6571Smckusic 	register int len;
428*6571Smckusic 
429*6571Smckusic 	for (len = 0; *s1++ != '\0'; len++)
430*6571Smckusic 		/* void */;
431*6571Smckusic 	return (len);
432*6571Smckusic }
4335972Swnj #endif
434