Skip to content
Snippets Groups Projects
fs.c 11.9 KiB
Newer Older
rtm's avatar
rtm committed
#include "types.h"
kaashoek's avatar
kaashoek committed
#include "stat.h"
rtm's avatar
rtm committed
#include "param.h"
#include "x86.h"
#include "mmu.h"
#include "proc.h"
#include "defs.h"
#include "spinlock.h"
#include "buf.h"
#include "fs.h"
#include "fsvar.h"
kaashoek's avatar
kaashoek committed
#include "dev.h"
rtm's avatar
rtm committed

// these are inodes currently in use
// an entry is free if count == 0
struct inode inode[NINODE];
struct spinlock inode_table_lock;
rtm's avatar
rtm committed

rtm's avatar
rtm committed
uint rootdev = 1;

void
iinit(void)
{
  initlock(&inode_table_lock, "inode_table");
}

kaashoek's avatar
kaashoek committed
static uint 
balloc(uint dev) 
{
  int b;
  struct buf *bp;
  struct superblock *sb;
kaashoek's avatar
kaashoek committed
  int bi = 0;
kaashoek's avatar
kaashoek committed
  int size;
  int ninodes;
  uchar m;

  bp = bread(dev, 1);
  sb = (struct superblock *) bp->data;
  size = sb->size;
  ninodes = sb->ninodes;

  for (b = 0; b < size; b++) {
    if (b % BPB == 0) {
      brelse(bp);
      bp = bread(dev, BBLOCK(b, ninodes));
    }
    bi = b % BPB;
    m = 0x1 << (bi % 8);
    if ((bp->data[bi/8] & m) == 0) {  // is block free?
      break;
    }
  }
  if (b >= size)
    panic("balloc: out of blocks\n");

  bp->data[bi/8] |= 0x1 << (bi % 8);
  bwrite (bp, BBLOCK(b, ninodes));  // mark it allocated on disk
kaashoek's avatar
kaashoek committed
  brelse(bp);
kaashoek's avatar
kaashoek committed
  return b;
}

kaashoek's avatar
kaashoek committed
static void 
bfree(int dev, uint b)
{
  struct buf *bp;
  struct superblock *sb;
  int bi;
  int ninodes;
  uchar m;

  bp = bread(dev, 1);
  sb = (struct superblock *) bp->data;
  ninodes = sb->ninodes;
  brelse(bp);

kaashoek's avatar
kaashoek committed
  bp = bread(dev, b);
  memset(bp->data, 0, BSIZE);
  bwrite(bp, b);
  brelse(bp);

kaashoek's avatar
kaashoek committed
  bp = bread(dev, BBLOCK(b, ninodes));
  bi = b % BPB;
  m = ~(0x1 << (bi %8));
  bp->data[bi/8] &= m;
  bwrite (bp, BBLOCK(b, ninodes));  // mark it free on disk
kaashoek's avatar
kaashoek committed
  brelse(bp);
}
kaashoek's avatar
kaashoek committed

rtm's avatar
rtm committed
// returns an inode with busy set and incremented reference count.
rtm's avatar
rtm committed
struct inode *
iget(uint dev, uint inum)
{
  struct inode *ip, *nip;
rtm's avatar
rtm committed
  struct dinode *dip;
  struct buf *bp;

  acquire(&inode_table_lock);

 loop:
rtm's avatar
rtm committed
  for(ip = &inode[0]; ip < &inode[NINODE]; ip++){
    if(ip->count > 0 && ip->dev == dev && ip->inum == inum){
      if(ip->busy){
        sleep(ip, &inode_table_lock);
        goto loop;
      }
      ip->count++;
      ip->busy = 1;
rtm's avatar
rtm committed
      release(&inode_table_lock);
      return ip;
    }
    if(nip == 0 && ip->count == 0)
      nip = ip;
  }

  if(nip == 0)
    panic("out of inodes");

  nip->dev = dev;
  nip->inum = inum;
  nip->count = 1;
  nip->busy = 1;

  release(&inode_table_lock);

kaashoek's avatar
kaashoek committed
  bp = bread(dev, IBLOCK(inum));
rtm's avatar
rtm committed
  dip = &((struct dinode *)(bp->data))[inum % IPB];
  nip->type = dip->type;
kaashoek's avatar
kaashoek committed
  nip->major = dip->major;
  nip->minor = dip->minor;
rtm's avatar
rtm committed
  nip->nlink = dip->nlink;
  nip->size = dip->size;
  memmove(nip->addrs, dip->addrs, sizeof(nip->addrs));
  brelse(bp);

  return nip;
}

