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