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 27 using quota::QuotaManager; 28 using quota::QuotaManagerProxy; 29 using quota::QuotaStatusCode; 30 using webkit_database::DatabaseTracker; 31 using webkit_database::DatabaseUtil; 32 using webkit_database::VfsBackend; 33 34 namespace content { 35 namespace { 36 37 const int kNumDeleteRetries = 2; 38 const int kDelayDeleteRetryMs = 100; 39 40 } // namespace 41 42 DatabaseMessageFilter::DatabaseMessageFilter( 43 webkit_database::DatabaseTracker* db_tracker) 44 : db_tracker_(db_tracker), 45 observer_added_(false) { 46 DCHECK(db_tracker_.get()); 47 } 48 49 void DatabaseMessageFilter::OnChannelClosing() { 50 BrowserMessageFilter::OnChannelClosing(); 51 if (observer_added_) { 52 observer_added_ = false; 53 BrowserThread::PostTask( 54 BrowserThread::FILE, FROM_HERE, 55 base::Bind(&DatabaseMessageFilter::RemoveObserver, this)); 56 } 57 } 58 59 void DatabaseMessageFilter::AddObserver() { 60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 61 db_tracker_->AddObserver(this); 62 } 63 64 void DatabaseMessageFilter::RemoveObserver() { 65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 66 db_tracker_->RemoveObserver(this); 67 68 // If the renderer process died without closing all databases, 69 // then we need to manually close those connections 70 db_tracker_->CloseDatabases(database_connections_); 71 database_connections_.RemoveAllConnections(); 72 } 73 74 void DatabaseMessageFilter::OverrideThreadForMessage( 75 const IPC::Message& message, 76 BrowserThread::ID* thread) { 77 if (message.type() == DatabaseHostMsg_GetSpaceAvailable::ID) 78 *thread = BrowserThread::IO; 79 else if (IPC_MESSAGE_CLASS(message) == DatabaseMsgStart) 80 *thread = BrowserThread::FILE; 81 82 if (message.type() == DatabaseHostMsg_Opened::ID && !observer_added_) { 83 observer_added_ = true; 84 BrowserThread::PostTask( 85 BrowserThread::FILE, FROM_HERE, 86 base::Bind(&DatabaseMessageFilter::AddObserver, this)); 87 } 88 } 89 90 bool DatabaseMessageFilter::OnMessageReceived( 91 const IPC::Message& message, 92 bool* message_was_ok) { 93 bool handled = true; 94 IPC_BEGIN_MESSAGE_MAP_EX(DatabaseMessageFilter, message, *message_was_ok) 95 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile, 96 OnDatabaseOpenFile) 97 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile, 98 OnDatabaseDeleteFile) 99 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes, 100 OnDatabaseGetFileAttributes) 101 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize, 102 OnDatabaseGetFileSize) 103 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetSpaceAvailable, 104 OnDatabaseGetSpaceAvailable) 105 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened) 106 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified) 107 IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed) 108 IPC_MESSAGE_HANDLER(DatabaseHostMsg_HandleSqliteError, OnHandleSqliteError) 109 IPC_MESSAGE_UNHANDLED(handled = false) 110 IPC_END_MESSAGE_MAP_EX() 111 return handled; 112 } 113 114 DatabaseMessageFilter::~DatabaseMessageFilter() { 115 } 116 117 void DatabaseMessageFilter::OnDatabaseOpenFile(const 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 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 166 void DatabaseMessageFilter::OnDatabaseDeleteFile(const string16& vfs_file_name, 167 const bool& sync_dir, 168 IPC::Message* reply_msg) { 169 DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries); 170 } 171 172 void DatabaseMessageFilter::DatabaseDeleteFile(const string16& vfs_file_name, 173 bool sync_dir, 174 IPC::Message* reply_msg, 175 int reschedule_count) { 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 177 178 // Return an error if the file name is invalid or if the file could not 179 // be deleted after kNumDeleteRetries attempts. 180 int error_code = SQLITE_IOERR_DELETE; 181 base::FilePath db_file = 182 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name); 183 if (!db_file.empty()) { 184 // In order to delete a journal file in incognito mode, we only need to 185 // close the open handle to it that's stored in the database tracker. 186 if (db_tracker_->IsIncognitoProfile()) { 187 const string16 wal_suffix(ASCIIToUTF16("-wal")); 188 string16 sqlite_suffix; 189 190 // WAL files can be deleted without having previously been opened. 191 if (!db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name) && 192 DatabaseUtil::CrackVfsFileName(vfs_file_name, 193 NULL, NULL, &sqlite_suffix) && 194 sqlite_suffix == wal_suffix) { 195 error_code = SQLITE_OK; 196 } else if (db_tracker_->CloseIncognitoFileHandle(vfs_file_name)) { 197 error_code = SQLITE_OK; 198 } 199 } else { 200 error_code = VfsBackend::DeleteFile(db_file, sync_dir); 201 } 202 203 if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) { 204 // If the file could not be deleted, try again. 205 BrowserThread::PostDelayedTask( 206 BrowserThread::FILE, FROM_HERE, 207 base::Bind(&DatabaseMessageFilter::DatabaseDeleteFile, this, 208 vfs_file_name, sync_dir, reply_msg, reschedule_count - 1), 209 base::TimeDelta::FromMilliseconds(kDelayDeleteRetryMs)); 210 return; 211 } 212 } 213 214 DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code); 215 Send(reply_msg); 216 } 217 218 void DatabaseMessageFilter::OnDatabaseGetFileAttributes( 219 const string16& vfs_file_name, 220 IPC::Message* reply_msg) { 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 222 int32 attributes = -1; 223 base::FilePath db_file = 224 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name); 225 if (!db_file.empty()) 226 attributes = VfsBackend::GetFileAttributes(db_file); 227 228 DatabaseHostMsg_GetFileAttributes::WriteReplyParams( 229 reply_msg, attributes); 230 Send(reply_msg); 231 } 232 233 void DatabaseMessageFilter::OnDatabaseGetFileSize( 234 const string16& vfs_file_name, IPC::Message* reply_msg) { 235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 236 int64 size = 0; 237 base::FilePath db_file = 238 DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name); 239 if (!db_file.empty()) 240 size = VfsBackend::GetFileSize(db_file); 241 242 DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size); 243 Send(reply_msg); 244 } 245 246 void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable( 247 const std::string& origin_identifier, IPC::Message* reply_msg) { 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 249 DCHECK(db_tracker_->quota_manager_proxy()); 250 251 QuotaManager* quota_manager = 252 db_tracker_->quota_manager_proxy()->quota_manager(); 253 if (!quota_manager) { 254 NOTREACHED(); // The system is shutting down, messages are unexpected. 255 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams( 256 reply_msg, static_cast<int64>(0)); 257 Send(reply_msg); 258 return; 259 } 260 261 quota_manager->GetUsageAndQuota( 262 webkit_database::GetOriginFromIdentifier(origin_identifier), 263 quota::kStorageTypeTemporary, 264 base::Bind(&DatabaseMessageFilter::OnDatabaseGetUsageAndQuota, 265 this, reply_msg)); 266 } 267 268 void DatabaseMessageFilter::OnDatabaseGetUsageAndQuota( 269 IPC::Message* reply_msg, 270 quota::QuotaStatusCode status, 271 int64 usage, 272 int64 quota) { 273 int64 available = 0; 274 if ((status == quota::kQuotaStatusOk) && (usage < quota)) 275 available = quota - usage; 276 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(reply_msg, available); 277 Send(reply_msg); 278 } 279 280 void DatabaseMessageFilter::OnDatabaseOpened( 281 const std::string& origin_identifier, 282 const string16& database_name, 283 const string16& description, 284 int64 estimated_size) { 285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 286 287 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) { 288 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 289 BadMessageReceived(); 290 return; 291 } 292 293 int64 database_size = 0; 294 db_tracker_->DatabaseOpened(origin_identifier, database_name, description, 295 estimated_size, &database_size); 296 database_connections_.AddConnection(origin_identifier, database_name); 297 Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name, 298 database_size)); 299 } 300 301 void DatabaseMessageFilter::OnDatabaseModified( 302 const std::string& origin_identifier, 303 const string16& database_name) { 304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 305 if (!database_connections_.IsDatabaseOpened( 306 origin_identifier, database_name)) { 307 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 308 BadMessageReceived(); 309 return; 310 } 311 312 db_tracker_->DatabaseModified(origin_identifier, database_name); 313 } 314 315 void DatabaseMessageFilter::OnDatabaseClosed( 316 const std::string& origin_identifier, 317 const string16& database_name) { 318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 319 if (!database_connections_.IsDatabaseOpened( 320 origin_identifier, database_name)) { 321 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 322 BadMessageReceived(); 323 return; 324 } 325 326 database_connections_.RemoveConnection(origin_identifier, database_name); 327 db_tracker_->DatabaseClosed(origin_identifier, database_name); 328 } 329 330 void DatabaseMessageFilter::OnHandleSqliteError( 331 const std::string& origin_identifier, 332 const string16& database_name, 333 int error) { 334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 335 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) { 336 RecordAction(UserMetricsAction("BadMessageTerminate_DBMF")); 337 BadMessageReceived(); 338 return; 339 } 340 341 db_tracker_->HandleSqliteError(origin_identifier, database_name, error); 342 } 343 344 void DatabaseMessageFilter::OnDatabaseSizeChanged( 345 const std::string& origin_identifier, 346 const string16& database_name, 347 int64 database_size) { 348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 349 if (database_connections_.IsOriginUsed(origin_identifier)) { 350 Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name, 351 database_size)); 352 } 353 } 354 355 void DatabaseMessageFilter::OnDatabaseScheduledForDeletion( 356 const std::string& origin_identifier, 357 const string16& database_name) { 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 359 Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name)); 360 } 361 362 } // namespace content 363