/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */
/*
 * NAME
 *	nblocks - calculate number of blocks and indirect blocks
 *
 * SYNOPSIS
 *	long nblocks(size, bsize, fsize)
 *	long size, bsize, fsize;
 *
 * DESCRIPTION
 *	Nblocks returns the total number of DEV_BSIZE blocks (typically
 *	512 bytes) a file of 'size' bytes would use on a file system.
 *	The total includes single indirect, double indirect and tripple
 *	indirect blocks.  The parameters 'bsize' and 'fsize' are the
 *	block size and frag size of the file system.
 *
 *	Partial blocks are counted in 'frag' size chunks (the
 *	smallest unit of allocation).
 *
 *	If bsize and fsize are equal, the indirect calculation
 *	is skipped.  NFS will always call nblocks in this
 *	manner.
 *
 * BUGS
 *	Sparce files may return an incorrect number of blocks.
 *
 *	Indirect block calculation will be skipped for local
 *	filesystems with bsize==fsize.
 */
#include <stdio.h>
#include <sys/types.h>		/* daddr_t */
#include <sys/param.h>		/* DEV_BSIZE */
#include <sys/ufsinode.h>	/* NDADDR */
#define SNGLIND	(bsize/sizeof(daddr_t))
#define DBLIND	(SNGLIND*SNGLIND)

unsigned long
nblocks(size, bsize, fsize)
register long size, bsize, fsize;
{
	register unsigned long blks, remainder, frags = 0;
	register unsigned long tot;

	/*
	 * Partial blocks are allocated in frag size
	 * chunks (the smallest unit of allocation).
	 *
	 * Don't calculate indirect blocks if the bsize
	 * (block size) and fsize (frag size) are the
	 * same.  NFS will always call with bsize==fsize.
	 */
	if (bsize == fsize || size <= (NDADDR * bsize)) {
		blks = size / bsize;
		if (remainder = size % bsize) {
			frags = (remainder + fsize - 1) / fsize;
		}
		blks *= bsize / DEV_BSIZE;
		blks += frags * (fsize / DEV_BSIZE);
		return(blks);
	}

	/*
	 * Calculate indirect block usage
	 */
	blks = (size + bsize - 1) / bsize;
	tot = blks;
	if (blks > NDADDR) {
		tot += (blks-NDADDR-1)/SNGLIND + 1;
		if (blks > NDADDR+SNGLIND)
			tot += (blks-NDADDR-SNGLIND-1)/DBLIND + 1;
		if (blks > NDADDR+SNGLIND+DBLIND)
			tot ++;
	}
	blks = tot * bsize / DEV_BSIZE;
	return(blks);
}

#ifdef TESTING
main(argc, argv)
int argc;
char *argv[];
{
	long n;

	if (argc != 4) {
		fprintf(stderr, "usage: %s <size> <bsize> <fsize>\n", argv[0]);
		exit(1);
	}
	n = nblocks(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));

	printf("%d\n", n);
}
#endif /* TESTING */
