Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/csync/csync_update.cpp33
-rw-r--r--src/libsync/discovery.cpp78
-rw-r--r--src/libsync/discovery.h40
-rw-r--r--src/libsync/discoveryphase.cpp178
-rw-r--r--src/libsync/discoveryphase.h43
-rw-r--r--test/testremotediscovery.cpp2
6 files changed, 105 insertions, 269 deletions
diff --git a/src/csync/csync_update.cpp b/src/csync/csync_update.cpp
index d506ba61b..34289aa16 100644
--- a/src/csync/csync_update.cpp
+++ b/src/csync/csync_update.cpp
@@ -523,39 +523,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
return 0;
}
- if ((dh = csync_vio_opendir(ctx, uri)) == NULL) {
- if (ctx->abort) {
- qCDebug(lcUpdate, "Aborted!");
- ctx->status_code = CSYNC_STATUS_ABORTED;
- goto error;
- }
- /* permission denied */
- ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR);
-
- // 403 Forbidden can be sent by the server if the file firewall is active.
- // A file or directory should be ignored and sync must continue. See #3490
- if (errno == ERRNO_FORBIDDEN) {
- qCWarning(lcUpdate, "Directory access Forbidden (File Firewall?)");
- if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_FORBIDDEN) ) {
- return 0;
- }
- /* if current_fs is not defined here, better throw an error */
- }
- // The server usually replies with the custom "503 Storage not available"
- // if some path is temporarily unavailable. But in some cases a standard 503
- // is returned too. Thus we can't distinguish the two and will treat any
- // 503 as request to ignore the folder. See #3113 #2884.
- else if(errno == ERRNO_STORAGE_UNAVAILABLE || errno == ERRNO_SERVICE_UNAVAILABLE) {
- qCWarning(lcUpdate, "Storage was not available!");
- if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_STORAGE_UNAVAILABLE ) ) {
- return 0;
- }
- /* if current_fs is not defined here, better throw an error */
- } else {
- qCWarning(lcUpdate, "opendir failed for %s - errno %d", uri, errno);
- }
- goto error;
- }
while (true) {
// Get the next item in the directory
diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp
index 0f9bb3a66..cda7ee963 100644
--- a/src/libsync/discovery.cpp
+++ b/src/libsync/discovery.cpp
@@ -29,53 +29,46 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcDisco, "sync.discovery", QtInfoMsg)
-static RemoteInfo remoteInfoFromCSync(const csync_file_stat_t &x)
-{
- RemoteInfo ri;
- ri.name = QFileInfo(QString::fromUtf8(x.path)).fileName();
- ri.etag = x.etag;
- ri.fileId = x.file_id;
- ri.checksumHeader = x.checksumHeader;
- ri.modtime = x.modtime;
- ri.size = x.size;
- ri.isDirectory = x.type == ItemTypeDirectory;
- ri.remotePerm = x.remotePerm;
- return ri;
-}
-
-DiscoverServerJob::DiscoverServerJob(const AccountPtr &account, const QString &path, QObject *parent)
- : DiscoverySingleDirectoryJob(account, path, parent)
-{
- connect(this, &DiscoverySingleDirectoryJob::finishedWithResult, this, [this] {
- auto csync_results = takeResults();
- QVector<RemoteInfo> results;
- std::transform(csync_results.begin(), csync_results.end(), std::back_inserter(results),
- [](const auto &x) { return remoteInfoFromCSync(*x); });
- emit this->finished(results);
- });
-
- connect(this, &DiscoverySingleDirectoryJob::finishedWithError, this,
- [this](int code, const QString &msg) {
- emit this->finished({ code, msg });
- });
-}
-
void ProcessDirectoryJob::start()
{
qCInfo(lcDisco) << "STARTING" << _currentFolder._server << _queryServer << _currentFolder._local << _queryLocal;
- DiscoverServerJob *serverJob = nullptr;
+ DiscoverySingleDirectoryJob *serverJob = nullptr;
if (_queryServer == NormalQuery) {
- serverJob = new DiscoverServerJob(_discoveryData->_account, _discoveryData->_remoteFolder + _currentFolder._server, this);
- connect(serverJob, &DiscoverServerJob::finished, this, [this](const auto &results) {
+ serverJob = new DiscoverySingleDirectoryJob(_discoveryData->_account, _discoveryData->_remoteFolder + _currentFolder._server, this);
+ connect(serverJob, &DiscoverySingleDirectoryJob::finished, this, [this](const auto &results) {
if (results) {
_serverEntries = *results;
_hasServerEntries = true;
if (_hasLocalEntries)
process();
} else {
- qWarning() << results.errorMessage();
- qFatal("TODO: ERROR HANDLING");
+ if (results.errorCode() == 403) {
+ // 403 Forbidden can be sent by the server if the file firewall is active.
+ // A file or directory should be ignored and sync must continue. See #3490
+ qCWarning(lcDisco, "Directory access Forbidden (File Firewall?)");
+ if (_dirItem) {
+ _dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE;
+ _dirItem->_errorString = results.errorMessage();
+ emit finished();
+ return;
+ }
+ } else if (results.errorCode() == 503) {
+ // The server usually replies with the custom "503 Storage not available"
+ // if some path is temporarily unavailable. But in some cases a standard 503
+ // is returned too. Thus we can't distinguish the two and will treat any
+ // 503 as request to ignore the folder. See #3113 #2884.
+ qCWarning(lcDisco(), "Storage was not available!");
+ if (_dirItem) {
+ _dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE;
+ _dirItem->_errorString = results.errorMessage();
+ emit finished();
+ return;
+ }
+ }
+ emit _discoveryData->fatalError(tr("Server replied with an error while reading directory '%1' : %2")
+ .arg(_currentFolder._server, results.errorMessage()));
+ emit finished();
}
});
serverJob->start();
@@ -115,12 +108,6 @@ void ProcessDirectoryJob::start()
}
} else if (errno == ENOENT) {
errorString = tr("Directory not found: %1").arg(_discoveryData->_localDir + _currentFolder._local);
- if (_dirItem) {
- _dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE;
- _dirItem->_errorString = errorString;
- emit finished();
- return;
- }
} else if (errno == ENOTDIR) {
// Not a directory..
// Just consider it is empty
@@ -133,6 +120,7 @@ void ProcessDirectoryJob::start()
emit finished();
return;
}
+ errno = 0;
while (auto dirent = csync_vio_local_readdir(dh)) {
LocalInfo i;
static QTextCodec *codec = QTextCodec::codecForName("UTF-8");
@@ -158,6 +146,12 @@ void ProcessDirectoryJob::start()
qFatal("FIXME: NEED TO CARE ABOUT THE OTHER STUFF ");
_localEntries.push_back(i);
}
+ if (errno != 0) {
+ // Note: Windows vio converts any error into EACCES
+ qCWarning(lcDisco) << "readdir failed for file in " << _currentFolder._local << " - errno: " << errno;
+ emit _discoveryData->fatalError(tr("Error while reading directory %1").arg(_discoveryData->_localDir + _currentFolder._local));
+ emit finished();
+ }
csync_vio_local_closedir(dh);
}
_hasLocalEntries = true;
diff --git a/src/libsync/discovery.h b/src/libsync/discovery.h
index 43444b030..638aa9024 100644
--- a/src/libsync/discovery.h
+++ b/src/libsync/discovery.h
@@ -24,44 +24,6 @@ class ExcludedFiles;
namespace OCC {
class SyncJournalDb;
-struct RemoteInfo
-{
- QString name;
- QByteArray etag;
- QByteArray fileId;
- QByteArray checksumHeader;
- OCC::RemotePermissions remotePerm;
- time_t modtime = 0;
- int64_t size = 0;
- bool isDirectory = false;
- bool isValid() const { return !name.isNull(); }
-};
-
-struct LocalInfo
-{
- QString name;
- time_t modtime = 0;
- int64_t size = 0;
- uint64_t inode = 0;
- bool isDirectory = false;
- bool isHidden = false;
- bool isVirtualFile = false;
- bool isValid() const { return !name.isNull(); }
-};
-
-/**
- * Do the propfind on the server.
- * TODO: merge with DiscoverySingleDirectoryJob
- */
-class DiscoverServerJob : public DiscoverySingleDirectoryJob
-{
- Q_OBJECT
-public:
- explicit DiscoverServerJob(const AccountPtr &account, const QString &path, QObject *parent = 0);
-signals:
- void finished(const Result<QVector<RemoteInfo>> &result);
-};
-
class ProcessDirectoryJob : public QObject
{
Q_OBJECT
@@ -122,7 +84,7 @@ private:
bool _hasServerEntries = false;
bool _hasLocalEntries = false;
int _pendingAsyncJobs = 0;
- QPointer<DiscoverServerJob> _serverJob;
+ QPointer<DiscoverySingleDirectoryJob> _serverJob;
std::deque<ProcessDirectoryJob *> _queuedJobs;
QVector<ProcessDirectoryJob *> _runningJobs;
QueryMode _queryServer;
diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp
index 9bb43fccd..a5ad24658 100644
--- a/src/libsync/discoveryphase.cpp
+++ b/src/libsync/discoveryphase.cpp
@@ -182,80 +182,6 @@ void DiscoveryJob::update_job_update_callback(bool local,
}
}*/
-// Only use for error cases! It will always set an error errno
-int get_errno_from_http_errcode(int err, const QString &reason)
-{
- int new_errno = EIO;
-
- switch (err) {
- case 401: /* Unauthorized */
- case 402: /* Payment Required */
- case 407: /* Proxy Authentication Required */
- case 405:
- new_errno = EPERM;
- break;
- case 301: /* Moved Permanently */
- case 303: /* See Other */
- case 404: /* Not Found */
- case 410: /* Gone */
- new_errno = ENOENT;
- break;
- case 408: /* Request Timeout */
- case 504: /* Gateway Timeout */
- new_errno = EAGAIN;
- break;
- case 423: /* Locked */
- new_errno = EACCES;
- break;
- case 403: /* Forbidden */
- new_errno = ERRNO_FORBIDDEN;
- break;
- case 400: /* Bad Request */
- case 409: /* Conflict */
- case 411: /* Length Required */
- case 412: /* Precondition Failed */
- case 414: /* Request-URI Too Long */
- case 415: /* Unsupported Media Type */
- case 424: /* Failed Dependency */
- case 501: /* Not Implemented */
- new_errno = EINVAL;
- break;
- case 507: /* Insufficient Storage */
- new_errno = ENOSPC;
- break;
- case 206: /* Partial Content */
- case 300: /* Multiple Choices */
- case 302: /* Found */
- case 305: /* Use Proxy */
- case 306: /* (Unused) */
- case 307: /* Temporary Redirect */
- case 406: /* Not Acceptable */
- case 416: /* Requested Range Not Satisfiable */
- case 417: /* Expectation Failed */
- case 422: /* Unprocessable Entity */
- case 500: /* Internal Server Error */
- case 502: /* Bad Gateway */
- case 505: /* HTTP Version Not Supported */
- new_errno = EIO;
- break;
- case 503: /* Service Unavailable */
- // https://github.com/owncloud/core/pull/26145/files
- if (reason == "Storage not available" || reason == "Storage is temporarily not available") {
- new_errno = ERRNO_STORAGE_UNAVAILABLE;
- } else {
- new_errno = ERRNO_SERVICE_UNAVAILABLE;
- }
- break;
- case 413: /* Request Entity too Large */
- new_errno = EFBIG;
- break;
- default:
- new_errno = EIO;
- }
- return new_errno;
-}
-
-
DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, QObject *parent)
: QObject(parent)
, _subPath(path)
@@ -306,50 +232,48 @@ void DiscoverySingleDirectoryJob::abort()
}
}
-static void propertyMapToFileStat(const QMap<QString, QString> &map, csync_file_stat_t *file_stat)
+static void propertyMapToFileStat(const QMap<QString, QString> &map, RemoteInfo &result)
{
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
QString property = it.key();
QString value = it.value();
if (property == "resourcetype") {
- if (value.contains("collection")) {
- file_stat->type = ItemTypeDirectory;
- } else {
- file_stat->type = ItemTypeFile;
- }
+ result.isDirectory = value.contains("collection");
} else if (property == "getlastmodified") {
- file_stat->modtime = oc_httpdate_parse(value.toUtf8());
+ result.modtime = oc_httpdate_parse(value.toUtf8());
} else if (property == "getcontentlength") {
// See #4573, sometimes negative size values are returned
bool ok = false;
qlonglong ll = value.toLongLong(&ok);
if (ok && ll >= 0) {
- file_stat->size = ll;
+ result.size = ll;
} else {
- file_stat->size = 0;
+ result.size = 0;
}
} else if (property == "getetag") {
- file_stat->etag = Utility::normalizeEtag(value.toUtf8());
+ result.etag = Utility::normalizeEtag(value.toUtf8());
} else if (property == "id") {
- file_stat->file_id = value.toUtf8();
+ result.fileId = value.toUtf8();
} else if (property == "downloadURL") {
- file_stat->directDownloadUrl = value.toUtf8();
+ qFatal("FIXME: downloadURL and dDC");
+ //file_stat->directDownloadUrl = value.toUtf8();
} else if (property == "dDC") {
- file_stat->directDownloadCookies = value.toUtf8();
+ qFatal("FIXME: downloadURL and dDC");
+ // file_stat->directDownloadCookies = value.toUtf8();
} else if (property == "permissions") {
- file_stat->remotePerm = RemotePermissions(value);
+ result.remotePerm = RemotePermissions(value);
} else if (property == "checksums") {
- file_stat->checksumHeader = findBestChecksum(value.toUtf8());
+ result.checksumHeader = findBestChecksum(value.toUtf8());
} else if (property == "share-types" && !value.isEmpty()) {
// Since QMap is sorted, "share-types" is always after "permissions".
- if (file_stat->remotePerm.isNull()) {
+ if (result.remotePerm.isNull()) {
qWarning() << "Server returned a share type, but no permissions?";
} else {
// S means shared with me.
// But for our purpose, we want to know if the file is shared. It does not matter
// if we are the owner or not.
// Piggy back on the persmission field
- file_stat->remotePerm.setPermission(RemotePermissions::IsShared);
+ result.remotePerm.setPermission(RemotePermissions::IsShared);
}
}
}
@@ -369,43 +293,33 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
_dataFingerprint = map.value("data-fingerprint").toUtf8();
}
} else {
- // Remove <webDAV-Url>/folder/ from <webDAV-Url>/folder/subfile.txt
- file.remove(0, _lsColJob->reply()->request().url().path().length());
- // remove trailing slash
- while (file.endsWith('/')) {
- file.chop(1);
- }
- // remove leading slash
- while (file.startsWith('/')) {
- file = file.remove(0, 1);
- }
- std::unique_ptr<csync_file_stat_t> file_stat(new csync_file_stat_t);
- file_stat->path = file.toUtf8();
- file_stat->size = -1;
- file_stat->modtime = -1;
- propertyMapToFileStat(map, file_stat.get());
- if (file_stat->type == ItemTypeDirectory)
- file_stat->size = 0;
- if (file_stat->type == ItemTypeSkip
- || file_stat->size == -1
- || file_stat->modtime == -1
- || file_stat->remotePerm.isNull()
- || file_stat->etag.isEmpty()
- || file_stat->file_id.isEmpty()) {
+ RemoteInfo result;
+ int slash = file.lastIndexOf('/');
+ result.name = file.mid(slash + 1);
+ result.size = -1;
+ result.modtime = -1;
+ propertyMapToFileStat(map, result);
+ if (result.isDirectory)
+ result.size = 0;
+ if (result.size == -1
+ || result.modtime == -1
+ || result.remotePerm.isNull()
+ || result.etag.isEmpty()
+ || result.fileId.isEmpty()) {
_error = tr("The server file discovery reply is missing data.");
qCWarning(lcDiscovery)
- << "Missing properties:" << file << file_stat->type << file_stat->size
- << file_stat->modtime << file_stat->remotePerm.toString()
- << file_stat->etag << file_stat->file_id;
+ << "Missing properties:" << file << result.isDirectory << result.size
+ << result.modtime << result.remotePerm.toString()
+ << result.etag << result.fileId;
}
- if (_isExternalStorage && file_stat->remotePerm.hasPermission(RemotePermissions::IsMounted)) {
+ if (_isExternalStorage && result.remotePerm.hasPermission(RemotePermissions::IsMounted)) {
/* All the entries in a external storage have 'M' in their permission. However, for all
purposes in the desktop client, we only need to know about the mount points.
So replace the 'M' by a 'm' for every sub entries in an external storage */
- file_stat->remotePerm.unsetPermission(RemotePermissions::IsMounted);
- file_stat->remotePerm.setPermission(RemotePermissions::IsMountedSub);
+ result.remotePerm.unsetPermission(RemotePermissions::IsMounted);
+ result.remotePerm.setPermission(RemotePermissions::IsMountedSub);
}
QStringRef fileRef(&file);
@@ -413,7 +327,7 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
if (slashPos > -1) {
fileRef = file.midRef(slashPos + 1);
}
- _results.push_back(std::move(file_stat));
+ _results.push_back(std::move(result));
}
//This works in concerto with the RequestEtagJob and the Folder object to check if the remote folder changed.
@@ -431,17 +345,17 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithoutErrorSlot()
if (!_ignoredFirst) {
// This is a sanity check, if we haven't _ignoredFirst then it means we never received any directoryListingIteratedSlot
// which means somehow the server XML was bogus
- emit finishedWithError(ERRNO_WRONG_CONTENT, QLatin1String("Server error: PROPFIND reply is not XML formatted!"));
+ emit finished({ERRNO_WRONG_CONTENT, tr("Server error: PROPFIND reply is not XML formatted!")});
deleteLater();
return;
} else if (!_error.isEmpty()) {
- emit finishedWithError(ERRNO_WRONG_CONTENT, _error);
+ emit finished({ERRNO_WRONG_CONTENT, _error});
deleteLater();
return;
}
emit etag(_firstEtag);
emit etagConcatenation(_etagConcatenation);
- emit finishedWithResult();
+ emit finished(_results);
deleteLater();
}
@@ -451,20 +365,12 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r)
int httpCode = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString httpReason = r->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
QString msg = r->errorString();
- int errnoCode = EIO; // Something went wrong
qCWarning(lcDiscovery) << "LSCOL job error" << r->errorString() << httpCode << r->error();
- if (httpCode != 0 && httpCode != 207) {
- errnoCode = get_errno_from_http_errcode(httpCode, httpReason);
- } else if (r->error() != QNetworkReply::NoError) {
- errnoCode = EIO;
- } else if (!contentType.contains("application/xml; charset=utf-8")) {
- msg = QLatin1String("Server error: PROPFIND reply is not XML formatted!");
- errnoCode = ERRNO_WRONG_CONTENT;
- } else {
- // Default keep at EIO, see above
+ if (httpCode == 0 && r->error() == QNetworkReply::NoError
+ && !contentType.contains("application/xml; charset=utf-8")) {
+ msg = tr("Server error: PROPFIND reply is not XML formatted!");
}
-
- emit finishedWithError(errnoCode == 0 ? EIO : errnoCode, msg);
+ emit finished({httpCode, msg});
deleteLater();
}
diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h
index 1166a0d75..31f18b9a2 100644
--- a/src/libsync/discoveryphase.h
+++ b/src/libsync/discoveryphase.h
@@ -35,24 +35,33 @@ namespace OCC {
class Account;
class SyncJournalDb;
-/**
- * The Discovery Phase was once called "update" phase in csync terms.
- * Its goal is to look at the files in one of the remote and check compared to the db
- * if the files are new, or changed.
- */
-struct DiscoveryDirectoryResult
+struct RemoteInfo
+{
+ QString name;
+ QByteArray etag;
+ QByteArray fileId;
+ QByteArray checksumHeader;
+ OCC::RemotePermissions remotePerm;
+ time_t modtime = 0;
+ int64_t size = 0;
+ bool isDirectory = false;
+ bool isValid() const { return !name.isNull(); }
+};
+
+struct LocalInfo
{
- QString path;
- QString msg;
- int code;
- std::deque<std::unique_ptr<csync_file_stat_t>> list;
- DiscoveryDirectoryResult()
- : code(EIO)
- {
- }
+ QString name;
+ time_t modtime = 0;
+ int64_t size = 0;
+ uint64_t inode = 0;
+ bool isDirectory = false;
+ bool isHidden = false;
+ bool isVirtualFile = false;
+ bool isValid() const { return !name.isNull(); }
};
+
/**
* @brief The DiscoverySingleDirectoryJob class
*
@@ -69,22 +78,20 @@ public:
void setIsRootPath() { _isRootPath = true; }
void start();
void abort();
- std::deque<std::unique_ptr<csync_file_stat_t>> &&takeResults() { return std::move(_results); }
// This is not actually a network job, it is just a job
signals:
void firstDirectoryPermissions(RemotePermissions);
void etagConcatenation(const QString &);
void etag(const QString &);
- void finishedWithResult();
- void finishedWithError(int csyncErrnoCode, const QString &msg);
+ void finished(const Result<QVector<RemoteInfo>> &result);
private slots:
void directoryListingIteratedSlot(QString, const QMap<QString, QString> &);
void lsJobFinishedWithoutErrorSlot();
void lsJobFinishedWithErrorSlot(QNetworkReply *);
private:
- std::deque<std::unique_ptr<csync_file_stat_t>> _results;
+ QVector<RemoteInfo> _results;
QString _subPath;
QString _etagConcatenation;
QString _firstEtag;
diff --git a/test/testremotediscovery.cpp b/test/testremotediscovery.cpp
index 4945bbf4c..4e9953e50 100644
--- a/test/testremotediscovery.cpp
+++ b/test/testremotediscovery.cpp
@@ -106,7 +106,7 @@ private slots:
QScopedValueRollback<int> setHttpTimeout(AbstractNetworkJob::httpTimeout, errorKind == Timeout ? 1 : 10000);
QSignalSpy errorSpy(&fakeFolder.syncEngine(), &SyncEngine::syncError);
- QCOMPARE(fakeFolder.syncOnce(), false);
+ QCOMPARE(fakeFolder.syncOnce(), syncSucceeds);
qDebug() << "errorSpy=" << errorSpy;
// The folder B should not have been sync'ed (and in particular not removed)