Logo Search packages:      
Sourcecode: hfsprogs version File versions  Download package

inode.c

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1980, 1986, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the University of
 *    California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include <sys/param.h>
#include <sys/time.h>

#include <ufs/ufs/dinode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ffs/fs.h>

#include <err.h>
#include <pwd.h>
#include <string.h>

#include "fsck.h"
#if   REV_ENDIAN_FS
#include "ufs_byte_order.h"
#endif      /* REV_ENDIAN_FS */

static ino_t startinum;

static int iblock __P((struct inodesc *, long ilevel, quad_t isize));

int
ckinode(dp, idesc)
      struct dinode *dp;
      register struct inodesc *idesc;
{
      ufs_daddr_t *ap;
      long ret, n, ndb, offset;
      struct dinode dino;
      quad_t remsize, sizepb;
      mode_t mode;

      if (idesc->id_fix != IGNORE)
            idesc->id_fix = DONTKNOW;
      idesc->id_entryno = 0;
      idesc->id_filesize = dp->di_size;
      mode = dp->di_mode & IFMT;
      if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
          dp->di_size < sblock.fs_maxsymlinklen))
            return (KEEPON);
      dino = *dp;
      ndb = howmany(dino.di_size, sblock.fs_bsize);
      for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
            if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
                  idesc->id_numfrags =
                        numfrags(&sblock, fragroundup(&sblock, offset));
            else
                  idesc->id_numfrags = sblock.fs_frag;
            if (*ap == 0)
                  continue;
            idesc->id_blkno = *ap;
            if (idesc->id_type == ADDR) {
//                printf("Calling id_func\n");
                  ret = (*idesc->id_func)(idesc);
            }
            else {
//                printf("Calling dirscan\n");
                  ret = dirscan(idesc);
            }
            if (ret & STOP)
                  return (ret);
      }
      idesc->id_numfrags = sblock.fs_frag;
      remsize = dino.di_size - sblock.fs_bsize * NDADDR;
      sizepb = sblock.fs_bsize;
      for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
            if (*ap) {
                  idesc->id_blkno = *ap;
                  ret = iblock(idesc, n, remsize);
                  if (ret & STOP) {
                        return (ret);
                  }
            }
            sizepb *= NINDIR(&sblock);
            remsize -= sizepb;
      }
      return (KEEPON);
}

static int
iblock(idesc, ilevel, isize)
      struct inodesc *idesc;
      long ilevel;
      quad_t isize;
{
      ufs_daddr_t *ap;
      ufs_daddr_t *aplim;
      struct bufarea *bp;
      int i, n, (*func)(), nif;
      quad_t sizepb;
      char buf[BUFSIZ];
      int rr=0;

      if (idesc->id_type == ADDR) {
            func = idesc->id_func;
            if (((n = (*func)(idesc)) & KEEPON) == 0)
                  return (n);
      } else
            func = dirscan;
//    printf("iblock :checkrange %d\n",ilevel);
      if (chkrange(idesc->id_blkno, idesc->id_numfrags))
            return (SKIP);
//    printf("iblock :getdatablk %d\n", ilevel);
      bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
#if   REV_ENDIAN_FS
      bp->b_type = INDIRECTBLOCK;
      swapblock(bp,0);
#endif      /* REV_ENDIAN_FS */
      ilevel--;
      for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
            sizepb *= NINDIR(&sblock);
      nif = howmany(isize , sizepb);
      if (nif > NINDIR(&sblock))
            nif = NINDIR(&sblock);
      if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
            aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
            rr=0;
            for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++,rr++) {
                  if (*ap == 0)
                        continue;
                  (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu and nif=%lu and rr=*lu",
                        idesc->id_number,nif,rr);
                  if (dofix(idesc, buf)) {
                        *ap = 0;
                        dirty(bp);
                  }
            }
            flush(fswritefd, bp);
      }
      aplim = &bp->b_un.b_indir[nif];
      for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
            if (*ap) {
                  idesc->id_blkno = *ap;
                  if (ilevel == 0)
                        n = (*func)(idesc);
                  else
                        n = iblock(idesc, ilevel, isize);
                  if (n & STOP) {
                        bp->b_flags &= ~B_INUSE;
                        return (n);
                  }
            }
            isize -= sizepb;
      }
      bp->b_flags &= ~B_INUSE;
      return (KEEPON);
}

/*
 * Check that a block in a legal block number.
 * Return 0 if in range, 1 if out of range.
 */
