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