csync_util.cpp
csync_misc.cpp
- csync_update.cpp
- csync_reconcile.cpp
-
- csync_rename.cpp
-
- vio/csync_vio.cpp
-
std/c_alloc.c
std/c_string.c
std/c_time.c
#define _GNU_SOURCE
#endif
-#include <cassert>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <ctime>
-#include <sys/types.h>
-
-
-#include "c_lib.h"
#include "csync_private.h"
-#include "csync_exclude.h"
-#include "csync_util.h"
-#include "csync_misc.h"
-#include "std/c_private.h"
-#include "csync_update.h"
-#include "csync_reconcile.h"
-#include "vio/csync_vio.h"
-
-#include "csync_rename.h"
-#include "common/c_jhash.h"
#include "common/syncjournalfilerecord.h"
-Q_LOGGING_CATEGORY(lcCSync, "nextcloud.sync.csync.csync", QtInfoMsg)
-
-
-csync_s::csync_s(const char *localUri, OCC::SyncJournalDb *statedb)
- : statedb(statedb)
-{
- size_t len = 0;
-
- /* remove trailing slashes */
- len = strlen(localUri);
- while(len > 0 && localUri[len - 1] == '/') --len;
-
- local.uri = c_strndup(localUri, len);
-}
-
-int csync_update(CSYNC *ctx) {
- int rc = -1;
-
- if (!ctx) {
- errno = EBADF;
- return -1;
- }
- ctx->status_code = CSYNC_STATUS_OK;
-
- ctx->status_code = CSYNC_STATUS_OK;
-
- csync_memstat_check();
-
- if (!ctx->exclude_traversal_fn) {
- qCInfo(lcCSync, "No exclude file loaded or defined!");
- }
-
- /* update detection for local replica */
- QElapsedTimer timer;
- timer.start();
- ctx->current = LOCAL_REPLICA;
-
- qCInfo(lcCSync, "## Starting local discovery ##");
-
- rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH);
- if (rc < 0) {
- if(ctx->status_code == CSYNC_STATUS_OK) {
- ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
- }
- return rc;
- }
-
- qCInfo(lcCSync) << "Update detection for local replica took" << timer.elapsed() / 1000.
- << "seconds walking" << ctx->local.files.size() << "files";
- csync_memstat_check();
-
- /* update detection for remote replica */
- timer.restart();
- ctx->current = REMOTE_REPLICA;
-
- qCInfo(lcCSync, "## Starting remote discovery ##");
-
- rc = csync_ftw(ctx, "", csync_walker, MAX_DEPTH);
- if (rc < 0) {
- if(ctx->status_code == CSYNC_STATUS_OK) {
- ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
- }
- return rc;
- }
-
-
- qCInfo(lcCSync) << "Update detection for remote replica took" << timer.elapsed() / 1000.
- << "seconds walking" << ctx->remote.files.size() << "files";
- csync_memstat_check();
-
- ctx->status |= CSYNC_STATUS_UPDATE;
-
- rc = 0;
- return rc;
-}
-
-int csync_reconcile(CSYNC *ctx) {
- Q_ASSERT(ctx);
- ctx->status_code = CSYNC_STATUS_OK;
-
- /* Reconciliation for local replica */
- QElapsedTimer timer;
- timer.start();
-
- ctx->current = LOCAL_REPLICA;
-
- csync_reconcile_updates(ctx);
-
- qCInfo(lcCSync) << "Reconciliation for local replica took " << timer.elapsed() / 1000.
- << "seconds visiting " << ctx->local.files.size() << " files.";
-
- /* Reconciliation for remote replica */
- timer.restart();
-
- ctx->current = REMOTE_REPLICA;
-
- csync_reconcile_updates(ctx);
-
- qCInfo(lcCSync) << "Reconciliation for remote replica took " << timer.elapsed() / 1000.
- << "seconds visiting " << ctx->remote.files.size() << " files.";
-
- ctx->status |= CSYNC_STATUS_RECONCILE;
- return 0;
-}
-
-/*
- * local visitor which calls the user visitor with repacked stat info.
- */
-static int _csync_treewalk_visitor(csync_file_stat_t *cur, CSYNC * ctx, const csync_treewalk_visit_func &visitor) {
- csync_s::FileMap *other_tree = nullptr;
-
- /* we need the opposite tree! */
- switch (ctx->current) {
- case LOCAL_REPLICA:
- other_tree = &ctx->remote.files;
- break;
- case REMOTE_REPLICA:
- other_tree = &ctx->local.files;
- break;
- default:
- break;
- }
-
- csync_s::FileMap::const_iterator other_file_it = other_tree->find(cur->path);
-
- if (other_file_it == other_tree->cend()) {
- /* Check the renamed path as well. */
- QByteArray renamed_path = csync_rename_adjust_parent_path(ctx, cur->path);
- if (renamed_path != cur->path)
- other_file_it = other_tree->find(renamed_path);
- }
-
- if (other_file_it == other_tree->cend()) {
- /* Check the source path as well. */
- QByteArray renamed_path = csync_rename_adjust_parent_path_source(ctx, cur->path);
- if (renamed_path != cur->path)
- other_file_it = other_tree->find(renamed_path);
- }
-
- csync_file_stat_t *other = (other_file_it != other_tree->cend()) ? other_file_it->second.get() : nullptr;
-
- ctx->status_code = CSYNC_STATUS_OK;
-
- Q_ASSERT(visitor);
- return visitor(cur, other);
-}
-
-/*
- * treewalk function, called from its wrappers below.
- */
-static int _csync_walk_tree(CSYNC *ctx, csync_s::FileMap &tree, const csync_treewalk_visit_func &visitor)
-{
- for (auto &pair : tree) {
- if (_csync_treewalk_visitor(pair.second.get(), ctx, visitor) < 0) {
- return -1;
- }
- }
- return 0;
-}
-
-/*
- * wrapper function for treewalk on the remote tree
- */
-int csync_walk_remote_tree(CSYNC *ctx, const csync_treewalk_visit_func &visitor)
-{
- ctx->status_code = CSYNC_STATUS_OK;
- ctx->current = REMOTE_REPLICA;
- return _csync_walk_tree(ctx, ctx->remote.files, visitor);
-}
-
-/*
- * wrapper function for treewalk on the local tree
- */
-int csync_walk_local_tree(CSYNC *ctx, const csync_treewalk_visit_func &visitor)
-{
- ctx->status_code = CSYNC_STATUS_OK;
- ctx->current = LOCAL_REPLICA;
- return _csync_walk_tree(ctx, ctx->local.files, visitor);
-}
-
-int csync_s::reinitialize() {
- int rc = 0;
-
- status_code = CSYNC_STATUS_OK;
-
- remote.read_from_db = false;
- read_remote_from_db = true;
-
- local.files.clear();
- remote.files.clear();
-
- renames.folder_renamed_from.clear();
- renames.folder_renamed_to.clear();
-
- status = CSYNC_STATUS_INIT;
- error_string.clear();
-
- rc = 0;
- return rc;
-}
-
-csync_s::~csync_s() {
- SAFE_FREE(local.uri);
-}
-
-void *csync_get_userdata(CSYNC *ctx) {
- if (!ctx) {
- return nullptr;
- }
- return ctx->callbacks.userdata;
-}
-
-int csync_set_userdata(CSYNC *ctx, void *userdata) {
- if (!ctx) {
- return -1;
- }
-
- ctx->callbacks.userdata = userdata;
-
- return 0;
-}
-
-csync_auth_callback csync_get_auth_callback(CSYNC *ctx) {
- if (!ctx) {
- return nullptr;
- }
-
- return ctx->callbacks.auth_function;
-}
-
-int csync_set_status(CSYNC *ctx, int status) {
- if (!ctx || status < 0) {
- return -1;
- }
-
- ctx->status = status;
-
- return 0;
-}
-
-CSYNC_STATUS csync_get_status(CSYNC *ctx) {
- if (!ctx) {
- return CSYNC_STATUS_ERROR;
- }
-
- return ctx->status_code;
-}
-
-void csync_request_abort(CSYNC *ctx)
-{
- if (ctx) {
- ctx->abort = true;
- }
-}
-
-void csync_resume(CSYNC *ctx)
-{
- if (ctx) {
- ctx->abort = false;
- }
-}
-
-int csync_abort_requested(CSYNC *ctx)
-{
- if (ctx) {
- return ctx->abort;
- } else {
- return (1 == 0);
- }
-}
-
std::unique_ptr<csync_file_stat_t> csync_file_stat_t::fromSyncJournalFileRecord(const OCC::SyncJournalFileRecord &rec)
{
std::unique_ptr<csync_file_stat_t> st(new csync_file_stat_t);
*/
using CSYNC = struct csync_s;
-using csync_auth_callback = int (*)(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata);
-
-using csync_update_callback = void (*)(bool local, const char *dirUrl, void *userdata);
-
-using csync_vio_handle_t = void;
-using csync_vio_opendir_hook = csync_vio_handle_t *(*)(const char *url, void *userdata);
-using csync_vio_readdir_hook = std::unique_ptr<csync_file_stat_t> (*)(csync_vio_handle_t *dhandle, void *userdata);
-using csync_vio_closedir_hook = void (*)(csync_vio_handle_t *dhandle, void *userdata);
-
-/* Compute the checksum of the given \a checksumTypeId for \a path. */
-using csync_checksum_hook = QByteArray (*)(const QByteArray &path, const QByteArray &otherChecksumHeader, void *userdata);
-
-/**
- * @brief Update detection
- *
- * @param ctx The context to run the update detection on.
- *
- * @return 0 on success, less than 0 if an error occurred.
- */
-int OCSYNC_EXPORT csync_update(CSYNC *ctx);
-
-/**
- * @brief Reconciliation
- *
- * @param ctx The context to run the reconciliation on.
- *
- * @return 0 on success, less than 0 if an error occurred.
- */
-int OCSYNC_EXPORT csync_reconcile(CSYNC *ctx);
-
-/**
- * @brief Get the userdata saved in the context.
- *
- * @param ctx The csync context.
- *
- * @return The userdata saved in the context, \c nullptr if an error
- * occurred.
- */
-void *csync_get_userdata(CSYNC *ctx);
-
-/**
- * @brief Save userdata to the context which is passed to the auth
- * callback function.
- *
- * @param ctx The csync context.
- *
- * @param userdata The userdata to be stored in the context.
- *
- * @return 0 on success, less than 0 if an error occurred.
- */
-int OCSYNC_EXPORT csync_set_userdata(CSYNC *ctx, void *userdata);
-
-/**
- * @brief Get the authentication callback set.
- *
- * @param ctx The csync context.
- *
- * @return The authentication callback set or \c nullptr if an error
- * occurred.
- */
-csync_auth_callback OCSYNC_EXPORT csync_get_auth_callback(CSYNC *ctx);
-
-/**
- * @brief Set the authentication callback.
- *
- * @param ctx The csync context.
- *
- * @param cb The authentication callback.
- *
- * @return 0 on success, less than 0 if an error occurred.
- */
-int OCSYNC_EXPORT csync_set_auth_callback(CSYNC *ctx, csync_auth_callback cb);
-
-/* Used for special modes or debugging */
-CSYNC_STATUS OCSYNC_EXPORT csync_get_status(CSYNC *ctx);
-
-/* Used for special modes or debugging */
-int OCSYNC_EXPORT csync_set_status(CSYNC *ctx, int status);
-
-using csync_treewalk_visit_func = std::function<int(csync_file_stat_t *cur, csync_file_stat_t *other)>;
-
-/**
- * @brief Walk the local file tree and call a visitor function for each file.
- *
- * @param ctx The csync context.
- * @param visitor A callback function to handle the file info.
- *
- * @return 0 on success, less than 0 if an error occurred.
- */
-int OCSYNC_EXPORT csync_walk_local_tree(CSYNC *ctx, const csync_treewalk_visit_func &visitor);
-
-/**
- * @brief Walk the remote file tree and call a visitor function for each file.
- *
- * @param ctx The csync context.
- * @param visitor A callback function to handle the file info.
- *
- * @return 0 on success, less than 0 if an error occurred.
- */
-int OCSYNC_EXPORT csync_walk_remote_tree(CSYNC *ctx, const csync_treewalk_visit_func &visitor);
-
-/**
- * @brief Aborts the current sync run as soon as possible. Can be called from another thread.
- *
- * @param ctx The csync context.
- */
-void OCSYNC_EXPORT csync_request_abort(CSYNC *ctx);
-
-/**
- * @brief Clears the abort flag. Can be called from another thread.
- *
- * @param ctx The csync context.
- */
-void OCSYNC_EXPORT csync_resume(CSYNC *ctx);
-
-/**
- * @brief Checks for the abort flag, to be used from the modules.
- *
- * @param ctx The csync context.
- */
-int OCSYNC_EXPORT csync_abort_requested(CSYNC *ctx);
time_t OCSYNC_EXPORT oc_httpdate_parse( const char *date );
*/
struct OCSYNC_EXPORT csync_s {
- class FileMap : public std::unordered_map<ByteArrayRef, std::unique_ptr<csync_file_stat_t>, ByteArrayRefHash> {
- public:
- csync_file_stat_t *findFile(const ByteArrayRef &key) const {
- auto it = find(key);
- return it != end() ? it->second.get() : nullptr;
- }
- csync_file_stat_t *findFileMangledName(const ByteArrayRef &key) const {
- auto it = begin();
- while (it != end()) {
- csync_file_stat_t *fs = it->second.get();
- if (fs->e2eMangledName == key) {
- return fs;
- }
- ++it;
- }
- return nullptr;
- }
- };
-
- struct {
- csync_auth_callback auth_function = nullptr;
- void *userdata = nullptr;
- csync_update_callback update_callback = nullptr;
- void *update_callback_userdata = nullptr;
-
- /* hooks for checking the white list (uses the update_callback_userdata) */
- int (*checkSelectiveSyncBlackListHook)(void*, const QByteArray &) = nullptr;
- int (*checkSelectiveSyncNewFolderHook)(void *, const QByteArray & /* path */, OCC::RemotePermissions) = nullptr;
-
-
- csync_vio_opendir_hook remote_opendir_hook = nullptr;
- csync_vio_readdir_hook remote_readdir_hook = nullptr;
- csync_vio_closedir_hook remote_closedir_hook = nullptr;
- void *vio_userdata = nullptr;
-
- /* hook for comparing checksums of files during discovery */
- csync_checksum_hook checksum_hook = nullptr;
- void *checksum_userdata = nullptr;
-
- } callbacks;
-
- OCC::SyncJournalDb *statedb;
-
- /**
- * Function used to determine whether an item is excluded
- * during the update phase.
- *
- * See ExcludedFiles in csync_exclude.
- */
- std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)> exclude_traversal_fn;
-
- struct {
- std::unordered_map<ByteArrayRef, QByteArray, ByteArrayRefHash> folder_renamed_to; // map from->to
- std::unordered_map<ByteArrayRef, QByteArray, ByteArrayRefHash> folder_renamed_from; // map to->from
- } renames;
-
- struct {
- char *uri = nullptr;
- FileMap files;
- } local;
-
- struct {
- FileMap files;
- bool read_from_db = false;
- OCC::RemotePermissions root_perms; /* Permission of the root folder. (Since the root folder is not in the db tree, we need to keep a separate entry.) */
- } remote;
-
- /* replica we are currently walking */
- enum csync_replica_e current = LOCAL_REPLICA;
-
- /* Used in the update phase so changes in the sub directories can be notified to
- parent directories */
- csync_file_stat_t *current_fs = nullptr;
-
- /* csync error code */
- enum CSYNC_STATUS status_code = CSYNC_STATUS_OK;
-
- /* Some additional string information which is added to the error message corresponding to the error code in errno.
- * Usually a filename
- */
- QString error_string;
-
- int status = CSYNC_STATUS_INIT;
- volatile bool abort = false;
-
- /**
- * Specify if it is allowed to read the remote tree from the DB (default to enabled)
- */
- bool read_remote_from_db = false;
-
- std::function<bool(const QByteArray &)> should_discover_locally_fn;
-
- bool ignore_hidden_files = true;
-
- bool upload_conflict_files = false;
-
- /**
- * Whether new remote files should start out as virtual.
- */
- bool new_files_are_virtual = false;
-
- /**
- * The suffix to use for virtual files.
- */
- QByteArray virtual_file_suffix;
-
- csync_s(const char *localUri, OCC::SyncJournalDb *statedb);
- ~csync_s();
- int reinitialize();
// For some reason MSVC references the copy constructor and/or the assignment operator
// if a class is exported. This is a problem since unique_ptr isn't copyable.
+++ /dev/null
-/*
- * 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 "config_csync.h"
-
-#include <cassert>
-#include "csync_private.h"
-#include "csync_reconcile.h"
-#include "csync_util.h"
-#include "csync_rename.h"
-#include "common/c_jhash.h"
-#include "common/asserts.h"
-#include "common/syncjournalfilerecord.h"
-
-#include <QLoggingCategory>
-Q_LOGGING_CATEGORY(lcReconcile, "nextcloud.sync.csync.reconciler", QtInfoMsg)
-
-// Needed for PRIu64 on MinGW in C++ mode.
-#define __STDC_FORMAT_MACROS
-#include <cinttypes>
-
-/* Check if a file is ignored because one parent is ignored.
- * return the node of the ignored directoy if it's the case, or \c nullptr if it is not ignored */
-static csync_file_stat_t *_csync_check_ignored(csync_s::FileMap *tree, const ByteArrayRef &path)
-{
- /* compute the size of the parent directory */
- int parentlen = path.size() - 1;
- while (parentlen > 0 && path.at(parentlen) != '/') {
- parentlen--;
- }
- if (parentlen <= 0) {
- return nullptr;
- }
- ByteArrayRef parentPath = path.left(parentlen);
- csync_file_stat_t *fs = tree->findFile(parentPath);
- if (fs) {
- if (fs->instruction == CSYNC_INSTRUCTION_IGNORE) {
- /* Yes, we are ignored */
- return fs;
- } else {
- /* Not ignored */
- return nullptr;
- }
- } else {
- /* Try if the parent itself is ignored */
- return _csync_check_ignored(tree, parentPath);
- }
-}
-
-
-/**
- * The main function in the reconcile pass.
- *
- * It's called for each entry in the local and remote files by
- * csync_reconcile()
- *
- * Before the reconcile phase the trees already know about changes
- * relative to the sync journal. This function's job is to spot conflicts
- * between local and remote changes and adjust the nodes accordingly.
- *
- * See doc/dev/sync-algorithm.md for an overview.
- *
- *
- * Older detail comment:
- *
- * We merge replicas at the file level. The merged replica contains the
- * superset of files that are on the local machine and server copies of
- * the replica. In the case where the same file is in both the local
- * and server copy, the file that was modified most recently is used.
- * This means that new files are not deleted, and updated versions of
- * existing files are not overwritten.
- *
- * When a file is updated, the merge algorithm compares the destination
- * file with the the source file. If the destination file is newer
- * (timestamp is newer), it is not overwritten. If both files, on the
- * source and the destination, have been changed, the newer file wins.
- */
-static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
- csync_s::FileMap *our_tree = nullptr;
- csync_s::FileMap *other_tree = nullptr;
-
- /* we need the opposite tree! */
- switch (ctx->current) {
- case LOCAL_REPLICA:
- our_tree = &ctx->local.files;
- other_tree = &ctx->remote.files;
- break;
- case REMOTE_REPLICA:
- our_tree = &ctx->remote.files;
- other_tree = &ctx->local.files;
- break;
- default:
- break;
- }
-
- csync_file_stat_t *other = other_tree->findFile(cur->path);
-
- if (!other) {
- if (ctx->current == REMOTE_REPLICA) {
- // The file was not found and the other tree is the local one
- // check if the path doesn't match a mangled file name
- other = other_tree->findFileMangledName(cur->path);
- } else {
- other = other_tree->findFile(cur->e2eMangledName);
- }
- }
-
- if (!other) {
- /* Check the renamed path as well. */
- other = other_tree->findFile(csync_rename_adjust_parent_path(ctx, cur->path));
- }
- if (!other) {
- /* Check if it is ignored */
- other = _csync_check_ignored(other_tree, cur->path);
- /* If it is ignored, other->instruction will be IGNORE so this one will also be ignored */
- }
-
- // If the user adds a file locally check whether a virtual file for that name exists.
- // If so, go to "potential conflict" mode by switching the remote entry to be a
- // real file.
- if (!other
- && ctx->current == LOCAL_REPLICA
- && cur->instruction == CSYNC_INSTRUCTION_NEW
- && cur->type != ItemTypeVirtualFile) {
- // Check if we have a virtual file entry in the remote tree
- auto virtualFilePath = cur->path;
- virtualFilePath.append(ctx->virtual_file_suffix);
- other = other_tree->findFile(virtualFilePath);
- if (!other) {
- /* Check the renamed path as well. */
- other = other_tree->findFile(csync_rename_adjust_parent_path(ctx, virtualFilePath));
- }
- if (other && other->type == ItemTypeVirtualFile) {
- qCInfo(lcReconcile) << "Found virtual file for local" << cur->path << "in remote tree";
- other->path = cur->path;
- other->type = ItemTypeVirtualFileDownload;
- other->instruction = CSYNC_INSTRUCTION_EVAL;
- } else {
- other = nullptr;
- }
- }
-
- /* file only found on current replica */
- if (!other) {
- switch(cur->instruction) {
- /* file has been modified */
- case CSYNC_INSTRUCTION_EVAL:
- cur->instruction = CSYNC_INSTRUCTION_NEW;
- break;
- /* file has been removed on the opposite replica */
- case CSYNC_INSTRUCTION_NONE:
- case CSYNC_INSTRUCTION_UPDATE_METADATA:
- if (cur->has_ignored_files) {
- /* Do not remove a directory that has ignored files */
- break;
- }
- if (cur->child_modified) {
- /* re-create directory that has modified contents */
- cur->instruction = CSYNC_INSTRUCTION_NEW;
- break;
- }
- /* If the local virtual file is gone, it should be reestablished.
- * Unless the base file is seen in the local tree now. */
- if (cur->type == ItemTypeVirtualFile
- && ctx->current == REMOTE_REPLICA
- && cur->path.endsWith(ctx->virtual_file_suffix)
- && !other_tree->findFile(cur->path.left(cur->path.size() - ctx->virtual_file_suffix.size()))) {
- cur->instruction = CSYNC_INSTRUCTION_NEW;
- break;
- }
-
- /* If a virtual file is supposed to be downloaded, the local tree
- * will see "foo.owncloud" NONE while the remote might see "foo".
- * In the common case of remote NEW we don't want to trigger the REMOVE
- * that would normally be done for foo.owncloud since the download for
- * "foo" will take care of it.
- * If it was removed remotely, or moved remotely, the REMOVE is what we want.
- */
- if (cur->type == ItemTypeVirtualFileDownload
- && ctx->current == LOCAL_REPLICA
- && cur->path.endsWith(ctx->virtual_file_suffix)) {
- auto actualOther = other_tree->findFile(cur->path.left(cur->path.size() - ctx->virtual_file_suffix.size()));
- if (actualOther
- && (actualOther->instruction == CSYNC_INSTRUCTION_NEW
- || actualOther->instruction == CSYNC_INSTRUCTION_CONFLICT)) {
- cur->instruction = CSYNC_INSTRUCTION_NONE;
- break;
- }
- }
- cur->instruction = CSYNC_INSTRUCTION_REMOVE;
- break;
- case CSYNC_INSTRUCTION_EVAL_RENAME: {
- // By default, the EVAL_RENAME decays into a NEW
- cur->instruction = CSYNC_INSTRUCTION_NEW;
-
- bool processedRename = false;
- auto renameCandidateProcessing = [&](const QByteArray &basePath) {
- if (processedRename)
- return;
- if (basePath.isEmpty())
- return;
-
- /* First, check that the file is NOT in our tree (another file with the same name was added) */
- if (our_tree->findFile(basePath)) {
- other = nullptr;
- qCInfo(lcReconcile, "Origin found in our tree : %s", basePath.constData());
- } else {
- /* Find the potential rename source 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(basePath);
- qCInfo(lcReconcile, "Rename origin in other tree (%s) %s",
- basePath.constData(), other ? "found" : "not found");
- }
-
- const auto curParentPath = [=]{
- const auto slashPosition = cur->path.lastIndexOf('/');
- if (slashPosition >= 0) {
- return cur->path.left(slashPosition);
- } else {
- return QByteArray();
- }
- }();
- auto curParent = our_tree->findFile(curParentPath);
-
- if (!other
- || !other->e2eMangledName.isEmpty()
- || (curParent && curParent->isE2eEncrypted)) {
- // Stick with the NEW since there's no "other" file
- // or if there's an "other" file it involves E2EE and
- // we want to always issue delete + upload in such cases
- return;
- } else if (other->instruction == CSYNC_INSTRUCTION_RENAME) {
- // Some other EVAL_RENAME already claimed other.
- // We do nothing: maybe a different candidate for
- // other is found as well?
- qCInfo(lcReconcile, "Other has already been renamed to %s",
- other->rename_path.constData());
- } else if (cur->type == ItemTypeDirectory
- // The local replica is reconciled first, so the remote tree would
- // have either NONE or UPDATE_METADATA if the remote file is safe to
- // move.
- // In the remote replica, REMOVE is also valid (local has already
- // been reconciled). NONE can still happen if the whole parent dir
- // was set to REMOVE by the local reconcile.
- || other->instruction == CSYNC_INSTRUCTION_NONE
- || other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA
- || other->instruction == CSYNC_INSTRUCTION_REMOVE) {
- qCInfo(lcReconcile, "Switching %s to RENAME to %s",
- other->path.constData(), cur->path.constData());
- other->instruction = CSYNC_INSTRUCTION_RENAME;
- other->rename_path = cur->path;
- if( !cur->file_id.isEmpty() ) {
- other->file_id = cur->file_id;
- }
- if (ctx->current == LOCAL_REPLICA) {
- // Keep the local mtime.
- other->modtime = cur->modtime;
- }
- other->inode = cur->inode;
- cur->instruction = CSYNC_INSTRUCTION_NONE;
- // We have consumed 'other': exit this loop to not consume another one.
- processedRename = true;
- } else if (our_tree->findFile(csync_rename_adjust_parent_path(ctx, other->path)) == cur) {
- // If we're here, that means that the other side's reconcile will be able
- // to work against cur: The filename itself didn't change, only a parent
- // directory was renamed! In that case it's safe to ignore the rename
- // since the parent directory rename will already deal with it.
-
- // Local: The remote reconcile will be able to deal with this.
- // Remote: The local replica has already dealt with this.
- // See the EVAL_RENAME case when other was found directly.
- qCInfo(lcReconcile, "File in a renamed directory, other side's instruction: %d",
- other->instruction);
- cur->instruction = CSYNC_INSTRUCTION_NONE;
- } else {
- // This can, for instance, happen when there was a local change in other
- // and the instruction in the local tree is NEW while cur has EVAL_RENAME
- // due to a remote move of the same file. In these scenarios we just
- // want the instruction to stay NEW.
- qCInfo(lcReconcile, "Other already has instruction %d",
- other->instruction);
- }
- };
-
- if (ctx->current == LOCAL_REPLICA) {
- /* use the old name to find the "other" node */
- OCC::SyncJournalFileRecord base;
- qCInfo(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
- cur->inode);
- ctx->statedb->getFileRecordByInode(cur->inode, &base);
- renameCandidateProcessing(base._path);
- } else {
- ASSERT(ctx->current == REMOTE_REPLICA);
-
- // The update phase has already mapped out all dir->dir renames, check the
- // path that is consistent with that first. Otherwise update mappings and
- // reconcile mappings might disagree, leading to odd situations down the
- // line.
- auto basePath = csync_rename_adjust_full_path_source(ctx, cur->path);
- if (basePath != cur->path) {
- qCInfo(lcReconcile, "Trying rename origin by csync_rename mapping %s",
- basePath.constData());
- // We go through getFileRecordsByFileId to ensure the basePath
- // computed in this way also has the expected fileid.
- ctx->statedb->getFileRecordsByFileId(cur->file_id,
- [&](const OCC::SyncJournalFileRecord &base) {
- if (base._path == basePath)
- renameCandidateProcessing(basePath);
- });
- }
-
- // Also feed all the other files with the same fileid if necessary
- if (!processedRename) {
- qCInfo(lcReconcile, "Finding rename origin through file ID %s",
- cur->file_id.constData());
- ctx->statedb->getFileRecordsByFileId(cur->file_id,
- [&](const OCC::SyncJournalFileRecord &base) { renameCandidateProcessing(base._path); });
- }
- }
-
- break;
- }
- default:
- break;
- }
- } else {
- /*
- * file found on the other replica
- */
-
- switch (cur->instruction) {
- case CSYNC_INSTRUCTION_UPDATE_METADATA:
- if (other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA && ctx->current == LOCAL_REPLICA) {
- // Remote wins, the SyncEngine will pick relevant local metadata since the remote tree is walked last.
- cur->instruction = CSYNC_INSTRUCTION_NONE;
- }
- break;
- case CSYNC_INSTRUCTION_EVAL_RENAME:
- /* If the file already exist on the other side, we have a conflict.
- Abort the rename and consider it is a new file. */
- cur->instruction = CSYNC_INSTRUCTION_NEW;
- /* fall through */
- /* file on current replica is changed or new */
- case CSYNC_INSTRUCTION_EVAL:
- case CSYNC_INSTRUCTION_NEW:
- switch (other->instruction) {
- /* file on other replica is changed or new */
- case CSYNC_INSTRUCTION_NEW:
- case CSYNC_INSTRUCTION_EVAL:
- // PORTED
-
- break;
- /* file on the other replica has not been modified */
- case CSYNC_INSTRUCTION_NONE:
- case CSYNC_INSTRUCTION_UPDATE_METADATA:
- if (cur->type != other->type) {
- // If the type of the entity changed, it's like NEW, but
- // needs to delete the other entity first.
- cur->instruction = CSYNC_INSTRUCTION_TYPE_CHANGE;
- other->instruction = CSYNC_INSTRUCTION_NONE;
- } else if (cur->type == ItemTypeDirectory) {
- cur->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
- other->instruction = CSYNC_INSTRUCTION_NONE;
- } else {
- if (cur->instruction != CSYNC_INSTRUCTION_NEW
- && cur->instruction != CSYNC_INSTRUCTION_SYNC) {
- cur->instruction = CSYNC_INSTRUCTION_SYNC;
- }
- other->instruction = CSYNC_INSTRUCTION_NONE;
- }
- break;
- case CSYNC_INSTRUCTION_IGNORE:
- cur->instruction = CSYNC_INSTRUCTION_IGNORE;
- break;
- default:
- break;
- }
- // Ensure we're not leaving discovery-only instructions
- // in place. This can happen, for instance, when other's
- // instruction is EVAL_RENAME because the parent dir was renamed.
- // NEW is safer than EVAL because it will end up with
- // propagation unless it's changed by something, and EVAL and
- // NEW are treated equivalently during reconcile.
- if (cur->instruction == CSYNC_INSTRUCTION_EVAL)
- cur->instruction = CSYNC_INSTRUCTION_NEW;
- break;
- case CSYNC_INSTRUCTION_NONE:
- // NONE/NONE on virtual files might become a REMOVE if the base file
- // is found in the local tree.
- if (cur->type == ItemTypeVirtualFile
- && other->instruction == CSYNC_INSTRUCTION_NONE
- && ctx->current == LOCAL_REPLICA
- && cur->path.endsWith(ctx->virtual_file_suffix)
- && ctx->local.files.findFile(cur->path.left(cur->path.size() - ctx->virtual_file_suffix.size()))) {
- cur->instruction = CSYNC_INSTRUCTION_REMOVE;
- }
- break;
- default:
- break;
- }
- }
-
- //hide instruction NONE messages when log level is set to debug,
- //only show these messages on log level trace
- const char *repo = ctx->current == REMOTE_REPLICA ? "server" : "client";
- if(cur->instruction ==CSYNC_INSTRUCTION_NONE)
- {
- if(cur->type == ItemTypeDirectory)
- {
- qCDebug(lcReconcile,
- "%-30s %s dir: %s",
- csync_instruction_str(cur->instruction),
- repo,
- cur->path.constData());
- }
- else
- {
- qCDebug(lcReconcile,
- "%-30s %s file: %s",
- csync_instruction_str(cur->instruction),
- repo,
- cur->path.constData());
- }
- }
- else
- {
- if(cur->type == ItemTypeDirectory)
- {
- qCInfo(lcReconcile,
- "%-30s %s dir: %s",
- csync_instruction_str(cur->instruction),
- repo,
- cur->path.constData());
- }
- else
- {
- qCInfo(lcReconcile,
- "%-30s %s file: %s",
- csync_instruction_str(cur->instruction),
- repo,
- cur->path.constData());
- }
- }
-}
-
-void csync_reconcile_updates(CSYNC *ctx) {
- csync_s::FileMap *tree = nullptr;
-
- switch (ctx->current) {
- case LOCAL_REPLICA:
- tree = &ctx->local.files;
- break;
- case REMOTE_REPLICA:
- tree = &ctx->remote.files;
- break;
- default:
- break;
- }
-
- for (auto &pair : *tree) {
- _csync_merge_algorithm_visitor(pair.second.get(), ctx);
- }
-}
-
-/* vim: set ts=8 sw=2 et cindent: */
+++ /dev/null
-/*
- * 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
- */
-
-#ifndef _CSYNC_RECONCILE_H
-#define _CSYNC_RECONCILE_H
-
-/**
- * @file csync_reconcile.h
- *
- * @brief Reconciliation
- *
- * The most important component is the update detector, because the reconciler
- * depends on it. The correctness of reconciler is mandatory because it can
- * damage a filesystem. It decides which file:
- *
- * - stays untouched
- * - has a conflict
- * - gets synchronized
- * - or is deleted.
- *
- * @defgroup csyncReconcilationInternals csync reconciliation internals
- * @ingroup csyncInternalAPI
- *
- * @{
- */
-
-/**
- * @brief Reconcile the files.
- *
- * @param ctx The csync context to use.
- *
- * @todo Add an argument to set the algorithm to use.
- */
-void OCSYNC_EXPORT csync_reconcile_updates(CSYNC *ctx);
-
-/**
- * }@
- */
-#endif /* _CSYNC_RECONCILE_H */
-
-/* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */
+++ /dev/null
-/*
- * libcsync -- a library to sync a directory with another
- *
- * Copyright (c) 2012 by Olivier Goffart <ogoffart@woboq.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 "csync_private.h"
-#include "csync_rename.h"
-
-#include <algorithm>
-
-static ByteArrayRef _parentDir(const ByteArrayRef &path) {
- int len = path.length();
- while(len > 0 && path.at(len-1)!='/') len--;
- while(len > 0 && path.at(len-1)=='/') len--;
- return path.left(len);
-}
-
-void csync_rename_record(CSYNC* ctx, const QByteArray &from, const QByteArray &to)
-{
- ctx->renames.folder_renamed_to[from] = to;
- ctx->renames.folder_renamed_from[to] = from;
-}
-
-QByteArray csync_rename_adjust_parent_path(CSYNC *ctx, const QByteArray &path)
-{
- if (ctx->renames.folder_renamed_to.empty())
- return path;
- for (auto p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
- auto it = ctx->renames.folder_renamed_to.find(p);
- if (it != ctx->renames.folder_renamed_to.end()) {
- QByteArray rep = it->second + path.mid(p.length());
- return rep;
- }
- }
- return path;
-}
-
-QByteArray csync_rename_adjust_parent_path_source(CSYNC *ctx, const QByteArray &path)
-{
- if (ctx->renames.folder_renamed_from.empty())
- return path;
- for (ByteArrayRef p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
- auto it = ctx->renames.folder_renamed_from.find(p);
- if (it != ctx->renames.folder_renamed_from.end()) {
- QByteArray rep = it->second + path.mid(p.length());
- return rep;
- }
- }
- return path;
-}
-
-QByteArray csync_rename_adjust_full_path_source(CSYNC *ctx, const QByteArray &path)
-{
- if (ctx->renames.folder_renamed_from.empty())
- return path;
- for (ByteArrayRef p = path; !p.isEmpty(); p = _parentDir(p)) {
- auto it = ctx->renames.folder_renamed_from.find(p);
- if (it != ctx->renames.folder_renamed_from.end()) {
- QByteArray rep = it->second + path.mid(p.length());
- return rep;
- }
- }
- return path;
-}
-
-bool csync_rename_count(CSYNC *ctx) {
- return ctx->renames.folder_renamed_from.size();
-}
+++ /dev/null
-/*
- * libcsync -- a library to sync a directory with another
- *
- * Copyright (c) 2012 by Olivier Goffart <ogoffart@woboq.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
- */
-
-#pragma once
-
-#include "csync.h"
-
-/* Return the final destination path of a given patch in case of renames
- *
- * Does only map the parent directories. If the directory "A" is renamed to
- * "B" then this function will not map "A" to "B". Only "A/foo" -> "B/foo".
-*/
-QByteArray OCSYNC_EXPORT csync_rename_adjust_parent_path(CSYNC *ctx, const QByteArray &path);
-
-/* Return the source of a given path in case of renames */
-QByteArray OCSYNC_EXPORT csync_rename_adjust_parent_path_source(CSYNC *ctx, const QByteArray &path);
-
-/* like the parent_path variant, but applying to the full path */
-QByteArray OCSYNC_EXPORT csync_rename_adjust_full_path_source(CSYNC *ctx, const QByteArray &path);
-
-void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const QByteArray &from, const QByteArray &to);
-/* Return the amount of renamed item recorded */
-bool OCSYNC_EXPORT csync_rename_count(CSYNC *ctx);
+++ /dev/null
-/*
- * 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 <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <ctime>
-#include <cmath>
-
-#include "c_lib.h"
-
-#include "csync_private.h"
-#include "csync_exclude.h"
-#include "csync_update.h"
-#include "csync_util.h"
-#include "csync_misc.h"
-
-#include "vio/csync_vio.h"
-
-#include "csync_rename.h"
-
-#include "common/utility.h"
-#include "common/asserts.h"
-
-#include <QtCore/QTextCodec>
-#include <QtCore/QFile>
-
-// Needed for PRIu64 on MinGW in C++ mode.
-#define __STDC_FORMAT_MACROS
-#include <cinttypes>
-
-Q_LOGGING_CATEGORY(lcUpdate, "nextcloud.sync.csync.updater", QtInfoMsg)
-
-#ifdef NO_RENAME_EXTENSION
-/* Return true if the two path have the same extension. false otherwise. */
-static bool _csync_sameextension(const char *p1, const char *p2) {
- /* Find pointer to the extensions */
- const char *e1 = strrchr(p1, '.');
- const char *e2 = strrchr(p2, '.');
-
- /* If the found extension contains a '/', it is because the . was in the folder name
- * => no extensions */
- if (e1 && strchr(e1, '/')) e1 = nullptr;
- if (e2 && strchr(e2, '/')) e2 = nullptr;
-
- /* If none have extension, it is the same extension */
- if (!e1 && !e2)
- return true;
-
- /* c_streq takes care of the rest */
- return c_streq(e1, e2);
-}
-#endif
-
-static QByteArray _rel_to_abs(CSYNC* ctx, const QByteArray &relativePath) {
- return QByteArray() % const_cast<const char *>(ctx->local.uri) % '/' % relativePath;
-}
-
-/* Return true if two mtime are considered equal
- * We consider mtime that are one hour difference to be equal if they are one hour appart
- * because on some system (FAT) the date is changing when the daylight saving is changing */
-static bool _csync_mtime_equal(time_t a, time_t b)
-{
- if (a == b)
- return true;
-
- /* 1h of difference +- 1 second because the accuracy of FAT is 2 seconds (#2438) */
- if (fabs(3600 - fabs(difftime(a, b))) < 2)
- return true;
-
- return false;
-}
-
-/**
- * The main function of the discovery/update pass.
- *
- * It's called (indirectly) by csync_update(), once for each entity in the
- * local filesystem and once for each entity in the server data.
- *
- * It has two main jobs:
- * - figure out whether anything happened compared to the sync journal
- * and set (primarily) the instruction flag accordingly
- * - build the ctx->local.tree / ctx->remote.tree
- *
- * See doc/dev/sync-algorithm.md for an overview.
- */
-static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
- Q_ASSERT(fs);
- OCC::SyncJournalFileRecord base;
- CSYNC_EXCLUDE_TYPE excluded = CSYNC_NOT_EXCLUDED;
- if (fs->type == ItemTypeSkip) {
- excluded =CSYNC_FILE_EXCLUDE_STAT_FAILED;
- } else {
- /* Check if file is excluded */
- if (ctx->exclude_traversal_fn)
- excluded = ctx->exclude_traversal_fn(fs->path, fs->type);
- }
-
- if( excluded == CSYNC_NOT_EXCLUDED ) {
- /* Even if it is not excluded by a pattern, maybe it is to be ignored
- * because it's a hidden file that should not be synced.
- * This code should probably be in csync_exclude, but it does not have the fs parameter.
- * Keep it here for now */
- if (ctx->ignore_hidden_files
- && fs->is_hidden
- && !fs->path.endsWith(".sync-exclude.lst")) {
- qCInfo(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
- excluded = CSYNC_FILE_EXCLUDE_HIDDEN;
- }
- } else {
- /* File is ignored because it's matched by a user- or system exclude pattern. */
- qCInfo(lcUpdate, "%s excluded (%d)", fs->path.constData(), excluded);
- if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
- return 1;
- }
- if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
- return 1;
- }
- }
-
- if (ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncBlackListHook) {
- if (ctx->callbacks.checkSelectiveSyncBlackListHook(ctx->callbacks.update_callback_userdata, fs->path)) {
- return 1;
- }
- }
-
-
- if (fs->type == ItemTypeFile ) {
- if (fs->modtime == 0) {
- qCInfo(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
- }
- }
-
-#if 0 // PORTED
- if (excluded > CSYNC_NOT_EXCLUDED || fs->type == ItemTypeSoftLink) {
- fs->instruction = CSYNC_INSTRUCTION_IGNORE;
- if (ctx->current_fs) {
- ctx->current_fs->has_ignored_files = true;
- }
-
- goto out;
- }
-#endif
-
- /* Update detection: Check if a database entry exists.
- * If not, the file is either new or has been renamed. To see if it is
- * renamed, the db gets queried by the inode of the file as that one
- * does not change on rename.
- */
- if(!ctx->statedb->getFileRecord(fs->path, &base)) {
- ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
- return -1;
- }
-
- /*
- * When file is encrypted it's phash (path hash) will not match the local file phash,
- * we could match the e2eMangledName but that might be slow wihout index, and it's
- * not UNIQUE at the moment.
- */
- if (!base.isValid()) {
- if(!ctx->statedb->getFileRecordByE2eMangledName(fs->path, &base)) {
- ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
- return -1;
- }
- }
-
- // The db entry might be for a virtual file, so look for that on the
- // remote side. If we find one, change the current fs to look like a
- // virtual file too, because that's what one would see if the remote
- // db was filled from the database.
- if (ctx->current == REMOTE_REPLICA && !base.isValid() && fs->type == ItemTypeFile) {
- auto virtualFilePath = fs->path;
- virtualFilePath.append(ctx->virtual_file_suffix);
- ctx->statedb->getFileRecord(virtualFilePath, &base);
- if (base.isValid() && base._type == ItemTypeVirtualFile) {
- fs->type = ItemTypeVirtualFile;
- fs->path = virtualFilePath;
- } else {
- base = OCC::SyncJournalFileRecord();
- }
- }
-
- if(base.isValid()) { /* there is an entry in the database */
- // When the file is loaded from the file system it misses
- // the e2e mangled name and e2e encryption status
- fs->isE2eEncrypted = base._isE2eEncrypted;
- if (fs->e2eMangledName.isEmpty() && !base._e2eMangledName.isEmpty()) {
- fs->e2eMangledName = base._e2eMangledName;
- fs->path = base._path;
- }
-
- /* we have an update! */
- qCInfo(lcUpdate, "Database entry found for %s, compare: %" PRId64 " <-> %" PRId64
- ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64
- ", size: %" PRId64 " <-> %" PRId64 ", perms: %x <-> %x"
- ", checksum: %s <-> %s, type: %d <-> %d, ignore: %d, e2e: %s",
- base._path.constData(), ((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),
- fs->checksumHeader.constData(), base._checksumHeader.constData(),
- fs->type, base._type,
- base._serverHasIgnoredFiles, base._e2eMangledName.constData());
-
- // If the db suggests a virtual file should be downloaded,
- // treat the file as new on the remote.
- if (ctx->current == REMOTE_REPLICA && base._type == ItemTypeVirtualFileDownload) {
- fs->instruction = CSYNC_INSTRUCTION_NEW;
- fs->type = ItemTypeVirtualFileDownload;
- goto out;
- }
-
- // If what the db thinks is a virtual file is actually a file/dir,
- // treat it as new locally.
- if (ctx->current == LOCAL_REPLICA
- && (base._type == ItemTypeVirtualFile || base._type == ItemTypeVirtualFileDownload)
- && fs->type != ItemTypeVirtualFile) {
- fs->instruction = CSYNC_INSTRUCTION_EVAL;
- goto out;
- }
-
- if (ctx->current == REMOTE_REPLICA && fs->etag != base._etag) {
- fs->instruction = CSYNC_INSTRUCTION_EVAL;
-
- if (fs->type == ItemTypeVirtualFile) {
- // If the local thing is a virtual file, we just update the metadata
- fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
- } else if (base._type != fs->type) {
- // Preserve the EVAL flag later on if the type has changed.
- fs->child_modified = true;
- }
-
- goto out;
- }
- if (ctx->current == LOCAL_REPLICA &&
- (!_csync_mtime_equal(fs->modtime, base._modtime)
- // zero size in statedb can happen during migration
- || (base._fileSize != 0 && fs->size != base._fileSize))) {
- // PORTED: EML
-
- // Preserve the EVAL flag later on if the type has changed.
- 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 != base._fileId
- || fs->remotePerm != base._remotePerm))
- || (ctx->current == LOCAL_REPLICA && fs->inode != base._inode);
- if (fs->type == ItemTypeDirectory && 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
- * the database.
- * The metadata comparison ensure that we fetch all the file id or permission when
- * upgrading owncloud
- */
- qCInfo(lcUpdate, "Reading from database: %s", fs->path.constData());
- ctx->remote.read_from_db = true;
- }
- /* If it was remembered in the db that the remote dir has ignored files, store
- * that so that the reconciler can make advantage of.
- */
- if( ctx->current == REMOTE_REPLICA ) {
- 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. */
- qCInfo(lcUpdate, "Need to update metadata for: %s", fs->path.constData());
- fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
- } else {
- fs->instruction = CSYNC_INSTRUCTION_NONE;
- }
- } else {
- /* check if it's a file and has been renamed */
- if (ctx->current == LOCAL_REPLICA) {
- /* ... PORTED */
-
- } else { /*... PORTED ... */
- }
- }
-
-out:
-
-#if 0
-PORTED
- /* Set the ignored error string. */
- if (fs->instruction == CSYNC_INSTRUCTION_IGNORE) {
- if( fs->type == ItemTypeSoftLink ) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK; /* Symbolic links are ignored. */
- } else {
- if (excluded == CSYNC_FILE_EXCLUDE_LIST) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST; /* File listed on ignore list. */
- } else if (excluded == CSYNC_FILE_EXCLUDE_INVALID_CHAR) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS; /* File contains invalid characters. */
- } else if (excluded == CSYNC_FILE_EXCLUDE_TRAILING_SPACE) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_TRAILING_SPACE; /* File ends with a trailing space. */
- } else if (excluded == CSYNC_FILE_EXCLUDE_LONG_FILENAME) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME; /* File name is too long. */
- } else if (excluded == CSYNC_FILE_EXCLUDE_HIDDEN ) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN;
- } else if (excluded == CSYNC_FILE_EXCLUDE_STAT_FAILED) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_STAT_FAILED;
- } else if (excluded == CSYNC_FILE_EXCLUDE_CONFLICT) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE;
- } else if (excluded == CSYNC_FILE_EXCLUDE_CANNOT_ENCODE) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_CANNOT_ENCODE;
- }
- }
- }
- if (fs->instruction != CSYNC_INSTRUCTION_NONE
- && fs->instruction != CSYNC_INSTRUCTION_IGNORE
- && fs->instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
- && fs->type != ItemTypeDirectory) {
- fs->child_modified = true;
- }
-#endif
-
- // If conflict files are uploaded, they won't be marked as IGNORE / CSYNC_FILE_EXCLUDE_CONFLICT
- // but we still want them marked!
- if (ctx->upload_conflict_files) {
- if (OCC::Utility::isConflictFile(fs->path.constData())) {
- fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE;
- }
- }
-
- ctx->current_fs = fs.get();
-
- qCInfo(lcUpdate, "file: %s, instruction: %s <<=", fs->path.constData(),
- csync_instruction_str(fs->instruction));
-
- QByteArray path = fs->path;
- switch (ctx->current) {
- case LOCAL_REPLICA:
- ctx->local.files[path] = std::move(fs);
- break;
- case REMOTE_REPLICA:
- ctx->remote.files[path] = std::move(fs);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
- int rc = -1;
-
- if (ctx->abort) {
- qCDebug(lcUpdate, "Aborted!");
- ctx->status_code = CSYNC_STATUS_ABORTED;
- return -1;
- }
-
- switch (fs->type) {
- case ItemTypeFile:
- if (ctx->current == REMOTE_REPLICA) {
- qCDebug(lcUpdate, "file: %s [file_id=%s size=%" PRIu64 "]", fs->path.constData(), fs->file_id.constData(), fs->size);
- } else {
- qCDebug(lcUpdate, "file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", fs->path.constData(), fs->inode, fs->size);
- }
- break;
- case ItemTypeDirectory: /* enter directory */
- if (ctx->current == REMOTE_REPLICA) {
- qCDebug(lcUpdate, "directory: %s [file_id=%s]", fs->path.constData(), fs->file_id.constData());
- } else {
- qCDebug(lcUpdate, "directory: %s [inode=%" PRIu64 "]", fs->path.constData(), fs->inode);
- }
- break;
- case ItemTypeSoftLink:
- qCInfo(lcUpdate, "symlink: %s - not supported", fs->path.constData());
- break;
- default:
- qCInfo(lcUpdate, "item: %s - item type %d not iterated", fs->path.constData(), fs->type);
- return 0;
- }
-
- rc = _csync_detect_update(ctx, std::move(fs));
-
- return rc;
-}
-
-static bool fill_tree_from_db(CSYNC *ctx, const char *uri, bool singleFile = false)
-{
- int64_t count = 0;
- QByteArray skipbase;
- auto &files = ctx->current == LOCAL_REPLICA ? ctx->local.files : ctx->remote.files;
- auto rowCallback = [ctx, &count, &skipbase, &files](const OCC::SyncJournalFileRecord &rec) {
- if (ctx->current == REMOTE_REPLICA) {
- /* When selective sync is used, the database may have subtrees with a parent
- * whose etag 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_") {
- qCInfo(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_NOT_EXCLUDED;
- if (ctx->exclude_traversal_fn)
- excluded = ctx->exclude_traversal_fn(st->path, st->type);
- if (excluded != CSYNC_NOT_EXCLUDED) {
- qInfo(lcUpdate, "%s excluded from db read (%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. */
- files[rec._path] = std::move(st);
- ++count;
- };
-
- if (singleFile) {
- OCC::SyncJournalFileRecord record;
- if (ctx->statedb->getFileRecord(QByteArray(uri), &record) && record.isValid()) {
- rowCallback(record);
- } else {
- ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
- return false;
- }
- } else {
- if (!ctx->statedb->getFilesBelowPath(uri, rowCallback)) {
- ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
- return false;
- }
- }
- qInfo(lcUpdate, "%" PRId64 " entries read below path %s from db.", count, uri);
-
- return true;
-}
-
-/* set the current item to an ignored state.
- * If the item is set to ignored, the update phase continues, ie. its not a hard error */
-static bool mark_current_item_ignored( CSYNC *ctx, csync_file_stat_t *previous_fs, CSYNC_STATUS status )
-{
- if(!ctx) {
- return false;
- }
-
- if (ctx->current_fs) {
- ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE;
- ctx->current_fs->error_status = status;
- /* If a directory has ignored files, put the flag on the parent directory as well */
- if( previous_fs ) {
- previous_fs->has_ignored_files = true;
- }
- return true;
- }
- return false;
-}
-
-/* File tree walker */
-int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
- unsigned int depth) {
- QByteArray filename;
- QByteArray fullpath;
- csync_vio_handle_t *dh = nullptr;
- std::unique_ptr<csync_file_stat_t> dirent;
- csync_file_stat_t *previous_fs = nullptr;
- int read_from_db = 0;
- int rc = 0;
-
- bool do_read_from_db = (ctx->current == REMOTE_REPLICA && ctx->remote.read_from_db);
- const char *db_uri = uri;
-
- if (ctx->current == LOCAL_REPLICA && ctx->should_discover_locally_fn) {
- const char *local_uri = uri + strlen(ctx->local.uri);
- if (*local_uri == '/')
- ++local_uri;
- db_uri = local_uri;
- do_read_from_db = !ctx->should_discover_locally_fn(QByteArray(local_uri));
- }
-
- if (!depth) {
- mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_INDIVIDUAL_TOO_DEEP);
- return 0;
- }
-
- read_from_db = ctx->remote.read_from_db;
-
- // if the etag of this dir is still the same, its content is restored from the
- // database.
- if( do_read_from_db ) {
- if(!fill_tree_from_db(ctx, db_uri)) {
- errno = ENOENT;
- ctx->status_code = CSYNC_STATUS_OPENDIR_ERROR;
- goto error;
- }
- return 0;
- }
-
-
- while (true) {
- // Get the next item in the directory
- errno = 0;
- dirent = csync_vio_readdir(ctx, dh);
- if (!dirent) {
- if (errno != 0) {
- // Note: Windows vio converts any error into EACCES
- qCWarning(lcUpdate, "readdir failed for file in %s - errno %d", uri, errno);
- goto error;
- }
-
- // Normal case: End of items in directory
- break;
- }
-
- /* Conversion error */
- if (dirent->path.isEmpty() && !dirent->original_path.isEmpty()) {
- ctx->status_code = CSYNC_STATUS_INVALID_CHARACTERS;
- ctx->error_string = QString::fromUtf8(dirent->original_path);
- dirent->original_path.clear();
- goto error;
- }
-
- // At this point dirent->path only contains the file name.
- filename = dirent->path;
- if (filename.isEmpty()) {
- ctx->status_code = CSYNC_STATUS_READDIR_ERROR;
- goto error;
- }
-
- /* skip "." and ".." */
- if ( filename == "." || filename == "..") {
- continue;
- }
-
- if (uri[0] == '\0') {
- fullpath = filename;
- } else {
- fullpath = QByteArray() % uri % '/' % filename;
- }
-
- // When encountering virtual files, read the relevant
- // entry from the db instead.
- if (ctx->current == LOCAL_REPLICA
- && dirent->type == ItemTypeFile
- && filename.endsWith(ctx->virtual_file_suffix)) {
- QByteArray db_uri = fullpath.mid(strlen(ctx->local.uri) + 1);
-
- if( ! fill_tree_from_db(ctx, db_uri.constData(), true) ) {
- qCWarning(lcUpdate) << "Virtual file without db entry for" << filename;
- QFile::remove(fullpath);
- }
-
- continue;
- }
-
-
-#if 0
- // Now process to have a relative path to the sync root for the local replica, or to the data root on the remote.
- dirent->path = fullpath;
- if (ctx->current == LOCAL_REPLICA) {
- ASSERT(dirent->path.startsWith(ctx->local.uri)); // path is relative to uri
- // "len + 1" to include the slash in-between.
- size_t uriLength = strlen(ctx->local.uri);
- dirent->path = dirent->path.mid(OCC::Utility::convertSizeToInt(uriLength) + 1);
- }
-
- previous_fs = ctx->current_fs;
- bool recurse = dirent->type == ItemTypeDirectory;
-
- /* Call walker function for each file */
- rc = fn(ctx, std::move(dirent));
- /* this function may update ctx->current and ctx->read_from_db */
-
- if (rc < 0) {
- if (CSYNC_STATUS_IS_OK(ctx->status_code)) {
- ctx->status_code = CSYNC_STATUS_UPDATE_ERROR;
- }
-
- ctx->current_fs = previous_fs;
- goto error;
- }
-
-PORTED
- if (recurse && rc == 0
- && (!ctx->current_fs || ctx->current_fs->instruction != CSYNC_INSTRUCTION_IGNORE)) {
- rc = csync_ftw(ctx, fullpath, fn, depth - 1);
- if (rc < 0) {
- ctx->current_fs = previous_fs;
- goto error;
- }
-
- if (ctx->current_fs && !ctx->current_fs->child_modified
- && ctx->current_fs->instruction == CSYNC_INSTRUCTION_EVAL) {
- if (ctx->current == REMOTE_REPLICA) {
- ctx->current_fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
- } else {
- ctx->current_fs->instruction = CSYNC_INSTRUCTION_NONE;
- }
- }
-
- if (ctx->current_fs && previous_fs && ctx->current_fs->has_ignored_files) {
- /* If a directory has ignored files, put the flag on the parent directory as well */
- previous_fs->has_ignored_files = ctx->current_fs->has_ignored_files;
- }
- }
-
- if (ctx->current_fs && previous_fs && ctx->current_fs->child_modified) {
- /* If a directory has modified files, put the flag on the parent directory as well */
- previous_fs->child_modified = ctx->current_fs->child_modified;
- }
-
- ctx->current_fs = previous_fs;
- ctx->remote.read_from_db = read_from_db;
-#endif
- }
-
- csync_vio_closedir(ctx, dh);
- qCInfo(lcUpdate, " <= Closing walk for %s with read_from_db %d", uri, read_from_db);
-
- return rc;
-
-error:
- ctx->remote.read_from_db = read_from_db;
- if (dh) {
- csync_vio_closedir(ctx, dh);
- }
- return -1;
-}
-
-/* vim: set ts=8 sw=2 et cindent: */
+++ /dev/null
-/*
- * 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
- */
-
-#ifndef _CSYNC_UPDATE_H
-#define _CSYNC_UPDATE_H
-
-#include "csync.h"
-
-/**
- * @file csync_update.h
- *
- * @brief Update Detection
- *
- * TODO
- *
- * @defgroup csyncUpdateDetectionInternals csync update detection internals
- * @ingroup csyncInternalAPI
- *
- * @{
- */
-
-using csync_walker_fn = int (*)(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs);
-
-/**
- * @brief The walker function to use in the file tree walker.
- *
- * @param ctx The used csync context.
- *
- * @param file The file we are researching.
- *
- * @param fs The stat information we got.
- *
- * @param flag The flag describing the type of the file.
- *
- * @return 0 on success, < 0 on error.
- */
-int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs);
-
-/**
- * @brief The file tree walker.
- *
- * This function walks through the directory tree that is located under the uri
- * specified. It calls a walker function which is provided as a function pointer
- * once for each entry in the tree. By default, directories are handled before
- * the files and subdirectories they contain (pre-order traversal).
- *
- * @param ctx The csync context to use.
- *
- * @param uri The uri/path to the directory tree to walk.
- *
- * @param fn The walker function to call once for each entry.
- *
- * @param depth The max depth to walk down the tree.
- *
- * @return 0 on success, < 0 on error. If fn() returns non-zero, then the tree
- * walk is terminated and the value returned by fn() is returned as the
- * result.
- */
-int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
- unsigned int depth);
-
-#endif /* _CSYNC_UPDATE_H */
-
-/* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */
#include "common/c_jhash.h"
#include "csync_util.h"
-#include "vio/csync_vio.h"
Q_LOGGING_CATEGORY(lcCSyncUtils, "nextcloud.sync.csync.utils", QtInfoMsg)
+++ /dev/null
-/*
- * 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
- */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <cerrno>
-#include <cstdio>
-#include "common/asserts.h"
-
-#include "csync_private.h"
-#include "csync_util.h"
-#include "vio/csync_vio.h"
-#include "vio/csync_vio_local.h"
-#include "common/c_jhash.h"
-
-csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {
- switch(ctx->current) {
- case REMOTE_REPLICA:
- ASSERT(!ctx->remote.read_from_db);
- return ctx->callbacks.remote_opendir_hook(name, ctx->callbacks.vio_userdata);
- break;
- case LOCAL_REPLICA:
- if( ctx->callbacks.update_callback ) {
- ctx->callbacks.update_callback(/*local=*/true, name, ctx->callbacks.update_callback_userdata);
- }
- return csync_vio_local_opendir(name);
- break;
- default:
- ASSERT(false);
- }
- return nullptr;
-}
-
-int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle) {
- int rc = -1;
-
- if (!dhandle) {
- errno = EBADF;
- return -1;
- }
-
- switch(ctx->current) {
- case REMOTE_REPLICA:
- ASSERT(!ctx->remote.read_from_db);
- ctx->callbacks.remote_closedir_hook(dhandle, ctx->callbacks.vio_userdata);
- rc = 0;
- break;
- case LOCAL_REPLICA:
- rc = csync_vio_local_closedir(dhandle);
- break;
- default:
- ASSERT(false);
- break;
- }
- return rc;
-}
-
-std::unique_ptr<csync_file_stat_t> csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle) {
- switch(ctx->current) {
- case REMOTE_REPLICA:
- ASSERT(!ctx->remote.read_from_db);
- return ctx->callbacks.remote_readdir_hook(dhandle, ctx->callbacks.vio_userdata);
- break;
- case LOCAL_REPLICA:
- return csync_vio_local_readdir(dhandle);
- break;
- default:
- ASSERT(false);
- }
-
- return nullptr;
-}
-
+++ /dev/null
-/*
- * 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
- */
-
-#ifndef _CSYNC_VIO_H
-#define _CSYNC_VIO_H
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "c_private.h"
-#include "csync.h"
-#include "csync_private.h"
-
-struct fhandle_t {
- int fd;
-};
-
-csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name);
-int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle);
-std::unique_ptr<csync_file_stat_t> csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle);
-#endif /* _CSYNC_VIO_H */
#ifndef _CSYNC_VIO_LOCAL_H
#define _CSYNC_VIO_LOCAL_H
+struct csync_vio_handle_t;
+
csync_vio_handle_t OCSYNC_EXPORT *csync_vio_local_opendir(const char *name);
int OCSYNC_EXPORT csync_vio_local_closedir(csync_vio_handle_t *dhandle);
std::unique_ptr<csync_file_stat_t> OCSYNC_EXPORT csync_vio_local_readdir(csync_vio_handle_t *dhandle);
#include "c_string.h"
#include "c_utf8.h"
#include "csync_util.h"
-#include "csync_vio.h"
#include "vio/csync_vio_local.h"
#include "common/checksums.h"
#include <csync_private.h>
-#include <csync_rename.h>
#include <csync_exclude.h>
#include <QLoggingCategory>
// Everything in the SyncEngine expects a trailing slash for the localPath.
ASSERT(localPath.endsWith(QLatin1Char('/')));
- _csync_ctx.reset(new CSYNC(localPath.toUtf8().data(), journal));
-
_excludedFiles.reset(new ExcludedFiles(localPath));
- _csync_ctx->exclude_traversal_fn = _excludedFiles->csyncTraversalMatchFun();
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));
return 0;
}
-void SyncEngine::handleSyncError(CSYNC *ctx, const char *state)
-{
- CSYNC_STATUS err = csync_get_status(ctx);
- QString errMsg = ctx->error_string;
- QString errStr = csyncErrorToString(err);
- if (!errMsg.isEmpty()) {
- if (!errStr.endsWith(" ")) {
- errStr.append(" ");
- }
- errStr += errMsg;
- }
- // Special handling CSYNC_STATUS_INVALID_CHARACTERS
- if (err == CSYNC_STATUS_INVALID_CHARACTERS) {
- errStr = tr("Invalid characters, please rename \"%1\"").arg(errMsg);
- }
-
- // if there is csyncs url modifier in the error message, replace it.
- if (errStr.contains("ownclouds://"))
- errStr.replace("ownclouds://", "https://");
- if (errStr.contains("owncloud://"))
- errStr.replace("owncloud://", "http://");
-
- qCWarning(lcEngine) << "ERROR during " << state << ": " << errStr;
-
- if (CSYNC_STATUS_IS_EQUAL(err, CSYNC_STATUS_ABORTED)) {
- qCInfo(lcEngine) << "Update phase was aborted by user!";
- } else if (CSYNC_STATUS_IS_EQUAL(err, CSYNC_STATUS_SERVICE_UNAVAILABLE)) {
- emit csyncUnavailable();
- } else {
- csyncError(errStr);
- }
- finalize(false);
-}
-
void SyncEngine::csyncError(const QString &message)
{
emit syncError(message, ErrorCategory::Normal);
}
-
void SyncEngine::startSync()
{
if (_journal->exists()) {
_syncItems.clear();
_needsUpdate = false;
- csync_resume(_csync_ctx.data());
-
if (!_journal->exists()) {
qCInfo(lcEngine) << "New sync (no sync journal exists)";
} else {
// undo the filter to allow this sync to retrieve and store the correct etags.
_journal->clearEtagStorageFilter();
- _csync_ctx->upload_conflict_files = _account->capabilities().uploadConflictFiles();
_excludedFiles->setExcludeConflictFiles(!_account->capabilities().uploadConflictFiles());
- _csync_ctx->read_remote_from_db = true;
-
_lastLocalDiscoveryStyle = _localDiscoveryStyle;
- _csync_ctx->new_files_are_virtual = _syncOptions._newFilesAreVirtual;
- _csync_ctx->virtual_file_suffix = _syncOptions._virtualFileSuffix.toUtf8();
-
- if (_csync_ctx->new_files_are_virtual && _csync_ctx->virtual_file_suffix.isEmpty()) {
+ if (_syncOptions._newFilesAreVirtual && _syncOptions._virtualFileSuffix.isEmpty()) {
csyncError(tr("Using virtual files but suffix is not set"));
finalize(false);
return;
finalize(false);
return;
}
- csync_set_userdata(_csync_ctx.data(), this);
-
- // Set up checksumming hook
- _csync_ctx->callbacks.checksum_hook = &CSyncChecksumHook::hook;
- _csync_ctx->callbacks.checksum_userdata = &_checksum_hook;
_stopWatch.start();
_progressInfo->_status = ProgressInfo::Starting;
_journal->commitIfNeededAndStartNewTransaction("Post discovery");
}
- // FIXME: This is a reasonable safety check, but specifically just a hotfix.
- // See: https://github.com/nextcloud/desktop/issues/1433
- // It's still unclear why we can get an empty FileMap even though folder isn't empty
- // For now: Re-check if folder is really empty, if not bail out
- if (_csync_ctx.data()->local.files.empty() && QDir(_localPath).entryInfoList(QDir::NoDotAndDotDot).count() > 0) {
- qCWarning(lcEngine) << "Received local tree with empty FileMap but sync folder isn't empty. Won't reconcile.";
- finalize(false);
- return;
- }
-
_progressInfo->_currentDiscoveredRemoteFolder.clear();
_progressInfo->_currentDiscoveredLocalFolder.clear();
_progressInfo->_status = ProgressInfo::Reconcile;
// make sure everything is allowed
// TODO checkForPermission(_syncItems);
- // Re-init the csync context to free memory
- _csync_ctx->reinitialize();
_localDiscoveryPaths.clear();
// To announce the beginning of the sync
void SyncEngine::finalize(bool success)
{
- _csync_ctx->reinitialize();
_journal->close();
qCInfo(lcEngine) << "CSync run took " << _stopWatch.addLapTime(QLatin1String("Sync Finished")) << "ms";
(*it)->_instruction = CSYNC_INSTRUCTION_CONFLICT;
(*it)->_direction = SyncFileItem::Down;
(*it)->_isRestoration = true;
- // Take the things to write to the db from the "other" node (i.e: info from server).
+ /*// Take the things to write to the db from the "other" node (i.e: info from server).
// Do a lookup into the csync remote tree to get the metadata we need to restore.
- ASSERT(_csync_ctx->status != CSYNC_STATUS_INIT);
auto csyncIt = _csync_ctx->remote.files.find((*it)->_file.toUtf8());
if (csyncIt != _csync_ctx->remote.files.end()) {
(*it)->_modtime = csyncIt->second->modtime;
(*it)->_size = csyncIt->second->size;
(*it)->_fileId = csyncIt->second->file_id;
(*it)->_etag = csyncIt->second->etag;
- }
+ }*/
(*it)->_errorString = tr("Not allowed to upload this file because it is read-only on the server, restoring");
continue;
}
RemotePermissions SyncEngine::getPermissions(const QString &file) const
{
- // Fetch from the csync context while we still have it.
- ASSERT(_csync_ctx->status != CSYNC_STATUS_INIT);
-
- if (file == QLatin1String(""))
- return _csync_ctx->remote.root_perms;
-
- auto it = _csync_ctx->remote.files.find(file.toUtf8());
- if (it != _csync_ctx->remote.files.end()) {
- return it->second->remotePerm;
- }
- return {};
+ qFatal("FIXME");
+ return RemotePermissions();
}
void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems)
if (_propagator)
qCInfo(lcEngine) << "Aborting sync";
- // Sets a flag for the update phase
- csync_request_abort(_csync_ctx.data());
-
// Aborts the discovery phase job
if (_discoveryJob) {
_discoveryJob->abort();
SyncOptions syncOptions() const { return _syncOptions; }
void setSyncOptions(const SyncOptions &options) { _syncOptions = options; }
- bool ignoreHiddenFiles() const { return _csync_ctx->ignore_hidden_files; }
- void setIgnoreHiddenFiles(bool ignore) { _csync_ctx->ignore_hidden_files = ignore; }
+ bool ignoreHiddenFiles() const { return _ignore_hidden_files; }
+ void setIgnoreHiddenFiles(bool ignore) { _ignore_hidden_files = ignore; }
ExcludedFiles &excludedFiles() { return *_excludedFiles; }
Utility::StopWatch &stopWatch() { return _stopWatch; }
void slotInsufficientRemoteStorage();
private:
- void handleSyncError(CSYNC *ctx, const char *state);
void csyncError(const QString &message);
int treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other, bool);
QVector<SyncFileItemPtr> _syncItems;
AccountPtr _account;
- QScopedPointer<CSYNC> _csync_ctx;
bool _needsUpdate;
bool _syncRunning;
QString _localPath;
// number of files which goes back in time from the server
int _backInTimeFiles;
+ // If ignored files should be ignored
+ bool _ignore_hidden_files;
+
int _uploadLimit;
int _downloadLimit;
add_cmocka_test(check_csync_misc csync_tests/check_csync_misc.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})
# encoding
{
public:
-static int setup(void **state) {
- CSYNC *csync = nullptr;
-
- csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
+static int setup(void **) {
excludedFiles = new ExcludedFiles;
excludedFiles->setWildcardsMatchSlash(false);
- csync->exclude_traversal_fn = excludedFiles->csyncTraversalMatchFun();
-
- *state = csync;
return 0;
}
-static int setup_init(void **state) {
- CSYNC *csync = nullptr;
-
- csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
+static int setup_init(void **) {
excludedFiles = new ExcludedFiles;
excludedFiles->setWildcardsMatchSlash(false);
- csync->exclude_traversal_fn = excludedFiles->csyncTraversalMatchFun();
excludedFiles->addExcludeFilePath(EXCLUDE_LIST_FILE);
assert_true(excludedFiles->reloadExcludeFiles());
excludedFiles->addManualExclude("latex/*/*.tex.tmp");
assert_true(excludedFiles->reloadExcludeFiles());
-
- *state = csync;
return 0;
}
-static int teardown(void **state) {
- auto *csync = (CSYNC*)*state;
+static int teardown(void **) {
int rc = 0;
- auto statedb = csync->statedb;
- delete csync;
- delete statedb;
delete excludedFiles;
rc = system("rm -rf /tmp/check_csync1");
rc = system("rm -rf /tmp/check_csync2");
assert_int_equal(rc, 0);
- *state = nullptr;
-
return 0;
}
+++ /dev/null
-/*
- * 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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <cstring>
-#include <cerrno>
-
-#include "csync_private.h"
-#include "std/c_utf8.h"
-#include "vio/csync_vio.h"
-
-#include "torture.h"
-
-#define CSYNC_TEST_DIR "/tmp/csync_test/"
-#define CSYNC_TEST_DIRS "/tmp/csync_test/this/is/a/mkdirs/test"
-#define CSYNC_TEST_FILE "/tmp/csync_test/file.txt"
-
-#define MKDIR_MASK (S_IRWXU |S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
-
-#define WD_BUFFER_SIZE 255
-
-static char wd_buffer[WD_BUFFER_SIZE];
-
-static int setup(void **state)
-{
- CSYNC *csync = nullptr;
- int rc = 0;
-
- assert_non_null(getcwd(wd_buffer, WD_BUFFER_SIZE));
-
- rc = system("rm -rf /tmp/csync_test");
- assert_int_equal(rc, 0);
-
- csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
-
- csync->current = LOCAL_REPLICA;
-
- *state = csync;
- return 0;
-}
-
-static int setup_dir(void **state) {
- int rc = 0;
- mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR);
-
- setup(state);
-
- rc = _tmkdir(dir, MKDIR_MASK);
- c_free_locale_string(dir);
- assert_int_equal(rc, 0);
-
- assert_non_null(getcwd(wd_buffer, WD_BUFFER_SIZE));
-
- rc = chdir(CSYNC_TEST_DIR);
- assert_int_equal(rc, 0);
- return 0;
-}
-
-static int teardown(void **state) {
- auto *csync = (CSYNC*)*state;
- int rc = 0;
-
- auto statedb = csync->statedb;
- delete csync;
- delete statedb;
-
- rc = chdir(wd_buffer);
- assert_int_equal(rc, 0);
-
- rc = system("rm -rf /tmp/csync_test/");
- assert_int_equal(rc, 0);
-
- *state = nullptr;
- return 0;
-}
-
-
-/*
- * Test directory function
- */
-
-static void check_csync_vio_opendir(void **state)
-{
- auto *csync = (CSYNC*)*state;
- csync_vio_handle_t *dh = nullptr;
- int rc = 0;
-
- dh = csync_vio_opendir(csync, CSYNC_TEST_DIR);
- assert_non_null(dh);
-
- rc = csync_vio_closedir(csync, dh);
- assert_int_equal(rc, 0);
-}
-
-static void check_csync_vio_opendir_perm(void **state)
-{
- auto *csync = (CSYNC*)*state;
- csync_vio_handle_t *dh = nullptr;
- int rc = 0;
- mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR);
-
- assert_non_null(dir);
-
- rc = _tmkdir(dir, (S_IWUSR|S_IXUSR));
- assert_int_equal(rc, 0);
-
- dh = csync_vio_opendir(csync, CSYNC_TEST_DIR);
- assert_null(dh);
- assert_int_equal(errno, EACCES);
-
- _tchmod(dir, MKDIR_MASK);
- c_free_locale_string(dir);
-}
-
-static void check_csync_vio_closedir_null(void **state)
-{
- auto *csync = (CSYNC*)*state;
- int rc = 0;
-
- rc = csync_vio_closedir(csync, nullptr);
- assert_int_equal(rc, -1);
-}
-
-int torture_run_tests(void)
-{
- const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup_teardown(check_csync_vio_opendir, setup_dir, teardown),
- cmocka_unit_test_setup_teardown(check_csync_vio_opendir_perm, setup, teardown),
- cmocka_unit_test(check_csync_vio_closedir_null),
- };
-
- return cmocka_run_group_tests(tests, nullptr, nullptr);
-}
#include "csync_private.h"
#include "std/c_utf8.h"
-#include "vio/csync_vio.h"
+#include "vio/csync_vio_local.h"
#ifdef _WIN32
#include <windows.h>
static mbchar_t wd_buffer[WD_BUFFER_SIZE];
struct statevar {
- CSYNC *csync;
char *result;
char *ignored_dir;
};
auto *mystate = (statevar*)malloc( sizeof(statevar) );
mystate->result = nullptr;
- mystate->csync = new CSYNC("/tmp/check_csync1", new OCC::SyncJournalDb(""));
-
- mystate->csync->current = LOCAL_REPLICA;
-
*state = mystate;
return 0;
}
}
static int teardown(void **state) {
- auto *sv = (statevar*) *state;
- CSYNC *csync = sv->csync;
int rc = 0;
output("================== Tearing down!\n");
- auto statedb = csync->statedb;
- delete csync;
- delete statedb;
-
rc = _tchdir(wd_buffer);
assert_int_equal(rc, 0);
csync_vio_handle_t *dh = nullptr;
std::unique_ptr<csync_file_stat_t> dirent;
auto *sv = (statevar*) *state;
- CSYNC *csync = sv->csync;
char *subdir = nullptr;
char *subdir_out = nullptr;
int rc = 0;
const char *format_str = "%s C:%s";
#endif
- dh = csync_vio_opendir(csync, dir);
+ dh = csync_vio_local_opendir(dir);
assert_non_null(dh);
- while( (dirent = csync_vio_readdir(csync, dh)) ) {
+ while( (dirent = csync_vio_local_readdir(dh)) ) {
assert_non_null(dirent.get());
if (!dirent->original_path.isEmpty()) {
sv->ignored_dir = c_strdup(dirent->original_path);
SAFE_FREE(subdir_out);
}
- rc = csync_vio_closedir(csync, dh);
+ rc = csync_vio_local_closedir(dh);
assert_int_equal(rc, 0);
}