1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2025-05-09 05:34:07 +02:00

Use SyncJournalDb in csync

This gets rid of the csync_statedb sqlite layer and use
the same code and same connection as the rest of the SyncEngine.

Missing functions are added to SyncJournalDb and change a few minor
things (like changing SyncJournalFileRecord::_modtime to be an int64
instead of a QDateTime, like it was in csync).
This commit is contained in:
Jocelyn Turcotte 2017-09-14 15:50:13 +02:00 committed by Olivier Goffart
parent 6f46764daa
commit a034ee894c
34 changed files with 377 additions and 1396 deletions

View File

@ -36,6 +36,26 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcDb, "sync.database", QtInfoMsg)
#define GET_FILE_RECORD_QUERY \
"SELECT path, inode, modtime, type, md5, fileid, remotePerm, filesize," \
" ignoredChildrenRemote, contentchecksumtype.name || ':' || contentChecksum" \
" FROM metadata" \
" LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id"
static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &query)
{
rec._path = query.baValue(0);
rec._inode = query.intValue(1);
rec._modtime = query.int64Value(2);
rec._type = query.intValue(3);
rec._etag = query.baValue(4);
rec._fileId = query.baValue(5);
rec._remotePerm = RemotePermissions(query.baValue(6).constData());
rec._fileSize = query.int64Value(7);
rec._serverHasIgnoredFiles = (query.intValue(8) > 0);
rec._checksumHeader = query.baValue(9);
}
static QString defaultJournalMode(const QString &dbPath)
{
#ifdef Q_OS_WIN
@ -500,14 +520,32 @@ bool SyncJournalDb::checkConnect()
_getFileRecordQuery.reset(new SqlQuery(_db));
if (_getFileRecordQuery->prepare(
"SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize,"
" ignoredChildrenRemote, contentchecksumtype.name || ':' || contentChecksum"
" FROM metadata"
" LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id"
GET_FILE_RECORD_QUERY
" WHERE phash=?1")) {
return sqlFail("prepare _getFileRecordQuery", *_getFileRecordQuery);
}
_getFileRecordQueryByInode.reset(new SqlQuery(_db));
if (_getFileRecordQueryByInode->prepare(
GET_FILE_RECORD_QUERY
" WHERE inode=?1")) {
return sqlFail("prepare _getFileRecordQueryByInode", *_getFileRecordQueryByInode);
}
_getFileRecordQueryByFileId.reset(new SqlQuery(_db));
if (_getFileRecordQueryByFileId->prepare(
GET_FILE_RECORD_QUERY
" WHERE fileid=?1")) {
return sqlFail("prepare _getFileRecordQueryByFileId", *_getFileRecordQueryByFileId);
}
_getFilesBelowPathQuery.reset(new SqlQuery(_db));
if (_getFilesBelowPathQuery->prepare(
GET_FILE_RECORD_QUERY
" WHERE path > (?1||'/') AND path < (?1||'0') ORDER BY path||'/' ASC")) {
return sqlFail("prepare _getFilesBelowPathQuery", *_getFilesBelowPathQuery);
}
_setFileRecordQuery.reset(new SqlQuery(_db));
if (_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, contentChecksum, contentChecksumTypeId) "
@ -651,6 +689,9 @@ void SyncJournalDb::close()
commitTransaction();
_getFileRecordQuery.reset(0);
_getFileRecordQueryByInode.reset(0);
_getFileRecordQueryByFileId.reset(0);
_getFilesBelowPathQuery.reset(0);
_setFileRecordQuery.reset(0);
_setFileRecordChecksumQuery.reset(0);
_setFileRecordLocalMetadataQuery.reset(0);
@ -860,18 +901,17 @@ QStringList SyncJournalDb::tableColumns(const QString &table)
return columns;
}
qint64 SyncJournalDb::getPHash(const QString &file)
qint64 SyncJournalDb::getPHash(const QByteArray &file)
{
QByteArray utf8File = file.toUtf8();
int64_t h;
if (file.isEmpty()) {
return -1;
}
int len = utf8File.length();
int len = file.length();
h = c_jhash64((uint8_t *)utf8File.data(), len, 0);
h = c_jhash64((uint8_t *)file.data(), len, 0);
return h;
}
@ -882,8 +922,8 @@ bool SyncJournalDb::setFileRecord(const SyncJournalFileRecord &_record)
if (!_avoidReadFromDbOnNextSyncFilter.isEmpty()) {
// If we are a directory that should not be read from db next time, don't write the etag
QString prefix = record._path + "/";
foreach (const QString &it, _avoidReadFromDbOnNextSyncFilter) {
QByteArray prefix = record._path + "/";
foreach (const QByteArray &it, _avoidReadFromDbOnNextSyncFilter) {
if (it.startsWith(prefix)) {
qCInfo(lcDb) << "Filtered writing the etag of" << prefix << "because it is a prefix of" << it;
record._etag = "_invalid_";
@ -899,13 +939,12 @@ bool SyncJournalDb::setFileRecord(const SyncJournalFileRecord &_record)
qlonglong phash = getPHash(record._path);
if (checkConnect()) {
QByteArray arr = record._path.toUtf8();
int plen = arr.length();
int plen = record._path.length();
QString etag(record._etag);
QByteArray etag(record._etag);
if (etag.isEmpty())
etag = "";
QString fileId(record._fileId);
QByteArray fileId(record._fileId);
if (fileId.isEmpty())
fileId = "";
QByteArray remotePerm = record._remotePerm.toString();
@ -920,8 +959,8 @@ bool SyncJournalDb::setFileRecord(const SyncJournalFileRecord &_record)
_setFileRecordQuery->bindValue(5, 0); // uid Not used
_setFileRecordQuery->bindValue(6, 0); // gid Not used
_setFileRecordQuery->bindValue(7, 0); // mode Not used
_setFileRecordQuery->bindValue(8, QString::number(Utility::qDateTimeToTime_t(record._modtime)));
_setFileRecordQuery->bindValue(9, QString::number(record._type));
_setFileRecordQuery->bindValue(8, record._modtime);
_setFileRecordQuery->bindValue(9, record._type);
_setFileRecordQuery->bindValue(10, etag);
_setFileRecordQuery->bindValue(11, fileId);
_setFileRecordQuery->bindValue(12, remotePerm);
@ -949,7 +988,7 @@ bool SyncJournalDb::deleteFileRecord(const QString &filename, bool recursively)
// if (!recursively) {
// always delete the actual file.
qlonglong phash = getPHash(filename);
qlonglong phash = getPHash(filename.toUtf8());
_deleteFileRecordPhash->reset_and_clear_bindings();
_deleteFileRecordPhash->bindValue(1, phash);
@ -972,7 +1011,7 @@ bool SyncJournalDb::deleteFileRecord(const QString &filename, bool recursively)
}
bool SyncJournalDb::getFileRecord(const QString &filename, SyncJournalFileRecord *rec)
bool SyncJournalDb::getFileRecord(const QByteArray &filename, SyncJournalFileRecord *rec)
{
QMutexLocker locker(&_mutex);
@ -995,19 +1034,7 @@ bool SyncJournalDb::getFileRecord(const QString &filename, SyncJournalFileRecord
}
if (_getFileRecordQuery->next()) {
rec->_path = _getFileRecordQuery->stringValue(0);
rec->_inode = _getFileRecordQuery->intValue(1);
//rec->_uid = _getFileRecordQuery->value(2).toInt(&ok); Not Used
//rec->_gid = _getFileRecordQuery->value(3).toInt(&ok); Not Used
//rec->_mode = _getFileRecordQuery->intValue(4);
rec->_modtime = Utility::qDateTimeFromTime_t(_getFileRecordQuery->int64Value(5));
rec->_type = _getFileRecordQuery->intValue(6);
rec->_etag = _getFileRecordQuery->baValue(7);
rec->_fileId = _getFileRecordQuery->baValue(8);
rec->_remotePerm = RemotePermissions(_getFileRecordQuery->baValue(9).constData());
rec->_fileSize = _getFileRecordQuery->int64Value(10);
rec->_serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
rec->_checksumHeader = _getFileRecordQuery->baValue(12);
fillFileRecordFromGetQuery(*rec, *_getFileRecordQuery);
} else {
int errId = _getFileRecordQuery->errorId();
if (errId != SQLITE_DONE) { // only do this if the problem is different from SQLITE_DONE
@ -1022,6 +1049,87 @@ bool SyncJournalDb::getFileRecord(const QString &filename, SyncJournalFileRecord
return true;
}
bool SyncJournalDb::getFileRecordByInode(quint64 inode, SyncJournalFileRecord *rec)
{
QMutexLocker locker(&_mutex);
// Reset the output var in case the caller is reusing it.
Q_ASSERT(rec);
rec->_path.clear();
Q_ASSERT(!rec->isValid());
if (!inode)
return true; // no error, yet nothing found (rec->isValid() == false)
if (!checkConnect())
return false;
_getFileRecordQueryByInode->reset_and_clear_bindings();
_getFileRecordQueryByInode->bindValue(1, inode);
if (!_getFileRecordQueryByInode->exec()) {
return false;
}
if (_getFileRecordQueryByInode->next()) {
fillFileRecordFromGetQuery(*rec, *_getFileRecordQueryByInode);
}
return true;
}
bool SyncJournalDb::getFileRecordByFileId(const QByteArray &fileId, SyncJournalFileRecord *rec)
{
QMutexLocker locker(&_mutex);
// Reset the output var in case the caller is reusing it.
Q_ASSERT(rec);
rec->_path.clear();
Q_ASSERT(!rec->isValid());
if (fileId.isEmpty())
return true; // no error, yet nothing found (rec->isValid() == false)
if (!checkConnect())
return false;
_getFileRecordQueryByFileId->reset_and_clear_bindings();
_getFileRecordQueryByFileId->bindValue(1, fileId);
if (!_getFileRecordQueryByFileId->exec()) {
return false;
}
if (_getFileRecordQueryByFileId->next()) {
fillFileRecordFromGetQuery(*rec, *_getFileRecordQueryByFileId);
}
return true;
}
bool SyncJournalDb::getFilesBelowPath(const QByteArray &path, const std::function<void(const SyncJournalFileRecord&)> &rowCallback)
{
QMutexLocker locker(&_mutex);
if (!checkConnect())
return false;
_getFilesBelowPathQuery->reset_and_clear_bindings();
_getFilesBelowPathQuery->bindValue(1, path);
if (!_getFilesBelowPathQuery->exec()) {
return false;
}
while (_getFilesBelowPathQuery->next()) {
SyncJournalFileRecord rec;
fillFileRecordFromGetQuery(rec, *_getFilesBelowPathQuery);
rowCallback(rec);
}
return true;
}
bool SyncJournalDb::postSyncCleanup(const QSet<QString> &filepathsToKeep,
const QSet<QString> &prefixesToKeep)
{
@ -1038,10 +1146,10 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &filepathsToKeep,
return false;
}
QStringList superfluousItems;
QByteArrayList superfluousItems;
while (query.next()) {
const QString file = query.stringValue(1);
const QString file = query.baValue(1);
bool keep = filepathsToKeep.contains(file);
if (!keep) {
foreach (const QString &prefix, prefixesToKeep) {
@ -1052,12 +1160,12 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &filepathsToKeep,
}
}
if (!keep) {
superfluousItems.append(query.stringValue(0));
superfluousItems.append(query.baValue(0));
}
}
if (superfluousItems.count()) {
QString sql = "DELETE FROM metadata WHERE phash in (" + superfluousItems.join(",") + ")";
QByteArray sql = "DELETE FROM metadata WHERE phash in (" + superfluousItems.join(",") + ")";
qCInfo(lcDb) << "Sync Journal cleanup for" << superfluousItems;
SqlQuery delQuery(_db);
delQuery.prepare(sql);
@ -1103,7 +1211,7 @@ bool SyncJournalDb::updateFileRecordChecksum(const QString &filename,
qCInfo(lcDb) << "Updating file checksum" << filename << contentChecksum << contentChecksumType;
qlonglong phash = getPHash(filename);
qlonglong phash = getPHash(filename.toUtf8());
if (!checkConnect()) {
qCWarning(lcDb) << "Failed to connect database.";
return false;
@ -1132,7 +1240,7 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename,
qCInfo(lcDb) << "Updating local metadata for:" << filename << modtime << size << inode;
qlonglong phash = getPHash(filename);
qlonglong phash = getPHash(filename.toUtf8());
if (!checkConnect()) {
qCWarning(lcDb) << "Failed to connect database.";
return false;
@ -1328,7 +1436,7 @@ SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString &file)
res._transferid = _getUploadInfoQuery->intValue(1);
res._errorCount = _getUploadInfoQuery->intValue(2);
res._size = _getUploadInfoQuery->int64Value(3);
res._modtime = Utility::qDateTimeFromTime_t(_getUploadInfoQuery->int64Value(4));
res._modtime = _getUploadInfoQuery->int64Value(4);
res._valid = ok;
}
}
@ -1350,7 +1458,7 @@ void SyncJournalDb::setUploadInfo(const QString &file, const SyncJournalDb::Uplo
_setUploadInfoQuery->bindValue(3, i._transferid);
_setUploadInfoQuery->bindValue(4, i._errorCount);
_setUploadInfoQuery->bindValue(5, i._size);
_setUploadInfoQuery->bindValue(6, Utility::qDateTimeToTime_t(i._modtime));
_setUploadInfoQuery->bindValue(6, i._modtime);
if (!_setUploadInfoQuery->exec()) {
return;
@ -1539,11 +1647,11 @@ void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord
_setErrorBlacklistQuery->reset_and_clear_bindings();
_setErrorBlacklistQuery->bindValue(1, item._file);
_setErrorBlacklistQuery->bindValue(2, item._lastTryEtag);
_setErrorBlacklistQuery->bindValue(3, QString::number(item._lastTryModtime));
_setErrorBlacklistQuery->bindValue(3, item._lastTryModtime);
_setErrorBlacklistQuery->bindValue(4, item._retryCount);
_setErrorBlacklistQuery->bindValue(5, item._errorString);
_setErrorBlacklistQuery->bindValue(6, QString::number(item._lastTryTime));
_setErrorBlacklistQuery->bindValue(7, QString::number(item._ignoreDuration));
_setErrorBlacklistQuery->bindValue(6, item._lastTryTime);
_setErrorBlacklistQuery->bindValue(7, item._ignoreDuration);
_setErrorBlacklistQuery->bindValue(8, item._renameTarget);
_setErrorBlacklistQuery->bindValue(9, item._errorCategory);
_setErrorBlacklistQuery->exec();
@ -1591,7 +1699,7 @@ void SyncJournalDb::setPollInfo(const SyncJournalDb::PollInfo &info)
} else {
SqlQuery query("INSERT OR REPLACE INTO poll (path, modtime, pollpath) VALUES( ? , ? , ? )", _db);
query.bindValue(1, info._file);
query.bindValue(2, QString::number(info._modtime));
query.bindValue(2, info._modtime);
query.bindValue(3, info._url);
query.exec();
}
@ -1651,7 +1759,7 @@ void SyncJournalDb::setSelectiveSyncList(SyncJournalDb::SelectiveSyncListType ty
}
}
void SyncJournalDb::avoidRenamesOnNextSync(const QString &path)
void SyncJournalDb::avoidRenamesOnNextSync(const QByteArray &path)
{
QMutexLocker locker(&_mutex);
@ -1671,7 +1779,7 @@ void SyncJournalDb::avoidRenamesOnNextSync(const QString &path)
avoidReadFromDbOnNextSync(path);
}
void SyncJournalDb::avoidReadFromDbOnNextSync(const QString &fileName)
void SyncJournalDb::avoidReadFromDbOnNextSync(const QByteArray &fileName)
{
// Make sure that on the next sync, fileName is not read from the DB but uses the PROPFIND to
// get the info from the server
@ -1798,6 +1906,7 @@ void SyncJournalDb::setDataFingerprint(const QByteArray &dataFingerprint)
void SyncJournalDb::clearFileTable()
{
QMutexLocker lock(&_mutex);
SqlQuery query(_db);
query.prepare("DELETE FROM metadata;");
query.exec();

View File

@ -23,6 +23,7 @@
#include <qmutex.h>
#include <QDateTime>
#include <QHash>
#include <functional>
#include "common/utility.h"
#include "common/ownsql.h"
@ -54,7 +55,11 @@ public:
static bool maybeMigrateDb(const QString &localPath, const QString &absoluteJournalPath);
// To verify that the record could be found check with SyncJournalFileRecord::isValid()
bool getFileRecord(const QString &filename, SyncJournalFileRecord *rec);
bool getFileRecord(const QString &filename, SyncJournalFileRecord *rec) { return getFileRecord(filename.toUtf8(), rec); }
bool getFileRecord(const QByteArray &filename, SyncJournalFileRecord *rec);
bool getFileRecordByInode(quint64 inode, SyncJournalFileRecord *rec);
bool getFileRecordByFileId(const QByteArray &fileId, SyncJournalFileRecord *rec);
bool getFilesBelowPath(const QByteArray &path, const std::function<void(const SyncJournalFileRecord&)> &rowCallback);
bool setFileRecord(const SyncJournalFileRecord &record);
/// Like setFileRecord, but preserves checksums
@ -72,7 +77,7 @@ public:
QString databaseFilePath() const;
static qint64 getPHash(const QString &);
static qint64 getPHash(const QByteArray &);
void setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item);
void wipeErrorBlacklistEntry(const QString &file);
@ -105,7 +110,7 @@ public:
int _chunk;
int _transferid;
quint64 _size; //currently unused
QDateTime _modtime;
qint64 _modtime;
int _errorCount;
bool _valid;
};
@ -114,7 +119,7 @@ public:
{
QString _file;
QString _url;
time_t _modtime;
qint64 _modtime;
};
DownloadInfo getDownloadInfo(const QString &file);
@ -130,7 +135,8 @@ public:
SyncJournalErrorBlacklistRecord errorBlacklistEntry(const QString &);
bool deleteStaleErrorBlacklistEntries(const QSet<QString> &keep);
void avoidRenamesOnNextSync(const QString &path);
void avoidRenamesOnNextSync(const QString &path) { avoidRenamesOnNextSync(path.toUtf8()); }
void avoidRenamesOnNextSync(const QByteArray &path);
void setPollInfo(const PollInfo &);
QVector<PollInfo> getPollInfos();
@ -164,7 +170,8 @@ public:
* _csync_detect_update skip them), the _invalid_ marker will stay and it. And any
* child items in the db will be ignored when reading a remote tree from the database.
*/
void avoidReadFromDbOnNextSync(const QString &fileName);
void avoidReadFromDbOnNextSync(const QString &fileName) { avoidReadFromDbOnNextSync(fileName.toUtf8()); }
void avoidReadFromDbOnNextSync(const QByteArray &fileName);
/**
* Ensures full remote discovery happens on the next sync.
@ -233,6 +240,9 @@ private:
// NOTE! when adding a query, don't forget to reset it in SyncJournalDb::close
QScopedPointer<SqlQuery> _getFileRecordQuery;
QScopedPointer<SqlQuery> _getFileRecordQueryByInode;
QScopedPointer<SqlQuery> _getFileRecordQueryByFileId;
QScopedPointer<SqlQuery> _getFilesBelowPathQuery;
QScopedPointer<SqlQuery> _setFileRecordQuery;
QScopedPointer<SqlQuery> _setFileRecordChecksumQuery;
QScopedPointer<SqlQuery> _setFileRecordLocalMetadataQuery;
@ -258,7 +268,7 @@ private:
* It means that they should not be written to the DB in any case since doing
* that would write the etag and would void the purpose of avoidReadFromDbOnNextSync
*/
QList<QString> _avoidReadFromDbOnNextSyncFilter;
QList<QByteArray> _avoidReadFromDbOnNextSyncFilter;
/** The journal mode to use for the db.
*

View File

@ -52,7 +52,7 @@ bool operator==(const SyncJournalFileRecord &lhs,
{
return lhs._path == rhs._path
&& lhs._inode == rhs._inode
&& lhs._modtime.toTime_t() == rhs._modtime.toTime_t()
&& lhs._modtime == rhs._modtime
&& lhs._type == rhs._type
&& lhs._etag == rhs._etag
&& lhs._fileId == rhs._fileId

View File

@ -24,6 +24,7 @@
#include "ocsynclib.h"
#include "remotepermissions.h"
#include "common/utility.h"
namespace OCC {
@ -50,10 +51,11 @@ public:
* It is used in the construction of private links.
*/
QByteArray numericFileId() const;
QDateTime modDateTime() const { return Utility::qDateTimeFromTime_t(_modtime); }
QString _path;
QByteArray _path;
quint64 _inode;
QDateTime _modtime;
qint64 _modtime;
int _type;
QByteArray _etag;
QByteArray _fileId;
@ -94,14 +96,14 @@ public:
/// The error category. Sometimes used for special actions.
Category _errorCategory;
time_t _lastTryModtime;
qint64 _lastTryModtime;
QByteArray _lastTryEtag;
/// The last time the operation was attempted (in s since epoch).
time_t _lastTryTime;
qint64 _lastTryTime;
/// The number of seconds the file shall be ignored.
time_t _ignoreDuration;
qint64 _ignoreDuration;
QString _file;
QString _renameTarget;

View File

@ -67,7 +67,6 @@ set(csync_SRCS
csync.cpp
csync_exclude.cpp
csync_log.cpp
csync_statedb.cpp
csync_time.c
csync_util.cpp
csync_misc.cpp

View File

@ -36,7 +36,6 @@
#include "c_lib.h"
#include "csync_private.h"
#include "csync_exclude.h"
#include "csync_statedb.h"
#include "csync_time.h"
#include "csync_util.h"
#include "csync_misc.h"
@ -52,7 +51,9 @@
#include "common/c_jhash.h"
csync_s::csync_s(const char *localUri, const char *db_file) {
csync_s::csync_s(const char *localUri, OCC::SyncJournalDb *statedb)
: statedb(statedb)
{
size_t len = 0;
/* remove trailing slashes */
@ -60,8 +61,6 @@ csync_s::csync_s(const char *localUri, const char *db_file) {
while(len > 0 && localUri[len - 1] == '/') --len;
local.uri = c_strndup(localUri, len);
statedb.file = c_strdup(db_file);
}
int csync_update(CSYNC *ctx) {
@ -74,12 +73,6 @@ int csync_update(CSYNC *ctx) {
}
ctx->status_code = CSYNC_STATUS_OK;
/* Path of database file is set in csync_init */
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
rc = -1;
return rc;
}
ctx->status_code = CSYNC_STATUS_OK;
csync_memstat_check();
@ -97,7 +90,7 @@ int csync_update(CSYNC *ctx) {
if(ctx->status_code == CSYNC_STATUS_OK) {
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
}
goto out;
return rc;
}
csync_gettime(&finish);
@ -116,7 +109,7 @@ int csync_update(CSYNC *ctx) {
if(ctx->status_code == CSYNC_STATUS_OK) {
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
}
goto out;
return rc;
}
csync_gettime(&finish);
@ -130,9 +123,6 @@ int csync_update(CSYNC *ctx) {
ctx->status |= CSYNC_STATUS_UPDATE;
rc = 0;
out:
csync_statedb_close(ctx);
return rc;
}
@ -149,11 +139,6 @@ int csync_reconcile(CSYNC *ctx) {
/* Reconciliation for local replica */
csync_gettime(&start);
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
rc = -1;
return rc;
}
ctx->current = LOCAL_REPLICA;
rc = csync_reconcile_updates(ctx);
@ -168,7 +153,7 @@ int csync_reconcile(CSYNC *ctx) {
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
ctx->status_code = csync_errno_to_status( errno, CSYNC_STATUS_RECONCILE_ERROR );
}
goto out;
return rc;
}
/* Reconciliation for remote replica */
@ -188,16 +173,13 @@ int csync_reconcile(CSYNC *ctx) {
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_RECONCILE_ERROR );
}
goto out;
return rc;
}
ctx->status |= CSYNC_STATUS_RECONCILE;
rc = 0;
out:
csync_statedb_close(ctx);
return 0;
return rc;
}
/*
@ -326,13 +308,6 @@ int csync_s::reinitialize() {
status_code = CSYNC_STATUS_OK;
if (statedb.db != NULL
&& csync_statedb_close(this) < 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "ERR: closing of statedb failed.");
rc = -1;
}
statedb.db = NULL;
remote.read_from_db = 0;
read_remote_from_db = true;
db_is_empty = false;
@ -348,13 +323,6 @@ int csync_s::reinitialize() {
}
csync_s::~csync_s() {
if (statedb.db != NULL
&& csync_statedb_close(this) < 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "ERR: closing of statedb failed.");
}
statedb.db = NULL;
SAFE_FREE(statedb.file);
SAFE_FREE(local.uri);
SAFE_FREE(error_string);
}

View File

@ -34,6 +34,8 @@
#include "std/c_private.h"
#include "ocsynclib.h"
#include "common/syncjournalfilerecord.h"
#include <sys/stat.h>
#include <stdbool.h>
#include <stdint.h>
@ -200,6 +202,22 @@ struct OCSYNC_EXPORT csync_file_stat_s {
, error_status(CSYNC_STATUS_OK)
, instruction(CSYNC_INSTRUCTION_NONE)
{ }
static std::unique_ptr<csync_file_stat_t> fromSyncJournalFileRecord(const OCC::SyncJournalFileRecord &rec)
{
std::unique_ptr<csync_file_stat_t> st(new csync_file_stat_t);
st->path = rec._path;
st->inode = rec._inode;
st->modtime = rec._modtime;
st->type = static_cast<csync_ftw_type_e>(rec._type);
st->etag = rec._etag;
st->file_id = rec._fileId;
st->remotePerm = rec._remotePerm;
st->size = rec._fileSize;
st->has_ignored_files = rec._serverHasIgnoredFiles;
st->checksumHeader = rec._checksumHeader;
return st;
}
};
/**

View File

@ -39,6 +39,7 @@
#include <sqlite3.h>
#include <map>
#include "common/syncjournaldb.h"
#include "config_csync.h"
#include "std/c_lib.h"
#include "std/c_private.h"
@ -103,17 +104,7 @@ struct OCSYNC_EXPORT csync_s {
} callbacks;
c_strlist_t *excludes = nullptr;
struct {
char *file = nullptr;
sqlite3 *db = nullptr;
bool exists = false;
sqlite3_stmt* by_hash_stmt = nullptr;
sqlite3_stmt* by_fileid_stmt = nullptr;
sqlite3_stmt* by_inode_stmt = nullptr;
int lastReturnValue;
} statedb;
OCC::SyncJournalDb *statedb;
struct {
std::map<QByteArray, QByteArray> folder_renamed_to; // map from->to
@ -159,7 +150,7 @@ struct OCSYNC_EXPORT csync_s {
bool ignore_hidden_files = true;
csync_s(const char *localUri, const char *db_file);
csync_s(const char *localUri, OCC::SyncJournalDb *statedb);
~csync_s();
int reinitialize();

View File

@ -24,7 +24,6 @@
#include "csync_private.h"
#include "csync_reconcile.h"
#include "csync_util.h"
#include "csync_statedb.h"
#include "csync_rename.h"
#include "common/c_jhash.h"
#include "common/asserts.h"
@ -104,8 +103,6 @@ static bool _csync_is_collision_safe_hash(const char *checksum_header)
* source and the destination, have been changed, the newer file wins.
*/
static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
std::unique_ptr<csync_file_stat_t> tmp;
csync_s::FileMap *other_tree = nullptr;
/* we need the opposite tree! */
@ -153,35 +150,34 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
}
cur->instruction = CSYNC_INSTRUCTION_REMOVE;
break;
case CSYNC_INSTRUCTION_EVAL_RENAME:
case CSYNC_INSTRUCTION_EVAL_RENAME: {
OCC::SyncJournalFileRecord base;
if(ctx->current == LOCAL_REPLICA ) {
/* use the old name to find the "other" node */
tmp = csync_statedb_get_stat_by_inode(ctx, cur->inode);
ctx->statedb->getFileRecordByInode(cur->inode, &base);
qCDebug(lcReconcile, "Finding opposite temp through inode %" PRIu64 ": %s",
cur->inode, tmp ? "true":"false");
cur->inode, base.isValid() ? "true":"false");
} else {
ASSERT( ctx->current == REMOTE_REPLICA );
tmp = csync_statedb_get_stat_by_file_id(ctx, cur->file_id);
ctx->statedb->getFileRecordByFileId(cur->file_id, &base);
qCDebug(lcReconcile, "Finding opposite temp through file ID %s: %s",
cur->file_id.constData(), tmp ? "true":"false");
cur->file_id.constData(), base.isValid() ? "true":"false");
}
if( tmp ) {
if( !tmp->path.isEmpty() ) {
/* First, check that the file is NOT in our tree (another file with the same name was added) */
csync_s::FileMap *our_tree = ctx->current == REMOTE_REPLICA ? &ctx->remote.files : &ctx->local.files;
if (our_tree->findFile(tmp->path)) {
qCDebug(lcReconcile, "Origin found in our tree : %s", tmp->path.constData());
} else {
if( base.isValid() ) {
/* First, check that the file is NOT in our tree (another file with the same name was added) */
csync_s::FileMap *our_tree = ctx->current == REMOTE_REPLICA ? &ctx->remote.files : &ctx->local.files;
if (our_tree->findFile(base._path)) {
qCDebug(lcReconcile, "Origin found in our tree : %s", base._path.constData());
} else {
/* Find the temporar file in the other tree.
* If the renamed file could not be found in the opposite tree, that is because it
* is not longer existing there, maybe because it was renamed or deleted.
* The journal is cleaned up later after propagation.
*/
other = other_tree->findFile(tmp->path);
other = other_tree->findFile(base._path);
qCDebug(lcReconcile, "Temporary opposite (%s) %s",
tmp->path.constData() , other ? "found": "not found" );
}
base._path.constData() , other ? "found": "not found" );
}
if(!other) {
@ -213,9 +209,9 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
cur->instruction = CSYNC_INSTRUCTION_NONE;
other->instruction = CSYNC_INSTRUCTION_SYNC;
}
}
}
break;
}
default:
break;
}

View File

@ -1,627 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012-2013 by Klaas Freitag <freitag@owncloud.com>
*
* 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) any later version.
*
* 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 "config_csync.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <sqlite3.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "c_lib.h"
#include "csync_private.h"
#include "csync_statedb.h"
#include "csync_util.h"
#include "csync_misc.h"
#include "csync_exclude.h"
#include "c_string.h"
#include "common/c_jhash.h"
#include "c_utf8.h"
#include "csync_time.h"
#define CSYNC_LOG_CATEGORY_NAME "csync.statedb"
#include "csync_log.h"
#include "csync_rename.h"
// Needed for PRIu64 on MinGW in C++ mode.
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#define BUF_SIZE 16
#define sqlite_open(A, B) sqlite3_open_v2(A,B, SQLITE_OPEN_READONLY+SQLITE_OPEN_NOMUTEX, NULL)
#define SQLTM_TIME 150
#define SQLTM_COUNT 10
#define SQLITE_BUSY_HANDLED(F) if(1) { \
int n = 0; \
do { rc = F ; \
if( (rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED) ) { \
n++; \
csync_sleep(SQLTM_TIME); \
} \
}while( (n < SQLTM_COUNT) && ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED))); \
}
void csync_set_statedb_exists(CSYNC *ctx, int val) {
ctx->statedb.exists = val;
}
int csync_get_statedb_exists(CSYNC *ctx) {
return ctx->statedb.exists;
}
static int _csync_check_db_integrity(sqlite3 *db) {
c_strlist_t *result = NULL;
int rc = -1;
result = csync_statedb_query(db, "PRAGMA quick_check;");
if (result != NULL) {
/* There is a result */
if (result->count > 0) {
if (c_streq(result->vector[0], "ok")) {
rc = 0;
}
}
c_strlist_destroy(result);
}
if( sqlite3_threadsafe() == 0 ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "* WARNING: SQLite module is not threadsafe!");
rc = -1;
}
return rc;
}
static int _csync_statedb_is_empty(sqlite3 *db) {
c_strlist_t *result = NULL;
int rc = 0;
result = csync_statedb_query(db, "SELECT COUNT(phash) FROM metadata LIMIT 1 OFFSET 0;");
if (result == NULL) {
rc = 1;
}
c_strlist_destroy(result);
return rc;
}
#ifndef NDEBUG
static void sqlite_profile( void *x, const char* sql, sqlite3_uint64 time)
{
(void)x;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"_SQL_ %s: %llu", sql, time);
}
#endif
int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb) {
int rc = -1;
c_strlist_t *result = NULL;
sqlite3 *db = NULL;
if( !ctx ) {
return -1;
}
if (ctx->statedb.db) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: DB already open");
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
return -1;
}
ctx->statedb.lastReturnValue = SQLITE_OK;
/* Openthe database */
if (sqlite_open(statedb, &db) != SQLITE_OK) {
const char *errmsg= sqlite3_errmsg(ctx->statedb.db);
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: Failed to sqlite3 open statedb - bail out: %s.",
errmsg ? errmsg : "<no sqlite3 errormsg>");
rc = -1;
ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
goto out;
}
if (_csync_check_db_integrity(db) != 0) {
const char *errmsg= sqlite3_errmsg(db);
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: sqlite3 integrity check failed - bail out: %s.",
errmsg ? errmsg : "<no sqlite3 errormsg>");
rc = -1;
ctx->status_code = CSYNC_STATUS_STATEDB_CORRUPTED;
goto out;
}
if (_csync_statedb_is_empty(db)) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "statedb contents doesn't exist");
csync_set_statedb_exists(ctx, 0);
} else {
csync_set_statedb_exists(ctx, 1);
}
/* Print out the version */
//
result = csync_statedb_query(db, "SELECT sqlite_version();");
if (result && result->count >= 1) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3 version \"%s\"", *result->vector);
}
c_strlist_destroy(result);
/* optimization for speeding up SQLite */
result = csync_statedb_query(db, "PRAGMA synchronous = NORMAL;");
c_strlist_destroy(result);
result = csync_statedb_query(db, "PRAGMA case_sensitive_like = ON;");
c_strlist_destroy(result);
/* set a busy handler with 5 seconds timeout */
sqlite3_busy_timeout(db, 5000);
#ifndef NDEBUG
sqlite3_profile(db, sqlite_profile, 0 );
#endif
*pdb = db;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Success");
return 0;
out:
sqlite3_close(db);
return rc;
}
int csync_statedb_close(CSYNC *ctx) {
int rc = 0;
if (!ctx) {
return -1;
}
/* deallocate query resources */
if( ctx->statedb.by_fileid_stmt ) {
sqlite3_finalize(ctx->statedb.by_fileid_stmt);
ctx->statedb.by_fileid_stmt = NULL;
}
if( ctx->statedb.by_hash_stmt ) {
sqlite3_finalize(ctx->statedb.by_hash_stmt);
ctx->statedb.by_hash_stmt = NULL;
}
if( ctx->statedb.by_inode_stmt) {
sqlite3_finalize(ctx->statedb.by_inode_stmt);
ctx->statedb.by_inode_stmt = NULL;
}
ctx->statedb.lastReturnValue = SQLITE_OK;
int sr = sqlite3_close(ctx->statedb.db);
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3_close=%d", sr);
ctx->statedb.db = 0;
return rc;
}
#define METADATA_QUERY \
"path, inode, modtime, type, md5, fileid, remotePerm, " \
"filesize, ignoredChildrenRemote, " \
"contentchecksumtype.name || ':' || contentChecksum " \
"FROM metadata " \
"LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id"
// This funciton parses a line from the metadata table into the given csync_file_stat
// structure which it is also allocating.
// Note that this function calls laso sqlite3_step to actually get the info from db and
// returns the sqlite return type.
static int _csync_file_stat_from_metadata_table( std::unique_ptr<csync_file_stat_t> &st, sqlite3_stmt *stmt )
{
int rc = SQLITE_ERROR;
if( ! stmt ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Fatal: Statement is NULL.");
return SQLITE_ERROR;
}
// Callers should all use METADATA_QUERY for their column list.
assert(sqlite3_column_count(stmt) == 10);
SQLITE_BUSY_HANDLED( sqlite3_step(stmt) );
if( rc == SQLITE_ROW ) {
st.reset(new csync_file_stat_t);
st->path = (char*)sqlite3_column_text(stmt, 0);
st->inode = sqlite3_column_int64(stmt, 1);
st->modtime = strtoul((char*)sqlite3_column_text(stmt, 2), NULL, 10);
st->type = static_cast<enum csync_ftw_type_e>(sqlite3_column_int(stmt, 3));
st->etag = (char*)sqlite3_column_text(stmt, 4);
st->file_id = (char*)sqlite3_column_text(stmt, 5);
const char *permStr = (char *)sqlite3_column_text(stmt, 6);
// If permStr is empty, construct a null RemotePermissions. We make sure that non-null
// permissions are never empty in RemotePermissions.toString()
st->remotePerm = permStr && *permStr ? OCC::RemotePermissions(permStr) : OCC::RemotePermissions();
st->size = sqlite3_column_int64(stmt, 7);
st->has_ignored_files = sqlite3_column_int(stmt, 8);
st->checksumHeader = (char *)sqlite3_column_text(stmt, 9);
} else {
if( rc != SQLITE_DONE ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Query results in %d", rc);
}
}
return rc;
}
/* caller must free the memory */
std::unique_ptr<csync_file_stat_t> csync_statedb_get_stat_by_path(CSYNC *ctx, const QByteArray &path)
{
std::unique_ptr<csync_file_stat_t> st;
int rc;
if( !ctx || ctx->db_is_empty ) {
return NULL;
}
if( ctx->statedb.by_hash_stmt == NULL ) {
const char *hash_query = "SELECT " METADATA_QUERY " WHERE phash=?1";
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, hash_query, strlen(hash_query), &ctx->statedb.by_hash_stmt, NULL));
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_OK ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for hash query.");
return NULL;
}
}
if( ctx->statedb.by_hash_stmt == NULL ) {
return NULL;
}
uint64_t phash = c_jhash64((const uint8_t*)path.constData(), path.size(), 0);
sqlite3_bind_int64(ctx->statedb.by_hash_stmt, 1, (long long signed int)phash);
rc = _csync_file_stat_from_metadata_table(st, ctx->statedb.by_hash_stmt);
ctx->statedb.lastReturnValue = rc;
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc);
}
sqlite3_reset(ctx->statedb.by_hash_stmt);
return st;
}
std::unique_ptr<csync_file_stat_t> csync_statedb_get_stat_by_file_id(CSYNC *ctx,
const char *file_id ) {
std::unique_ptr<csync_file_stat_t> st;
int rc = 0;
if (!file_id) {
return 0;
}
if (c_streq(file_id, "")) {
return 0;
}
if( !ctx || ctx->db_is_empty ) {
return NULL;
}
if( ctx->statedb.by_fileid_stmt == NULL ) {
const char *query = "SELECT " METADATA_QUERY " WHERE fileid=?1";
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, query, strlen(query), &ctx->statedb.by_fileid_stmt, NULL));
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_OK ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for file id query.");
return NULL;
}
}
/* bind the query value */
sqlite3_bind_text(ctx->statedb.by_fileid_stmt, 1, file_id, -1, SQLITE_STATIC);
rc = _csync_file_stat_from_metadata_table(st, ctx->statedb.by_fileid_stmt);
ctx->statedb.lastReturnValue = rc;
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc);
}
// clear the resources used by the statement.
sqlite3_reset(ctx->statedb.by_fileid_stmt);
return st;
}
/* caller must free the memory */
std::unique_ptr<csync_file_stat_t> csync_statedb_get_stat_by_inode(CSYNC *ctx,
uint64_t inode)
{
std::unique_ptr<csync_file_stat_t> st;
int rc;
if (!inode) {
return NULL;
}
if( !ctx || ctx->db_is_empty ) {
return NULL;
}
if( ctx->statedb.by_inode_stmt == NULL ) {
const char *inode_query = "SELECT " METADATA_QUERY " WHERE inode=?1";
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, inode_query, strlen(inode_query), &ctx->statedb.by_inode_stmt, NULL));
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_OK ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for inode query.");
return NULL;
}
}
if( ctx->statedb.by_inode_stmt == NULL ) {
return NULL;
}
sqlite3_bind_int64(ctx->statedb.by_inode_stmt, 1, (long long signed int)inode);
rc = _csync_file_stat_from_metadata_table(st, ctx->statedb.by_inode_stmt);
ctx->statedb.lastReturnValue = rc;
if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata by inode: %d!", rc);
}
sqlite3_reset(ctx->statedb.by_inode_stmt);
return st;
}
int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
int rc;
sqlite3_stmt *stmt = NULL;
int64_t cnt = 0;
if( !path ) {
return -1;
}
if( !ctx || ctx->db_is_empty ) {
return -1;
}
/* Select the entries for anything that starts with (path+'/')
* In other words, anything that is between path+'/' and path+'0',
* (because '0' follows '/' in ascii)
*/
const char *below_path_query = "SELECT " METADATA_QUERY " WHERE path > (?||'/') AND path < (?||'0') ORDER BY path||'/' ASC";
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL));
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_OK ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query.");
return -1;
}
if (stmt == NULL) {
return -1;
}
sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, path, -1, SQLITE_STATIC);
cnt = 0;
ctx->statedb.lastReturnValue = rc;
do {
std::unique_ptr<csync_file_stat_t> st;
rc = _csync_file_stat_from_metadata_table(st, stmt);
if( st ) {
/* When selective sync is used, the database may have subtrees with a parent
* whose etag (md5) is _invalid_. These are ignored and shall not appear in the
* remote tree.
* Sometimes folders that are not ignored by selective sync get marked as
* _invalid_, but that is not a problem as the next discovery will retrieve
* their correct etags again and we don't run into this case.
*/
if( st->etag == "_invalid_") {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded", st->path.constData());
QByteArray skipbase = st->path;
skipbase += '/';
/* Skip over all entries with the same base path. Note that this depends
* strongly on the ordering of the retrieved items. */
do {
st.reset();
rc = _csync_file_stat_from_metadata_table(st, stmt);
if( st ) {
if( !st->path.startsWith(skipbase) )
break;
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded because the parent is", st->path.constData());
}
} while( rc == SQLITE_ROW );
/* End of data? */
if( rc != SQLITE_ROW || !st ) {
continue;
}
}
/* Check for exclusion from the tree.
* Note that this is only a safety net in case the ignore list changes
* without a full remote discovery being triggered. */
CSYNC_EXCLUDE_TYPE excluded = csync_excluded_traversal(ctx->excludes, st->path, st->type);
if (excluded != CSYNC_NOT_EXCLUDED) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", st->path.constData(), excluded);
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE
|| excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
st.reset();
continue;
}
st->instruction = CSYNC_INSTRUCTION_IGNORE;
}
/* store into result list. */
QByteArray path = st->path;
ctx->remote.files[path] = std::move(st);
cnt++;
}
} while( rc == SQLITE_ROW );
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_DONE ) {
ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path);
}
sqlite3_finalize(stmt);
return 0;
}
/* query the statedb, caller must free the memory */
c_strlist_t *csync_statedb_query(sqlite3 *db,
const char *statement) {
int err = SQLITE_OK;
int rc = SQLITE_OK;
size_t i = 0;
size_t busy_count = 0;
size_t retry_count = 0;
size_t column_count = 0;
sqlite3_stmt *stmt;
const char *tail = NULL;
const char *field = NULL;
c_strlist_t *result = NULL;
int row = 0;
do {
/* compile SQL program into a virtual machine, reattempteing if busy */
do {
if (busy_count) {
csync_sleep(100);
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3_prepare: BUSY counter: %zu", busy_count);
}
err = sqlite3_prepare(db, statement, -1, &stmt, &tail);
} while (err == SQLITE_BUSY && busy_count ++ < 120);
if (err != SQLITE_OK) {
if (err == SQLITE_BUSY) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Gave up waiting for lock to clear");
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN,
"sqlite3_compile error: %s - on query %s",
sqlite3_errmsg(db), statement);
break;
} else {
busy_count = 0;
column_count = sqlite3_column_count(stmt);
/* execute virtual machine by iterating over rows */
for(;;) {
err = sqlite3_step(stmt);
if (err == SQLITE_BUSY) {
if (busy_count++ > 120) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Busy counter has reached its maximum. Aborting this sql statement");
break;
}
csync_sleep(100);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "sqlite3_step: BUSY counter: %zu", busy_count);
continue;
}
if (err == SQLITE_MISUSE) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite3_step: MISUSE!!");
}
if (err == SQLITE_DONE) {
if (result == NULL) {
result = c_strlist_new(1);
}
break;
}
if (err == SQLITE_ERROR) {
break;
}
row++;
if( result ) {
result = c_strlist_expand(result, row*column_count);
} else {
result = c_strlist_new(column_count);
}
if (result == NULL) {
return NULL;
}
/* iterate over columns */
for (i = 0; i < column_count; i++) {
field = (const char *) sqlite3_column_text(stmt, i);
if (!field)
field = "";
// CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "sqlite3_column_text: %s", field);
if (c_strlist_add(result, field) < 0) {
c_strlist_destroy(result);
return NULL;
}
}
} /* end infinite for loop */
/* deallocate vm resources */
rc = sqlite3_finalize(stmt);
if (err != SQLITE_DONE && rc != SQLITE_SCHEMA) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite_step error: %s - on query: %s", sqlite3_errmsg(db), statement);
if (result != NULL) {
c_strlist_destroy(result);
}
return NULL;
}
if (rc == SQLITE_SCHEMA) {
retry_count ++;
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "SQLITE_SCHEMA error occurred on query: %s", statement);
if (retry_count < 10) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Retrying now.");
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "RETRY count has reached its maximum. Aborting statement: %s", statement);
if (result != NULL) {
c_strlist_destroy(result);
}
result = c_strlist_new(1);
}
}
}
} while (rc == SQLITE_SCHEMA && retry_count < 10);
return result;
}
/* vim: set ts=8 sw=2 et cindent: */

