/* Copyright (C) 2014 InfiniDB, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "utility.h" #include "BufferedFile.h" #include "IDBLogger.h" using namespace std; namespace idbdatafile { BufferedFile::BufferedFile(const char* fname, const char* mode, unsigned opts) : IDBDataFile(fname), m_fp(0), m_buffer(0) { m_fp = fopen(fname, mode); if (m_fp == NULL) { throw std::runtime_error("unable to open Buffered file "); } applyOptions(opts); } void BufferedFile::applyOptions(unsigned opts) { if (opts & IDBDataFile::USE_VBUF) { const int DEFAULT_BUFSIZ = 1 * 1024 * 1024; m_buffer = new char[DEFAULT_BUFSIZ]; setvbuf(m_fp, m_buffer, _IOFBF, DEFAULT_BUFSIZ); } else if (opts & IDBDataFile::USE_NOVBUF) { setvbuf(m_fp, NULL, _IONBF, 0); } } BufferedFile::~BufferedFile() { close(); m_fp = 0; delete[] m_buffer; } ssize_t BufferedFile::pread(void* ptr, off64_t offset, size_t count) { ssize_t ret = 0; int savedErrno; ssize_t curpos = tell(); seek(offset, SEEK_SET); ret = read(ptr, count); savedErrno = errno; seek(curpos, SEEK_SET); if (IDBLogger::isEnabled()) IDBLogger::logRW("pread", m_fname, this, offset, count, ret); errno = savedErrno; return ret; } ssize_t BufferedFile::read(void* ptr, size_t count) { ssize_t ret = 0; ssize_t offset = tell(); int savedErrno = -1; size_t progress = 0; uint8_t* ptr8 = (uint8_t*)ptr; while (progress < count) { ret = fread(ptr8 + progress, 1, count - progress, m_fp); savedErrno = errno; if (ret <= 0) { if (ferror(m_fp)) { errno = savedErrno; return -1; } else if (feof(m_fp)) return progress; } progress += ret; } if (IDBLogger::isEnabled()) IDBLogger::logRW("read", m_fname, this, offset, count, progress); errno = savedErrno; return progress; } ssize_t BufferedFile::write(const void* ptr, size_t count) { ssize_t ret = 0; off64_t offset = tell(); int savedErrno = 0; size_t progress = 0; uint8_t* ptr8 = (uint8_t*)ptr; while (progress < count) { ret = fwrite(ptr8 + progress, 1, count - progress, m_fp); savedErrno = errno; if (ret <= 0 && ferror(m_fp)) { errno = savedErrno; return -1; } else if (ret > 0) progress += ret; // can fwrite() continually return 0 with no error? } if (IDBLogger::isEnabled()) IDBLogger::logRW("write", m_fname, this, offset, count, progress); errno = savedErrno; return progress; } int BufferedFile::seek(off64_t offset, int whence) { int ret = 0; int savedErrno; ret = fseek(m_fp, offset, whence); savedErrno = errno; if (IDBLogger::isEnabled()) IDBLogger::logSeek(m_fname, this, offset, whence, ret); errno = savedErrno; return ret; } int BufferedFile::truncate(off64_t length) { int ret = 0; int savedErrno; ret = ftruncate(fileno(m_fp), length); savedErrno = errno; if (IDBLogger::isEnabled()) IDBLogger::logTruncate(m_fname, this, length, ret); errno = savedErrno; return ret; } off64_t BufferedFile::size() { // going to calculate size 2 ways - first, via seek off64_t length = -1; off64_t here; flockfile(m_fp); try { if ((here = ftell(m_fp)) > -1) { if (fseek(m_fp, 0, SEEK_END) > -1) { length = ftell(m_fp); fseek(m_fp, here, SEEK_SET); } } funlockfile(m_fp); } catch (...) { funlockfile(m_fp); } return length; } off64_t BufferedFile::tell() { return ftell(m_fp); } int BufferedFile::flush() { int rc = fflush(m_fp); int savedErrno = errno; if (rc == 0) { rc = fsync(fileno(m_fp)); savedErrno = errno; } if (IDBLogger::isEnabled()) IDBLogger::logNoArg(m_fname, this, "flush", rc); errno = savedErrno; return rc; } time_t BufferedFile::mtime() { time_t ret = 0; struct stat statbuf; if (::fstat(fileno(m_fp), &statbuf) == 0) ret = statbuf.st_mtime; else ret = (time_t)-1; return ret; } int BufferedFile::close() { int ret = fclose(m_fp); int savedErrno = errno; if (IDBLogger::isEnabled()) IDBLogger::logNoArg(m_fname, this, "close", ret); errno = savedErrno; return ret; } /** @brief The wrapper for fallocate function. @see This one is used in shared/we_fileop.cpp to skip expensive file preallocation. */ int BufferedFile::fallocate(int mode, off64_t offset, off64_t length) { int ret = 0; int savedErrno = 0; ret = ::fallocate(fileno(m_fp), mode, offset, length); savedErrno = errno; if (IDBLogger::isEnabled()) { IDBLogger::logNoArg(m_fname, this, "fallocate", errno); } errno = savedErrno; return ret; } } // namespace idbdatafile