Impove temp file creation and handling, especially for memory mapped

temp files
This commit is contained in:
Jussi Laako 2012-10-15 10:24:10 +03:00 committed by Patrick Ohly
parent 976d30c24b
commit fa2b30fea1
5 changed files with 279 additions and 57 deletions

View file

@ -36,8 +36,6 @@
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <pcrecpp.h>
#include <algorithm>
@ -71,7 +69,7 @@ public:
typedef std::map<std::string, pcrecpp::StringPiece> Content;
typedef std::map<std::string, boost::variant<std::string> > 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<std::pair<GDBusCXX::DBusObject_t, Params> > pullall(*m_session, "PullAll");
std::pair<GDBusCXX::DBusObject_t, Params> tuple = pullall(std::string(tmpfile.getFilename()));
std::pair<GDBusCXX::DBusObject_t, Params> 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<unsigned> (tmpFile.size()));
content = tmpFile.stringPiece();
// closing tmp file leaves the mapping
tmpFile.close();
} else {
GDBusCXX::DBusClientCall1<std::string> pullall(*m_session, "PullAll");
buffer = pullall();
@ -403,9 +360,6 @@ PbapSyncSource::PbapSyncSource(const SyncSourceParams &params) :
PbapSyncSource::~PbapSyncSource()
{
if (m_memRange.data()) {
munmap(const_cast<char *>(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();
}

View file

@ -31,6 +31,7 @@
#include <pcrecpp.h>
#include <syncevo/declarations.h>
#include <syncevo/TmpFile.h>
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

135
src/syncevo/TmpFile.cpp Normal file
View file

@ -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 <cstdio>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <glib.h>
#include <glib/gstdio.h>
#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<int> (m_mapsize));
return sp;
}

129
src/syncevo/TmpFile.h Normal file
View file

@ -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 <stdexcept>
#include <string>
#include <pcrecpp.h>
/**
* 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

View file

@ -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 \