kaashoek's avatar
kaashoek committed
void 
iupdate (struct inode *ip)
{
  struct buf *bp;
  struct dinode *dip;

  bp = bread(ip->dev, IBLOCK(ip->inum));
  dip = &((struct dinode *)(bp->data))[ip->inum % IPB];
  dip->type = ip->type;
  dip->major = ip->major;
  dip->minor = ip->minor;
  dip->nlink = ip->nlink;
  dip->size = ip->size;
  memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
  bwrite (bp, IBLOCK(ip->inum));   // mark it allocated on the disk
kaashoek's avatar
kaashoek committed
  brelse(bp); 
}

kaashoek's avatar
kaashoek committed
struct inode *
ialloc(uint dev, short type)
{
  struct inode *ip;
  struct dinode *dip = 0;
  struct superblock *sb;
  int ninodes;
  int inum;
  struct buf *bp;

  bp = bread(dev, 1);
kaashoek's avatar
kaashoek committed
  sb = (struct superblock *) bp->data;
kaashoek's avatar
kaashoek committed
  ninodes = sb->ninodes;
  brelse(bp);
kaashoek's avatar
kaashoek committed

kaashoek's avatar
kaashoek committed
  for (inum = 1; inum < ninodes; inum++) {  // loop over inode blocks
kaashoek's avatar
kaashoek committed
    bp = bread(dev, IBLOCK(inum));
kaashoek's avatar
kaashoek committed
    dip = &((struct dinode *)(bp->data))[inum % IPB];
    if (dip->type == 0) {  // a free inode
      break;
    }
    brelse(bp);
  }
 
  if (inum >= ninodes) 
kaashoek's avatar
kaashoek committed
    panic ("ialloc: no inodes left\n");
kaashoek's avatar
kaashoek committed

  dip->type = type;
  bwrite (bp, IBLOCK(inum));   // mark it allocated on the disk
kaashoek's avatar
kaashoek committed
  brelse(bp);
  ip = iget (dev, inum);
  return ip;
}

kaashoek's avatar
kaashoek committed
static void
kaashoek's avatar
kaashoek committed
ifree(struct inode *ip)
kaashoek's avatar
kaashoek committed
{
kaashoek's avatar
kaashoek committed
  ip->type = 0;
  iupdate(ip);
kaashoek's avatar
kaashoek committed
}

rtm's avatar
rtm committed
void
ilock(struct inode *ip)
{
  if(ip->count < 1)
    panic("ilock");

  acquire(&inode_table_lock);

  while(ip->busy)
    sleep(ip, &inode_table_lock);
  ip->busy = 1;

  release(&inode_table_lock);
}

// caller is holding onto a reference to this inode, but no
// longer needs to examine or change it, so clear ip->busy.
void
iunlock(struct inode *ip)
{
  if(ip->busy != 1 || ip->count < 1)
rtm's avatar
rtm committed
    panic("iunlock");

  acquire(&inode_table_lock);

  ip->busy = 0;
  wakeup(ip);

  release(&inode_table_lock);
}

