Checksums: Reuse the discovery checksum where possible

This commit is contained in:
Christian Kamm 2015-11-23 13:44:49 +01:00
parent 6cf5fc7f7d
commit 2458f07ca1
8 changed files with 59 additions and 28 deletions

View file

@ -421,6 +421,8 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
trav.error_status = cur->error_status;
trav.should_update_metadata = cur->should_update_metadata;
trav.has_ignored_files = cur->has_ignored_files;
trav.checksum = cur->checksum;
trav.checksumTypeId = cur->checksumTypeId;
if( other_node ) {
csync_file_stat_t *other_stat = (csync_file_stat_t*)other_node->data;

View file

@ -262,6 +262,10 @@ struct csync_tree_walk_file_s {
const char *remotePerm;
char *directDownloadUrl;
char *directDownloadCookies;
const char *checksum;
uint32_t checksumTypeId;
struct {
int64_t size;
time_t modtime;
@ -301,11 +305,9 @@ typedef void (*csync_vio_closedir_hook) (csync_vio_handle_t *dhhandle,
typedef int (*csync_vio_stat_hook) (csync_vio_handle_t *dhhandle,
void *userdata);
/* compute the checksum of the given \a checksumTypeId for \a path
* and return true if it's the same as \a checksum */
typedef bool (*csync_checksum_hook) (const char *path,
uint32_t checksumTypeId, const char *checksum,
void *userdata);
/* Compute the checksum of the given \a checksumTypeId for \a path. */
typedef const char* (*csync_checksum_hook) (
const char *path, uint32_t checksumTypeId, void *userdata);
/**
* @brief Allocate a csync context.

View file

@ -197,7 +197,7 @@ struct csync_file_stat_s {
char *directDownloadCookies;
char remotePerm[REMOTE_PERM_BUF_SIZE+1];
char *checksum;
const char *checksum;
uint32_t checksumTypeId;
CSYNC_STATUS error_status;

View file

@ -280,12 +280,16 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|| (tmp->size != 0 && fs->size != tmp->size))) {
if (fs->size == tmp->size && tmp->checksumTypeId) {
bool checksumIdentical = false;
if (ctx->callbacks.checksum_hook) {
checksumIdentical = ctx->callbacks.checksum_hook(
file, tmp->checksumTypeId, tmp->checksum,
st->checksum = ctx->callbacks.checksum_hook(
file, tmp->checksumTypeId,
ctx->callbacks.checksum_userdata);
}
bool checksumIdentical = false;
if (st->checksum) {
st->checksumTypeId = tmp->checksumTypeId;
checksumIdentical = strncmp(st->checksum, tmp->checksum, 1000) == 0;
}
if (checksumIdentical) {
st->instruction = CSYNC_INSTRUCTION_NONE;
st->should_update_metadata = true;

View file

@ -160,26 +160,35 @@ CSyncChecksumHook::CSyncChecksumHook(SyncJournalDb *journal)
{
}
bool CSyncChecksumHook::hook(const char* path, uint32_t checksumTypeId, const char* checksum, void *this_obj)
const char* CSyncChecksumHook::hook(const char* path, uint32_t checksumTypeId, void *this_obj)
{
CSyncChecksumHook* checksumHook = static_cast<CSyncChecksumHook*>(this_obj);
return checksumHook->check(QString::fromUtf8(path), checksumTypeId, QByteArray(checksum));
QByteArray checksum = checksumHook->compute(QString::fromUtf8(path), checksumTypeId);
if (checksum.isNull()) {
return NULL;
}
char* result = (char*)malloc(checksum.size() + 1);
memcpy(result, checksum.constData(), checksum.size());
result[checksum.size()] = 0;
return result;
}
bool CSyncChecksumHook::check(const QString& path, int checksumTypeId, const QByteArray& checksum)
QByteArray CSyncChecksumHook::compute(const QString& path, int checksumTypeId)
{
QByteArray checksumType = _journal->getChecksumType(checksumTypeId);
if (checksumType.isEmpty()) {
qDebug() << "Checksum type" << checksumTypeId << "not found";
return false;
return QByteArray();
}
QByteArray newChecksum = ComputeChecksum::computeNow(path, checksumType);
if (newChecksum.isNull()) {
QByteArray checksum = ComputeChecksum::computeNow(path, checksumType);
if (checksum.isNull()) {
qDebug() << "Failed to compute checksum" << checksumType << "for" << path;
return false;
return QByteArray();
}
return newChecksum == checksum;
return checksum;
}

View file

@ -121,15 +121,15 @@ public:
explicit CSyncChecksumHook(SyncJournalDb* journal);
/**
* Returns true if the checksum for \a path is the same as the one provided.
* Returns the checksum value for \a path for the given \a checksumTypeId.
*
* Called from csync, where a instance of CSyncChecksumHook
* has to be set as userdata.
* Called from csync, where a instance of CSyncChecksumHook has
* to be set as userdata.
* The return value will be owned by csync.
*/
static bool hook(const char* path, uint32_t checksumTypeId, const char* checksum,
void* this_obj);
static const char* hook(const char* path, uint32_t checksumTypeId, void* this_obj);
bool check(const QString& path, int checksumTypeId, const QByteArray& checksum);
QByteArray compute(const QString& path, int checksumTypeId);
private:
SyncJournalDb* _journal;

View file

@ -216,16 +216,24 @@ void PropagateUploadFileQNAM::start()
_stopWatch.start();
// Compute the content checksum.
auto computeChecksum = new ComputeChecksum(this);
QByteArray contentChecksumType;
// We currently only do content checksums for the particular .eml case
// This should be done more generally in the future!
if (filePath.endsWith(QLatin1String(".eml"), Qt::CaseInsensitive)) {
computeChecksum->setChecksumType("MD5");
} else {
computeChecksum->setChecksumType(QByteArray());
contentChecksumType = "MD5";
}
// Maybe the discovery already computed the checksum?
if (_item->_contentChecksumType == contentChecksumType
&& !_item->_contentChecksum.isEmpty()) {
slotComputeTransmissionChecksum(contentChecksumType, _item->_contentChecksum);
return;
}
// Compute the content checksum.
auto computeChecksum = new ComputeChecksum(this);
computeChecksum->setChecksumType(contentChecksumType);
connect(computeChecksum, SIGNAL(done(QByteArray,QByteArray)),
SLOT(slotComputeTransmissionChecksum(QByteArray,QByteArray)));
computeChecksum->start(filePath);

View file

@ -375,6 +375,12 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
item->_serverHasIgnoredFiles = (file->has_ignored_files > 0);
}
// Sometimes the discovery computes checksums for local files
if (!remote && file->checksum && file->checksumTypeId) {
item->_contentChecksum = QByteArray(file->checksum);
item->_contentChecksumType = _journal->getChecksumType(file->checksumTypeId);
}
// record the seen files to be able to clean the journal later
_seenFiles.insert(item->_file);
if (!renameTarget.isEmpty()) {