int
chkrange(blk, cnt)
      ufs_daddr_t blk;
      int cnt;
{
      register int c;

      if ((unsigned)(blk + cnt) > maxfsblock)
            return (1);
      c = dtog(&sblock, blk);
      if (blk < cgdmin(&sblock, c)) {
            if ((blk + cnt) > cgsblock(&sblock, c)) {
                  if (debug) {
                        printf("blk %ld < cgdmin %ld;",
                            blk, cgdmin(&sblock, c));
                        printf(" blk + cnt %ld > cgsbase %ld\n",
                            blk + cnt, cgsblock(&sblock, c));
                  }
                  return (1);
            }
      } else {
            if ((blk + cnt) > cgbase(&sblock, c+1)) {
                  if (debug)  {
                        printf("blk %ld >= cgdmin %ld;",
                            blk, cgdmin(&sblock, c));
                        printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
                            blk+cnt, sblock.fs_fpg);
                  }
                  return (1);
            }
      }
      return (0);
}

/*
 * General purpose interface for reading inodes.
 */
struct dinode *
ginode(inumber)
      ino_t inumber;
{
      ufs_daddr_t iblk;

      if (inumber < ROOTINO || inumber > maxino)
            errx(EEXIT, "bad inode number %d to ginode", inumber);
      if (startinum == 0 ||
          inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
            iblk = ino_to_fsba(&sblock, inumber);
            if (pbp != 0)
                  pbp->b_flags &= ~B_INUSE;
            pbp = getdatablk(iblk, sblock.fs_bsize);
#if   REV_ENDIAN_FS
            pbp->b_type = INODEBLOCK;
            swapblock(pbp,0);
#endif      /* REV_ENDIAN_FS */
            startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
      }
      return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
}

/*
 * Special purpose version of ginode used to optimize first pass
 * over all the inodes in numerical order.
 */
ino_t nextino, lastinum;
long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
struct dinode *inodebuf;

struct dinode *
getnextinode(inumber)
      ino_t inumber;
{
      long size;
      ufs_daddr_t dblk;
      static struct dinode *dp;

      if (inumber != nextino++ || inumber > maxino)
            errx(EEXIT, "bad inode number %d to nextinode", inumber);
      if (inumber >= lastinum) {
            readcnt++;
            dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
            if (readcnt % readpercg == 0) {
                  size = partialsize;
                  lastinum += partialcnt;
            } else {
                  size = inobufsize;
                  lastinum += fullcnt;
            }
            (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
#if   REV_ENDIAN_FS
            if(rev_endian)
                  byte_swap_dinodecount(inodebuf,size,0);
#endif      /* REV_ENDIAN_FS */
            dp = inodebuf;
      }
      return (dp++);
}

void
resetinodebuf()
{

      startinum = 0;
      nextino = 0;
      lastinum = 0;
      readcnt = 0;
      inobufsize = blkroundup(&sblock, INOBUFSIZE);
      fullcnt = inobufsize / sizeof(struct dinode);
      readpercg = sblock.fs_ipg / fullcnt;
      partialcnt = sblock.fs_ipg % fullcnt;
      partialsize = partialcnt * sizeof(struct dinode);
      if (partialcnt != 0) {
            readpercg++;
      } else {
            partialcnt = fullcnt;
            partialsize = inobufsize;
      }
      if (inodebuf == NULL &&
          (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
            errx(EEXIT, "Cannot allocate space for inode buffer");
      while (nextino < ROOTINO)
            (void)getnextinode(nextino);
}

void
freeinodebuf()
{

      if (inodebuf != NULL)
            free((char *)inodebuf);
      inodebuf = NULL;
}

/*
 * Routines to maintain information about directory inodes.
 * This is built during the first pass and used during the
 * second and third passes.
 *
 * Enter inodes into the cache.
 */
void
cacheino(dp, inumber)
      register struct dinode *dp;
      ino_t inumber;
{
      register struct inoinfo *inp;
      struct inoinfo **inpp;
      unsigned int blks;

      blks = howmany(dp->di_size, sblock.fs_bsize);
      if (blks > NDADDR)
            blks = NDADDR + NIADDR;
      inp = (struct inoinfo *)
            malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs_daddr_t));
      if (inp == NULL)
            return;
      inpp = &inphead[inumber % numdirs];
      inp->i_nexthash = *inpp;
      *inpp = inp;
      if (inumber == ROOTINO)
            inp->i_parent = ROOTINO;
      else
            inp->i_parent = (ino_t)0;
      inp->i_dotdot = (ino_t)0;
      inp->i_number = inumber;
      inp->i_isize = dp->di_size;
      inp->i_numblks = blks * sizeof(ufs_daddr_t);
      memmove(&inp->i_blks[0], &dp->di_db[0], (size_t)inp->i_numblks);
      if (inplast == listmax) {
            listmax += 100;
            inpsort = (struct inoinfo **)realloc((char *)inpsort,
                (unsigned)listmax * sizeof(struct inoinfo *));
            if (inpsort == NULL)
                  errx(EEXIT, "cannot increase directory list");
      }
      inpsort[inplast++] = inp;
}

/*
 * Look up an inode cache structure.
 */
struct inoinfo *
getinoinfo(inumber)
      ino_t inumber;
{
      register struct inoinfo *inp;

      for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
            if (inp->i_number != inumber)
                  continue;
            return (inp);
      }
      errx(EEXIT, "cannot find inode %d", inumber);
      return ((struct inoinfo *)0);
}

