Newer
Older
#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"
// Inode table. The inode table is an in-memory cache of the
// on-disk inode structures. If an inode in the table has a non-zero
// reference count, then some open files refer to it and it must stay
// in memory. If an inode has a zero reference count, it is only in
// memory as a cache in hopes of being used again (avoiding a disk read).
// Any inode with reference count zero can be evicted from the table.
//
// In addition to having a reference count, inodes can be marked busy
// (just like bufs), meaning that some code has logically locked the
// inode, and others are not allowed to look at it.
// This locking can last for a long
// time (for example, if the inode is busy during a disk access),
// so we don't use spin locks. Instead, if a process wants to use
// a particular inode, it must sleep(ip) to wait for it to be not busy.
// See iget below.
struct spinlock inode_table_lock;
void
iinit(void)
{
initlock(&inode_table_lock, "inode_table");
}
struct buf *bp;
struct superblock *sb;
bp = bread(dev, 1);
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?
bp->data[bi/8] |= 0x1 << (bi % 8);
bwrite(bp, BBLOCK(b, ninodes)); // mark it allocated on disk
brelse(bp);
return b;
bfree(int dev, uint b)
{
struct buf *bp;
struct superblock *sb;
bp = bread(dev, b);
memset(bp->data, 0, BSIZE);
bwrite(bp, b);
brelse(bp);
bwrite(bp, BBLOCK(b, ninodes)); // mark it free on disk
// Find the inode with number inum on device dev
// and return an in-memory copy. Loads the inode
// from disk into the in-core table if necessary.
// The returned inode has busy set and has its ref count incremented.
// Caller must iput the return value when done with it.
struct dinode *dip;
struct buf *bp;
acquire(&inode_table_lock);
loop:
// Since we droped inode_table_lock, ip might have been reused
// for some other inode entirely. Must start the scan over,
// and hopefully this time we will find the inode we want
// and it will not be busy.
dip = &((struct dinode*)(bp->data))[inum % IPB];
nip->nlink = dip->nlink;
nip->size = dip->size;
memmove(nip->addrs, dip->addrs, sizeof(nip->addrs));
brelse(bp);
return nip;
}
{
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));
// Allocate a new inode with the given type
// from the file system on device dev.
ialloc(uint dev, short type)
{
struct inode *ip;
struct superblock *sb;
int ninodes;
int inum;
struct buf *bp;
bp = bread(dev, 1);
for(inum = 1; inum < ninodes; inum++) { // loop over inode blocks
dip = &((struct dinode*)(bp->data))[inum % IPB];
if(dip->type == 0) { // a free inode
memset(dip, 0, sizeof(*dip));
dip->type = type;
bwrite(bp, IBLOCK(inum)); // mark it allocated on the disk
brelse(bp);
ip = iget(dev, inum);
return ip;
panic("ialloc: no inodes");
// Lock the given inode (wait for it to be not busy,
// and then ip->busy).
// Caller must already hold a reference to ip.
// Otherwise, if all the references to ip go away,
// it might be reused underfoot.
panic("ilock");
acquire(&inode_table_lock);
while(ip->busy)
sleep(ip, &inode_table_lock);
ip->busy = 1;
release(&inode_table_lock);
}
// Caller holds reference to ip and has locked it.
// Caller no longer needs to examine / change it.
// Unlock it, but keep the reference.
panic("iunlock");
acquire(&inode_table_lock);
ip->busy = 0;
wakeup(ip);
release(&inode_table_lock);
}
uint
bmap(struct inode *ip, uint bn)
{
panic("bmap 1");
return x;
}
{
for(i = 0; i < NADDRS; i++) {
if(ip->addrs[i] != 0) {
if(i == INDIRECT) {
inbp = bread(ip->dev, ip->addrs[INDIRECT]);
for(j = 0; j < NINDIRECT; j++) {
if(a[j] != 0) {
bfree(ip->dev, a[j]);
a[j] = 0;
}
}
brelse(inbp);
ip->addrs[i] = 0;
}
}
ip->size = 0;
iupdate(ip);
}
// Caller holds reference to ip and has locked it,
// possibly editing it.
// Release lock and drop the reference.
ilock(ip);
iput(ip);
// Returns ip to enable ip = iincref(ip1) idiom.
struct inode*
st->dev = ip->dev;
st->ino = ip->inum;
st->type = ip->type;
st->nlink = ip->nlink;
st->size = ip->size;
#define min(a, b) ((a) < (b) ? (a) : (b))
readi(struct inode *ip, char *dst, uint off, uint n)
if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read)
n1 = min(n1, BSIZE - (off % BSIZE));
memmove(dst, bp->data + (off % BSIZE), n1);
n -= n1;
off += n1;
dst += n1;
brelse(bp);
}
return target - n;
}
newblock(struct inode *ip, uint lbn)
{
struct buf *inbp;
uint *inaddrs;
uint b;
if(lbn < NDIRECT) {
if(ip->addrs[lbn] == 0) {
inaddrs = (uint*) inbp->data;
if(inaddrs[lbn - NDIRECT] == 0) {
}
brelse(inbp);
}
return 0;
}
writei(struct inode *ip, char *addr, uint off, uint n)
if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write)
}
for(r=0; r<n; ) {
lbn = off / BSIZE;
if(lbn >= MAXFILE)
return r;
if(newblock(ip, lbn) < 0) {
cprintf("newblock failed\n");
return r;
m = min(BSIZE - off % BSIZE, n-r);
bp = bread(ip->dev, bmap(ip, lbn));
memmove(bp->data + off % BSIZE, addr, m);
bwrite(bp, bmap(ip, lbn));
brelse(bp);
r += m;
off += m;
}
if(r > 0 && off > ip->size) {
if(ip->type == T_DIR)
ip->size = ((off / BSIZE) + 1) * BSIZE;
else
ip->size = off;
iupdate(ip);
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
// Skip over the next path element in path,
// saving it in *name and its length in *len.
// Return a pointer to the element after that
// (after any trailing slashes).
// Thus the caller can check whether *path=='\0'
// to see whether the name just removed was
// the last one.
// If there is no name to remove, return 0.
//
// Examples:
// skipelem("a/bb/c") = "bb/c", with *name = "a/bb/c", len=1
// skipelem("///a/bb") = "b", with *name="a/bb", len=1
// skipelem("") = skipelem("////") = 0
//
static char*
skipelem(char *path, char **name, int *len)
{
while(*path == '/')
path++;
if(*path == 0)
return 0;
*name = path;
while(*path != '/' && *path != 0)
path++;
*len = path - *name;
while(*path == '/')
path++;
return path;
}
// Look for a directory entry in a directory.
// If not found, return -1.
// If found:
// set *poff to the byte offset of the directory entry
// set *pinum to the inode number
// return 0.
static int
lookup(struct inode *dp, char *name, int namelen, uint *poff, uint *pinum)
{
uint off;
struct buf *bp;
struct dirent *de;
if(dp->type != T_DIR)
return -1;
for(off = 0; off < dp->size; off += BSIZE){
bp = bread(dp->dev, bmap(dp, off / BSIZE));
for(de = (struct dirent*) bp->data;
de < (struct dirent*) (bp->data + BSIZE);
de++){
if(de->inum == 0)
continue;
if(memcmp(name, de->name, namelen) == 0 &&
(namelen == DIRSIZ || de->name[namelen]== 0)){
// entry matches path element
*poff = off + (uchar*)de - bp->data;
*pinum = de->inum;
brelse(bp);
return 0;
}
}
brelse(bp);
}
return -1;
}
// look up a path name, in one of three modes.
// NAMEI_LOOKUP: return locked target inode.
// NAMEI_CREATE: return locked parent inode.
// return 0 if name does exist.
// *ret_last points to last path component (i.e. new file name).
// *ret_ip points to the the name that did exist, if it did.
// *ret_ip and *ret_last may be zero even if return value is zero.
// NAMEI_DELETE: return locked parent inode, offset of dirent in *ret_off.
// return 0 if name doesn't exist.
namei(char *path, int mode, uint *ret_off,
char **ret_last, struct inode **ret_ip)
if(ret_off)
*ret_off = 0xffffffff;
if(ret_last)
*ret_last = 0;
if(ret_ip)
*ret_ip = 0;
while((path = skipelem(path, &name, &namelen)) != 0){
// Truncate names in path to DIRSIZ chars.
if(namelen > DIRSIZ)
namelen = DIRSIZ;
if(lookup(dp, name, namelen, &off, &inum) < 0){
if(mode == NAMEI_CREATE && *path == '\0'){
*ret_last = name;
return dp;
if((namelen == 1 && memcmp(name, ".", 1) == 0) ||
(namelen == 2 && memcmp(name, "..", 2) == 0)){
goto fail;
*ret_off = off;
return dp;
}
if(mode == NAMEI_LOOKUP)
return dp;
if(mode == NAMEI_CREATE && ret_ip){
*ret_ip = dp;
return 0;
}
goto fail;
fail:
iput(dp);
return 0;
// Write a new directory entry (name, ino) into the directory dp.
// Caller must have locked dp.
if(readi(dp, (char*) &de, off, sizeof(de)) != sizeof(de))
if(writei(dp, (char*) &de, off, sizeof(de)) != sizeof(de))
struct inode *ip, *dp;
ip = mknod1(dp, last, type, major, minor);
iput(dp);
return ip;
}
// Create a new inode named name inside dp
// and return its locked inode structure.
// If name already exists, return 0.
mknod1(struct inode *dp, char *name, short type, short major, short minor)
{
struct inode *ip;
struct inode *ip, *dp;
struct dirent de;
dev = dp->dev;
if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de) || de.inum == 0)
panic("unlink no entry");
// Cannot remove "." or ".." - the 2 and 3 count the trailing NUL.
if(memcmp(de.name, ".", 2) == 0 || memcmp(de.name, "..", 3) == 0){
iput(dp);
return -1;
}
memset(&de, 0, sizeof(de));
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
panic("unlink dir write");
ip->nlink--;
iupdate(ip);
iput(ip);
if((ip = namei(name1, NAMEI_LOOKUP, 0, 0, 0)) == 0)
return -1;
if(ip->type == T_DIR){
iput(ip);
if((dp = namei(name2, NAMEI_CREATE, 0, &last, 0)) == 0) {
idecref(ip);
return -1;
}
if(dp->dev != ip->dev){
idecref(ip);
iput(dp);