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/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