2.11BSD-UFS/ufs_bmap.c
/*
* Copyright (c) 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)ufs_bmap.c 1.2 (2.11BSD) 1996/9/19
*/
#include "param.h"
#include "../machine/seg.h"
#include "systm.h"
#include "conf.h"
#include "dir.h"
#include "inode.h"
#include "user.h"
#include "buf.h"
#include "fs.h"
#include "mount.h"
#include "uio.h"
/*
* Bmap defines the structure of file system storage
* by returning the physical block number on a device given the
* inode and the logical block number in a file.
* When convenient, it also leaves the physical
* block number of the next block of the file in rablock
* for use in read-ahead.
*/
daddr_t
bmap(ip, bn, rwflg, flags)
register struct inode *ip;
daddr_t bn;
int rwflg, flags;
{
register int i;
register struct buf *bp;
struct buf *nbp;
int j, sh;
daddr_t nb, *bap, ra;
int async = ip->i_fs->fs_flags & MNT_ASYNC;
if (bn < 0) {
u.u_error = EFBIG;
return((daddr_t)0);
}
ra = rablock = 0;
/*
* blocks 0..NADDR-4 are direct blocks
*/
if (bn < NADDR-3) {
i = bn;
nb = ip->i_addr[i];
if (nb == 0) {
if (rwflg == B_READ || (bp = balloc(ip, flags)) == NULL)
return((daddr_t)-1);
nb = dbtofsb(bp->b_blkno);
/*
* directory blocks are usually the only thing written synchronously at this
* point (so they never appear with garbage in them on the disk). This is
* overridden if the filesystem was mounted 'async'.
*/
if (flags & B_SYNC)
bwrite(bp);
else
bdwrite(bp);
ip->i_addr[i] = nb;
ip->i_flag |= IUPD|ICHG;
}
if (i < NADDR-4)
rablock = ip->i_addr[i+1];
return(nb);
}
/*
* addresses NADDR-3, NADDR-2, and NADDR-1
* have single, double, triple indirect blocks.
* the first step is to determine
* how many levels of indirection.
*/
sh = 0;
nb = 1;
bn -= NADDR-3;
for (j = 3;j > 0;j--) {
sh += NSHIFT;
nb <<= NSHIFT;
if (bn < nb)
break;
bn -= nb;
}
if (j == 0) {
u.u_error = EFBIG;
return((daddr_t)0);
}
/*
* fetch the first indirect block
*/
nb = ip->i_addr[NADDR-j];
if (nb == 0) {
if (rwflg == B_READ || (bp = balloc(ip, flags | B_CLRBUF)) == NULL)
return((daddr_t) -1);
nb = dbtofsb(bp->b_blkno);
/*
* Write synchronously if requested so that indirect blocks
* never point at garbage.
*/
if (async)
bdwrite(bp);
else
bwrite(bp);
ip->i_addr[NADDR-j] = nb;
ip->i_flag |= IUPD|ICHG;
}
/*
* fetch through the indirect blocks
*/
for(;j <= 3;j++) {
bp = bread(ip->i_dev, nb);
if ((bp->b_flags & B_ERROR) || bp->b_resid) {
brelse(bp);
return((daddr_t)0);
}
bap = (daddr_t *) mapin(bp);
sh -= NSHIFT;
i = (bn>>sh) & NMASK;
nb = bap[i];
/*
* calculate read-ahead
*/
if (i < NINDIR-1)
ra = bap[i+1];
mapout(bp);
if (nb == 0) {
if (rwflg == B_READ || (nbp = balloc(ip, flags | B_CLRBUF)) == NULL) {
brelse(bp);
return((daddr_t) -1);
}
nb = dbtofsb(nbp->b_blkno);
/*
* Write synchronously so indirect blocks never point at garbage and blocks
* in directories never contain garbage. This check used to be based on the
* type of inode, if it was a directory then 'sync' writes were done. See the
* comments earlier about filesystems being mounted 'async'.
*/
if (!async && (j < 3 || (flags & B_SYNC)))
bwrite(nbp);
else
bdwrite(nbp);
bap = (daddr_t *) mapin(bp);
bap[i] = nb;
mapout(bp);
bdwrite(bp);
} else
brelse(bp);
}
rablock = ra;
return(nb);
}