Home | History | Annotate | Line # | Download | only in fsck_lfs
inode.c revision 1.10.2.1
      1  1.10.2.1  perseant /* $NetBSD: inode.c,v 1.10.2.1 2001/06/27 03:49:41 perseant Exp $	 */
      2       1.1  perseant 
      3       1.1  perseant /*
      4       1.1  perseant  * Copyright (c) 1997, 1998
      5       1.1  perseant  *      Konrad Schroder.  All rights reserved.
      6       1.1  perseant  * Copyright (c) 1980, 1986, 1993
      7       1.1  perseant  *	The Regents of the University of California.  All rights reserved.
      8       1.1  perseant  *
      9       1.1  perseant  * Redistribution and use in source and binary forms, with or without
     10       1.1  perseant  * modification, are permitted provided that the following conditions
     11       1.1  perseant  * are met:
     12       1.1  perseant  * 1. Redistributions of source code must retain the above copyright
     13       1.1  perseant  *    notice, this list of conditions and the following disclaimer.
     14       1.1  perseant  * 2. Redistributions in binary form must reproduce the above copyright
     15       1.1  perseant  *    notice, this list of conditions and the following disclaimer in the
     16       1.1  perseant  *    documentation and/or other materials provided with the distribution.
     17       1.1  perseant  * 3. All advertising materials mentioning features or use of this software
     18       1.1  perseant  *    must display the following acknowledgement:
     19       1.1  perseant  *	This product includes software developed by the University of
     20       1.1  perseant  *	California, Berkeley and its contributors.
     21       1.1  perseant  * 4. Neither the name of the University nor the names of its contributors
     22       1.1  perseant  *    may be used to endorse or promote products derived from this software
     23       1.1  perseant  *    without specific prior written permission.
     24       1.1  perseant  *
     25       1.1  perseant  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26       1.1  perseant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27       1.1  perseant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28       1.1  perseant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29       1.1  perseant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30       1.1  perseant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31       1.1  perseant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32       1.1  perseant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33       1.1  perseant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34       1.1  perseant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35       1.1  perseant  * SUCH DAMAGE.
     36       1.1  perseant  */
     37       1.1  perseant 
     38       1.1  perseant #include <sys/param.h>
     39       1.1  perseant #include <sys/time.h>
     40       1.1  perseant #include <ufs/ufs/dinode.h>
     41       1.1  perseant #include <ufs/ufs/dir.h>
     42       1.6  perseant #include <sys/mount.h>		/* XXX */
     43       1.1  perseant #include <ufs/lfs/lfs.h>
     44       1.1  perseant #ifndef SMALL
     45       1.1  perseant #include <pwd.h>
     46       1.1  perseant #endif
     47       1.1  perseant #include <stdio.h>
     48       1.1  perseant #include <stdlib.h>
     49       1.1  perseant #include <string.h>
     50       1.1  perseant 
     51       1.1  perseant #include "fsck.h"
     52       1.1  perseant #include "fsutil.h"
     53       1.1  perseant #include "extern.h"
     54       1.1  perseant 
     55       1.6  perseant extern SEGUSE  *seg_table;
     56       1.5  perseant extern daddr_t *din_table;
     57       1.1  perseant 
     58       1.6  perseant static int      iblock(struct inodesc *, long, u_int64_t);
     59       1.6  perseant int             blksreqd(struct lfs *, int);
     60       1.6  perseant int             lfs_maxino(void);
     61       1.6  perseant /* static void dump_inoblk (struct lfs *, struct dinode *); */
     62       1.1  perseant 
     63       1.1  perseant /* stolen from lfs_inode.c */
     64       1.1  perseant /* Search a block for a specific dinode. */
     65       1.6  perseant struct dinode  *
     66       1.6  perseant lfs_difind(struct lfs * fs, ino_t ino, struct dinode * dip)
     67       1.1  perseant {
     68  1.10.2.1  perseant 	struct dinode *ldip, *fin;
     69       1.1  perseant 
     70  1.10.2.1  perseant 	if (fs->lfs_version == 1)
     71  1.10.2.1  perseant 		fin = dip + INOPB(fs);
     72  1.10.2.1  perseant 	else
     73  1.10.2.1  perseant 		fin = dip + INOPS(fs);
     74  1.10.2.1  perseant 
     75  1.10.2.1  perseant 	for (ldip = dip; ldip < fin; ++ldip) {
     76  1.10.2.1  perseant 		if (ldip->di_inumber == ino)
     77  1.10.2.1  perseant 			return ldip;
     78  1.10.2.1  perseant 	}
     79       1.6  perseant 	/* printf("lfs_difind: dinode %u not found\n", ino); */
     80       1.6  perseant 	return NULL;
     81       1.1  perseant }
     82       1.1  perseant 
     83       1.1  perseant /*
     84       1.1  perseant  * Calculate the number of blocks required to be able to address data block
     85       1.1  perseant  * blkno (counting, of course, indirect blocks).  blkno must >=0.
     86       1.1  perseant  */
     87       1.6  perseant int
     88       1.6  perseant blksreqd(struct lfs * fs, int blkno)
     89       1.1  perseant {
     90       1.6  perseant 	long            n = blkno;
     91       1.1  perseant 
     92       1.6  perseant 	if (blkno < NDADDR)
     93       1.6  perseant 		return blkno;
     94       1.6  perseant 	n -= NDADDR;
     95       1.6  perseant 	if (n < NINDIR(fs))
     96       1.6  perseant 		return blkno + 1;
     97       1.6  perseant 	n -= NINDIR(fs);
     98       1.6  perseant 	if (n < NINDIR(fs) * NINDIR(fs))
     99       1.6  perseant 		return blkno + 2 + n / NINDIR(fs) + 1;
    100       1.6  perseant 	n -= NINDIR(fs) * NINDIR(fs);
    101       1.6  perseant 	return blkno + 2 + NINDIR(fs) + n / (NINDIR(fs) * NINDIR(fs)) + 1;
    102       1.1  perseant }
    103       1.1  perseant 
    104       1.1  perseant #define BASE_SINDIR (NDADDR)
    105       1.1  perseant #define BASE_DINDIR (NDADDR+NINDIR(fs))
    106       1.1  perseant #define BASE_TINDIR (NDADDR+NINDIR(fs)+NINDIR(fs)*NINDIR(fs))
    107       1.1  perseant 
    108       1.1  perseant #define D_UNITS (NINDIR(fs))
    109       1.1  perseant #define T_UNITS (NINDIR(fs)*NINDIR(fs))
    110       1.1  perseant 
    111       1.6  perseant ufs_daddr_t     lfs_bmap(struct lfs *, struct dinode *, ufs_daddr_t);
    112       1.1  perseant 
    113       1.6  perseant ufs_daddr_t
    114       1.6  perseant lfs_bmap(struct lfs * fs, struct dinode * idinode, ufs_daddr_t lbn)
    115       1.1  perseant {
    116       1.6  perseant 	ufs_daddr_t     residue, up, off = 0;
    117       1.1  perseant 	struct bufarea *bp;
    118       1.6  perseant 
    119       1.6  perseant 	if (lbn > 0 && lbn > (idinode->di_size - 1) / dev_bsize) {
    120       1.1  perseant 		return UNASSIGNED;
    121       1.1  perseant 	}
    122       1.1  perseant 	/*
    123       1.1  perseant 	 * Indirect blocks: if it is a first-level indirect, pull its
    124       1.1  perseant 	 * address from the inode; otherwise, call ourselves to find the
    125       1.1  perseant 	 * address of the parent indirect block, and load that to find
    126       1.1  perseant 	 * the desired address.
    127       1.1  perseant 	 */
    128       1.6  perseant 	if (lbn < 0) {
    129       1.1  perseant 		lbn *= -1;
    130       1.6  perseant 		if (lbn == NDADDR) {
    131       1.1  perseant 			/* printf("lbn %d: single indir base\n", -lbn); */
    132       1.6  perseant 			return idinode->di_ib[0];	/* single indirect */
    133       1.6  perseant 		} else if (lbn == BASE_DINDIR + 1) {
    134       1.1  perseant 			/* printf("lbn %d: double indir base\n", -lbn); */
    135       1.6  perseant 			return idinode->di_ib[1];	/* double indirect */
    136       1.6  perseant 		} else if (lbn == BASE_TINDIR + 2) {
    137       1.1  perseant 			/* printf("lbn %d: triple indir base\n", -lbn); */
    138       1.6  perseant 			return idinode->di_ib[2];	/* triple indirect */
    139       1.1  perseant 		}
    140       1.1  perseant 		/*
    141       1.1  perseant 		 * Find the immediate parent. This is essentially finding the
    142       1.1  perseant 		 * residue of modulus, and then rounding accordingly.
    143       1.1  perseant 		 */
    144       1.6  perseant 		residue = (lbn - NDADDR) % NINDIR(fs);
    145       1.6  perseant 		if (residue == 1) {
    146       1.1  perseant 			/* Double indirect.  Parent is the triple. */
    147       1.1  perseant 			up = idinode->di_ib[2];
    148       1.6  perseant 			off = (lbn - 2 - BASE_TINDIR) / (NINDIR(fs) * NINDIR(fs));
    149       1.6  perseant 			if (up == UNASSIGNED || up == LFS_UNUSED_DADDR)
    150       1.1  perseant 				return UNASSIGNED;
    151       1.1  perseant 			/* printf("lbn %d: parent is the triple\n", -lbn); */
    152       1.6  perseant 			bp = getddblk(up, sblock.lfs_bsize);
    153       1.1  perseant 			bp->b_flags &= ~B_INUSE;
    154       1.1  perseant 			return ((daddr_t *)(bp->b_un.b_buf))[off];
    155       1.6  perseant 		} else {	/* residue == 0 */
    156       1.1  perseant 			/* Single indirect.  Two cases. */
    157       1.6  perseant 			if (lbn < BASE_TINDIR) {
    158       1.1  perseant 				/* Parent is the double, simple */
    159       1.1  perseant 				up = -(BASE_DINDIR) - 1;
    160       1.6  perseant 				off = (lbn - BASE_DINDIR) / D_UNITS;
    161       1.6  perseant 				/*
    162       1.6  perseant 				 * printf("lbn %d: parent is %d/%d\n", -lbn,
    163       1.6  perseant 				 * up,off);
    164       1.6  perseant 				 */
    165       1.1  perseant 			} else {
    166       1.1  perseant 				/* Ancestor is the triple, more complex */
    167       1.6  perseant 				up = ((lbn - BASE_TINDIR) / T_UNITS)
    168       1.1  perseant 					* T_UNITS + BASE_TINDIR + 1;
    169       1.6  perseant 				off = (lbn / D_UNITS) - (up / D_UNITS);
    170       1.1  perseant 				up = -up;
    171       1.6  perseant 				/*
    172       1.6  perseant 				 * printf("lbn %d: parent is %d/%d\n", -lbn,
    173       1.6  perseant 				 * up,off);
    174       1.6  perseant 				 */
    175       1.1  perseant 			}
    176       1.1  perseant 		}
    177       1.1  perseant 	} else {
    178       1.1  perseant 		/* Direct block.  Its parent must be a single indirect. */
    179       1.6  perseant 		if (lbn < NDADDR)
    180       1.1  perseant 			return idinode->di_db[lbn];
    181       1.1  perseant 		else {
    182       1.1  perseant 			/* Parent is an indirect block. */
    183       1.6  perseant 			up = -(((lbn - NDADDR) / D_UNITS) * D_UNITS + NDADDR);
    184       1.6  perseant 			off = (lbn - NDADDR) % D_UNITS;
    185       1.1  perseant 			/* printf("lbn %d: parent is %d/%d\n", lbn,up,off); */
    186       1.1  perseant 		}
    187       1.1  perseant 	}
    188       1.6  perseant 	up = lfs_bmap(fs, idinode, up);
    189       1.6  perseant 	if (up == UNASSIGNED || up == LFS_UNUSED_DADDR)
    190       1.1  perseant 		return UNASSIGNED;
    191       1.6  perseant 	bp = getddblk(up, sblock.lfs_bsize);
    192       1.1  perseant 	bp->b_flags &= ~B_INUSE;
    193       1.1  perseant 	return ((daddr_t *)(bp->b_un.b_buf))[off];
    194       1.1  perseant }
    195       1.1  perseant 
    196       1.1  perseant /*
    197       1.1  perseant  * This is kind of gross.  We use this to find the nth block
    198       1.1  perseant  * from a file whose inode has disk address idaddr.  In practice
    199       1.1  perseant  * we will only use this to find blocks of the ifile.
    200       1.1  perseant  */
    201       1.7  perseant static struct bufarea empty;
    202       1.7  perseant 
    203       1.1  perseant struct bufarea *
    204       1.6  perseant getfileblk(struct lfs * fs, struct dinode * idinode, ino_t lbn)
    205       1.1  perseant {
    206       1.6  perseant 	struct bufarea *bp;
    207       1.6  perseant 	ufs_daddr_t     blkno;
    208       1.6  perseant 	static char     empty_buf[65536];
    209       1.6  perseant 
    210       1.6  perseant 	empty.b_un.b_buf = &(empty_buf[0]);
    211       1.6  perseant 
    212       1.6  perseant 	blkno = lfs_bmap(fs, idinode, lbn);
    213       1.6  perseant 	if (blkno == UNASSIGNED || blkno == LFS_UNUSED_DADDR) {
    214       1.6  perseant 		printf("Warning: ifile lbn %d unassigned!\n", lbn);
    215       1.6  perseant 		return &empty;
    216       1.6  perseant 	}
    217       1.6  perseant 	bp = getddblk(blkno, sblock.lfs_bsize);
    218       1.6  perseant 	return bp;
    219       1.1  perseant }
    220       1.1  perseant 
    221       1.5  perseant #if 0
    222       1.6  perseant static struct dinode *
    223       1.6  perseant gidinode(void)
    224       1.1  perseant {
    225       1.6  perseant 	static struct dinode *idinode;
    226       1.1  perseant 
    227       1.6  perseant 	if (!idinode) {		/* only need to do this once */
    228       1.6  perseant 		idinode = lfs_difind(&sblock, sblock.lfs_ifile, &ifblock);
    229       1.6  perseant 	}
    230       1.6  perseant 	return idinode;
    231       1.1  perseant }
    232       1.5  perseant #endif
    233       1.1  perseant 
    234       1.6  perseant struct ifile   *
    235       1.6  perseant lfs_ientry(ino_t ino, struct bufarea ** bpp)
    236       1.1  perseant {
    237  1.10.2.1  perseant 	IFILE *ifp;
    238       1.1  perseant 
    239       1.6  perseant 	*bpp = getfileblk(&sblock, lfs_ginode(LFS_IFILE_INUM),
    240       1.6  perseant 			  ino / sblock.lfs_ifpb + sblock.lfs_cleansz +
    241       1.6  perseant 			  sblock.lfs_segtabsz);
    242       1.7  perseant 	if (*bpp == &empty) {
    243       1.7  perseant 		printf("Warning: ino %d ientry in unassigned block\n", ino);
    244       1.7  perseant 	}
    245       1.6  perseant 	if (*bpp) {
    246  1.10.2.1  perseant 		if (sblock.lfs_version > 1) {
    247  1.10.2.1  perseant 			ifp = (((IFILE *)((*bpp)->b_un.b_buf)) +
    248  1.10.2.1  perseant 			       (ino % sblock.lfs_ifpb));
    249  1.10.2.1  perseant 		} else {
    250  1.10.2.1  perseant 			ifp = (IFILE *)(((IFILE_V1 *)
    251  1.10.2.1  perseant 					 ((*bpp)->b_un.b_buf)) +
    252  1.10.2.1  perseant 					(ino % sblock.lfs_ifpb));
    253  1.10.2.1  perseant 		}
    254       1.6  perseant 		return ifp;
    255       1.6  perseant 	} else
    256       1.6  perseant 		return NULL;
    257       1.6  perseant }
    258       1.6  perseant 
    259       1.6  perseant SEGUSE         *
    260       1.6  perseant lfs_gseguse(int segnum, struct bufarea ** bpp)
    261       1.6  perseant {
    262       1.6  perseant 	int             blkno;
    263  1.10.2.1  perseant 	struct bufarea *bp;
    264       1.6  perseant 
    265  1.10.2.1  perseant 	blkno = segnum / sblock.lfs_sepb + sblock.lfs_cleansz;
    266  1.10.2.1  perseant 	(*bpp) = bp = getfileblk(&sblock, lfs_ginode(LFS_IFILE_INUM), blkno);
    267  1.10.2.1  perseant 	if (sblock.lfs_version == 1)
    268  1.10.2.1  perseant 		return (SEGUSE *)((SEGUSE_V1 *)(bp->b_un.b_buf) +
    269  1.10.2.1  perseant 				  segnum % sblock.lfs_sepb);
    270  1.10.2.1  perseant 	else
    271  1.10.2.1  perseant 		return (SEGUSE *)(bp->b_un.b_buf) + segnum % sblock.lfs_sepb;
    272       1.5  perseant }
    273       1.5  perseant 
    274       1.5  perseant daddr_t
    275       1.5  perseant lfs_ino_daddr(ino_t inumber)
    276       1.5  perseant {
    277       1.6  perseant 	daddr_t         daddr;
    278       1.6  perseant 	IFILE          *ifp;
    279       1.5  perseant 	struct bufarea *bp;
    280       1.5  perseant 
    281       1.6  perseant 	if (din_table[inumber]) {
    282       1.5  perseant 		daddr = din_table[inumber];
    283       1.5  perseant 	} else {
    284       1.6  perseant 		if (inumber == LFS_IFILE_INUM)
    285       1.7  perseant 			daddr = idaddr;
    286       1.5  perseant 		else {
    287       1.5  perseant 			ifp = lfs_ientry(inumber, &bp);
    288       1.6  perseant 			if (ifp == NULL) {
    289       1.5  perseant 				return NULL;
    290       1.5  perseant 			}
    291       1.6  perseant 			if (ifp->if_daddr == LFS_UNUSED_DADDR) {
    292       1.5  perseant 				bp->b_flags &= ~B_INUSE;
    293       1.5  perseant 				return NULL;
    294       1.5  perseant 			}
    295       1.5  perseant 			bp->b_flags &= ~B_INUSE;
    296       1.5  perseant 			daddr = ifp->if_daddr;
    297       1.5  perseant 		}
    298       1.6  perseant 
    299       1.5  perseant 		din_table[inumber] = daddr;
    300       1.6  perseant 		seg_table[datosn(&sblock, daddr)].su_nbytes += DINODE_SIZE;
    301       1.5  perseant 	}
    302       1.5  perseant 	return daddr;
    303       1.1  perseant }
    304       1.1  perseant 
    305       1.6  perseant struct dinode  *
    306       1.5  perseant lfs_ginode(ino_t inumber)
    307       1.5  perseant {
    308       1.6  perseant 	struct ifile   *ifp;
    309       1.6  perseant 	struct dinode  *din;
    310       1.6  perseant 	struct bufarea *bp;
    311       1.6  perseant 	daddr_t         daddr;
    312       1.5  perseant 
    313       1.7  perseant 	if (inumber >= maxino)
    314       1.6  perseant 		errexit("bad inode number %d to lfs_ginode\n", inumber);
    315       1.1  perseant 
    316       1.5  perseant #if 0
    317       1.6  perseant 	if (inumber == LFS_IFILE_INUM) {
    318       1.7  perseant 		daddr = idaddr;
    319       1.6  perseant 		if (din_table[LFS_IFILE_INUM] == 0) {
    320       1.6  perseant 			din_table[LFS_IFILE_INUM] = daddr;
    321       1.6  perseant 			seg_table[datosn(&sblock, daddr)].su_nbytes += DINODE_SIZE;
    322       1.6  perseant 		}
    323       1.6  perseant 		return gidinode();
    324       1.6  perseant 	}
    325       1.5  perseant #endif
    326       1.5  perseant 
    327       1.6  perseant 	daddr = lfs_ino_daddr(inumber);
    328       1.6  perseant 	if (daddr == 0)
    329       1.6  perseant 		return NULL;
    330       1.6  perseant 
    331       1.6  perseant 	if (pbp)
    332       1.6  perseant 		pbp->b_flags &= ~B_INUSE;
    333       1.6  perseant 
    334       1.6  perseant 	pbp = getddblk(daddr, sblock.lfs_bsize);
    335       1.6  perseant 	din = lfs_difind(&sblock, inumber, pbp->b_un.b_dinode);
    336       1.6  perseant 
    337       1.6  perseant 	if (din == NULL) {
    338       1.6  perseant 		pfatal("INODE %d NOT FOUND\n", inumber);
    339       1.6  perseant 		if (reply("free")) {
    340       1.6  perseant 			ifp = lfs_ientry(inumber, &bp);
    341       1.6  perseant 			ifp->if_daddr = LFS_UNUSED_DADDR;
    342       1.6  perseant 			ifp->if_nextfree = sblock.lfs_free;
    343       1.6  perseant 			sblock.lfs_free = inumber;
    344       1.6  perseant 			sbdirty();
    345       1.6  perseant 			dirty(bp);
    346       1.6  perseant 			bp->b_flags &= ~B_INUSE;
    347       1.6  perseant 		}
    348       1.6  perseant 	}
    349       1.6  perseant 	return din;
    350       1.1  perseant }
    351       1.1  perseant 
    352       1.1  perseant /* imported from lfs_vfsops.c */
    353       1.1  perseant int
    354       1.6  perseant ino_to_fsba(struct lfs * fs, ino_t ino)
    355       1.1  perseant {
    356       1.6  perseant 	daddr_t         daddr = LFS_UNUSED_DADDR;
    357       1.6  perseant 	struct ifile   *ifp;
    358       1.6  perseant 	struct bufarea *bp;
    359       1.6  perseant 
    360       1.6  perseant 	/* Translate the inode number to a disk address. */
    361       1.6  perseant 	if (ino == LFS_IFILE_INUM)
    362       1.6  perseant 		daddr = fs->lfs_idaddr;
    363       1.6  perseant 	else {
    364       1.6  perseant 		ifp = lfs_ientry(ino, &bp);
    365       1.6  perseant 		if (ifp) {
    366       1.6  perseant 			daddr = ifp->if_daddr;
    367       1.6  perseant 		} else {
    368       1.6  perseant 			pwarn("Can't locate inode #%ud\n", ino);
    369       1.6  perseant 		}
    370       1.6  perseant 		bp->b_flags &= ~B_INUSE;
    371       1.6  perseant 	}
    372       1.6  perseant 	return daddr;
    373       1.1  perseant }
    374       1.1  perseant 
    375       1.1  perseant /*
    376       1.1  perseant  * Check validity of held (direct) blocks in an inode.
    377       1.1  perseant  */
    378       1.1  perseant int
    379       1.6  perseant ckinode(struct dinode *dp, struct inodesc *idesc)
    380       1.6  perseant {
    381       1.6  perseant 	register ufs_daddr_t *ap;
    382       1.6  perseant 	long            ret, n, ndb, offset;
    383       1.6  perseant 	struct dinode   dino;
    384       1.6  perseant 	u_int64_t       remsize, sizepb;
    385       1.6  perseant 	mode_t          mode;
    386       1.6  perseant 	char            pathbuf[MAXPATHLEN + 1];
    387       1.6  perseant 
    388       1.6  perseant 	if (idesc->id_fix != IGNORE)
    389       1.6  perseant 		idesc->id_fix = DONTKNOW;
    390       1.6  perseant 	idesc->id_entryno = 0;
    391       1.6  perseant 	idesc->id_filesize = dp->di_size;
    392       1.6  perseant 	mode = dp->di_mode & IFMT;
    393       1.6  perseant 	if (mode == IFBLK || mode == IFCHR ||
    394       1.6  perseant 	    (mode == IFLNK && (dp->di_size < sblock.lfs_maxsymlinklen ||
    395       1.6  perseant 	                       (sblock.lfs_maxsymlinklen == 0 &&
    396       1.6  perseant 				dp->di_blocks == 0))))
    397       1.6  perseant 		return (KEEPON);
    398       1.6  perseant 	dino = *dp;
    399       1.6  perseant 	ndb = howmany(dino.di_size, sblock.lfs_bsize);
    400       1.6  perseant 
    401       1.6  perseant 	for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
    402       1.6  perseant 		if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) {
    403       1.6  perseant 			idesc->id_numfrags =
    404       1.6  perseant 				numfrags(&sblock, fragroundup(&sblock, offset));
    405       1.6  perseant 		} else
    406       1.6  perseant 			idesc->id_numfrags = sblock.lfs_frag;
    407       1.6  perseant 		if (*ap == 0) {
    408       1.6  perseant 			if (idesc->id_type == DATA && ndb >= 0) {
    409       1.6  perseant 				/* An empty block in a directory XXX */
    410       1.6  perseant 				getpathname(pathbuf, idesc->id_number,
    411       1.6  perseant 					    idesc->id_number);
    412       1.6  perseant 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
    413       1.6  perseant 				       pathbuf);
    414       1.6  perseant 				if (reply("ADJUST LENGTH") == 1) {
    415       1.6  perseant 					dp = ginode(idesc->id_number);
    416       1.6  perseant 					dp->di_size = (ap - &dino.di_db[0]) *
    417       1.6  perseant 						sblock.lfs_bsize;
    418       1.6  perseant 					printf(
    419       1.6  perseant 					"YOU MUST RERUN FSCK AFTERWARDS\n");
    420       1.6  perseant 					rerun = 1;
    421       1.6  perseant 					inodirty();
    422       1.6  perseant 				}
    423       1.6  perseant 			}
    424       1.6  perseant 			continue;
    425       1.6  perseant 		}
    426       1.6  perseant 		idesc->id_blkno = *ap;
    427       1.6  perseant 		idesc->id_lblkno = ap - &dino.di_db[0];
    428       1.6  perseant 		if (idesc->id_type == ADDR) {
    429       1.6  perseant 			ret = (*idesc->id_func)(idesc);
    430       1.6  perseant 		} else
    431       1.6  perseant 			ret = dirscan(idesc);
    432       1.6  perseant 		idesc->id_lblkno = 0;
    433       1.6  perseant 		if (ret & STOP)
    434       1.6  perseant 			return (ret);
    435       1.6  perseant 	}
    436       1.6  perseant 	idesc->id_numfrags = sblock.lfs_frag;
    437       1.6  perseant 	remsize = dino.di_size - sblock.lfs_bsize * NDADDR;
    438       1.6  perseant 	sizepb = sblock.lfs_bsize;
    439       1.6  perseant 	for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
    440       1.6  perseant 		if (*ap) {
    441       1.6  perseant 			idesc->id_blkno = *ap;
    442       1.6  perseant 			ret = iblock(idesc, n, remsize);
    443       1.6  perseant 			if (ret & STOP)
    444       1.6  perseant 				return (ret);
    445       1.6  perseant 		} else {
    446       1.6  perseant 			if (idesc->id_type == DATA && remsize > 0) {
    447       1.6  perseant 				/* An empty block in a directory XXX */
    448       1.6  perseant 				getpathname(pathbuf, idesc->id_number,
    449       1.6  perseant 					    idesc->id_number);
    450       1.6  perseant 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
    451       1.6  perseant 				       pathbuf);
    452       1.6  perseant 				if (reply("ADJUST LENGTH") == 1) {
    453       1.6  perseant 					dp = ginode(idesc->id_number);
    454       1.6  perseant 					dp->di_size -= remsize;
    455       1.6  perseant 					remsize = 0;
    456       1.6  perseant 					printf(
    457       1.6  perseant 					"YOU MUST RERUN FSCK AFTERWARDS\n");
    458       1.6  perseant 					rerun = 1;
    459       1.6  perseant 					inodirty();
    460       1.6  perseant 					break;
    461       1.6  perseant 				}
    462       1.6  perseant 			}
    463       1.6  perseant 		}
    464       1.6  perseant 		sizepb *= NINDIR(&sblock);
    465       1.6  perseant 		remsize -= sizepb;
    466       1.6  perseant 	}
    467       1.6  perseant 	return (KEEPON);
    468       1.1  perseant }
    469       1.1  perseant 
    470       1.1  perseant static int
    471       1.6  perseant iblock(struct inodesc * idesc, long ilevel, u_int64_t isize)
    472       1.6  perseant {
    473       1.7  perseant 	daddr_t	       *ap, *aplim;
    474       1.7  perseant 	struct bufarea *bp;
    475       1.6  perseant 	int             i, n, (*func)(struct inodesc *), nif;
    476       1.6  perseant 	u_int64_t       sizepb;
    477       1.6  perseant 	char            pathbuf[MAXPATHLEN + 1], buf[BUFSIZ];
    478       1.6  perseant 	struct dinode  *dp;
    479       1.6  perseant 
    480       1.6  perseant 	if (idesc->id_type == ADDR) {
    481       1.6  perseant 		func = idesc->id_func;
    482       1.6  perseant 		n = (*func)(idesc);
    483       1.6  perseant 		if ((n & KEEPON) == 0)
    484       1.6  perseant 			return (n);
    485       1.6  perseant 	} else
    486       1.6  perseant 		func = dirscan;
    487       1.9      joff 	if (chkrange(idesc->id_blkno, fragstodb(&sblock, idesc->id_numfrags)))
    488       1.6  perseant 		return (SKIP);
    489       1.6  perseant 	bp = getddblk(idesc->id_blkno, sblock.lfs_bsize);
    490       1.6  perseant 	ilevel--;
    491       1.6  perseant 	for (sizepb = sblock.lfs_bsize, i = 0; i < ilevel; i++)
    492       1.6  perseant 		sizepb *= NINDIR(&sblock);
    493       1.6  perseant 	if (isize > sizepb * NINDIR(&sblock))
    494       1.6  perseant 		nif = NINDIR(&sblock);
    495       1.6  perseant 	else
    496       1.6  perseant 		nif = howmany(isize, sizepb);
    497       1.6  perseant 	if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
    498       1.6  perseant 		aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
    499       1.6  perseant 		for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
    500       1.6  perseant 			if (*ap == 0)
    501       1.6  perseant 				continue;
    502       1.6  perseant 			(void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%u",
    503       1.7  perseant 					      idesc->id_number);
    504       1.6  perseant 			if (dofix(idesc, buf)) {
    505       1.6  perseant 				*ap = 0;
    506       1.6  perseant 				dirty(bp);
    507       1.6  perseant 			}
    508       1.6  perseant 		}
    509       1.6  perseant 		flush(fswritefd, bp);
    510       1.6  perseant 	}
    511       1.6  perseant 	aplim = &bp->b_un.b_indir[nif];
    512       1.6  perseant 	for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
    513       1.6  perseant 		if (*ap) {
    514       1.6  perseant 			idesc->id_blkno = *ap;
    515       1.6  perseant 			if (ilevel == 0)
    516       1.6  perseant 				n = (*func)(idesc);
    517       1.6  perseant 			else
    518       1.6  perseant 				n = iblock(idesc, ilevel, isize);
    519       1.6  perseant 			if (n & STOP) {
    520       1.6  perseant 				bp->b_flags &= ~B_INUSE;
    521       1.6  perseant 				return (n);
    522       1.6  perseant 			}
    523       1.6  perseant 		} else {
    524       1.6  perseant 			if (idesc->id_type == DATA && isize > 0) {
    525       1.6  perseant 				/* An empty block in a directory XXX */
    526       1.6  perseant 				getpathname(pathbuf, idesc->id_number,
    527       1.6  perseant 					    idesc->id_number);
    528       1.6  perseant 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
    529       1.6  perseant 				       pathbuf);
    530       1.6  perseant 				if (reply("ADJUST LENGTH") == 1) {
    531       1.6  perseant 					dp = ginode(idesc->id_number);
    532       1.6  perseant 					dp->di_size -= isize;
    533       1.6  perseant 					isize = 0;
    534       1.6  perseant 					printf(
    535       1.6  perseant 					"YOU MUST RERUN FSCK AFTERWARDS\n");
    536       1.6  perseant 					rerun = 1;
    537       1.6  perseant 					inodirty();
    538       1.6  perseant 					bp->b_flags &= ~B_INUSE;
    539       1.6  perseant 					return (STOP);
    540       1.6  perseant 				}
    541       1.6  perseant 			}
    542       1.6  perseant 		}
    543       1.6  perseant 		isize -= sizepb;
    544       1.6  perseant 	}
    545       1.6  perseant 	bp->b_flags &= ~B_INUSE;
    546       1.6  perseant 	return (KEEPON);
    547       1.1  perseant }
    548       1.1  perseant 
    549       1.1  perseant /*
    550       1.1  perseant  * Check that a block in a legal block number.
    551       1.1  perseant  * Return 0 if in range, 1 if out of range.
    552       1.1  perseant  */
    553       1.1  perseant int
    554       1.6  perseant chkrange(daddr_t blk, int cnt)
    555       1.6  perseant {
    556       1.7  perseant 	if (blk < btodb(LFS_LABELPAD+LFS_SBPAD)) {
    557       1.7  perseant 		return (1);
    558       1.7  perseant 	}
    559       1.6  perseant 	if (blk > fsbtodb(&sblock, maxfsblock)) {
    560       1.6  perseant 		return (1);
    561       1.6  perseant 	}
    562       1.6  perseant 	return (0);
    563       1.1  perseant }
    564       1.1  perseant 
    565       1.1  perseant /*
    566       1.1  perseant  * General purpose interface for reading inodes.
    567       1.1  perseant  */
    568       1.6  perseant struct dinode  *
    569       1.1  perseant ginode(ino_t inumber)
    570       1.1  perseant {
    571       1.6  perseant 	return lfs_ginode(inumber);
    572       1.1  perseant }
    573       1.1  perseant 
    574       1.1  perseant /*
    575       1.1  perseant  * Routines to maintain information about directory inodes.
    576       1.1  perseant  * This is built during the first pass and used during the
    577       1.1  perseant  * second and third passes.
    578       1.1  perseant  *
    579       1.1  perseant  * Enter inodes into the cache.
    580       1.1  perseant  */
    581       1.1  perseant void
    582       1.6  perseant cacheino(struct dinode *dp, ino_t inumber)
    583       1.6  perseant {
    584       1.6  perseant 	register struct inoinfo *inp;
    585       1.6  perseant 	struct inoinfo **inpp;
    586       1.6  perseant 	unsigned int    blks;
    587       1.6  perseant 
    588       1.6  perseant 	blks = howmany(dp->di_size, sblock.lfs_bsize);
    589       1.6  perseant 	if (blks > NDADDR)
    590       1.6  perseant 		blks = NDADDR + NIADDR;
    591       1.6  perseant 	inp = (struct inoinfo *)
    592       1.6  perseant 		malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
    593       1.6  perseant 	if (inp == NULL)
    594       1.6  perseant 		return;
    595       1.6  perseant 	inpp = &inphead[inumber % numdirs];
    596       1.6  perseant 	inp->i_nexthash = *inpp;
    597       1.6  perseant 	*inpp = inp;
    598       1.6  perseant 	inp->i_child = inp->i_sibling = inp->i_parentp = 0;
    599       1.6  perseant 	if (inumber == ROOTINO)
    600       1.6  perseant 		inp->i_parent = ROOTINO;
    601       1.6  perseant 	else
    602       1.6  perseant 		inp->i_parent = (ino_t)0;
    603       1.6  perseant 	inp->i_dotdot = (ino_t)0;
    604       1.6  perseant 	inp->i_number = inumber;
    605       1.6  perseant 	inp->i_isize = dp->di_size;
    606       1.6  perseant 	inp->i_numblks = blks * sizeof(daddr_t);
    607       1.6  perseant 	memcpy(&inp->i_blks[0], &dp->di_db[0], (size_t)inp->i_numblks);
    608       1.6  perseant 	if (inplast == listmax) {
    609       1.6  perseant 		listmax += 100;
    610       1.6  perseant 		inpsort = (struct inoinfo **)realloc((char *) inpsort,
    611       1.6  perseant 			     (unsigned)listmax * sizeof(struct inoinfo *));
    612       1.6  perseant 		if (inpsort == NULL)
    613       1.6  perseant 			errexit("cannot increase directory list");
    614       1.6  perseant 	}
    615       1.6  perseant 	inpsort[inplast++] = inp;
    616       1.1  perseant }
    617       1.1  perseant 
    618       1.1  perseant /*
    619       1.1  perseant  * Look up an inode cache structure.
    620       1.1  perseant  */
    621       1.1  perseant struct inoinfo *
    622       1.6  perseant getinoinfo(ino_t inumber)
    623       1.1  perseant {
    624       1.6  perseant 	register struct inoinfo *inp;
    625       1.1  perseant 
    626       1.6  perseant 	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
    627       1.6  perseant 		if (inp->i_number != inumber)
    628       1.6  perseant 			continue;
    629       1.6  perseant 		return (inp);
    630       1.6  perseant 	}
    631       1.6  perseant 	errexit("cannot find inode %d\n", inumber);
    632       1.6  perseant 	return ((struct inoinfo *)0);
    633       1.1  perseant }
    634       1.1  perseant 
    635       1.1  perseant /*
    636       1.1  perseant  * Clean up all the inode cache structure.
    637       1.1  perseant  */
    638       1.1  perseant void
    639       1.1  perseant inocleanup()
    640       1.1  perseant {
    641       1.6  perseant 	register struct inoinfo **inpp;
    642       1.1  perseant 
    643       1.6  perseant 	if (inphead == NULL)
    644       1.6  perseant 		return;
    645       1.6  perseant 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
    646       1.6  perseant 		free((char *)(*inpp));
    647       1.6  perseant 	free((char *)inphead);
    648       1.6  perseant 	free((char *)inpsort);
    649       1.6  perseant 	inphead = inpsort = NULL;
    650       1.1  perseant }
    651       1.1  perseant 
    652       1.1  perseant void
    653       1.1  perseant inodirty()
    654       1.1  perseant {
    655       1.6  perseant 	dirty(pbp);
    656       1.1  perseant }
    657       1.1  perseant 
    658       1.1  perseant void
    659       1.6  perseant clri(struct inodesc *idesc, char *type, int flag)
    660       1.6  perseant {
    661       1.6  perseant 	register struct dinode *dp;
    662       1.6  perseant 	struct bufarea *bp;
    663       1.6  perseant 	IFILE          *ifp;
    664       1.6  perseant 
    665       1.6  perseant 	dp = ginode(idesc->id_number);
    666       1.6  perseant 	if (flag == 1) {
    667       1.6  perseant 		pwarn("%s %s", type,
    668       1.6  perseant 		      (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
    669       1.6  perseant 		pinode(idesc->id_number);
    670       1.6  perseant 	}
    671       1.6  perseant 	if (preen || reply("CLEAR") == 1) {
    672       1.6  perseant 		if (preen)
    673       1.6  perseant 			printf(" (CLEARED)\n");
    674       1.6  perseant 		n_files--;
    675       1.6  perseant 		(void)ckinode(dp, idesc);
    676       1.6  perseant 		clearinode(dp);
    677       1.6  perseant 		statemap[idesc->id_number] = USTATE;
    678       1.6  perseant 		inodirty();
    679       1.6  perseant 
    680       1.6  perseant 		/* Send cleared inode to the free list */
    681       1.6  perseant 
    682       1.6  perseant 		ifp = lfs_ientry(idesc->id_number, &bp);
    683       1.6  perseant 		ifp->if_daddr = LFS_UNUSED_DADDR;
    684       1.6  perseant 		ifp->if_nextfree = sblock.lfs_free;
    685       1.6  perseant 		sblock.lfs_free = idesc->id_number;
    686       1.6  perseant 		sbdirty();
    687       1.6  perseant 		dirty(bp);
    688       1.6  perseant 		bp->b_flags &= ~B_INUSE;
    689       1.6  perseant 	}
    690       1.1  perseant }
    691       1.1  perseant 
    692       1.1  perseant int
    693       1.6  perseant findname(struct inodesc *idesc)
    694       1.1  perseant {
    695       1.6  perseant 	register struct direct *dirp = idesc->id_dirp;
    696       1.1  perseant 
    697       1.6  perseant 	if (dirp->d_ino != idesc->id_parent)
    698       1.6  perseant 		return (KEEPON);
    699       1.6  perseant 	memcpy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
    700       1.6  perseant 	return (STOP | FOUND);
    701       1.1  perseant }
    702       1.1  perseant 
    703       1.1  perseant int
    704       1.6  perseant findino(struct inodesc *idesc)
    705       1.1  perseant {
    706       1.6  perseant 	register struct direct *dirp = idesc->id_dirp;
    707       1.1  perseant 
    708       1.6  perseant 	if (dirp->d_ino == 0)
    709       1.6  perseant 		return (KEEPON);
    710       1.6  perseant 	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
    711       1.7  perseant 	    dirp->d_ino >= ROOTINO && dirp->d_ino < maxino) {
    712       1.6  perseant 		idesc->id_parent = dirp->d_ino;
    713       1.6  perseant 		return (STOP | FOUND);
    714       1.6  perseant 	}
    715       1.6  perseant 	return (KEEPON);
    716       1.1  perseant }
    717       1.1  perseant 
    718       1.1  perseant void
    719       1.6  perseant pinode(ino_t ino)
    720       1.1  perseant {
    721       1.6  perseant 	register struct dinode *dp;
    722       1.6  perseant 	register char  *p;
    723       1.6  perseant 	struct passwd  *pw;
    724       1.6  perseant 	time_t          t;
    725       1.6  perseant 
    726       1.6  perseant 	printf(" I=%u ", ino);
    727       1.7  perseant 	if (ino < ROOTINO || ino >= maxino)
    728       1.6  perseant 		return;
    729       1.6  perseant 	dp = ginode(ino);
    730       1.6  perseant 	if (dp) {
    731       1.6  perseant 		printf(" OWNER=");
    732       1.1  perseant #ifndef SMALL
    733       1.6  perseant 		if ((pw = getpwuid((int)dp->di_uid)) != 0)
    734       1.6  perseant 			printf("%s ", pw->pw_name);
    735       1.6  perseant 		else
    736       1.1  perseant #endif
    737       1.6  perseant 			printf("%u ", (unsigned)dp->di_uid);
    738       1.6  perseant 		printf("MODE=%o\n", dp->di_mode);
    739       1.6  perseant 		if (preen)
    740       1.6  perseant 			printf("%s: ", cdevname());
    741       1.8     lukem 		printf("SIZE=%llu ", (unsigned long long)dp->di_size);
    742       1.6  perseant 		t = dp->di_mtime;
    743       1.6  perseant 		p = ctime(&t);
    744       1.6  perseant 		printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
    745       1.6  perseant 	}
    746       1.1  perseant }
    747       1.1  perseant 
    748       1.1  perseant void
    749       1.6  perseant blkerror(ino_t ino, char *type, daddr_t blk)
    750       1.6  perseant {
    751       1.6  perseant 
    752       1.6  perseant 	pfatal("%d %s I=%u", blk, type, ino);
    753       1.6  perseant 	printf("\n");
    754       1.6  perseant 	if (exitonfail)
    755       1.6  perseant 		exit(1);
    756       1.6  perseant 	switch (statemap[ino]) {
    757       1.6  perseant 
    758       1.6  perseant 	case FSTATE:
    759       1.6  perseant 		statemap[ino] = FCLEAR;
    760       1.6  perseant 		return;
    761       1.6  perseant 
    762       1.6  perseant 	case DSTATE:
    763       1.6  perseant 		statemap[ino] = DCLEAR;
    764       1.6  perseant 		return;
    765       1.6  perseant 
    766       1.6  perseant 	case FCLEAR:
    767       1.6  perseant 	case DCLEAR:
    768       1.6  perseant 		return;
    769       1.6  perseant 
    770       1.6  perseant 	default:
    771       1.6  perseant 		errexit("BAD STATE %d TO BLKERR", statemap[ino]);
    772       1.6  perseant 		/* NOTREACHED */
    773       1.6  perseant 	}
    774       1.1  perseant }
    775       1.1  perseant 
    776       1.1  perseant /*
    777       1.1  perseant  * allocate an unused inode
    778       1.1  perseant  */
    779       1.1  perseant ino_t
    780       1.6  perseant allocino(ino_t request, int type)
    781       1.6  perseant {
    782       1.6  perseant 	register ino_t  ino;
    783       1.6  perseant 	register struct dinode *dp;
    784       1.6  perseant 	time_t          t;
    785       1.6  perseant 
    786       1.6  perseant 	if (request == 0)
    787       1.6  perseant 		request = ROOTINO;
    788       1.6  perseant 	else if (statemap[request] != USTATE)
    789       1.6  perseant 		return (0);
    790       1.6  perseant 	for (ino = request; ino < maxino; ino++)
    791       1.6  perseant 		if (statemap[ino] == USTATE)
    792       1.6  perseant 			break;
    793       1.6  perseant 	if (ino == maxino)
    794       1.6  perseant 		return (0);
    795       1.6  perseant 	switch (type & IFMT) {
    796       1.6  perseant 	case IFDIR:
    797       1.6  perseant 		statemap[ino] = DSTATE;
    798       1.6  perseant 		break;
    799       1.6  perseant 	case IFREG:
    800       1.6  perseant 	case IFLNK:
    801       1.6  perseant 		statemap[ino] = FSTATE;
    802       1.6  perseant 		break;
    803       1.6  perseant 	default:
    804       1.6  perseant 		return (0);
    805       1.6  perseant 	}
    806       1.6  perseant 	dp = ginode(ino);
    807       1.6  perseant 	dp->di_db[0] = allocblk((long)1);
    808       1.6  perseant 	if (dp->di_db[0] == 0) {
    809       1.6  perseant 		statemap[ino] = USTATE;
    810       1.6  perseant 		return (0);
    811       1.6  perseant 	}
    812       1.6  perseant 	dp->di_mode = type;
    813       1.6  perseant 	(void)time(&t);
    814       1.6  perseant 	dp->di_atime = t;
    815       1.6  perseant 	dp->di_mtime = dp->di_ctime = dp->di_atime;
    816       1.6  perseant 	dp->di_size = sblock.lfs_fsize;
    817       1.6  perseant 	dp->di_blocks = btodb(sblock.lfs_fsize);
    818       1.6  perseant 	n_files++;
    819       1.6  perseant 	inodirty();
    820       1.6  perseant 	if (newinofmt)
    821       1.6  perseant 		typemap[ino] = IFTODT(type);
    822       1.6  perseant 	return (ino);
    823       1.1  perseant }
    824       1.1  perseant 
    825       1.1  perseant /*
    826       1.1  perseant  * deallocate an inode
    827       1.1  perseant  */
    828       1.1  perseant void
    829       1.6  perseant freeino(ino_t ino)
    830       1.1  perseant {
    831       1.6  perseant 	struct inodesc  idesc;
    832       1.6  perseant 	struct dinode  *dp;
    833       1.1  perseant 
    834       1.6  perseant 	memset(&idesc, 0, sizeof(struct inodesc));
    835       1.6  perseant 	idesc.id_type = ADDR;
    836       1.6  perseant 	idesc.id_func = pass4check;
    837       1.6  perseant 	idesc.id_number = ino;
    838       1.6  perseant 	dp = ginode(ino);
    839       1.6  perseant 	(void)ckinode(dp, &idesc);
    840       1.6  perseant 	clearinode(dp);
    841       1.6  perseant 	inodirty();
    842       1.6  perseant 	statemap[ino] = USTATE;
    843       1.5  perseant 
    844       1.6  perseant 	n_files--;
    845       1.1  perseant }
    846