Commit 9cbc986d authored by Denis Zalevskiy's avatar Denis Zalevskiy

Merge pull request #17 from nemomobile/lock-repo

Lock repository when working with it
parents 03a379c4 14305ea7
......@@ -8,6 +8,7 @@
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/
#include <qtaround/os.hpp>
#include <functional>
#include <QString>
......@@ -18,7 +19,8 @@
namespace vault {
enum class File { Message, VersionTree, VersionRepo, State };
enum class File { Message, VersionTree, VersionRepo, State, Lock
, Last_ = Lock };
QString fileName(File);
......@@ -36,6 +38,10 @@ private:
Gittin::Tag m_tag;
};
typedef std::shared_ptr<qtaround::os::FileLock> Lock;
typedef std::weak_ptr<qtaround::os::FileLock> Barrier;
class Vault
{
public:
......@@ -85,6 +91,8 @@ public:
bool ensureValid();
void reset(const QByteArray &treeish = QByteArray());
Lock lock() const;
private:
bool setState(const QString &state);
bool backupUnit(const QString &home, const QString &unit, const ProgressCallback &callback);
......@@ -93,7 +101,7 @@ private:
void resetMaster();
void setup(const QVariantMap *config);
QString absolutePath(QString const &);
QString absolutePath(QString const &) const;
QString readFile(const QString &relPath);
void setVersion(File, int);
......@@ -103,6 +111,7 @@ private:
const QString m_blobStorage;
Gittin::Repo m_vcs;
config::Vault m_config;
mutable Barrier m_barrier;
};
}
......
......@@ -14,6 +14,7 @@ BuildRequires: pkgconfig(gittin)
BuildRequires: pkgconfig(tut) >= 0.0.3
BuildRequires: pkgconfig(Qt5Core) >= 5.2.0
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(qtaround) >= 0.2.3
%{?_with_usersession:Requires: systemd-user-session-targets}
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
......
......@@ -210,6 +210,8 @@ void CardTransfer::validateDump(QString const &archive, QVariantMap const &err)
void CardTransfer::exportStorage(CardTransfer::progressCallback onProgress)
{
auto v = getVault();
auto l = v ? v->lock() : nullptr;
auto tag_fname = vault::fileName(File::State);
QStringList options = {"-cf", dst_, "-C", src_, ".git", tag_fname};
onProgress({{"type", "stage"}, {"stage", "Copy"}});
......
......@@ -37,6 +37,7 @@ static const QMap<File, QString> fileNames = {
, {File::VersionTree, ".vault"}
, {File::VersionRepo, os::path::join(".git", "vault.version")}
, {File::State, ".vault.state"}
, {File::Lock, ".vault.lock"}
};
QString fileName(File id)
......@@ -144,6 +145,7 @@ int Vault::execute(const QVariantMap &options)
}
Vault vault(options.value("vault").toString());
auto l = vault.lock();
QStringList units{str(options.value("unit")).split(",", QString::SkipEmptyParts)};
auto unitsResult = [](Result &&res) {
......@@ -232,19 +234,22 @@ int Vault::getVersion(File src)
return readFile(fileName(src)).toInt();
}
QString Vault::absolutePath(QString const &relativePath)
QString Vault::absolutePath(QString const &relativePath) const
{
return os::path::join(m_path, relativePath);
}
void Vault::setup(const QVariantMap *config)
{
auto createRepo = [this]() {
debug::debug("Setup vault", config ? *config : QVariantMap{});
auto l = lock();
auto createRepo = [this, &l]() {
debug::debug("Creating repo at", m_path);
if (!os::path::exists(m_path))
if (!os::mkdir(m_path))
error::raise({{"msg", "Can't create repo dir"}, {"path", m_path}});
l = lock();
if (!m_vcs.init())
error::raise({{"msg", "Can't init git repo"}, {"path", m_path}});
};
......@@ -352,6 +357,7 @@ void Vault::setup(const QVariantMap *config)
bool Vault::init(const QVariantMap &config)
{
try {
auto l = lock();
setup(&config);
return true;
} catch (std::exception const &e) {
......@@ -364,6 +370,7 @@ bool Vault::init(const QVariantMap &config)
bool Vault::ensureValid()
{
auto l = lock();
if (!os::path::exists(absolutePath(".git"))) {
debug::info("Can't find .git", m_path);
return false;
......@@ -387,6 +394,7 @@ bool Vault::ensureValid()
Vault::Result Vault::backup(const QString &home, const QStringList &units, const QString &message, const ProgressCallback &callback)
{
debug::info("Backup units", units, ", home", home);
auto l = lock();
Result res;
res.failedUnits << units;
......@@ -433,6 +441,7 @@ Vault::Result Vault::backup(const QString &home, const QStringList &units, const
bool Vault::clear(const QVariantMap &options)
{
auto l = lock();
if (!os::path::isDir(m_path)) {
debug::info("vault.clear:", "Path", m_path, "is not a dir");
return false;
......@@ -465,6 +474,7 @@ bool Vault::clear(const QVariantMap &options)
void Vault::reset(const QByteArray &treeish)
{
auto l = lock();
m_vcs.clean(CleanOptions::Force | CleanOptions::RemoveDirectories);
if (!treeish.isEmpty()) {
m_vcs.reset(ResetOptions::Hard, treeish);
......@@ -481,12 +491,14 @@ void Vault::resetMaster()
Vault::Result Vault::restore(const QString &snapshot, const QString &home, const QStringList &units, const ProgressCallback &callback)
{
auto l = lock();
Snapshot ss(Gittin::Tag(&m_vcs, QString(">") + snapshot));
return restore(ss, home, units, callback);
}
Vault::Result Vault::restore(const Snapshot &snapshot, const QString &home, const QStringList &units, const ProgressCallback &callback)
{
auto l = lock();
debug::info("Restore units", units, ", home", home);
Result res;
res.failedUnits << units;
......@@ -523,6 +535,7 @@ Vault::Result Vault::restore(const Snapshot &snapshot, const QString &home, cons
QList<Snapshot> Vault::snapshots() const
{
auto l = lock();
auto tags = m_vcs.tags();
QList<Snapshot> list;
for (const Gittin::Tag &tag: tags) {
......@@ -535,6 +548,7 @@ QList<Snapshot> Vault::snapshots() const
Snapshot Vault::snapshot(const QByteArray &tagName) const
{
auto l = lock();
auto tags = m_vcs.tags();
for (const Gittin::Tag &tag: tags) {
if (tag.name() == tagName) {
......@@ -547,6 +561,7 @@ Snapshot Vault::snapshot(const QByteArray &tagName) const
QString Vault::notes(const QString &snapshot)
{
auto l = lock();
Gittin::Tag tag(&m_vcs, snapshot);
return tag.notes();
}
......@@ -775,4 +790,28 @@ void Vault::tagSnapshot(const QString &msg)
m_vcs.tag(QLatin1String(">") + msg);
}
/**
* \note thread-unsafe
*/
Lock Vault::lock() const
{
if (!exists())
return Lock{};
auto handle = m_barrier.lock();
if (!handle) {
int timeout = 1 * 1000;
auto lock_fname = absolutePath(fileName(File::Lock));
auto gotLock = [&handle](os::FileLock l) { handle = box(std::move(l)); };
auto locker = os::tryLock(lock_fname, gotLock, timeout);
while (locker) {
// max 8s timeout
if (timeout <= 4 * 1000)
timeout *= 2;
locker = os::tryLock(std::move(locker), gotLock, timeout);
}
m_barrier = handle;
}
return handle;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment