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