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 "third_party/leveldatabase/src/include/leveldb/write_batch.h" 11 #include "url/gurl.h" 12 #include "webkit/browser/fileapi/file_system_url.h" 13 #include "webkit/common/fileapi/file_system_types.h" 14 15 namespace sync_file_system { 16 namespace drive_backend { 17 18 namespace { 19 20 const base::FilePath::CharType kV0FormatPathPrefix[] = 21 FILE_PATH_LITERAL("drive/"); 22 const char kWapiFileIdPrefix[] = "file:"; 23 const char kWapiFolderIdPrefix[] = "folder:"; 24 25 std::string RemovePrefix(const std::string& str, const std::string& prefix) { 26 if (StartsWithASCII(str, prefix, true)) 27 return std::string(str.begin() + prefix.size(), str.end()); 28 return str; 29 } 30 31 } // namespace 32 33 bool ParseV0FormatFileSystemURL(const GURL& url, 34 GURL* origin, 35 base::FilePath* path) { 36 fileapi::FileSystemType mount_type; 37 base::FilePath virtual_path; 38 39 if (!fileapi::FileSystemURL::ParseFileSystemSchemeURL( 40 url, origin, &mount_type, &virtual_path) || 41 mount_type != fileapi::kFileSystemTypeExternal) { 42 NOTREACHED() << "Failed to parse filesystem scheme URL " << url.spec(); 43 return false; 44 } 45 46 base::FilePath::StringType prefix = 47 base::FilePath(kV0FormatPathPrefix).NormalizePathSeparators().value(); 48 if (virtual_path.value().substr(0, prefix.size()) != prefix) 49 return false; 50 51 *path = base::FilePath(virtual_path.value().substr(prefix.size())); 52 return true; 53 } 54 55 std::string AddWapiFilePrefix(const std::string& resource_id) { 56 DCHECK(!StartsWithASCII(resource_id, kWapiFileIdPrefix, true)); 57 DCHECK(!StartsWithASCII(resource_id, kWapiFolderIdPrefix, true)); 58 59 if (resource_id.empty() || 60 StartsWithASCII(resource_id, kWapiFileIdPrefix, true) || 61 StartsWithASCII(resource_id, kWapiFolderIdPrefix, true)) 62 return resource_id; 63 return kWapiFileIdPrefix + resource_id; 64 } 65 66 std::string AddWapiFolderPrefix(const std::string& resource_id) { 67 DCHECK(!StartsWithASCII(resource_id, kWapiFileIdPrefix, true)); 68 DCHECK(!StartsWithASCII(resource_id, kWapiFolderIdPrefix, true)); 69 70 if (resource_id.empty() || 71 StartsWithASCII(resource_id, kWapiFileIdPrefix, true) || 72 StartsWithASCII(resource_id, kWapiFolderIdPrefix, true)) 73 return resource_id; 74 return kWapiFolderIdPrefix + resource_id; 75 } 76 77 std::string AddWapiIdPrefix(const std::string& resource_id, 78 DriveMetadata_ResourceType type) { 79 switch (type) { 80 case DriveMetadata_ResourceType_RESOURCE_TYPE_FILE: 81 return AddWapiFilePrefix(resource_id); 82 case DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER: 83 return AddWapiFolderPrefix(resource_id); 84 } 85 NOTREACHED(); 86 return resource_id; 87 } 88 89 std::string RemoveWapiIdPrefix(const std::string& resource_id) { 90 if (StartsWithASCII(resource_id, kWapiFileIdPrefix, true)) 91 return RemovePrefix(resource_id, kWapiFileIdPrefix); 92 if (StartsWithASCII(resource_id, kWapiFolderIdPrefix, true)) 93 return RemovePrefix(resource_id, kWapiFolderIdPrefix); 94 return resource_id; 95 } 96 97 SyncStatusCode MigrateDatabaseFromV0ToV1(leveldb::DB* db) { 98 // Version 0 database format: 99 // key: "CHANGE_STAMP" 100 // value: <Largest Changestamp> 101 // 102 // key: "SYNC_ROOT_DIR" 103 // value: <Resource ID of the sync root directory> 104 // 105 // key: "METADATA: " + 106 // <FileSystemURL serialized by SerializeSyncableFileSystemURL> 107 // value: <Serialized DriveMetadata> 108 // 109 // key: "BSYNC_ORIGIN: " + <URL string of a batch sync origin> 110 // value: <Resource ID of the drive directory for the origin> 111 // 112 // key: "ISYNC_ORIGIN: " + <URL string of a incremental sync origin> 113 // value: <Resource ID of the drive directory for the origin> 114 // 115 // Version 1 database format (changed keys/fields are marked with '*'): 116 // * key: "VERSION" (new) 117 // * value: 1 118 // 119 // key: "CHANGE_STAMP" 120 // value: <Largest Changestamp> 121 // 122 // key: "SYNC_ROOT_DIR" 123 // value: <Resource ID of the sync root directory> 124 // 125 // * key: "METADATA: " + <Origin and URL> (changed) 126 // * value: <Serialized DriveMetadata> 127 // 128 // key: "BSYNC_ORIGIN: " + <URL string of a batch sync origin> 129 // value: <Resource ID of the drive directory for the origin> 130 // 131 // key: "ISYNC_ORIGIN: " + <URL string of a incremental sync origin> 132 // value: <Resource ID of the drive directory for the origin> 133 // 134 // key: "DISABLED_ORIGIN: " + <URL string of a disabled origin> 135 // value: <Resource ID of the drive directory for the origin> 136 137 const char kDatabaseVersionKey[] = "VERSION"; 138 const char kDriveMetadataKeyPrefix[] = "METADATA: "; 139 const char kMetadataKeySeparator = ' '; 140 141 leveldb::WriteBatch write_batch; 142 write_batch.Put(kDatabaseVersionKey, "1"); 143 144 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 145 for (itr->Seek(kDriveMetadataKeyPrefix); itr->Valid(); itr->Next()) { 146 std::string key = itr->key().ToString(); 147 if (!StartsWithASCII(key, kDriveMetadataKeyPrefix, true)) 148 break; 149 std::string serialized_url(RemovePrefix(key, kDriveMetadataKeyPrefix)); 150 151 GURL origin; 152 base::FilePath path; 153 bool success = ParseV0FormatFileSystemURL( 154 GURL(serialized_url), &origin, &path); 155 DCHECK(success) << serialized_url; 156 std::string new_key = kDriveMetadataKeyPrefix + origin.spec() + 157 kMetadataKeySeparator + path.AsUTF8Unsafe(); 158 159 write_batch.Put(new_key, itr->value()); 160 write_batch.Delete(key); 161 } 162 163 return LevelDBStatusToSyncStatusCode( 164 db->Write(leveldb::WriteOptions(), &write_batch)); 165 } 166 167 SyncStatusCode MigrateDatabaseFromV1ToV2(leveldb::DB* db) { 168 // Strips prefix of WAPI resource ID, and discards batch sync origins. 169 // (i.e. "file:xxxx" => "xxxx", "folder:yyyy" => "yyyy") 170 // 171 // Version 2 database format (changed keys/fields are marked with '*'): 172 // key: "VERSION" 173 // * value: 2 174 // 175 // key: "CHANGE_STAMP" 176 // value: <Largest Changestamp> 177 // 178 // key: "SYNC_ROOT_DIR" 179 // * value: <Resource ID of the sync root directory> (striped) 180 // 181 // key: "METADATA: " + <Origin and URL> 182 // * value: <Serialized DriveMetadata> (stripped) 183 // 184 // * key: "BSYNC_ORIGIN: " + <URL string of a batch sync origin> (deleted) 185 // * value: <Resource ID of the drive directory for the origin> (deleted) 186 // 187 // key: "ISYNC_ORIGIN: " + <URL string of a incremental sync origin> 188 // * value: <Resource ID of the drive directory for the origin> (stripped) 189 // 190 // key: "DISABLED_ORIGIN: " + <URL string of a disabled origin> 191 // * value: <Resource ID of the drive directory for the origin> (stripped) 192 193 const char kDatabaseVersionKey[] = "VERSION"; 194 const char kSyncRootDirectoryKey[] = "SYNC_ROOT_DIR"; 195 const char kDriveMetadataKeyPrefix[] = "METADATA: "; 196 const char kDriveBatchSyncOriginKeyPrefix[] = "BSYNC_ORIGIN: "; 197 const char kDriveIncrementalSyncOriginKeyPrefix[] = "ISYNC_ORIGIN: "; 198 const char kDriveDisabledOriginKeyPrefix[] = "DISABLED_ORIGIN: "; 199 200 leveldb::WriteBatch write_batch; 201 write_batch.Put(kDatabaseVersionKey, "2"); 202 203 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 204 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) { 205 std::string key = itr->key().ToString(); 206 207 // Strip resource id for the sync root directory. 208 if (StartsWithASCII(key, kSyncRootDirectoryKey, true)) { 209 write_batch.Put(key, RemoveWapiIdPrefix(itr->value().ToString())); 210 continue; 211 } 212 213 // Strip resource ids in the drive metadata. 214 if (StartsWithASCII(key, kDriveMetadataKeyPrefix, true)) { 215 DriveMetadata metadata; 216 bool success = metadata.ParseFromString(itr->value().ToString()); 217 DCHECK(success); 218 219 metadata.set_resource_id(RemoveWapiIdPrefix(metadata.resource_id())); 220 std::string metadata_string; 221 metadata.SerializeToString(&metadata_string); 222 223 write_batch.Put(key, metadata_string); 224 continue; 225 } 226 227 // Deprecate legacy batch sync origin entries that are no longer needed. 228 if (StartsWithASCII(key, kDriveBatchSyncOriginKeyPrefix, true)) { 229 write_batch.Delete(key); 230 continue; 231 } 232 233 // Strip resource ids of the incremental sync origins. 234 if (StartsWithASCII(key, kDriveIncrementalSyncOriginKeyPrefix, true)) { 235 write_batch.Put(key, RemoveWapiIdPrefix(itr->value().ToString())); 236 continue; 237 } 238 239 // Strip resource ids of the disabled sync origins. 240 if (StartsWithASCII(key, kDriveDisabledOriginKeyPrefix, true)) { 241 write_batch.Put(key, RemoveWapiIdPrefix(itr->value().ToString())); 242 continue; 243 } 244 } 245 246 return LevelDBStatusToSyncStatusCode( 247 db->Write(leveldb::WriteOptions(), &write_batch)); 248 } 249 250 } // namespace drive_backend 251 } // namespace sync_file_system 252