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