database_message_filter.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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. 4 5#include "content/browser/renderer_host/database_message_filter.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/platform_file.h" 11#include "base/strings/string_util.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/threading/thread.h" 14#include "content/common/database_messages.h" 15#include "content/public/browser/user_metrics.h" 16#include "content/public/common/result_codes.h" 17#include "third_party/sqlite/sqlite3.h" 18#include "webkit/browser/database/database_util.h" 19#include "webkit/browser/database/vfs_backend.h" 20#include "webkit/browser/quota/quota_manager.h" 21#include "webkit/common/database/database_identifier.h" 22 23#if defined(OS_POSIX) 24#include "base/file_descriptor_posix.h" 25#endif 26 27using quota::QuotaManager; 28using quota::QuotaManagerProxy; 29using quota::QuotaStatusCode; 30using webkit_database::DatabaseTracker; 31using webkit_database::DatabaseUtil; 32using webkit_database::VfsBackend; 33 34namespace content { 35namespace { 36 37const int kNumDeleteRetries = 2; 38const int kDelayDeleteRetryMs = 100; 39 40} // namespace 41 42DatabaseMessageFilter::DatabaseMessageFilter( 43 webkit_database::DatabaseTracker* db_tracker) 44 : db_tracker_(db_tracker), 45 observer_added_(false) { 46 DCHECK(db_tracker_.get()); 47} 48 49void DatabaseMessageFilter::OnChannelClosing() { 50 if (observer_added_) { 51 observer_added_ = false; 52 BrowserThread::PostTask( 53 BrowserThread::FILE, FROM_HERE, 54 base::Bind(&DatabaseMessageFilter::RemoveObserver, this)); 55 } 56} 57 58void DatabaseMessageFilter::AddObserver() { 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 60 db_tracker_->AddObserver(this); 61} 62 63void DatabaseMessageFilter::RemoveObserver() { 64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 65 db_tracker_->RemoveObserver(this); 66 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(); 71} 72 73void 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; 80 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)); 86 } 87} 88 89bool DatabaseMessageFilter::OnMessageReceived( 90 const IPC::Message& message, 91 bool* message_was_ok) { 92 bool handled = true; 93 IPC_BEGIN_MESSAGE_MAP_EX(DatabaseMessageFilter, message, *message_was_ok) 94 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile, 95 OnDatabaseOpenFile) 96 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile, 97 OnDatabaseDeleteFile) 98 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes, 99 OnDatabaseGetFileAttributes) 100 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize, 101 OnDatabaseGetFileSize) 102 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetSpaceAvailable, 103 OnDatabaseGetSpaceAvailable) 104 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened) 105 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified) 106 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed) 107 IPC_MESSAGE_HANDLER(DatabaseHostMsg_HandleSqliteError, OnHandleSqliteError) 108 IPC_MESSAGE_UNHANDLED(handled = false) 109 IPC_END_MESSAGE_MAP_EX() 110 return handled; 111} 112 113DatabaseMessageFilter::~DatabaseMessageFilter() { 114} 115 116void DatabaseMessageFilter::OnDatabaseOpenFile( 117 const base::string16& vfs_file_name, 118 int desired_flags, 119 IPC::Message* reply_msg) { 120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 121 base::PlatformFile file_handle = base::kInvalidPlatformFileValue; 122 std::string origin_identifier; 123 base::string16 database_name; 124 125 // When in incognito mode, we want to make sure that all DB files are 126 // removed when the incognito browser context goes away, so we add the 127 // SQLITE_OPEN_DELETEONCLOSE flag when opening all files, and keep 128 // open handles to them in the database tracker to make sure they're 129 // around for as long as needed. 130 if (vfs_file_name.empty()) { 131 VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(), 132 desired_flags, &file_handle); 133 } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier, 134 &database_name, NULL) && 135 !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier, 136 database_name)) { 137 base::FilePath db_file = DatabaseUtil::GetFullFilePathForVfsFile( 138 db_tracker_.get(), vfs_file_name); 139 if (!db_file.empty()) { 140 if (db_tracker_->IsIncognitoProfile()) { 141 db_tracker_->GetIncognitoFileHandle(vfs_file_name, &file_handle); 142 if (file_handle == base::kInvalidPlatformFileValue) { 143 VfsBackend::OpenFile(db_file, 144 desired_flags | SQLITE_OPEN_DELETEONCLOSE, 145 &file_handle); 146 if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE)) 147 db_tracker_->SaveIncognitoFileHandle(vfs_file_name, file_handle); 148 } 149 } else { 150 VfsBackend::OpenFile(db_file, desired_flags, &file_handle); 151 } 152 } 153 } 154 155 // Then we duplicate the file handle to make it useable in the renderer 156 // process. The original handle is closed, unless we saved it in the 157 // database tracker. 158 bool auto_close = !db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name); 159 IPC::PlatformFileForTransit target_handle = 160 IPC::GetFileHandleForProcess(file_handle, PeerHandle(), auto_close); 161 162 DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle); 163 Send(reply_msg); 164} 165 166void DatabaseMessageFilter::OnDatabaseDeleteFile( 167 const base::string16& vfs_file_name, 168 const bool& sync_dir, 169 IPC::Message* reply_msg) { 170 DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries); 171} 172 173void DatabaseMessageFilter::DatabaseDeleteFile( 174 const base::string16& vfs_file_name, 175 bool sync_dir, 176 IPC::Message* reply_msg, 177 int reschedule_count) { 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 179 180 // Return an error if the file name is invalid or if the file could not 181 // be deleted after kNumDeleteRetries attempts. 182 int error_code = SQLITE_IOERR_DELETE; 183 base::FilePath db_file = 184 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name); 185 if (!db_file.empty()) { 186 // In order to delete a journal file in incognito mode, we only need to 187 // close the open handle to it that's stored in the database tracker. 188 if (db_tracker_->IsIncognitoProfile()) { 189 const base::string16 wal_suffix(ASCIIToUTF16("-wal")); 190 base::string16 sqlite_suffix; 191 192 // WAL files can be deleted without having previously been opened. 193 if (!db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name) && 194 DatabaseUtil::CrackVfsFileName(vfs_file_name, 195 NULL, NULL, &sqlite_suffix) && 196 sqlite_suffix == wal_suffix) { 197 error_code = SQLITE_OK; 198 } else if (db_tracker_->CloseIncognitoFileHandle(vfs_file_name)) { 199 error_code = SQLITE_OK; 200 } 201 } else { 202 error_code = VfsBackend::DeleteFile(db_file, sync_dir); 203 } 204 205 if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) { 206 // If the file could not be deleted, try again. 207 BrowserThread::PostDelayedTask( 208 BrowserThread::FILE, FROM_HERE, 209 base::Bind(&DatabaseMessageFilter::DatabaseDeleteFile, this, 210 vfs_file_name, sync_dir, reply_msg, reschedule_count - 1), 211 base::TimeDelta::FromMilliseconds(kDelayDeleteRetryMs)); 212 return; 213 } 214 } 215 216 DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code); 217 Send(reply_msg); 218} 219 220void DatabaseMessageFilter::OnDatabaseGetFileAttributes( 221 const base::string16& vfs_file_name, 222 IPC::Message* reply_msg) { 223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 224 int32 attributes = -1; 225 base::FilePath db_file = 226 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name); 227 if (!db_file.empty()) 228 attributes = VfsBackend::GetFileAttributes(db_file); 229 230 DatabaseHostMsg_GetFileAttributes::WriteReplyParams( 231 reply_msg, attributes); 232 Send(reply_msg); 233} 234 235void DatabaseMessageFilter::OnDatabaseGetFileSize( 236 const base::string16& vfs_file_name, IPC::Message* reply_msg) { 237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 238 int64 size = 0; 239 base::FilePath db_file = 240 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name); 241 if (!db_file.empty()) 242 size = VfsBackend::GetFileSize(db_file); 243 244 DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size); 245 Send(reply_msg); 246} 247 248void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable( 249 const std::string& origin_identifier, IPC::Message* reply_msg) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 251 DCHECK(db_tracker_->quota_manager_proxy()); 252 253 QuotaManager* quota_manager = 254 db_tracker_->quota_manager_proxy()->quota_manager(); 255 if (!quota_manager) { 256 NOTREACHED(); // The system is shutting down, messages are unexpected. 257 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams( 258 reply_msg, static_cast<int64>(0)); 259 Send(reply_msg); 260 return; 261 } 262 263 quota_manager->GetUsageAndQuota( 264 webkit_database::GetOriginFromIdentifier(origin_identifier), 265 quota::kStorageTypeTemporary, 266 base::Bind(&DatabaseMessageFilter::OnDatabaseGetUsageAndQuota, 267 this, reply_msg)); 268} 269 270void DatabaseMessageFilter::OnDatabaseGetUsageAndQuota( 271 IPC::Message* reply_msg, 272 quota::QuotaStatusCode status, 273 int64 usage, 274 int64 quota) { 275 int64 available = 0; 276 if ((status == quota::kQuotaStatusOk) && (usage < quota)) 277 available = quota - usage; 278 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(reply_msg, available); 279 Send(reply_msg); 280} 281 282void DatabaseMessageFilter::OnDatabaseOpened( 283 const std::string& origin_identifier, 284 const base::string16& database_name, 285 const base::string16& description, 286 int64 estimated_size) { 287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 288 289 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) { 290 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 291 BadMessageReceived(); 292 return; 293 } 294 295 int64 database_size = 0; 296 db_tracker_->DatabaseOpened(origin_identifier, database_name, description, 297 estimated_size, &database_size); 298 database_connections_.AddConnection(origin_identifier, database_name); 299 Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name, 300 database_size)); 301} 302 303void DatabaseMessageFilter::OnDatabaseModified( 304 const std::string& origin_identifier, 305 const base::string16& database_name) { 306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 307 if (!database_connections_.IsDatabaseOpened( 308 origin_identifier, database_name)) { 309 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 310 BadMessageReceived(); 311 return; 312 } 313 314 db_tracker_->DatabaseModified(origin_identifier, database_name); 315} 316 317void DatabaseMessageFilter::OnDatabaseClosed( 318 const std::string& origin_identifier, 319 const base::string16& database_name) { 320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 321 if (!database_connections_.IsDatabaseOpened( 322 origin_identifier, database_name)) { 323 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 324 BadMessageReceived(); 325 return; 326 } 327 328 database_connections_.RemoveConnection(origin_identifier, database_name); 329 db_tracker_->DatabaseClosed(origin_identifier, database_name); 330} 331 332void DatabaseMessageFilter::OnHandleSqliteError( 333 const std::string& origin_identifier, 334 const base::string16& database_name, 335 int error) { 336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 337 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) { 338 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 339 BadMessageReceived(); 340 return; 341 } 342 343 db_tracker_->HandleSqliteError(origin_identifier, database_name, error); 344} 345 346void DatabaseMessageFilter::OnDatabaseSizeChanged( 347 const std::string& origin_identifier, 348 const base::string16& database_name, 349 int64 database_size) { 350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 351 if (database_connections_.IsOriginUsed(origin_identifier)) { 352 Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name, 353 database_size)); 354 } 355} 356 357void DatabaseMessageFilter::OnDatabaseScheduledForDeletion( 358 const std::string& origin_identifier, 359 const base::string16& database_name) { 360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 361 Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name)); 362} 363 364} // namespace content 365