1 // Copyright (c) 2012 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.
5 #include "content/browser/renderer_host/database_message_filter.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/thread.h"
13 #include "content/common/database_messages.h"
14 #include "content/public/browser/user_metrics.h"
15 #include "content/public/common/result_codes.h"
16 #include "storage/browser/database/database_util.h"
17 #include "storage/browser/database/vfs_backend.h"
18 #include "storage/browser/quota/quota_manager.h"
19 #include "storage/browser/quota/quota_manager_proxy.h"
20 #include "storage/common/database/database_identifier.h"
21 #include "third_party/sqlite/sqlite3.h"
24 #include "base/file_descriptor_posix.h"
27 using storage::QuotaManager;
28 using storage::QuotaStatusCode;
29 using storage::DatabaseTracker;
30 using storage::DatabaseUtil;
31 using storage::VfsBackend;
36 const int kNumDeleteRetries = 2;
37 const int kDelayDeleteRetryMs = 100;
41 DatabaseMessageFilter::DatabaseMessageFilter(
42 storage::DatabaseTracker* db_tracker)
43 : BrowserMessageFilter(DatabaseMsgStart),
44 db_tracker_(db_tracker),
45 observer_added_(false) {
46 DCHECK(db_tracker_.get());
49 void DatabaseMessageFilter::OnChannelClosing() {
50 if (observer_added_) {
51 observer_added_ = false;
52 BrowserThread::PostTask(
53 BrowserThread::FILE, FROM_HERE,
54 base::Bind(&DatabaseMessageFilter::RemoveObserver, this));
58 void DatabaseMessageFilter::AddObserver() {
59 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
60 db_tracker_->AddObserver(this);
63 void DatabaseMessageFilter::RemoveObserver() {
64 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
65 db_tracker_->RemoveObserver(this);
67 // If the renderer process died without closing all databases,
68 // then we need to manually close those connections
69 db_tracker_->CloseDatabases(database_connections_);
70 database_connections_.RemoveAllConnections();
73 void DatabaseMessageFilter::OverrideThreadForMessage(
74 const IPC::Message& message,
75 BrowserThread::ID* thread) {
76 if (message.type() == DatabaseHostMsg_GetSpaceAvailable::ID)
77 *thread = BrowserThread::IO;
78 else if (IPC_MESSAGE_CLASS(message) == DatabaseMsgStart)
79 *thread = BrowserThread::FILE;
81 if (message.type() == DatabaseHostMsg_Opened::ID && !observer_added_) {
82 observer_added_ = true;
83 BrowserThread::PostTask(
84 BrowserThread::FILE, FROM_HERE,
85 base::Bind(&DatabaseMessageFilter::AddObserver, this));
89 bool DatabaseMessageFilter::OnMessageReceived(const IPC::Message& message) {
91 IPC_BEGIN_MESSAGE_MAP(DatabaseMessageFilter, message)
92 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile,
94 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile,
96 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes,
97 OnDatabaseGetFileAttributes)
98 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize,
99 OnDatabaseGetFileSize)
100 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetSpaceAvailable,
101 OnDatabaseGetSpaceAvailable)
102 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened)
103 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified)
104 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed)
105 IPC_MESSAGE_HANDLER(DatabaseHostMsg_HandleSqliteError, OnHandleSqliteError)
106 IPC_MESSAGE_UNHANDLED(handled = false)
107 IPC_END_MESSAGE_MAP()
111 DatabaseMessageFilter::~DatabaseMessageFilter() {
114 void DatabaseMessageFilter::OnDatabaseOpenFile(
115 const base::string16& vfs_file_name,
117 IPC::Message* reply_msg) {
118 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
120 const base::File* tracked_file = NULL;
121 std::string origin_identifier;
122 base::string16 database_name;
124 // When in incognito mode, we want to make sure that all DB files are
125 // removed when the incognito browser context goes away, so we add the
126 // SQLITE_OPEN_DELETEONCLOSE flag when opening all files, and keep
127 // open handles to them in the database tracker to make sure they're
128 // around for as long as needed.
129 if (vfs_file_name.empty()) {
130 file = VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
132 } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
133 &database_name, NULL) &&
134 !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
136 base::FilePath db_file = DatabaseUtil::GetFullFilePathForVfsFile(
137 db_tracker_.get(), vfs_file_name);
138 if (!db_file.empty()) {
139 if (db_tracker_->IsIncognitoProfile()) {
140 tracked_file = db_tracker_->GetIncognitoFile(vfs_file_name);
143 VfsBackend::OpenFile(db_file,
144 desired_flags | SQLITE_OPEN_DELETEONCLOSE);
145 if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE)) {
146 tracked_file = db_tracker_->SaveIncognitoFile(vfs_file_name,
151 file = VfsBackend::OpenFile(db_file, desired_flags);
156 // Then we duplicate the file handle to make it useable in the renderer
157 // process. The original handle is closed, unless we saved it in the
159 IPC::PlatformFileForTransit target_handle =
160 IPC::InvalidPlatformFileForTransit();
161 if (file.IsValid()) {
162 target_handle = IPC::TakeFileHandleForProcess(file.Pass(), PeerHandle());
163 } else if (tracked_file) {
164 DCHECK(tracked_file->IsValid());
166 IPC::GetFileHandleForProcess(tracked_file->GetPlatformFile(),
167 PeerHandle(), false);
170 DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle);
174 void DatabaseMessageFilter::OnDatabaseDeleteFile(
175 const base::string16& vfs_file_name,
176 const bool& sync_dir,
177 IPC::Message* reply_msg) {
178 DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries);
181 void DatabaseMessageFilter::DatabaseDeleteFile(
182 const base::string16& vfs_file_name,
184 IPC::Message* reply_msg,
185 int reschedule_count) {
186 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
188 // Return an error if the file name is invalid or if the file could not
189 // be deleted after kNumDeleteRetries attempts.
190 int error_code = SQLITE_IOERR_DELETE;
191 base::FilePath db_file =
192 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
193 if (!db_file.empty()) {
194 // In order to delete a journal file in incognito mode, we only need to
195 // close the open handle to it that's stored in the database tracker.
196 if (db_tracker_->IsIncognitoProfile()) {
197 const base::string16 wal_suffix(base::ASCIIToUTF16("-wal"));
198 base::string16 sqlite_suffix;
200 // WAL files can be deleted without having previously been opened.
201 if (!db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name) &&
202 DatabaseUtil::CrackVfsFileName(vfs_file_name,
203 NULL, NULL, &sqlite_suffix) &&
204 sqlite_suffix == wal_suffix) {
205 error_code = SQLITE_OK;
207 db_tracker_->CloseIncognitoFileHandle(vfs_file_name);
208 error_code = SQLITE_OK;
211 error_code = VfsBackend::DeleteFile(db_file, sync_dir);
214 if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) {
215 // If the file could not be deleted, try again.
216 BrowserThread::PostDelayedTask(
217 BrowserThread::FILE, FROM_HERE,
218 base::Bind(&DatabaseMessageFilter::DatabaseDeleteFile, this,
219 vfs_file_name, sync_dir, reply_msg, reschedule_count - 1),
220 base::TimeDelta::FromMilliseconds(kDelayDeleteRetryMs));
225 DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code);
229 void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
230 const base::string16& vfs_file_name,
231 IPC::Message* reply_msg) {
232 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
233 int32 attributes = -1;
234 base::FilePath db_file =
235 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
236 if (!db_file.empty())
237 attributes = VfsBackend::GetFileAttributes(db_file);
239 DatabaseHostMsg_GetFileAttributes::WriteReplyParams(
240 reply_msg, attributes);
244 void DatabaseMessageFilter::OnDatabaseGetFileSize(
245 const base::string16& vfs_file_name, IPC::Message* reply_msg) {
246 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
248 base::FilePath db_file =
249 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
250 if (!db_file.empty())
251 size = VfsBackend::GetFileSize(db_file);
253 DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size);
257 void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable(
258 const std::string& origin_identifier, IPC::Message* reply_msg) {
259 DCHECK_CURRENTLY_ON(BrowserThread::IO);
260 DCHECK(db_tracker_->quota_manager_proxy());
262 QuotaManager* quota_manager =
263 db_tracker_->quota_manager_proxy()->quota_manager();
264 if (!quota_manager) {
265 NOTREACHED(); // The system is shutting down, messages are unexpected.
266 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(
267 reply_msg, static_cast<int64>(0));
272 quota_manager->GetUsageAndQuota(
273 storage::GetOriginFromIdentifier(origin_identifier),
274 storage::kStorageTypeTemporary,
276 &DatabaseMessageFilter::OnDatabaseGetUsageAndQuota, this, reply_msg));
279 void DatabaseMessageFilter::OnDatabaseGetUsageAndQuota(
280 IPC::Message* reply_msg,
281 storage::QuotaStatusCode status,
285 if ((status == storage::kQuotaStatusOk) && (usage < quota))
286 available = quota - usage;
287 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(reply_msg, available);
291 void DatabaseMessageFilter::OnDatabaseOpened(
292 const std::string& origin_identifier,
293 const base::string16& database_name,
294 const base::string16& description,
295 int64 estimated_size) {
296 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
298 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) {
299 RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
300 BadMessageReceived();
304 int64 database_size = 0;
305 db_tracker_->DatabaseOpened(origin_identifier, database_name, description,
306 estimated_size, &database_size);
307 database_connections_.AddConnection(origin_identifier, database_name);
308 Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
312 void DatabaseMessageFilter::OnDatabaseModified(
313 const std::string& origin_identifier,
314 const base::string16& database_name) {
315 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
316 if (!database_connections_.IsDatabaseOpened(
317 origin_identifier, database_name)) {
318 RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
319 BadMessageReceived();
323 db_tracker_->DatabaseModified(origin_identifier, database_name);
326 void DatabaseMessageFilter::OnDatabaseClosed(
327 const std::string& origin_identifier,
328 const base::string16& database_name) {
329 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
330 if (!database_connections_.IsDatabaseOpened(
331 origin_identifier, database_name)) {
332 RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
333 BadMessageReceived();
337 database_connections_.RemoveConnection(origin_identifier, database_name);
338 db_tracker_->DatabaseClosed(origin_identifier, database_name);
341 void DatabaseMessageFilter::OnHandleSqliteError(
342 const std::string& origin_identifier,
343 const base::string16& database_name,
345 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
346 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) {
347 RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
348 BadMessageReceived();
352 db_tracker_->HandleSqliteError(origin_identifier, database_name, error);
355 void DatabaseMessageFilter::OnDatabaseSizeChanged(
356 const std::string& origin_identifier,
357 const base::string16& database_name,
358 int64 database_size) {
359 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
360 if (database_connections_.IsOriginUsed(origin_identifier)) {
361 Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
366 void DatabaseMessageFilter::OnDatabaseScheduledForDeletion(
367 const std::string& origin_identifier,
368 const base::string16& database_name) {
369 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
370 Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name));
373 } // namespace content