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