xref: /original-bsd/sys/ufs/ffs/ufs_lookup.c (revision 9de3f1a2)
1 /*	ufs_lookup.c	4.22	82/08/14	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/inode.h"
6 #include "../h/fs.h"
7 #include "../h/mount.h"
8 #include "../h/dir.h"
9 #include "../h/user.h"
10 #include "../h/buf.h"
11 #include "../h/conf.h"
12 
13 struct	buf *blkatoff();
14 int	dirchk = 1;
15 /*
16  * Convert a pathname into a pointer to a locked inode,
17  * with side effects usable in creating and removing files.
18  * This is a very central and rather complicated routine.
19  *
20  * The func argument gives the routine which returns successive
21  * characters of the name to be translated.  The flag
22  * argument is (0, 1, 2) depending on whether the name is to be
23  * (looked up, created, deleted).  The follow argument is 1 when
24  * symbolic links are to be followed when they occur at the end of
25  * the name translation process.
26  *
27  * Overall outline:
28  *
29  *	copy in name
30  *	get starting directory
31  * dirloop:
32  *	check accessibility of directory
33  * dirloop2:
34  *	copy next component of name to u.u_dent
35  *	handle degenerate case where name is null string
36  *	search for name in directory, to found or notfound
37  * notfound:
38  *	if creating, return locked inode, leaving information on avail. slots
39  *	else return error
40  * found:
41  *	if at end of path and deleting, return information to allow delete
42  *	if .. and on mounted filesys, look in mount table for parent
43  *	if symbolic link, massage name in buffer and continue at dirloop
44  *	if more components of name, do next level at dirloop
45  *	return the answer as locked inode
46  */
47 struct inode *
48 namei(func, flag, follow)
49 	int (*func)(), flag, follow;
50 {
51 	register char *cp;		/* pointer into pathname argument */
52 /* these variables refer to things which must be freed or unlocked */
53 	register struct inode *dp = 0;	/* the directory we are searching */
54 	register struct fs *fs;		/* file system that directory is in */
55 	register struct buf *bp = 0;	/* a buffer of directory entries */
56 	register struct direct *ep;	/* the current directory entry */
57 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
58 	register struct buf *nbp;	/* buffer storing path name argument */
59 /* these variables hold information about the search for a slot */
60 	enum {NONE, COMPACT, FOUND} slotstatus;
61 	int slotoffset = -1;		/* offset of area with free space */
62 	int slotsize;			/* size of area at slotoffset */
63 	int slotfreespace;		/* amount of space free in slot */
64 	int slotneeded;			/* size of the entry we're seeking */
65 /* */
66 	int dirsize;
67 	int prevoff;			/* u.u_offset of previous entry */
68 	int nlink = 0;			/* number of symbolic links taken */
69 	struct inode *pdp;		/* saved dp during symlink work */
70 	int i;
71 
72 	/*
73 	 * Get a buffer for the name to be translated, and copy the
74 	 * name into the buffer.
75 	 */
76 	nbp = geteblk(MAXPATHLEN);
77 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
78 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
79 			u.u_error = EPERM;
80 			goto bad;
81 		}
82 		cp++;
83 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
84 			u.u_error = ENOENT;
85 			goto bad;
86 		}
87 	}
88 	if (u.u_error)
89 		goto bad;
90 
91 	/*
92 	 * Get starting directory.
93 	 */
94 	cp = nbp->b_un.b_addr;
95 	if (*cp == '/') {
96 		while (*cp == '/')
97 			cp++;
98 		if ((dp = u.u_rdir) == NULL)
99 			dp = rootdir;
100 	} else
101 		dp = u.u_cdir;
102 	fs = dp->i_fs;
103 	ilock(dp);
104 	dp->i_count++;
105 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
106 
107 	/*
108 	 * We come to dirloop to search a new directory.
109 	 * The directory must be locked so that it can be
110 	 * iput, and fs must be already set to dp->i_fs.
111 	 */
112 dirloop:
113 	/*
114 	 * Check accessiblity of directory.
115 	 */
116 	if ((dp->i_mode&IFMT) != IFDIR) {
117 		u.u_error = ENOTDIR;
118 		goto bad;
119 	}
120 	if (access(dp, IEXEC))
121 		goto bad;
122 
123 dirloop2:
124 	/*
125 	 * Copy next component of name to u.u_dent.
126 	 */
127 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
128 		if (i >= MAXNAMLEN) {
129 			u.u_error = ENOENT;
130 			goto bad;
131 		}
132 		u.u_dent.d_name[i++] = *cp;
133 	}
134 	u.u_dent.d_namlen = i;
135 	u.u_dent.d_name[i] = 0;
136 
137 	/*
138 	 * Check for degenerate name (e.g. / or "")
139 	 * which is a way of talking about a directory,
140 	 * e.g. like "/." or ".".
141 	 */
142 	if (u.u_dent.d_name[0] == 0) {
143 		if (flag) {
144 			u.u_error = ENOENT;
145 			goto bad;
146 		}
147 		brelse(nbp);
148 		return (dp);
149 	}
150 
151 	/*
152 	 * Suppress search for slots unless creating
153 	 * file and at end of pathname, in which case
154 	 * we watch for a place to put the new file in
155 	 * case it doesn't already exist.
156 	 */
157 	slotstatus = FOUND;
158 	if (flag == 1 && *cp == 0) {
159 		slotstatus = NONE;
160 		slotfreespace = 0;
161 		slotneeded = DIRSIZ(&u.u_dent);
162 	}
163 
164 	dirsize = roundup(dp->i_size, DIRBLKSIZ);
165 	u.u_offset = 0;
166 	while (u.u_offset < dirsize) {
167 		/*
168 		 * If offset is on a block boundary,
169 		 * read the next directory block.
170 		 * Release previous if it exists.
171 		 */
172 		if (blkoff(fs, u.u_offset) == 0) {
173 			if (bp != NULL)
174 				brelse(bp);
175 			bp = blkatoff(dp, u.u_offset, (char **)0);
176 			if (bp == 0)
177 				goto bad;
178 			entryoffsetinblock = 0;
179 		}
180 
181 		/*
182 		 * If still looking for a slot, and at a DIRBLKSIZE
183 		 * boundary, have to start looking for free space
184 		 * again.
185 		 */
186 		if (slotstatus == NONE &&
187 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
188 			slotoffset = -1;
189 			slotfreespace = 0;
190 		}
191 
192 		/*
193 		 * Get pointer to next entry, and do consistency checking:
194 		 *	record length must be multiple of 4
195 		 *	record length must not be zero
196 		 *	entry must fit in rest of this DIRBLKSIZ block
197 		 *	record must be large enough to contain name
198 		 * When dirchk is set we also check:
199 		 *	name is not longer than MAXNAMLEN
200 		 *	name must be as long as advertised, and null terminated
201 		 * Checking last two conditions is done only when dirchk is
202 		 * set, to save time.
203 		 */
204 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
205 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
206 		if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
207 		    ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
208 		    dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
209 			dirbad(dp, "mangled entry");
210 			u.u_offset += i;
211 			entryoffsetinblock += i;
212 			continue;
213 		}
214 
215 		/*
216 		 * If an appropriate sized slot has not yet been found,
217 		 * check to see if one is available. Also accumulate space
218 		 * in the current block so that we can determine if
219 		 * compaction is viable.
220 		 */
221 		if (slotstatus != FOUND) {
222 			int size = ep->d_reclen;
223 
224 			if (ep->d_ino != 0)
225 				size -= DIRSIZ(ep);
226 			if (size > 0) {
227 				if (size >= slotneeded) {
228 					slotstatus = FOUND;
229 					slotoffset = u.u_offset;
230 					slotsize = ep->d_reclen;
231 				} else if (slotstatus == NONE) {
232 					slotfreespace += size;
233 					if (slotoffset == -1)
234 						slotoffset = u.u_offset;
235 					if (slotfreespace >= slotneeded) {
236 						slotstatus = COMPACT;
237 						slotsize =
238 						    u.u_offset+ep->d_reclen -
239 						      slotoffset;
240 					}
241 				}
242 			}
243 		}
244 
245 		/*
246 		 * Check for a name match.
247 		 */
248 		if (ep->d_ino) {
249 			if (ep->d_namlen == u.u_dent.d_namlen &&
250 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
251 				goto found;
252 		}
253 		prevoff = u.u_offset;
254 		u.u_offset += ep->d_reclen;
255 		entryoffsetinblock += ep->d_reclen;
256 	}
257 /* notfound: */
258 	/*
259 	 * If creating, and at end of pathname and current
260 	 * directory has not been removed, then can consider allowing
261 	 * file to be created.
262 	 */
263 	if (flag == 1 && *cp == 0 && dp->i_nlink != 0) {
264 		/*
265 		 * Access for write is interpreted as allowing
266 		 * creation of files in the directory.
267 		 */
268 		if (access(dp, IWRITE))
269 			goto bad;
270 		/*
271 		 * Return an indication of where the new directory
272 		 * entry should be put.  If we didn't find a slot,
273 		 * then set u.u_count to 0 indicating that the
274 		 * new slot belongs at the end of the directory.
275 		 * If we found a slot, then the new entry can be
276 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
277 		 */
278 		if (slotstatus == NONE)
279 			u.u_count = 0;
280 		else {
281 			u.u_offset = slotoffset;
282 			u.u_count = slotsize;
283 		}
284 		dp->i_flag |= IUPD|ICHG;
285 		if (bp)
286 			brelse(bp);
287 		brelse(nbp);
288 		/*
289 		 * We return with the directory locked, so that
290 		 * the parameters we set up above will still be
291 		 * valid if we actually decide to do a direnter().
292 		 * We return NULL to indicate that the entry doesn't
293 		 * currently exist, leaving a pointer to the (locked)
294 		 * directory inode in u.u_pdir.
295 		 */
296 		u.u_pdir = dp;
297 		return (NULL);
298 	}
299 	u.u_error = ENOENT;
300 	goto bad;
301 found:
302 	/*
303 	 * Check that directory length properly reflects presence
304 	 * of this entry.
305 	 */
306 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
307 		dirbad(dp, "i_size too small");
308 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
309 		dp->i_flag |= IUPD|ICHG;
310 	}
311 
312 	/*
313 	 * Found component in pathname; save directory
314 	 * entry in u.u_dent, and release directory buffer.
315 	 */
316 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
317 	brelse(bp);
318 	bp = NULL;
319 
320 	/*
321 	 * If deleting, and at end of pathname, return
322 	 * parameters which can be used to remove file.
323 	 * Note that in this case we return the directory
324 	 * inode, not the inode of the file being deleted.
325 	 */
326 	if (flag == 2 && *cp == 0) {
327 		/*
328 		 * Write access to directory required to delete files.
329 		 */
330 		if (access(dp, IWRITE))
331 			goto bad;
332 		/*
333 		 * Return pointer to current entry in u.u_offset,
334 		 * and distance past previous entry (if there
335 		 * is a previous entry in this block) in u.u_count.
336 		 * Save directory inode pointer in u.u_pdir for dirremove().
337 		 */
338 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
339 			u.u_count = 0;
340 		else
341 			u.u_count = u.u_offset - prevoff;
342 		brelse(nbp);
343 		u.u_pdir = dp;		/* for dirremove() */
344 		return (dp);
345 	}
346 
347 	/*
348 	 * Special handling for ".." allowing chdir out of mounted
349 	 * file system: indirect .. in root inode to reevaluate
350 	 * in directory file system was mounted on.
351 	 */
352 	if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
353 	    u.u_dent.d_name[2] == '\0') {
354 		if (dp == u.u_rdir)
355 			u.u_dent.d_ino = dp->i_number;
356 		else if (u.u_dent.d_ino == ROOTINO &&
357 		   dp->i_number == ROOTINO) {
358 			for (i = 1; i < NMOUNT; i++)
359 			if (mount[i].m_bufp != NULL &&
360 			   mount[i].m_dev == dp->i_dev) {
361 				iput(dp);
362 				dp = mount[i].m_inodp;
363 				ilock(dp);
364 				dp->i_count++;
365 				fs = dp->i_fs;
366 				cp -= 2;     /* back over .. */
367 				goto dirloop2;
368 			}
369 		}
370 	}
371 
372 	/*
373 	 * Check for symbolic link, which may require us
374 	 * to massage the name before we continue translation.
375 	 * To avoid deadlock have to unlock the current directory,
376 	 * but don't iput it because we may need it again (if
377 	 * the symbolic link is relative to .).  Instead save
378 	 * it (unlocked) as pdp.
379 	 */
380 	pdp = dp;
381 	iunlock(pdp);
382 	dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
383 	if (dp == NULL)
384 		goto bad2;
385 	fs = dp->i_fs;
386 
387 	/*
388 	 * Check for symbolic link
389 	 */
390 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
391 		int pathlen = strlen(cp) + 1;
392 		int bn;
393 
394 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
395 		    ++nlink > MAXSYMLINKS) {
396 			u.u_error = ELOOP;
397 			goto bad2;
398 		}
399 		bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
400 		u.u_error =
401 		    readip1(dp, nbp->b_un.b_addr, dp->i_size, 0, 1, 0);
402 		if (u.u_error)
403 			goto bad2;
404 		cp = nbp->b_un.b_addr;
405 		iput(dp);
406 		if (*cp == '/') {
407 			irele(pdp);
408 			while (*cp == '/')
409 				cp++;
410 			if ((dp = u.u_rdir) == NULL)
411 				dp = rootdir;
412 			ilock(dp);
413 			dp->i_count++;
414 		} else {
415 			dp = pdp;
416 			ilock(dp);
417 		}
418 		fs = dp->i_fs;
419 		goto dirloop;
420 	}
421 	irele(pdp);
422 
423 	/*
424 	 * Not a symbolic link.  If more pathname,
425 	 * continue at next component, else return.
426 	 */
427 	if (*cp == '/') {
428 		while (*cp == '/')
429 			cp++;
430 		goto dirloop;
431 	}
432 	brelse(nbp);
433 	return (dp);
434 bad2:
435 	irele(pdp);
436 bad:
437 	if (bp)
438 		brelse(bp);
439 	if (dp)
440 		iput(dp);
441 	brelse(nbp);
442 	return (NULL);
443 }
444 
445 dirbad(ip, how)
446 	struct inode *ip;
447 	char *how;
448 {
449 
450 	printf("%s: bad dir ino %d at offset %d: %s\n",
451 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
452 }
453 
454 dirbadname(ep)
455 	register struct direct *ep;
456 {
457 	register char *cp;
458 	register int i;
459 
460 	for (i = 0; i < ep->d_namlen; i++)
461 		if (ep->d_name[i] == 0)
462 			return (1);
463 	return (ep->d_name[i]);
464 }
465 
466 /*
467  * Write a directory entry after a call to namei, using the parameters
468  * which it left in the u. area.  The argument ip is the inode which
469  * the new directory entry will refer to.  The u. area field u.u_pdir is
470  * a pointer to the directory to be written, which was left locked by
471  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
472  * how the space for the new entry is to be gotten.
473  */
474 direnter(ip)
475 	struct inode *ip;
476 {
477 	register struct direct *ep, *nep;
478 	struct fs *fs;
479 	struct buf *bp;
480 	int loc, dsize, freespace, newentrysize;
481 	char *dirbuf;
482 
483 	u.u_dent.d_ino = ip->i_number;
484 	u.u_segflg = 1;
485 	newentrysize = DIRSIZ(&u.u_dent);
486 	if (u.u_count == 0) {
487 		/*
488 		 * If u.u_count is 0, then namei could find no space in the
489 		 * directory.  In this case u.u_offset will be on a directory
490 		 * block boundary and we will write the new entry into a fresh
491 		 * block.
492 		 */
493 		if (u.u_offset&(DIRBLKSIZ-1))
494 			panic("wdir: newblk");
495 		u.u_dent.d_reclen = DIRBLKSIZ;
496 		u.u_count = newentrysize;
497 		u.u_base = (caddr_t)&u.u_dent;
498 		u.u_segflg = 1;
499 		writei(u.u_pdir);
500 		iput(u.u_pdir);
501 		return;
502 	}
503 
504 	/*
505 	 * If u.u_count is non-zero, then namei found space for the
506 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
507 	 * in the directory.  To use this space, we may have to compact
508 	 * the entries located there, by copying them together towards
509 	 * the beginning of the block, leaving the free space in
510 	 * one usable chunk at the end.
511 	 */
512 
513 	/*
514 	 * Increase size of directory if entry eats into new space.
515 	 * This should never push the size past a new multiple of
516 	 * DIRBLKSIZE.
517 	 */
518 	if (u.u_offset+u.u_count > u.u_pdir->i_size) {
519 /*ZZ*/		if (((u.u_offset+u.u_count-1)&~(DIRBLKSIZ-1)) !=
520 /*ZZ*/		    ((u.u_pdir->i_size-1)&~(DIRBLKSIZ-1))) {
521 /*ZZ*/			panic("wdir: span");
522 /*ZZ*/		}
523 		u.u_pdir->i_size = u.u_offset + u.u_count;
524 	}
525 
526 	/*
527 	 * Get the block containing the space for the new directory
528 	 * entry.
529 	 */
530 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
531 	if (bp == 0)
532 		return;
533 
534 	/*
535 	 * Find space for the new entry.  In the simple case, the
536 	 * entry at offset base will have the space.  If it does
537 	 * not, then namei arranged that compacting the region
538 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
539 	 */
540 	ep = (struct direct *)dirbuf;
541 	dsize = DIRSIZ(ep);
542 	freespace = ep->d_reclen - dsize;
543 	for (loc = ep->d_reclen; loc < u.u_count; ) {
544 		nep = (struct direct *)(dirbuf + loc);
545 		if (ep->d_ino) {
546 			/* trim the existing slot */
547 			ep->d_reclen = dsize;
548 			ep = (struct direct *)((char *)ep + dsize);
549 		} else {
550 			/* overwrite; nothing there; header is ours */
551 			freespace += dsize;
552 		}
553 		dsize = DIRSIZ(nep);
554 		freespace += nep->d_reclen - dsize;
555 		loc += nep->d_reclen;
556 /*ZZ*/if((loc&~0x1ff)!=(loc+nep->d_reclen-1&~0x1ff))
557 /*ZZ*/printf("wdir: compact loc %d reclen %d (dir %s/%d)\n",loc,nep->d_reclen,
558 /*ZZ*/u.u_pdir->i_fs->fs_fsmnt,u.u_pdir->i_number);
559 		bcopy(nep, ep, dsize);
560 	}
561 	/*
562 	 * Update the pointer fields in the previous entry (if any),
563 	 * copy in the new entry, and write out the block.
564 	 */
565 	if (ep->d_ino == 0) {
566 		if (freespace + dsize < newentrysize)
567 			panic("wdir: compact1");
568 /*ZZ*/if(freespace+dsize>512)panic("wdir: compact screwup");
569 		u.u_dent.d_reclen = freespace + dsize;
570 	} else {
571 		if (freespace < newentrysize)
572 			panic("wdir: compact2");
573 		u.u_dent.d_reclen = freespace;
574 /*ZZ*/if ((((char *)ep-bp->b_un.b_addr)&0x1ff)+dsize>512) panic("wdir: reclen");
575 		ep->d_reclen = dsize;
576 		ep = (struct direct *)((char *)ep + dsize);
577 	}
578 /*ZZ*/if((((char*)ep-bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen>512)panic("wdir: botch");
579 	bcopy(&u.u_dent, ep, newentrysize);
580 	bwrite(bp);
581 	u.u_pdir->i_flag |= IUPD|ICHG;
582 	iput(u.u_pdir);
583 }
584 
585 dirremove()
586 {
587 	register struct inode *dp = u.u_pdir;
588 	register struct fs *fs = dp->i_fs;
589 	register struct buf *bp;
590 	struct direct *ep;
591 
592 	if (u.u_count == 0) {
593 		/*
594 		 * First entry in block: set d_ino to zero.
595 		 */
596 /*ZZ*/if(u.u_offset&0x1ff)printf("missed dir compact dir %s/%d off %d file %s\n"
597 /*ZZ*/,dp->i_fs->fs_fsmnt,dp->i_number,u.u_offset,u.u_dent.d_name);
598 		u.u_base = (caddr_t)&u.u_dent;
599 		u.u_count = DIRSIZ(&u.u_dent);
600 		u.u_segflg = 1;
601 		u.u_dent.d_ino = 0;
602 		writei(dp);
603 	} else {
604 		/*
605 		 * Collapse new free space into previous entry.
606 		 */
607 		bp = blkatoff(dp, u.u_offset - u.u_count, (char **)&ep);
608 		if (bp == 0)
609 			return (0);
610 		ep->d_reclen += u.u_dent.d_reclen;
611 /*ZZ*/if((((char *)ep - bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen > 512)
612 /*ZZ*/	panic("unlink: reclen");
613 		bwrite(bp);
614 		dp->i_flag |= IUPD|ICHG;
615 	}
616 	return (1);
617 }
618 
619 /*
620  * Return buffer with contents of block "offset"
621  * from the beginning of directory "ip".  If "res"
622  * is non-zero, fill it in with a pointer to the
623  * remaining space in the directory.
624  */
625 struct buf *
626 blkatoff(ip, offset, res)
627 	struct inode *ip;
628 	off_t offset;
629 	char **res;
630 {
631 	register struct fs *fs = ip->i_fs;
632 	int lbn = lblkno(fs, offset);
633 	int base = blkoff(fs, offset);
634 	int bsize = blksize(fs, ip, lbn);
635 	int bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
636 	register struct buf *bp;
637 
638 	if (u.u_error)
639 		return (0);
640 	bp = bread(ip->i_dev, bn, bsize);
641 	if (bp->b_flags & B_ERROR) {
642 		brelse(bp);
643 		return (0);
644 	}
645 	if (res)
646 		*res = bp->b_un.b_addr + base;
647 	return (bp);
648 }
649