/*
* Copyright (c) 2003-2006 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: fsspace.c,v 1.19 2006/10/05 04:27:37 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/fs.h"
#include "sm/statfs.h"
/* update only every 60s */
#ifndef FS_UPD_TO
# define FS_UPD_TO 60
#endif
typedef struct filesys_S filesys_T, *filesys_P;
struct filesys_S
{
dev_t fs_dev; /* unique device id */
ulong fs_kbfree; /* KB free */
ulong fs_blksize; /* block size, in bytes */
time_T fs_lastupdate; /* last time fs_kbfree was updated */
const char *fs_path; /* some path in the FS */
};
struct fs_ctx_S
{
#if MTA_USE_PTHREADS
pthread_mutex_t fsc_mutex;
#endif
uint fsc_cur_entries; /* cur. number of entries in fsc_sys*/
uint fsc_max_entries; /* max. number of entries in fsc_sys*/
filesys_P fsc_sys; /* array of filesys_T */
};
/*
** FS_CTX_CLOSE -- close/free FS context
**
** Parameters:
** fs_ctx -- FS context
**
** Returns:
** SM_SUCCESS
**
** Last code review: 2005-03-17 18:53:54
** Last code change:
*/
sm_ret_T
fs_ctx_close(fs_ctx_P fs_ctx)
{
if (fs_ctx == NULL)
return SM_SUCCESS;
#if MTA_USE_PTHREADS
(void) pthread_mutex_destroy(&fs_ctx->fsc_mutex);
#endif
SM_FREE(fs_ctx->fsc_sys);
SM_FREE_SIZE(fs_ctx, sizeof(*fs_ctx));
return SM_SUCCESS;
}
/*
** FS_CTX_OPEN -- create a FS context
**
** Parameters:
** entries -- maximum number of entries in the FS context
** fs_ctx -- FS context
**
** Returns:
** usual sm_error code
**
** Side Effects: none on error
**
** Last code review: 2005-03-17 19:00:45
** Last code change: 2005-03-17 18:59:18
*/
sm_ret_T
fs_ctx_open(uint entries, fs_ctx_P *pfs_ctx)
{
sm_ret_T ret;
fs_ctx_P fs_ctx;
size_t s;
#if MTA_USE_PTHREADS
int r;
#endif
SM_REQUIRE(pfs_ctx != NULL);
if (entries < 1)
return sm_err_perm(EINVAL);
s = 0;
fs_ctx = (fs_ctx_P) sm_zalloc(sizeof(*fs_ctx));
if (fs_ctx == NULL)
return sm_err_temp(ENOMEM);
#if MTA_USE_PTHREADS
r = pthread_mutex_init(&fs_ctx->fsc_mutex, SM_PTHREAD_MUTEXATTR);
if (r != 0)
{
ret = sm_err_perm(r);
goto err;
}
#endif /* MTA_USE_PTHREADS */
s = sizeof(*(fs_ctx->fsc_sys)) * entries;
if (s <= 0 || s <= sizeof(*(fs_ctx->fsc_sys)) || s <= entries)
{
ret = sm_err_perm(E2BIG);
goto error;
}
fs_ctx->fsc_sys = (filesys_P) sm_zalloc(s);
if (fs_ctx->fsc_sys == NULL)
{
ret = sm_err_temp(ENOMEM);
goto error;
}
fs_ctx->fsc_max_entries = entries;
fs_ctx->fsc_cur_entries = 0;
*pfs_ctx = fs_ctx;
return SM_SUCCESS;
error:
#if MTA_USE_PTHREADS
(void) pthread_mutex_destroy(&fs_ctx->fsc_mutex);
err:
#endif
if (fs_ctx != NULL)
{
if (s > 0 && fs_ctx->fsc_sys != NULL)
SM_FREE_SIZE(fs_ctx->fsc_sys, s);
SM_FREE_SIZE(fs_ctx, sizeof(*fs_ctx));
}
return ret;
}
/*
** FS_UPDATE -- update an FS entry in an FS context
** (only if last update is "too old")
**
** Parameters:
** fs_ctx -- FS context
** fs_idx -- index in FS
**
** Returns:
** usual sm_error code; errno from stat
**
** Side Effects: none on error
**
** Last code review: 2005-03-17 21:31:33
** Last code change: 2005-03-17 18:47:08
*/
static sm_ret_T
fs_update(fs_ctx_P fs_ctx, uint fs_idx)
{
sm_ret_T ret;
ulong kbfree;
time_T nowt;
SM_IS_FS_CTX(fs_ctx);
SM_REQUIRE(/*fs_idx >= 0 &&*/ fs_idx < fs_ctx->fsc_max_entries);
nowt = time(NULLT);
ret = SM_SUCCESS;
if ((fs_ctx->fsc_sys)[fs_idx].fs_lastupdate + FS_UPD_TO < nowt)
{
ret = freediskspace((fs_ctx->fsc_sys)[fs_idx].fs_path,
&((fs_ctx->fsc_sys)[fs_idx].fs_blksize), &kbfree);
if (sm_is_success(ret))
{
(fs_ctx->fsc_sys)[fs_idx].fs_kbfree = kbfree;
(fs_ctx->fsc_sys)[fs_idx].fs_lastupdate = nowt;
}
}
return ret;
}
/*
** FS_NEW -- add an FS entry to an FS context
** Allow this function to "grow" fsc_sys??
**
** Parameters:
** fs_ctx -- FS context
** path -- path to FS (must stay available, is NOT copied)
** pfs_idx -- (pointer to) index in FS (output)
**
** Returns:
** usual sm_error code; E2BIG, stat() errno,
**
** Side Effects: none on error (except if unlock fails)
**
** Last code review: 2005-03-17 18:50:59
** Last code change:
*/
sm_ret_T
fs_new(fs_ctx_P fs_ctx, const char *path, int *pfs_idx)
{
sm_ret_T ret;
uint u;
#if MTA_USE_PTHREADS
int r;
#endif
struct stat st;
SM_IS_FS_CTX(fs_ctx);
SM_REQUIRE(pfs_idx != NULL);
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&fs_ctx->fsc_mutex);
SM_LOCK_OK(r);
if (r != 0)
return sm_err_perm(r);
#endif
*pfs_idx = -1;
ret = SM_SUCCESS;
if (stat(path, &st) < 0)
{
ret = sm_err_temp(errno);
goto error;
}
for (u = 0; u < fs_ctx->fsc_cur_entries; ++u)
{
if ((fs_ctx->fsc_sys)[u].fs_dev == st.st_dev)
{
*pfs_idx = u;
break;
}
}
if (*pfs_idx == -1)
{
if (fs_ctx->fsc_cur_entries >= fs_ctx->fsc_max_entries)
{
ret = sm_err_temp(E2BIG);
goto error;
}
u = fs_ctx->fsc_cur_entries;
(fs_ctx->fsc_sys)[u].fs_path = path;
(fs_ctx->fsc_sys)[u].fs_dev = st.st_dev;
(fs_ctx->fsc_sys)[u].fs_kbfree = 0;
(fs_ctx->fsc_sys)[u].fs_blksize = 0;
(fs_ctx->fsc_sys)[u].fs_lastupdate = 0;
ret = fs_update(fs_ctx, u);
*pfs_idx = u;
++fs_ctx->fsc_cur_entries;
}
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&fs_ctx->fsc_mutex);
SM_ASSERT(r == 0);
if (r != 0 && sm_is_success(ret))
ret = sm_err_perm(r);
#endif
return ret;
error:
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&fs_ctx->fsc_mutex);
SM_ASSERT(r == 0);
if (r != 0 && sm_is_success(ret))
ret = sm_err_perm(r);
#endif
return ret;
}
/*
** FS_GETFREE -- get free space in FS
**
** Parameters:
** fs_ctx -- FS context
** fs_idx -- index in FS
** pkbfree -- (pointer to) free space (KB) (output)
**
** Returns:
** usual sm_error code; fs_update()
**
** Side Effects: none on error (except if unlock fails)
** ok: may update free space value in fs_ctx
**
** Locking: locks fs_ctx
**
** Last code review: 2005-03-17 21:33:09
** Last code change:
*/
sm_ret_T
fs_getfree(fs_ctx_P fs_ctx, uint fs_idx, ulong *pkbfree)
{
sm_ret_T ret;
#if MTA_USE_PTHREADS
int r;
#endif
SM_IS_FS_CTX(fs_ctx);
/* SM_REQUIRE(fs_idx >= 0); */
SM_REQUIRE(pkbfree != NULLPTR);
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&fs_ctx->fsc_mutex);
SM_LOCK_OK(r);
if (r != 0)
return sm_err_perm(r);
#endif
SM_REQUIRE(fs_idx < fs_ctx->fsc_max_entries);
ret = fs_update(fs_ctx, fs_idx);
if (!sm_is_err(ret))
*pkbfree = (fs_ctx->fsc_sys)[fs_idx].fs_kbfree;
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&fs_ctx->fsc_mutex);
SM_ASSERT(r == 0);
if (r != 0 && sm_is_success(ret))
ret = sm_err_perm(r);
#endif
return ret;
}
/*
** FS_CHGFREE -- change free space in FS
**
** Parameters:
** fs_ctx -- FS context
** fs_idx -- index in FS
** chg_free -- change in free space (KB)
** >0: more free space
** <0: less free space
** pkbfree -- (pointer to) free space (KB) (output)
**
** Returns:
** SM_SUCCESS except for (un)lock errors
**
** Locking: locks fs_ctx
**
** Last code review: 2005-03-17 21:35:25
** Last code change:
*/
sm_ret_T
fs_chgfree(fs_ctx_P fs_ctx, uint fs_idx, long chg_free, ulong *pkbfree)
{
#if MTA_USE_PTHREADS
int r;
#endif
sm_ret_T ret;
long value;
SM_IS_FS_CTX(fs_ctx);
SM_REQUIRE(pkbfree != NULLPTR);
/* SM_REQUIRE(fs_idx >= 0); */
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&fs_ctx->fsc_mutex);
SM_LOCK_OK(r);
if (r != 0)
return sm_err_perm(r);
#endif
SM_REQUIRE(fs_idx < fs_ctx->fsc_max_entries);
ret = SM_SUCCESS;
if (chg_free < 0)
{
value = 0 - chg_free;
if (value > (fs_ctx->fsc_sys)[fs_idx].fs_kbfree)
(fs_ctx->fsc_sys)[fs_idx].fs_kbfree = 0;
else
(fs_ctx->fsc_sys)[fs_idx].fs_kbfree += chg_free;
}
else if (chg_free > 0)
{
value = chg_free + (fs_ctx->fsc_sys)[fs_idx].fs_kbfree;
if (value < chg_free
|| value < (fs_ctx->fsc_sys)[fs_idx].fs_kbfree)
(fs_ctx->fsc_sys)[fs_idx].fs_kbfree = LONG_MAX;
else
(fs_ctx->fsc_sys)[fs_idx].fs_kbfree = value;
}
*pkbfree = (fs_ctx->fsc_sys)[fs_idx].fs_kbfree;
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&fs_ctx->fsc_mutex);
SM_ASSERT(r == 0);
if (r != 0 && sm_is_success(ret))
ret = sm_err_perm(r);
#endif
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1