metadata_db_migration_util.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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#include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
6
7#include "base/files/file_path.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/strings/string_util.h"
10#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
11#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
12#include "url/gurl.h"
13#include "webkit/common/fileapi/file_system_types.h"
14#include "webkit/common/fileapi/file_system_util.h"
15
16namespace sync_file_system {
17namespace drive_backend {
18
19namespace {
20
21const base::FilePath::CharType kV0FormatPathPrefix[] =
22    FILE_PATH_LITERAL("drive/");
23const char kWapiFileIdPrefix[] = "file:";
24const char kWapiFolderIdPrefix[] = "folder:";
25
26}  // namespace
27
28bool ParseV0FormatFileSystemURL(const GURL& url,
29                                GURL* origin,
30                                base::FilePath* path) {
31  fileapi::FileSystemType mount_type;
32  base::FilePath virtual_path;
33
34  if (!fileapi::ParseFileSystemSchemeURL(
35          url, origin, &mount_type, &virtual_path) ||
36      mount_type != fileapi::kFileSystemTypeExternal) {
37    NOTREACHED() << "Failed to parse filesystem scheme URL " << url.spec();
38    return false;
39  }
40
41  base::FilePath::StringType prefix =
42      base::FilePath(kV0FormatPathPrefix).NormalizePathSeparators().value();
43  if (virtual_path.value().substr(0, prefix.size()) != prefix)
44    return false;
45
46  *path = base::FilePath(virtual_path.value().substr(prefix.size()));
47  return true;
48}
49
50std::string AddWapiFilePrefix(const std::string& resource_id) {
51  DCHECK(!StartsWithASCII(resource_id, kWapiFileIdPrefix, true));
52  DCHECK(!StartsWithASCII(resource_id, kWapiFolderIdPrefix, true));
53
54  if (resource_id.empty() ||
55      StartsWithASCII(resource_id, kWapiFileIdPrefix, true) ||
56      StartsWithASCII(resource_id, kWapiFolderIdPrefix, true))
57    return resource_id;
58  return kWapiFileIdPrefix + resource_id;
59}
60
61std::string AddWapiFolderPrefix(const std::string& resource_id) {
62  DCHECK(!StartsWithASCII(resource_id, kWapiFileIdPrefix, true));
63  DCHECK(!StartsWithASCII(resource_id, kWapiFolderIdPrefix, true));
64
65  if (resource_id.empty() ||
66      StartsWithASCII(resource_id, kWapiFileIdPrefix, true) ||
67      StartsWithASCII(resource_id, kWapiFolderIdPrefix, true))
68    return resource_id;
69  return kWapiFolderIdPrefix + resource_id;
70}
71
72std::string AddWapiIdPrefix(const std::string& resource_id,
73                            DriveMetadata_ResourceType type) {
74  switch (type) {
75    case DriveMetadata_ResourceType_RESOURCE_TYPE_FILE:
76      return AddWapiFilePrefix(resource_id);
77    case DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER:
78      return AddWapiFolderPrefix(resource_id);
79  }
80  NOTREACHED();
81  return resource_id;
82}
83
84std::string RemoveWapiIdPrefix(const std::string& resource_id) {
85  std::string value;
86  if (RemovePrefix(resource_id, kWapiFileIdPrefix, &value))
87    return value;
88  if (RemovePrefix(resource_id, kWapiFolderIdPrefix, &value))
89    return value;
90  return resource_id;
91}
92
93SyncStatusCode MigrateDatabaseFromV0ToV1(leveldb::DB* db) {
94  // Version 0 database format:
95  //   key: "CHANGE_STAMP"
96  //   value: <Largest Changestamp>
97  //
98  //   key: "SYNC_ROOT_DIR"
99  //   value: <Resource ID of the sync root directory>
100  //
101  //   key: "METADATA: " +
102  //        <FileSystemURL serialized by SerializeSyncableFileSystemURL>
103  //   value: <Serialized DriveMetadata>
104  //
105  //   key: "BSYNC_ORIGIN: " + <URL string of a batch sync origin>
106  //   value: <Resource ID of the drive directory for the origin>
107  //
108  //   key: "ISYNC_ORIGIN: " + <URL string of a incremental sync origin>
109  //   value: <Resource ID of the drive directory for the origin>
110  //
111  // Version 1 database format (changed keys/fields are marked with '*'):
112  // * key: "VERSION" (new)
113  // * value: 1
114  //
115  //   key: "CHANGE_STAMP"
116  //   value: <Largest Changestamp>
117  //
118  //   key: "SYNC_ROOT_DIR"
119  //   value: <Resource ID of the sync root directory>
120  //
121  // * key: "METADATA: " + <Origin and URL> (changed)
122  // * value: <Serialized DriveMetadata>
123  //
124  //   key: "BSYNC_ORIGIN: " + <URL string of a batch sync origin>
125  //   value: <Resource ID of the drive directory for the origin>
126  //
127  //   key: "ISYNC_ORIGIN: " + <URL string of a incremental sync origin>
128  //   value: <Resource ID of the drive directory for the origin>
129  //
130  //   key: "DISABLED_ORIGIN: " + <URL string of a disabled origin>
131  //   value: <Resource ID of the drive directory for the origin>
132
133  const char kDatabaseVersionKey[] = "VERSION";
134  const char kDriveMetadataKeyPrefix[] = "METADATA: ";
135  const char kMetadataKeySeparator = ' ';
136
137  leveldb::WriteBatch write_batch;
138  write_batch.Put(kDatabaseVersionKey, "1");
139
140  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
141  for (itr->Seek(kDriveMetadataKeyPrefix); itr->Valid(); itr->Next()) {
142    std::string key = itr->key().ToString();
143    if (!StartsWithASCII(key, kDriveMetadataKeyPrefix, true))
144      break;
145    std::string serialized_url;
146    RemovePrefix(key, kDriveMetadataKeyPrefix, &serialized_url);
147
148    GURL origin;
149    base::FilePath path;
150    bool success = ParseV0FormatFileSystemURL(
151        GURL(serialized_url), &origin, &path);
152    DCHECK(success) << serialized_url;
153    std::string new_key = kDriveMetadataKeyPrefix + origin.spec() +
154        kMetadataKeySeparator + path.AsUTF8Unsafe();
155
156    write_batch.Put(new_key, itr->value());
157    write_batch.Delete(key);
158  }
159
160  return LevelDBStatusToSyncStatusCode(
161      db->Write(leveldb::WriteOptions(), &write_batch));
162}
163
164SyncStatusCode MigrateDatabaseFromV1ToV2(leveldb::DB* db) {
165  // Strips prefix of WAPI resource ID, and discards batch sync origins.
166  // (i.e. "file:xxxx" => "xxxx", "folder:yyyy" => "yyyy")
167  //
168  // Version 2 database format (changed keys/fields are marked with '*'):
169  //   key: "VERSION"
170  // * value: 2
171  //
172  //   key: "CHANGE_STAMP"
173  //   value: <Largest Changestamp>
174  //
175  //   key: "SYNC_ROOT_DIR"
176  // * value: <Resource ID of the sync root directory> (striped)
177  //
178  //   key: "METADATA: " + <Origin and URL>
179  // * value: <Serialized DriveMetadata> (stripped)
180  //
181  // * key: "BSYNC_ORIGIN: " + <URL string of a batch sync origin> (deleted)
182  // * value: <Resource ID of the drive directory for the origin> (deleted)
183  //
184  //   key: "ISYNC_ORIGIN: " + <URL string of a incremental sync origin>
185  // * value: <Resource ID of the drive directory for the origin> (stripped)
186  //
187  //   key: "DISABLED_ORIGIN: " + <URL string of a disabled origin>
188  // * value: <Resource ID of the drive directory for the origin> (stripped)
189
190  const char kDatabaseVersionKey[] = "VERSION";
191  const char kSyncRootDirectoryKey[] = "SYNC_ROOT_DIR";
192  const char kDriveMetadataKeyPrefix[] = "METADATA: ";
193  const char kDriveBatchSyncOriginKeyPrefix[] = "BSYNC_ORIGIN: ";
194  const char kDriveIncrementalSyncOriginKeyPrefix[] = "ISYNC_ORIGIN: ";
195  const char kDriveDisabledOriginKeyPrefix[] = "DISABLED_ORIGIN: ";
196
197  leveldb::WriteBatch write_batch;
198  write_batch.Put(kDatabaseVersionKey, "2");
199
200  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
201  for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
202    std::string key = itr->key().ToString();
203
204    // Strip resource id for the sync root directory.
205    if (StartsWithASCII(key, kSyncRootDirectoryKey, true)) {
206      write_batch.Put(key, RemoveWapiIdPrefix(itr->value().ToString()));
207      continue;
208    }
209
210    // Strip resource ids in the drive metadata.
211    if (StartsWithASCII(key, kDriveMetadataKeyPrefix, true)) {
212      DriveMetadata metadata;
213      bool success = metadata.ParseFromString(itr->value().ToString());
214      DCHECK(success);
215
216      metadata.set_resource_id(RemoveWapiIdPrefix(metadata.resource_id()));
217      std::string metadata_string;
218      metadata.SerializeToString(&metadata_string);
219
220      write_batch.Put(key, metadata_string);
221      continue;
222    }
223
224    // Deprecate legacy batch sync origin entries that are no longer needed.
225    if (StartsWithASCII(key, kDriveBatchSyncOriginKeyPrefix, true)) {
226      write_batch.Delete(key);
227      continue;
228    }
229
230    // Strip resource ids of the incremental sync origins.
231    if (StartsWithASCII(key, kDriveIncrementalSyncOriginKeyPrefix, true)) {
232      write_batch.Put(key, RemoveWapiIdPrefix(itr->value().ToString()));
233      continue;
234    }
235
236    // Strip resource ids of the disabled sync origins.
237    if (StartsWithASCII(key, kDriveDisabledOriginKeyPrefix, true)) {
238      write_batch.Put(key, RemoveWapiIdPrefix(itr->value().ToString()));
239      continue;
240    }
241  }
242
243  return LevelDBStatusToSyncStatusCode(
244      db->Write(leveldb::WriteOptions(), &write_batch));
245}
246
247SyncStatusCode MigrateDatabaseFromV4ToV3(leveldb::DB* db) {
248  // Rollback from version 4 to version 3.
249  // Please see metadata_database_index.cc for version 3 format, and
250  // metadata_database_index_on_disk.cc for version 4 format.
251
252  const char kDatabaseVersionKey[] = "VERSION";
253  const char kServiceMetadataKey[] = "SERVICE";
254  const char kFileMetadataKeyPrefix[] = "FILE: ";
255  const char kFileTrackerKeyPrefix[] = "TRACKER: ";
256
257  // Key prefixes used in version 4.
258  const char kAppRootIDByAppIDKeyPrefix[] = "APP_ROOT: ";
259  const char kActiveTrackerIDByFileIDKeyPrefix[] = "ACTIVE_FILE: ";
260  const char kTrackerIDByFileIDKeyPrefix[] = "TRACKER_FILE: ";
261  const char kMultiTrackerByFileIDKeyPrefix[] = "MULTI_FILE: ";
262  const char kActiveTrackerIDByParentAndTitleKeyPrefix[] = "ACTIVE_PATH: ";
263  const char kTrackerIDByParentAndTitleKeyPrefix[] = "TRACKER_PATH: ";
264  const char kMultiBackingParentAndTitleKeyPrefix[] = "MULTI_PATH: ";
265  const char kDirtyIDKeyPrefix[] = "DIRTY: ";
266  const char kDemotedDirtyIDKeyPrefix[] = "DEMOTED_DIRTY: ";
267
268  leveldb::WriteBatch write_batch;
269  write_batch.Put(kDatabaseVersionKey, "3");
270
271  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
272  for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
273    std::string key = itr->key().ToString();
274
275    // Do nothing for valid entries in both versions.
276    if (StartsWithASCII(key, kServiceMetadataKey, true) ||
277        StartsWithASCII(key, kFileMetadataKeyPrefix, true) ||
278        StartsWithASCII(key, kFileTrackerKeyPrefix, true)) {
279      continue;
280    }
281
282    // Drop entries used in version 4 only.
283    if (StartsWithASCII(key, kAppRootIDByAppIDKeyPrefix, true) ||
284        StartsWithASCII(key, kActiveTrackerIDByFileIDKeyPrefix, true) ||
285        StartsWithASCII(key, kTrackerIDByFileIDKeyPrefix, true) ||
286        StartsWithASCII(key, kMultiTrackerByFileIDKeyPrefix, true) ||
287        StartsWithASCII(key, kActiveTrackerIDByParentAndTitleKeyPrefix, true) ||
288        StartsWithASCII(key, kTrackerIDByParentAndTitleKeyPrefix, true) ||
289        StartsWithASCII(key, kMultiBackingParentAndTitleKeyPrefix, true) ||
290        StartsWithASCII(key, kDirtyIDKeyPrefix, true) ||
291        StartsWithASCII(key, kDemotedDirtyIDKeyPrefix, true)) {
292      write_batch.Delete(key);
293      continue;
294    }
295
296    DVLOG(3) << "Unknown key: " << key << " was found.";
297  }
298
299  return LevelDBStatusToSyncStatusCode(
300      db->Write(leveldb::WriteOptions(), &write_batch));
301}
302
303}  // namespace drive_backend
304}  // namespace sync_file_system
305