diff options
Diffstat (limited to 'plugins/Dbx_kyoto/src/kyotocabinet/kcfile.cc')
-rw-r--r-- | plugins/Dbx_kyoto/src/kyotocabinet/kcfile.cc | 2712 |
1 files changed, 2712 insertions, 0 deletions
diff --git a/plugins/Dbx_kyoto/src/kyotocabinet/kcfile.cc b/plugins/Dbx_kyoto/src/kyotocabinet/kcfile.cc new file mode 100644 index 0000000000..ce7f93583f --- /dev/null +++ b/plugins/Dbx_kyoto/src/kyotocabinet/kcfile.cc @@ -0,0 +1,2712 @@ +/************************************************************************************************* + * Filesystem abstraction + * Copyright (C) 2009-2012 FAL Labs + * This file is part of Kyoto Cabinet. + * 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, either version + * 3 of the License, or any later version. + * 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, see <http://www.gnu.org/licenses/>. + *************************************************************************************************/ + + +#include "kcfile.h" +#include "myconf.h" + +namespace kyotocabinet { // common namespace + + +/** + * Constants for implementation. + */ +namespace { +const int32_t FILEPERM = 00644; ///< default permission of a new file +const int32_t DIRPERM = 00755; ///< default permission of a new directory +const int32_t PATHBUFSIZ = 8192; ///< size of the path buffer +const int32_t IOBUFSIZ = 16384; ///< size of the IO buffer +const int64_t FILEMAXSIZ = INT64MAX - INT32MAX; // maximum size of a file +const char* const WALPATHEXT = "wal"; ///< extension of the WAL file +const char WALMAGICDATA[] = "KW\n"; ///< magic data of the WAL file +const uint8_t WALMSGMAGIC = 0xee; ///< magic data for WAL record +} + + +/** + * File internal. + */ +struct FileCore { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + Mutex alock; ///< attribute lock + TSDKey errmsg; ///< error message + ::HANDLE fh; ///< file handle + ::HANDLE mh; ///< map view handle + char* map; ///< mapped memory + int64_t msiz; ///< map size + int64_t lsiz; ///< logical size + int64_t psiz; ///< physical size + std::string path; ///< file path + bool recov; ///< flag of recovery + uint32_t omode; ///< open mode + ::HANDLE walfh; ///< file handle for WAL + int64_t walsiz; ///< size of WAL + bool tran; ///< whether in transaction + bool trhard; ///< whether hard transaction + int64_t trbase; ///< base offset of guarded region + int64_t trmsiz; ///< minimum size during transaction +#else + Mutex alock; ///< attribute lock + TSDKey errmsg; ///< error message + int32_t fd; ///< file descriptor + char* map; ///< mapped memory + int64_t msiz; ///< map size + int64_t lsiz; ///< logical size + int64_t psiz; ///< physical size + std::string path; ///< file path + bool recov; ///< flag of recovery + uint32_t omode; ///< open mode + int32_t walfd; ///< file descriptor for WAL + int64_t walsiz; ///< size of WAL + bool tran; ///< whether in transaction + bool trhard; ///< whether hard transaction + int64_t trbase; ///< base offset of guarded region + int64_t trmsiz; ///< minimum size during transaction +#endif +}; + + +/** + * WAL message. + */ +struct WALMessage { + int64_t off; ///< offset of the region + std::string body; ///< body data +}; + + +/** + * DirStream internal. + */ +struct DirStreamCore { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + Mutex alock; ///< attribute lock + ::HANDLE dh; ///< directory handle + std::string cur; ///< current file +#else + Mutex alock; ///< attribute lock + ::DIR* dh; ///< directory handle +#endif +}; + + +/** + * Set the error message. + * @param core the inner condition. + * @param msg the error message. + */ +static void seterrmsg(FileCore* core, const char* msg); + + +/** + * Get the path of the WAL file. + * @param path the path of the destination file. + * @return the path of the WAL file. + */ +static std::string walpath(const std::string& path); + + +/** + * Write a log message into the WAL file. + * @param core the inner condition. + * @param off the offset of the destination. + * @param size the size of the data region. + * @param base the base offset. + * @return true on success, or false on failure. + */ +static bool walwrite(FileCore *core, int64_t off, size_t size, int64_t base); + + +/** + * Apply log messages in the WAL file. + * @param core the inner condition. + * @return true on success, or false on failure. + */ +static bool walapply(FileCore* core); + + +/** + * Write data into a file. + * @param fd the file descriptor. + * @param off the offset of the destination. + * @param buf the pointer to the data region. + * @param size the size of the data region. + * @return true on success, or false on failure. + */ +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +static bool mywrite(::HANDLE fh, int64_t off, const void* buf, size_t size); +#else +static bool mywrite(int32_t fd, int64_t off, const void* buf, size_t size); +#endif + + +/** + * Read data from a file. + * @param fd the file descriptor. + * @param buf the pointer to the destination region. + * @param size the size of the data to be read. + * @return true on success, or false on failure. + */ +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +static size_t myread(::HANDLE fh, void* buf, size_t size); +#else +static size_t myread(int32_t fd, void* buf, size_t count); +#endif + + +/** + * System call emulation for Win32. + */ +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +static int64_t win_pwrite(::HANDLE fh, const void* buf, size_t count, int64_t offset); +static int64_t win_pread(::HANDLE fh, void* buf, size_t count, int64_t offset); +static int64_t win_write(::HANDLE fh, const void* buf, size_t count); +static int64_t win_read(::HANDLE fh, void* buf, size_t count); +static int32_t win_ftruncate(::HANDLE fh, int64_t length); +#endif + + +/** Path delimiter character. */ +const char File::PATHCHR = MYPATHCHR; + + +/** Path delimiter string. */ +const char* const File::PATHSTR = MYPATHSTR; + + +/** Extension delimiter character. */ +const char File::EXTCHR = MYEXTCHR; + + +/** Extension delimiter string. */ +const char* const File::EXTSTR = MYEXTSTR; + + +/** Current directory string. */ +const char* const File::CDIRSTR = MYCDIRSTR; + + +/** Parent directory string. */ +const char* const File::PDIRSTR = MYPDIRSTR; + + +/** + * Default constructor. + */ +File::File() : opq_(NULL) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = new FileCore; + core->fh = NULL; + core->mh = NULL; + core->map = NULL; + core->msiz = 0; + core->lsiz = 0; + core->psiz = 0; + core->recov = false; + core->omode = 0; + core->walfh = NULL; + core->walsiz = 0; + core->tran = false; + core->trhard = false; + core->trmsiz = 0; + opq_ = core; +#else + _assert_(true); + FileCore* core = new FileCore; + core->fd = -1; + core->map = NULL; + core->msiz = 0; + core->lsiz = 0; + core->psiz = 0; + core->recov = false; + core->omode = 0; + core->walfd = -1; + core->walsiz = 0; + core->tran = false; + core->trhard = false; + core->trmsiz = 0; + opq_ = core; +#endif +} + + +/** + * Destructor. + */ +File::~File() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = (FileCore*)opq_; + if (core->fh) close(); + delete core; +#else + _assert_(true); + FileCore* core = (FileCore*)opq_; + if (core->fd >= 0) close(); + delete core; +#endif +} + + +/** + * Get the last happened error information. + */ +const char* File::error() const { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = (FileCore*)opq_; + const char* msg = (const char*)core->errmsg.get(); + if (!msg) msg = "no error"; + return msg; +#else + _assert_(true); + FileCore* core = (FileCore*)opq_; + const char* msg = (const char*)core->errmsg.get(); + if (!msg) msg = "no error"; + return msg; +#endif +} + + +/** + * Open a file. + */ +bool File::open(const std::string& path, uint32_t mode, int64_t msiz) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(msiz >= 0 && msiz <= FILEMAXSIZ); + FileCore* core = (FileCore*)opq_; + ::DWORD amode = GENERIC_READ; + ::DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ::DWORD cmode = OPEN_EXISTING; + if (mode & OWRITER) { + amode |= GENERIC_WRITE; + if (mode & OCREATE) { + cmode = OPEN_ALWAYS; + if (mode & OTRUNCATE) cmode = CREATE_ALWAYS; + } else { + if (mode & OTRUNCATE) cmode = TRUNCATE_EXISTING; + } + } + ::HANDLE fh = ::CreateFileA(path.c_str(), amode, smode, NULL, cmode, + FILE_ATTRIBUTE_NORMAL, NULL); + if (!fh || fh == INVALID_HANDLE_VALUE) { + seterrmsg(core, "CreateFile failed"); + return false; + } + if (!(mode & ONOLOCK)) { + ::DWORD lmode = mode & OWRITER ? LOCKFILE_EXCLUSIVE_LOCK : 0; + if (mode & OTRYLOCK) lmode |= LOCKFILE_FAIL_IMMEDIATELY; + OVERLAPPED ol; + ol.Offset = INT32MAX; + ol.OffsetHigh = 0; + ol.hEvent = 0; + if (!::LockFileEx(fh, lmode, 0, 1, 0, &ol)) { + seterrmsg(core, "LockFileEx failed"); + ::CloseHandle(fh); + return false; + } + } + ::LARGE_INTEGER sbuf; + if (!::GetFileSizeEx(fh, &sbuf)) { + seterrmsg(core, "GetFileSizeEx failed"); + ::CloseHandle(fh); + return false; + } + bool recov = false; + if ((!(mode & OWRITER) || !(mode & OTRUNCATE)) && !(mode & ONOLOCK)) { + const std::string& wpath = walpath(path); + ::HANDLE walfh = ::CreateFileA(wpath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (walfh && walfh != INVALID_HANDLE_VALUE) { + recov = true; + ::LARGE_INTEGER li; + if (::GetFileSizeEx(walfh, &li) && li.QuadPart >= (int64_t)sizeof(WALMAGICDATA)) { + char mbuf[sizeof(WALMAGICDATA)]; + if (myread(walfh, mbuf, sizeof(mbuf)) && + !std::memcmp(mbuf, WALMAGICDATA, sizeof(WALMAGICDATA))) { + ::HANDLE ofh = fh; + if (!(mode & OWRITER)) ofh = ::CreateFileA(wpath.c_str(), GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (ofh && ofh != INVALID_HANDLE_VALUE) { + core->fh = ofh; + core->walfh = walfh; + walapply(core); + if (ofh != fh && !::CloseHandle(ofh)) seterrmsg(core, "CloseHandle failed"); + li.QuadPart = 0; + if (win_ftruncate(walfh, 0) != 0) seterrmsg(core, "win_ftruncate failed"); + core->fh = NULL; + core->walfh = NULL; + if (!::GetFileSizeEx(fh, &sbuf)) { + seterrmsg(core, "GetFileSizeEx failed"); + ::CloseHandle(fh); + return false; + } + } else { + seterrmsg(core, "CreateFile failed"); + } + } + } + if (!::CloseHandle(walfh)) seterrmsg(core, "CloseHandle failed"); + ::DeleteFileA(wpath.c_str()); + } + } + int64_t lsiz = sbuf.QuadPart; + int64_t psiz = lsiz; + int64_t diff = msiz % PAGESIZ; + if (diff > 0) msiz += PAGESIZ - diff; + ::DWORD mprot = PAGE_READONLY; + ::DWORD vmode = FILE_MAP_READ; + if (mode & OWRITER) { + mprot = PAGE_READWRITE; + vmode = FILE_MAP_WRITE; + } else if (msiz > lsiz) { + msiz = lsiz; + } + sbuf.QuadPart = msiz; + ::HANDLE mh = NULL; + void* map = NULL; + if (msiz > 0) { + mh = ::CreateFileMapping(fh, NULL, mprot, sbuf.HighPart, sbuf.LowPart, NULL); + if (!mh || mh == INVALID_HANDLE_VALUE) { + seterrmsg(core, "CreateFileMapping failed"); + ::CloseHandle(fh); + return false; + } + map = ::MapViewOfFile(mh, vmode, 0, 0, 0); + if (!map) { + seterrmsg(core, "MapViewOfFile failed"); + ::CloseHandle(mh); + ::CloseHandle(fh); + return false; + } + if (psiz < msiz) psiz = msiz; + } + core->fh = fh; + core->mh = mh; + core->map = (char*)map; + core->msiz = msiz; + core->lsiz = lsiz; + core->psiz = psiz; + core->recov = recov; + core->omode = mode; + core->path.append(path); + return true; +#else + _assert_(msiz >= 0 && msiz <= FILEMAXSIZ); + FileCore* core = (FileCore*)opq_; + int32_t oflags = O_RDONLY; + if (mode & OWRITER) { + oflags = O_RDWR; + if (mode & OCREATE) oflags |= O_CREAT; + if (mode & OTRUNCATE) oflags |= O_TRUNC; + } + int32_t fd = ::open(path.c_str(), oflags, FILEPERM); + if (fd < 0) { + switch (errno) { + case EACCES: seterrmsg(core, "open failed (permission denied)"); break; + case EISDIR: seterrmsg(core, "open failed (directory)"); break; + case ENOENT: seterrmsg(core, "open failed (file not found)"); break; + case ENOTDIR: seterrmsg(core, "open failed (invalid path)"); break; + case ENOSPC: seterrmsg(core, "open failed (no space)"); break; + default: seterrmsg(core, "open failed"); break; + } + return false; + } + if (!(mode & ONOLOCK)) { + struct flock flbuf; + std::memset(&flbuf, 0, sizeof(flbuf)); + flbuf.l_type = mode & OWRITER ? F_WRLCK : F_RDLCK; + flbuf.l_whence = SEEK_SET; + flbuf.l_start = 0; + flbuf.l_len = 0; + flbuf.l_pid = 0; + int32_t cmd = mode & OTRYLOCK ? F_SETLK : F_SETLKW; + while (::fcntl(fd, cmd, &flbuf) != 0) { + if (errno != EINTR) { + seterrmsg(core, "fcntl failed"); + ::close(fd); + return false; + } + } + } + struct ::stat sbuf; + if (::fstat(fd, &sbuf) != 0) { + seterrmsg(core, "fstat failed"); + ::close(fd); + return false; + } + if (!S_ISREG(sbuf.st_mode)) { + seterrmsg(core, "not a regular file"); + ::close(fd); + return false; + } + bool recov = false; + if ((!(mode & OWRITER) || !(mode & OTRUNCATE)) && !(mode & ONOLOCK)) { + const std::string& wpath = walpath(path); + int32_t walfd = ::open(wpath.c_str(), O_RDWR, FILEPERM); + if (walfd >= 0) { + struct ::stat wsbuf; + if (::fstat(walfd, &wsbuf) == 0 && wsbuf.st_uid == sbuf.st_uid) { + recov = true; + if (wsbuf.st_size >= (int64_t)sizeof(WALMAGICDATA)) { + char mbuf[sizeof(WALMAGICDATA)]; + if (myread(walfd, mbuf, sizeof(mbuf)) && + !std::memcmp(mbuf, WALMAGICDATA, sizeof(WALMAGICDATA))) { + int32_t ofd = mode & OWRITER ? fd : ::open(path.c_str(), O_WRONLY, FILEPERM); + if (ofd >= 0) { + core->fd = ofd; + core->walfd = walfd; + walapply(core); + if (ofd != fd && ::close(ofd) != 0) seterrmsg(core, "close failed"); + if (::ftruncate(walfd, 0) != 0) seterrmsg(core, "ftruncate failed"); + core->fd = -1; + core->walfd = -1; + if (::fstat(fd, &sbuf) != 0) { + seterrmsg(core, "fstat failed"); + ::close(fd); + return false; + } + } else { + seterrmsg(core, "open failed"); + } + } + } + } + if (::close(walfd) != 0) seterrmsg(core, "close failed"); + if (::unlink(wpath.c_str()) != 0) seterrmsg(core, "unlink failed"); + } + } + int64_t lsiz = sbuf.st_size; + int64_t psiz = lsiz; + int64_t diff = msiz % PAGESIZ; + if (diff > 0) msiz += PAGESIZ - diff; + int32_t mprot = PROT_READ; + if (mode & OWRITER) { + mprot |= PROT_WRITE; + } else if (msiz > lsiz) { + msiz = lsiz; + } + void* map = NULL; + if (msiz > 0) { + map = ::mmap(0, msiz, mprot, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + seterrmsg(core, "mmap failed"); + ::close(fd); + return false; + } + } + core->fd = fd; + core->map = (char*)map; + core->msiz = msiz; + core->lsiz = lsiz; + core->psiz = psiz; + core->recov = recov; + core->omode = mode; + core->path.append(path); + return true; +#endif +} + + +/** + * Close the file. + */ +bool File::close() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = (FileCore*)opq_; + bool err = false; + if (core->tran && !end_transaction(false)) err = true; + if (core->walfh) { + if (!::CloseHandle(core->walfh)) { + seterrmsg(core, "CloseHandle failed"); + err = true; + } + const std::string& wpath = walpath(core->path); + ::DeleteFileA(wpath.c_str()); + } + if (core->msiz > 0) { + if (!::UnmapViewOfFile(core->map)) { + seterrmsg(core, "UnmapViewOfFile failed"); + err = true; + } + if (!::CloseHandle(core->mh)) { + seterrmsg(core, "CloseHandle failed"); + err = true; + } + } + ::LARGE_INTEGER li; + if (::GetFileSizeEx(core->fh, &li)) { + if ((li.QuadPart != core->lsiz || core->psiz != core->lsiz) && + win_ftruncate(core->fh, core->lsiz) != 0) { + seterrmsg(core, "win_ftruncate failed"); + err = true; + } + } else { + seterrmsg(core, "GetFileSizeEx failed"); + err = true; + } + if (!(core->omode & ONOLOCK)) { + OVERLAPPED ol; + ol.Offset = INT32MAX; + ol.OffsetHigh = 0; + ol.hEvent = 0; + if (!::UnlockFileEx(core->fh, 0, 1, 0, &ol)) { + seterrmsg(core, "UnlockFileEx failed"); + err = true; + } + } + if (!::CloseHandle(core->fh)) { + seterrmsg(core, "CloseHandle failed"); + err = true; + } + core->fh = NULL; + core->mh = NULL; + core->map = NULL; + core->msiz = 0; + core->lsiz = 0; + core->psiz = 0; + core->path.clear(); + core->walfh = NULL; + core->walsiz = 0; + core->tran = false; + core->trhard = false; + core->trmsiz = 0; + return !err; +#else + _assert_(true); + FileCore* core = (FileCore*)opq_; + bool err = false; + if (core->tran && !end_transaction(false)) err = true; + if (core->walfd >= 0) { + if (::close(core->walfd) != 0) { + seterrmsg(core, "close failed"); + err = true; + } + const std::string& wpath = walpath(core->path); + struct ::stat sbuf; + if (::lstat(wpath.c_str(), &sbuf) == 0 && S_ISREG(sbuf.st_mode) && + ::unlink(wpath.c_str()) != 0) { + seterrmsg(core, "unlink failed"); + err = true; + } + } + if (core->msiz > 0 && ::munmap(core->map, core->msiz) != 0) { + seterrmsg(core, "munmap failed"); + err = true; + } + if (core->psiz != core->lsiz && ::ftruncate(core->fd, core->lsiz) != 0) { + seterrmsg(core, "ftruncate failed"); + err = true; + } + if (!(core->omode & ONOLOCK)) { + struct flock flbuf; + std::memset(&flbuf, 0, sizeof(flbuf)); + flbuf.l_type = F_UNLCK; + flbuf.l_whence = SEEK_SET; + flbuf.l_start = 0; + flbuf.l_len = 0; + flbuf.l_pid = 0; + while (::fcntl(core->fd, F_SETLKW, &flbuf) != 0) { + if (errno != EINTR) { + seterrmsg(core, "fcntl failed"); + err = true; + break; + } + } + } + if (::close(core->fd) != 0) { + seterrmsg(core, "close failed"); + err = true; + } + core->fd = -1; + core->map = NULL; + core->msiz = 0; + core->lsiz = 0; + core->psiz = 0; + core->path.clear(); + core->walfd = -1; + core->walsiz = 0; + core->tran = false; + core->trhard = false; + core->trmsiz = 0; + return !err; +#endif +} + + +/** + * Write data. + */ +bool File::write(int64_t off, const void* buf, size_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + if (size < 1) return true; + FileCore* core = (FileCore*)opq_; + if (core->tran && !walwrite(core, off, size, core->trbase)) return false; + int64_t end = off + size; + core->alock.lock(); + if (end <= core->msiz) { + if (end > core->psiz) { + int64_t psiz = end + core->psiz / 2; + int64_t diff = psiz % PAGESIZ; + if (diff > 0) psiz += PAGESIZ - diff; + if (psiz > core->msiz) psiz = core->msiz; + if (win_ftruncate(core->fh, psiz) != 0) { + seterrmsg(core, "win_ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = psiz; + } + if (end > core->lsiz) core->lsiz = end; + core->alock.unlock(); + std::memcpy(core->map + off, buf, size); + return true; + } + if (off < core->msiz) { + if (end > core->psiz) { + if (win_ftruncate(core->fh, end) != 0) { + seterrmsg(core, "win_ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = end; + } + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, buf, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + if (end > core->lsiz) core->lsiz = end; + if (end > core->psiz) { + if (core->psiz < core->msiz && win_ftruncate(core->fh, core->msiz) != 0) { + seterrmsg(core, "win_ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = end; + } + core->alock.unlock(); + if (!mywrite(core->fh, off, buf, size)) { + seterrmsg(core, "mywrite failed"); + return false; + } + return true; +#else + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + if (size < 1) return true; + FileCore* core = (FileCore*)opq_; + if (core->tran && !walwrite(core, off, size, core->trbase)) return false; + int64_t end = off + size; + core->alock.lock(); + if (end <= core->msiz) { + if (end > core->psiz) { + int64_t psiz = end + core->psiz / 2; + int64_t diff = psiz % PAGESIZ; + if (diff > 0) psiz += PAGESIZ - diff; + if (psiz > core->msiz) psiz = core->msiz; + if (::ftruncate(core->fd, psiz) != 0) { + seterrmsg(core, "ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = psiz; + } + if (end > core->lsiz) core->lsiz = end; + core->alock.unlock(); + std::memcpy(core->map + off, buf, size); + return true; + } + if (off < core->msiz) { + if (end > core->psiz) { + if (::ftruncate(core->fd, end) != 0) { + seterrmsg(core, "ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = end; + } + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, buf, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + if (end > core->lsiz) core->lsiz = end; + if (end > core->psiz) { + if (core->psiz < core->msiz && ::ftruncate(core->fd, core->msiz) != 0) { + seterrmsg(core, "ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = end; + } + core->alock.unlock(); + if (!mywrite(core->fd, off, buf, size)) { + seterrmsg(core, "mywrite failed"); + return false; + } + return true; +#endif +} + + +/** + * Write data with assuring the region does not spill from the file size. + */ +bool File::write_fast(int64_t off, const void* buf, size_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + FileCore* core = (FileCore*)opq_; + if (core->tran && !walwrite(core, off, size, core->trbase)) return false; + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(core->map + off, buf, size); + return true; + } + if (off < core->msiz) { + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, buf, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + if (!mywrite(core->fh, off, buf, size)) { + seterrmsg(core, "mywrite failed"); + return false; + } + return true; +#else + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + FileCore* core = (FileCore*)opq_; + if (core->tran && !walwrite(core, off, size, core->trbase)) return false; + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(core->map + off, buf, size); + return true; + } + if (off < core->msiz) { + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, buf, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + if (!mywrite(core->fd, off, buf, size)) { + seterrmsg(core, "mywrite failed"); + return false; + } + return true; +#endif +} + + +/** + * Write data at the end of the file. + */ +bool File::append(const void* buf, size_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(buf && size <= MEMMAXSIZ); + if (size < 1) return true; + FileCore* core = (FileCore*)opq_; + core->alock.lock(); + int64_t off = core->lsiz; + int64_t end = off + size; + if (end <= core->msiz) { + if (end > core->psiz) { + int64_t psiz = end + core->psiz / 2; + int64_t diff = psiz % PAGESIZ; + if (diff > 0) psiz += PAGESIZ - diff; + if (psiz > core->msiz) psiz = core->msiz; + if (win_ftruncate(core->fh, psiz) != 0) { + seterrmsg(core, "win_ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = psiz; + } + core->lsiz = end; + core->alock.unlock(); + std::memcpy(core->map + off, buf, size); + return true; + } + if (off < core->msiz) { + if (end > core->psiz) { + if (win_ftruncate(core->fh, end) != 0) { + seterrmsg(core, "win_ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = end; + } + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, buf, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + core->lsiz = end; + core->psiz = end; + core->alock.unlock(); + while (true) { + int64_t wb = win_pwrite(core->fh, buf, size, off); + if (wb >= (int64_t)size) { + return true; + } else if (wb > 0) { + buf = (char*)buf + wb; + size -= wb; + off += wb; + } else if (wb == -1) { + seterrmsg(core, "win_pwrite failed"); + return false; + } else if (size > 0) { + seterrmsg(core, "win_pwrite failed"); + return false; + } + } + return true; +#else + _assert_(buf && size <= MEMMAXSIZ); + if (size < 1) return true; + FileCore* core = (FileCore*)opq_; + core->alock.lock(); + int64_t off = core->lsiz; + int64_t end = off + size; + if (end <= core->msiz) { + if (end > core->psiz) { + int64_t psiz = end + core->psiz / 2; + int64_t diff = psiz % PAGESIZ; + if (diff > 0) psiz += PAGESIZ - diff; + if (psiz > core->msiz) psiz = core->msiz; + if (::ftruncate(core->fd, psiz) != 0) { + seterrmsg(core, "ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = psiz; + } + core->lsiz = end; + core->alock.unlock(); + std::memcpy(core->map + off, buf, size); + return true; + } + if (off < core->msiz) { + if (end > core->psiz) { + if (::ftruncate(core->fd, end) != 0) { + seterrmsg(core, "ftruncate failed"); + core->alock.unlock(); + return false; + } + core->psiz = end; + } + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, buf, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + core->lsiz = end; + core->psiz = end; + core->alock.unlock(); + while (true) { + ssize_t wb = ::pwrite(core->fd, buf, size, off); + if (wb >= (ssize_t)size) { + return true; + } else if (wb > 0) { + buf = (char*)buf + wb; + size -= wb; + off += wb; + } else if (wb == -1) { + if (errno != EINTR) { + seterrmsg(core, "pwrite failed"); + return false; + } + } else if (size > 0) { + seterrmsg(core, "pwrite failed"); + return false; + } + } + return true; +#endif +} + + +/** + * Read data. + */ +bool File::read(int64_t off, void* buf, size_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + if (size < 1) return true; + FileCore* core = (FileCore*)opq_; + int64_t end = off + size; + core->alock.lock(); + if (end > core->lsiz) { + seterrmsg(core, "out of bounds"); + core->alock.unlock(); + return false; + } + core->alock.unlock(); + if (end <= core->msiz) { + std::memcpy(buf, core->map + off, size); + return true; + } + if (off < core->msiz) { + int64_t hsiz = core->msiz - off; + std::memcpy(buf, core->map + off, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + while (true) { + int64_t rb = win_pread(core->fh, buf, size, off); + if (rb >= (int64_t)size) { + break; + } else if (rb > 0) { + buf = (char*)buf + rb; + size -= rb; + off += rb; + } else if (rb == -1) { + seterrmsg(core, "win_pread failed"); + return false; + } else if (size > 0) { + Thread::yield(); + } + } + return true; +#else + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + if (size < 1) return true; + FileCore* core = (FileCore*)opq_; + int64_t end = off + size; + core->alock.lock(); + if (end > core->lsiz) { + seterrmsg(core, "out of bounds"); + core->alock.unlock(); + return false; + } + core->alock.unlock(); + if (end <= core->msiz) { + std::memcpy(buf, core->map + off, size); + return true; + } + if (off < core->msiz) { + int64_t hsiz = core->msiz - off; + std::memcpy(buf, core->map + off, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + while (true) { + ssize_t rb = ::pread(core->fd, buf, size, off); + if (rb >= (ssize_t)size) { + break; + } else if (rb > 0) { + buf = (char*)buf + rb; + size -= rb; + off += rb; + } else if (rb == -1) { + if (errno != EINTR) { + seterrmsg(core, "pread failed"); + return false; + } + } else if (size > 0) { + Thread::yield(); + } + } + return true; +#endif +} + + +/** + * Read data with assuring the region does not spill from the file size. + */ +bool File::read_fast(int64_t off, void* buf, size_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + FileCore* core = (FileCore*)opq_; + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(buf, core->map + off, size); + return true; + } + if (off < core->msiz) { + int64_t hsiz = core->msiz - off; + std::memcpy(buf, core->map + off, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + while (true) { + int64_t rb = win_pread(core->fh, buf, size, off); + if (rb >= (int64_t)size) { + break; + } else if (rb > 0) { + buf = (char*)buf + rb; + size -= rb; + off += rb; + Thread::yield(); + } else if (rb == -1) { + seterrmsg(core, "win_pread failed"); + return false; + } else if (size > 0) { + if (end > core->lsiz) { + seterrmsg(core, "out of bounds"); + return false; + } + Thread::yield(); + } + } + return true; +#else + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + FileCore* core = (FileCore*)opq_; + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(buf, core->map + off, size); + return true; + } + if (off < core->msiz) { + int64_t hsiz = core->msiz - off; + std::memcpy(buf, core->map + off, hsiz); + off += hsiz; + buf = (char*)buf + hsiz; + size -= hsiz; + } + while (true) { + ssize_t rb = ::pread(core->fd, buf, size, off); + if (rb >= (ssize_t)size) { + break; + } else if (rb > 0) { + buf = (char*)buf + rb; + size -= rb; + off += rb; + Thread::yield(); + } else if (rb == -1) { + if (errno != EINTR) { + seterrmsg(core, "pread failed"); + return false; + } + } else if (size > 0) { + if (end > core->lsiz) { + seterrmsg(core, "out of bounds"); + return false; + } + Thread::yield(); + } + } + return true; +#endif +} + + +/** + * Truncate the file. + */ +bool File::truncate(int64_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(size >= 0 && size <= FILEMAXSIZ); + FileCore* core = (FileCore*)opq_; + if (core->tran && size < core->trmsiz) { + if (!walwrite(core, size, core->trmsiz - size, core->trbase)) return false; + core->trmsiz = size; + } + bool err = false; + core->alock.lock(); + if (core->msiz > 0) { + if (!::UnmapViewOfFile(core->map)) { + seterrmsg(core, "UnmapViewOfFile failed"); + err = true; + } + if (!::CloseHandle(core->mh)) { + seterrmsg(core, "CloseHandle failed"); + err = true; + } + } + if (win_ftruncate(core->fh, size) != 0) { + seterrmsg(core, "win_ftruncate failed"); + err = true; + } + if (core->msiz) { + ::LARGE_INTEGER li; + li.QuadPart = core->msiz; + ::HANDLE mh = ::CreateFileMapping(core->fh, NULL, PAGE_READWRITE, + li.HighPart, li.LowPart, NULL); + if (mh && mh != INVALID_HANDLE_VALUE) { + void* map = ::MapViewOfFile(mh, FILE_MAP_WRITE, 0, 0, 0); + if (map) { + core->mh = mh; + core->map = (char*)map; + } else { + seterrmsg(core, "MapViewOfFile failed"); + ::CloseHandle(mh); + core->mh = NULL; + core->map = NULL; + core->msiz = 0; + err = true; + } + } else { + seterrmsg(core, "CreateFileMapping failed"); + core->mh = NULL; + core->map = NULL; + core->msiz = 0; + err = true; + } + } + core->lsiz = size; + core->psiz = size; + core->alock.unlock(); + return !err; +#else + _assert_(size >= 0 && size <= FILEMAXSIZ); + FileCore* core = (FileCore*)opq_; + if (core->tran && size < core->trmsiz) { + if (!walwrite(core, size, core->trmsiz - size, core->trbase)) return false; + core->trmsiz = size; + } + bool err = false; + core->alock.lock(); + if (::ftruncate(core->fd, size) != 0) { + seterrmsg(core, "ftruncate failed"); + err = true; + } + core->lsiz = size; + core->psiz = size; + core->alock.unlock(); + return !err; +#endif +} + + +/** + * Synchronize updated contents with the file and the device. + */ +bool File::synchronize(bool hard) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = (FileCore*)opq_; + bool err = false; + core->alock.lock(); + if (hard && core->msiz > 0) { + int64_t msiz = core->msiz; + if (msiz > core->psiz) msiz = core->psiz; + if (msiz > 0 && !::FlushViewOfFile(core->map, msiz)) { + seterrmsg(core, "FlushViewOfFile failed"); + err = true; + } + } + if (win_ftruncate(core->fh, core->lsiz) != 0) { + seterrmsg(core, "win_ftruncate failed"); + err = true; + } + if (core->psiz > core->lsiz) core->psiz = core->lsiz; + if (hard && !::FlushFileBuffers(core->fh)) { + seterrmsg(core, "FlushFileBuffers failed"); + err = true; + } + core->alock.unlock(); + return !err; +#else + _assert_(true); + FileCore* core = (FileCore*)opq_; + bool err = false; + core->alock.lock(); + if (hard && core->msiz > 0) { + int64_t msiz = core->msiz; + if (msiz > core->psiz) msiz = core->psiz; + if (msiz > 0 && ::msync(core->map, msiz, MS_SYNC) != 0) { + seterrmsg(core, "msync failed"); + err = true; + } + } + if (::ftruncate(core->fd, core->lsiz) != 0) { + seterrmsg(core, "ftruncate failed"); + err = true; + } + if (core->psiz > core->lsiz) core->psiz = core->lsiz; + if (hard && ::fsync(core->fd) != 0) { + seterrmsg(core, "fsync failed"); + err = true; + } + core->alock.unlock(); + return !err; +#endif +} + + +/** + * Refresh the internal state for update by others. + */ +bool File::refresh() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = (FileCore*)opq_; + ::LARGE_INTEGER sbuf; + if (!::GetFileSizeEx(core->fh, &sbuf)) { + seterrmsg(core, "GetFileSizeEx failed"); + return false; + } + core->lsiz = sbuf.QuadPart; + core->psiz = sbuf.QuadPart; + return true; +#else + _assert_(true); + FileCore* core = (FileCore*)opq_; + struct ::stat sbuf; + if (::fstat(core->fd, &sbuf) != 0) { + seterrmsg(core, "fstat failed"); + return false; + } + core->lsiz = sbuf.st_size; + core->psiz = sbuf.st_size; + bool err = false; + int64_t msiz = core->msiz; + if (msiz > core->psiz) msiz = core->psiz; + if (msiz > 0 && ::msync(core->map, msiz, MS_INVALIDATE) != 0) { + seterrmsg(core, "msync failed"); + err = true; + } + return !err; +#endif +} + + +/** + * Begin transaction. + */ +bool File::begin_transaction(bool hard, int64_t off) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(off >= 0 && off <= FILEMAXSIZ); + FileCore* core = (FileCore*)opq_; + core->alock.lock(); + if (!core->walfh) { + const std::string& wpath = walpath(core->path); + ::DWORD amode = GENERIC_READ | GENERIC_WRITE; + ::DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ::HANDLE fh = ::CreateFileA(wpath.c_str(), amode, smode, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (!fh || fh == INVALID_HANDLE_VALUE) { + seterrmsg(core, "CreateFile failed"); + core->alock.unlock(); + return false; + } + if (hard && !::FlushFileBuffers(fh)) { + seterrmsg(core, "FlushFileBuffers failed"); + ::CloseHandle(fh); + core->alock.unlock(); + return false; + } + core->walfh = fh; + } + char wbuf[NUMBUFSIZ]; + char* wp = wbuf; + std::memcpy(wp, WALMAGICDATA, sizeof(WALMAGICDATA)); + wp += sizeof(WALMAGICDATA); + int64_t num = hton64(core->lsiz); + std::memcpy(wp, &num, sizeof(num)); + wp += sizeof(num); + int64_t wsiz = wp - wbuf; + if (!mywrite(core->walfh, 0, wbuf, wsiz)) { + seterrmsg(core, "mywrite failed"); + core->alock.unlock(); + return false; + } + core->walsiz = wsiz; + core->tran = true; + core->trhard = hard; + core->trbase = off; + core->trmsiz = core->lsiz; + core->alock.unlock(); + return true; +#else + _assert_(off >= 0 && off <= FILEMAXSIZ); + FileCore* core = (FileCore*)opq_; + core->alock.lock(); + if (core->walfd < 0) { + const std::string& wpath = walpath(core->path); + int32_t fd = ::open(wpath.c_str(), O_RDWR | O_CREAT | O_TRUNC, FILEPERM); + if (fd < 0) { + switch (errno) { + case EACCES: seterrmsg(core, "open failed (permission denied)"); break; + case ENOENT: seterrmsg(core, "open failed (file not found)"); break; + case ENOTDIR: seterrmsg(core, "open failed (invalid path)"); break; + default: seterrmsg(core, "open failed"); break; + } + core->alock.unlock(); + return false; + } + core->walfd = fd; + } + char wbuf[NUMBUFSIZ]; + char* wp = wbuf; + std::memcpy(wp, WALMAGICDATA, sizeof(WALMAGICDATA)); + wp += sizeof(WALMAGICDATA); + int64_t num = hton64(core->lsiz); + std::memcpy(wp, &num, sizeof(num)); + wp += sizeof(num); + int64_t wsiz = wp - wbuf; + if (!mywrite(core->walfd, 0, wbuf, wsiz)) { + seterrmsg(core, "mywrite failed"); + core->alock.unlock(); + return false; + } + core->walsiz = wsiz; + core->tran = true; + core->trhard = hard; + core->trbase = off; + core->trmsiz = core->lsiz; + core->alock.unlock(); + return true; +#endif +} + + +/** + * Commit transaction. + */ +bool File::end_transaction(bool commit) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + FileCore* core = (FileCore*)opq_; + bool err = false; + core->alock.lock(); + if (!commit && !walapply(core)) err = true; + if (!err) { + if (core->walsiz <= IOBUFSIZ) { + char mbuf[IOBUFSIZ]; + std::memset(mbuf, 0, core->walsiz); + if (!mywrite(core->walfh, 0, mbuf, core->walsiz)) { + seterrmsg(core, "mywrite failed"); + err = true; + } + } else { + if (win_ftruncate(core->walfh, 0) != 0) { + seterrmsg(core, "win_ftruncate failed"); + err = true; + } + } + } + if (core->trhard) { + int64_t msiz = core->msiz; + if (msiz > core->psiz) msiz = core->psiz; + if (msiz > 0 && !::FlushViewOfFile(core->map, msiz)) { + seterrmsg(core, "FlushViewOfFile failed"); + err = true; + } + if (!::FlushFileBuffers(core->fh)) { + seterrmsg(core, "FlushFileBuffers failed"); + err = true; + } + if (!::FlushFileBuffers(core->walfh)) { + seterrmsg(core, "FlushFileBuffers failed"); + err = true; + } + } + core->tran = false; + core->alock.unlock(); + return !err; +#else + _assert_(true); + FileCore* core = (FileCore*)opq_; + bool err = false; + core->alock.lock(); + if (!commit && !walapply(core)) err = true; + if (!err) { + if (core->walsiz <= IOBUFSIZ) { + char mbuf[IOBUFSIZ]; + std::memset(mbuf, 0, core->walsiz); + if (!mywrite(core->walfd, 0, mbuf, core->walsiz)) { + seterrmsg(core, "mywrite failed"); + err = true; + } + } else { + if (::ftruncate(core->walfd, 0) != 0) { + seterrmsg(core, "ftruncate failed"); + err = true; + } + } + } + if (core->trhard) { + int64_t msiz = core->msiz; + if (msiz > core->psiz) msiz = core->psiz; + if (msiz > 0 && ::msync(core->map, msiz, MS_SYNC) != 0) { + seterrmsg(core, "msync failed"); + err = true; + } + if (::fsync(core->fd) != 0) { + seterrmsg(core, "fsync failed"); + err = true; + } + if (::fsync(core->walfd) != 0) { + seterrmsg(core, "fsync failed"); + err = true; + } + } + core->tran = false; + core->alock.unlock(); + return !err; +#endif +} + + +/** + * Write a WAL message of transaction explicitly. + */ +bool File::write_transaction(int64_t off, size_t size) { + _assert_(off >= 0 && off <= FILEMAXSIZ && size <= MEMMAXSIZ); + FileCore* core = (FileCore*)opq_; + return walwrite(core, off, size, 0); +} + + +/** + * Get the size of the file. + */ +int64_t File::size() const { + _assert_(true); + FileCore* core = (FileCore*)opq_; + return core->lsiz; +} + + +/** + * Get the path of the file. + */ +std::string File::path() const { + _assert_(true); + FileCore* core = (FileCore*)opq_; + return core->path; +} + + +/** + * Check whether the file was recovered or not. + */ +bool File::recovered() const { + _assert_(true); + FileCore* core = (FileCore*)opq_; + return core->recov; +} + + +/** + * Read the whole data from a file. + */ +char* File::read_file(const std::string& path, int64_t* sp, int64_t limit) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(sp); + if (limit < 0) limit = INT64MAX; + ::DWORD amode = GENERIC_READ; + ::DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ::DWORD cmode = OPEN_EXISTING; + ::HANDLE fh = ::CreateFileA(path.c_str(), amode, smode, NULL, cmode, + FILE_ATTRIBUTE_NORMAL, NULL); + if (!fh || fh == INVALID_HANDLE_VALUE) return NULL; + ::LARGE_INTEGER sbuf; + if (!::GetFileSizeEx(fh, &sbuf)) { + ::CloseHandle(fh); + return false; + } + if (limit > (int64_t)sbuf.QuadPart) limit = sbuf.QuadPart; + char* buf = new char[limit+1]; + char* wp = buf; + int64_t rsiz; + while ((rsiz = win_read(fh, wp, limit - (wp - buf))) > 0) { + wp += rsiz; + } + *wp = '\0'; + ::CloseHandle(fh); + *sp = wp - buf; + return buf; +#else + _assert_(sp); + if (limit < 0) limit = INT64MAX; + int32_t fd = ::open(path.c_str(), O_RDONLY, FILEPERM); + if (fd < 0) return NULL; + struct stat sbuf; + if (::fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)) { + ::close(fd); + return NULL; + } + if (limit > (int64_t)sbuf.st_size) limit = sbuf.st_size; + char* buf = new char[limit+1]; + char* wp = buf; + ssize_t rsiz; + while ((rsiz = ::read(fd, wp, limit - (wp - buf))) > 0) { + wp += rsiz; + } + *wp = '\0'; + ::close(fd); + *sp = wp - buf; + return buf; +#endif +} + + +/** + * Write the whole data into a file. + */ +bool File::write_file(const std::string& path, const char* buf, int64_t size) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(buf && size >= 0 && size <= FILEMAXSIZ); + ::DWORD amode = GENERIC_WRITE; + ::DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ::DWORD cmode = CREATE_ALWAYS; + double wsec = 1.0 / CLOCKTICK; + ::HANDLE fh = INVALID_HANDLE_VALUE; + for (int32_t i = 0; i < 10; i++) { + fh = ::CreateFileA(path.c_str(), amode, smode, NULL, cmode, FILE_ATTRIBUTE_NORMAL, NULL); + if (fh && fh != INVALID_HANDLE_VALUE) break; + if (::GetLastError() != ERROR_ACCESS_DENIED) return false; + if (wsec > 1.0) wsec = 1.0; + Thread::sleep(wsec); + wsec *= 2; + } + bool err = false; + const char* rp = buf; + while (!err && size > 0) { + int64_t wb = win_write(fh, rp, size); + switch (wb) { + case -1: { + if (errno != EINTR) { + err = true; + break; + } + } + case 0: { + break; + } + default: { + rp += wb; + size -= wb; + break; + } + } + } + if (!::CloseHandle(fh)) err = true; + return !err; +#else + _assert_(buf && size >= 0 && size <= FILEMAXSIZ); + int32_t fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, FILEPERM); + if (fd < 0) return false; + bool err = false; + const char* rp = buf; + while (!err && size > 0) { + ssize_t wb = ::write(fd, rp, size); + switch (wb) { + case -1: { + if (errno != EINTR) { + err = true; + break; + } + } + case 0: { + break; + } + default: { + rp += wb; + size -= wb; + break; + } + } + } + if (::close(fd) != 0) err = true; + return !err; +#endif +} + + +/** + * Get the status information of a file. + */ +bool File::status(const std::string& path, Status* buf) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + ::WIN32_FILE_ATTRIBUTE_DATA ibuf; + if (!::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &ibuf)) return false; + if (buf) { + buf->isdir = ibuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + ::LARGE_INTEGER li; + li.LowPart = ibuf.nFileSizeLow; + li.HighPart = ibuf.nFileSizeHigh; + buf->size = li.QuadPart; + li.LowPart = ibuf.ftLastWriteTime.dwLowDateTime; + li.HighPart = ibuf.ftLastWriteTime.dwHighDateTime; + buf->mtime = li.QuadPart; + } + return true; +#else + _assert_(true); + struct ::stat sbuf; + if (::lstat(path.c_str(), &sbuf) != 0) return false; + if (buf) { + buf->isdir = S_ISDIR(sbuf.st_mode); + buf->size = sbuf.st_size; + buf->mtime = sbuf.st_mtime; + } + return true; +#endif +} + + +/** + * Get the absolute path of a file. + */ +std::string File::absolute_path(const std::string& path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + char buf[PATHBUFSIZ]; + ::DWORD size = ::GetFullPathNameA(path.c_str(), sizeof(buf), buf, NULL); + if (size < 1) return ""; + if (size < sizeof(buf)) return std::string(buf); + char* lbuf = new char[size]; + ::DWORD nsiz = ::GetFullPathNameA(path.c_str(), size, lbuf, NULL); + if (nsiz < 1 || nsiz >= size) { + delete[] lbuf; + return ""; + } + std::string rbuf(lbuf); + delete[] lbuf; + return rbuf; +#else + _assert_(true); + char buf[PATHBUFSIZ]; + if (!realpath(path.c_str(), buf)) return ""; + return std::string(buf); +#endif +} + + +/** + * Remove a file. + */ +bool File::remove(const std::string& path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + double wsec = 1.0 / CLOCKTICK; + for (int32_t i = 0; i < 10; i++) { + if (::DeleteFileA(path.c_str())) return true; + if (::GetLastError() != ERROR_ACCESS_DENIED) return false; + if (wsec > 1.0) wsec = 1.0; + Thread::sleep(wsec); + wsec *= 2; + } + std::string tmppath; + strprintf(&tmppath, "%s%ctmp%c%llx", path.c_str(), EXTCHR, EXTCHR, + ((unsigned long long)(time() * UINT16MAX)) % UINT32MAX); + if (::MoveFileExA(path.c_str(), tmppath.c_str(), MOVEFILE_REPLACE_EXISTING)) { + ::DeleteFileA(tmppath.c_str()); + ::DWORD amode = GENERIC_READ | GENERIC_WRITE; + ::DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ::HANDLE fh = ::CreateFileA(tmppath.c_str(), amode, smode, NULL, OPEN_EXISTING, + FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (fh && fh != INVALID_HANDLE_VALUE) ::CloseHandle(fh); + return true; + } + return false; +#else + _assert_(true); + return ::unlink(path.c_str()) == 0; +#endif +} + + +/** + * Change the name or location of a file. + */ +bool File::rename(const std::string& opath, const std::string& npath) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + double wsec = 1.0 / CLOCKTICK; + for (int32_t i = 0; i < 10; i++) { + if (::MoveFileExA(opath.c_str(), npath.c_str(), MOVEFILE_REPLACE_EXISTING)) return true; + if (::GetLastError() != ERROR_ACCESS_DENIED) return false; + if (wsec > 1.0) wsec = 1.0; + Thread::sleep(wsec); + wsec *= 2; + } + std::string tmppath; + strprintf(&tmppath, "%s%ctmp%c%llx", npath.c_str(), EXTCHR, EXTCHR, + ((unsigned long long)(time() * UINT16MAX)) % UINT32MAX); + if (::MoveFileExA(npath.c_str(), tmppath.c_str(), MOVEFILE_REPLACE_EXISTING)) { + if (::MoveFileExA(opath.c_str(), npath.c_str(), MOVEFILE_REPLACE_EXISTING)) { + ::DeleteFileA(tmppath.c_str()); + ::DWORD amode = GENERIC_READ | GENERIC_WRITE; + ::DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ::HANDLE fh = ::CreateFileA(tmppath.c_str(), amode, smode, NULL, OPEN_EXISTING, + FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (fh && fh != INVALID_HANDLE_VALUE) ::CloseHandle(fh); + return true; + } else { + wsec = 1.0 / CLOCKTICK; + for (int32_t i = 0; i < 10; i++) { + if (::MoveFileExA(tmppath.c_str(), npath.c_str(), MOVEFILE_REPLACE_EXISTING)) break; + if (wsec > 1.0) wsec = 1.0; + Thread::sleep(wsec); + wsec *= 2; + } + } + } + return false; +#else + _assert_(true); + return ::rename(opath.c_str(), npath.c_str()) == 0; +#endif +} + + +/** + * Read a directory. + */ +bool File::read_directory(const std::string& path, std::vector<std::string>* strvec) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(strvec); + std::string dpath = path; + size_t plen = path.size(); + if (plen < 1 || path[plen-1] != PATHCHR) dpath.append(PATHSTR); + dpath.append("*"); + ::WIN32_FIND_DATAA fbuf; + ::HANDLE dh = ::FindFirstFileA(dpath.c_str(), &fbuf); + if (!dh || dh == INVALID_HANDLE_VALUE) return false; + if (std::strcmp(fbuf.cFileName, CDIRSTR) && std::strcmp(fbuf.cFileName, PDIRSTR)) + strvec->push_back(fbuf.cFileName); + while (::FindNextFileA(dh, &fbuf)) { + if (std::strcmp(fbuf.cFileName, CDIRSTR) && std::strcmp(fbuf.cFileName, PDIRSTR)) + strvec->push_back(fbuf.cFileName); + } + if (!::FindClose(dh)) return false; + return true; +#else + _assert_(strvec); + ::DIR* dir = ::opendir(path.c_str()); + if (!dir) return false; + struct ::dirent *dp; + while ((dp = ::readdir(dir)) != NULL) { + if (std::strcmp(dp->d_name, CDIRSTR) && std::strcmp(dp->d_name, PDIRSTR)) + strvec->push_back(dp->d_name); + } + if (::closedir(dir) != 0) return false; + return true; +#endif +} + + +/** + * Make a directory. + */ +bool File::make_directory(const std::string& path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + double wsec = 1.0 / CLOCKTICK; + for (int32_t i = 0; i < 10; i++) { + if (::CreateDirectoryA(path.c_str(), NULL)) return true; + if (::GetLastError() != ERROR_ACCESS_DENIED) return false; + if (wsec > 1.0) wsec = 1.0; + Thread::sleep(wsec); + wsec *= 2; + } + return false; +#else + _assert_(true); + return ::mkdir(path.c_str(), DIRPERM) == 0; +#endif +} + + +/** + * Remove a directory. + */ +bool File::remove_directory(const std::string& path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + double wsec = 1.0 / CLOCKTICK; + for (int32_t i = 0; i < 10; i++) { + if (::RemoveDirectoryA(path.c_str())) return true; + if (::GetLastError() != ERROR_ACCESS_DENIED) return false; + if (wsec > 1.0) wsec = 1.0; + Thread::sleep(wsec); + wsec *= 2; + } + return false; +#else + _assert_(true); + return ::rmdir(path.c_str()) == 0; +#endif +} + + +/** + * Remove a file or a directory recursively. + */ +bool File::remove_recursively(const std::string& path) { + bool err = false; + std::vector<std::string> list; + list.push_back(path); + while (!list.empty()) { + const std::string& cpath = list.back(); + Status sbuf; + if (status(cpath, &sbuf)) { + if (sbuf.isdir) { + if (remove_directory(cpath)) { + list.pop_back(); + } else { + DirStream dir; + if (dir.open(cpath)) { + std::string ccname; + while (dir.read(&ccname)) { + const std::string& ccpath = cpath + MYPATHCHR + ccname; + if (!remove(ccpath)) list.push_back(ccpath); + } + if (!dir.close()) err = true; + } else { + list.pop_back(); + err = true; + } + } + } else { + if (!remove(cpath)) err = true; + list.pop_back(); + } + } else { + list.pop_back(); + err = true; + } + } + return !err; +} + + +/** + * Get the path of the current working directory. + */ +std::string File::get_current_directory() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + char buf[PATHBUFSIZ]; + ::DWORD size = ::GetCurrentDirectoryA(sizeof(buf), buf); + if (size < 1) return ""; + if (size < sizeof(buf)) return std::string(buf); + char* lbuf = new char[size]; + ::DWORD nsiz = ::GetCurrentDirectoryA(size, lbuf); + if (nsiz < 1 || nsiz >= size) { + delete[] lbuf; + return ""; + } + std::string rbuf(lbuf); + delete[] lbuf; + return rbuf; +#else + _assert_(true); + char buf[PATHBUFSIZ]; + if (!::getcwd(buf, sizeof(buf))) return ""; + return std::string(buf); +#endif +} + + +/** + * Set the current working directory. + */ +bool File::set_current_directory(const std::string& path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + return ::SetCurrentDirectoryA(path.c_str()); +#else + _assert_(true); + return ::chdir(path.c_str()) == 0; +#endif +} + + +/** + * Synchronize the whole of the file system with the device. + */ +bool File::synchronize_whole() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + return true; +#else + _assert_(true); + ::sync(); + return true; +#endif +} + + + +/** + * Default constructor. + */ +DirStream::DirStream() : opq_(NULL) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + DirStreamCore* core = new DirStreamCore; + core->dh = NULL; + opq_ = core; +#else + _assert_(true); + DirStreamCore* core = new DirStreamCore; + core->dh = NULL; + opq_ = core; +#endif +} + + +/** + * Destructor. + */ +DirStream::~DirStream() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + DirStreamCore* core = (DirStreamCore*)opq_; + if (core->dh) close(); + delete core; +#else + _assert_(true); + DirStreamCore* core = (DirStreamCore*)opq_; + if (core->dh) close(); + delete core; +#endif +} + + +/** + * Open a directory. + */ +bool DirStream::open(const std::string& path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + DirStreamCore* core = (DirStreamCore*)opq_; + ScopedMutex lock(&core->alock); + if (core->dh) return false; + std::string dpath = path; + size_t plen = path.size(); + if (plen < 1 || path[plen-1] != File::PATHCHR) dpath.append(File::PATHSTR); + dpath.append("*"); + ::WIN32_FIND_DATAA fbuf; + ::HANDLE dh = ::FindFirstFileA(dpath.c_str(), &fbuf); + if (!dh || dh == INVALID_HANDLE_VALUE) return false; + core->dh = dh; + core->cur = fbuf.cFileName; + return true; +#else + _assert_(true); + DirStreamCore* core = (DirStreamCore*)opq_; + ScopedMutex lock(&core->alock); + if (core->dh) return false; + ::DIR* dh = ::opendir(path.c_str()); + if (!dh) return false; + core->dh = dh; + return true; +#endif +} + + +/** + * Close the file. + */ +bool DirStream::close() { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(true); + DirStreamCore* core = (DirStreamCore*)opq_; + ScopedMutex lock(&core->alock); + if (!core->dh) return false; + bool err = false; + if (!::FindClose(core->dh)) err = true; + core->dh = NULL; + core->cur.clear(); + return !err; +#else + _assert_(true); + DirStreamCore* core = (DirStreamCore*)opq_; + ScopedMutex lock(&core->alock); + if (!core->dh) return false; + bool err = false; + if (::closedir(core->dh) != 0) err = true; + core->dh = NULL; + return !err; +#endif +} + + +/** + * Read the next file in the directory. + */ +bool DirStream::read(std::string* path) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(path); + DirStreamCore* core = (DirStreamCore*)opq_; + ScopedMutex lock(&core->alock); + if (!core->dh) return false; + while (core->cur == File::CDIRSTR || core->cur == File::PDIRSTR) { + ::WIN32_FIND_DATAA fbuf; + if (::FindNextFileA(core->dh, &fbuf)) { + core->cur = fbuf.cFileName; + } else { + core->cur.clear(); + return false; + } + } + if (core->cur.empty()) return false; + path->clear(); + path->append(core->cur); + ::WIN32_FIND_DATAA fbuf; + if (::FindNextFileA(core->dh, &fbuf)) { + core->cur = fbuf.cFileName; + } else { + core->cur.clear(); + } + return true; +#else + _assert_(path); + DirStreamCore* core = (DirStreamCore*)opq_; + ScopedMutex lock(&core->alock); + if (!core->dh) return false; + struct ::dirent *dp; + do { + dp = ::readdir(core->dh); + if (!dp) return false; + } while (!std::strcmp(dp->d_name, File::CDIRSTR) || !std::strcmp(dp->d_name, File::PDIRSTR)); + path->clear(); + path->append(dp->d_name); + return true; +#endif +} + + +/** + * Set the error message. + */ +static void seterrmsg(FileCore* core, const char* msg) { + _assert_(core && msg); + core->errmsg.set((void*)msg); +} + + +/** + * Get the path of the WAL file. + */ +static std::string walpath(const std::string& path) { + _assert_(true); + return path + File::EXTCHR + WALPATHEXT; +} + + +/** + * Write a log message into the WAL file. + */ +static bool walwrite(FileCore *core, int64_t off, size_t size, int64_t base) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(core && off >= 0 && off <= FILEMAXSIZ && size <= MEMMAXSIZ && base >= 0); + bool err = false; + if (off < base) { + int64_t diff = base - off; + if (diff >= (int64_t)size) return true; + off = base; + size -= diff; + } + int64_t rem = core->trmsiz - off; + if (rem < 1) return true; + if (rem < (int64_t)size) size = rem; + char stack[IOBUFSIZ]; + size_t rsiz = sizeof(int8_t) + sizeof(int64_t) * 2 + size; + char* rbuf = rsiz > sizeof(stack) ? new char[rsiz] : stack; + char* wp = rbuf; + *(wp++) = WALMSGMAGIC; + int64_t num = hton64(off); + std::memcpy(wp, &num, sizeof(num)); + wp += sizeof(num); + num = hton64(size); + std::memcpy(wp, &num, sizeof(num)); + wp += sizeof(num); + core->alock.lock(); + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(wp, core->map + off, size); + } else { + if (off < core->msiz) { + int64_t hsiz = core->msiz - off; + std::memcpy(wp, core->map + off, hsiz); + off += hsiz; + wp += hsiz; + size -= hsiz; + } + while (true) { + int64_t rb = win_pread(core->fh, wp, size, off); + if (rb >= (int64_t)size) { + break; + } else if (rb > 0) { + wp += rb; + size -= rb; + off += rb; + } else { + err = true; + break; + } + } + if (err) { + seterrmsg(core, "win_pread failed"); + std::memset(wp, 0, size); + } + } + if (!mywrite(core->walfh, core->walsiz, rbuf, rsiz)) { + seterrmsg(core, "mywrite failed"); + err = true; + } + if (core->trhard && !::FlushFileBuffers(core->walfh)) { + seterrmsg(core, "FlushFileBuffers failed"); + err = true; + } + core->walsiz += rsiz; + if (rbuf != stack) delete[] rbuf; + core->alock.unlock(); + return !err; +#else + _assert_(core && off >= 0 && off <= FILEMAXSIZ && size <= MEMMAXSIZ && base >= 0); + bool err = false; + if (off < base) { + int64_t diff = base - off; + if (diff >= (int64_t)size) return true; + off = base; + size -= diff; + } + int64_t rem = core->trmsiz - off; + if (rem < 1) return true; + if (rem < (int64_t)size) size = rem; + char stack[IOBUFSIZ]; + size_t rsiz = sizeof(int8_t) + sizeof(int64_t) * 2 + size; + char* rbuf = rsiz > sizeof(stack) ? new char[rsiz] : stack; + char* wp = rbuf; + *(wp++) = WALMSGMAGIC; + int64_t num = hton64(off); + std::memcpy(wp, &num, sizeof(num)); + wp += sizeof(num); + num = hton64(size); + std::memcpy(wp, &num, sizeof(num)); + wp += sizeof(num); + core->alock.lock(); + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(wp, core->map + off, size); + } else { + if (off < core->msiz) { + int64_t hsiz = core->msiz - off; + std::memcpy(wp, core->map + off, hsiz); + off += hsiz; + wp += hsiz; + size -= hsiz; + } + while (true) { + ssize_t rb = ::pread(core->fd, wp, size, off); + if (rb >= (ssize_t)size) { + break; + } else if (rb > 0) { + wp += rb; + size -= rb; + off += rb; + } else if (rb == -1) { + if (errno != EINTR) { + err = true; + break; + } + } else { + err = true; + break; + } + } + if (err) { + seterrmsg(core, "pread failed"); + std::memset(wp, 0, size); + } + } + if (!mywrite(core->walfd, core->walsiz, rbuf, rsiz)) { + seterrmsg(core, "mywrite failed"); + err = true; + } + if (core->trhard && ::fsync(core->walfd) != 0) { + seterrmsg(core, "fsync failed"); + err = true; + } + core->walsiz += rsiz; + if (rbuf != stack) delete[] rbuf; + core->alock.unlock(); + return !err; +#endif +} + + +/** + * Apply log messages in the WAL file. + */ +static bool walapply(FileCore* core) { +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) + _assert_(core); + bool err = false; + char buf[IOBUFSIZ]; + int64_t hsiz = sizeof(WALMAGICDATA) + sizeof(int64_t); + ::LARGE_INTEGER li; + if (!::GetFileSizeEx(core->walfh, &li)) { + seterrmsg(core, "GetFileSizeEx failed"); + return false; + } + int64_t rem = li.QuadPart; + if (rem < hsiz) { + seterrmsg(core, "too short WAL file"); + return false; + } + li.QuadPart = 0; + if (!::SetFilePointerEx(core->walfh, li, NULL, FILE_BEGIN)) { + seterrmsg(core, "SetFilePointerEx failed"); + return false; + } + if (!myread(core->walfh, buf, hsiz)) { + seterrmsg(core, "myread failed"); + return false; + } + if (*buf == 0) return true; + if (std::memcmp(buf, WALMAGICDATA, sizeof(WALMAGICDATA))) { + seterrmsg(core, "invalid magic data of WAL"); + return false; + } + int64_t osiz; + std::memcpy(&osiz, buf + sizeof(WALMAGICDATA), sizeof(osiz)); + osiz = ntoh64(osiz); + rem -= hsiz; + hsiz = sizeof(uint8_t) + sizeof(int64_t) * 2; + std::vector<WALMessage> msgs; + int64_t end = 0; + while (rem >= hsiz) { + if (!myread(core->walfh, buf, hsiz)) { + seterrmsg(core, "myread failed"); + err = true; + break; + } + if (*buf == 0) { + rem = 0; + break; + } + rem -= hsiz; + char* rp = buf; + if (*(uint8_t*)(rp++) != WALMSGMAGIC) { + seterrmsg(core, "invalid magic data of WAL message"); + err = true; + break; + } + if (rem > 0) { + int64_t off; + std::memcpy(&off, rp, sizeof(off)); + off = ntoh64(off); + rp += sizeof(off); + int64_t size; + std::memcpy(&size, rp, sizeof(size)); + size = ntoh64(size); + rp += sizeof(size); + if (off < 0 || size < 0) { + seterrmsg(core, "invalid meta data of WAL message"); + err = true; + break; + } + if (rem < size) { + seterrmsg(core, "too short WAL message"); + err = true; + break; + } + char* rbuf = size > (int64_t)sizeof(buf) ? new char[size] : buf; + if (!myread(core->walfh, rbuf, size)) { + seterrmsg(core, "myread failed"); + if (rbuf != buf) delete[] rbuf; + err = true; + break; + } + rem -= size; + WALMessage msg = { off, std::string(rbuf, size) }; + msgs.push_back(msg); + if (off + size > end) end = off + size; + if (rbuf != buf) delete[] rbuf; + } + } + if (rem != 0) { + if (!myread(core->walfh, buf, 1)) { + seterrmsg(core, "myread failed"); + err = true; + } else if (*buf != 0) { + seterrmsg(core, "too few messages of WAL"); + err = true; + } + } + if (end > core->msiz) end = core->msiz; + if (core->psiz < end && win_ftruncate(core->fh, end) != 0) { + seterrmsg(core, "win_ftruncate failed"); + err = true; + } + for (int64_t i = (int64_t)msgs.size() - 1; i >= 0; i--) { + const WALMessage& msg = msgs[i]; + int64_t off = msg.off; + const char* rbuf = msg.body.c_str(); + size_t size = msg.body.size(); + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(core->map + off, rbuf, size); + } else { + if (off < core->msiz) { + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, rbuf, hsiz); + off += hsiz; + rbuf += hsiz; + size -= hsiz; + } + while (true) { + int64_t wb = win_pwrite(core->fh, rbuf, size, off); + if (wb >= (int64_t)size) { + break; + } else if (wb > 0) { + rbuf += wb; + size -= wb; + off += wb; + } else if (wb == -1) { + seterrmsg(core, "win_pwrite failed"); + err = true; + break; + } else if (size > 0) { + seterrmsg(core, "pwrite failed"); + err = true; + break; + } + } + } + } + if (win_ftruncate(core->fh, osiz) == 0) { + core->lsiz = osiz; + core->psiz = osiz; + } else { + seterrmsg(core, "win_ftruncate failed"); + err = true; + } + return !err; +#else + _assert_(core); + bool err = false; + char buf[IOBUFSIZ]; + int64_t hsiz = sizeof(WALMAGICDATA) + sizeof(int64_t); + int64_t rem = ::lseek(core->walfd, 0, SEEK_END); + if (rem < hsiz) { + seterrmsg(core, "lseek failed"); + return false; + } + if (::lseek(core->walfd, 0, SEEK_SET) != 0) { + seterrmsg(core, "lseek failed"); + return false; + } + if (!myread(core->walfd, buf, hsiz)) { + seterrmsg(core, "myread failed"); + return false; + } + if (*buf == 0) return true; + if (std::memcmp(buf, WALMAGICDATA, sizeof(WALMAGICDATA))) { + seterrmsg(core, "invalid magic data of WAL"); + return false; + } + int64_t osiz; + std::memcpy(&osiz, buf + sizeof(WALMAGICDATA), sizeof(osiz)); + osiz = ntoh64(osiz); + rem -= hsiz; + hsiz = sizeof(uint8_t) + sizeof(int64_t) * 2; + std::vector<WALMessage> msgs; + int64_t end = 0; + while (rem >= hsiz) { + if (!myread(core->walfd, buf, hsiz)) { + seterrmsg(core, "myread failed"); + err = true; + break; + } + if (*buf == 0) { + rem = 0; + break; + } + rem -= hsiz; + char* rp = buf; + if (*(uint8_t*)(rp++) != WALMSGMAGIC) { + seterrmsg(core, "invalid magic data of WAL message"); + err = true; + break; + } + if (rem > 0) { + int64_t off; + std::memcpy(&off, rp, sizeof(off)); + off = ntoh64(off); + rp += sizeof(off); + int64_t size; + std::memcpy(&size, rp, sizeof(size)); + size = ntoh64(size); + rp += sizeof(size); + if (off < 0 || size < 0) { + seterrmsg(core, "invalid meta data of WAL message"); + err = true; + break; + } + if (rem < size) { + seterrmsg(core, "too short WAL message"); + err = true; + break; + } + char* rbuf = size > (int64_t)sizeof(buf) ? new char[size] : buf; + if (!myread(core->walfd, rbuf, size)) { + seterrmsg(core, "myread failed"); + if (rbuf != buf) delete[] rbuf; + err = true; + break; + } + rem -= size; + WALMessage msg = { off, std::string(rbuf, size) }; + msgs.push_back(msg); + if (off + size > end) end = off + size; + if (rbuf != buf) delete[] rbuf; + } + } + if (rem != 0) { + if (!myread(core->walfd, buf, 1)) { + seterrmsg(core, "myread failed"); + err = true; + } else if (*buf != 0) { + seterrmsg(core, "too few messages of WAL"); + err = true; + } + } + if (end > core->msiz) end = core->msiz; + if (core->psiz < end && ::ftruncate(core->fd, end) != 0) { + seterrmsg(core, "ftruncate failed"); + err = true; + } + for (int64_t i = (int64_t)msgs.size() - 1; i >= 0; i--) { + const WALMessage& msg = msgs[i]; + int64_t off = msg.off; + const char* rbuf = msg.body.c_str(); + size_t size = msg.body.size(); + int64_t end = off + size; + if (end <= core->msiz) { + std::memcpy(core->map + off, rbuf, size); + } else { + if (off < core->msiz) { + size_t hsiz = core->msiz - off; + std::memcpy(core->map + off, rbuf, hsiz); + off += hsiz; + rbuf += hsiz; + size -= hsiz; + } + while (true) { + ssize_t wb = ::pwrite(core->fd, rbuf, size, off); + if (wb >= (ssize_t)size) { + break; + } else if (wb > 0) { + rbuf += wb; + size -= wb; + off += wb; + } else if (wb == -1) { + if (errno != EINTR) { + seterrmsg(core, "pwrite failed"); + err = true; + break; + } + } else if (size > 0) { + seterrmsg(core, "pwrite failed"); + err = true; + break; + } + } + } + } + if (::ftruncate(core->fd, osiz) == 0) { + core->lsiz = osiz; + core->psiz = osiz; + } else { + seterrmsg(core, "ftruncate failed"); + err = true; + } + return !err; +#endif +} + + +/** + * Write data into a file. + */ +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +static bool mywrite(::HANDLE fh, int64_t off, const void* buf, size_t size) { + _assert_(off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + while (true) { + int64_t wb = win_pwrite(fh, buf, size, off); + if (wb >= (int64_t)size) { + return true; + } else if (wb > 0) { + buf = (char*)buf + wb; + size -= wb; + off += wb; + } else if (wb == -1) { + return false; + } else if (size > 0) { + return false; + } + } + return true; +} +#else +static bool mywrite(int32_t fd, int64_t off, const void* buf, size_t size) { + _assert_(fd >= 0 && off >= 0 && off <= FILEMAXSIZ && buf && size <= MEMMAXSIZ); + while (true) { + ssize_t wb = ::pwrite(fd, buf, size, off); + if (wb >= (ssize_t)size) { + return true; + } else if (wb > 0) { + buf = (char*)buf + wb; + size -= wb; + off += wb; + } else if (wb == -1) { + if (errno != EINTR) return false; + } else if (size > 0) { + return false; + } + } + return true; +} +#endif + + +/** + * Read data from a file. + */ +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +static size_t myread(::HANDLE fh, void* buf, size_t size) { + _assert_(buf && size <= MEMMAXSIZ); + while (true) { + int64_t rb = win_read(fh, buf, size); + if (rb >= (int64_t)size) { + break; + } else if (rb > 0) { + buf = (char*)buf + rb; + size -= rb; + } else if (rb == -1) { + return false; + } else if (size > 0) { + return false; + } + } + return true; +} +#else +static size_t myread(int32_t fd, void* buf, size_t size) { + _assert_(fd >= 0 && buf && size <= MEMMAXSIZ); + while (true) { + ssize_t rb = ::read(fd, buf, size); + if (rb >= (ssize_t)size) { + break; + } else if (rb > 0) { + buf = (char*)buf + rb; + size -= rb; + } else if (rb == -1) { + if (errno != EINTR) return false; + } else if (size > 0) { + return false; + } + } + return true; +} +#endif + + +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +/** + * Emulate the pwrite call + */ +static int64_t win_pwrite(::HANDLE fh, const void* buf, size_t count, int64_t offset) { + _assert_(buf && count <= MEMMAXSIZ && offset >= 0 && offset <= FILEMAXSIZ); + ::DWORD wb; + ::LARGE_INTEGER li; + li.QuadPart = offset; + ::OVERLAPPED ol; + ol.Offset = li.LowPart; + ol.OffsetHigh = li.HighPart; + ol.hEvent = NULL; + if (!::WriteFile(fh, buf, count, &wb, &ol)) return -1; + return wb; +} +#endif + + +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +/** + * Emulate the pread call + */ +static int64_t win_pread(::HANDLE fh, void* buf, size_t count, int64_t offset) { + _assert_(buf && count <= MEMMAXSIZ && offset >= 0 && offset <= FILEMAXSIZ); + ::DWORD rb; + ::LARGE_INTEGER li; + li.QuadPart = offset; + ::OVERLAPPED ol; + ol.Offset = li.LowPart; + ol.OffsetHigh = li.HighPart; + ol.hEvent = NULL; + if (!::ReadFile(fh, buf, count, &rb, &ol)) return -1; + return rb; +} +#endif + + +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +/** + * Emulate the write call + */ +static int64_t win_write(::HANDLE fh, const void* buf, size_t count) { + _assert_(buf && count <= MEMMAXSIZ); + ::DWORD wb; + if (!::WriteFile(fh, buf, count, &wb, NULL)) return -1; + return wb; +} +#endif + + +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +/** + * Emulate the read call + */ +static int64_t win_read(::HANDLE fh, void* buf, size_t count) { + _assert_(buf && count <= MEMMAXSIZ); + ::DWORD rb; + if (!::ReadFile(fh, buf, count, &rb, NULL)) return -1; + return rb; +} +#endif + + +#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) +/** + * Emulate the ftruncate call + */ +static int32_t win_ftruncate(::HANDLE fh, int64_t length) { + _assert_(length >= 0 && length <= FILEMAXSIZ); + ::LARGE_INTEGER li; + li.QuadPart = length; + if (!::SetFilePointerEx(fh, li, NULL, FILE_BEGIN)) return -1; + if (!::SetEndOfFile(fh) && ::GetLastError() != 1224) return -1; + return 0; +} +#endif + + +} // common namespace + +// END OF FILE |