Add class for threadsafe temp file handling

FileName::tempName() is not thread safe, since the QTemporaryFile object is
immediately deleted after creating it. Therefore, another thread could create
the same temporary file in the time span before the user of FileName::tempName()
recreates it. This is not as theoretical as it may look: I observed that
repeated creation and deletion of QTemporaryFile objects always use the same
name.
This problem is solved by the new class TempFile which should be used like
QTemporaryFile itself.
This commit is contained in:
Georg Baum 2013-04-14 18:17:56 +02:00
parent f6ca8350cd
commit db0ba3a3c6
5 changed files with 142 additions and 1 deletions

View File

@ -442,6 +442,7 @@ static string createTempFile(QString const & mask)
// Therefore the next call to createTempFile() may create the
// same file again. To make this safe the QTemporaryFile object
// needs to be kept for the whole life time of the temp file name.
// This can be achieved by using the TempFile class.
QTemporaryFile qt_tmp(mask);
if (qt_tmp.open()) {
string const temp_file = fromqstr(qt_tmp.fileName());

View File

@ -170,10 +170,12 @@ public:
void changeExtension(std::string const & extension);
static FileName fromFilesystemEncoding(std::string const & name);
/// (securely) create a temporary file with the given mask.
/// Create a temporary file with the given mask.
/// \p mask must be in filesystem encoding, if it contains a
/// relative path, the template file will be created in the global
/// temporary directory as given by 'package().temp_dir()'.
/// CAUTION: This method may create race conditions.
/// Do not use, use the TempFile class instead.
static FileName tempName(std::string const & mask);
static FileName tempName(FileName const & temp_dir,
std::string const & mask);

View File

@ -94,6 +94,8 @@ liblyxsupport_a_SOURCES = \
Systemcall.h \
SystemcallPrivate.h \
shared_ptr.h \
TempFile.cpp \
TempFile.h \
textutils.h \
Translator.h \
Timeout.cpp \

78
src/support/TempFile.cpp Normal file
View File

@ -0,0 +1,78 @@
/**
* \file TempFile.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Georg Baum
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "support/TempFile.h"
#include "support/debug.h"
#include "support/FileName.h"
#include "support/Package.h"
#include "support/qstring_helpers.h"
#include <QFileInfo>
#include <QDir>
#include <QTemporaryFile>
using namespace std;
namespace lyx {
namespace support {
struct TempFile::Private
{
///
Private(QString const & mask) : f(mask)
{
LYXERR(Debug::FILES, "Temporary file in " << fromqstr(mask));
if (f.open())
LYXERR(Debug::FILES, "Temporary file `"
<< fromqstr(f.fileName()) << "' created.");
else
LYXERR(Debug::FILES, "Unable to create temporary file with following template: "
<< f.fileTemplate());
}
///
QTemporaryFile f;
};
TempFile::TempFile(FileName const & temp_dir, string const & mask)
{
QFileInfo tmp_fi(QDir(toqstr(temp_dir.absoluteFilePath())), toqstr(mask));
d = new Private(tmp_fi.absoluteFilePath());
}
TempFile::TempFile(string const & mask)
{
QFileInfo tmp_fi(QDir(toqstr(package().temp_dir().absoluteFilePath())), toqstr(mask));
d = new Private(tmp_fi.absoluteFilePath());
}
TempFile::~TempFile()
{
delete d;
}
FileName TempFile::name() const
{
QString const n = d->f.fileName();
if (n.isNull())
return FileName();
return FileName(fromqstr(n));
}
} // namespace support
} // namespace lyx

58
src/support/TempFile.h Normal file
View File

@ -0,0 +1,58 @@
// -*- C++ -*-
/**
* \file TempFile.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Georg Baum
*
* Full author contact details are available in file CREDITS.
*/
#ifndef TEMPFILE_H
#define TEMPFILE_H
#include <string>
namespace lyx {
namespace support {
class FileName;
/**
* Class for safely creating temporary files without race conditions.
* The file is created in the constructor, and deleted in the destructor.
* You may do anything with the file (including deletion), but the instance
* of this class must stay alive as long as the file is needed.
*/
class TempFile {
public:
/**
*Create a temporary file with the given mask.
* \p mask must be in filesystem encoding, if it contains a
* relative path, the template file will be created in the global
* temporary directory as given by 'package().temp_dir()'.
* If the mask contains "XXXXXX" this portion will be replaced by
* a uniquely generetd string. If it does not contain this portion,
* it will be automatically appended using a dot. Therefore, please
* specify the "XXXXXX" portion if the extension of the generated
* name is important (e.g. for the converter machinery).
*/
TempFile(std::string const & mask);
TempFile(FileName const & temp_dir, std::string const & mask);
~TempFile();
/**
* Get the name of the temporary file.
* This is empty if the file could not be created.
*/
FileName name() const;
private:
///
struct Private;
Private * d;
};
} // namespace support
} // namespace lyx
#endif