View File

@ -1,99 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012-2013 by Klaas Freitag <freitag@owncloud.com>
*
* 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) any later version.
*
* 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
*/
/**
* @file csync_private.h
*
* @brief Private interface of csync
*
* @defgroup csyncstatedbInternals csync statedb internals
* @ingroup csyncInternalAPI
*
* @{
*/
#ifndef _CSYNC_STATEDB_H
#define _CSYNC_STATEDB_H
#include "c_lib.h"
#include "csync_private.h"
void csync_set_statedb_exists(CSYNC *ctx, int val);
int csync_get_statedb_exists(CSYNC *ctx);
/**
* @brief Load the statedb.
*
* This function tries to load the statedb. If it doesn't exists it creates
* the sqlite3 database, but doesn't create the tables. This will be done when
* csync gets destroyed.
*
* @param ctx The csync context.
* @param statedb Path to the statedb file (sqlite3 db).
*
* @return 0 on success, less than 0 if an error occurred with errno set.
*/
OCSYNC_EXPORT int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb);
OCSYNC_EXPORT int csync_statedb_close(CSYNC *ctx);
OCSYNC_EXPORT std::unique_ptr<csync_file_stat_t> csync_statedb_get_stat_by_path(CSYNC *ctx, const QByteArray &path);
OCSYNC_EXPORT std::unique_ptr<csync_file_stat_t> csync_statedb_get_stat_by_inode(CSYNC *ctx, uint64_t inode);
OCSYNC_EXPORT std::unique_ptr<csync_file_stat_t> csync_statedb_get_stat_by_file_id(CSYNC *ctx, const char *file_id);
/**
* @brief Query all files metadata inside and below a path.
* @param ctx The csync context.
* @param path The path.
*
* This function queries all metadata of all files inside or below the
* given path. The result is a linear string list with a multiple of 9
* entries. For each result file there are 9 strings which are phash,
* path, inode, uid, gid, mode, modtime, type and md5 (unique id).
*
* Note that not only the files in the given path are part of the result
* but also the files in directories below the given path. Ie. if the
* parameter path is /home/kf/test, we have /home/kf/test/file.txt in
* the result but also /home/kf/test/homework/another_file.txt
*
* @return A stringlist containing a multiple of 9 entries.
*/
int csync_statedb_get_below_path(CSYNC *ctx, const char *path);
/**
* @brief A generic statedb query.
*
* @param ctx The csync context.
* @param statement The SQL statement to execute
*
* @return A stringlist of the entries of a column. An emtpy stringlist if
* nothing has been found. NULL on error.
*/
c_strlist_t *csync_statedb_query(sqlite3 *db, const char *statement);
/**
* }@
*/
#endif /* _CSYNC_STATEDB_H */
/* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */

View File

@ -35,7 +35,6 @@
#include "csync_private.h"
#include "csync_exclude.h"
#include "csync_statedb.h"
#include "csync_update.h"
#include "csync_util.h"
#include "csync_misc.h"
@ -74,10 +73,6 @@ static bool _csync_sameextension(const char *p1, const char *p2) {
}
#endif
static bool _last_db_return_error(CSYNC* ctx) {
return ctx->statedb.lastReturnValue != SQLITE_OK && ctx->statedb.lastReturnValue != SQLITE_DONE && ctx->statedb.lastReturnValue != SQLITE_ROW;
}
static QByteArray _rel_to_abs(CSYNC* ctx, const QByteArray &relativePath) {
return QByteArray() % const_cast<const char *>(ctx->local.uri) % '/' % relativePath;
}
@ -111,7 +106,7 @@ static bool _csync_mtime_equal(time_t a, time_t b)
* See doc/dev/sync-algorithm.md for an overview.
*/
static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
std::unique_ptr<csync_file_stat_t> tmp;
OCC::SyncJournalFileRecord base;
CSYNC_EXCLUDE_TYPE excluded;
if (fs == NULL) {
@ -173,48 +168,46 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
* renamed, the db gets queried by the inode of the file as that one
* does not change on rename.
*/
tmp = csync_statedb_get_stat_by_path(ctx, fs->path);
if(_last_db_return_error(ctx)) {
if(!ctx->statedb->getFileRecord(fs->path, &base)) {
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
return -1;
}
if(tmp && tmp->path == fs->path ) { /* there is an entry in the database */
if(base.isValid()) { /* there is an entry in the database */
/* we have an update! */
qCInfo(lcUpdate, "Database entry found, compare: %" PRId64 " <-> %" PRId64
", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64
", size: %" PRId64 " <-> %" PRId64 ", perms: %x <-> %x, ignore: %d",
((int64_t) fs->modtime), ((int64_t) tmp->modtime),
fs->etag.constData(), tmp->etag.constData(), (uint64_t) fs->inode, (uint64_t) tmp->inode,
(uint64_t) fs->size, (uint64_t) tmp->size, *reinterpret_cast<short*>(&fs->remotePerm), *reinterpret_cast<short*>(&tmp->remotePerm), tmp->has_ignored_files );
if (ctx->current == REMOTE_REPLICA && fs->etag != tmp->etag) {
((int64_t) fs->modtime), ((int64_t) base._modtime),
fs->etag.constData(), base._etag.constData(), (uint64_t) fs->inode, (uint64_t) base._inode,
(uint64_t) fs->size, (uint64_t) base._fileSize, *reinterpret_cast<short*>(&fs->remotePerm), *reinterpret_cast<short*>(&base._remotePerm), base._serverHasIgnoredFiles );
if (ctx->current == REMOTE_REPLICA && fs->etag != base._etag) {
fs->instruction = CSYNC_INSTRUCTION_EVAL;
// Preserve the EVAL flag later on if the type has changed.
if (tmp->type != fs->type) {
if (base._type != fs->type) {
fs->child_modified = true;
}
goto out;
}
if (ctx->current == LOCAL_REPLICA &&
(!_csync_mtime_equal(fs->modtime, tmp->modtime)
(!_csync_mtime_equal(fs->modtime, base._modtime)
// zero size in statedb can happen during migration
|| (tmp->size != 0 && fs->size != tmp->size))) {
|| (base._fileSize != 0 && fs->size != base._fileSize))) {
// Checksum comparison at this stage is only enabled for .eml files,
// check #4754 #4755
bool isEmlFile = csync_fnmatch("*.eml", fs->path, FNM_CASEFOLD) == 0;
if (isEmlFile && fs->size == tmp->size && !tmp->checksumHeader.isEmpty()) {
if (isEmlFile && fs->size == base._fileSize && !base._checksumHeader.isEmpty()) {
if (ctx->callbacks.checksum_hook) {
fs->checksumHeader = ctx->callbacks.checksum_hook(
_rel_to_abs(ctx, fs->path), tmp->checksumHeader,
_rel_to_abs(ctx, fs->path), base._checksumHeader,
ctx->callbacks.checksum_userdata);
}
bool checksumIdentical = false;
if (!fs->checksumHeader.isEmpty()) {
checksumIdentical = fs->checksumHeader == tmp->checksumHeader;
checksumIdentical = fs->checksumHeader == base._checksumHeader;
}
if (checksumIdentical) {
qCDebug(lcUpdate, "NOTE: Checksums are identical, file did not actually change: %s", fs->path.constData());
@ -224,16 +217,16 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
}
// Preserve the EVAL flag later on if the type has changed.
if (tmp->type != fs->type) {
if (base._type != fs->type) {
fs->child_modified = true;
}
fs->instruction = CSYNC_INSTRUCTION_EVAL;
goto out;
}
bool metadata_differ = (ctx->current == REMOTE_REPLICA && (fs->file_id != tmp->file_id
|| fs->remotePerm != tmp->remotePerm))
|| (ctx->current == LOCAL_REPLICA && fs->inode != tmp->inode);
bool metadata_differ = (ctx->current == REMOTE_REPLICA && (fs->file_id != base._fileId
|| fs->remotePerm != base._remotePerm))
|| (ctx->current == LOCAL_REPLICA && fs->inode != base._inode);
if (fs->type == CSYNC_FTW_TYPE_DIR && ctx->current == REMOTE_REPLICA
&& !metadata_differ && ctx->read_remote_from_db) {
/* If both etag and file id are equal for a directory, read all contents from
@ -248,7 +241,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
* that so that the reconciler can make advantage of.
*/
if( ctx->current == REMOTE_REPLICA ) {
fs->has_ignored_files = tmp->has_ignored_files;
fs->has_ignored_files = base._serverHasIgnoredFiles;
}
if (metadata_differ) {
/* file id or permissions has changed. Which means we need to update them in the DB. */
@ -262,9 +255,8 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
if (ctx->current == LOCAL_REPLICA) {
qCDebug(lcUpdate, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode);
tmp = csync_statedb_get_stat_by_inode(ctx, fs->inode);
if(_last_db_return_error(ctx)) {
OCC::SyncJournalFileRecord base;
if(!ctx->statedb->getFileRecordByInode(fs->inode, &base)) {
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
return -1;
}
@ -273,23 +265,23 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
fs->instruction = CSYNC_INSTRUCTION_NEW;
bool isRename =
tmp && tmp->inode == fs->inode && tmp->type == fs->type
&& (tmp->modtime == fs->modtime || fs->type == CSYNC_FTW_TYPE_DIR)
base.isValid() && base._inode == fs->inode && base._type == fs->type
&& (base._modtime == fs->modtime || fs->type == CSYNC_FTW_TYPE_DIR)
#ifdef NO_RENAME_EXTENSION
&& _csync_sameextension(tmp->path, fs->path)
&& _csync_sameextension(base._path, fs->path)
#endif
;
// Verify the checksum where possible
if (isRename && !tmp->checksumHeader.isEmpty() && ctx->callbacks.checksum_hook
if (isRename && !base._checksumHeader.isEmpty() && ctx->callbacks.checksum_hook
&& fs->type == CSYNC_FTW_TYPE_FILE) {
fs->checksumHeader = ctx->callbacks.checksum_hook(
_rel_to_abs(ctx, fs->path), tmp->checksumHeader,
_rel_to_abs(ctx, fs->path), base._checksumHeader,
ctx->callbacks.checksum_userdata);
if (!fs->checksumHeader.isEmpty()) {
qCDebug(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), tmp->checksumHeader.constData());
isRename = fs->checksumHeader == tmp->checksumHeader;
qCDebug(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), base._checksumHeader.constData());
isRename = fs->checksumHeader == base._checksumHeader;
}
}
@ -298,31 +290,30 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
/* inode found so the file has been renamed */
fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
if (fs->type == CSYNC_FTW_TYPE_DIR) {
csync_rename_record(ctx, tmp->path, fs->path);
csync_rename_record(ctx, base._path, fs->path);
}
}
goto out;
} else {
/* Remote Replica Rename check */
tmp = csync_statedb_get_stat_by_file_id(ctx, fs->file_id);
if(_last_db_return_error(ctx)) {
OCC::SyncJournalFileRecord base;
if(!ctx->statedb->getFileRecordByFileId(fs->file_id, &base)) {
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
return -1;
}
if(tmp ) { /* tmp existing at all */
if (tmp->type != fs->type) {
if (base.isValid()) { /* tmp existing at all */
if (base._type != fs->type) {
qCWarning(lcUpdate, "file types different is not!");
fs->instruction = CSYNC_INSTRUCTION_NEW;
goto out;
}
qCDebug(lcUpdate, "remote rename detected based on fileid %s --> %s", tmp->path.constData(), fs->path.constData());
qCDebug(lcUpdate, "remote rename detected based on fileid %s --> %s", base._path.constData(), fs->path.constData());
fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
if (fs->type == CSYNC_FTW_TYPE_DIR) {
csync_rename_record(ctx, tmp->path, fs->path);
csync_rename_record(ctx, base._path, fs->path);
} else {
if( tmp->etag != fs->etag ) {
if( base._etag != fs->etag ) {
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "ETags are different!"); */
/* File with different etag, don't do a rename, but download the file again */
fs->instruction = CSYNC_INSTRUCTION_NEW;
@ -442,10 +433,59 @@ int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
{
if( csync_statedb_get_below_path(ctx, uri) < 0 ) {
qCWarning(lcUpdate, "StateDB could not be read!");
int64_t count = 0;
QByteArray skipbase;
auto rowCallback = [ctx, &count, &skipbase](const OCC::SyncJournalFileRecord &rec) {
/* When selective sync is used, the database may have subtrees with a parent
* whose etag (md5) is _invalid_. These are ignored and shall not appear in the
* remote tree.
* Sometimes folders that are not ignored by selective sync get marked as
* _invalid_, but that is not a problem as the next discovery will retrieve
* their correct etags again and we don't run into this case.
*/
if( rec._etag == "_invalid_") {
qCDebug(lcUpdate, "%s selective sync excluded", rec._path.constData());
skipbase = rec._path;
skipbase += '/';
return;
}
/* Skip over all entries with the same base path. Note that this depends
* strongly on the ordering of the retrieved items. */
if( !skipbase.isEmpty() && rec._path.startsWith(skipbase) ) {
qCDebug(lcUpdate, "%s selective sync excluded because the parent is", rec._path.constData());
return;
} else {
skipbase.clear();
}
std::unique_ptr<csync_file_stat_t> st = csync_file_stat_t::fromSyncJournalFileRecord(rec);
/* Check for exclusion from the tree.
* Note that this is only a safety net in case the ignore list changes
* without a full remote discovery being triggered. */
CSYNC_EXCLUDE_TYPE excluded = csync_excluded_traversal(ctx->excludes, st->path, st->type);
if (excluded != CSYNC_NOT_EXCLUDED) {
qDebug(lcUpdate, "%s excluded (%d)", st->path.constData(), excluded);
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE
|| excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
return;
}
st->instruction = CSYNC_INSTRUCTION_IGNORE;
}
/* store into result list. */
ctx->remote.files[rec._path] = std::move(st);
++count;
};
if (!ctx->statedb->getFilesBelowPath(uri, rowCallback)) {
ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
return false;
}
qDebug(lcUpdate, "%" PRId64 " entries read below path %s from db.", count, uri);
return true;
}

View File

@ -36,7 +36,6 @@
#define CSYNC_LOG_CATEGORY_NAME "csync.util"
#include "csync_log.h"
#include "csync_statedb.h"
typedef struct {
const char *instr_str;

View File

@ -30,7 +30,6 @@
#include "csync_util.h"
#include "vio/csync_vio.h"
#include "vio/csync_vio_local.h"
#include "csync_statedb.h"
#include "common/c_jhash.h"
csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {

View File

@ -470,7 +470,7 @@ void Folder::slotWatchedPathChanged(const QString &path)
SyncJournalFileRecord record;
if (_journal.getFileRecord(relativePath, &record)
&& record.isValid()
&& !FileSystem::fileChanged(path, record._fileSize, Utility::qDateTimeToTime_t(record._modtime))) {
&& !FileSystem::fileChanged(path, record._fileSize, record._modtime)) {
qCInfo(lcFolder) << "Ignoring spurious notification for file" << relativePath;
return; // probably a spurious notification
}

View File

@ -112,13 +112,13 @@ PropagateItemJob::~PropagateItemJob()
}
}
static time_t getMinBlacklistTime()
static qint64 getMinBlacklistTime()
{
return qMax(qgetenv("OWNCLOUD_BLACKLIST_TIME_MIN").toInt(),
25); // 25 seconds
}
static time_t getMaxBlacklistTime()
static qint64 getMaxBlacklistTime()
{
int v = qgetenv("OWNCLOUD_BLACKLIST_TIME_MAX").toInt();
if (v > 0)
@ -142,15 +142,15 @@ static SyncJournalErrorBlacklistRecord createBlacklistEntry(
entry._renameTarget = item._renameTarget;
entry._retryCount = old._retryCount + 1;
static time_t minBlacklistTime(getMinBlacklistTime());
static time_t maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
static qint64 minBlacklistTime(getMinBlacklistTime());
static qint64 maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
entry._ignoreDuration = old._ignoreDuration * 5;
if (item._httpErrorCode == 403) {
qCWarning(lcPropagator) << "Probably firewall error: " << item._httpErrorCode << ", blacklisting up to 1h only";
entry._ignoreDuration = qMin(entry._ignoreDuration, time_t(60 * 60));
entry._ignoreDuration = qMin(entry._ignoreDuration, qint64(60 * 60));
} else if (item._httpErrorCode == 413 || item._httpErrorCode == 415) {
qCWarning(lcPropagator) << "Fatal Error condition" << item._httpErrorCode << ", maximum blacklist ignore time!";

View File

@ -171,7 +171,7 @@ void PropagateRemoteMove::finalize()
propagator()->_journal->deleteFileRecord(_item->_originalFile);
SyncJournalFileRecord record = _item->toSyncJournalFileRecordWithInode(propagator()->getFilePath(_item->_renameTarget));
record._path = _item->_renameTarget;
record._path = _item->_renameTarget.toUtf8();
if (oldRecord.isValid()) {
record._checksumHeader = oldRecord._checksumHeader;
if (record._fileSize != oldRecord._fileSize) {

View File

@ -83,7 +83,7 @@ void PropagateUploadFileNG::doStartUpload()
propagator()->_activeJobList.append(this);
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
if (progressInfo._valid && Utility::qDateTimeToTime_t(progressInfo._modtime) == _item->_modtime) {
if (progressInfo._valid && progressInfo._modtime == _item->_modtime) {
_transferId = progressInfo._transferid;
auto url = chunkUrl();
auto job = new LsColJob(propagator()->account(), url, this);
@ -229,7 +229,7 @@ void PropagateUploadFileNG::startNewUpload()
SyncJournalDb::UploadInfo pi;
pi._valid = true;
pi._transferid = _transferId;
pi._modtime = Utility::qDateTimeFromTime_t(_item->_modtime);
pi._modtime = _item->_modtime;
propagator()->_journal->setUploadInfo(_item->_file, pi);
propagator()->_journal->commit("Upload info");
QMap<QByteArray, QByteArray> headers;

View File

@ -43,7 +43,7 @@ void PropagateUploadFileV1::doStartUpload()
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
if (progressInfo._valid && Utility::qDateTimeToTime_t(progressInfo._modtime) == _item->_modtime) {
if (progressInfo._valid && progressInfo._modtime == _item->_modtime) {
_startChunk = progressInfo._chunk;
_transferId = progressInfo._transferid;
qCInfo(lcPropagateUpload) << _item->_file << ": Resuming from chunk " << _startChunk;
@ -272,7 +272,7 @@ void PropagateUploadFileV1::slotPutFinished()
}
pi._chunk = (currentChunk + _startChunk + 1) % _chunkCount; // next chunk to start with
pi._transferid = _transferId;
pi._modtime = Utility::qDateTimeFromTime_t(_item->_modtime);
pi._modtime = _item->_modtime;
pi._errorCount = 0; // successful chunk upload resets
propagator()->_journal->setUploadInfo(_item->_file, pi);
propagator()->_journal->commit("Upload info");

View File

@ -240,7 +240,7 @@ void PropagateLocalRename::start()
_item->_file = _item->_renameTarget;
SyncJournalFileRecord record = _item->toSyncJournalFileRecordWithInode(targetFile);
record._path = _item->_renameTarget;
record._path = _item->_renameTarget.toUtf8();
if (oldRecord.isValid()) {
record._checksumHeader = oldRecord._checksumHeader;
}

View File

@ -87,8 +87,7 @@ SyncEngine::SyncEngine(AccountPtr account, const QString &localPath,
// Everything in the SyncEngine expects a trailing slash for the localPath.
ASSERT(localPath.endsWith(QLatin1Char('/')));
const QString dbFile = _journal->databaseFilePath();
_csync_ctx.reset(new CSYNC(localPath.toUtf8().data(), dbFile.toUtf8().data()));
_csync_ctx.reset(new CSYNC(localPath.toUtf8().data(), journal));
_excludedFiles.reset(new ExcludedFiles(&_csync_ctx->excludes));
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));

View File

@ -26,8 +26,8 @@ Q_LOGGING_CATEGORY(lcFileItem, "sync.fileitem", QtInfoMsg)
SyncJournalFileRecord SyncFileItem::toSyncJournalFileRecordWithInode(const QString &localFileName)
{
SyncJournalFileRecord rec;
rec._path = _file;
rec._modtime = Utility::qDateTimeFromTime_t(_modtime);
rec._path = _file.toUtf8();
rec._modtime = _modtime;
rec._type = _type;
rec._etag = _etag;
rec._fileId = _fileId;
@ -57,7 +57,7 @@ SyncFileItemPtr SyncFileItem::fromSyncJournalFileRecord(const SyncJournalFileRec
SyncFileItemPtr item(new SyncFileItem);
item->_file = rec._path;
item->_inode = rec._inode;
item->_modtime = Utility::qDateTimeToTime_t(rec._modtime);
item->_modtime = rec._modtime;
item->_type = static_cast<SyncFileItem::Type>(rec._type);
item->_etag = rec._etag;
item->_fileId = rec._fileId;

View File

@ -33,7 +33,6 @@ IF( APPLE )
list(APPEND FolderWatcher_SRC ../src/gui/folderwatcher_mac.cpp)
list(APPEND FolderWatcher_SRC ../src/gui/socketapisocket_mac.mm)
ENDIF()
owncloud_add_test(CSyncSqlite "")
owncloud_add_test(NetrcParser ../src/cmd/netrcparser.cpp)
owncloud_add_test(OwnSql "")
owncloud_add_test(SyncJournalDB "")

View File

@ -32,13 +32,9 @@ add_cmocka_test(check_std_c_time std_tests/check_std_c_time.c ${TEST_TARGET_LIBR
add_cmocka_test(check_csync_log csync_tests/check_csync_log.cpp ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_csync_exclude csync_tests/check_csync_exclude.cpp ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_csync_statedb_load csync_tests/check_csync_statedb_load.cpp ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_csync_util csync_tests/check_csync_util.cpp ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_csync_misc csync_tests/check_csync_misc.cpp ${TEST_TARGET_LIBRARIES})
# csync tests which require init
add_cmocka_test(check_csync_statedb_query csync_tests/check_csync_statedb_query.cpp ${TEST_TARGET_LIBRARIES})
# vio
add_cmocka_test(check_vio vio_tests/check_vio.cpp ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_vio_ext vio_tests/check_vio_ext.cpp ${TEST_TARGET_LIBRARIES})

View File

@ -32,7 +32,7 @@
static int setup(void **state) {
CSYNC *csync;
csync = new CSYNC("/tmp/check_csync1", "");
csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
*state = csync;
return 0;
@ -42,7 +42,7 @@ static int setup_init(void **state) {
CSYNC *csync;
int rc;
csync = new CSYNC("/tmp/check_csync1", "");
csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes));
assert_int_equal(rc, 0);
@ -67,7 +67,9 @@ static int teardown(void **state) {
CSYNC *csync = (CSYNC*)*state;
int rc;
auto statedb = csync->statedb;
delete csync;
delete statedb;
rc = system("rm -rf /tmp/check_csync1");
assert_int_equal(rc, 0);

View File

@ -1,127 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* 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) any later version.
*
* 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 <string.h>
#define CSYNC_TEST 1
#include "csync_statedb.cpp"
#include "torture.h"
#define TESTDB "/tmp/check_csync1/test.db"
static int setup(void **state) {
CSYNC *csync;
int rc;
rc = system("rm -rf /tmp/check_csync1");
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp/check_csync1", TESTDB);
*state = csync;
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
assert_int_equal(rc, SQLITE_OK);
rc = sqlite3_close(db);
assert_int_equal(rc, SQLITE_OK);
return 0;
}
static int teardown(void **state) {
CSYNC *csync = (CSYNC*)*state;
int rc;
delete csync;
rc = system("rm -rf /tmp/check_csync1");
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_csync_statedb_load(void **state)
{
CSYNC *csync = (CSYNC*)*state;
int rc;
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
assert_int_equal(rc, 0);
sqlite3_close(csync->statedb.db);
}
static void check_csync_statedb_close(void **state)
{
CSYNC *csync = (CSYNC*)*state;
csync_stat_t sb;
time_t modtime;
mbchar_t *testdb = c_utf8_path_to_locale(TESTDB);
int rc;
/* statedb not written */
csync_statedb_load(csync, TESTDB, &csync->statedb.db);
rc = _tstat(testdb, &sb);
assert_int_equal(rc, 0);
modtime = sb.st_mtime;
rc = csync_statedb_close(csync);
assert_int_equal(rc, 0);
rc = _tstat(testdb, &sb);
assert_int_equal(rc, 0);
assert_int_equal(modtime, sb.st_mtime);
csync_statedb_load(csync, TESTDB, &csync->statedb.db);
rc = _tstat(testdb, &sb);
assert_int_equal(rc, 0);
modtime = sb.st_mtime;
/* wait a sec or the modtime will be the same */
sleep(1);
/* statedb written */
rc = csync_statedb_close(csync);
assert_int_equal(rc, 0);
rc = _tstat(testdb, &sb);
assert_int_equal(rc, 0);
c_free_locale_string(testdb);
}
int torture_run_tests(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(check_csync_statedb_load, setup, teardown),
cmocka_unit_test_setup_teardown(check_csync_statedb_close, setup, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -1,217 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* 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) any later version.
*
* 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
*/
#define CSYNC_TEST 1
#include "csync_statedb.cpp"
#include "torture.h"
#define TESTDB "/tmp/check_csync1/test.db"
#define TESTDBTMP "/tmp/check_csync1/test.db.ctmp"
static int setup(void **state)
{
CSYNC *csync;
int rc = 0;
rc = system("rm -rf /tmp/check_csync1");
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync");
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp/check_csync1", TESTDB);
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
assert_int_equal(rc, SQLITE_OK);
rc = sqlite3_close(db);
assert_int_equal(rc, SQLITE_OK);
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
assert_int_equal(rc, 0);
*state = csync;
return 0;
}
static int setup_db(void **state)
{
char *errmsg;
int rc = 0;
sqlite3 *db = NULL;
const char *sql = "CREATE TABLE IF NOT EXISTS metadata ("
"phash INTEGER(8),"
"pathlen INTEGER,"
"path VARCHAR(4096),"
"inode INTEGER,"
"uid INTEGER,"
"gid INTEGER,"
"mode INTEGER,"
"modtime INTEGER(8),"
"type INTEGER,"
"md5 VARCHAR(32),"
"PRIMARY KEY(phash)"
");";
const char *sql2 = "INSERT INTO metadata"
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5) VALUES"
"(42, 42, 'Its funny stuff', 23, 42, 43, 55, 66, 2, 54);";
setup(state);
rc = sqlite3_open( TESTDB, &db);
assert_int_equal(rc, SQLITE_OK);
rc = sqlite3_exec( db, sql, NULL, NULL, &errmsg );
assert_int_equal(rc, SQLITE_OK);
rc = sqlite3_exec( db, sql2, NULL, NULL, &errmsg );
assert_int_equal(rc, SQLITE_OK);
sqlite3_close(db);
return 0;
}
static int teardown(void **state) {
CSYNC *csync = (CSYNC*)*state;
int rc = 0;
delete csync;
rc = system("rm -rf /tmp/check_csync");
assert_int_equal(rc, 0);
rc = system("rm -rf /tmp/check_csync1");
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_csync_statedb_query_statement(void **state)
{
CSYNC *csync = (CSYNC*)*state;
c_strlist_t *result;
result = csync_statedb_query(csync->statedb.db, "");
assert_null(result);
if (result != NULL) {
c_strlist_destroy(result);
}
result = csync_statedb_query(csync->statedb.db, "SELECT;");
assert_null(result);
if (result != NULL) {
c_strlist_destroy(result);
}
}
static void check_csync_statedb_drop_tables(void **state)
{
// CSYNC *csync = (CSYNC*)*state;
int rc = 0;
(void) state;
// rc = csync_statedb_drop_tables(csync->statedb.db);
assert_int_equal(rc, 0);
// rc = csync_statedb_create_tables(csync->statedb.db);
assert_int_equal(rc, 0);
// rc = csync_statedb_drop_tables(csync->statedb.db);
assert_int_equal(rc, 0);
}
static void check_csync_statedb_insert_metadata(void **state)
{
CSYNC *csync = (CSYNC*)*state;
std::unique_ptr<csync_file_stat_t> st;
int i, rc = 0;
// rc = csync_statedb_create_tables(csync->statedb.db);
assert_int_equal(rc, 0);
for (i = 0; i < 100; i++) {
st.reset(new csync_file_stat_t);
st->path = QString("file_%1").arg(i).toUtf8();
csync->local.files[st->path] = std::move(st);
}
// rc = csync_statedb_insert_metadata(csync, csync->statedb.db);
assert_int_equal(rc, 0);
}
static void check_csync_statedb_write(void **state)
{
CSYNC *csync = (CSYNC*)*state;
std::unique_ptr<csync_file_stat_t> st;
int i, rc = 0;
for (i = 0; i < 100; i++) {
st.reset(new csync_file_stat_t);
st->path = QString("file_%1").arg(i).toUtf8();
csync->local.files[st->path] = std::move(st);
assert_int_equal(rc, 0);
}
// rc = csync_statedb_write(csync, csync->statedb.db);
assert_int_equal(rc, 0);
}
static void check_csync_statedb_get_stat_by_path_not_found(void **state)
{
CSYNC *csync = (CSYNC*)*state;
std::unique_ptr<csync_file_stat_t> tmp;
tmp = csync_statedb_get_stat_by_path(csync, "666");
assert_null(tmp.get());
}
static void check_csync_statedb_get_stat_by_inode_not_found(void **state)
{
CSYNC *csync = (CSYNC*)*state;
std::unique_ptr<csync_file_stat_t> tmp;
tmp = csync_statedb_get_stat_by_inode(csync, (ino_t) 666);
assert_null(tmp.get());
}
int torture_run_tests(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown),
cmocka_unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown),
cmocka_unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown),
cmocka_unit_test_setup_teardown(check_csync_statedb_write, setup, teardown),
cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_path_not_found, setup_db, teardown),
cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_inode_not_found, setup_db, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -98,12 +98,10 @@ static int setup(void **state)
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp/check_csync1", TESTDB);
/* Create a new db with metadata */
sqlite3 *db;
// csync->statedb.file = c_strdup(TESTDB);
rc = sqlite3_open(csync->statedb.file, &db);
rc = sqlite3_open(TESTDB, &db);
statedb_create_metadata_table(db);
if( firstrun ) {
statedb_insert_metadata(db);
@ -111,8 +109,8 @@ static int setup(void **state)
}
sqlite3_close(db);
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(TESTDB));
assert_true(csync->statedb->isConnected());
*state = csync;
@ -128,7 +126,7 @@ static int setup_ftw(void **state)
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp", TESTDB);
csync = new CSYNC("/tmp", new OCC::SyncJournalDb(TESTDB));
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
@ -137,10 +135,9 @@ static int setup_ftw(void **state)
rc = sqlite3_close(db);
assert_int_equal(rc, SQLITE_OK);
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp", new OCC::SyncJournalDb(TESTDB));
assert_true(csync->statedb->isConnected());
csync->statedb.file = c_strdup( TESTDB );
*state = csync;
return 0;
@ -150,8 +147,10 @@ static int teardown(void **state)
{
CSYNC *csync = (CSYNC*)*state;
unlink( csync->statedb.file);
unlink(TESTDB);
auto statedb = csync->statedb;
delete csync;
delete statedb;
*state = NULL;
@ -288,7 +287,6 @@ static void check_csync_detect_update_db_rename(void **state)
int rc = 0;
fs = create_fstat("wurst.txt", 0, 42);
csync_set_statedb_exists(csync, 1);
rc = _csync_detect_update(csync, std::move(fs));
assert_int_equal(rc, 0);

View File

@ -49,7 +49,7 @@ static int setup(void **state)
rc = system("rm -rf /tmp/csync_test");
assert_int_equal(rc, 0);
csync = new CSYNC("/tmp/check_csync1", "");
csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
csync->current = LOCAL_REPLICA;
@ -78,7 +78,9 @@ static int teardown(void **state) {
CSYNC *csync = (CSYNC*)*state;
int rc;
auto statedb = csync->statedb;
delete csync;
delete statedb;
rc = chdir(wd_buffer);
assert_int_equal(rc, 0);

View File

@ -97,7 +97,7 @@ static int setup_testenv(void **state) {
statevar *mystate = (statevar*)malloc( sizeof(statevar) );
mystate->result = NULL;
mystate->csync = new CSYNC("/tmp/check_csync1", "");
mystate->csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
mystate->csync->current = LOCAL_REPLICA;
@ -124,7 +124,9 @@ static int teardown(void **state) {
output("================== Tearing down!\n");
auto statedb = csync->statedb;
delete csync;
delete statedb;
rc = _tchdir(wd_buffer);
assert_int_equal(rc, 0);

View File

@ -1,80 +0,0 @@
/*
This software is in the public domain, furnished "as is", without technical
support, and with no warranty, express or implied, as to its usefulness for
any purpose.
*/
#include "csync_statedb.h"
#include "csync_private.h"
#include <QtTest>
class TestCSyncSqlite : public QObject
{
Q_OBJECT
private:
CSYNC *_ctx;
private slots:
void initTestCase() {
int rc;
QString db = QCoreApplication::applicationDirPath() + "/test_journal.db";
_ctx = new CSYNC("/tmp/check_csync1", db.toLocal8Bit());
rc = csync_statedb_load(_ctx, _ctx->statedb.file, &(_ctx->statedb.db));
QVERIFY(rc == 0);
}
void testFullResult() {
std::unique_ptr<csync_file_stat_t> st = csync_statedb_get_stat_by_path( _ctx, "test2/zu/zuzu" );
QVERIFY(st.get());
QCOMPARE( QString::fromUtf8(st->path), QLatin1String("test2/zu/zuzu") );
QCOMPARE( QString::number(st->inode), QString::number(1709554));
QCOMPARE( QString::number(st->modtime), QString::number(1384415006));
QCOMPARE( QString::number(st->type), QString::number(2));
QCOMPARE( QString::fromUtf8(st->etag), QLatin1String("52847f2090665"));
QCOMPARE( QString::fromUtf8(st->file_id), QLatin1String("00000557525d5af3d9625"));
}
void testByHash() {
std::unique_ptr<csync_file_stat_t> st = csync_statedb_get_stat_by_path(_ctx, "documents/c1");
QVERIFY(st.get());
QCOMPARE(QString::fromUtf8(st->path), QLatin1String("documents/c1"));
st = csync_statedb_get_stat_by_path(_ctx, "documents/c1/c2");
QVERIFY(st.get());
QCOMPARE(QString::fromUtf8(st->path), QLatin1String("documents/c1/c2"));
}
void testByInode() {
std::unique_ptr<csync_file_stat_t> st = csync_statedb_get_stat_by_inode(_ctx, 1709555);
QVERIFY(st.get());
QCOMPARE(QString::fromUtf8(st->path), QLatin1String("test2/zu/zuzu/zuzuzu"));
st = csync_statedb_get_stat_by_inode(_ctx, 1706571);
QVERIFY(st.get());
QCOMPARE(QString::fromUtf8(st->path), QLatin1String("Shared/for_kf/a2"));
}
void testByFileId() {
std::unique_ptr<csync_file_stat_t> st = csync_statedb_get_stat_by_file_id(_ctx, "00000556525d5af3d9625");
QVERIFY(st.get());
QCOMPARE(QString::fromUtf8(st->path), QLatin1String("test2/zu"));
st = csync_statedb_get_stat_by_file_id(_ctx, "-0000001525d5af3d9625");
QVERIFY(st.get());
QCOMPARE(QString::fromUtf8(st->path), QLatin1String("Shared"));
}
void cleanupTestCase() {
csync_statedb_close(_ctx);
delete _ctx;
_ctx = nullptr;
}
};
QTEST_GUILESS_MAIN(TestCSyncSqlite)
#include "testcsyncsqlite.moc"

View File

@ -183,7 +183,7 @@ private slots:
// Remove subFolderA with selectiveSync:
fakeFolder.syncEngine().journal()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
{"parentFolder/subFolderA/"});
fakeFolder.syncEngine().journal()->avoidReadFromDbOnNextSync("parentFolder/subFolderA/");
fakeFolder.syncEngine().journal()->avoidReadFromDbOnNextSync(QByteArrayLiteral("parentFolder/subFolderA/"));
fakeFolder.syncOnce();
@ -238,7 +238,7 @@ private slots:
// folders are uploaded anyway is some circumstances.
FakeFolder fakeFolder{FileInfo{ QString(), {
FileInfo { QStringLiteral("parentFolder"), {
FileInfo{ QStringLiteral("subFolder"), {
FileInfo{ QStringLiteral("subFolderA"), {
{ QStringLiteral("fileA.txt"), 400 },
{ QStringLiteral("fileB.txt"), 400, 'o' },
FileInfo { QStringLiteral("subsubFolder"), {
@ -252,23 +252,24 @@ private slots:
{ QStringLiteral("fileF.txt"), 400, 'o' }
}}
}}
}}
}},
FileInfo{ QStringLiteral("subFolderB"), {} }
}}
}}};
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
auto expectedServerState = fakeFolder.currentRemoteState();
// Remove subFolder with selectiveSync:
// Remove subFolderA with selectiveSync:
fakeFolder.syncEngine().journal()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
{"parentFolder/subFolder/"});
fakeFolder.syncEngine().journal()->avoidReadFromDbOnNextSync("parentFolder/subFolder/");
{"parentFolder/subFolderA/"});
fakeFolder.syncEngine().journal()->avoidReadFromDbOnNextSync(QByteArrayLiteral("parentFolder/subFolderA/"));
// But touch local file before the next sync, such that the local folder
// can't be removed
fakeFolder.localModifier().setContents("parentFolder/subFolder/fileB.txt", 'n');
fakeFolder.localModifier().setContents("parentFolder/subFolder/subsubFolder/fileD.txt", 'n');
fakeFolder.localModifier().setContents("parentFolder/subFolder/anotherFolder/subsubFolder/fileF.txt", 'n');
fakeFolder.localModifier().setContents("parentFolder/subFolderA/fileB.txt", 'n');
fakeFolder.localModifier().setContents("parentFolder/subFolderA/subsubFolder/fileD.txt", 'n');
fakeFolder.localModifier().setContents("parentFolder/subFolderA/anotherFolder/subsubFolder/fileF.txt", 'n');
// Several follow-up syncs don't change the state at all,
// in particular the remote state doesn't change and fileB.txt
@ -282,14 +283,15 @@ private slots:
QCOMPARE(fakeFolder.currentRemoteState(), expectedServerState);
// The local state should still have subFolderA
auto local = fakeFolder.currentLocalState();
QVERIFY(local.find("parentFolder/subFolder"));
QVERIFY(!local.find("parentFolder/subFolder/fileA.txt"));
QVERIFY(local.find("parentFolder/subFolder/fileB.txt"));
QVERIFY(!local.find("parentFolder/subFolder/subsubFolder/fileC.txt"));
QVERIFY(local.find("parentFolder/subFolder/subsubFolder/fileD.txt"));
QVERIFY(!local.find("parentFolder/subFolder/anotherFolder/subsubFolder/fileE.txt"));
QVERIFY(local.find("parentFolder/subFolder/anotherFolder/subsubFolder/fileF.txt"));
QVERIFY(!local.find("parentFolder/subFolder/anotherFolder/emptyFolder"));
QVERIFY(local.find("parentFolder/subFolderA"));
QVERIFY(!local.find("parentFolder/subFolderA/fileA.txt"));
QVERIFY(local.find("parentFolder/subFolderA/fileB.txt"));
QVERIFY(!local.find("parentFolder/subFolderA/subsubFolder/fileC.txt"));
QVERIFY(local.find("parentFolder/subFolderA/subsubFolder/fileD.txt"));
QVERIFY(!local.find("parentFolder/subFolderA/anotherFolder/subsubFolder/fileE.txt"));
QVERIFY(local.find("parentFolder/subFolderA/anotherFolder/subsubFolder/fileF.txt"));
QVERIFY(!local.find("parentFolder/subFolderA/anotherFolder/emptyFolder"));
QVERIFY(local.find("parentFolder/subFolderB"));
}
}
}

View File

@ -26,9 +26,9 @@ public:
QVERIFY(_tempDir.isValid());
}
QDateTime dropMsecs(QDateTime time)
qint64 dropMsecs(QDateTime time)
{
return Utility::qDateTimeFromTime_t(Utility::qDateTimeToTime_t(time));
return Utility::qDateTimeToTime_t(time);
}
private slots:
@ -46,7 +46,7 @@ private slots:
void testFileRecord()
{
SyncJournalFileRecord record;
QVERIFY(_db.getFileRecord("nonexistant", &record));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("nonexistant"), &record));
QVERIFY(!record.isValid());
record._path = "foo";
@ -61,13 +61,13 @@ private slots:
QVERIFY(_db.setFileRecord(record));
SyncJournalFileRecord storedRecord;
QVERIFY(_db.getFileRecord("foo", &storedRecord));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("foo"), &storedRecord));
QVERIFY(storedRecord == record);
// Update checksum
record._checksumHeader = "Adler32:newchecksum";
_db.updateFileRecordChecksum("foo", "newchecksum", "Adler32");
QVERIFY(_db.getFileRecord("foo", &storedRecord));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("foo"), &storedRecord));
QVERIFY(storedRecord == record);
// Update metadata
@ -79,11 +79,11 @@ private slots:
record._remotePerm = RemotePermissions("NV");
record._fileSize = 289055;
_db.setFileRecordMetadata(record);
QVERIFY(_db.getFileRecord("foo", &storedRecord));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("foo"), &storedRecord));
QVERIFY(storedRecord == record);
QVERIFY(_db.deleteFileRecord("foo"));
QVERIFY(_db.getFileRecord("foo", &record));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("foo"), &record));
QVERIFY(!record.isValid());
}
@ -95,11 +95,11 @@ private slots:
record._path = "foo-checksum";
record._remotePerm = RemotePermissions("RW");
record._checksumHeader = "MD5:mychecksum";
record._modtime = QDateTime::currentDateTimeUtc();
record._modtime = Utility::qDateTimeToTime_t(QDateTime::currentDateTimeUtc());
QVERIFY(_db.setFileRecord(record));
SyncJournalFileRecord storedRecord;
QVERIFY(_db.getFileRecord("foo-checksum", &storedRecord));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("foo-checksum"), &storedRecord));
QVERIFY(storedRecord._path == record._path);
QVERIFY(storedRecord._remotePerm == record._remotePerm);
QVERIFY(storedRecord._checksumHeader == record._checksumHeader);
@ -108,19 +108,19 @@ private slots:
// Attention: compare time_t types here, as QDateTime seem to maintain
// milliseconds internally, which disappear in sqlite. Go for full seconds here.
QVERIFY(storedRecord._modtime.toTime_t() == record._modtime.toTime_t());
QVERIFY(storedRecord._modtime == record._modtime);
QVERIFY(storedRecord == record);
}
{
SyncJournalFileRecord record;
record._path = "foo-nochecksum";
record._remotePerm = RemotePermissions("RWN");
record._modtime = QDateTime::currentDateTimeUtc();
record._modtime = Utility::qDateTimeToTime_t(QDateTime::currentDateTimeUtc());
QVERIFY(_db.setFileRecord(record));
SyncJournalFileRecord storedRecord;
QVERIFY(_db.getFileRecord("foo-nochecksum", &storedRecord));
QVERIFY(_db.getFileRecord(QByteArrayLiteral("foo-nochecksum"), &storedRecord));
QVERIFY(storedRecord == record);
}
}

View File

@ -35,7 +35,7 @@ private slots:
SyncJournalDb::UploadInfo uploadInfo;
uploadInfo._transferid = 1;
uploadInfo._valid = true;
uploadInfo._modtime = modTime;
uploadInfo._modtime = Utility::qDateTimeToTime_t(modTime);
fakeFolder.syncEngine().journal()->setUploadInfo("A/a0", uploadInfo);
fakeFolder.uploadState().mkdir("1");