Home | History | Annotate | Download | only in drive_backend
      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