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