From fa2b30fea1e5e05f5da91a604ddd8565f59d645f Mon Sep 17 00:00:00 2001 From: Jussi Laako Date: Mon, 15 Oct 2012 10:24:10 +0300 Subject: [PATCH] Impove temp file creation and handling, especially for memory mapped temp files --- src/backends/pbap/PbapSyncSource.cpp | 66 ++----------- src/backends/pbap/PbapSyncSource.h | 3 +- src/syncevo/TmpFile.cpp | 135 +++++++++++++++++++++++++++ src/syncevo/TmpFile.h | 129 +++++++++++++++++++++++++ src/syncevo/syncevo.am | 3 + 5 files changed, 279 insertions(+), 57 deletions(-) create mode 100644 src/syncevo/TmpFile.cpp create mode 100644 src/syncevo/TmpFile.h diff --git a/src/backends/pbap/PbapSyncSource.cpp b/src/backends/pbap/PbapSyncSource.cpp index b0ffbe82..55838cc3 100644 --- a/src/backends/pbap/PbapSyncSource.cpp +++ b/src/backends/pbap/PbapSyncSource.cpp @@ -36,8 +36,6 @@ #include #include -#include -#include #include #include @@ -71,7 +69,7 @@ public: typedef std::map Content; typedef std::map > Params; - void pullAll(Content &dst, std::string &buffer, pcrecpp::StringPiece &memRange); + void pullAll(Content &dst, std::string &buffer, TmpFile &tmpFile); void shutdown(void); @@ -294,47 +292,14 @@ void PbapSession::initSession(const std::string &address, const std::string &for SE_LOG_DEBUG(NULL, NULL, "PBAP session initialized"); } -void PbapSession::pullAll(Content &dst, std::string &buffer, pcrecpp::StringPiece &memRange) +void PbapSession::pullAll(Content &dst, std::string &buffer, TmpFile &tmpFile) { pcrecpp::StringPiece content; if (m_newobex) { - char *addr; - class TmpFileGuard { - int m_fd; - PlainGStr m_filename; - public: - /** - * Create temporary file. Will be closed and unlinked when - * this instance destructs. - */ - TmpFileGuard(PbapSyncSource &parent) { - gchar *filename; - GErrorCXX gerror; - m_fd = g_file_open_tmp (NULL, &filename, gerror); - m_filename = filename; - if (m_fd == -1) { - parent.throwError(std::string("opening temporary file for PBAP: ") + - (gerror ? gerror->message : "unknown failure")); - } - } - ~TmpFileGuard() { - // Unlink before closing to avoid race condition - // (close, someone else opens file, we remove it). - if (remove(m_filename) == -1) { - // Continue despite error. - SE_LOG_ERROR(NULL, NULL, "Unable to remove temporary file %s: %s", - m_filename.get(), strerror(errno)); - } - close (m_fd); - } - - const char *getFilename() const { return m_filename; } - int getFD() const { return m_fd; } - } tmpfile(m_parent); - - SE_LOG_DEBUG(NULL, NULL, "Created temporary file for PullAll %s", tmpfile.getFilename()); + tmpFile.create(); + SE_LOG_DEBUG(NULL, NULL, "Created temporary file for PullAll %s", tmpFile.filename().c_str()); GDBusCXX::DBusClientCall1 > pullall(*m_session, "PullAll"); - std::pair tuple = pullall(std::string(tmpfile.getFilename())); + std::pair tuple = pullall(tmpFile.filename()); const GDBusCXX::DBusObject_t &transfer = tuple.first; const Params &properties = tuple.second; @@ -349,19 +314,11 @@ void PbapSession::pullAll(Content &dst, std::string &buffer, pcrecpp::StringPiec m_transferErrorMsg.c_str())); } - struct stat sb; - if (fstat(tmpfile.getFD(), &sb) == -1) { - m_parent.throwError("stat on PBAP temp file", errno); - } - SE_LOG_DEBUG(NULL, NULL, "Temporary file size is %ld", (long)sb.st_size); - - addr = (char*)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmpfile.getFD(), 0); - if (addr == MAP_FAILED) { - m_parent.throwError("mmap temporary file", errno); - } - memRange.set(addr, sb.st_size); - content = memRange; + SE_LOG_DEBUG(NULL, NULL, "Temporary file size is %u", static_cast (tmpFile.size())); + content = tmpFile.stringPiece(); + // closing tmp file leaves the mapping + tmpFile.close(); } else { GDBusCXX::DBusClientCall1 pullall(*m_session, "PullAll"); buffer = pullall(); @@ -403,9 +360,6 @@ PbapSyncSource::PbapSyncSource(const SyncSourceParams ¶ms) : PbapSyncSource::~PbapSyncSource() { - if (m_memRange.data()) { - munmap(const_cast(m_memRange.data()), m_memRange.size()); - } } std::string PbapSyncSource::getMimeType() const @@ -430,7 +384,7 @@ void PbapSyncSource::open() std::string address = database.substr(prefix.size()); m_session->initSession(address, getDatabaseFormat()); - m_session->pullAll(m_content, m_buffer, m_memRange); + m_session->pullAll(m_content, m_buffer, m_tmpFile); m_session->shutdown(); } diff --git a/src/backends/pbap/PbapSyncSource.h b/src/backends/pbap/PbapSyncSource.h index 9f9c1d58..57083faf 100644 --- a/src/backends/pbap/PbapSyncSource.h +++ b/src/backends/pbap/PbapSyncSource.h @@ -31,6 +31,7 @@ #include #include +#include SE_BEGIN_CXX class PbapSession; @@ -68,7 +69,7 @@ class PbapSyncSource : public TrackingSyncSource, private boost::noncopyable Content m_content; std::string m_buffer; - pcrecpp::StringPiece m_memRange; + TmpFile m_tmpFile; }; SE_END_CXX diff --git a/src/syncevo/TmpFile.cpp b/src/syncevo/TmpFile.cpp new file mode 100644 index 00000000..1cac4dc3 --- /dev/null +++ b/src/syncevo/TmpFile.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 + +#include "TmpFile.h" + + +TmpFile::TmpFile() : + m_fd(-1), + m_mapptr(0), + m_mapsize(0) +{ +} + + +TmpFile::~TmpFile() +{ + try { + unmap(); + close(); + } catch (std::exception &x) { + fprintf(stderr, "TmpFile::~TmpFile(): %s\n", x.what()); + } catch (...) { + fputs("TmpFile::~TmpFile(): unknown exception\n", stderr); + } +} + + +void TmpFile::create() +{ + gchar *filename = NULL; + GError *error = NULL; + + if (m_fd >= 0 || m_mapptr || m_mapsize) { + throw TmpFileException("TmpFile::create(): busy"); + } + m_fd = g_file_open_tmp(NULL, &filename, &error); + if (error != NULL) { + throw TmpFileException( + std::string("TmpFile::create(): g_file_open_tmp(): ") + + std::string(error->message)); + } + m_filename = filename; + g_free(filename); +} + + +void TmpFile::map(void **mapptr, size_t *mapsize) +{ + struct stat sb; + + if (m_mapptr || m_mapsize) { + throw TmpFileException("TmpFile::map(): busy"); + } + if (m_fd < 0) { + throw TmpFileException("TmpFile::map(): m_fd < 0"); + } + if (fstat(m_fd, &sb) != 0) { + throw TmpFileException("TmpFile::map(): fstat()"); + } + m_mapptr = mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, + m_fd, 0); + if (m_mapptr == MAP_FAILED) { + m_mapptr = 0; + throw TmpFileException("TmpFile::map(): mmap()"); + } + m_mapsize = sb.st_size; + + if (mapptr != NULL) { + *mapptr = m_mapptr; + } + if (mapsize != NULL) { + *mapsize = m_mapsize; + } +} + + +void TmpFile::unmap() +{ + if (m_mapptr && m_mapsize) { + munmap(m_mapptr, m_mapsize); + } + m_mapsize = 0; + m_mapptr = 0; +} + + +void TmpFile::close() +{ + if (!m_filename.empty()) { + unlink(m_filename.c_str()); + m_filename.clear(); + } + if (m_fd >= 0) { + ::close(m_fd); + m_fd = -1; + } +} + + +pcrecpp::StringPiece TmpFile::stringPiece() +{ + pcrecpp::StringPiece sp; + + if (!(m_mapptr && m_mapsize)) { + map(); + } + sp.set(m_mapptr, static_cast (m_mapsize)); + return sp; +} + diff --git a/src/syncevo/TmpFile.h b/src/syncevo/TmpFile.h new file mode 100644 index 00000000..fca1ef06 --- /dev/null +++ b/src/syncevo/TmpFile.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef INCL_SYNCEVOLUTION_TMPFILE +#define INCL_SYNCEVOLUTION_TMPFILE + +#include +#include + +#include + + +/** + * Exception class for TmpFile. + */ +class TmpFileException : public std::runtime_error +{ + public: + TmpFileException(const std::string &what) + : std::runtime_error(what) + { } +}; + + +/** + * Class for handling temporary files, either read/write access + * or memory mapped. + * + * Closing and removing a mapped file is supported by calling close() + * after map(). + */ +class TmpFile +{ + protected: + int m_fd; + void *m_mapptr; + size_t m_mapsize; + std::string m_filename; + + public: + TmpFile(); + virtual ~TmpFile(); + + /** + * Create a temporary file. + */ + void create(); + /** + * Map a view of file and optionally return pointer and/or size. + * + * File should already have a correct size. + * + * @param mapptr Pointer to variable for mapped pointer. (can be NULL) + * @param mapsize Pointer to variable for mapped size. (can be NULL) + */ + void map(void **mapptr = 0, size_t *mapsize = 0); + /** + * Unmap a view of file. + */ + void unmap(); + /** + * Remove and close the file. + * + * Calling this after map() will make the file disappear from + * filesystem but the mapping will be valid until unmapped or + * instance of this class is destroyed. + */ + void close(); + + /** + * Retrieve file name of the file. + * + * @return file name + */ + const std::string & filename() const + { return m_filename; } + /** + * Retrieve descriptor of the file. + * + * @return descriptor + */ + int fd() + { return m_fd; } + /** + * Size of the mapping. + * + * @return mapped size + */ + size_t size() const + { return m_mapsize; } + /** + * Pointer to the mapping. + * + * @return pointer to the mapping + */ + operator void *() + { return m_mapptr; } + /** + * @overload + */ + operator const void *() const + { return m_mapptr; } + + /** + * Retrieve pcrecpp::StringPiece object for the mapped view. + * + * @return pcrecpp::StringPiece of the mapped view + */ + pcrecpp::StringPiece stringPiece(); +}; + +#endif // INCL_SYNCEVOLUTION_TMPFILE + diff --git a/src/syncevo/syncevo.am b/src/syncevo/syncevo.am index 292f86e8..cf7cd190 100644 --- a/src/syncevo/syncevo.am +++ b/src/syncevo/syncevo.am @@ -62,6 +62,9 @@ src_syncevo_sources = \ src/syncevo/util.h \ src/syncevo/BoostHelper.h \ \ + src/syncevo/TmpFile.cpp \ + src/syncevo/TmpFile.h \ + \ src/syncevo/Timespec.h \ \ src/syncevo/lcs.h \