/*
* Copyright (c) 2002-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: ibdbr.c,v 1.68 2007/06/18 04:42:31 ca Exp $")
#include "sm/error.h"
#include "sm/fcntl.h"
#include "sm/memops.h"
#include "sm/stat.h"
#include "sm/time.h"
#include "sm/heap.h"
#include "sm/assert.h"
#include "sm/str.h"
#include "sm/io.h"
#include "sm/fdset.h"
#include "sm/reccom.h"
#include "sm/cstr.h"
#include "sm/ibdb.h"
#include "ibdb.h"
/* specify an INCEDB backup on disk */
struct ibdbr_ctx_S
{
uint ibdbr_version; /* version of this ibdb */
sm_file_T *ibdbr_fp; /* current file */
sm_str_P ibdbr_path; /* pathname of file */
sm_str_P ibdbr_name_rm; /* pathname of file to remove */
char *ibdbr_dir; /* directory of file */
char *ibdbr_base_dir; /* name of base directory */
char *ibdbr_name; /* basename of file */
int ibdbr_mode; /* file mode */
uint32_t ibdbr_sequence; /* current sequence number */
bool ibdbr_first; /* first record? */
uchar *ibdbr_fp_buf; /* buffer for fp if necessary */
};
/*
** IBDBR_CTX_FREE -- free an IBDBR context
**
** Parameters:
** ibdbr_ctx -- INCEDB context
**
** Returns:
** none.
*/
static void
ibdbr_ctx_free(ibdbr_ctx_P ibdbr_ctx)
{
if (ibdbr_ctx == NULL)
return;
SM_FREE(ibdbr_ctx->ibdbr_name);
SM_FREE(ibdbr_ctx->ibdbr_fp_buf);
SM_STR_FREE(ibdbr_ctx->ibdbr_path);
SM_STR_FREE(ibdbr_ctx->ibdbr_name_rm);
sm_free_size(ibdbr_ctx, sizeof(*ibdbr_ctx));
return;
}
/*
** IBDBR_CTX_NEW -- allocate new ibdbr context
**
** Parameters:
** base_dir -- name of base directory (can be NULL)
** name -- name of IBDB file
** seq -- initial sequence number
** flags -- flags
**
** Returns:
** INCEDB context (NULL on error)
*/
static ibdbr_ctx_P
ibdbr_ctx_new(const char *base_dir, const char *name, uint32_t seq, uint flags)
{
size_t l;
ibdbr_ctx_P ibdbr_ctx;
SM_ASSERT(name != NULL);
ibdbr_ctx = (ibdbr_ctx_P) sm_zalloc(sizeof(*ibdbr_ctx));
if (ibdbr_ctx == NULL)
return NULL;
if (base_dir != NULL && *base_dir != '\0') {
size_t lb;
lb = strlen(base_dir) + 1;
ibdbr_ctx->ibdbr_base_dir = (char *) sm_malloc(lb);
if (NULL == ibdbr_ctx->ibdbr_base_dir)
goto error;
strlcpy(ibdbr_ctx->ibdbr_base_dir, base_dir, lb);
}
IBDB_DIR(ibdbr_ctx->ibdbr_dir, ibdbr_ctx->ibdbr_base_dir, flags);
l = strlen(name) + 1;
ibdbr_ctx->ibdbr_name = (char *) sm_malloc(l);
if (ibdbr_ctx->ibdbr_name == NULL)
goto error;
strlcpy(ibdbr_ctx->ibdbr_name, name, l);
l += strlen(ibdbr_ctx->ibdbr_dir) + 1 + UINT32_LEN;
ibdbr_ctx->ibdbr_path = sm_str_new(NULL, l, l);
if (ibdbr_ctx->ibdbr_path == NULL)
goto error;
crt_ibdb_path(ibdbr_ctx->ibdbr_path, ibdbr_ctx->ibdbr_dir, name, seq);
++l;
ibdbr_ctx->ibdbr_name_rm = sm_str_new(NULL, l, l);
if (ibdbr_ctx->ibdbr_name_rm == NULL)
goto error;
ibdbr_ctx->ibdbr_first = true;
ibdbr_ctx->ibdbr_sequence = seq;
return ibdbr_ctx;
error:
ibdbr_ctx_free(ibdbr_ctx);
return NULL;
}
/*
** IBDBR_NEXT -- open next ibdb backup on disk
**
** Parameters:
** ibdbr_ctx -- INCEDB context
**
** Returns:
** usual sm_error code (sm_io_{open,close}())
*/
static sm_ret_T
ibdbr_next(ibdbr_ctx_P ibdbr_ctx)
{
sm_ret_T ret;
sm_file_T *fp;
SM_ASSERT(ibdbr_ctx != NULL);
ret = sm_io_close(ibdbr_ctx->ibdbr_fp, SM_IO_CF_NONE);
ibdbr_ctx->ibdbr_fp = NULL;
if (sm_is_err(ret))
return ret;
ibdbr_ctx->ibdbr_sequence++;
sm_str_clr(ibdbr_ctx->ibdbr_path);
crt_ibdb_path(ibdbr_ctx->ibdbr_path, ibdbr_ctx->ibdbr_dir,
ibdbr_ctx->ibdbr_name, ibdbr_ctx->ibdbr_sequence);
ret = sm_io_open(SmStStdio, sm_str_getdata(ibdbr_ctx->ibdbr_path),
ibdbr_ctx->ibdbr_mode, &fp, SM_IO_WHAT_END);
if (sm_is_err(ret))
return ret;
if (NULL != ibdbr_ctx->ibdbr_fp_buf)
{
ret = sm_io_setvbuf(fp, ibdbr_ctx->ibdbr_fp_buf, SM_IO_FBF,
IBDB_BUF_SIZE);
if (sm_is_err(ret))
return ret;
}
ibdbr_ctx->ibdbr_fp = fp;
ibdbr_ctx->ibdbr_first = true;
return ret;
}
/*
** IBDBR_OPEN -- open ibdb backup on disk
**
** Parameters:
** base_dir -- name of base directory (can be NULL)
** name -- base name, will be extended by seq
** mode -- open mode
** seq -- initial sequence number (> 0)
** size -- maximum size (ignored)
** flags -- flags
** pibdbr_ctx -- (pointer to) INCEDB context (output)
**
** Returns:
** usual sm_error code; ENOMEM,
*/
sm_ret_T
ibdbr_open(const char *base_dir, const char *name, int mode, uint32_t seq, size_t size, uint flags, ibdbr_ctx_P *pibdbr_ctx)
{
sm_ret_T ret;
sm_file_T *fp;
ibdbr_ctx_P ibdbr_ctx;
SM_REQUIRE(pibdbr_ctx != NULL);
SM_REQUIRE(seq > 0);
ibdbr_ctx = ibdbr_ctx_new(base_dir, name, seq, flags);
if (ibdbr_ctx == NULL)
return sm_error_temp(SM_EM_IBDB, ENOMEM);
ret = sm_io_open(SmStStdio, sm_str_getdata(ibdbr_ctx->ibdbr_path),
mode, &fp, SM_IO_WHAT_END);
if (sm_is_err(ret))
goto error;
if (f_blksize(*fp) < IBDB_MINBUF_SIZE ||
(f_blksize(*fp) % IBDB_MINBUF_SIZE) != 0)
{
ibdbr_ctx->ibdbr_fp_buf = sm_malloc(IBDB_BUF_SIZE);
if (NULL == ibdbr_ctx->ibdbr_fp_buf)
goto error;
ret = sm_io_setvbuf(fp, ibdbr_ctx->ibdbr_fp_buf, SM_IO_FBF,
IBDB_BUF_SIZE);
if (sm_is_err(ret))
goto error;
}
ibdbr_ctx->ibdbr_fp = fp;
ibdbr_ctx->ibdbr_first = true;
ibdbr_ctx->ibdbr_mode = mode;
*pibdbr_ctx = ibdbr_ctx;
return ret;
error:
ibdbr_ctx_free(ibdbr_ctx);
return sm_is_err(ret) ? ret : sm_error_perm(SM_EM_IBDB, ENOMEM);
}
/*
** IBDBR_HDR_MOD -- read header modification record
**
** Parameters:
** ibdbr_ctx -- INCEDB context
** ibdb_hdrmod -- header modification record
**
** Returns:
** usual sm_error code
**
** Note: ibdb_hdrmod structure must be allocated
*/
static sm_ret_T
ibdbr_hdr_mod(ibdbr_ctx_P ibdbr_ctx, ibdb_hdrmod_P ibdb_hdrmod)
{
uint32_t n;
sm_file_T *fp;
sm_ret_T ret;
SM_ASSERT(ibdbr_ctx != NULL);
SM_ASSERT(ibdb_hdrmod != NULL);
fp = ibdbr_ctx->ibdbr_fp;
/* transaction ID */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_HM_TAID)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > SMTP_STID_SIZE)
goto error;
ret = sm_io_fgetn(fp, (uchar *) ibdb_hdrmod->ibh_ta_id, n);
if (sm_is_err(ret))
goto error;
ibdb_hdrmod->ibh_ta_id[SMTP_STID_SIZE - 1] = '\0';
/* type */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_HM_TYPE)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != 4)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret))
goto error;
ibdb_hdrmod->ibh_type = n;
/* position */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_HM_POS)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != 4)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret))
goto error;
ibdb_hdrmod->ibh_pos = n;
/* header XXX this may exceed the record size??? */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_HM_HDR)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > SM_MAXHDRLEN)
goto error;
if (n > 0)
{
ret = sm_io_fgetncstr(fp, &ibdb_hdrmod->ibh_hdr, n);
if (sm_is_err(ret))
goto error;
}
ret = sm_io_falign(fp, IBDB_REC_SIZE);
if (sm_is_err(ret))
goto error;
return SM_SUCCESS;
error:
/* cleanup? */
if (!sm_is_err(ret))
ret = sm_error_perm(SM_EM_IBDB, SM_E_PR_ERR);
return ret;
}
/*
** IBDBR_TA_STATUS -- read transaction status
**
** Parameters:
** ibdbr_ctx -- INCEDB context
** ta -- transaction (sender) data
** pstatus -- (pointer to) transaction status (output)
**
** Returns:
** usual sm_error code
**
** Note: ta structure must be allocated by caller except for cdb_id.
*/
static sm_ret_T
ibdbr_ta_status(ibdbr_ctx_P ibdbr_ctx, ibdb_ta_P ibdb_ta, int *pstatus)
{
uint32_t n;
sm_file_T *fp;
sm_ret_T ret;
SM_ASSERT(ibdbr_ctx != NULL);
SM_ASSERT(ibdb_ta != NULL);
fp = ibdbr_ctx->ibdbr_fp;
/* transaction status */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_TA_ST)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != 4)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret))
goto error;
*pstatus = n;
/* transaction ID */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_TAID)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > SMTP_STID_SIZE)
goto error;
ret = sm_io_fgetn(fp, (uchar *) ibdb_ta->ibt_ta_id, n);
if (sm_is_err(ret))
goto error;
ibdb_ta->ibt_ta_id[SMTP_STID_SIZE - 1] = '\0';
/* CDB ID */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_CDBID)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > CDB_ID_SIZE)
goto error;
/* todo: don't read CDB if n==1? can callers deal with that?? */
SM_ASSERT(ibdb_ta->ibt_cdb_id == NULL);
ret = sm_io_fgetncstr(fp, &ibdb_ta->ibt_cdb_id, n);
if (sm_is_err(ret))
goto error;
#if 0
/* ??? */
if (n == CDB_ID_SIZE)
sm_cstr_wr_elem(ibdb_ta->ibt_cdb_id, CDB_ID_SIZE - 1) = '\0';
#endif /* 0 */
/* nrcpts */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_NRCPTS)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != 4)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret))
goto error;
ibdb_ta->ibt_nrcpts = n;
/* mail XXX this may exceed the record size??? */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_MAIL)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > sm_str_getsize(ibdb_ta->ibt_mail_pa))
goto error;
ret = sm_io_fgetn(fp, sm_str_getdata(ibdb_ta->ibt_mail_pa), n);
if (sm_is_err(ret))
goto error;
SM_STR_SETLEN(ibdb_ta->ibt_mail_pa, n);
ret = sm_io_falign(fp, IBDB_REC_SIZE);
if (sm_is_err(ret))
goto error;
return SM_SUCCESS;
error:
/* cleanup? */
if (!sm_is_err(ret))
ret = sm_error_perm(SM_EM_IBDB, SM_E_PR_ERR);
return ret;
}
/*
** IBDBR_RCPT_STATUS -- read recipient status
**
** Parameters:
** ibdbr_ctx -- INCEDB context
** ibdb_rcpt -- recipient data
** pstatus -- (pointer to) recipient status (output)
**
** Returns:
** usual sm_error code
**
** Note: ibdb_rcpt structure must be allocated by caller.
*/
static sm_ret_T
ibdbr_rcpt_status(ibdbr_ctx_P ibdbr_ctx, ibdb_rcpt_P ibdb_rcpt, int *pstatus)
{
uint32_t n;
sm_file_T *fp;
sm_ret_T ret;
fp = ibdbr_ctx->ibdbr_fp;
/* recipient status */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_RCPT_ST)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != 4)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret))
goto error;
*pstatus = n;
/* recipient index */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_RCPT_IDX)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != 4)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret))
goto error;
ibdb_rcpt->ibr_idx = n;
/* transaction ID */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_TAID)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > SMTP_STID_SIZE)
goto error;
ret = sm_io_fgetn(fp, (uchar *) ibdb_rcpt->ibr_ta_id, n);
if (sm_is_err(ret))
goto error;
ibdb_rcpt->ibr_ta_id[SMTP_STID_SIZE - 1] = '\0';
/* rcpt XXX this may exceed the record size??? */
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n != RT_IBDB_RCPT_PA)
goto error;
ret = sm_io_fgetuint32(fp, &n);
if (sm_is_err(ret) || n > sm_str_getsize(ibdb_rcpt->ibr_pa))
goto error;
ret = sm_io_fgetn(fp, sm_str_getdata(ibdb_rcpt->ibr_pa), n);
if (sm_is_err(ret))
goto error;
SM_STR_SETLEN(ibdb_rcpt->ibr_pa, n);
ret = sm_io_falign(fp, IBDB_REC_SIZE);
if (sm_is_err(ret))
goto error;
return SM_SUCCESS;
error:
/* cleanup? */
if (!sm_is_err(ret))
ret = sm_error_perm(SM_EM_IBDB, SM_E_PR_ERR);
return ret;
}
/*
** IDBR_GET -- read record from INCEDB
**
** Parameters:
** ibdbr_ctx -- INCEDB context
** ibdb_rcpt -- recipient data
** ibdb_ta -- transaction (sender) data
** ibdb_hdrmod -- header modification data
** pstatus -- (pointer to) transaction status (output)
**
** Returns:
** >0: RT_IBDB_RCPT, RT_IBDB_TA, etc
** <0: usual sm_error code
**
** Locking: must be done by caller.
*/
sm_ret_T
ibdbr_get(ibdbr_ctx_P ibdbr_ctx, ibdb_rcpt_P ibdb_rcpt, ibdb_ta_P ibdb_ta, ibdb_hdrmod_P ibdb_hdrmod, int *pstatus)
{
uint32_t val;
sm_ret_T ret;
SM_ASSERT(ibdbr_ctx != NULL);
SM_ASSERT(ibdb_rcpt != NULL);
SM_ASSERT(ibdb_ta != NULL);
again:
if (sm_io_eof(ibdbr_ctx->ibdbr_fp))
{
/* roll-over */
ret = ibdbr_next(ibdbr_ctx);
if (sm_is_err(ret))
goto error;
}
/* first: make sure one record is in file buffer... */
ret = sm_io_ffill(ibdbr_ctx->ibdbr_fp, IBDB_REC_SIZE);
if (sm_is_err(ret))
{
if (sm_io_eof(ibdbr_ctx->ibdbr_fp))
{
/* roll-over */
ret = ibdbr_next(ibdbr_ctx);
if (sm_is_err(ret))
goto error;
ret = sm_io_ffill(ibdbr_ctx->ibdbr_fp, IBDB_REC_SIZE);
}
if (sm_is_err(ret))
{
/*
** This may happen if an entry wasn't written
** completely. Just ignore that entry.
*/
if (SM_IO_EOF == ret ||
sm_error_perm(SM_EM_IO, EIO) == ret)
ret = sm_error_perm(SM_EM_IBDB, ENOENT);
goto error;
}
}
/* next: get record type */
ret = sm_io_fgetuint32(ibdbr_ctx->ibdbr_fp, &val);
if (sm_is_err(ret))
goto error;
/* first record in file? check version number */
if (ibdbr_ctx->ibdbr_first)
{
if (val != RT_IBDB_VERS_R)
goto error;
/* version number */
ret = sm_io_fgetuint32(ibdbr_ctx->ibdbr_fp, &val);
if (sm_is_err(ret) || val != RT_IBDB_VERS_N)
goto error;
ret = sm_io_fgetuint32(ibdbr_ctx->ibdbr_fp, &val);
if (sm_is_err(ret) || val != 4)
goto error;
ret = sm_io_fgetuint32(ibdbr_ctx->ibdbr_fp, &val);
if (sm_is_err(ret))
goto error;
if (!IBDB_VRS_COMPAT(IBDB_VERSION, val))
goto error;
ret = sm_io_falign(ibdbr_ctx->ibdbr_fp, IBDB_REC_SIZE);
if (sm_is_err(ret))
goto error;
ibdbr_ctx->ibdbr_version = val;
ibdbr_ctx->ibdbr_first = false;
goto again;
}
if (val == RT_IBDB_TA)
ret = ibdbr_ta_status(ibdbr_ctx, ibdb_ta, pstatus);
else if (val == RT_IBDB_RCPT)
ret = ibdbr_rcpt_status(ibdbr_ctx, ibdb_rcpt, pstatus);
else if (val == RT_IBDB_HDRMOD)
{
*pstatus = 0; /* ??? */
ret = ibdbr_hdr_mod(ibdbr_ctx, ibdb_hdrmod);
}
else
ret = sm_error_perm(SM_EM_IBDB, SM_E_PR_ERR);
if (sm_is_err(ret))
goto error;
return val;
error:
if (sm_is_success(ret))
ret = sm_error_perm(SM_EM_IBDB, SM_E_VER_MIX);
return ret;
}
/*
** IDBR_CLOSE -- close the file
**
** Parameters:
** ibdbr_ctx -- INCEDB context
**
** Returns:
** usual sm_error code (from sm_io_close())
*/
sm_ret_T
ibdbr_close(ibdbr_ctx_P ibdbr_ctx)
{
sm_ret_T ret;
sm_file_T *fp;
SM_ASSERT(ibdbr_ctx != NULL);
ret = SM_SUCCESS;
fp = ibdbr_ctx->ibdbr_fp;
if (fp != NULL)
{
ret = sm_io_close(fp, SM_IO_CF_NONE);
if (sm_is_err(ret))
return ret;
}
ibdbr_ctx_free(ibdbr_ctx);
return ret;
}
/*
** IBDBR_UNLINK -- unlink IBDB files
**
** Parameters:
** ibdbr_ctx -- ibdbr context
** first -- first sequence number to unlink
** last -- last sequence number to unlink
**
** Returns:
** SM_SUCCESS
*/
sm_ret_T
ibdbr_unlink(ibdbr_ctx_P ibdbr_ctx, uint32_t first, uint32_t last)
{
size_t i;
SM_ASSERT(ibdbr_ctx != NULL);
for (i = first; i <= last; i++)
{
sm_str_clr(ibdbr_ctx->ibdbr_name_rm);
crt_ibdb_path(ibdbr_ctx->ibdbr_name_rm, ibdbr_ctx->ibdbr_dir,
ibdbr_ctx->ibdbr_name, i);
(void) unlink((const char *)
sm_str_getdata(ibdbr_ctx->ibdbr_name_rm));
sm_str_clr(ibdbr_ctx->ibdbr_name_rm);
}
return SM_SUCCESS;
}
syntax highlighted by Code2HTML, v. 0.9.1