uint
bmap(struct inode *ip, uint bn)
{
  unsigned x;
kaashoek's avatar
kaashoek committed
  uint *a;
  struct buf *inbp;
kaashoek's avatar
kaashoek committed
  if(bn >= MAXFILE)
kaashoek's avatar
kaashoek committed
  if (bn < NDIRECT) {
    x = ip->addrs[bn];
    if (x == 0)
      panic("bmap 2");
  } else {
kaashoek's avatar
kaashoek committed
    inbp = bread(ip->dev, ip->addrs[INDIRECT]);
kaashoek's avatar
kaashoek committed
    a = (uint *) inbp->data;
    x = a[bn - NDIRECT];
    brelse(inbp);
    if (x == 0)
      panic("bmap 3");
  }
kaashoek's avatar
kaashoek committed
  int i, j;
kaashoek's avatar
kaashoek committed
  struct buf *inbp;

  // free inode, its blocks, and remove dir entry
kaashoek's avatar
kaashoek committed
  for (i = 0; i < NADDRS; i++) {
kaashoek's avatar
kaashoek committed
      if (i == INDIRECT) {
kaashoek's avatar
kaashoek committed
	inbp = bread(ip->dev, ip->addrs[INDIRECT]);
rtm's avatar
rtm committed
        uint *a = (uint *) inbp->data;
kaashoek's avatar
kaashoek committed
	for (j = 0; j < NINDIRECT; j++) {
	  if (a[j] != 0) {
	    bfree(ip->dev, a[j]);
	    a[j] = 0;
	  }
	}
kaashoek's avatar
kaashoek committed
	brelse(inbp);
      }	
      bfree(ip->dev, ip->addrs[i]);
      ip->addrs[i] = 0;
    }
  }
  ip->size = 0;
  ip->major = 0;
  ip->minor = 0;
  iupdate(ip);
  ifree(ip);  // is this the right order?
}

rtm's avatar
rtm committed
// caller is releasing a reference to this inode.
// you must have the inode lock.
rtm's avatar
rtm committed
void
iput(struct inode *ip)
{
rtm's avatar
rtm committed
  if(ip->count < 1 || ip->busy != 1)
    panic("iput");

  if ((ip->count == 1) && (ip->nlink == 0)) 
rtm's avatar
rtm committed
  acquire(&inode_table_lock);

  ip->count -= 1;
  ip->busy = 0;
  wakeup(ip);

  release(&inode_table_lock);
}
rtm's avatar
rtm committed

void
rtm's avatar
rtm committed
idecref(struct inode *ip)
rtm's avatar
rtm committed
{
rtm's avatar
rtm committed
}

kaashoek's avatar
kaashoek committed
void
iincref(struct inode *ip)
{
  ilock(ip);
  ip->count++;
  iunlock(ip);
}

kaashoek's avatar
kaashoek committed
void
stati(struct inode *ip, struct stat *st)
{
  st->st_dev = ip->dev;
  st->st_ino = ip->inum;
  st->st_type = ip->type;
  st->st_nlink = ip->nlink;
  st->st_size = ip->size;
}

#define min(a, b) ((a) < (b) ? (a) : (b))

rtm's avatar
rtm committed
int
readi(struct inode *ip, char *dst, uint off, uint n)
rtm's avatar
rtm committed
{
  uint target = n, n1;
  struct buf *bp;

kaashoek's avatar
kaashoek committed
  if (ip->type == T_DEV) {
    if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].d_read)
      return -1;
    return devsw[ip->major].d_read (ip->minor, dst, n);
kaashoek's avatar
kaashoek committed
  }

rtm's avatar
rtm committed
  while(n > 0 && off < ip->size){
kaashoek's avatar
kaashoek committed
    bp = bread(ip->dev, bmap(ip, off / BSIZE));
rtm's avatar
rtm committed
    n1 = min(n, ip->size - off);
kaashoek's avatar
kaashoek committed
    n1 = min(n1, BSIZE - (off % BSIZE));
    memmove(dst, bp->data + (off % BSIZE), n1);
rtm's avatar
rtm committed
    n -= n1;
    off += n1;
    dst += n1;
    brelse(bp);
  }

  return target - n;
}

kaashoek's avatar
kaashoek committed
static int
kaashoek's avatar
kaashoek committed
newblock(struct inode *ip, uint lbn)
{
  struct buf *inbp;
  uint *inaddrs;
  uint b;

  if (lbn < NDIRECT) {
    if (ip->addrs[lbn] == 0) {
      b = balloc(ip->dev);
      if (b <= 0) return -1;
      ip->addrs[lbn] = b;
    }
  } else {
    if (ip->addrs[INDIRECT] == 0) {
      b = balloc(ip->dev);
      if (b <= 0) return -1;
      ip->addrs[INDIRECT] = b;
    }
kaashoek's avatar
kaashoek committed
    inbp = bread(ip->dev, ip->addrs[INDIRECT]);
kaashoek's avatar
kaashoek committed
    inaddrs = (uint *) inbp->data;
    if (inaddrs[lbn - NDIRECT] == 0) {
      b = balloc(ip->dev);
      if (b <= 0) return -1;
      inaddrs[lbn - NDIRECT] = b;
kaashoek's avatar
kaashoek committed
      bwrite(inbp, ip->addrs[INDIRECT]);
kaashoek's avatar
kaashoek committed
    }
    brelse(inbp);
  }
  return 0;
}