/*
 * Clean up all the inode cache structure.
 */
void
inocleanup()
{
      register struct inoinfo **inpp;

      if (inphead == NULL)
            return;
      for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
            free((char *)(*inpp));
      free((char *)inphead);
      free((char *)inpsort);
      inphead = inpsort = NULL;
}
      
void
inodirty()
{
      
      dirty(pbp);
}

void
clri(idesc, type, flag)
      register struct inodesc *idesc;
      char *type;
      int flag;
{
      register struct dinode *dp;

      dp = ginode(idesc->id_number);
      if (flag == 1) {
            pwarn("%s %s", type,
                (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
            pinode(idesc->id_number);
      }
      if (preen || reply("CLEAR") == 1) {
            if (preen)
                  printf(" (CLEARED)\n");
            n_files--;
            (void)ckinode(dp, idesc);
            clearinode(dp);
            statemap[idesc->id_number] = USTATE;
            inodirty();
      }
}

int
findname(idesc)
      struct inodesc *idesc;
{
      register struct direct *dirp = idesc->id_dirp;

      if (dirp->d_ino != idesc->id_parent)
            return (KEEPON);
      memmove(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
      return (STOP|FOUND);
}

int
findino(idesc)
      struct inodesc *idesc;
{
      register struct direct *dirp = idesc->id_dirp;

      if (dirp->d_ino == 0)
            return (KEEPON);
      if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
          dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
            idesc->id_parent = dirp->d_ino;
            return (STOP|FOUND);
      }
      return (KEEPON);
}

void
pinode(ino)
      ino_t ino;
{
      register struct dinode *dp;
      register char *p;
      struct passwd *pw;
      char *ctime();

      printf(" I=%lu ", ino);
      if (ino < ROOTINO || ino > maxino)
            return;
      dp = ginode(ino);
      printf(" OWNER=");
      if ((pw = getpwuid((int)dp->di_uid)) != 0)
            printf("%s ", pw->pw_name);
      else
            printf("%u ", (unsigned)dp->di_uid);
      printf("MODE=%o\n", dp->di_mode);
      if (preen)
            printf("%s: ", cdevname);
      printf("SIZE=%qu ", dp->di_size);
      p = ctime(&dp->di_mtime);
      printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
}

void
blkerror(ino, type, blk)
      ino_t ino;
      char *type;
      ufs_daddr_t blk;
{

      pfatal("%ld %s I=%lu", blk, type, ino);
      printf("\n");
      switch (statemap[ino]) {

      case FSTATE:
            statemap[ino] = FCLEAR;
            return;

      case DSTATE:
            statemap[ino] = DCLEAR;
            return;

      case FCLEAR:
      case DCLEAR:
            return;

      default:
            errx(EEXIT, "BAD STATE %d TO BLKERR", statemap[ino]);
            /* NOTREACHED */
      }
}

/*
 * allocate an unused inode
 */
ino_t
allocino(request, type)
      ino_t request;
      int type;
{
      register ino_t ino;
      register struct dinode *dp;

      if (request == 0)
            request = ROOTINO;
      else if (statemap[request] != USTATE)
            return (0);
      for (ino = request; ino < maxino; ino++)
            if (statemap[ino] == USTATE)
                  break;
      if (ino == maxino)
            return (0);
      switch (type & IFMT) {
      case IFDIR:
            statemap[ino] = DSTATE;
            break;
      case IFREG:
      case IFLNK:
            statemap[ino] = FSTATE;
            break;
      default:
            return (0);
      }
      dp = ginode(ino);
      dp->di_db[0] = allocblk((long)1);
      if (dp->di_db[0] == 0) {
            statemap[ino] = USTATE;
            return (0);
      }
      dp->di_mode = type;
      (void)time(&dp->di_atime);
      dp->di_mtime = dp->di_ctime = dp->di_atime;
      dp->di_size = sblock.fs_fsize;
#ifdef __APPLE__
      dp->di_blocks = btodb(sblock.fs_fsize, dev_bsize);
#else
      dp->di_blocks = btodb(sblock.fs_fsize);
#endif /* __APPLE__ */
      n_files++;
      inodirty();
      if (newinofmt)
            typemap[ino] = IFTODT(type);
      return (ino);
}

/*
 * deallocate an inode
 */
void
freeino(ino)
      ino_t ino;
{
      struct inodesc idesc;
      struct dinode *dp;

      memset(&idesc, 0, sizeof(struct inodesc));
      idesc.id_type = ADDR;
      idesc.id_func = pass4check;
      idesc.id_number = ino;
      dp = ginode(ino);
      (void)ckinode(dp, &idesc);
      clearinode(dp);
      inodirty();
      statemap[ino] = USTATE;
      n_files--;
}

Generated by  Doxygen 1.6.0   Back to index