text: // Copyright 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h"
  
  #include "base/files/file.h"
  #include "base/files/file_path.h"
  #include "base/files/file_util.h"
  #include "base/logging.h"
  #include "base/pickle.h"
  #include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
  #include "webkit/browser/fileapi/sandbox_origin_database.h"
  
  namespace storage {
  
  namespace {
  
  const base::FilePath::CharType kPrimaryDirectory[] =
      FILE_PATH_LITERAL("primary");
  const base::FilePath::CharType kPrimaryOriginFile[] =
      FILE_PATH_LITERAL("primary.origin");
  
  bool WritePrimaryOriginFile(const base::FilePath& path,
                              const std::string& origin) {
    base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
    if (!file.IsValid())
      return false;
    if (!file.created())
      file.SetLength(0);
    Pickle pickle;
    pickle.WriteString(origin);
    file.Write(0, static_cast<const char*>(pickle.data()), pickle.size());
    file.Flush();
    return true;
  }
  
  bool ReadPrimaryOriginFile(const base::FilePath& path,
                             std::string* origin) {
    std::string buffer;
    if (!base::ReadFileToString(path, &buffer))
      return false;
    Pickle pickle(buffer.data(), buffer.size());
    PickleIterator iter(pickle);
    return pickle.ReadString(&iter, origin) && !origin->empty();
  }
  
  }  // namespace
  
  SandboxPrioritizedOriginDatabase::SandboxPrioritizedOriginDatabase(
      const base::FilePath& file_system_directory,
      leveldb::Env* env_override)
      : file_system_directory_(file_system_directory),
        env_override_(env_override),
        primary_origin_file_(
            file_system_directory_.Append(kPrimaryOriginFile)) {
  }
  
  SandboxPrioritizedOriginDatabase::~SandboxPrioritizedOriginDatabase() {
  }
  
  bool SandboxPrioritizedOriginDatabase::InitializePrimaryOrigin(
      const std::string& origin) {
    if (!primary_origin_database_) {
      if (!MaybeLoadPrimaryOrigin() && ResetPrimaryOrigin(origin)) {
        MaybeMigrateDatabase(origin);
        primary_origin_database_.reset(
            new SandboxIsolatedOriginDatabase(
                origin,
                file_system_directory_,
                base::FilePath(kPrimaryDirectory)));
        return true;
      }
    }
  
    if (primary_origin_database_)
      return primary_origin_database_->HasOriginPath(origin);
  
    return false;
  }
  
  std::string SandboxPrioritizedOriginDatabase::GetPrimaryOrigin() {
    MaybeLoadPrimaryOrigin();
    if (primary_origin_database_)
      return primary_origin_database_->origin();
    return std::string();
  }
  
  bool SandboxPrioritizedOriginDatabase::HasOriginPath(
      const std::string& origin) {
    MaybeInitializeDatabases(false);
    if (primary_origin_database_ &&
        primary_origin_database_->HasOriginPath(origin))
      return true;
    if (origin_database_)
      return origin_database_->HasOriginPath(origin);
    return false;
  }
  
  bool SandboxPrioritizedOriginDatabase::GetPathForOrigin(
      const std::string& origin, base::FilePath* directory) {
    MaybeInitializeDatabases(true);
    if (primary_origin_database_ &&
        primary_origin_database_->GetPathForOrigin(origin, directory))
      return true;
    DCHECK(origin_database_);
    return origin_database_->GetPathForOrigin(origin, directory);
  }
  
  bool SandboxPrioritizedOriginDatabase::RemovePathForOrigin(
      const std::string& origin) {
    MaybeInitializeDatabases(false);
    if (primary_origin_database_ &&
        primary_origin_database_->HasOriginPath(origin)) {
      primary_origin_database_.reset();
      base::DeleteFile(file_system_directory_.Append(kPrimaryOriginFile),
                       true /* recursive */);
      return true;
    }
    if (origin_database_)
      return origin_database_->RemovePathForOrigin(origin);
    return true;
  }
  
  bool SandboxPrioritizedOriginDatabase::ListAllOrigins(
      std::vector<OriginRecord>* origins) {
    // SandboxOriginDatabase may clear the |origins|, so call this before
    // primary_origin_database_.
    MaybeInitializeDatabases(false);
    if (origin_database_ && !origin_database_->ListAllOrigins(origins))
      return false;
    if (primary_origin_database_)
      return primary_origin_database_->ListAllOrigins(origins);
    return true;
  }
  
  void SandboxPrioritizedOriginDatabase::DropDatabase() {
    primary_origin_database_.reset();
    origin_database_.reset();
  }
  
  bool SandboxPrioritizedOriginDatabase::MaybeLoadPrimaryOrigin() {
    if (primary_origin_database_)
      return true;
    std::string saved_origin;
    if (!ReadPrimaryOriginFile(primary_origin_file_, &saved_origin))
      return false;
    primary_origin_database_.reset(
        new SandboxIsolatedOriginDatabase(
            saved_origin,
            file_system_directory_,
            base::FilePath(kPrimaryDirectory)));
    return true;
  }
  
  bool SandboxPrioritizedOriginDatabase::ResetPrimaryOrigin(
      const std::string& origin) {
    DCHECK(!primary_origin_database_);
    if (!WritePrimaryOriginFile(primary_origin_file_, origin))
      return false;
    // We reset the primary origin directory too.
    // (This means the origin file corruption causes data loss
    // We could keep the directory there as the same origin will likely
    // become the primary origin, but let's play conservatively.)
    base::DeleteFile(file_system_directory_.Append(kPrimaryDirectory),
                     true /* recursive */);
    return true;
  }
  
  void SandboxPrioritizedOriginDatabase::MaybeMigrateDatabase(
      const std::string& origin) {
    MaybeInitializeNonPrimaryDatabase(false);
    if (!origin_database_)
      return;
    if (origin_database_->HasOriginPath(origin)) {
      base::FilePath directory_name;
      if (origin_database_->GetPathForOrigin(origin, &directory_name) &&
          directory_name != base::FilePath(kPrimaryOriginFile)) {
        base::FilePath from_path = file_system_directory_.Append(directory_name);
        base::FilePath to_path = file_system_directory_.Append(kPrimaryDirectory);
  
        if (base::PathExists(to_path))
          base::DeleteFile(to_path, true /* recursive */);
        base::Move(from_path, to_path);
      }
  
      origin_database_->RemovePathForOrigin(origin);
    }
  
    std::vector<OriginRecord> origins;
    origin_database_->ListAllOrigins(&origins);
    if (origins.empty()) {
      origin_database_->RemoveDatabase();
      origin_database_.reset();
    }
  }
  
  void SandboxPrioritizedOriginDatabase::MaybeInitializeDatabases(
      bool create) {
    MaybeLoadPrimaryOrigin();
    MaybeInitializeNonPrimaryDatabase(create);
  }
  
  void SandboxPrioritizedOriginDatabase::MaybeInitializeNonPrimaryDatabase(
      bool create) {
    if (origin_database_)
      return;
  
    origin_database_.reset(new SandboxOriginDatabase(file_system_directory_,
                                                     env_override_));
    if (!create && !base::DirectoryExists(origin_database_->GetDatabasePath())) {
      origin_database_.reset();
      return;
    }
  }
  
  SandboxOriginDatabase*
  SandboxPrioritizedOriginDatabase::GetSandboxOriginDatabase() {
    MaybeInitializeNonPrimaryDatabase(true);
    return origin_database_.get();
  }
  
  }  // namespace storage
  