/*
 * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
 *      All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: freediskspace.c,v 1.9 2005/03/17 22:09:36 ca Exp $")
#include "sm/types.h"
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/statfs.h"
#include "sm/limits.h"

/*
**  FREEDISKSPACE -- see how much free space is on the queue filesystem
**
**	Only implemented if you have statfs.
**
**	Parameters:
**		dir -- the directory in question.
**		bsize -- (pointer to) filesystem block size (output)
**		pkbfree -- (pointer to) free space (KB) (output)
**
**	Returns:
**		The number of blocks free on the queue filesystem.
**		sm_err_temp(errno) if the statfs call fails.
**
**	Side Effects:
**		Puts the filesystem block size into bsize (if not NULL)
**
**	Last code review: 2005-03-17 21:24:24
**	Last code change:
*/

sm_ret_T
freediskspace(const char *dir, ulong *bsize, ulong *pkbfree)
{
	ulong kbfree;
#if WIN32
	ULARGE_INTEGER i64FreeBytesToCaller;
	ULARGE_INTEGER i64TotalBytes;
	extern void _dosmaperr(ULONG);

	SM_ASSERT(pkbfree != NULL);

	/* initialize */
	(void) memset(&i64FreeBytesToCaller, 0, sizeof(i64FreeBytesToCaller));
	(void) memset(&i64TotalBytes, 0, sizeof(i64TotalBytes));
	if (bsize != NULL)
		*bsize = ONEKB;

	/* get the free disk space available */
	if (GetDiskFreeSpaceEx(dir, (PULARGE_INTEGER) &i64FreeBytesToCaller,
		(PULARGE_INTEGER) &i64TotalBytes, NULL))
	{
		/* convert to Kb, watch out for overflow */
		if ((i64FreeBytesToCaller.QuadPart / ONEKB) > ULONG_MAX)
			*pkbfree = ULONG_MAX;
		else
			*pkbfree = (ulong)
				(i64FreeBytesToCaller.QuadPart / ONEKB);
		return SM_SUCCESS;
	}
	return sm_err_temp(GetLastError());
#else /* WIN32 */
# if SFS_TYPE == SFS_USTAT
	struct ustat fs;
	struct stat statbuf;
#  define FSBLOCKSIZE	DEV_BSIZE
#  define SFS_BAVAIL	f_tfree
# else /* SFS_TYPE == SFS_USTAT */
#  if defined(ultrix)
	struct fs_data fs;
#   define SFS_BAVAIL	fd_bfreen
#   define FSBLOCKSIZE	1024L
#  else /* defined(ultrix) */
#   if SFS_TYPE == SFS_STATVFS
	struct statvfs fs;
#    define FSBLOCKSIZE	fs.f_frsize
#   else /* SFS_TYPE == SFS_STATVFS */
	struct statfs fs;
#    define FSBLOCKSIZE	fs.f_bsize
#   endif /* SFS_TYPE == SFS_STATVFS */
#  endif /* defined(ultrix) */
# endif /* SFS_TYPE == SFS_USTAT */
# ifndef SFS_BAVAIL
#  define SFS_BAVAIL f_bavail
# endif /* ! SFS_BAVAIL */

	SM_ASSERT(pkbfree != NULL);
# if SFS_TYPE == SFS_USTAT
	if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
# else /* SFS_TYPE == SFS_USTAT */
#  if SFS_TYPE == SFS_4ARGS
	if (statfs(dir, &fs, sizeof fs, 0) == 0)
#  else /* SFS_TYPE == SFS_4ARGS */
#   if SFS_TYPE == SFS_STATVFS
	if (statvfs(dir, &fs) == 0)
#   else /* SFS_TYPE == SFS_STATVFS */
#    if defined(ultrix)
	if (statfs(dir, &fs) > 0)
#    else /* defined(ultrix) */
	if (statfs(dir, &fs) == 0)
#    endif /* defined(ultrix) */
#   endif /* SFS_TYPE == SFS_STATVFS */
#  endif /* SFS_TYPE == SFS_4ARGS */
# endif /* SFS_TYPE == SFS_USTAT */
	{
		if (bsize != NULL)
			*bsize = FSBLOCKSIZE;
		if (fs.SFS_BAVAIL <= 0)
			return 0;
		else
		{
			if (fs.SFS_BAVAIL > ULONG_MAX)
				kbfree = (ulong) ULONG_MAX;
			else
				kbfree = (ulong) fs.SFS_BAVAIL;
			if (FSBLOCKSIZE > ONEKB)
				kbfree *= FSBLOCKSIZE / ONEKB;
			else if (FSBLOCKSIZE < ONEKB)
				kbfree /= ONEKB / FSBLOCKSIZE;
			*pkbfree = kbfree;
			return SM_SUCCESS;
		}
	}
	return sm_err_temp(errno);
}
#endif /* WIN32 */


syntax highlighted by Code2HTML, v. 0.9.1