xv6/fs.c

735 lines
15 KiB
C
Raw Normal View History

#include "types.h"
2006-08-12 04:33:50 +00:00
#include "stat.h"
#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"
#include "dev.h"
2006-09-07 14:28:12 +00:00
// 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 inode inode[NINODE];
struct spinlock inode_table_lock;
2006-07-21 22:10:40 +00:00
uint rootdev = 1;
void
iinit(void)
{
initlock(&inode_table_lock, "inode_table");
}
2006-09-06 17:50:20 +00:00
// Allocate a disk block.
2006-09-06 17:27:19 +00:00
static uint
balloc(uint dev)
2006-08-09 01:09:36 +00:00
{
int b;
struct buf *bp;
struct superblock *sb;
int bi = 0;
2006-08-09 01:09:36 +00:00
int size;
int ninodes;
uchar m;
bp = bread(dev, 1);
2006-09-06 17:27:19 +00:00
sb = (struct superblock*) bp->data;
2006-08-09 01:09:36 +00:00
size = sb->size;
ninodes = sb->ninodes;
2006-09-06 17:27:19 +00:00
for(b = 0; b < size; b++) {
if(b % BPB == 0) {
2006-08-09 01:09:36 +00:00
brelse(bp);
bp = bread(dev, BBLOCK(b, ninodes));
}
bi = b % BPB;
m = 0x1 << (bi % 8);
2006-09-06 17:27:19 +00:00
if((bp->data[bi/8] & m) == 0) { // is block free?
2006-08-09 01:09:36 +00:00
break;
}
}
2006-09-06 17:27:19 +00:00
if(b >= size)
2006-08-30 18:55:06 +00:00
panic("balloc: out of blocks");
2006-08-09 01:09:36 +00:00
bp->data[bi/8] |= 0x1 << (bi % 8);
2006-09-06 17:27:19 +00:00
bwrite(bp, BBLOCK(b, ninodes)); // mark it allocated on disk
brelse(bp);
2006-08-09 01:09:36 +00:00
return b;
}
2006-09-07 14:28:12 +00:00
// Free a disk block.
2006-09-06 17:27:19 +00:00
static void
bfree(int dev, uint b)
{
struct buf *bp;
struct superblock *sb;
int bi;
int ninodes;
uchar m;
bp = bread(dev, 1);
2006-09-06 17:27:19 +00:00
sb = (struct superblock*) bp->data;
ninodes = sb->ninodes;
brelse(bp);
bp = bread(dev, b);
memset(bp->data, 0, BSIZE);
bwrite(bp, b);
brelse(bp);
bp = bread(dev, BBLOCK(b, ninodes));
bi = b % BPB;
m = ~(0x1 << (bi %8));
bp->data[bi/8] &= m;
2006-09-06 17:27:19 +00:00
bwrite(bp, BBLOCK(b, ninodes)); // mark it free on disk
brelse(bp);
}
2006-08-09 01:09:36 +00:00
2006-09-06 17:50:20 +00:00
// 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.
2006-09-06 17:27:19 +00:00
struct inode*
iget(uint dev, uint inum)
{
struct inode *ip, *nip;
struct dinode *dip;
struct buf *bp;
acquire(&inode_table_lock);
loop:
nip = 0;
for(ip = &inode[0]; ip < &inode[NINODE]; ip++){
2006-09-07 15:15:32 +00:00
if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
if(ip->busy){
sleep(ip, &inode_table_lock);
2006-09-07 14:28:12 +00:00
// 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.
goto loop;
}
2006-09-07 15:15:32 +00:00
ip->ref++;
ip->busy = 1;
release(&inode_table_lock);
return ip;
}
2006-09-07 15:15:32 +00:00
if(nip == 0 && ip->ref == 0)
nip = ip;
}
if(nip == 0)
panic("out of inodes");
nip->dev = dev;
nip->inum = inum;
2006-09-07 15:15:32 +00:00
nip->ref = 1;
nip->busy = 1;
release(&inode_table_lock);
2006-08-09 01:09:36 +00:00
bp = bread(dev, IBLOCK(inum));
2006-09-06 17:27:19 +00:00
dip = &((struct dinode*)(bp->data))[inum % IPB];
nip->type = dip->type;
2006-08-08 18:07:37 +00:00
nip->major = dip->major;
nip->minor = dip->minor;
nip->nlink = dip->nlink;
nip->size = dip->size;
memmove(nip->addrs, dip->addrs, sizeof(nip->addrs));
brelse(bp);
return nip;
}
2006-09-07 14:28:12 +00:00
// Copy ip->d, which has changed, to disk.
// Caller must have locked ip.
2006-09-06 17:27:19 +00:00
void
iupdate(struct inode *ip)
{
struct buf *bp;
struct dinode *dip;
bp = bread(ip->dev, IBLOCK(ip->inum));
2006-09-06 17:27:19 +00:00
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));
2006-09-06 17:27:19 +00:00
bwrite(bp, IBLOCK(ip->inum)); // mark it allocated on the disk
brelse(bp);
}
2006-09-07 14:28:12 +00:00
// Allocate a new inode with the given type
// from the file system on device dev.
2006-09-06 17:27:19 +00:00
struct inode*
2006-08-08 18:07:37 +00:00
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);
2006-09-06 17:27:19 +00:00
sb = (struct superblock*) bp->data;
2006-08-08 18:07:37 +00:00
ninodes = sb->ninodes;
brelse(bp);
2006-08-09 01:09:36 +00:00
2006-09-06 17:27:19 +00:00
for(inum = 1; inum < ninodes; inum++) { // loop over inode blocks
2006-08-09 01:09:36 +00:00
bp = bread(dev, IBLOCK(inum));
2006-09-06 17:27:19 +00:00
dip = &((struct dinode*)(bp->data))[inum % IPB];
if(dip->type == 0) { // a free inode
2006-08-08 18:07:37 +00:00
break;
}
brelse(bp);
}
2006-09-06 17:27:19 +00:00
if(inum >= ninodes)
panic("ialloc: no inodes left");
2006-08-08 18:07:37 +00:00
2006-08-30 18:55:06 +00:00
memset(dip, 0, sizeof(*dip));
2006-08-08 18:07:37 +00:00
dip->type = type;
2006-09-06 17:27:19 +00:00
bwrite(bp, IBLOCK(inum)); // mark it allocated on the disk
2006-08-08 18:07:37 +00:00
brelse(bp);
2006-09-06 17:27:19 +00:00
ip = iget(dev, inum);
2006-08-08 18:07:37 +00:00
return ip;
}
2006-09-07 14:28:12 +00:00
// Free the given inode from its file system.
static void
ifree(struct inode *ip)
2006-08-08 18:07:37 +00:00
{
ip->type = 0;
iupdate(ip);
2006-08-08 18:07:37 +00:00
}
2006-09-07 14:28:12 +00:00
// 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.
2006-07-21 22:10:40 +00:00
void
ilock(struct inode *ip)
{
2006-09-07 15:15:32 +00:00
if(ip->ref < 1)
2006-07-21 22:10:40 +00:00
panic("ilock");
acquire(&inode_table_lock);
while(ip->busy)
sleep(ip, &inode_table_lock);
ip->busy = 1;
release(&inode_table_lock);
}
2006-09-07 14:28:12 +00:00
// Caller holds reference to ip and has locked it.
// Caller no longer needs to examine / change it.
// Unlock it, but keep the reference.
2006-07-21 22:10:40 +00:00
void
iunlock(struct inode *ip)
{
2006-09-07 15:15:32 +00:00
if(ip->busy != 1 || ip->ref < 1)
2006-07-21 22:10:40 +00:00
panic("iunlock");
acquire(&inode_table_lock);
ip->busy = 0;
wakeup(ip);
release(&inode_table_lock);
}
2006-09-07 14:28:12 +00:00
// Return the disk block address of the nth block in inode ip.
uint
bmap(struct inode *ip, uint bn)
{
unsigned x;
uint *a;
struct buf *inbp;
if(bn >= MAXFILE)
panic("bmap 1");
2006-09-06 17:27:19 +00:00
if(bn < NDIRECT) {
x = ip->addrs[bn];
2006-09-06 17:27:19 +00:00
if(x == 0)
panic("bmap 2");
} else {
if(ip->addrs[INDIRECT] == 0)
panic("bmap 3");
inbp = bread(ip->dev, ip->addrs[INDIRECT]);
2006-09-06 17:27:19 +00:00
a = (uint*) inbp->data;
x = a[bn - NDIRECT];
brelse(inbp);
2006-09-06 17:27:19 +00:00
if(x == 0)
panic("bmap 4");
}
return x;
}
2006-09-07 14:28:12 +00:00
// Truncate the inode ip, discarding all its data blocks.
2006-09-06 17:27:19 +00:00
void
2006-08-30 18:55:06 +00:00
itrunc(struct inode *ip)
{
int i, j;
struct buf *inbp;
2006-09-06 17:27:19 +00:00
for(i = 0; i < NADDRS; i++) {
if(ip->addrs[i] != 0) {
if(i == INDIRECT) {
inbp = bread(ip->dev, ip->addrs[INDIRECT]);
2006-09-06 17:27:19 +00:00
uint *a = (uint*) inbp->data;
for(j = 0; j < NINDIRECT; j++) {
if(a[j] != 0) {
bfree(ip->dev, a[j]);
a[j] = 0;
}
}
brelse(inbp);
2006-09-06 17:27:19 +00:00
}
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
ip->size = 0;
iupdate(ip);
}
2006-09-07 14:28:12 +00:00
// Caller holds reference to ip and has locked it,
// possibly editing it.
// Release lock and drop the reference.
void
iput(struct inode *ip)
{
2006-09-07 15:15:32 +00:00
if(ip->ref < 1 || ip->busy != 1)
2006-07-21 22:10:40 +00:00
panic("iput");
2006-09-07 15:15:32 +00:00
if((ip->ref == 1) && (ip->nlink == 0)) {
2006-08-30 18:55:06 +00:00
itrunc(ip);
ifree(ip);
}
acquire(&inode_table_lock);
2006-09-07 15:15:32 +00:00
ip->ref -= 1;
ip->busy = 0;
wakeup(ip);
release(&inode_table_lock);
}
2006-07-21 22:10:40 +00:00
2006-09-07 14:28:12 +00:00
// Caller holds reference to ip but not lock.
// Drop reference.
2006-07-21 22:10:40 +00:00
void
2006-07-29 09:35:02 +00:00
idecref(struct inode *ip)
2006-07-21 22:10:40 +00:00
{
ilock(ip);
iput(ip);
2006-07-21 22:10:40 +00:00
}
2006-09-07 14:28:12 +00:00
// Increment reference count for ip.
2006-08-15 15:53:46 +00:00
void
iincref(struct inode *ip)
{
ilock(ip);
2006-09-07 15:15:32 +00:00
ip->ref++;
2006-08-15 15:53:46 +00:00
iunlock(ip);
}
2006-09-07 14:28:12 +00:00
// Copy stat information from inode.
2006-08-12 04:33:50 +00:00
void
stati(struct inode *ip, struct stat *st)
{
2006-09-07 13:08:23 +00:00
st->dev = ip->dev;
st->ino = ip->inum;
st->type = ip->type;
st->nlink = ip->nlink;
st->size = ip->size;
2006-08-12 04:33:50 +00:00
}
#define min(a, b) ((a) < (b) ? (a) : (b))
2006-09-07 14:28:12 +00:00
// Read data from inode.
2006-07-27 21:10:00 +00:00
int
readi(struct inode *ip, char *dst, uint off, uint n)
2006-07-27 21:10:00 +00:00
{
uint target = n, n1;
struct buf *bp;
2006-09-06 17:27:19 +00:00
if(ip->type == T_DEV) {
2006-09-07 13:08:23 +00:00
if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read)
return -1;
2006-09-07 13:08:23 +00:00
return devsw[ip->major].read(ip->minor, dst, n);
}
2006-07-27 21:10:00 +00:00
while(n > 0 && off < ip->size){
2006-08-09 01:09:36 +00:00
bp = bread(ip->dev, bmap(ip, off / BSIZE));
2006-07-27 21:10:00 +00:00
n1 = min(n, ip->size - off);
2006-08-09 01:09:36 +00:00
n1 = min(n1, BSIZE - (off % BSIZE));
memmove(dst, bp->data + (off % BSIZE), n1);
2006-07-27 21:10:00 +00:00
n -= n1;
off += n1;
dst += n1;
brelse(bp);
}
return target - n;
}
2006-09-07 14:28:12 +00:00
// Allocate the nth block in inode ip if necessary.
static int
newblock(struct inode *ip, uint lbn)
{
struct buf *inbp;
uint *inaddrs;
uint b;
2006-09-06 17:27:19 +00:00
if(lbn < NDIRECT) {
if(ip->addrs[lbn] == 0) {
b = balloc(ip->dev);
2006-09-06 17:57:47 +00:00
if(b <= 0)
return -1;
ip->addrs[lbn] = b;
}
} else {
2006-09-06 17:27:19 +00:00
if(ip->addrs[INDIRECT] == 0) {
b = balloc(ip->dev);
2006-09-06 17:57:47 +00:00
if(b <= 0)
return -1;
ip->addrs[INDIRECT] = b;
}
inbp = bread(ip->dev, ip->addrs[INDIRECT]);
2006-09-06 17:27:19 +00:00
inaddrs = (uint*) inbp->data;
if(inaddrs[lbn - NDIRECT] == 0) {
b = balloc(ip->dev);
2006-09-06 17:57:47 +00:00
if(b <= 0)
return -1;
inaddrs[lbn - NDIRECT] = b;
bwrite(inbp, ip->addrs[INDIRECT]);
}
brelse(inbp);
}
return 0;
}
2006-09-07 14:28:12 +00:00
// Write data to inode.
int
writei(struct inode *ip, char *addr, uint off, uint n)
{
2006-09-06 17:27:19 +00:00
if(ip->type == T_DEV) {
2006-09-07 13:08:23 +00:00
if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write)
return -1;
2006-09-07 13:08:23 +00:00
return devsw[ip->major].write(ip->minor, addr, n);
2006-09-06 17:27:19 +00:00
} else if(ip->type == T_FILE || ip->type == T_DIR) {
struct buf *bp;
int r = 0;
int m;
int lbn;
2006-09-06 17:27:19 +00:00
while(r < n) {
lbn = off / BSIZE;
2006-09-06 17:57:47 +00:00
if(lbn >= MAXFILE)
return r;
2006-09-06 17:27:19 +00:00
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));
2006-09-06 17:27:19 +00:00
memmove(bp->data + off % BSIZE, addr, m);
bwrite(bp, bmap(ip, lbn));
brelse(bp);
r += m;
off += m;
}
2006-09-06 17:27:19 +00:00
if(r > 0) {
if(off > ip->size) {
2006-09-06 17:57:47 +00:00
if(ip->type == T_DIR)
ip->size = ((off / BSIZE) + 1) * BSIZE;
else
ip->size = off;
}
iupdate(ip);
}
return r;
} else {
2006-09-06 17:27:19 +00:00
panic("writei: unknown type");
return 0;
}
}
// 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.
2006-09-06 17:27:19 +00:00
struct inode*
2006-09-06 19:08:14 +00:00
namei(char *path, int mode, uint *ret_off,
char **ret_last, struct inode **ret_ip)
2006-07-21 22:10:40 +00:00
{
struct inode *dp;
struct proc *p = curproc[cpu()];
char *cp = path, *cp1;
2006-07-21 22:10:40 +00:00
uint off, dev;
struct buf *bp;
struct dirent *ep;
2006-09-07 15:34:28 +00:00
int i, l, atend;
uint ninum;
if(ret_off)
*ret_off = 0xffffffff;
if(ret_last)
*ret_last = 0;
if(ret_ip)
*ret_ip = 0;
2006-09-06 17:57:47 +00:00
if(*cp == '/')
dp = iget(rootdev, 1);
else {
dp = p->cwd;
iincref(dp);
ilock(dp);
}
2006-07-21 22:10:40 +00:00
2006-09-06 18:47:51 +00:00
for(;;){
2006-09-07 15:15:32 +00:00
while(*cp == '/')
cp++;
if(*cp == '\0'){
if(mode == NAMEI_LOOKUP)
return dp;
if(mode == NAMEI_CREATE && ret_ip){
*ret_ip = dp;
return 0;
}
iput(dp);
return 0;
}
2006-07-21 22:10:40 +00:00
if(dp->type != T_DIR){
iput(dp);
return 0;
}
2006-09-07 15:15:32 +00:00
for(i = 0; cp[i] != 0 && cp[i] != '/'; i++)
;
2006-09-07 15:34:28 +00:00
l = i;
if(i > DIRSIZ)
l = DIRSIZ;
2006-09-07 15:15:32 +00:00
2006-08-09 01:09:36 +00:00
for(off = 0; off < dp->size; off += BSIZE){
bp = bread(dp->dev, bmap(dp, off / BSIZE));
2006-09-06 17:27:19 +00:00
for(ep = (struct dirent*) bp->data;
ep < (struct dirent*) (bp->data + BSIZE);
2006-07-21 22:10:40 +00:00
ep++){
2006-09-06 17:27:19 +00:00
if(ep->inum == 0)
2006-07-21 22:10:40 +00:00
continue;
2006-09-07 15:34:28 +00:00
if(memcmp(cp, ep->name, l) == 0 &&
(l == DIRSIZ || ep->name[l]== 0)){
2006-09-07 15:15:32 +00:00
// entry matches path element
off += (uchar*)ep - bp->data;
2006-07-21 22:10:40 +00:00
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){
if(*cp == '\0'){
iput(dp);
return 0;
}
*ret_last = cp;
return dp;
}
2006-07-21 22:10:40 +00:00
iput(dp);
return 0;
found:
if(mode == NAMEI_DELETE && *cp == '\0'){
*ret_off = off;
return dp;
}
2006-07-21 22:10:40 +00:00
dev = dp->dev;
iput(dp);
dp = iget(dev, ninum);
if(dp->type == 0 || dp->nlink < 1)
panic("namei");
2006-07-21 22:10:40 +00:00
}
}
2006-08-08 18:07:37 +00:00
2006-09-07 14:28:12 +00:00
// Write a new directory entry (name, ino) into the directory dp.
// Caller must have locked dp.
2006-08-13 02:12:44 +00:00
void
wdir(struct inode *dp, char *name, uint ino)
{
uint off;
2006-08-13 20:06:42 +00:00
struct dirent de;
2006-08-13 02:12:44 +00:00
int i;
2006-08-13 20:06:42 +00:00
for(off = 0; off < dp->size; off += sizeof(de)){
2006-09-06 17:27:19 +00:00
if(readi(dp, (char*) &de, off, sizeof(de)) != sizeof(de))
2006-08-13 20:06:42 +00:00
panic("wdir read");
if(de.inum == 0)
break;
}
2006-08-13 20:06:42 +00:00
de.inum = ino;
2006-08-30 18:55:06 +00:00
for(i = 0; i < DIRSIZ && name[i]; i++)
2006-08-13 20:06:42 +00:00
de.name[i] = name[i];
2006-08-13 02:12:44 +00:00
for( ; i < DIRSIZ; i++)
2006-08-13 20:06:42 +00:00
de.name[i] = '\0';
2006-09-06 17:27:19 +00:00
if(writei(dp, (char*) &de, off, sizeof(de)) != sizeof(de))
2006-08-13 20:06:42 +00:00
panic("wdir write");
2006-08-13 02:12:44 +00:00
}
2006-09-07 14:28:12 +00:00
// Create the path cp and return its locked inode structure.
// If cp already exists, return 0.
2006-09-06 17:27:19 +00:00
struct inode*
mknod(char *cp, short type, short major, short minor)
2006-08-08 18:07:37 +00:00
{
struct inode *ip, *dp;
char *last;
2006-08-08 18:07:37 +00:00
2006-09-06 17:27:19 +00:00
if((dp = namei(cp, NAMEI_CREATE, 0, &last, 0)) == 0)
return 0;
ip = mknod1(dp, last, type, major, minor);
iput(dp);
return ip;
}
2006-09-07 14:28:12 +00:00
// Create a new inode named name inside dp
// and return its locked inode structure.
// If name already exists, return 0.
2006-09-06 17:27:19 +00:00
struct inode*
mknod1(struct inode *dp, char *name, short type, short major, short minor)
{
struct inode *ip;
2006-08-08 18:07:37 +00:00
ip = ialloc(dp->dev, type);
2006-09-06 17:27:19 +00:00
if(ip == 0)
return 0;
2006-08-08 18:07:37 +00:00
ip->major = major;
ip->minor = minor;
2006-08-09 01:19:48 +00:00
ip->size = 0;
ip->nlink = 1;
2006-08-09 01:19:48 +00:00
2006-09-06 17:27:19 +00:00
iupdate(ip); // write new inode to disk
wdir(dp, name, ip->inum);
return ip;
2006-08-08 18:07:37 +00:00
}
2006-09-07 14:28:12 +00:00
// Unlink the inode named cp.
int
unlink(char *cp)
{
struct inode *ip, *dp;
struct dirent de;
uint off, inum, dev;
2006-09-06 17:27:19 +00:00
dp = namei(cp, NAMEI_DELETE, &off, 0, 0);
if(dp == 0)
return -1;
dev = dp->dev;
if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de) || de.inum == 0)
panic("unlink no entry");
inum = de.inum;
memset(&de, 0, sizeof(de));
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
panic("unlink dir write");
2006-09-06 17:27:19 +00:00
iupdate(dp);
iput(dp);
ip = iget(dev, inum);
2006-08-24 19:21:19 +00:00
if(ip->nlink < 1)
panic("unlink nlink < 1");
ip->nlink--;
iupdate(ip);
iput(ip);
return 0;
}
2006-08-13 02:12:44 +00:00
2006-09-07 14:28:12 +00:00
// Create the path new as a link to the same inode as old.
2006-08-13 02:12:44 +00:00
int
link(char *name1, char *name2)
{
struct inode *ip, *dp;
char *last;
2006-08-13 02:12:44 +00:00
2006-09-06 17:27:19 +00:00
if((ip = namei(name1, NAMEI_LOOKUP, 0, 0, 0)) == 0)
return -1;
if(ip->type == T_DIR){
iput(ip);
2006-08-13 02:12:44 +00:00
return -1;
}
iunlock(ip);
2006-09-06 17:27:19 +00:00
if((dp = namei(name2, NAMEI_CREATE, 0, &last, 0)) == 0) {
idecref(ip);
return -1;
}
if(dp->dev != ip->dev){
idecref(ip);
iput(dp);
2006-08-13 02:12:44 +00:00
return -1;
}
2006-09-06 17:27:19 +00:00
ilock(ip);
2006-08-13 02:12:44 +00:00
ip->nlink += 1;
2006-09-06 17:27:19 +00:00
iupdate(ip);
2006-08-13 02:12:44 +00:00
wdir(dp, last, ip->inum);
2006-08-13 02:12:44 +00:00
iput(dp);
iput(ip);
return 0;
}