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 "storage/browser/fileapi/sandbox_prioritized_origin_database.h"
6
7#include "base/files/file.h"
8#include "base/files/file_path.h"
9#include "base/files/file_util.h"
10#include "base/logging.h"
11#include "base/pickle.h"
12#include "storage/browser/fileapi/sandbox_isolated_origin_database.h"
13#include "storage/browser/fileapi/sandbox_origin_database.h"
14
15namespace storage {
16
17namespace {
18
19const base::FilePath::CharType kPrimaryDirectory[] =
20    FILE_PATH_LITERAL("primary");
21const base::FilePath::CharType kPrimaryOriginFile[] =
22    FILE_PATH_LITERAL("primary.origin");
23
24bool WritePrimaryOriginFile(const base::FilePath& path,
25                            const std::string& origin) {
26  base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
27  if (!file.IsValid())
28    return false;
29  if (!file.created())
30    file.SetLength(0);
31  Pickle pickle;
32  pickle.WriteString(origin);
33  file.Write(0, static_cast<const char*>(pickle.data()), pickle.size());
34  file.Flush();
35  return true;
36}
37
38bool ReadPrimaryOriginFile(const base::FilePath& path,
39                           std::string* origin) {
40  std::string buffer;
41  if (!base::ReadFileToString(path, &buffer))
42    return false;
43  Pickle pickle(buffer.data(), buffer.size());
44  PickleIterator iter(pickle);
45  return pickle.ReadString(&iter, origin) && !origin->empty();
46}
47
48}  // namespace
49
50SandboxPrioritizedOriginDatabase::SandboxPrioritizedOriginDatabase(
51    const base::FilePath& file_system_directory,
52    leveldb::Env* env_override)
53    : file_system_directory_(file_system_directory),
54      env_override_(env_override),
55      primary_origin_file_(
56          file_system_directory_.Append(kPrimaryOriginFile)) {
57}
58
59SandboxPrioritizedOriginDatabase::~SandboxPrioritizedOriginDatabase() {
60}
61
62bool SandboxPrioritizedOriginDatabase::InitializePrimaryOrigin(
63    const std::string& origin) {
64  if (!primary_origin_database_) {
65    if (!MaybeLoadPrimaryOrigin() && ResetPrimaryOrigin(origin)) {
66      MaybeMigrateDatabase(origin);
67      primary_origin_database_.reset(
68          new SandboxIsolatedOriginDatabase(
69              origin,
70              file_system_directory_,
71              base::FilePath(kPrimaryDirectory)));
72      return true;
73    }
74  }
75
76  if (primary_origin_database_)
77    return primary_origin_database_->HasOriginPath(origin);
78
79  return false;
80}
81
82std::string SandboxPrioritizedOriginDatabase::GetPrimaryOrigin() {
83  MaybeLoadPrimaryOrigin();
84  if (primary_origin_database_)
85    return primary_origin_database_->origin();
86  return std::string();
87}
88
89bool SandboxPrioritizedOriginDatabase::HasOriginPath(
90    const std::string& origin) {
91  MaybeInitializeDatabases(false);
92  if (primary_origin_database_ &&
93      primary_origin_database_->HasOriginPath(origin))
94    return true;
95  if (origin_database_)
96    return origin_database_->HasOriginPath(origin);
97  return false;
98}
99
100bool SandboxPrioritizedOriginDatabase::GetPathForOrigin(
101    const std::string& origin, base::FilePath* directory) {
102  MaybeInitializeDatabases(true);
103  if (primary_origin_database_ &&
104      primary_origin_database_->GetPathForOrigin(origin, directory))
105    return true;
106  DCHECK(origin_database_);
107  return origin_database_->GetPathForOrigin(origin, directory);
108}
109
110bool SandboxPrioritizedOriginDatabase::RemovePathForOrigin(
111    const std::string& origin) {
112  MaybeInitializeDatabases(false);
113  if (primary_origin_database_ &&
114      primary_origin_database_->HasOriginPath(origin)) {
115    primary_origin_database_.reset();
116    base::DeleteFile(file_system_directory_.Append(kPrimaryOriginFile),
117                     true /* recursive */);
118    return true;
119  }
120  if (origin_database_)
121    return origin_database_->RemovePathForOrigin(origin);
122  return true;
123}
124
125bool SandboxPrioritizedOriginDatabase::ListAllOrigins(
126    std::vector<OriginRecord>* origins) {
127  // SandboxOriginDatabase may clear the |origins|, so call this before
128  // primary_origin_database_.
129  MaybeInitializeDatabases(false);
130  if (origin_database_ && !origin_database_->ListAllOrigins(origins))
131    return false;
132  if (primary_origin_database_)
133    return primary_origin_database_->ListAllOrigins(origins);
134  return true;
135}
136
137void SandboxPrioritizedOriginDatabase::DropDatabase() {
138  primary_origin_database_.reset();
139  origin_database_.reset();
140}
141
142bool SandboxPrioritizedOriginDatabase::MaybeLoadPrimaryOrigin() {
143  if (primary_origin_database_)
144    return true;
145  std::string saved_origin;
146  if (!ReadPrimaryOriginFile(primary_origin_file_, &saved_origin))
147    return false;
148  primary_origin_database_.reset(
149      new SandboxIsolatedOriginDatabase(
150          saved_origin,
151          file_system_directory_,
152          base::FilePath(kPrimaryDirectory)));
153  return true;
154}
155
156bool SandboxPrioritizedOriginDatabase::ResetPrimaryOrigin(
157    const std::string& origin) {
158  DCHECK(!primary_origin_database_);
159  if (!WritePrimaryOriginFile(primary_origin_file_, origin))
160    return false;
161  // We reset the primary origin directory too.
162  // (This means the origin file corruption causes data loss
163  // We could keep the directory there as the same origin will likely
164  // become the primary origin, but let's play conservatively.)
165  base::DeleteFile(file_system_directory_.Append(kPrimaryDirectory),
166                   true /* recursive */);
167  return true;
168}
169
170void SandboxPrioritizedOriginDatabase::MaybeMigrateDatabase(
171    const std::string& origin) {
172  MaybeInitializeNonPrimaryDatabase(false);
173  if (!origin_database_)
174    return;
175  if (origin_database_->HasOriginPath(origin)) {
176    base::FilePath directory_name;
177    if (origin_database_->GetPathForOrigin(origin, &directory_name) &&
178        directory_name != base::FilePath(kPrimaryOriginFile)) {
179      base::FilePath from_path = file_system_directory_.Append(directory_name);
180      base::FilePath to_path = file_system_directory_.Append(kPrimaryDirectory);
181
182      if (base::PathExists(to_path))
183        base::DeleteFile(to_path, true /* recursive */);
184      base::Move(from_path, to_path);
185    }
186
187    origin_database_->RemovePathForOrigin(origin);
188  }
189
190  std::vector<OriginRecord> origins;
191  origin_database_->ListAllOrigins(&origins);
192  if (origins.empty()) {
193    origin_database_->RemoveDatabase();
194    origin_database_.reset();
195  }
196}
197
198void SandboxPrioritizedOriginDatabase::MaybeInitializeDatabases(
199    bool create) {
200  MaybeLoadPrimaryOrigin();
201  MaybeInitializeNonPrimaryDatabase(create);
202}
203
204void SandboxPrioritizedOriginDatabase::MaybeInitializeNonPrimaryDatabase(
205    bool create) {
206  if (origin_database_)
207    return;
208
209  origin_database_.reset(new SandboxOriginDatabase(file_system_directory_,
210                                                   env_override_));
211  if (!create && !base::DirectoryExists(origin_database_->GetDatabasePath())) {
212    origin_database_.reset();
213    return;
214  }
215}
216
217SandboxOriginDatabase*
218SandboxPrioritizedOriginDatabase::GetSandboxOriginDatabase() {
219  MaybeInitializeNonPrimaryDatabase(true);
220  return origin_database_.get();
221}
222
223}  // namespace storage
224