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