Home | History | Annotate | Download | only in renderer_host
      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