xref: /original-bsd/sbin/fsck/inode.c (revision 92ab646d)
1 /*
2  * Copyright (c) 1980, 1986 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)inode.c	5.16 (Berkeley) 07/20/90";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <ufs/dinode.h>
14 #include <ufs/fs.h>
15 #include <ufs/dir.h>
16 #include <pwd.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include "fsck.h"
20 
21 static ino_t startinum;
22 
23 ckinode(dp, idesc)
24 	struct dinode *dp;
25 	register struct inodesc *idesc;
26 {
27 	register daddr_t *ap;
28 	long ret, n, ndb, offset;
29 	struct dinode dino;
30 
31 	idesc->id_fix = DONTKNOW;
32 	idesc->id_entryno = 0;
33 	idesc->id_filesize = dp->di_size;
34 	if ((dp->di_mode & IFMT) == IFBLK || (dp->di_mode & IFMT) == IFCHR)
35 		return (KEEPON);
36 	dino = *dp;
37 	ndb = howmany(dino.di_size, sblock.fs_bsize);
38 	for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
39 		if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
40 			idesc->id_numfrags =
41 				numfrags(&sblock, fragroundup(&sblock, offset));
42 		else
43 			idesc->id_numfrags = sblock.fs_frag;
44 		if (*ap == 0)
45 			continue;
46 		idesc->id_blkno = *ap;
47 		if (idesc->id_type == ADDR)
48 			ret = (*idesc->id_func)(idesc);
49 		else
50 			ret = dirscan(idesc);
51 		if (ret & STOP)
52 			return (ret);
53 	}
54 	idesc->id_numfrags = sblock.fs_frag;
55 	for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
56 		if (*ap) {
57 			idesc->id_blkno = *ap;
58 			ret = iblock(idesc, n,
59 				dino.di_size - sblock.fs_bsize * NDADDR);
60 			if (ret & STOP)
61 				return (ret);
62 		}
63 	}
64 	return (KEEPON);
65 }
66 
67 iblock(idesc, ilevel, isize)
68 	struct inodesc *idesc;
69 	register long ilevel;
70 	u_long isize;
71 {
72 	register daddr_t *ap;
73 	register daddr_t *aplim;
74 	int i, n, (*func)(), nif, sizepb;
75 	register struct bufarea *bp;
76 	char buf[BUFSIZ];
77 	extern int dirscan(), pass1check();
78 
79 	if (idesc->id_type == ADDR) {
80 		func = idesc->id_func;
81 		if (((n = (*func)(idesc)) & KEEPON) == 0)
82 			return (n);
83 	} else
84 		func = dirscan;
85 	if (chkrange(idesc->id_blkno, idesc->id_numfrags))
86 		return (SKIP);
87 	bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
88 	ilevel--;
89 	for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
90 		sizepb *= NINDIR(&sblock);
91 	nif = isize / sizepb + 1;
92 	if (nif > NINDIR(&sblock))
93 		nif = NINDIR(&sblock);
94 	if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
95 		aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
96 		for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
97 			if (*ap == 0)
98 				continue;
99 			(void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
100 				idesc->id_number);
101 			if (dofix(idesc, buf)) {
102 				*ap = 0;
103 				dirty(bp);
104 			}
105 		}
106 		flush(fswritefd, bp);
107 	}
108 	aplim = &bp->b_un.b_indir[nif];
109 	for (ap = bp->b_un.b_indir, i = 1; ap < aplim; ap++, i++) {
110 		if (*ap) {
111 			idesc->id_blkno = *ap;
112 			if (ilevel > 0)
113 				n = iblock(idesc, ilevel, isize - i * sizepb);
114 			else
115 				n = (*func)(idesc);
116 			if (n & STOP) {
117 				bp->b_flags &= ~B_INUSE;
118 				return (n);
119 			}
120 		}
121 	}
122 	bp->b_flags &= ~B_INUSE;
123 	return (KEEPON);
124 }
125 
126 /*
127  * Check that a block in a legal block number.
128  * Return 0 if in range, 1 if out of range.
129  */
130 chkrange(blk, cnt)
131 	daddr_t blk;
132 	int cnt;
133 {
134 	register int c;
135 
136 	if ((unsigned)(blk + cnt) > maxfsblock)
137 		return (1);
138 	c = dtog(&sblock, blk);
139 	if (blk < cgdmin(&sblock, c)) {
140 		if ((blk + cnt) > cgsblock(&sblock, c)) {
141 			if (debug) {
142 				printf("blk %ld < cgdmin %ld;",
143 				    blk, cgdmin(&sblock, c));
144 				printf(" blk + cnt %ld > cgsbase %ld\n",
145 				    blk + cnt, cgsblock(&sblock, c));
146 			}
147 			return (1);
148 		}
149 	} else {
150 		if ((blk + cnt) > cgbase(&sblock, c+1)) {
151 			if (debug)  {
152 				printf("blk %ld >= cgdmin %ld;",
153 				    blk, cgdmin(&sblock, c));
154 				printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
155 				    blk+cnt, sblock.fs_fpg);
156 			}
157 			return (1);
158 		}
159 	}
160 	return (0);
161 }
162 
163 /*
164  * General purpose interface for reading inodes.
165  */
166 struct dinode *
167 ginode(inumber)
168 	ino_t inumber;
169 {
170 	daddr_t iblk;
171 
172 	if (inumber < ROOTINO || inumber > maxino)
173 		errexit("bad inode number %d to ginode\n", inumber);
174 	if (startinum == 0 ||
175 	    inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
176 		iblk = itod(&sblock, inumber);
177 		if (pbp != 0)
178 			pbp->b_flags &= ~B_INUSE;
179 		pbp = getdatablk(iblk, sblock.fs_bsize);
180 		startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
181 	}
182 	return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
183 }
184 
185 /*
186  * Special purpose version of ginode used to optimize first pass
187  * over all the inodes in numerical order.
188  */
189 ino_t nextino, lastinum;
190 long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
191 struct dinode *inodebuf;
192 
193 struct dinode *
194 getnextinode(inumber)
195 	ino_t inumber;
196 {
197 	long size;
198 	daddr_t dblk;
199 	static struct dinode *dp;
200 
201 	if (inumber != nextino++ || inumber > maxino)
202 		errexit("bad inode number %d to nextinode\n", inumber);
203 	if (inumber >= lastinum) {
204 		readcnt++;
205 		dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
206 		if (readcnt % readpercg == 0) {
207 			size = partialsize;
208 			lastinum += partialcnt;
209 		} else {
210 			size = inobufsize;
211 			lastinum += fullcnt;
212 		}
213 		(void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
214 		dp = inodebuf;
215 	}
216 	return (dp++);
217 }
218 
219 resetinodebuf()
220 {
221 
222 	startinum = 0;
223 	nextino = 0;
224 	lastinum = 0;
225 	readcnt = 0;
226 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
227 	fullcnt = inobufsize / sizeof(struct dinode);
228 	readpercg = sblock.fs_ipg / fullcnt;
229 	partialcnt = sblock.fs_ipg % fullcnt;
230 	partialsize = partialcnt * sizeof(struct dinode);
231 	if (partialcnt != 0) {
232 		readpercg++;
233 	} else {
234 		partialcnt = fullcnt;
235 		partialsize = inobufsize;
236 	}
237 	if (inodebuf == NULL &&
238 	    (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
239 		errexit("Cannot allocate space for inode buffer\n");
240 	while (nextino < ROOTINO)
241 		(void)getnextinode(nextino);
242 }
243 
244 freeinodebuf()
245 {
246 
247 	if (inodebuf != NULL)
248 		free((char *)inodebuf);
249 	inodebuf = NULL;
250 }
251 
252 /*
253  * Routines to maintain information about directory inodes.
254  * This is built during the first pass and used during the
255  * second and third passes.
256  *
257  * Enter inodes into the cache.
258  */
259 cacheino(dp, inumber)
260 	register struct dinode *dp;
261 	ino_t inumber;
262 {
263 	register struct inoinfo *inp;
264 	struct inoinfo **inpp;
265 	unsigned int blks;
266 
267 	blks = howmany(dp->di_size, sblock.fs_bsize);
268 	if (blks > NDADDR)
269 		blks = NDADDR + NIADDR;
270 	inp = (struct inoinfo *)
271 		malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
272 	if (inp == NULL)
273 		return;
274 	inpp = &inphead[inumber % numdirs];
275 	inp->i_nexthash = *inpp;
276 	*inpp = inp;
277 	inp->i_parent = (ino_t)0;
278 	inp->i_dotdot = (ino_t)0;
279 	inp->i_number = inumber;
280 	inp->i_isize = dp->di_size;
281 	inp->i_numblks = blks * sizeof(daddr_t);
282 	bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
283 	    (size_t)inp->i_numblks);
284 	if (inplast == listmax) {
285 		listmax += 100;
286 		inpsort = (struct inoinfo **)realloc((char *)inpsort,
287 		    (unsigned)listmax * sizeof(struct inoinfo *));
288 		if (inpsort == NULL)
289 			errexit("cannot increase directory list");
290 	}
291 	inpsort[inplast++] = inp;
292 }
293 
294 /*
295  * Look up an inode cache structure.
296  */
297 struct inoinfo *
298 getinoinfo(inumber)
299 	ino_t inumber;
300 {
301 	register struct inoinfo *inp;
302 
303 	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
304 		if (inp->i_number != inumber)
305 			continue;
306 		return (inp);
307 	}
308 	errexit("cannot find inode %d\n", inumber);
309 	return ((struct inoinfo *)0);
310 }
311 
312 /*
313  * Clean up all the inode cache structure.
314  */
315 inocleanup()
316 {
317 	register struct inoinfo **inpp;
318 
319 	if (inphead == NULL)
320 		return;
321 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
322 		free((char *)(*inpp));
323 	free((char *)inphead);
324 	free((char *)inpsort);
325 	inphead = inpsort = NULL;
326 }
327 
328 inodirty()
329 {
330 
331 	dirty(pbp);
332 }
333 
334 clri(idesc, type, flag)
335 	register struct inodesc *idesc;
336 	char *type;
337 	int flag;
338 {
339 	register struct dinode *dp;
340 
341 	dp = ginode(idesc->id_number);
342 	if (flag == 1) {
343 		pwarn("%s %s", type,
344 		    (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
345 		pinode(idesc->id_number);
346 	}
347 	if (preen || reply("CLEAR") == 1) {
348 		if (preen)
349 			printf(" (CLEARED)\n");
350 		n_files--;
351 		(void)ckinode(dp, idesc);
352 		clearinode(dp);
353 		statemap[idesc->id_number] = USTATE;
354 		inodirty();
355 	}
356 }
357 
358 findname(idesc)
359 	struct inodesc *idesc;
360 {
361 	register struct direct *dirp = idesc->id_dirp;
362 
363 	if (dirp->d_ino != idesc->id_parent)
364 		return (KEEPON);
365 	bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
366 	return (STOP|FOUND);
367 }
368 
369 findino(idesc)
370 	struct inodesc *idesc;
371 {
372 	register struct direct *dirp = idesc->id_dirp;
373 
374 	if (dirp->d_ino == 0)
375 		return (KEEPON);
376 	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
377 	    dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
378 		idesc->id_parent = dirp->d_ino;
379 		return (STOP|FOUND);
380 	}
381 	return (KEEPON);
382 }
383 
384 pinode(ino)
385 	ino_t ino;
386 {
387 	register struct dinode *dp;
388 	register char *p;
389 	struct passwd *pw;
390 	char *ctime();
391 
392 	printf(" I=%lu ", ino);
393 	if (ino < ROOTINO || ino > maxino)
394 		return;
395 	dp = ginode(ino);
396 	printf(" OWNER=");
397 	if ((pw = getpwuid((int)dp->di_uid)) != 0)
398 		printf("%s ", pw->pw_name);
399 	else
400 		printf("%u ", (unsigned)dp->di_uid);
401 	printf("MODE=%o\n", dp->di_mode);
402 	if (preen)
403 		printf("%s: ", devname);
404 	printf("SIZE=%lu ", dp->di_size);
405 	p = ctime(&dp->di_mtime);
406 	printf("MTIME=%12.12s %4.4s ", p + 4, p + 20);
407 }
408 
409 blkerror(ino, type, blk)
410 	ino_t ino;
411 	char *type;
412 	daddr_t blk;
413 {
414 
415 	pfatal("%ld %s I=%lu", blk, type, ino);
416 	printf("\n");
417 	switch (statemap[ino]) {
418 
419 	case FSTATE:
420 		statemap[ino] = FCLEAR;
421 		return;
422 
423 	case DSTATE:
424 		statemap[ino] = DCLEAR;
425 		return;
426 
427 	case FCLEAR:
428 	case DCLEAR:
429 		return;
430 
431 	default:
432 		errexit("BAD STATE %d TO BLKERR", statemap[ino]);
433 		/* NOTREACHED */
434 	}
435 }
436 
437 /*
438  * allocate an unused inode
439  */
440 ino_t
441 allocino(request, type)
442 	ino_t request;
443 	int type;
444 {
445 	register ino_t ino;
446 	register struct dinode *dp;
447 
448 	if (request == 0)
449 		request = ROOTINO;
450 	else if (statemap[request] != USTATE)
451 		return (0);
452 	for (ino = request; ino < maxino; ino++)
453 		if (statemap[ino] == USTATE)
454 			break;
455 	if (ino == maxino)
456 		return (0);
457 	switch (type & IFMT) {
458 	case IFDIR:
459 		statemap[ino] = DSTATE;
460 		break;
461 	case IFREG:
462 	case IFLNK:
463 		statemap[ino] = FSTATE;
464 		break;
465 	default:
466 		return (0);
467 	}
468 	dp = ginode(ino);
469 	dp->di_db[0] = allocblk((long)1);
470 	if (dp->di_db[0] == 0) {
471 		statemap[ino] = USTATE;
472 		return (0);
473 	}
474 	dp->di_mode = type;
475 	(void)time(&dp->di_atime);
476 	dp->di_mtime = dp->di_ctime = dp->di_atime;
477 	dp->di_size = sblock.fs_fsize;
478 	dp->di_blocks = btodb(sblock.fs_fsize);
479 	n_files++;
480 	inodirty();
481 	return (ino);
482 }
483 
484 /*
485  * deallocate an inode
486  */
487 freeino(ino)
488 	ino_t ino;
489 {
490 	struct inodesc idesc;
491 	extern int pass4check();
492 	struct dinode *dp;
493 
494 	bzero((char *)&idesc, sizeof(struct inodesc));
495 	idesc.id_type = ADDR;
496 	idesc.id_func = pass4check;
497 	idesc.id_number = ino;
498 	dp = ginode(ino);
499 	(void)ckinode(dp, &idesc);
500 	clearinode(dp);
501 	inodirty();
502 	statemap[ino] = USTATE;
503 	n_files--;
504 }
505