/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:ufs_bmap.c 12.0$ */
/* $ACIS:ufs_bmap.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/sys/RCS/ufs_bmap.c,v $ */

#ifndef VFS
#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:ufs_bmap.c 12.0$";
#endif

/*
 * Copyright (c) 1982, 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  7.1 (Berkeley) 6/5/86
 */

#include "param.h"
#include "systm.h"
#include "conf.h"
#include "inode.h"
#include "dir.h"
#include "user.h"
#include "buf.h"
#include "proc.h"
#include "fs.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.
 */
/*VARARGS3*/
daddr_t
bmap(ip, bn, rwflg, size)
	register struct inode *ip;
	daddr_t bn;
	int rwflg;
	int size;	/* supplied only when rwflg == B_WRITE */
{
	register int i;
	int osize, nsize;
	struct buf *bp, *nbp;
	struct fs *fs;
	int j, sh;
	daddr_t nb, lbn, *bap, pref, blkpref();

	if (bn < 0) {
		u.u_error = EFBIG;
		return ((daddr_t)0);
	}
	fs = ip->i_fs;
	rablock = 0;
	rasize = 0;		/* conservative */

#ifdef VICE
	/* KAZAR-RA */
	if (rwflg == B_READ)
#ifdef ibm032
	    while ((ip->i_flag & IFILLING) && ((bn+2) << fs->fs_bshift) >= ip->i_size) {
#else
	    while ((ip->i_flag & IFILLING) && ((bn+2) * fs->fs_bsize) >= ip->i_size) {
#endif
		ip->i_flag |= IFILLWAIT;
		IUNLOCK(ip);		/* hopefully not bogus */
		sleep(&ip->i_flag, PZERO);
		ILOCK(ip);
	    }
	if (ip->i_flag & IFILLERR) {
	    u.u_error = EIO;
	    return ((daddr_t)0);
	}
#endif

	/*
	 * If the next write will extend the file into a new block,
	 * and the file is currently composed of a fragment
	 * this fragment has to be extended to be a full block.
	 */
	nb = lblkno(fs, ip->i_size);
	if (rwflg == B_WRITE && nb < NDADDR && nb < bn) {
		osize = blksize(fs, ip, nb);
		if (osize < fs->fs_bsize && osize > 0) {
			bp = realloccg(ip, ip->i_db[nb],
				blkpref(ip, nb, (int)nb, &ip->i_db[0]),
				osize, (int)fs->fs_bsize);
			if (bp == NULL)
				return ((daddr_t)-1);
			ip->i_size = (nb + 1) * fs->fs_bsize;
			ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
			ip->i_flag |= IUPD|ICHG;
			bdwrite(bp);
		}
	}
	/*
	 * The first NDADDR blocks are direct blocks
	 */
	if (bn < NDADDR) {
		nb = ip->i_db[bn];
		if (rwflg == B_READ) {
			if (nb == 0)
				return ((daddr_t)-1);
			goto gotit;
		}
#ifdef ibm032
		if (nb == 0 || ip->i_size < ((bn + 1) << fs->fs_bshift)) {
#else
		if (nb == 0 || ip->i_size < (bn + 1) * fs->fs_bsize) {
#endif ibm032
			if (nb != 0) {
				/* consider need to reallocate a frag */
				osize = fragroundup(fs, blkoff(fs, ip->i_size));
				nsize = fragroundup(fs, size);
				if (nsize <= osize)
					goto gotit;
				bp = realloccg(ip, nb,
					blkpref(ip, bn, (int)bn, &ip->i_db[0]),
					osize, nsize);
			} else {
#ifdef ibm032
				if (ip->i_size < ((bn + 1) << fs->fs_bshift))
#else
				if (ip->i_size < (bn + 1) * fs->fs_bsize)
#endif ibm032
					nsize = fragroundup(fs, size);
				else
					nsize = fs->fs_bsize;
				bp = alloc(ip,
					blkpref(ip, bn, (int)bn, &ip->i_db[0]),
					nsize);
			}
			if (bp == NULL)
				return ((daddr_t)-1);
			nb = dbtofsb(fs, bp->b_blkno);
			if ((ip->i_mode&IFMT) == IFDIR)
				/*
				 * Write directory blocks synchronously
				 * so they never appear with garbage in
				 * them on the disk.
				 */
				bwrite(bp);
			else
				bdwrite(bp);
			ip->i_db[bn] = nb;
			ip->i_flag |= IUPD|ICHG;
		}
gotit:
		if (bn < NDADDR - 1) {
			rablock = fsbtodb(fs, ip->i_db[bn + 1]);
			rasize = blksize(fs, ip, bn + 1);
		}
		return (nb);
	}

	/*
	 * Determine how many levels of indirection.
	 */
	pref = 0;
	sh = 1;
	lbn = bn;
	bn -= NDADDR;
	for (j = NIADDR; j>0; j--) {
#ifdef ibm032
		switch(NINDIR(fs)) {	/* optimized to avoid / and % */
		case 2048:	/* MAXBSIZE/(sizeof daddr_t) */
			sh <<= 11;	/* multiply by 2048 */
			break;
		case 1024:	/* MINBSIZE/(sizeof daddr_t) */
			sh <<= 10;
			break;
		default:
			sh *= NINDIR(fs);
			break;
		}
#else
		sh *= NINDIR(fs);
#endif ibm032
		if (bn < sh)
			break;
		bn -= sh;
	}
	if (j == 0) {
		u.u_error = EFBIG;
		return ((daddr_t)0);
	}

	/*
	 * fetch the first indirect block
	 */
	nb = ip->i_ib[NIADDR - j];
	if (nb == 0) {
		if (rwflg == B_READ)
			return ((daddr_t)-1);
		pref = blkpref(ip, lbn, 0, (daddr_t *)0);
	        bp = alloc(ip, pref, (int)fs->fs_bsize);
		if (bp == NULL)
			return ((daddr_t)-1);
		nb = dbtofsb(fs, bp->b_blkno);
		/*
		 * Write synchronously so that indirect blocks
		 * never point at garbage.
		 */
		bwrite(bp);
		ip->i_ib[NIADDR - j] = nb;
		ip->i_flag |= IUPD|ICHG;
	}

	/*
	 * fetch through the indirect blocks
	 */
	for (; j <= NIADDR; j++) {
		bp = bread(ip->i_dev, fsbtodb(fs, nb), (int)fs->fs_bsize);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			return ((daddr_t)0);
		}
		bap = bp->b_un.b_daddr;
#ifdef ibm032
		switch(NINDIR(fs)) {	/* optimized to avoid / and % */
		case 2048:	/* MAXBSIZE/(sizeof daddr_t) */
			sh >>= 11;	/* divide by 2048 */
			i = (bn / sh) & (2048-1);	/* remainder 2048 */
			break;
		case 1024:	/* MINBSIZE/(sizeof daddr_t) */
			sh >>= 10;
			i = (bn / sh) & (1024-1);
			break;
		default:
			sh /= NINDIR(fs);
			i = (bn / sh) % NINDIR(fs);
		}
#else
		sh /= NINDIR(fs);
		i = (bn / sh) % NINDIR(fs);
#endif
		nb = bap[i];
		if (nb == 0) {
			if (rwflg==B_READ) {
				brelse(bp);
				return ((daddr_t)-1);
			}
			if (pref == 0)
				if (j < NIADDR)
					pref = blkpref(ip, lbn, 0,
						(daddr_t *)0);
				else
					pref = blkpref(ip, lbn, i, &bap[0]);
		        nbp = alloc(ip, pref, (int)fs->fs_bsize);
			if (nbp == NULL) {
				brelse(bp);
				return ((daddr_t)-1);
			}
			nb = dbtofsb(fs, nbp->b_blkno);
			if (j < NIADDR || (ip->i_mode&IFMT) == IFDIR)
				/*
				 * Write synchronously so indirect blocks
				 * never point at garbage and blocks
				 * in directories never contain garbage.
				 */
				bwrite(nbp);
			else
				bdwrite(nbp);
			bap[i] = nb;
			bdwrite(bp);
		} else
			brelse(bp);
	}

	/*
	 * calculate read-ahead.
	 */
	if (i < NINDIR(fs) - 1) {
		rablock = fsbtodb(fs, bap[i+1]);
		rasize = fs->fs_bsize;
	}
	return (nb);
}
#endif !VFS
