#include "sm/generic.h"
SM_RCSID("@(#)$Id: cdbbf.c,v 1.3 2006/07/18 02:43:18 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 "libmta/io-int.h"
#include "sm/fdset.h"
#include "sm/cdb.h"
#include "cdb.h"
#include "cdbbf.h"
static open_F sm_cdbf_open;
static read_F sm_cdbf_read;
static write_F sm_cdbf_write;
static close_F sm_cdbf_close;
static getinfo_F sm_cdbf_getinfo;
static setinfo_F sm_cdbf_setinfo;
static seek_F sm_cdbf_seek;
sm_stream_T SmCDBStL =
SM_STREAM_STRUCT(sm_cdbf_open, sm_cdbf_close,
sm_cdbf_read, sm_cdbf_write,
NULL, NULL, sm_cdbf_seek,
sm_cdbf_getinfo, sm_cdbf_setinfo);
/*
** CDBF_OPEN -- open file
**
** Parameters:
** fp -- file pointer to be associated with the open
** info -- pathname of the file to be opened
** flags -- indicates type of access methods
**
** Returns:
** Failure: error code
** Success: 0 or greater (fd of file from open(2)).
**
*/
static sm_ret_T
cdbf_open(sm_file_T *fp, const char *path, int oflags)
{
sm_cdbf_P sm_cdbf;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
SM_REQUIRE(sm_cdbf != NULL);
if (sm_is_err(sm_cdbf->cdbf_open_status))
return sm_cdbf->cdbf_open_status;
if (sm_cdbf->cdbf_ondisk || is_valid_fd(f_fd(*fp)))
return SM_SUCCESS;
f_fd(*fp) = open(path, oflags | O_NONBLOCK,
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
if (!is_valid_fd(f_fd(*fp)))
{
sm_cdbf->cdbf_open_status = sm_error_perm(SM_EM_IO, errno);
return sm_cdbf->cdbf_open_status;
}
sm_cdbf->cdbf_open_status = SM_SUCCESS;
sm_cdbf->cdbf_ondisk = true;
return f_fd(*fp);
}
/*
** CDB_CDBF_NEW -- create a CDB BF context based on cdb id
**
** Parameters:
** cdb_id -- cdb id
** cdb_ctx -- CDB context
**
** Returns:
** CDB BF context (NULL on error [ENOMEM])
**
** Last code review:
** Last code change:
*/
static sm_cdbf_P
sm_cdbf_new(const char *cdb_id, cdb_ctx_P cdb_ctx, size_t bufsize)
{
size_t l;
sm_cdbf_P sm_cdbf;
sm_ret_T ret;
SM_ASSERT(cdb_id != NULL);
SM_IS_CDB_CTX(cdb_ctx);
sm_cdbf = (sm_cdbf_P) sm_zalloc(sizeof(*sm_cdbf));
if (sm_cdbf == NULL)
return NULL;
l = strlen(cdb_id) + 3;
SM_ASSERT(l >= 5);
if (cdb_ctx->cdbx_base != NULL && *cdb_ctx->cdbx_base != '\0')
l += strlen(cdb_ctx->cdbx_base);
sm_cdbf->cdbf_path = (char *) sm_malloc(l);
if (sm_cdbf->cdbf_path == NULL)
goto error;
sm_cdbf->cdbf_ctx = cdb_ctx;
ret = cdb_crt_path(cdb_id, cdb_ctx->cdbx_base, sm_cdbf->cdbf_path, l);
if (sm_is_err(ret))
goto error;
return sm_cdbf;
error:
if (sm_cdbf != NULL)
{
if (sm_cdbf->cdbf_path != NULL)
sm_free(sm_cdbf->cdbf_path);
sm_free_size(sm_cdbf, sizeof(*sm_cdbf));
}
return NULL;
}
/*
** CDB_CDBF_FREE -- free a CDB BF context
**
** Parameters:
** sm_cdbf -- CDB BF context
**
** Returns:
** none
**
** Last code review:
** Last code change:
*/
static void
sm_cdbf_free(sm_cdbf_P sm_cdbf)
{
if (NULL == sm_cdbf)
return;
SM_FREE(sm_cdbf->cdbf_path);
sm_free_size(sm_cdbf, sizeof(*sm_cdbf));
return;
}
/*
** SM_CDBF_OPEN -- the "base" open function called by sm_io_open() for the
** internal, file-type-specific info setup.
**
** Parameters:
** fp -- file pointer being filled-in for file being open'd
** info -- information about file being opened
** flags -- indicates type of access methods
** ap -- further arguments
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_cdbf_open(sm_file_T *fp, const void *info, int flags, va_list ap)
{
sm_ret_T ret;
int oflags, l;
size_t bsize;
const char *path;
sm_cdbf_P sm_cdbf;
cdb_ctx_P cdb_ctx;
#if 0
struct stat st;
#endif
SM_IS_FP(fp);
SM_REQUIRE(info != NULL);
path = (const char *) info;
bsize = SIZE_T_MAX;
cdb_ctx = NULL;
#if 0
/* Sanity checks */
if (stat(path, &st) == 0)
{
/* File already exists on disk */
return sm_error_perm(SM_EM_IO, EEXIST);
}
#endif /* 0 */
switch (flags)
{
case SM_IO_RDWR:
oflags = O_RDWR;
break;
case SM_IO_RDWRCR:
oflags = O_RDWR | O_CREAT;
break;
case SM_IO_RDWRTR:
oflags = O_RDWR | O_CREAT | O_TRUNC;
break;
case SM_IO_RDONLY:
oflags = O_RDONLY;
break;
case SM_IO_WRONLY:
oflags = O_WRONLY | O_CREAT | O_TRUNC;
break;
case SM_IO_WREXCL:
oflags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
break;
case SM_IO_RDWRCRX:
oflags = O_RDWR | O_CREAT | O_TRUNC | O_EXCL;
break;
case SM_IO_APPEND:
oflags = O_APPEND | O_WRONLY | O_CREAT;
break;
case SM_IO_APPENDRW:
oflags = O_APPEND | O_RDWR | O_CREAT;
break;
default:
return sm_error_perm(SM_EM_CDB, EINVAL);
}
for (;;)
{
l = va_arg(ap, int);
if (l == SM_IO_WHAT_END)
break;
switch (l)
{
case SM_IO_WHAT_BSIZE:
bsize = va_arg(ap, size_t);
break;
/*
case SM_IO_WHAT_FMODE:
fmode = (mode_t) va_arg(ap, int);
break;
*/
case SM_IO_WHAT_CDB_HDL:
cdb_ctx = va_arg(ap, cdb_ctx_P);
SM_IS_CDB_CTX(cdb_ctx);
break;
default: /* ignore unknown values? */
/* what should we do about the argument then? */
(void *)va_arg(ap, int);
break;
}
}
sm_cdbf = sm_cdbf_new(path, cdb_ctx, bsize);
if (sm_cdbf == NULL)
return sm_error_temp(SM_EM_IO, ENOMEM);
if (bsize != SIZE_T_MAX)
{
ret = sm_io_setvbuf(fp, NULL, SM_IO_FBF, bsize);
if (sm_is_err(ret))
goto error;
}
sm_cdbf->cdbf_ondisk = false;
sm_cdbf->cdbf_oflags = oflags;
f_cookie(*fp) = sm_cdbf;
return SM_SUCCESS;
error:
if (sm_cdbf != NULL)
sm_cdbf_free(sm_cdbf);
return ret;
}
/*
** SM_CDBF_GETINFO -- returns info about an open file pointer
**
** Parameters:
** fp -- file pointer to get info about
** what -- type of info to obtain
** valp -- thing to return the info in
*/
static sm_ret_T
sm_cdbf_getinfo(sm_file_T *fp, int what, void *valp)
{
sm_cdbf_P sm_cdbf;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
switch (what)
{
/*
case SM_IO_WHAT_BF_BUFSIZE:
return sm_cdbf->cdbf_bufsize;
case SM_IO_WHAT_SIZE:
return sm_cdbf->cdbf_size;
*/
default:
return sm_error_perm(SM_EM_IO, EINVAL);
}
}
/*
** SM_CDBF_ABORT -- abort file I/O, remove file
**
** Parameters:
** fp -- file pointer to get info about
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_cdbf_abort(sm_file_T *fp)
{
sm_cdbf_P sm_cdbf;
sm_ret_T ret;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
ret = SM_SUCCESS;
if (sm_cdbf->cdbf_ondisk)
{
int r;
const char *path;
r = close(f_fd(*fp));
if (-1 == r)
ret = sm_error_perm(SM_EM_CDB, errno);
path = cdbf_path(fp);
r = unlink(path);
if (-1 == r && !sm_is_err(ret))
ret = sm_error_perm(SM_EM_CDB, errno);
}
sm_cdbf_free(sm_cdbf);
f_cookie(*fp) = NULL;
return ret;
}
/*
** SM_CDBF_COMMIT -- commit file to disk
**
** Parameters:
** fp -- file pointer to get info commit
** sync -- invoke fsync(2)?
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_cdbf_commit(sm_file_T *fp, bool sync)
{
sm_cdbf_P sm_cdbf;
sm_ret_T ret;
int r;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
if (!sm_cdbf->cdbf_ondisk)
{
ret = cdbf_open(fp, sm_cdbf->cdbf_path, sm_cdbf->cdbf_oflags);
if (sm_is_err(ret))
return ret;
}
if (sync)
{
if (!is_valid_fd(f_fd(*fp)))
return sm_error_perm(SM_EM_CDB, EINVAL);
r = fsync(f_fd(*fp));
if (-1 == r)
return sm_error_perm(SM_EM_CDB, errno);
}
return SM_SUCCESS;
}
/*
** SM_CDBF_CLOSE -- close a buffered file
**
** Parameters:
** fp -- file to close
** flags -- flags
**
** Returns:
** usual sm_error code
*/
static int
sm_cdbf_close(sm_file_T *fp, int flags)
{
sm_ret_T ret;
sm_cdbf_P sm_cdbf;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
/* abort was called before? */
if (NULL == sm_cdbf)
return SM_SUCCESS;
if (SM_IS_FLAG(flags, SM_IO_CF_RM))
return sm_cdbf_abort(fp);
/* need to make sure the file is on disk */
ret = sm_cdbf_commit(fp, SM_IS_FLAG(flags, SM_IO_CF_SYNC));
if (is_valid_fd(f_fd(*fp)) && close(f_fd(*fp)) == -1 && !sm_is_err(ret))
ret = sm_error_perm(SM_EM_IO, errno);
return ret;
}
/*
** SM_CDBF_READ -- read a buffered file
**
** Parameters:
** cookie -- cookie of file to read
** buf -- buffer to fill
** nbytes -- how many bytes to read
** bytesread -- number of bytes read (output)
**
** Returns:
** number of bytes read or -1 indicate failure
**
** Side Effects:
** none.
**
*/
static sm_ret_T
sm_cdbf_read(sm_file_T *fp, uchar *buf, size_t nbytes, ssize_t *bytesread)
{
ssize_t ret;
sm_ret_T res;
/* should we decrease the timeout in the loop? */
*bytesread = 0;
do
{
if (fp->f_timeout > 0)
{
res = sm_read_wait(f_fd(*fp), fp->f_timeout);
if (sm_is_err(res))
return res;
}
errno = 0;
ret = read(f_fd(*fp), buf, nbytes);
} while (ret == -1 && errno == EINTR);
if (ret == -1)
return sm_error_perm(SM_EM_IO, errno);
*bytesread = ret;
/* if the read succeeded, update the current offset */
if (ret > 0)
fp->f_lseekoff += ret;
return SM_SUCCESS;
}
/*
** SM_CDBF_SEEK -- seek to a position in a buffered file
**
** Parameters:
** fp -- fp of file to seek
** offset -- position to seek to
** whence -- how to seek
**
** Returns:
** new file offset or -1 indicate failure
**
*/
static sm_ret_T
sm_cdbf_seek(sm_file_T *fp, off_t offset, int whence)
{
off_t off;
sm_cdbf_P sm_cdbf;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
SM_REQUIRE(sm_cdbf != NULL);
if (sm_cdbf->cdbf_ondisk)
{
off = lseek(f_fd(*fp), offset, whence);
if (off == (off_t) -1)
return sm_error_perm(SM_EM_IO, errno);
fp->f_lseekoff = off;
}
else
{
/* reset pointers in buffer ... */
}
return SM_SUCCESS;
}
/*
** SM_CDBF_WRITE -- write to a buffered file
**
** Parameters:
** fp -- fp of file to write
** buf -- data buffer
** nbytes -- how many bytes to write
** byteswritten -- number of bytes written (output)
**
** Returns:
** number of bytes written or -1 indicate failure
**
** Side Effects:
** may create backing file if over memory limit for file.
**
*/
static sm_ret_T
sm_cdbf_write(sm_file_T *fp, const uchar *buf, size_t nbytes, ssize_t *byteswritten)
{
ssize_t ret;
sm_ret_T res;
sm_cdbf_P sm_cdbf;
SM_REQUIRE(fp != NULL);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
SM_REQUIRE(sm_cdbf != NULL);
if (!is_valid_fd(f_fd(*fp)))
{
ret = cdbf_open(fp, sm_cdbf->cdbf_path, sm_cdbf->cdbf_oflags);
if (sm_is_err(ret))
return ret;
}
/* should we decrease the timeout in the loop? */
*byteswritten = 0;
do
{
if (fp->f_timeout > 0)
{
res = sm_write_wait(f_fd(*fp), fp->f_timeout);
if (sm_is_err(res))
return res;
}
errno = 0;
ret = write(f_fd(*fp), buf, nbytes);
} while (ret == -1 && errno == EINTR);
if (ret == -1)
return sm_error_perm(SM_EM_IO, errno);
*byteswritten = ret;
return SM_SUCCESS;
}
#if 0
/*
** BFREWIND -- rewinds the sm_file_T *
**
** Parameters:
** fp -- sm_file_T * to rewind
**
** Returns:
** usual sm_error code
**
** Side Effects:
** rewinds the sm_file_T * and puts it into read mode. Normally one
** would cdbfopen() a file, write to it, then cdbfrewind() and
** fread(). If fp is not a buffered file, this is equivalent to
** rewind().
*/
static sm_ret_T
cdbfrewind(sm_file_T *fp)
{
SM_IS_FP(fp);
(void) sm_io_flush(fp);
sm_io_clearerr(fp); /* quicker just to do it */
return sm_io_seek(fp, 0L, SM_IO_SEEK_SET);
}
#endif /* 0 */
#if 0
/*
** SM_CDBF_TRUNCATE -- rewinds and truncates the sm_file_T *
**
** Parameters:
** fp -- sm_file_T * to truncate
**
** Returns:
** usual sm_error code
**
** Side Effects:
** rewinds the sm_file_T *, truncates it to zero length, and puts
** it into write mode.
*/
static sm_ret_T
sm_cdbf_truncate(sm_file_T *fp)
{
sm_ret_T ret;
sm_cdbf_P sm_cdbf;
SM_IS_FP(fp);
ret = cdbfrewind(fp);
if (sm_is_err(ret))
return ret;
/* Get cdbf structure */
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
sm_cdbf->cdbf_buffilled = 0;
sm_cdbf->cdbf_size = 0;
if (sm_cdbf->cdbf_ondisk)
{
#if NOFTRUNCATE
/* XXX: Not much we can do except rewind it */
return sm_error_perm(SM_EM_IO, EINVAL);
#else
if (ftruncate(sm_cdbf->cdbf_disk_fd, (off_t) 0) < 0)
return sm_error_perm(SM_EM_IO, errno);
#endif
}
return SM_SUCCESS;
}
#endif /* 0 */
/*
** SM_CDBF_SETINFO -- set/change info for an open file pointer
**
** Parameters:
** fp -- file pointer to get info about
** what -- type of info to set/change
** valp -- thing to set/change the info to
**
*/
static sm_ret_T
sm_cdbf_setinfo(sm_file_T *fp, int what, void *valp)
{
sm_cdbf_P sm_cdbf;
sm_ret_T ret;
SM_IS_FP(fp);
sm_cdbf = (sm_cdbf_P) f_cookie(*fp);
switch (what)
{
case SM_IO_WHAT_ABORT:
ret = sm_cdbf_abort(fp);
return ret;
case SM_IO_WHAT_COMMIT:
ret = sm_cdbf_commit(fp, true);
return ret;
#if 0
case SM_IO_WHAT_BF_COMMIT:
return sm_cdbfcommit(fp, false);
case SM_IO_WHAT_BF_TRUNCATE:
return sm_cdbftruncate(fp);
case SM_IO_WHAT_BF_TEST:
return SM_SUCCESS; /* always */
#endif /* 0 */
default:
return sm_error_perm(SM_EM_IO, EINVAL);
}
/* NOTREACHED */
}
syntax highlighted by Code2HTML, v. 0.9.1