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/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" 22 23 #if defined(OS_POSIX) 24 #include "base/file_descriptor_posix.h" 25 #endif 26 27 using storage::QuotaManager; 28 using storage::QuotaStatusCode; 29 using storage::DatabaseTracker; 30 using storage::DatabaseUtil; 31 using storage::VfsBackend; 32 33 namespace content { 34 namespace { 35 36 const int kNumDeleteRetries = 2; 37 const int kDelayDeleteRetryMs = 100; 38 39 } // namespace 40 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()); 47 } 48 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)); 55 } 56 } 57 58 void DatabaseMessageFilter::AddObserver() { 59 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 60 db_tracker_->AddObserver(this); 61 } 62 63 void DatabaseMessageFilter::RemoveObserver() { 64 DCHECK_CURRENTLY_ON(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 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; 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 89 bool DatabaseMessageFilter::OnMessageReceived(const IPC::Message& message) { 90 bool handled = true; 91 IPC_BEGIN_MESSAGE_MAP(DatabaseMessageFilter, message) 92 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile, 93 OnDatabaseOpenFile) 94 IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile, 95 OnDatabaseDeleteFile) 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() 108 return handled; 109 } 110 111 DatabaseMessageFilter::~DatabaseMessageFilter() { 112 } 113 114 void DatabaseMessageFilter::OnDatabaseOpenFile( 115 const base::string16& vfs_file_name, 116 int desired_flags, 117 IPC::Message* reply_msg) { 118 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 119 base::File file; 120 const base::File* tracked_file = NULL; 121 std::string origin_identifier; 122 base::string16 database_name; 123 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(), 131 desired_flags); 132 } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier, 133 &database_name, NULL) && 134 !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier, 135 database_name)) { 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); 141 if (!tracked_file) { 142 file = 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, 147 file.Pass()); 148 } 149 } 150 } else { 151 file = VfsBackend::OpenFile(db_file, desired_flags); 152 } 153 } 154 } 155 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 158 // database tracker. 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()); 165 target_handle = 166 IPC::GetFileHandleForProcess(tracked_file->GetPlatformFile(), 167 PeerHandle(), false); 168 } 169 170 DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle); 171 Send(reply_msg); 172 } 173 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); 179 } 180 181 void DatabaseMessageFilter::DatabaseDeleteFile( 182 const base::string16& vfs_file_name, 183 bool sync_dir, 184 IPC::Message* reply_msg, 185 int reschedule_count) { 186 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 187 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; 199 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; 206 } else { 207 db_tracker_->CloseIncognitoFileHandle(vfs_file_name); 208 error_code = SQLITE_OK; 209 } 210 } else { 211 error_code = VfsBackend::DeleteFile(db_file, sync_dir); 212 } 213 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)); 221 return; 222 } 223 } 224 225 DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code); 226 Send(reply_msg); 227 } 228 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); 238 239 DatabaseHostMsg_GetFileAttributes::WriteReplyParams( 240 reply_msg, attributes); 241 Send(reply_msg); 242 } 243 244 void DatabaseMessageFilter::OnDatabaseGetFileSize( 245 const base::string16& vfs_file_name, IPC::Message* reply_msg) { 246 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 247 int64 size = 0; 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); 252 253 DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size); 254 Send(reply_msg); 255 } 256 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()); 261 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)); 268 Send(reply_msg); 269 return; 270 } 271 272 quota_manager->GetUsageAndQuota( 273 storage::GetOriginFromIdentifier(origin_identifier), 274 storage::kStorageTypeTemporary, 275 base::Bind( 276 &DatabaseMessageFilter::OnDatabaseGetUsageAndQuota, this, reply_msg)); 277 } 278 279 void DatabaseMessageFilter::OnDatabaseGetUsageAndQuota( 280 IPC::Message* reply_msg, 281 storage::QuotaStatusCode status, 282 int64 usage, 283 int64 quota) { 284 int64 available = 0; 285 if ((status == storage::kQuotaStatusOk) && (usage < quota)) 286 available = quota - usage; 287 DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(reply_msg, available); 288 Send(reply_msg); 289 } 290 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); 297 298 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) { 299 RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF")); 300 BadMessageReceived(); 301 return; 302 } 303 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, 309 database_size)); 310 } 311 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(); 320 return; 321 } 322 323 db_tracker_->DatabaseModified(origin_identifier, database_name); 324 } 325 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(); 334 return; 335 } 336 337 database_connections_.RemoveConnection(origin_identifier, database_name); 338 db_tracker_->DatabaseClosed(origin_identifier, database_name); 339 } 340 341 void DatabaseMessageFilter::OnHandleSqliteError( 342 const std::string& origin_identifier, 343 const base::string16& database_name, 344 int error) { 345 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 346 if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) { 347 RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF")); 348 BadMessageReceived(); 349 return; 350 } 351 352 db_tracker_->HandleSqliteError(origin_identifier, database_name, error); 353 } 354 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, 362 database_size)); 363 } 364 } 365 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)); 371 } 372 373 } // namespace content 374