1*4e3fced9Sperseant /* $NetBSD: inode.c,v 1.11 2001/07/13 20:30:18 perseant Exp $ */ 2369e9cadSperseant 3369e9cadSperseant /* 4369e9cadSperseant * Copyright (c) 1997, 1998 5369e9cadSperseant * Konrad Schroder. All rights reserved. 6369e9cadSperseant * Copyright (c) 1980, 1986, 1993 7369e9cadSperseant * The Regents of the University of California. All rights reserved. 8369e9cadSperseant * 9369e9cadSperseant * Redistribution and use in source and binary forms, with or without 10369e9cadSperseant * modification, are permitted provided that the following conditions 11369e9cadSperseant * are met: 12369e9cadSperseant * 1. Redistributions of source code must retain the above copyright 13369e9cadSperseant * notice, this list of conditions and the following disclaimer. 14369e9cadSperseant * 2. Redistributions in binary form must reproduce the above copyright 15369e9cadSperseant * notice, this list of conditions and the following disclaimer in the 16369e9cadSperseant * documentation and/or other materials provided with the distribution. 17369e9cadSperseant * 3. All advertising materials mentioning features or use of this software 18369e9cadSperseant * must display the following acknowledgement: 19369e9cadSperseant * This product includes software developed by the University of 20369e9cadSperseant * California, Berkeley and its contributors. 21369e9cadSperseant * 4. Neither the name of the University nor the names of its contributors 22369e9cadSperseant * may be used to endorse or promote products derived from this software 23369e9cadSperseant * without specific prior written permission. 24369e9cadSperseant * 25369e9cadSperseant * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26369e9cadSperseant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27369e9cadSperseant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28369e9cadSperseant * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29369e9cadSperseant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30369e9cadSperseant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31369e9cadSperseant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32369e9cadSperseant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33369e9cadSperseant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34369e9cadSperseant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35369e9cadSperseant * SUCH DAMAGE. 36369e9cadSperseant */ 37369e9cadSperseant 38369e9cadSperseant #include <sys/param.h> 39369e9cadSperseant #include <sys/time.h> 40369e9cadSperseant #include <ufs/ufs/dinode.h> 41369e9cadSperseant #include <ufs/ufs/dir.h> 42369e9cadSperseant #include <sys/mount.h> /* XXX */ 43369e9cadSperseant #include <ufs/lfs/lfs.h> 44369e9cadSperseant #ifndef SMALL 45369e9cadSperseant #include <pwd.h> 46369e9cadSperseant #endif 47369e9cadSperseant #include <stdio.h> 48369e9cadSperseant #include <stdlib.h> 49369e9cadSperseant #include <string.h> 50369e9cadSperseant 51369e9cadSperseant #include "fsck.h" 52369e9cadSperseant #include "fsutil.h" 53369e9cadSperseant #include "extern.h" 54369e9cadSperseant 55369e9cadSperseant extern SEGUSE *seg_table; 56e6c70652Sperseant extern daddr_t *din_table; 57369e9cadSperseant 581d259671Sperseant static int iblock(struct inodesc *, long, u_int64_t); 59369e9cadSperseant int blksreqd(struct lfs *, int); 60369e9cadSperseant int lfs_maxino(void); 611d259671Sperseant /* static void dump_inoblk (struct lfs *, struct dinode *); */ 62369e9cadSperseant 63369e9cadSperseant /* stolen from lfs_inode.c */ 64369e9cadSperseant /* Search a block for a specific dinode. */ 65369e9cadSperseant struct dinode * 66ad4e6868Sperseant lfs_difind(struct lfs * fs, ino_t ino, struct dinode * dip) 67369e9cadSperseant { 68*4e3fced9Sperseant struct dinode *ldip, *fin; 69369e9cadSperseant 70*4e3fced9Sperseant #ifdef LFS_IFILE_FRAG_ADDRESSING 71*4e3fced9Sperseant if (fs->lfs_version == 1) 72*4e3fced9Sperseant fin = dip + INOPB(fs); 73*4e3fced9Sperseant else 74*4e3fced9Sperseant fin = dip + INOPF(fs); 75*4e3fced9Sperseant #else 76*4e3fced9Sperseant fin = dip + INOPB(fs); 77*4e3fced9Sperseant #endif 78*4e3fced9Sperseant 79*4e3fced9Sperseant for (ldip = dip; ldip < fin; ++ldip) { 80*4e3fced9Sperseant if (ldip->di_inumber == ino) 81*4e3fced9Sperseant return ldip; 82*4e3fced9Sperseant } 83ad4e6868Sperseant /* printf("lfs_difind: dinode %u not found\n", ino); */ 84369e9cadSperseant return NULL; 85369e9cadSperseant } 86369e9cadSperseant 87369e9cadSperseant /* 88369e9cadSperseant * Calculate the number of blocks required to be able to address data block 89369e9cadSperseant * blkno (counting, of course, indirect blocks). blkno must >=0. 90369e9cadSperseant */ 911d259671Sperseant int 921d259671Sperseant blksreqd(struct lfs * fs, int blkno) 93369e9cadSperseant { 94369e9cadSperseant long n = blkno; 95369e9cadSperseant 96369e9cadSperseant if (blkno < NDADDR) 97369e9cadSperseant return blkno; 98369e9cadSperseant n -= NDADDR; 99369e9cadSperseant if (n < NINDIR(fs)) 100369e9cadSperseant return blkno + 1; 101369e9cadSperseant n -= NINDIR(fs); 102369e9cadSperseant if (n < NINDIR(fs) * NINDIR(fs)) 103369e9cadSperseant return blkno + 2 + n / NINDIR(fs) + 1; 104369e9cadSperseant n -= NINDIR(fs) * NINDIR(fs); 105369e9cadSperseant return blkno + 2 + NINDIR(fs) + n / (NINDIR(fs) * NINDIR(fs)) + 1; 106369e9cadSperseant } 107369e9cadSperseant 108369e9cadSperseant #define BASE_SINDIR (NDADDR) 109369e9cadSperseant #define BASE_DINDIR (NDADDR+NINDIR(fs)) 110369e9cadSperseant #define BASE_TINDIR (NDADDR+NINDIR(fs)+NINDIR(fs)*NINDIR(fs)) 111369e9cadSperseant 112369e9cadSperseant #define D_UNITS (NINDIR(fs)) 113369e9cadSperseant #define T_UNITS (NINDIR(fs)*NINDIR(fs)) 114369e9cadSperseant 115369e9cadSperseant ufs_daddr_t lfs_bmap(struct lfs *, struct dinode *, ufs_daddr_t); 116369e9cadSperseant 1171d259671Sperseant ufs_daddr_t 1181d259671Sperseant lfs_bmap(struct lfs * fs, struct dinode * idinode, ufs_daddr_t lbn) 119369e9cadSperseant { 120369e9cadSperseant ufs_daddr_t residue, up, off = 0; 121369e9cadSperseant struct bufarea *bp; 122369e9cadSperseant 123369e9cadSperseant if (lbn > 0 && lbn > (idinode->di_size - 1) / dev_bsize) { 124369e9cadSperseant return UNASSIGNED; 125369e9cadSperseant } 126369e9cadSperseant /* 127369e9cadSperseant * Indirect blocks: if it is a first-level indirect, pull its 128369e9cadSperseant * address from the inode; otherwise, call ourselves to find the 129369e9cadSperseant * address of the parent indirect block, and load that to find 130369e9cadSperseant * the desired address. 131369e9cadSperseant */ 132369e9cadSperseant if (lbn < 0) { 133369e9cadSperseant lbn *= -1; 134369e9cadSperseant if (lbn == NDADDR) { 135369e9cadSperseant /* printf("lbn %d: single indir base\n", -lbn); */ 136369e9cadSperseant return idinode->di_ib[0]; /* single indirect */ 137369e9cadSperseant } else if (lbn == BASE_DINDIR + 1) { 138369e9cadSperseant /* printf("lbn %d: double indir base\n", -lbn); */ 139369e9cadSperseant return idinode->di_ib[1]; /* double indirect */ 140369e9cadSperseant } else if (lbn == BASE_TINDIR + 2) { 141369e9cadSperseant /* printf("lbn %d: triple indir base\n", -lbn); */ 142369e9cadSperseant return idinode->di_ib[2]; /* triple indirect */ 143369e9cadSperseant } 144369e9cadSperseant /* 145369e9cadSperseant * Find the immediate parent. This is essentially finding the 146369e9cadSperseant * residue of modulus, and then rounding accordingly. 147369e9cadSperseant */ 148369e9cadSperseant residue = (lbn - NDADDR) % NINDIR(fs); 149369e9cadSperseant if (residue == 1) { 150369e9cadSperseant /* Double indirect. Parent is the triple. */ 151369e9cadSperseant up = idinode->di_ib[2]; 152369e9cadSperseant off = (lbn - 2 - BASE_TINDIR) / (NINDIR(fs) * NINDIR(fs)); 153369e9cadSperseant if (up == UNASSIGNED || up == LFS_UNUSED_DADDR) 154369e9cadSperseant return UNASSIGNED; 155369e9cadSperseant /* printf("lbn %d: parent is the triple\n", -lbn); */ 156369e9cadSperseant bp = getddblk(up, sblock.lfs_bsize); 157369e9cadSperseant bp->b_flags &= ~B_INUSE; 158369e9cadSperseant return ((daddr_t *)(bp->b_un.b_buf))[off]; 1591d259671Sperseant } else { /* residue == 0 */ 160369e9cadSperseant /* Single indirect. Two cases. */ 161369e9cadSperseant if (lbn < BASE_TINDIR) { 162369e9cadSperseant /* Parent is the double, simple */ 163369e9cadSperseant up = -(BASE_DINDIR) - 1; 164369e9cadSperseant off = (lbn - BASE_DINDIR) / D_UNITS; 1651d259671Sperseant /* 1661d259671Sperseant * printf("lbn %d: parent is %d/%d\n", -lbn, 1671d259671Sperseant * up,off); 1681d259671Sperseant */ 169369e9cadSperseant } else { 170369e9cadSperseant /* Ancestor is the triple, more complex */ 171369e9cadSperseant up = ((lbn - BASE_TINDIR) / T_UNITS) 172369e9cadSperseant * T_UNITS + BASE_TINDIR + 1; 173369e9cadSperseant off = (lbn / D_UNITS) - (up / D_UNITS); 174369e9cadSperseant up = -up; 1751d259671Sperseant /* 1761d259671Sperseant * printf("lbn %d: parent is %d/%d\n", -lbn, 1771d259671Sperseant * up,off); 1781d259671Sperseant */ 179369e9cadSperseant } 180369e9cadSperseant } 181369e9cadSperseant } else { 182369e9cadSperseant /* Direct block. Its parent must be a single indirect. */ 183369e9cadSperseant if (lbn < NDADDR) 184369e9cadSperseant return idinode->di_db[lbn]; 185369e9cadSperseant else { 186369e9cadSperseant /* Parent is an indirect block. */ 187369e9cadSperseant up = -(((lbn - NDADDR) / D_UNITS) * D_UNITS + NDADDR); 188369e9cadSperseant off = (lbn - NDADDR) % D_UNITS; 189369e9cadSperseant /* printf("lbn %d: parent is %d/%d\n", lbn,up,off); */ 190369e9cadSperseant } 191369e9cadSperseant } 192369e9cadSperseant up = lfs_bmap(fs, idinode, up); 193369e9cadSperseant if (up == UNASSIGNED || up == LFS_UNUSED_DADDR) 194369e9cadSperseant return UNASSIGNED; 195369e9cadSperseant bp = getddblk(up, sblock.lfs_bsize); 196369e9cadSperseant bp->b_flags &= ~B_INUSE; 197369e9cadSperseant return ((daddr_t *)(bp->b_un.b_buf))[off]; 198369e9cadSperseant } 199369e9cadSperseant 200369e9cadSperseant /* 201369e9cadSperseant * This is kind of gross. We use this to find the nth block 202369e9cadSperseant * from a file whose inode has disk address idaddr. In practice 203369e9cadSperseant * we will only use this to find blocks of the ifile. 204369e9cadSperseant */ 20575453f28Sperseant static struct bufarea empty; 20675453f28Sperseant 207369e9cadSperseant struct bufarea * 208369e9cadSperseant getfileblk(struct lfs * fs, struct dinode * idinode, ino_t lbn) 209369e9cadSperseant { 210369e9cadSperseant struct bufarea *bp; 211369e9cadSperseant ufs_daddr_t blkno; 212369e9cadSperseant static char empty_buf[65536]; 213369e9cadSperseant 214369e9cadSperseant empty.b_un.b_buf = &(empty_buf[0]); 215369e9cadSperseant 216369e9cadSperseant blkno = lfs_bmap(fs, idinode, lbn); 217369e9cadSperseant if (blkno == UNASSIGNED || blkno == LFS_UNUSED_DADDR) { 218369e9cadSperseant printf("Warning: ifile lbn %d unassigned!\n", lbn); 219369e9cadSperseant return ∅ 220369e9cadSperseant } 221369e9cadSperseant bp = getddblk(blkno, sblock.lfs_bsize); 222369e9cadSperseant return bp; 223369e9cadSperseant } 224369e9cadSperseant 225e6c70652Sperseant #if 0 2261d259671Sperseant static struct dinode * 2271d259671Sperseant gidinode(void) 228369e9cadSperseant { 229369e9cadSperseant static struct dinode *idinode; 230369e9cadSperseant 231369e9cadSperseant if (!idinode) { /* only need to do this once */ 232ad4e6868Sperseant idinode = lfs_difind(&sblock, sblock.lfs_ifile, &ifblock); 233369e9cadSperseant } 234369e9cadSperseant return idinode; 235369e9cadSperseant } 236e6c70652Sperseant #endif 237369e9cadSperseant 238369e9cadSperseant struct ifile * 239e6c70652Sperseant lfs_ientry(ino_t ino, struct bufarea ** bpp) 240369e9cadSperseant { 241*4e3fced9Sperseant IFILE *ifp; 242369e9cadSperseant 243e6c70652Sperseant *bpp = getfileblk(&sblock, lfs_ginode(LFS_IFILE_INUM), 244e6c70652Sperseant ino / sblock.lfs_ifpb + sblock.lfs_cleansz + 245e6c70652Sperseant sblock.lfs_segtabsz); 24675453f28Sperseant if (*bpp == &empty) { 24775453f28Sperseant printf("Warning: ino %d ientry in unassigned block\n", ino); 24875453f28Sperseant } 2491d259671Sperseant if (*bpp) { 250*4e3fced9Sperseant if (sblock.lfs_version > 1) { 251*4e3fced9Sperseant ifp = (((IFILE *)((*bpp)->b_un.b_buf)) + 252e6c70652Sperseant (ino % sblock.lfs_ifpb)); 253*4e3fced9Sperseant } else { 254*4e3fced9Sperseant ifp = (IFILE *)(((IFILE_V1 *) 255*4e3fced9Sperseant ((*bpp)->b_un.b_buf)) + 256*4e3fced9Sperseant (ino % sblock.lfs_ifpb)); 257*4e3fced9Sperseant } 258369e9cadSperseant return ifp; 2591d259671Sperseant } else 260369e9cadSperseant return NULL; 261369e9cadSperseant } 262369e9cadSperseant 263369e9cadSperseant SEGUSE * 264e6c70652Sperseant lfs_gseguse(int segnum, struct bufarea ** bpp) 265369e9cadSperseant { 266369e9cadSperseant int blkno; 267*4e3fced9Sperseant struct bufarea *bp; 268369e9cadSperseant 269*4e3fced9Sperseant blkno = segnum / sblock.lfs_sepb + sblock.lfs_cleansz; 270*4e3fced9Sperseant (*bpp) = bp = getfileblk(&sblock, lfs_ginode(LFS_IFILE_INUM), blkno); 271*4e3fced9Sperseant if (sblock.lfs_version == 1) 272*4e3fced9Sperseant return (SEGUSE *)((SEGUSE_V1 *)(bp->b_un.b_buf) + 273*4e3fced9Sperseant segnum % sblock.lfs_sepb); 274*4e3fced9Sperseant else 275*4e3fced9Sperseant return (SEGUSE *)(bp->b_un.b_buf) + segnum % sblock.lfs_sepb; 276e6c70652Sperseant } 277e6c70652Sperseant 278e6c70652Sperseant daddr_t 279e6c70652Sperseant lfs_ino_daddr(ino_t inumber) 280e6c70652Sperseant { 281e6c70652Sperseant daddr_t daddr; 282e6c70652Sperseant IFILE *ifp; 283e6c70652Sperseant struct bufarea *bp; 284e6c70652Sperseant 285e6c70652Sperseant if (din_table[inumber]) { 286e6c70652Sperseant daddr = din_table[inumber]; 287e6c70652Sperseant } else { 288e6c70652Sperseant if (inumber == LFS_IFILE_INUM) 28975453f28Sperseant daddr = idaddr; 290e6c70652Sperseant else { 291e6c70652Sperseant ifp = lfs_ientry(inumber, &bp); 292e6c70652Sperseant if (ifp == NULL) { 293e6c70652Sperseant return NULL; 294e6c70652Sperseant } 295e6c70652Sperseant if (ifp->if_daddr == LFS_UNUSED_DADDR) { 296369e9cadSperseant bp->b_flags &= ~B_INUSE; 297e6c70652Sperseant return NULL; 298e6c70652Sperseant } 299e6c70652Sperseant bp->b_flags &= ~B_INUSE; 300e6c70652Sperseant daddr = ifp->if_daddr; 301e6c70652Sperseant } 302e6c70652Sperseant 303e6c70652Sperseant din_table[inumber] = daddr; 304*4e3fced9Sperseant seg_table[dtosn(&sblock, daddr)].su_nbytes += DINODE_SIZE; 305e6c70652Sperseant } 306e6c70652Sperseant return daddr; 307369e9cadSperseant } 308369e9cadSperseant 309369e9cadSperseant struct dinode * 310e6c70652Sperseant lfs_ginode(ino_t inumber) 311e6c70652Sperseant { 312369e9cadSperseant struct ifile *ifp; 313369e9cadSperseant struct dinode *din; 314e6c70652Sperseant struct bufarea *bp; 315e6c70652Sperseant daddr_t daddr; 316369e9cadSperseant 31775453f28Sperseant if (inumber >= maxino) 318369e9cadSperseant errexit("bad inode number %d to lfs_ginode\n", inumber); 319e6c70652Sperseant 320e6c70652Sperseant #if 0 321e6c70652Sperseant if (inumber == LFS_IFILE_INUM) { 32275453f28Sperseant daddr = idaddr; 323e6c70652Sperseant if (din_table[LFS_IFILE_INUM] == 0) { 324e6c70652Sperseant din_table[LFS_IFILE_INUM] = daddr; 325*4e3fced9Sperseant seg_table[dtosn(&sblock, daddr)].su_nbytes += DINODE_SIZE; 326369e9cadSperseant } 327e6c70652Sperseant return gidinode(); 328e6c70652Sperseant } 329e6c70652Sperseant #endif 330e6c70652Sperseant 331e6c70652Sperseant daddr = lfs_ino_daddr(inumber); 332e6c70652Sperseant if (daddr == 0) 333e6c70652Sperseant return NULL; 334e6c70652Sperseant 335369e9cadSperseant if (pbp) 336369e9cadSperseant pbp->b_flags &= ~B_INUSE; 337e6c70652Sperseant 338*4e3fced9Sperseant if (sblock.lfs_version == 1) 339e6c70652Sperseant pbp = getddblk(daddr, sblock.lfs_bsize); 340*4e3fced9Sperseant else 341*4e3fced9Sperseant pbp = getddblk(daddr, sblock.lfs_fsize); 342ad4e6868Sperseant din = lfs_difind(&sblock, inumber, pbp->b_un.b_dinode); 343369e9cadSperseant 344e6c70652Sperseant if (din == NULL) { 345e6c70652Sperseant pfatal("INODE %d NOT FOUND\n", inumber); 346e6c70652Sperseant if (reply("free")) { 347e6c70652Sperseant ifp = lfs_ientry(inumber, &bp); 348e6c70652Sperseant ifp->if_daddr = LFS_UNUSED_DADDR; 349e6c70652Sperseant ifp->if_nextfree = sblock.lfs_free; 350e6c70652Sperseant sblock.lfs_free = inumber; 351e6c70652Sperseant sbdirty(); 352e6c70652Sperseant dirty(bp); 353e6c70652Sperseant bp->b_flags &= ~B_INUSE; 354e6c70652Sperseant } 355e6c70652Sperseant } 356369e9cadSperseant return din; 357369e9cadSperseant } 358369e9cadSperseant 359369e9cadSperseant /* imported from lfs_vfsops.c */ 360369e9cadSperseant int 361369e9cadSperseant ino_to_fsba(struct lfs * fs, ino_t ino) 362369e9cadSperseant { 363369e9cadSperseant daddr_t daddr = LFS_UNUSED_DADDR; 364369e9cadSperseant struct ifile *ifp; 365e6c70652Sperseant struct bufarea *bp; 366369e9cadSperseant 367369e9cadSperseant /* Translate the inode number to a disk address. */ 368369e9cadSperseant if (ino == LFS_IFILE_INUM) 369369e9cadSperseant daddr = fs->lfs_idaddr; 370369e9cadSperseant else { 371e6c70652Sperseant ifp = lfs_ientry(ino, &bp); 372369e9cadSperseant if (ifp) { 373369e9cadSperseant daddr = ifp->if_daddr; 374369e9cadSperseant } else { 375369e9cadSperseant pwarn("Can't locate inode #%ud\n", ino); 376369e9cadSperseant } 377e6c70652Sperseant bp->b_flags &= ~B_INUSE; 378369e9cadSperseant } 379369e9cadSperseant return daddr; 380369e9cadSperseant } 381369e9cadSperseant 382369e9cadSperseant /* 383369e9cadSperseant * Check validity of held (direct) blocks in an inode. 384369e9cadSperseant */ 385369e9cadSperseant int 3861d259671Sperseant ckinode(struct dinode *dp, struct inodesc *idesc) 387369e9cadSperseant { 388369e9cadSperseant register ufs_daddr_t *ap; 389369e9cadSperseant long ret, n, ndb, offset; 390369e9cadSperseant struct dinode dino; 391369e9cadSperseant u_int64_t remsize, sizepb; 392369e9cadSperseant mode_t mode; 393369e9cadSperseant char pathbuf[MAXPATHLEN + 1]; 394369e9cadSperseant 395369e9cadSperseant if (idesc->id_fix != IGNORE) 396369e9cadSperseant idesc->id_fix = DONTKNOW; 397369e9cadSperseant idesc->id_entryno = 0; 398369e9cadSperseant idesc->id_filesize = dp->di_size; 399369e9cadSperseant mode = dp->di_mode & IFMT; 4001d259671Sperseant if (mode == IFBLK || mode == IFCHR || 4011d259671Sperseant (mode == IFLNK && (dp->di_size < sblock.lfs_maxsymlinklen || 4021d259671Sperseant (sblock.lfs_maxsymlinklen == 0 && 4031d259671Sperseant dp->di_blocks == 0)))) 404369e9cadSperseant return (KEEPON); 405369e9cadSperseant dino = *dp; 406369e9cadSperseant ndb = howmany(dino.di_size, sblock.lfs_bsize); 407369e9cadSperseant 408369e9cadSperseant for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { 409369e9cadSperseant if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) { 410369e9cadSperseant idesc->id_numfrags = 411369e9cadSperseant numfrags(&sblock, fragroundup(&sblock, offset)); 412369e9cadSperseant } else 413369e9cadSperseant idesc->id_numfrags = sblock.lfs_frag; 414369e9cadSperseant if (*ap == 0) { 415369e9cadSperseant if (idesc->id_type == DATA && ndb >= 0) { 416369e9cadSperseant /* An empty block in a directory XXX */ 417369e9cadSperseant getpathname(pathbuf, idesc->id_number, 418369e9cadSperseant idesc->id_number); 419369e9cadSperseant pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 420369e9cadSperseant pathbuf); 421369e9cadSperseant if (reply("ADJUST LENGTH") == 1) { 422369e9cadSperseant dp = ginode(idesc->id_number); 423369e9cadSperseant dp->di_size = (ap - &dino.di_db[0]) * 424369e9cadSperseant sblock.lfs_bsize; 425369e9cadSperseant printf( 426369e9cadSperseant "YOU MUST RERUN FSCK AFTERWARDS\n"); 427369e9cadSperseant rerun = 1; 428369e9cadSperseant inodirty(); 429369e9cadSperseant } 430369e9cadSperseant } 431369e9cadSperseant continue; 432369e9cadSperseant } 433369e9cadSperseant idesc->id_blkno = *ap; 434369e9cadSperseant idesc->id_lblkno = ap - &dino.di_db[0]; 435369e9cadSperseant if (idesc->id_type == ADDR) { 436369e9cadSperseant ret = (*idesc->id_func)(idesc); 4371d259671Sperseant } else 438369e9cadSperseant ret = dirscan(idesc); 439369e9cadSperseant idesc->id_lblkno = 0; 440369e9cadSperseant if (ret & STOP) 441369e9cadSperseant return (ret); 442369e9cadSperseant } 443369e9cadSperseant idesc->id_numfrags = sblock.lfs_frag; 444369e9cadSperseant remsize = dino.di_size - sblock.lfs_bsize * NDADDR; 445369e9cadSperseant sizepb = sblock.lfs_bsize; 446369e9cadSperseant for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { 447369e9cadSperseant if (*ap) { 448369e9cadSperseant idesc->id_blkno = *ap; 449369e9cadSperseant ret = iblock(idesc, n, remsize); 450369e9cadSperseant if (ret & STOP) 451369e9cadSperseant return (ret); 452369e9cadSperseant } else { 453369e9cadSperseant if (idesc->id_type == DATA && remsize > 0) { 454369e9cadSperseant /* An empty block in a directory XXX */ 455369e9cadSperseant getpathname(pathbuf, idesc->id_number, 456369e9cadSperseant idesc->id_number); 457369e9cadSperseant pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 458369e9cadSperseant pathbuf); 459369e9cadSperseant if (reply("ADJUST LENGTH") == 1) { 460369e9cadSperseant dp = ginode(idesc->id_number); 461369e9cadSperseant dp->di_size -= remsize; 462369e9cadSperseant remsize = 0; 463369e9cadSperseant printf( 464369e9cadSperseant "YOU MUST RERUN FSCK AFTERWARDS\n"); 465369e9cadSperseant rerun = 1; 466369e9cadSperseant inodirty(); 467369e9cadSperseant break; 468369e9cadSperseant } 469369e9cadSperseant } 470369e9cadSperseant } 471369e9cadSperseant sizepb *= NINDIR(&sblock); 472369e9cadSperseant remsize -= sizepb; 473369e9cadSperseant } 474369e9cadSperseant return (KEEPON); 475369e9cadSperseant } 476369e9cadSperseant 477369e9cadSperseant static int 4781d259671Sperseant iblock(struct inodesc * idesc, long ilevel, u_int64_t isize) 479369e9cadSperseant { 48075453f28Sperseant daddr_t *ap, *aplim; 48175453f28Sperseant struct bufarea *bp; 4821d259671Sperseant int i, n, (*func)(struct inodesc *), nif; 483369e9cadSperseant u_int64_t sizepb; 484369e9cadSperseant char pathbuf[MAXPATHLEN + 1], buf[BUFSIZ]; 485369e9cadSperseant struct dinode *dp; 486369e9cadSperseant 487369e9cadSperseant if (idesc->id_type == ADDR) { 488369e9cadSperseant func = idesc->id_func; 489369e9cadSperseant n = (*func)(idesc); 490369e9cadSperseant if ((n & KEEPON) == 0) 491369e9cadSperseant return (n); 492369e9cadSperseant } else 493369e9cadSperseant func = dirscan; 494*4e3fced9Sperseant if (chkrange(idesc->id_blkno, fragstofsb(&sblock, idesc->id_numfrags))) 495369e9cadSperseant return (SKIP); 496369e9cadSperseant bp = getddblk(idesc->id_blkno, sblock.lfs_bsize); 497369e9cadSperseant ilevel--; 498369e9cadSperseant for (sizepb = sblock.lfs_bsize, i = 0; i < ilevel; i++) 499369e9cadSperseant sizepb *= NINDIR(&sblock); 500369e9cadSperseant if (isize > sizepb * NINDIR(&sblock)) 501369e9cadSperseant nif = NINDIR(&sblock); 502369e9cadSperseant else 503369e9cadSperseant nif = howmany(isize, sizepb); 504369e9cadSperseant if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { 505369e9cadSperseant aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; 506369e9cadSperseant for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { 507369e9cadSperseant if (*ap == 0) 508369e9cadSperseant continue; 509369e9cadSperseant (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%u", 510369e9cadSperseant idesc->id_number); 511369e9cadSperseant if (dofix(idesc, buf)) { 512369e9cadSperseant *ap = 0; 513369e9cadSperseant dirty(bp); 514369e9cadSperseant } 515369e9cadSperseant } 516369e9cadSperseant flush(fswritefd, bp); 517369e9cadSperseant } 518369e9cadSperseant aplim = &bp->b_un.b_indir[nif]; 519369e9cadSperseant for (ap = bp->b_un.b_indir; ap < aplim; ap++) { 520369e9cadSperseant if (*ap) { 521369e9cadSperseant idesc->id_blkno = *ap; 522369e9cadSperseant if (ilevel == 0) 523369e9cadSperseant n = (*func)(idesc); 524369e9cadSperseant else 525369e9cadSperseant n = iblock(idesc, ilevel, isize); 526369e9cadSperseant if (n & STOP) { 527369e9cadSperseant bp->b_flags &= ~B_INUSE; 528369e9cadSperseant return (n); 529369e9cadSperseant } 530369e9cadSperseant } else { 531369e9cadSperseant if (idesc->id_type == DATA && isize > 0) { 532369e9cadSperseant /* An empty block in a directory XXX */ 533369e9cadSperseant getpathname(pathbuf, idesc->id_number, 534369e9cadSperseant idesc->id_number); 535369e9cadSperseant pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 536369e9cadSperseant pathbuf); 537369e9cadSperseant if (reply("ADJUST LENGTH") == 1) { 538369e9cadSperseant dp = ginode(idesc->id_number); 539369e9cadSperseant dp->di_size -= isize; 540369e9cadSperseant isize = 0; 541369e9cadSperseant printf( 542369e9cadSperseant "YOU MUST RERUN FSCK AFTERWARDS\n"); 543369e9cadSperseant rerun = 1; 544369e9cadSperseant inodirty(); 545369e9cadSperseant bp->b_flags &= ~B_INUSE; 546369e9cadSperseant return (STOP); 547369e9cadSperseant } 548369e9cadSperseant } 549369e9cadSperseant } 550369e9cadSperseant isize -= sizepb; 551369e9cadSperseant } 552369e9cadSperseant bp->b_flags &= ~B_INUSE; 553369e9cadSperseant return (KEEPON); 554369e9cadSperseant } 555369e9cadSperseant 556369e9cadSperseant /* 557369e9cadSperseant * Check that a block in a legal block number. 558369e9cadSperseant * Return 0 if in range, 1 if out of range. 559369e9cadSperseant */ 560369e9cadSperseant int 5611d259671Sperseant chkrange(daddr_t blk, int cnt) 562369e9cadSperseant { 563*4e3fced9Sperseant if (blk < sntod(&sblock, 0)) { 56475453f28Sperseant return (1); 56575453f28Sperseant } 566*4e3fced9Sperseant if (blk > maxfsblock) { 567*4e3fced9Sperseant return (1); 568*4e3fced9Sperseant } 569*4e3fced9Sperseant if (blk + cnt < sntod(&sblock, 0)) { 570*4e3fced9Sperseant return (1); 571*4e3fced9Sperseant } 572*4e3fced9Sperseant if (blk + cnt > maxfsblock) { 573369e9cadSperseant return (1); 574369e9cadSperseant } 575369e9cadSperseant return (0); 576369e9cadSperseant } 577369e9cadSperseant 578369e9cadSperseant /* 579369e9cadSperseant * General purpose interface for reading inodes. 580369e9cadSperseant */ 581369e9cadSperseant struct dinode * 582369e9cadSperseant ginode(ino_t inumber) 583369e9cadSperseant { 584369e9cadSperseant return lfs_ginode(inumber); 585369e9cadSperseant } 586369e9cadSperseant 587369e9cadSperseant /* 588369e9cadSperseant * Routines to maintain information about directory inodes. 589369e9cadSperseant * This is built during the first pass and used during the 590369e9cadSperseant * second and third passes. 591369e9cadSperseant * 592369e9cadSperseant * Enter inodes into the cache. 593369e9cadSperseant */ 594369e9cadSperseant void 5951d259671Sperseant cacheino(struct dinode *dp, ino_t inumber) 596369e9cadSperseant { 597369e9cadSperseant register struct inoinfo *inp; 598369e9cadSperseant struct inoinfo **inpp; 599369e9cadSperseant unsigned int blks; 600369e9cadSperseant 601369e9cadSperseant blks = howmany(dp->di_size, sblock.lfs_bsize); 602369e9cadSperseant if (blks > NDADDR) 603369e9cadSperseant blks = NDADDR + NIADDR; 604369e9cadSperseant inp = (struct inoinfo *) 605369e9cadSperseant malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t)); 606369e9cadSperseant if (inp == NULL) 607369e9cadSperseant return; 608369e9cadSperseant inpp = &inphead[inumber % numdirs]; 609369e9cadSperseant inp->i_nexthash = *inpp; 610369e9cadSperseant *inpp = inp; 611369e9cadSperseant inp->i_child = inp->i_sibling = inp->i_parentp = 0; 612369e9cadSperseant if (inumber == ROOTINO) 613369e9cadSperseant inp->i_parent = ROOTINO; 614369e9cadSperseant else 615369e9cadSperseant inp->i_parent = (ino_t)0; 616369e9cadSperseant inp->i_dotdot = (ino_t)0; 617369e9cadSperseant inp->i_number = inumber; 618369e9cadSperseant inp->i_isize = dp->di_size; 619369e9cadSperseant inp->i_numblks = blks * sizeof(daddr_t); 620369e9cadSperseant memcpy(&inp->i_blks[0], &dp->di_db[0], (size_t)inp->i_numblks); 621369e9cadSperseant if (inplast == listmax) { 622369e9cadSperseant listmax += 100; 623369e9cadSperseant inpsort = (struct inoinfo **)realloc((char *) inpsort, 624369e9cadSperseant (unsigned)listmax * sizeof(struct inoinfo *)); 625369e9cadSperseant if (inpsort == NULL) 626369e9cadSperseant errexit("cannot increase directory list"); 627369e9cadSperseant } 628369e9cadSperseant inpsort[inplast++] = inp; 629369e9cadSperseant } 630369e9cadSperseant 631369e9cadSperseant /* 632369e9cadSperseant * Look up an inode cache structure. 633369e9cadSperseant */ 634369e9cadSperseant struct inoinfo * 6351d259671Sperseant getinoinfo(ino_t inumber) 636369e9cadSperseant { 637369e9cadSperseant register struct inoinfo *inp; 638369e9cadSperseant 639369e9cadSperseant for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { 640369e9cadSperseant if (inp->i_number != inumber) 641369e9cadSperseant continue; 642369e9cadSperseant return (inp); 643369e9cadSperseant } 644369e9cadSperseant errexit("cannot find inode %d\n", inumber); 645369e9cadSperseant return ((struct inoinfo *)0); 646369e9cadSperseant } 647369e9cadSperseant 648369e9cadSperseant /* 649369e9cadSperseant * Clean up all the inode cache structure. 650369e9cadSperseant */ 651369e9cadSperseant void 652369e9cadSperseant inocleanup() 653369e9cadSperseant { 654369e9cadSperseant register struct inoinfo **inpp; 655369e9cadSperseant 656369e9cadSperseant if (inphead == NULL) 657369e9cadSperseant return; 658369e9cadSperseant for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) 659369e9cadSperseant free((char *)(*inpp)); 660369e9cadSperseant free((char *)inphead); 661369e9cadSperseant free((char *)inpsort); 662369e9cadSperseant inphead = inpsort = NULL; 663369e9cadSperseant } 664369e9cadSperseant 665369e9cadSperseant void 666369e9cadSperseant inodirty() 667369e9cadSperseant { 668369e9cadSperseant dirty(pbp); 669369e9cadSperseant } 670369e9cadSperseant 671369e9cadSperseant void 6721d259671Sperseant clri(struct inodesc *idesc, char *type, int flag) 673369e9cadSperseant { 674369e9cadSperseant register struct dinode *dp; 675e6c70652Sperseant struct bufarea *bp; 676e6c70652Sperseant IFILE *ifp; 677369e9cadSperseant 678369e9cadSperseant dp = ginode(idesc->id_number); 679369e9cadSperseant if (flag == 1) { 680369e9cadSperseant pwarn("%s %s", type, 681369e9cadSperseant (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); 682369e9cadSperseant pinode(idesc->id_number); 683369e9cadSperseant } 684369e9cadSperseant if (preen || reply("CLEAR") == 1) { 685369e9cadSperseant if (preen) 686369e9cadSperseant printf(" (CLEARED)\n"); 687369e9cadSperseant n_files--; 688369e9cadSperseant (void)ckinode(dp, idesc); 689369e9cadSperseant clearinode(dp); 690369e9cadSperseant statemap[idesc->id_number] = USTATE; 691369e9cadSperseant inodirty(); 692e6c70652Sperseant 693e6c70652Sperseant /* Send cleared inode to the free list */ 694e6c70652Sperseant 695e6c70652Sperseant ifp = lfs_ientry(idesc->id_number, &bp); 696e6c70652Sperseant ifp->if_daddr = LFS_UNUSED_DADDR; 697e6c70652Sperseant ifp->if_nextfree = sblock.lfs_free; 698e6c70652Sperseant sblock.lfs_free = idesc->id_number; 699e6c70652Sperseant sbdirty(); 700e6c70652Sperseant dirty(bp); 701e6c70652Sperseant bp->b_flags &= ~B_INUSE; 702369e9cadSperseant } 703369e9cadSperseant } 704369e9cadSperseant 705369e9cadSperseant int 7061d259671Sperseant findname(struct inodesc *idesc) 707369e9cadSperseant { 708369e9cadSperseant register struct direct *dirp = idesc->id_dirp; 709369e9cadSperseant 710369e9cadSperseant if (dirp->d_ino != idesc->id_parent) 711369e9cadSperseant return (KEEPON); 712369e9cadSperseant memcpy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); 713369e9cadSperseant return (STOP | FOUND); 714369e9cadSperseant } 715369e9cadSperseant 716369e9cadSperseant int 7171d259671Sperseant findino(struct inodesc *idesc) 718369e9cadSperseant { 719369e9cadSperseant register struct direct *dirp = idesc->id_dirp; 720369e9cadSperseant 721369e9cadSperseant if (dirp->d_ino == 0) 722369e9cadSperseant return (KEEPON); 723369e9cadSperseant if (strcmp(dirp->d_name, idesc->id_name) == 0 && 72475453f28Sperseant dirp->d_ino >= ROOTINO && dirp->d_ino < maxino) { 725369e9cadSperseant idesc->id_parent = dirp->d_ino; 726369e9cadSperseant return (STOP | FOUND); 727369e9cadSperseant } 728369e9cadSperseant return (KEEPON); 729369e9cadSperseant } 730369e9cadSperseant 731369e9cadSperseant void 7321d259671Sperseant pinode(ino_t ino) 733369e9cadSperseant { 734369e9cadSperseant register struct dinode *dp; 735369e9cadSperseant register char *p; 736369e9cadSperseant struct passwd *pw; 737369e9cadSperseant time_t t; 738369e9cadSperseant 739369e9cadSperseant printf(" I=%u ", ino); 74075453f28Sperseant if (ino < ROOTINO || ino >= maxino) 741369e9cadSperseant return; 742369e9cadSperseant dp = ginode(ino); 743369e9cadSperseant if (dp) { 744369e9cadSperseant printf(" OWNER="); 745369e9cadSperseant #ifndef SMALL 746369e9cadSperseant if ((pw = getpwuid((int)dp->di_uid)) != 0) 747369e9cadSperseant printf("%s ", pw->pw_name); 748369e9cadSperseant else 749369e9cadSperseant #endif 750369e9cadSperseant printf("%u ", (unsigned)dp->di_uid); 751369e9cadSperseant printf("MODE=%o\n", dp->di_mode); 752369e9cadSperseant if (preen) 753369e9cadSperseant printf("%s: ", cdevname()); 754f7650338Slukem printf("SIZE=%llu ", (unsigned long long)dp->di_size); 755369e9cadSperseant t = dp->di_mtime; 756369e9cadSperseant p = ctime(&t); 757369e9cadSperseant printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); 758369e9cadSperseant } 759369e9cadSperseant } 760369e9cadSperseant 761369e9cadSperseant void 7621d259671Sperseant blkerror(ino_t ino, char *type, daddr_t blk) 763369e9cadSperseant { 764369e9cadSperseant 765369e9cadSperseant pfatal("%d %s I=%u", blk, type, ino); 766369e9cadSperseant printf("\n"); 767369e9cadSperseant if (exitonfail) 768369e9cadSperseant exit(1); 769369e9cadSperseant switch (statemap[ino]) { 770369e9cadSperseant 771369e9cadSperseant case FSTATE: 772369e9cadSperseant statemap[ino] = FCLEAR; 773369e9cadSperseant return; 774369e9cadSperseant 775369e9cadSperseant case DSTATE: 776369e9cadSperseant statemap[ino] = DCLEAR; 777369e9cadSperseant return; 778369e9cadSperseant 779369e9cadSperseant case FCLEAR: 780369e9cadSperseant case DCLEAR: 781369e9cadSperseant return; 782369e9cadSperseant 783369e9cadSperseant default: 784369e9cadSperseant errexit("BAD STATE %d TO BLKERR", statemap[ino]); 785369e9cadSperseant /* NOTREACHED */ 786369e9cadSperseant } 787369e9cadSperseant } 788369e9cadSperseant 789369e9cadSperseant /* 790369e9cadSperseant * allocate an unused inode 791369e9cadSperseant */ 792369e9cadSperseant ino_t 7931d259671Sperseant allocino(ino_t request, int type) 794369e9cadSperseant { 795369e9cadSperseant register ino_t ino; 796369e9cadSperseant register struct dinode *dp; 797369e9cadSperseant time_t t; 798369e9cadSperseant 799369e9cadSperseant if (request == 0) 800369e9cadSperseant request = ROOTINO; 801369e9cadSperseant else if (statemap[request] != USTATE) 802369e9cadSperseant return (0); 803369e9cadSperseant for (ino = request; ino < maxino; ino++) 804369e9cadSperseant if (statemap[ino] == USTATE) 805369e9cadSperseant break; 806369e9cadSperseant if (ino == maxino) 807369e9cadSperseant return (0); 808369e9cadSperseant switch (type & IFMT) { 809369e9cadSperseant case IFDIR: 810369e9cadSperseant statemap[ino] = DSTATE; 811369e9cadSperseant break; 812369e9cadSperseant case IFREG: 813369e9cadSperseant case IFLNK: 814369e9cadSperseant statemap[ino] = FSTATE; 815369e9cadSperseant break; 816369e9cadSperseant default: 817369e9cadSperseant return (0); 818369e9cadSperseant } 819369e9cadSperseant dp = ginode(ino); 820369e9cadSperseant dp->di_db[0] = allocblk((long)1); 821369e9cadSperseant if (dp->di_db[0] == 0) { 822369e9cadSperseant statemap[ino] = USTATE; 823369e9cadSperseant return (0); 824369e9cadSperseant } 825369e9cadSperseant dp->di_mode = type; 826369e9cadSperseant (void)time(&t); 827369e9cadSperseant dp->di_atime = t; 828369e9cadSperseant dp->di_mtime = dp->di_ctime = dp->di_atime; 829369e9cadSperseant dp->di_size = sblock.lfs_fsize; 830*4e3fced9Sperseant dp->di_blocks = btofsb(&sblock, sblock.lfs_fsize); 831369e9cadSperseant n_files++; 832369e9cadSperseant inodirty(); 833369e9cadSperseant if (newinofmt) 834369e9cadSperseant typemap[ino] = IFTODT(type); 835369e9cadSperseant return (ino); 836369e9cadSperseant } 837369e9cadSperseant 838369e9cadSperseant /* 839369e9cadSperseant * deallocate an inode 840369e9cadSperseant */ 841369e9cadSperseant void 8421d259671Sperseant freeino(ino_t ino) 843369e9cadSperseant { 844369e9cadSperseant struct inodesc idesc; 845369e9cadSperseant struct dinode *dp; 846369e9cadSperseant 847369e9cadSperseant memset(&idesc, 0, sizeof(struct inodesc)); 848369e9cadSperseant idesc.id_type = ADDR; 849369e9cadSperseant idesc.id_func = pass4check; 850369e9cadSperseant idesc.id_number = ino; 851369e9cadSperseant dp = ginode(ino); 852369e9cadSperseant (void)ckinode(dp, &idesc); 853369e9cadSperseant clearinode(dp); 854369e9cadSperseant inodirty(); 855369e9cadSperseant statemap[ino] = USTATE; 856e6c70652Sperseant 857369e9cadSperseant n_files--; 858369e9cadSperseant } 859