xref: /original-bsd/sys/ufs/ufs/ufs_lookup.c (revision 9a96b58b)
1 /*	ufs_lookup.c	4.15	82/04/19	*/
2 
3 /* merged into kernel:	@(#)nami.c 2.3 4/8/82 */
4 
5 #include "../h/param.h"
6 #include "../h/systm.h"
7 #include "../h/inode.h"
8 #include "../h/fs.h"
9 #include "../h/mount.h"
10 #include "../h/dir.h"
11 #include "../h/user.h"
12 #include "../h/buf.h"
13 #include "../h/conf.h"
14 
15 /*
16  * Convert a pathname into a pointer to
17  * a locked inode.
18  *
19  * func = function called to get next char of name
20  *	&uchar if name is in user space
21  *	&schar if name is in system space
22  * flag = 0 if name is sought
23  *	1 if name is to be created
24  *	2 if name is to be deleted
25  * follow = 1 if links are to be followed at the end of the name
26  */
27 struct inode *
28 namei(func, flag, follow)
29 	int (*func)(), flag, follow;
30 {
31 	register struct inode *dp;
32 	register char *cp;
33 	register struct buf *bp, *nbp;
34 	register struct direct *ep;
35 	register struct fs *fs;
36 	struct inode *pdp;
37 	enum {NONE, COMPACT, FOUND} slot;
38 	int entryfree, entrysize;
39 	int spccnt, size, newsize;
40 	int loc, prevoff, curoff;
41 	int i, nlink, bsize;
42 	unsigned pathlen;
43 	daddr_t lbn, bn;
44 	dev_t d;
45 
46 	/*
47 	 * allocate name buffer; copy name
48 	 */
49 	nbp = geteblk(MAXPATHLEN);
50 	nlink = 0;
51 	for (i = 0, cp = nbp->b_un.b_addr; *cp = (*func)(); i++) {
52 		if ((*cp & 0377) == ('/'|0200)) {
53 			u.u_error = EPERM;
54 			break;
55 		}
56 #ifdef notdef
57 		if (*cp++ & 0200 && flag == 1 ||
58 		    cp >= nbp->b_un.b_addr + MAXPATHLEN) {
59 #else
60 		cp++;
61 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
62 #endif
63 			u.u_error = ENOENT;
64 			break;
65 		}
66 	}
67 	if (u.u_error) {
68 		brelse(nbp);
69 		return (NULL);
70 	}
71 	cp = nbp->b_un.b_addr;
72 	/*
73 	 * If name starts with '/' start from
74 	 * root; otherwise start from current dir.
75 	 */
76 	dp = u.u_cdir;
77 	if (*cp == '/') {
78 		while (*cp == '/')
79 			cp++;
80 		if ((dp = u.u_rdir) == NULL)
81 			dp = rootdir;
82 	}
83 	ilock(dp);
84 	dp->i_count++;
85 	fs = dp->i_fs;
86 	newsize = 0;
87 dirloop:
88 	/*
89 	 * dp must be a directory and
90 	 * must have X permission.
91 	 * cp is a path name relative to that directory.
92 	 */
93 	if ((dp->i_mode&IFMT) != IFDIR)
94 		u.u_error = ENOTDIR;
95 	(void) access(dp, IEXEC);
96 dirloop2:
97 	for (i = 0; *cp != '\0' && *cp != '/'; cp++) {
98 #ifdef notdef
99 		if (i >= MAXNAMLEN) {
100 			u.u_error = ENOENT;
101 			break;
102 		}
103 		u.u_dent.d_name[i] = *cp;
104 #else
105 		if (i < MAXNAMLEN) {
106 			u.u_dent.d_name[i] = *cp;
107 			i++;
108 		}
109 #endif
110 	}
111 	if (u.u_error) {
112 		iput(dp);
113 		brelse(nbp);
114 		return (NULL);
115 	}
116 	u.u_dent.d_namlen = i;
117 	u.u_dent.d_name[i] = '\0';
118 	newsize = DIRSIZ(&u.u_dent);
119 	u.u_pdir = dp;
120 	if (u.u_dent.d_name[0] == '\0') {	/* null name, e.g. "/" or "" */
121 		if (flag != 0) {
122 			u.u_error = ENOENT;
123 			iput(dp);
124 			dp = NULL;
125 		}
126 		u.u_offset = 0;
127 		u.u_count = newsize;
128 		brelse(nbp);
129 		return (dp);
130 	}
131 	/*
132 	 * set up to search a directory
133 	 */
134 	if (flag == 1)
135 		slot = NONE;
136 	else
137 		slot = FOUND;
138 	u.u_offset = 0;
139 	u.u_segflg = 1;
140 	bp = NULL;
141 	spccnt = 0;
142 	loc = 0;
143 	while (u.u_offset < dp->i_size) {
144 		/*
145 		 * check to see if enough space has been accumulated to make
146 		 * an entry by compaction. Reset the free space counter each
147 		 * time a directory block is crossed.
148 		 */
149 		if (slot == NONE) {
150 			if (spccnt >= newsize) {
151 				slot = COMPACT;
152 				entrysize = u.u_offset - entryfree;
153 			} else if (loc % DIRBLKSIZ == 0) {
154 				entryfree = NULL;
155 				spccnt = 0;
156 			}
157 		}
158 		/*
159 		 * If offset is on a block boundary,
160 		 * read the next directory block.
161 		 * Release previous if it exists.
162 		 */
163 		if (blkoff(fs, u.u_offset) == 0) {
164 			if (bp != NULL)
165 				brelse(bp);
166 			lbn = (daddr_t)lblkno(fs, u.u_offset);
167 			bsize = blksize(fs, dp, lbn);
168 			if ((bn = bmap(dp, lbn, B_READ)) < 0) {
169 				printf("hole in dir: %s i = %d\n",
170 				    fs->fs_fsmnt, dp->i_number);
171 				if (fs->fs_ronly != 0 ||
172 				    (bn = bmap(dp, lbn, B_WRITE, bsize)) < 0) {
173 					u.u_offset += bsize;
174 					bp = NULL;
175 					continue;
176 				}
177 			}
178 			bp = bread(dp->i_dev, fsbtodb(fs, bn), bsize);
179 			if (bp->b_flags & B_ERROR) {
180 				brelse(bp);
181 				iput(dp);
182 				brelse(nbp);
183 				return (NULL);
184 			}
185 			loc = 0;
186 		} else {
187 			loc += ep->d_reclen;
188 		}
189 		/*
190 		 * calculate the next directory entry and run
191 		 * some rudimentary bounds checks to make sure
192 		 * that it is reasonable. If the check fails
193 		 * resync at the beginning of the next directory
194 		 * block.
195 		 */
196 		ep = (struct direct *)(bp->b_un.b_addr + loc);
197 		i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
198 		if (ep->d_reclen <= 0 || ep->d_reclen > i) {
199 			loc += i;
200 			u.u_offset += i;
201 			continue;
202 		}
203 		/*
204 		 * If an appropriate sized hole has not yet been found,
205 		 * check to see if one is available. Also accumulate space
206 		 * in the current block so that we can determine if
207 		 * compaction is viable.
208 		 */
209 		if (slot != FOUND) {
210 			size = ep->d_reclen;
211 			if (ep->d_ino != 0)
212 				size -= DIRSIZ(ep);
213 			if (size > 0) {
214 				if (size >= newsize) {
215 					slot = FOUND;
216 					entryfree = u.u_offset;
217 					entrysize = DIRSIZ(ep) + newsize;
218 				}
219 				if (entryfree == NULL)
220 					entryfree = u.u_offset;
221 				spccnt += size;
222 			}
223 		}
224 		/*
225 		 * String compare the directory entry
226 		 * and the current component.
227 		 * If they do not match, continue to the next entry.
228 		 */
229 		prevoff = curoff;
230 		curoff = u.u_offset;
231 		u.u_offset += ep->d_reclen;
232 		if (ep->d_ino == 0)
233 			continue;
234 		if (ep->d_namlen != u.u_dent.d_namlen)
235 			continue;
236 		if (bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
237 			continue;
238 		/*
239 		 * Here a component matched in a directory.
240 		 * If there is more pathname, go back to
241 		 * dirloop, otherwise return.
242 		 */
243 		bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
244 		brelse(bp);
245 		if (flag == 2 && *cp == '\0') {
246 			brelse(nbp);
247 			if (access(dp, IWRITE)) {
248 				iput(dp);
249 				return (NULL);
250 			}
251 			if (curoff % DIRBLKSIZ == 0) {
252 				u.u_offset = curoff;
253 				u.u_count = 0;
254 				return (dp);
255 			}
256 			u.u_offset = prevoff;
257 			u.u_count = DIRSIZ((struct direct *)
258 			    (bp->b_un.b_addr + blkoff(fs, prevoff)));
259 			return (dp);
260 		}
261 		/*
262 		 * Special handling for ".."
263 		 */
264 		if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
265 		    u.u_dent.d_name[2] == '\0') {
266 			if (dp == u.u_rdir)
267 				u.u_dent.d_ino = dp->i_number;
268 			else if (u.u_dent.d_ino == ROOTINO &&
269 			   dp->i_number == ROOTINO) {
270 				for (i = 1; i < NMOUNT; i++)
271 					if (mount[i].m_bufp != NULL &&
272 					   mount[i].m_dev == dp->i_dev) {
273 						iput(dp);
274 						dp = mount[i].m_inodp;
275 						ilock(dp);
276 						dp->i_count++;
277 						fs = dp->i_fs;
278 						cp -= 2;     /* back over .. */
279 						goto dirloop2;
280 					}
281 			}
282 		}
283 		d = dp->i_dev;
284 		irele(dp);
285 		pdp = dp;
286 		dp = iget(d, fs, u.u_dent.d_ino);
287 		if (dp == NULL)  {
288 			iput(pdp);
289 			brelse(nbp);
290 			return (NULL);
291 		}
292 		fs = dp->i_fs;
293 		/*
294 		 * Check for symbolic link
295 		 */
296 		if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
297 			pathlen = strlen(cp) + 1;
298 			if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
299 			    ++nlink > MAXSYMLINKS) {
300 				u.u_error = ELOOP;
301 				iput(pdp);
302 				iput(dp);
303 				brelse(nbp);
304 				return (NULL);
305 			}
306 			bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
307 			bn =  bmap(dp, (daddr_t)0, B_READ);
308 			if (bn < 0) {
309 				printf("hole in symlink: %s i = %d\n",
310 				    fs->fs_fsmnt, dp->i_number);
311 				iput(pdp);
312 				iput(dp);
313 				brelse(nbp);
314 				return (NULL);
315 			}
316 			bp = bread(dp->i_dev, fsbtodb(fs, bn),
317 				   (int)blksize(fs, dp, (daddr_t)0));
318 			if (bp->b_flags & B_ERROR) {
319 				brelse(bp);
320 				iput(pdp);
321 				iput(dp);
322 				brelse(nbp);
323 				return (NULL);
324 			}
325 			bcopy(bp->b_un.b_addr, nbp->b_un.b_addr,
326 			  (unsigned)dp->i_size);
327 			brelse(bp);
328 			cp = nbp->b_un.b_addr;
329 			iput(dp);
330 			if (*cp == '/') {
331 				iput(pdp);
332 				while (*cp == '/')
333 					cp++;
334 				if ((dp = u.u_rdir) == NULL)
335 					dp = rootdir;
336 				ilock(dp);
337 				dp->i_count++;
338 			} else {
339 				dp = pdp;
340 				ilock(dp);
341 			}
342 			fs = dp->i_fs;
343 			goto dirloop;
344 		}
345 		iput(pdp);
346 		if (*cp == '/') {
347 			while (*cp == '/')
348 				cp++;
349 			goto dirloop;
350 		}
351 		/*
352 		 * End of path, so return name matched.
353 		 */
354 		u.u_offset -= ep->d_reclen;
355 		u.u_count = newsize;
356 		brelse(nbp);
357 		return (dp);
358 	}
359 	/*
360 	 * Search failed.
361 	 * Report what is appropriate as per flag.
362 	 */
363 	if (bp != NULL)
364 		brelse(bp);
365 	if (flag == 1 && *cp == '\0' && dp->i_nlink != 0) {
366 		brelse(nbp);
367 		if (access(dp, IWRITE)) {
368 			iput(dp);
369 			return (NULL);
370 		}
371 		if (slot == NONE) {
372 			u.u_count = 0;
373 		} else {
374 			u.u_offset = entryfree;
375 			u.u_count = entrysize;
376 		}
377 		dp->i_flag |= IUPD|ICHG;
378 		return (NULL);
379 	}
380 	u.u_error = ENOENT;
381 	iput(dp);
382 	brelse(nbp);
383 	return (NULL);
384 }
385 
386 /*
387  * Return the next character from the
388  * kernel string pointed at by dirp.
389  */
390 schar()
391 {
392 
393 	return (*u.u_dirp++ & 0377);
394 }
395 
396 /*
397  * Return the next character from the
398  * user string pointed at by dirp.
399  */
400 uchar()
401 {
402 	register c;
403 
404 	c = fubyte(u.u_dirp++);
405 	if (c == -1) {
406 		u.u_error = EFAULT;
407 		c = 0;
408 	}
409 	return (c);
410 }
411 
412 #ifndef vax
413 bcmp(s1, s2, len)
414 	register char *s1, *s2;
415 	register int len;
416 {
417 
418 	while (--len)
419 		if (*s1++ != *s2++)
420 			return (1);
421 	return (0);
422 }
423 
424 strlen(s1)
425 	register char *s1;
426 {
427 	register int len;
428 
429 	for (len = 0; *s1++ != '\0'; len++)
430 		/* void */;
431 	return (len);
432 }
433 #endif
434