kaashoek's avatar
kaashoek committed
int
writei(struct inode *ip, char *addr, uint off, uint n)
kaashoek's avatar
kaashoek committed
{
  if (ip->type == T_DEV) {
kaashoek's avatar
kaashoek committed
    if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].d_write)
      return -1;
kaashoek's avatar
kaashoek committed
    return devsw[ip->major].d_write (ip->minor, addr, n);
kaashoek's avatar
kaashoek committed
  } else if (ip->type == T_FILE || ip->type == T_DIR) {
kaashoek's avatar
kaashoek committed
    struct buf *bp;
    int r = 0;
    int m;
    int lbn;
    while (r < n) {
      lbn = off / BSIZE;
kaashoek's avatar
kaashoek committed
      if (lbn >= MAXFILE) return r;
      if (newblock(ip, lbn) < 0) {
	cprintf("newblock failed\n");
	return r;
kaashoek's avatar
kaashoek committed
      }
kaashoek's avatar
kaashoek committed
      bp = bread(ip->dev, bmap(ip, lbn));
kaashoek's avatar
kaashoek committed
      memmove (bp->data + off % BSIZE, addr, m);
kaashoek's avatar
kaashoek committed
      bwrite (bp, bmap(ip, lbn));
kaashoek's avatar
kaashoek committed
      brelse (bp);
      r += m;
      off += m;
    }
    if (r > 0) {
      if (off > ip->size) {
	if (ip->type == T_DIR) ip->size = ((off / BSIZE) + 1) * BSIZE;
	else ip->size = off;
kaashoek's avatar
kaashoek committed
      }
      iupdate(ip);
    }
    return r;
kaashoek's avatar
kaashoek committed
  } else {
    panic ("writei: unknown type\n");
kaashoek's avatar
kaashoek committed
  }
}

