fs.c 15.95 KiB
// File system implementation.  Five layers:
//   + Blocks: allocator for raw disk blocks.
//   + Log: crash recovery for multi-step updates.
//   + Files: inode allocator, reading, writing, metadata.
//   + Directories: inode with special contents (list of other inodes!)
//   + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
//
// This file contains the low-level file system manipulation
// routines.  The (higher-level) system call implementations
// are in sysfile.c.
#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "param.h"
#include "include/stat.h"
#include "include/spinlock.h"
#include "proc.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
#include "file.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
// there should be one superblock per disk device, but we run with
// only one device
struct superblock sb; 
// Read the super block.
static void
readsb(int dev, struct superblock *sb)
  struct buf *bp;
  bp = bread(dev, 1);
  memmove(sb, bp->data, sizeof(*sb));
  brelse(bp);
// Init fs
void
fsinit(int dev) {
  readsb(dev, &sb);
  if(sb.magic != FSMAGIC)
    panic("invalid file system");
  initlog(dev, &sb);
// Zero a block.
static void
bzero(int dev, int bno)
  struct buf *bp;
  bp = bread(dev, bno);
  memset(bp->data, 0, BSIZE);
  log_write(bp);
  brelse(bp);
// Blocks.
// Allocate a zeroed disk block.
// returns 0 if out of disk space.
static uint
balloc(uint dev)
  int b, bi, m;
  struct buf *bp;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
bp = 0; for(b = 0; b < sb.size; b += BPB){ bp = bread(dev, BBLOCK(b, sb)); for(bi = 0; bi < BPB && b + bi < sb.size; bi++){ m = 1 << (bi % 8); if((bp->data[bi/8] & m) == 0){ // Is block free? bp->data[bi/8] |= m; // Mark block in use. log_write(bp); brelse(bp); bzero(dev, b + bi); return b + bi; } } brelse(bp); } printf("balloc: out of blocks\n"); return 0; } // Free a disk block. static void bfree(int dev, uint b) { struct buf *bp; int bi, m; bp = bread(dev, BBLOCK(b, sb)); bi = b % BPB; m = 1 << (bi % 8); if((bp->data[bi/8] & m) == 0) panic("freeing free block"); bp->data[bi/8] &= ~m; log_write(bp); brelse(bp); } // Inodes. // // An inode describes a single unnamed file. // The inode disk structure holds metadata: the file's type, // its size, the number of links referring to it, and the // list of blocks holding the file's content. // // The inodes are laid out sequentially on disk at block // sb.inodestart. Each inode has a number, indicating its // position on the disk. // // The kernel keeps a table of in-use inodes in memory // to provide a place for synchronizing access // to inodes used by multiple processes. The in-memory // inodes include book-keeping information that is // not stored on disk: ip->ref and ip->valid. // // An inode and its in-memory representation go through a // sequence of states before they can be used by the // rest of the file system code. // // * Allocation: an inode is allocated if its type (on disk) // is non-zero. ialloc() allocates, and iput() frees if // the reference and link counts have fallen to zero. // // * Referencing in table: an entry in the inode table // is free if ip->ref is zero. Otherwise ip->ref tracks // the number of in-memory pointers to the entry (open // files and current directories). iget() finds or // creates a table entry and increments its ref; iput() // decrements ref. // // * Valid: the information (type, size, &c) in an inode // table entry is only correct when ip->valid is 1.