#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/buf.h>
#include <sys/disp.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_quota.h>
#include <sys/fs/ufs_trans.h>
#include <sys/fs/ufs_bio.h>
#include <vm/seg.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
enum ufs_owner_type {
ufs_no_owner,
ufs_inode_direct,
ufs_inode_indirect,
ufs_indirect_block
};
struct ufs_allocated_block {
daddr_t this_block;
off_t block_size;
enum ufs_owner_type owner;
daddr_t owner_block;
uint_t owner_offset;
int usage_flags;
};
static int findextent(struct fs *fs, daddr32_t *sbp, int n, int *lenp,
int maxtrans);
static void ufs_undo_allocation(inode_t *ip, int block_count,
struct ufs_allocated_block table[], int inode_sector_adjust);
#define DOEXTENT(fs, lbn, boff, bnp, lenp, size, tblp, n, chkfrag, maxtrans) {\
register daddr32_t *dp = (tblp); \
register int _chkfrag = chkfrag; \
\
if (*dp == 0) { \
*(bnp) = UFS_HOLE; \
} else { \
register int len; \
\
len = findextent(fs, dp, (int)(n), lenp, maxtrans) << \
(fs)->fs_bshift; \
if (_chkfrag) { \
register u_offset_t tmp; \
\
tmp = fragroundup((fs), size) - \
(((u_offset_t)lbn) << fs->fs_bshift); \
len = (int)MIN(tmp, len); \
} \
len -= (boff); \
if (len <= 0) { \
*(bnp) = UFS_HOLE; \
} else { \
*(bnp) = fsbtodb(fs, *dp) + btodb(boff); \
*(lenp) = len; \
} \
} \
}
#define VERYLARGEFILESIZE 0x7FE00000
int
bmap_read(struct inode *ip, u_offset_t off, daddr_t *bnp, int *lenp)
{
daddr_t lbn;
ufsvfs_t *ufsvfsp = ip->i_ufsvfs;
struct fs *fs = ufsvfsp->vfs_fs;
struct buf *bp;
int i, j, boff;
int shft;
daddr_t ob, nb, tbn;
daddr32_t *bap;
int nindirshift, nindiroffset;
ASSERT(RW_LOCK_HELD(&ip->i_contents));
lbn = (daddr_t)lblkno(fs, off);
boff = (int)blkoff(fs, off);
if (lbn < 0)
return (EFBIG);
if (lbn < NDADDR) {
DOEXTENT(fs, lbn, boff, bnp, lenp,
ip->i_size, &ip->i_db[lbn], NDADDR - lbn, 1,
ufsvfsp->vfs_iotransz);
return (0);
}
nindirshift = ufsvfsp->vfs_nindirshift;
nindiroffset = ufsvfsp->vfs_nindiroffset;
shft = 0;
tbn = lbn - NDADDR;
for (j = NIADDR; j > 0; j--) {
longlong_t sh;
shft += nindirshift;
sh = 1LL << shft;
if (tbn < sh)
break;
tbn -= sh;
}
if (j == 0)
return (EFBIG);
nb = ip->i_ib[NIADDR - j];
if (nb == 0) {
*bnp = UFS_HOLE;
return (0);
}
for (; j <= NIADDR; j++) {
ob = nb;
bp = UFS_BREAD(ufsvfsp,
ip->i_dev, fsbtodb(fs, ob), fs->fs_bsize);
if (bp->b_flags & B_ERROR) {
brelse(bp);
return (EIO);
}
bap = bp->b_un.b_daddr;
ASSERT(!ufs_indir_badblock(ip, bap));
shft -= nindirshift;
i = (tbn >> shft) & nindiroffset;
nb = bap[i];
if (nb == 0) {
*bnp = UFS_HOLE;
brelse(bp);
return (0);
}
if (j != NIADDR)
brelse(bp);
}
DOEXTENT(fs, lbn, boff, bnp, lenp, ip->i_size, &bap[i],
MIN(NINDIR(fs) - i, (daddr_t)lblkno(fs, ip->i_size - 1) - lbn + 1),
0, ufsvfsp->vfs_iotransz);
brelse(bp);
return (0);
}
int
bmap_write(struct inode *ip, u_offset_t off, int size,
enum bi_type alloc_type, daddr_t *allocblk, struct cred *cr)
{
struct fs *fs;
struct buf *bp;
int i;
struct buf *nbp;
int j;
int shft;
daddr_t ob, nb, pref, lbn, llbn, tbn;
daddr32_t *bap;
struct vnode *vp = ITOV(ip);
long bsize = VBSIZE(vp);
long osize, nsize;
int issync, metaflag, isdirquota;
int err;
dev_t dev;
struct fbuf *fbp;
int nindirshift;
int nindiroffset;
struct ufsvfs *ufsvfsp;
int added_sectors;
int alloced_blocks;
struct ufs_allocated_block undo_table[NIADDR+1];
int verylargefile = 0;
ASSERT(RW_WRITE_HELD(&ip->i_contents));
if (allocblk)
*allocblk = 0;
ufsvfsp = ip->i_ufsvfs;
fs = ufsvfsp->vfs_bufp->b_un.b_fs;
lbn = (daddr_t)lblkno(fs, off);
if (lbn < 0)
return (EFBIG);
if (ip->i_blocks >= VERYLARGEFILESIZE)
verylargefile = 1;
llbn = (daddr_t)((ip->i_size) ? lblkno(fs, ip->i_size - 1) : 0);
metaflag = isdirquota = 0;
if (((ip->i_mode & IFMT) == IFDIR) ||
((ip->i_mode & IFMT) == IFATTRDIR))
isdirquota = metaflag = I_DIR;
else if ((ip->i_mode & IFMT) == IFSHAD)
metaflag = I_SHAD;
else if (ip->i_ufsvfs->vfs_qinod == ip)
isdirquota = metaflag = I_QUOTA;
issync = ((ip->i_flag & ISYNC) != 0);
if (isdirquota || issync) {
alloc_type = BI_NORMAL;
}
if (llbn < NDADDR && llbn < lbn && (ob = ip->i_db[llbn]) != 0) {
osize = blksize(fs, ip, llbn);
if (osize < bsize && osize > 0) {
if (verylargefile == 1) {
if (((unsigned)ip->i_blocks +
btodb(bsize - osize)) > INT_MAX) {
return (EFBIG);
}
}
err =
fbread(ITOV(ip), ((offset_t)llbn << fs->fs_bshift),
(uint_t)bsize, S_OTHER, &fbp);
if (err)
return (err);
pref = blkpref(ip, llbn, (int)llbn, &ip->i_db[0]);
err = realloccg(ip, ob, pref, (int)osize, (int)bsize,
&nb, cr);
if (err) {
if (fbp)
fbrelse(fbp, S_OTHER);
return (err);
}
ASSERT(!ufs_badblock(ip, nb));
TRANS_MATA_ALLOC(ufsvfsp, ip, nb, bsize, 0);
ip->i_db[llbn] = nb;
UFS_SET_ISIZE(((u_offset_t)(llbn + 1)) << fs->fs_bshift,
ip);
ip->i_blocks += btodb(bsize - osize);
ASSERT((unsigned)ip->i_blocks <= INT_MAX);
TRANS_INODE(ufsvfsp, ip);
ip->i_flag |= IUPD | ICHG | IATTCHG;
if (issync) {
(void) ufs_fbiwrite(fbp, ip, nb, fs->fs_fsize);
} else {
ASSERT(fbp);
fbrelse(fbp, S_WRITE);
}
if (nb != ob) {
(void) free(ip, ob, (off_t)osize, metaflag);
}
}
}
if (lbn < NDADDR) {
nb = ip->i_db[lbn];
if (nb == 0 ||
ip->i_size < ((u_offset_t)(lbn + 1)) << fs->fs_bshift) {
if (nb != 0) {
osize = fragroundup(fs, blkoff(fs, ip->i_size));
nsize = fragroundup(fs, size);
if (nsize <= osize)
goto gotit;
if (verylargefile == 1) {
if (((unsigned)ip->i_blocks +
btodb(nsize - osize)) > INT_MAX) {
return (EFBIG);
}
}
ob = nb;
pref = blkpref(ip, lbn, (int)lbn,
&ip->i_db[0]);
err = realloccg(ip, ob, pref, (int)osize,
(int)nsize, &nb, cr);
if (err)
return (err);
if (allocblk)
*allocblk = nb;
ASSERT(!ufs_badblock(ip, nb));
} else {
osize = 0;
if (ip->i_size <
((u_offset_t)(lbn + 1)) << fs->fs_bshift)
nsize = fragroundup(fs, size);
else
nsize = bsize;
if (verylargefile == 1) {
if (((unsigned)ip->i_blocks +
btodb(nsize - osize)) > INT_MAX) {
return (EFBIG);
}
}
pref = blkpref(ip, lbn, (int)lbn, &ip->i_db[0]);
err = alloc(ip, pref, (int)nsize, &nb, cr);
if (err)
return (err);
if (allocblk)
*allocblk = nb;
ASSERT(!ufs_badblock(ip, nb));
ob = nb;
}
fbp = NULL;
if (osize == 0) {
if (alloc_type == BI_NORMAL ||
alloc_type == BI_FALLOCATE ||
P2ROUNDUP_TYPED(size,
PAGESIZE, u_offset_t) < nsize) {
fbzero(ITOV(ip),
((offset_t)lbn << fs->fs_bshift),
(uint_t)nsize, &fbp);
}
} else {
err = fbread(vp,
((offset_t)lbn << fs->fs_bshift),
(uint_t)nsize, S_OTHER, &fbp);
if (err) {
if (nb != ob) {
(void) free(ip, nb,
(off_t)nsize, metaflag);
} else {
(void) free(ip,
ob + numfrags(fs, osize),
(off_t)(nsize - osize),
metaflag);
}
ASSERT(nsize >= osize);
(void) chkdq(ip,
-(long)btodb(nsize - osize),
0, cr, (char **)NULL,
(size_t *)NULL);
return (err);
}
}
TRANS_MATA_ALLOC(ufsvfsp, ip, nb, nsize, 0);
ip->i_db[lbn] = nb;
ip->i_blocks += btodb(nsize - osize);
ASSERT((unsigned)ip->i_blocks <= INT_MAX);
TRANS_INODE(ufsvfsp, ip);
ip->i_flag |= IUPD | ICHG | IATTCHG;
if (isdirquota && (ip->i_size ||
TRANS_ISTRANS(ufsvfsp))) {
(void) ufs_fbiwrite(fbp, ip, nb, fs->fs_fsize);
} else if (fbp) {
fbrelse(fbp, S_WRITE);
}
if (nb != ob)
(void) free(ip, ob, (off_t)osize, metaflag);
}
gotit:
return (0);
}
added_sectors = alloced_blocks = 0;
nindirshift = ip->i_ufsvfs->vfs_nindirshift;
nindiroffset = ip->i_ufsvfs->vfs_nindiroffset;
pref = 0;
shft = 0;
tbn = lbn - NDADDR;
for (j = NIADDR; j > 0; j--) {
longlong_t sh;
shft += nindirshift;
sh = 1LL << shft;
if (tbn < sh)
break;
tbn -= sh;
}
if (j == 0)
return (EFBIG);
dev = ip->i_dev;
nb = ip->i_ib[NIADDR - j];
if (nb == 0) {
if (verylargefile == 1) {
if (((unsigned)ip->i_blocks + btodb(bsize))
> INT_MAX) {
return (EFBIG);
}
}
pref = blkpref(ip, lbn, 0, (daddr32_t *)0);
err = alloc(ip, pref, (int)bsize, &nb, cr);
if (err)
return (err);
TRANS_MATA_ALLOC(ufsvfsp, ip, nb, bsize, 1);
ASSERT(!ufs_badblock(ip, nb));
ASSERT(alloced_blocks <= NIADDR);
undo_table[alloced_blocks].this_block = nb;
undo_table[alloced_blocks].block_size = bsize;
undo_table[alloced_blocks].owner = ufs_no_owner;
undo_table[alloced_blocks].usage_flags = metaflag | I_IBLK;
alloced_blocks++;
bp = UFS_GETBLK(ufsvfsp, dev, fsbtodb(fs, nb), bsize);
clrbuf(bp);
TRANS_BUF(ufsvfsp, 0, bsize, bp, DT_ABZERO);
UFS_BWRITE2(ufsvfsp, bp);
if (bp->b_flags & B_ERROR) {
err = geterror(bp);
brelse(bp);
ufs_undo_allocation(ip, alloced_blocks,
undo_table, added_sectors);
return (err);
}
brelse(bp);
ip->i_ib[NIADDR - j] = nb;
added_sectors += btodb(bsize);
ip->i_blocks += btodb(bsize);
ASSERT((unsigned)ip->i_blocks <= INT_MAX);
TRANS_INODE(ufsvfsp, ip);
ip->i_flag |= IUPD | ICHG | IATTCHG;
undo_table[alloced_blocks-1].owner = ufs_inode_indirect;
undo_table[alloced_blocks-1].owner_offset = NIADDR - j;
}
for (; j <= NIADDR; j++) {
ob = nb;
bp = UFS_BREAD(ufsvfsp, ip->i_dev, fsbtodb(fs, ob), bsize);
if (bp->b_flags & B_ERROR) {
err = geterror(bp);
brelse(bp);
ufs_undo_allocation(ip, alloced_blocks,
undo_table, added_sectors);
return (err);
}
bap = bp->b_un.b_daddr;
shft -= nindirshift;
i = (tbn >> shft) & nindiroffset;
nb = bap[i];
if (nb == 0) {
if (verylargefile == 1) {
if (((unsigned)ip->i_blocks + btodb(bsize))
> INT_MAX) {
brelse(bp);
ufs_undo_allocation(ip, alloced_blocks,
undo_table, added_sectors);
return (EFBIG);
}
}
if (pref == 0) {
if (j < NIADDR) {
pref = blkpref(ip, lbn, 0,
(daddr32_t *)0);
} else {
pref = blkpref(ip, lbn, i, &bap[0]);
}
}
brelse(bp);
err = alloc(ip, pref, (int)bsize, &nb, cr);
if (err) {
ufs_undo_allocation(ip, alloced_blocks,
undo_table, added_sectors);
return (err);
}
ASSERT(!ufs_badblock(ip, nb));
ASSERT(alloced_blocks <= NIADDR);
if (allocblk)
*allocblk = nb;
undo_table[alloced_blocks].this_block = nb;
undo_table[alloced_blocks].block_size = bsize;
undo_table[alloced_blocks].owner = ufs_no_owner;
undo_table[alloced_blocks].usage_flags = metaflag |
((j < NIADDR) ? I_IBLK : 0);
alloced_blocks++;
if (j < NIADDR) {
TRANS_MATA_ALLOC(ufsvfsp, ip, nb, bsize, 1);
nbp = UFS_GETBLK(
ufsvfsp, dev, fsbtodb(fs, nb), bsize);
clrbuf(nbp);
TRANS_BUF(ufsvfsp, 0, bsize, nbp, DT_ABZERO);
UFS_BWRITE2(ufsvfsp, nbp);
if (nbp->b_flags & B_ERROR) {
err = geterror(nbp);
brelse(nbp);
ufs_undo_allocation(ip,
alloced_blocks,
undo_table, added_sectors);
return (err);
}
brelse(nbp);
} else if (alloc_type == BI_NORMAL ||
P2ROUNDUP_TYPED(size,
PAGESIZE, u_offset_t) < bsize) {
TRANS_MATA_ALLOC(ufsvfsp, ip, nb, bsize, 0);
fbzero(ITOV(ip),
((offset_t)lbn << fs->fs_bshift),
(uint_t)bsize, &fbp);
if (isdirquota || (issync &&
lbn < llbn))
(void) ufs_fbiwrite(fbp, ip, nb,
fs->fs_fsize);
else
fbrelse(fbp, S_WRITE);
}
bp = UFS_BREAD(ufsvfsp,
ip->i_dev, fsbtodb(fs, ob), bsize);
if (bp->b_flags & B_ERROR) {
err = geterror(bp);
brelse(bp);
ufs_undo_allocation(ip,
alloced_blocks,
undo_table, added_sectors);
return (err);
}
bap = bp->b_un.b_daddr;
bap[i] = nb;
if (alloc_type == BI_FALLOCATE && j == NIADDR)
bap[i] = -bap[i];
TRANS_BUF_ITEM_128(ufsvfsp, bap[i], bap, bp, DT_AB);
added_sectors += btodb(bsize);
ip->i_blocks += btodb(bsize);
ASSERT((unsigned)ip->i_blocks <= INT_MAX);
TRANS_INODE(ufsvfsp, ip);
ip->i_flag |= IUPD | ICHG | IATTCHG;
undo_table[alloced_blocks-1].owner =
ufs_indirect_block;
undo_table[alloced_blocks-1].owner_block = ob;
undo_table[alloced_blocks-1].owner_offset = i;
if (issync) {
UFS_BWRITE2(ufsvfsp, bp);
if (bp->b_flags & B_ERROR) {
err = geterror(bp);
brelse(bp);
ufs_undo_allocation(ip,
alloced_blocks,
undo_table, added_sectors);
return (err);
}
brelse(bp);
} else {
bdrwrite(bp);
}
} else {
brelse(bp);
}
}
return (0);
}
int
bmap_has_holes(struct inode *ip)
{
struct fs *fs = ip->i_fs;
uint_t dblks;
uint_t mblks;
int nindirshift;
int nindiroffset;
uint_t cnt;
int n, j, shft;
uint_t nindirblks;
int fsbshift = fs->fs_bshift;
int fsboffset = (1 << fsbshift) - 1;
if (ip->i_writer != NULL && ip->i_writer != curthread) {
return (1);
}
dblks = (ip->i_size + fsboffset) >> fsbshift;
mblks = (ldbtob((u_offset_t)ip->i_blocks) + fsboffset) >> fsbshift;
if (dblks <= NDADDR)
return (mblks < dblks);
nindirshift = ip->i_ufsvfs->vfs_nindirshift;
nindiroffset = ip->i_ufsvfs->vfs_nindiroffset;
nindirblks = nindiroffset + 1;
dblks -= NDADDR;
shft = 0;
for (j = NIADDR; j > 0; j--) {
longlong_t sh;
shft += nindirshift;
sh = 1LL << shft;
if (dblks <= sh)
break;
dblks -= sh;
}
ASSERT(NIADDR <= 3);
ASSERT(j <= NIADDR);
if (j == NIADDR)
cnt = NDADDR + 1 + dblks;
else if (j == NIADDR-1)
cnt = NDADDR + 1 + nindirblks +
1 + (dblks + nindiroffset)/nindirblks + dblks;
else if (j == NIADDR-2) {
n = (dblks + nindiroffset)/nindirblks;
cnt = NDADDR + 1 + nindirblks +
1 + nindirblks + nindirblks*nindirblks +
1 + (n + nindiroffset)/nindirblks + n + dblks;
}
return (mblks < cnt);
}
static int
findextent(struct fs *fs, daddr32_t *sbp, int n, int *lenp, int maxtransfer)
{
register daddr_t bn, nextbn;
register daddr32_t *bp;
register int diff;
int maxtransblk;
if (n <= 0)
return (0);
bn = *sbp;
if (bn == 0)
return (0);
diff = fs->fs_frag;
if (*lenp) {
n = MIN(n, lblkno(fs, *lenp));
} else {
maxtransblk = maxtransfer >> DEV_BSHIFT;
if (fs->fs_maxcontig < maxtransblk) {
n = MIN(n, fs->fs_maxcontig);
} else {
n = MIN(n, maxtransblk);
}
}
bp = sbp;
while (--n > 0) {
nextbn = *(bp + 1);
if (nextbn == 0 || bn + diff != nextbn)
break;
bn = nextbn;
bp++;
}
return ((int)(bp - sbp) + 1);
}
static void
ufs_undo_allocation(
inode_t *ip,
int block_count,
struct ufs_allocated_block table[],
int inode_sector_adjust)
{
int i;
int inode_changed;
int error_updating_pointers;
struct ufsvfs *ufsvfsp;
inode_changed = 0;
error_updating_pointers = 0;
ufsvfsp = ip->i_ufsvfs;
for (i = 0; i < block_count; i++) {
switch (table[i].owner) {
case ufs_no_owner:
break;
case ufs_inode_direct:
ASSERT(table[i].owner_offset < NDADDR);
ip->i_db[table[i].owner_offset] = 0;
inode_changed = 1;
break;
case ufs_inode_indirect:
ASSERT(table[i].owner_offset < NIADDR);
ip->i_ib[table[i].owner_offset] = 0;
inode_changed = 1;
break;
case ufs_indirect_block: {
buf_t *bp;
daddr32_t *block_data;
ASSERT(table[i].owner_offset <
(VBSIZE(ITOV(ip)) / sizeof (daddr32_t)));
bp = UFS_BREAD(ufsvfsp, ip->i_dev,
fsbtodb(ufsvfsp->vfs_fs, table[i].owner_block),
VBSIZE(ITOV(ip)));
if (bp->b_flags & B_ERROR) {
error_updating_pointers = 1;
brelse(bp);
break;
}
block_data = bp->b_un.b_daddr;
block_data[table[i].owner_offset] = 0;
TRANS_BUF_ITEM_128(ufsvfsp,
block_data[table[i].owner_offset],
block_data, bp, DT_AB);
UFS_BWRITE2(ufsvfsp, bp);
if (bp->b_flags & B_ERROR) {
error_updating_pointers = 1;
}
brelse(bp);
break;
}
default:
(void) ufs_fault(ITOV(ip),
"ufs_undo_allocation failure\n");
break;
}
}
if (inode_changed || (inode_sector_adjust != 0)) {
ip->i_blocks -= inode_sector_adjust;
ASSERT((unsigned)ip->i_blocks <= INT_MAX);
TRANS_INODE(ufsvfsp, ip);
ip->i_flag |= IUPD | ICHG | IATTCHG;
ip->i_seq++;
if (!TRANS_ISTRANS(ufsvfsp))
ufs_iupdat(ip, I_SYNC);
}
if (!error_updating_pointers) {
for (i = 0; i < block_count; i++) {
free(ip, table[i].this_block, table[i].block_size,
table[i].usage_flags);
}
}
}
int
bmap_find(struct inode *ip, boolean_t hole, u_offset_t *off)
{
ufsvfs_t *ufsvfsp = ip->i_ufsvfs;
struct fs *fs = ufsvfsp->vfs_fs;
buf_t *bp[NIADDR];
int i, j;
int shft;
int nindirshift, nindiroffset;
daddr_t ob, nb, tbn, lbn, skip;
daddr32_t *bap;
u_offset_t isz = (offset_t)ip->i_size;
int32_t bs = fs->fs_bsize;
int32_t nindir = fs->fs_nindir;
dev_t dev;
int error = 0;
daddr_t limits[NIADDR];
ASSERT(*off < isz);
ASSERT(RW_LOCK_HELD(&ip->i_contents));
lbn = (daddr_t)lblkno(fs, *off);
ASSERT(lbn >= 0);
for (i = 0; i < NIADDR; i++)
bp[i] = NULL;
if (lbn < NDADDR) {
for (; lbn < NDADDR; lbn++) {
if ((hole && (ip->i_db[lbn] == 0)) ||
(!hole && (ip->i_db[lbn] != 0))) {
goto out;
}
}
if ((u_offset_t)lbn << fs->fs_bshift >= isz)
goto out;
}
nindir = fs->fs_nindir;
nindirshift = ufsvfsp->vfs_nindirshift;
nindiroffset = ufsvfsp->vfs_nindiroffset;
dev = ip->i_dev;
for (limits[0] = NDADDR, j = 1; j < NIADDR; j++)
limits[j] = limits[j-1] + (1ULL << (nindirshift * j));
loop:
shft = 0;
tbn = lbn - NDADDR;
for (j = NIADDR; j > 0; j--) {
longlong_t sh;
shft += nindirshift;
sh = 1LL << shft;
if (tbn < sh)
break;
tbn -= sh;
}
if (j == 0) {
ASSERT(((u_offset_t)lbn << fs->fs_bshift) >= isz);
goto out;
}
nb = ip->i_ib[NIADDR - j];
if (nb == 0) {
if (hole) {
lbn = limits[NIADDR - j];
goto out;
} else {
lbn = limits[NIADDR - j + 1];
if ((u_offset_t)lbn << fs->fs_bshift >= isz)
goto out;
goto loop;
}
}
for (; ((j <= NIADDR) && (nb != 0)); j++) {
ob = nb;
if ((bp[j-1] == NULL) || bp[j-1]->b_blkno != fsbtodb(fs, ob)) {
if (bp[j-1] != NULL)
brelse(bp[j-1]);
bp[j-1] = UFS_BREAD(ufsvfsp, dev, fsbtodb(fs, ob), bs);
if (bp[j-1]->b_flags & B_ERROR) {
error = EIO;
goto out;
}
}
bap = bp[j-1]->b_un.b_daddr;
shft -= nindirshift;
i = (tbn >> shft) & nindiroffset;
nb = bap[i];
skip = 1LL << (nindirshift * (NIADDR - j));
}
for (; i < nindir; i++, lbn += skip) {
if (hole && (bap[i] == 0))
goto out;
if (!hole && (bap[i] != 0)) {
if (skip == 1) {
goto out;
} else {
goto loop;
}
}
}
if (((u_offset_t)lbn << fs->fs_bshift) < isz)
goto loop;
out:
for (i = 0; i < NIADDR; i++) {
if (bp[i])
brelse(bp[i]);
}
if (error == 0) {
if (((u_offset_t)lbn << fs->fs_bshift) >= isz) {
error = ENXIO;
} else {
*off = (u_offset_t)lbn << fs->fs_bshift;
}
}
return (error);
}
int
bmap_set_bn(struct vnode *vp, u_offset_t off, daddr32_t bn)
{
daddr_t lbn;
struct inode *ip;
ufsvfs_t *ufsvfsp;
struct fs *fs;
struct buf *bp;
int i, j;
int shft;
int err;
daddr_t ob, nb, tbn;
daddr32_t *bap;
int nindirshift, nindiroffset;
ip = VTOI(vp);
ufsvfsp = ip->i_ufsvfs;
fs = ufsvfsp->vfs_fs;
lbn = (daddr_t)lblkno(fs, off);
ASSERT(RW_LOCK_HELD(&ip->i_contents));
if (lbn < 0)
return (EFBIG);
if (lbn < NDADDR) {
ip->i_db[lbn] = bn;
return (0);
}
nindirshift = ip->i_ufsvfs->vfs_nindirshift;
nindiroffset = ip->i_ufsvfs->vfs_nindiroffset;
shft = 0;
tbn = lbn - NDADDR;
for (j = NIADDR; j > 0; j--) {
longlong_t sh;
shft += nindirshift;
sh = 1LL << shft;
if (tbn < sh)
break;
tbn -= sh;
}
if (j == 0)
return (EFBIG);
nb = ip->i_ib[NIADDR - j];
if (nb == 0) {
err = ufs_fault(ITOV(ip), "ufs_set_bn: nb == UFS_HOLE");
return (err);
}
for (; j <= NIADDR; j++) {
ob = nb;
bp = UFS_BREAD(ufsvfsp,
ip->i_dev, fsbtodb(fs, ob), fs->fs_bsize);
if (bp->b_flags & B_ERROR) {
err = geterror(bp);
brelse(bp);
return (err);
}
bap = bp->b_un.b_daddr;
ASSERT(!ufs_indir_badblock(ip, bap));
shft -= nindirshift;
i = (tbn >> shft) & nindiroffset;
nb = bap[i];
if (nb == 0) {
err = ufs_fault(ITOV(ip), "ufs_set_bn: nb == UFS_HOLE");
return (err);
}
if (j == NIADDR) {
bap[i] = bn;
bdrwrite(bp);
return (0);
}
brelse(bp);
}
return (0);
}