1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_
6#define CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_
7
8#include <map>
9#include <set>
10#include <string>
11#include <vector>
12
13#include "base/containers/hash_tables.h"
14#include "base/containers/scoped_ptr_hash_map.h"
15#include "base/files/file_path.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/memory/scoped_vector.h"
18#include "base/memory/weak_ptr.h"
19#include "base/sequence_checker.h"
20#include "base/values.h"
21#include "chrome/browser/sync_file_system/drive_backend/tracker_id_set.h"
22#include "chrome/browser/sync_file_system/sync_status_code.h"
23
24namespace leveldb {
25class Env;
26}
27
28namespace google_apis {
29class ChangeResource;
30class FileResource;
31class ResourceEntry;
32}
33
34namespace tracked_objects {
35class Location;
36}
37
38namespace sync_file_system {
39namespace drive_backend {
40
41class FileDetails;
42class FileMetadata;
43class FileTracker;
44class LevelDBWrapper;
45class MetadataDatabaseIndexInterface;
46class ServiceMetadata;
47
48// MetadataDatabase holds and maintains a LevelDB instance and its indexes,
49// which holds 1)ServiceMetadata, 2)FileMetadata and 3)FileTracker.
50// 1) ServiceMetadata is a singleton in the database which holds information for
51//    the backend.
52// 2) FileMetadata represents a remote-side file and holds latest known
53//    metadata of the remote file.
54// 3) FileTracker represents a synced or to-be-synced file and maintains
55//    the local-side folder tree.
56//
57// The term "file" includes files, folders and other resources on Drive.
58//
59// FileTrackers form a tree structure on the database, which represents the
60// FileSystem trees of SyncFileSystem.  The tree has a FileTracker named
61// sync-root as its root node, and a set of FileTracker named app-root.  An
62// app-root represents a remote folder for an installed Chrome App and holds all
63// synced contents for the App.
64//
65// One FileMetadata is created for each tracked remote file, which is identified
66// by FileID.
67// One FileTracker is created for every different {parent tracker, FileID} pair,
68// excluding non-app-root inactive parent trackers. Multiple trackers may be
69// associated to one FileID when the file has multiple parents. Multiple
70// trackers may have the same {parent tracker, title} pair when the associated
71// remote files have the same title.
72//
73// Files have following state:
74//   - Unknown file
75//     - Has a dirty inactive tracker and empty synced_details.
76//     - Is initial state of a tracker, only file_id and parent_tracker_id field
77//       are known.
78//   - Folder
79//     - Is either one of sync-root folder, app-root folder or a regular folder.
80//     - Sync-root folder holds app-root folders as its direct children, and
81//       holds entire SyncFileSystem files as its descentants.  Its tracker
82//       should be stored in ServiceMetadata by its tracker_id.
83//     - App-root folder holds all files for an application as its descendants.
84//   - File
85//   - Unsupported file
86//     - Represents unsupported files such as hosted documents. Must be
87//       inactive.
88//
89// Invariants:
90//   - Any tracker in the database must either:
91//     - be sync-root,
92//     - have an app-root as its parent tracker, or
93//     - have an active tracker as its parent.
94//   That is, all trackers must be reachable from sync-root via app-root folders
95//   and active trackers.
96//
97//   - Any active tracker must either:
98//     - have |needs_folder_listing| flag and dirty flag, or
99//     - have all children at the stored largest change ID.
100//
101//   - If multiple trackers have the same parent tracker and same title, they
102//     must not have same |file_id|, and at most one of them may be active.
103//   - If multiple trackers have the same |file_id|, at most one of them may be
104//     active.
105//
106class MetadataDatabase {
107 public:
108  typedef std::vector<std::string> FileIDList;
109
110  enum ActivationStatus {
111    ACTIVATION_PENDING,
112    ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER,
113  };
114
115  enum UpdateOption {
116    UPDATE_TRACKER_FOR_UNSYNCED_FILE,
117    UPDATE_TRACKER_FOR_SYNCED_FILE,
118  };
119
120  // The entry point of the MetadataDatabase for production code.
121  // If |env_override| is non-NULL, internal LevelDB uses |env_override| instead
122  // of leveldb::Env::Default().  Use leveldb::MemEnv in test code for faster
123  // testing.
124  static scoped_ptr<MetadataDatabase> Create(
125      const base::FilePath& database_path,
126      leveldb::Env* env_override,
127      SyncStatusCode* status);
128  static scoped_ptr<MetadataDatabase> CreateInternal(
129      const base::FilePath& database_path,
130      leveldb::Env* env_override,
131      bool enable_on_disk_index,
132      SyncStatusCode* status);
133  static SyncStatusCode CreateForTesting(
134      scoped_ptr<LevelDBWrapper> db,
135      bool enable_on_disk_index,
136      scoped_ptr<MetadataDatabase>* metadata_database_out);
137
138  ~MetadataDatabase();
139
140  static void ClearDatabase(scoped_ptr<MetadataDatabase> metadata_database);
141
142  int64 GetLargestFetchedChangeID() const;
143  int64 GetSyncRootTrackerID() const;
144  bool HasSyncRoot() const;
145
146  // Returns all file metadata for the given |app_id|.
147  scoped_ptr<base::ListValue> DumpFiles(const std::string& app_id);
148
149  // Returns all database data.
150  scoped_ptr<base::ListValue> DumpDatabase();
151
152  // TODO(tzik): Move GetLargestKnownChangeID() to private section, and hide its
153  // handling in the class, instead of letting user do.
154  //
155  // Gets / updates the largest known change ID.
156  // The largest known change ID is on-memory and not persist over restart.
157  // This is supposed to use when a task fetches ChangeList in parallel to other
158  // operation.  When a task starts fetching paged ChangeList one by one, it
159  // should update the largest known change ID on the first round and background
160  // remaining fetch job.
161  // Then, when other tasks that update FileMetadata by UpdateByFileResource,
162  // it should use largest known change ID as the |change_id| that prevents
163  // FileMetadata from overwritten by ChangeList.
164  // Also if other tasks try to update a remote resource whose change is not yet
165  // retrieved the task should fail due to etag check, so we should be fine.
166  int64 GetLargestKnownChangeID() const;
167  void UpdateLargestKnownChangeID(int64 change_id);
168
169  // Populates empty database with initial data.
170  // Adds a file metadata and a file tracker for |sync_root_folder|, and adds
171  // file metadata and file trackers for each |app_root_folders|.
172  // Newly added tracker for |sync_root_folder| is active and non-dirty.
173  // Newly added trackers for |app_root_folders| are inactive and non-dirty.
174  // Trackers for |app_root_folders| are not yet registered as app-roots, but
175  // are ready to register.
176  SyncStatusCode PopulateInitialData(
177      int64 largest_change_id,
178      const google_apis::FileResource& sync_root_folder,
179      const ScopedVector<google_apis::FileResource>& app_root_folders);
180
181  // Returns true if the folder associated to |app_id| is enabled.
182  bool IsAppEnabled(const std::string& app_id) const;
183
184  // Registers existing folder as the app-root for |app_id|.  The folder
185  // must be an inactive folder that does not yet associated to any App.
186  // This method associates the folder with |app_id| and activates it.
187  SyncStatusCode RegisterApp(const std::string& app_id,
188                             const std::string& folder_id);
189
190  // Inactivates the folder associated to the app to disable |app_id|.
191  // Does nothing if |app_id| is already disabled.
192  SyncStatusCode DisableApp(const std::string& app_id);
193
194  // Activates the folder associated to |app_id| to enable |app_id|.
195  // Does nothing if |app_id| is already enabled.
196  SyncStatusCode EnableApp(const std::string& app_id);
197
198  // Unregisters the folder as the app-root for |app_id|.  If |app_id| does not
199  // exist, does nothing.  The folder is left as an inactive regular folder.
200  // Note that the inactivation drops all descendant files since they are no
201  // longer reachable from sync-root via active folder or app-root.
202  SyncStatusCode UnregisterApp(const std::string& app_id);
203
204  // Finds the app-root folder for |app_id|.  Returns true if exists.
205  // Copies the result to |tracker| if it is non-NULL.
206  bool FindAppRootTracker(const std::string& app_id,
207                          FileTracker* tracker) const;
208
209  // Finds the file identified by |file_id|.  Returns true if the file is found.
210  // Copies the metadata identified by |file_id| into |file| if exists and
211  // |file| is non-NULL.
212  bool FindFileByFileID(const std::string& file_id, FileMetadata* file) const;
213
214  // Finds the tracker identified by |tracker_id|.  Returns true if the tracker
215  // is found.
216  // Copies the tracker identified by |tracker_id| into |tracker| if exists and
217  // |tracker| is non-NULL.
218  bool FindTrackerByTrackerID(int64 tracker_id, FileTracker* tracker) const;
219
220  // Finds the trackers tracking |file_id|.  Returns true if the trackers are
221  // found.
222  bool FindTrackersByFileID(const std::string& file_id,
223                            TrackerIDSet* trackers) const;
224
225  // Finds the set of trackers whose parent's tracker ID is |parent_tracker_id|,
226  // and who has |title| as its title in the synced_details.
227  // Copies the tracker set to |trackers| if it is non-NULL.
228  // Returns true if the trackers are found.
229  bool FindTrackersByParentAndTitle(
230      int64 parent_tracker_id,
231      const std::string& title,
232      TrackerIDSet* trackers) const;
233
234  // Builds the file path for the given tracker.  Returns true on success.
235  // |path| can be NULL.
236  // The file path is relative to app-root and have a leading path separator.
237  bool BuildPathForTracker(int64 tracker_id, base::FilePath* path) const;
238
239  // Builds the file path for the given tracker for display purpose.
240  // This may return a path ending with '<unknown>' if the given tracker does
241  // not have title information (yet). This may return an empty path.
242  base::FilePath BuildDisplayPathForTracker(const FileTracker& tracker) const;
243
244  // Returns false if no registered app exists associated to |app_id|.
245  // If |full_path| is active, assigns the tracker of |full_path| to |tracker|.
246  // Otherwise, assigns the nearest active ancestor to |full_path| to |tracker|.
247  // Also, assigns the full path of |tracker| to |path|.
248  bool FindNearestActiveAncestor(const std::string& app_id,
249                                 const base::FilePath& full_path,
250                                 FileTracker* tracker,
251                                 base::FilePath* path) const;
252
253  // Updates database by |changes|.
254  // Marks each tracker for modified file as dirty and adds new trackers if
255  // needed.
256  SyncStatusCode UpdateByChangeList(
257      int64 largest_change_id,
258      ScopedVector<google_apis::ChangeResource> changes);
259
260  // Updates database by |resource|.
261  // Marks each tracker for modified file as dirty and adds new trackers if
262  // needed.
263  SyncStatusCode UpdateByFileResource(
264      const google_apis::FileResource& resource);
265  SyncStatusCode UpdateByFileResourceList(
266      ScopedVector<google_apis::FileResource> resources);
267
268  SyncStatusCode UpdateByDeletedRemoteFile(const std::string& file_id);
269  SyncStatusCode UpdateByDeletedRemoteFileList(const FileIDList& file_ids);
270
271  // Adds new FileTracker and FileMetadata.  The database must not have
272  // |resource| beforehand.
273  // The newly added tracker under |parent_tracker_id| is active and non-dirty.
274  // Deactivates existing active tracker if exists that has the same title and
275  // parent_tracker to the newly added tracker.
276  SyncStatusCode ReplaceActiveTrackerWithNewResource(
277      int64 parent_tracker_id,
278      const google_apis::FileResource& resource);
279
280  // Adds |child_file_ids| to |folder_id| as its children.
281  // This method affects the active tracker only.
282  // If the tracker has no further change to sync, unmarks its dirty flag.
283  SyncStatusCode PopulateFolderByChildList(const std::string& folder_id,
284                                           const FileIDList& child_file_ids);
285
286  // Updates |synced_details| of the tracker with |updated_details|.
287  SyncStatusCode UpdateTracker(int64 tracker_id,
288                               const FileDetails& updated_details);
289
290  // Activates a tracker identified by |parent_tracker_id| and |file_id| if the
291  // tracker can be activated without inactivating other trackers that have the
292  // same |file_id| but different paths.
293  // If |file_id| has another active tracker, the function returns
294  // ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER and does not invoke |callback|.
295  // If there is another active tracker that has the same path but different
296  // |file_id|, inactivates the tracker.
297  // In success case, returns ACTIVATION_PENDING and invokes |callback| upon
298  // completion.
299  //
300  // The tracker to be activated must:
301  //  - have a tracked metadata in the database,
302  //  - have |synced_details| with valid |title|.
303  ActivationStatus TryActivateTracker(int64 parent_tracker_id,
304                                      const std::string& file_id,
305                                      SyncStatusCode* status);
306
307  // Changes the priority of the tracker to low.
308  void DemoteTracker(int64 tracker_id);
309  bool PromoteDemotedTrackers();
310  void PromoteDemotedTracker(int64 tracker_id);
311
312  // Returns true if there is a normal priority dirty tracker.
313  // Assigns the dirty tracker if exists and |tracker| is non-NULL.
314  bool GetDirtyTracker(FileTracker* tracker) const;
315
316  // Returns true if there is a low priority dirty tracker.
317  bool HasDemotedDirtyTracker() const;
318
319  bool HasDirtyTracker() const;
320  size_t CountDirtyTracker() const;
321  size_t CountFileMetadata() const;
322  size_t CountFileTracker() const;
323
324  bool GetMultiParentFileTrackers(std::string* file_id,
325                                  TrackerIDSet* trackers);
326  bool GetConflictingTrackers(TrackerIDSet* trackers);
327
328  // Sets |app_ids| to a list of all registered app ids.
329  void GetRegisteredAppIDs(std::vector<std::string>* app_ids);
330
331  // Clears dirty flag of trackers that can be cleared without external
332  // interactien.
333  SyncStatusCode SweepDirtyTrackers(const std::vector<std::string>& file_ids);
334
335 private:
336  friend class MetadataDatabaseTest;
337
338  MetadataDatabase(const base::FilePath& database_path,
339                   bool enable_on_disk_index,
340                   leveldb::Env* env_override);
341  SyncStatusCode Initialize();
342
343  // Database manipulation methods.
344  void RegisterTrackerAsAppRoot(const std::string& app_id,
345                                int64 tracker_id);
346
347  void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker,
348                                       const std::string& file_id);
349  void CreateTrackerForParentAndFileMetadata(const FileTracker& parent_tracker,
350                                             const FileMetadata& file_metadata,
351                                             UpdateOption option);
352  void CreateTrackerInternal(const FileTracker& parent_tracker,
353                             const std::string& file_id,
354                             const FileDetails* details,
355                             UpdateOption option);
356
357  void MaybeAddTrackersForNewFile(const FileMetadata& file,
358                                  UpdateOption option);
359
360  int64 IncrementTrackerID();
361
362  bool CanActivateTracker(const FileTracker& tracker);
363  bool ShouldKeepDirty(const FileTracker& tracker) const;
364
365  bool HasDisabledAppRoot(const FileTracker& tracker) const;
366  bool HasActiveTrackerForFileID(const std::string& file_id) const;
367  bool HasActiveTrackerForPath(int64 parent_tracker,
368                               const std::string& title) const;
369
370  void RemoveUnneededTrackersForMissingFile(const std::string& file_id);
371  void UpdateByFileMetadata(const tracked_objects::Location& from_where,
372                            scoped_ptr<FileMetadata> file,
373                            UpdateOption option);
374
375  SyncStatusCode WriteToDatabase();
376
377  bool HasNewerFileMetadata(const std::string& file_id, int64 change_id);
378
379  scoped_ptr<base::ListValue> DumpTrackers();
380  scoped_ptr<base::ListValue> DumpMetadata();
381
382  void AttachSyncRoot(const google_apis::FileResource& sync_root_folder);
383  void AttachInitialAppRoot(const google_apis::FileResource& app_root_folder);
384
385  void ForceActivateTrackerByPath(int64 parent_tracker_id,
386                                  const std::string& title,
387                                  const std::string& file_id);
388
389  bool CanClearDirty(const FileTracker& tracker);
390
391  base::FilePath database_path_;
392  leveldb::Env* env_override_;
393  scoped_ptr<LevelDBWrapper> db_;
394
395  bool enable_on_disk_index_;
396
397  int64 largest_known_change_id_;
398
399  scoped_ptr<MetadataDatabaseIndexInterface> index_;
400
401  base::WeakPtrFactory<MetadataDatabase> weak_ptr_factory_;
402
403  DISALLOW_COPY_AND_ASSIGN(MetadataDatabase);
404};
405
406}  // namespace drive_backend
407}  // namespace sync_file_system
408
409#endif  // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_
410