discovery.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. * for more details.
  13. */
  14. #pragma once
  15. #include <QObject>
  16. #include "discoveryphase.h"
  17. #include "syncfileitem.h"
  18. #include "common/asserts.h"
  19. #include "common/syncjournaldb.h"
  20. class ExcludedFiles;
  21. namespace OCC {
  22. class SyncJournalDb;
  23. /**
  24. * Job that handles discovery of a directory.
  25. *
  26. * This includes:
  27. * - Do a DiscoverySingleDirectoryJob network job which will do a PROPFIND of this directory
  28. * - Stat all the entries in the local file system for this directory
  29. * - Merge all information (and the information from the database) in order to know what needs
  30. * to be done for every file within this directory.
  31. * - For every sub-directory within this directory, "recursively" create a new ProcessDirectoryJob.
  32. *
  33. * This job is tightly coupled with the DiscoveryPhase class.
  34. *
  35. * After being start()'ed this job will perform work asynchronously and emit finished() when done.
  36. *
  37. * Internally, this job will call DiscoveryPhase::scheduleMoreJobs when one of its sub-jobs is
  38. * finished. DiscoveryPhase::scheduleMoreJobs will call processSubJobs() to continue work until
  39. * the job is finished.
  40. *
  41. * Results are fed outwards via the DiscoveryPhase::itemDiscovered() signal.
  42. */
  43. class ProcessDirectoryJob : public QObject
  44. {
  45. Q_OBJECT
  46. struct PathTuple;
  47. public:
  48. enum QueryMode {
  49. NormalQuery,
  50. ParentDontExist, // Do not query this folder because it does not exist
  51. ParentNotChanged, // No need to query this folder because it has not changed from what is in the DB
  52. InBlackList // Do not query this folder because it is in the blacklist (remote entries only)
  53. };
  54. Q_ENUM(QueryMode)
  55. /** For creating the root job
  56. *
  57. * The base pin state is used if the root dir's pin state can't be retrieved.
  58. */
  59. explicit ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, QObject *parent)
  60. : QObject(parent)
  61. , _discoveryData(data)
  62. {
  63. computePinState(basePinState);
  64. }
  65. /// For creating subjobs
  66. explicit ProcessDirectoryJob(const PathTuple &path, const SyncFileItemPtr &dirItem,
  67. QueryMode queryLocal, QueryMode queryServer,
  68. ProcessDirectoryJob *parent)
  69. : QObject(parent)
  70. , _dirItem(dirItem)
  71. , _queryServer(queryServer)
  72. , _queryLocal(queryLocal)
  73. , _discoveryData(parent->_discoveryData)
  74. , _currentFolder(path)
  75. {
  76. computePinState(parent->_pinState);
  77. }
  78. void start();
  79. /** Start up to nbJobs, return the number of job started; emit finished() when done */
  80. int processSubJobs(int nbJobs);
  81. SyncFileItemPtr _dirItem;
  82. private:
  83. /** Structure representing a path during discovery. A same path may have different value locally
  84. * or on the server in case of renames.
  85. *
  86. * These strings never start or ends with slashes. They are all relative to the folder's root.
  87. * Usually they are all the same and are even shared instance of the same QString.
  88. *
  89. * _server and _local paths will differ if there are renames, example:
  90. * remote renamed A/ to B/ and local renamed A/X to A/Y then
  91. * target: B/Y/file
  92. * original: A/X/file
  93. * local: A/Y/file
  94. * server: B/X/file
  95. */
  96. struct PathTuple
  97. {
  98. QString _original; // Path as in the DB (before the sync)
  99. QString _target; // Path that will be the result after the sync (and will be in the DB)
  100. QString _server; // Path on the server (before the sync)
  101. QString _local; // Path locally (before the sync)
  102. static QString pathAppend(const QString &base, const QString &name)
  103. {
  104. return base.isEmpty() ? name : base + QLatin1Char('/') + name;
  105. }
  106. PathTuple addName(const QString &name) const
  107. {
  108. PathTuple result;
  109. result._original = pathAppend(_original, name);
  110. auto buildString = [&](const QString &other) {
  111. // Optimize by trying to keep all string implicitly shared if they are the same (common case)
  112. return other == _original ? result._original : pathAppend(other, name);
  113. };
  114. result._target = buildString(_target);
  115. result._server = buildString(_server);
  116. result._local = buildString(_local);
  117. return result;
  118. }
  119. };
  120. /** Iterate over entries inside the directory (non-recursively).
  121. *
  122. * Called once _serverEntries and _localEntries are filled
  123. * Calls processFile() for each non-excluded one.
  124. * Will start scheduling subdir jobs when done.
  125. */
  126. void process();
  127. // return true if the file is excluded.
  128. // path is the full relative path of the file. localName is the base name of the local entry.
  129. bool handleExcluded(const QString &path, const QString &localName, bool isDirectory,
  130. bool isHidden, bool isSymlink);
  131. /** Reconcile local/remote/db information for a single item.
  132. *
  133. * Can be a file or a directory.
  134. * Usually ends up emitting itemDiscovered() or creating a subdirectory job.
  135. *
  136. * This main function delegates some work to the processFile* functions.
  137. */
  138. void processFile(PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &);
  139. /// processFile helper for when remote information is available, typically flows into AnalyzeLocalInfo when done
  140. void processFileAnalyzeRemoteInfo(const SyncFileItemPtr &item, PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &);
  141. /// processFile helper for reconciling local changes
  142. void processFileAnalyzeLocalInfo(const SyncFileItemPtr &item, PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &, QueryMode recurseQueryServer);
  143. /// processFile helper for local/remote conflicts
  144. void processFileConflict(const SyncFileItemPtr &item, PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &);
  145. /// processFile helper for common final processing
  146. void processFileFinalize(const SyncFileItemPtr &item, PathTuple, bool recurse, QueryMode recurseQueryLocal, QueryMode recurseQueryServer);
  147. /** Checks the permission for this item, if needed, change the item to a restoration item.
  148. * @return false indicate that this is an error and if it is a directory, one should not recurse
  149. * inside it.
  150. */
  151. bool checkPermissions(const SyncFileItemPtr &item);
  152. struct MovePermissionResult
  153. {
  154. // whether moving/renaming the source is ok
  155. bool sourceOk = false;
  156. // whether the destination accepts (always true for renames)
  157. bool destinationOk = false;
  158. // whether creating a new file/dir in the destination is ok
  159. bool destinationNewOk = false;
  160. };
  161. /**
  162. * Check if the move is of a specified file within this directory is allowed.
  163. * Return true if it is allowed, false otherwise
  164. */
  165. MovePermissionResult checkMovePermissions(RemotePermissions srcPerm, const QString &srcPath, bool isDirectory);
  166. void processBlacklisted(const PathTuple &, const LocalInfo &, const SyncJournalFileRecord &dbEntry);
  167. void subJobFinished();
  168. /** An DB operation failed */
  169. void dbError();
  170. void addVirtualFileSuffix(QString &str) const;
  171. bool hasVirtualFileSuffix(const QString &str) const;
  172. void chopVirtualFileSuffix(QString &str) const;
  173. /** Convenience to detect suffix-vfs modes */
  174. bool isVfsWithSuffix() const;
  175. /** Start a remote discovery network job
  176. *
  177. * It fills _serverNormalQueryEntries and sets _serverQueryDone when done.
  178. */
  179. DiscoverySingleDirectoryJob *startAsyncServerQuery();
  180. /** Discover the local directory
  181. *
  182. * Fills _localNormalQueryEntries.
  183. */
  184. void startAsyncLocalQuery();
  185. /** Sets _pinState, the directory's pin state
  186. *
  187. * If the folder exists locally its state is retrieved, otherwise the
  188. * parent's pin state is inherited.
  189. */
  190. void computePinState(PinState parentState);
  191. /** Adjust record._type if the db pin state suggests it.
  192. *
  193. * If the pin state is stored in the database (suffix vfs only right now)
  194. * its effects won't be seen in localEntry._type. Instead the effects
  195. * should materialize in dbEntry._type.
  196. *
  197. * This function checks whether the combination of file type and pin
  198. * state suggests a hydration or dehydration action and changes the
  199. * _type field accordingly.
  200. */
  201. void setupDbPinStateActions(SyncJournalFileRecord &record);
  202. QueryMode _queryServer = QueryMode::NormalQuery;
  203. QueryMode _queryLocal = QueryMode::NormalQuery;
  204. // Holds entries that resulted from a NormalQuery
  205. QVector<RemoteInfo> _serverNormalQueryEntries;
  206. QVector<LocalInfo> _localNormalQueryEntries;
  207. // Whether the local/remote directory item queries are done. Will be set
  208. // even even for do-nothing (!= NormalQuery) queries.
  209. bool _serverQueryDone = false;
  210. bool _localQueryDone = false;
  211. RemotePermissions _rootPermissions;
  212. QPointer<DiscoverySingleDirectoryJob> _serverJob;
  213. /** Number of currently running async jobs.
  214. *
  215. * These "async jobs" have nothing to do with the jobs for subdirectories
  216. * which are being tracked by _queuedJobs and _runningJobs.
  217. *
  218. * They are jobs that need to be completed to finish processing of directory
  219. * entries. This variable is used to ensure this job doesn't finish while
  220. * these jobs are still in flight.
  221. */
  222. int _pendingAsyncJobs = 0;
  223. /** The queued and running jobs for subdirectories.
  224. *
  225. * The jobs are enqueued while processind directory entries and
  226. * then gradually run via calls to processSubJobs().
  227. */
  228. std::deque<ProcessDirectoryJob *> _queuedJobs;
  229. QVector<ProcessDirectoryJob *> _runningJobs;
  230. DiscoveryPhase *_discoveryData;
  231. PathTuple _currentFolder;
  232. bool _childModified = false; // the directory contains modified item what would prevent deletion
  233. bool _childIgnored = false; // The directory contains ignored item that would prevent deletion
  234. PinState _pinState = PinState::Unspecified; // The directory's pin-state, see computePinState()
  235. signals:
  236. void finished();
  237. // The root etag of this directory was fetched
  238. void etag(const QString &, const QDateTime &time);
  239. };
  240. }