#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 */ }