// look up a path name, in one of three modes.
// NAMEI_LOOKUP: return locked target inode.
// NAMEI_CREATE: return locked parent inode.
//   but return 0 if name does exist.
// NAMEI_DELETE: return locked parent inode, offset of dirent in *ret_off.
//   return 0 if name doesn't exist.
rtm's avatar
rtm committed
struct inode *
namei(char *path, int mode, uint *ret_off)
rtm's avatar
rtm committed
{
  struct inode *dp;
kaashoek's avatar
kaashoek committed
  struct proc *p = curproc[cpu()];
  char *cp = path, *cp1;
rtm's avatar
rtm committed
  uint off, dev;
  struct buf *bp;
  struct dirent *ep;
rtm's avatar
rtm committed
  unsigned ninum;
kaashoek's avatar
kaashoek committed
  if (*cp == '/') dp = iget(rootdev, 1);
  else {
    dp = p->cwd;
    iincref(dp);
    ilock(dp);
  }
rtm's avatar
rtm committed

  while(*cp == '/')
    cp++;

  while(1){
    if(*cp == '\0'){
      if(mode == NAMEI_LOOKUP)
        return dp;
      iput(dp);
      return 0;
rtm's avatar
rtm committed

    if(dp->type != T_DIR){
      iput(dp);
      return 0;
    }

kaashoek's avatar
kaashoek committed
    for(off = 0; off < dp->size; off += BSIZE){
      bp = bread(dp->dev, bmap(dp, off / BSIZE));
rtm's avatar
rtm committed
      for(ep = (struct dirent *) bp->data;
kaashoek's avatar
kaashoek committed
          ep < (struct dirent *) (bp->data + BSIZE);
rtm's avatar
rtm committed
          ep++){
        if(ep->inum == 0) 
          continue;
        for(i = 0; i < DIRSIZ && cp[i] != '/' && cp[i]; i++)
          if(cp[i] != ep->name[i])
            break;
        if((cp[i] == '\0' || cp[i] == '/') && (i >= DIRSIZ || ep->name[i] == '\0')){
          off += (uchar*)ep - bp->data;
rtm's avatar
rtm committed
          ninum = ep->inum;
          brelse(bp);
          cp += i;
          goto found;
        }
      }
      brelse(bp);
    }
    atend = 1;
    for(cp1 = cp; *cp1; cp1++)
      if(*cp1 == '/')
        atend = 0;
    if(mode == NAMEI_CREATE && atend)
      return dp;

rtm's avatar
rtm committed
    iput(dp);
    return 0;

  found:
    if(mode == NAMEI_DELETE && *cp == '\0'){
      *ret_off = off;
      return dp;
    }
rtm's avatar
rtm committed
    dev = dp->dev;
    iput(dp);
    dp = iget(dev, ninum);
rtm's avatar
rtm committed
    if(dp->type == 0 || dp->nlink < 1)
      panic("namei");
rtm's avatar
rtm committed
    while(*cp == '/')
      cp++;
  }
}
kaashoek's avatar
kaashoek committed

rtm's avatar
rtm committed
void
wdir(struct inode *dp, char *name, uint ino)
{
  uint off;
rtm's avatar
rtm committed
  struct dirent de;
rtm's avatar
rtm committed
  int i;
rtm's avatar
rtm committed

  for(off = 0; off < dp->size; off += sizeof(de)){
    if(readi(dp, (char *) &de, off, sizeof(de)) != sizeof(de))
      panic("wdir read");
    if(de.inum == 0)
      break;
kaashoek's avatar
kaashoek committed
  }
rtm's avatar
rtm committed
  de.inum = ino;
rtm's avatar
rtm committed
  for(i = 0; i < DIRSIZ && name[i]; i++)
rtm's avatar
rtm committed
    de.name[i] = name[i];
rtm's avatar
rtm committed
  for( ; i < DIRSIZ; i++)
rtm's avatar
rtm committed
    de.name[i] = '\0';

  if(writei(dp, (char *) &de, off, sizeof(de)) != sizeof(de))
    panic("wdir write");
rtm's avatar
rtm committed
}

kaashoek's avatar
kaashoek committed
struct inode *
mknod(char *cp, short type, short major, short minor)
kaashoek's avatar
kaashoek committed
{
kaashoek's avatar
kaashoek committed

  if ((dp = namei(cp, NAMEI_CREATE, 0)) == 0)
kaashoek's avatar
kaashoek committed
  ip = ialloc(dp->dev, type);
kaashoek's avatar
kaashoek committed
  ip->major = major;
  ip->minor = minor;
  ip->size = 0;
rtm's avatar
rtm committed
  ip->nlink = 1;

  iupdate (ip);  // write new inode to disk
kaashoek's avatar
kaashoek committed
  
rtm's avatar
rtm committed
  wdir(dp, cp, ip->inum);
  iput(dp);
  return ip;
kaashoek's avatar
kaashoek committed
}
kaashoek's avatar
kaashoek committed

int
unlink(char *cp)
{
  struct inode *ip, *dp;
  struct dirent de;
  uint off, inum, dev;
  if ((dp = namei(cp, NAMEI_DELETE, &off)) == 0)
kaashoek's avatar
kaashoek committed
    return -1;

  dev = dp->dev;

  if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de) || de.inum == 0)
    panic("unlink no entry");
  memset(&de, 0, sizeof(de));
  if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
    panic("unlink dir write");
kaashoek's avatar
kaashoek committed
  iput(dp);
  ip = iget(dev, inum);
rtm's avatar
rtm committed
  if(ip->nlink < 1)
    panic("unlink nlink < 1");

kaashoek's avatar
kaashoek committed
  return 0;
}
rtm's avatar
rtm committed

int
link(char *name1, char *name2)
{
  struct inode *ip, *dp;
rtm's avatar
rtm committed

  if ((ip = namei(name1, NAMEI_LOOKUP, 0)) == 0)
    return -1;
  if(ip->type == T_DIR){
    iput(ip);
rtm's avatar
rtm committed
    return -1;
  }

  iunlock(ip);

  if ((dp = namei(name2, NAMEI_CREATE, 0)) == 0) {
    idecref(ip);
    return -1;
  }
  if(dp->dev != ip->dev){
    idecref(ip);
    iput(dp);
rtm's avatar
rtm committed
    return -1;
  }
  
rtm's avatar
rtm committed
  ip->nlink += 1;
  iupdate (ip);

  wdir(dp, name2, ip->inum);
  iput(dp);
  iput(ip);

  return 0;
}