| // Copyright 2013 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ppapi/proxy/nacl_message_scanner.h" | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <tuple> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "build/build_config.h" | 
 | #include "ipc/ipc_message.h" | 
 | #include "ipc/ipc_message_macros.h" | 
 | #include "ppapi/proxy/ppapi_messages.h" | 
 | #include "ppapi/proxy/resource_message_params.h" | 
 | #include "ppapi/proxy/serialized_handle.h" | 
 | #include "ppapi/proxy/serialized_var.h" | 
 |  | 
 | class NaClDescImcShm; | 
 |  | 
 | namespace IPC { | 
 | class Message; | 
 | } | 
 |  | 
 | using ppapi::proxy::ResourceMessageReplyParams; | 
 | using ppapi::proxy::SerializedHandle; | 
 | using ppapi::proxy::SerializedVar; | 
 |  | 
 | namespace { | 
 |  | 
 | typedef std::vector<SerializedHandle> Handles; | 
 |  | 
 | struct ScanningResults { | 
 |   ScanningResults() : handle_index(0), pp_resource(0) {} | 
 |  | 
 |   // Vector to hold handles found in the message. | 
 |   Handles handles; | 
 |   // Current handle index in the rewritten message. During the scan, it will be | 
 |   // be less than or equal to handles.size(). After the scan it should be equal. | 
 |   int handle_index; | 
 |   // The rewritten message. This may be NULL, so all ScanParam overloads should | 
 |   // check for NULL before writing to it. In some cases, a ScanParam overload | 
 |   // may set this to NULL when it can determine that there are no parameters | 
 |   // that need conversion. (See the ResourceMessageReplyParams overload.) | 
 |   std::unique_ptr<IPC::Message> new_msg; | 
 |   // Resource id for resource messages. Save this when scanning resource replies | 
 |   // so when we audit the nested message, we know which resource it is for. | 
 |   PP_Resource pp_resource; | 
 |   // Callback to receive the nested message in a resource message or reply. | 
 |   base::Callback<void(PP_Resource, const IPC::Message&, SerializedHandle*)> | 
 |       nested_msg_callback; | 
 | }; | 
 |  | 
 | void WriteHandle(int handle_index, | 
 |                  const SerializedHandle& handle, | 
 |                  base::Pickle* msg) { | 
 |   SerializedHandle::WriteHeader(handle.header(), msg); | 
 |  | 
 |   if (handle.type() == SerializedHandle::SHARED_MEMORY) { | 
 |     // Now write the handle itself in POSIX style. | 
 |     // This serialization must be kept in sync with | 
 |     // ParamTraits<SharedMemoryHandle>::Write and | 
 |     // ParamTraits<UnguessableToken>::Write. | 
 |     if (handle.shmem().IsValid()) { | 
 |       msg->WriteBool(true);  // valid == true | 
 |       msg->WriteInt(handle_index); | 
 |       msg->WriteUInt64(handle.shmem().GetGUID().GetHighForSerialization()); | 
 |       msg->WriteUInt64(handle.shmem().GetGUID().GetLowForSerialization()); | 
 |       msg->WriteUInt64(handle.shmem().GetSize()); | 
 |     } else { | 
 |       msg->WriteBool(false);  // valid == false | 
 |     } | 
 |   } else if (handle.type() != SerializedHandle::INVALID) { | 
 |     // Now write the handle itself in POSIX style. | 
 |     // This serialization must be kept in sync with | 
 |     // ParamTraits<FileDescriptor>::Write. | 
 |     msg->WriteBool(true);  // valid == true | 
 |     msg->WriteInt(handle_index); | 
 |   } | 
 | } | 
 |  | 
 | // Define overloads for each kind of message parameter that requires special | 
 | // handling. See ScanTuple for how these get used. | 
 |  | 
 | // Overload to match SerializedHandle. | 
 | void ScanParam(const SerializedHandle& handle, ScanningResults* results) { | 
 |   results->handles.push_back(handle); | 
 |   if (results->new_msg) | 
 |     WriteHandle(results->handle_index++, handle, results->new_msg.get()); | 
 | } | 
 |  | 
 | void HandleWriter(int* handle_index, | 
 |                   base::Pickle* m, | 
 |                   const SerializedHandle& handle) { | 
 |   WriteHandle((*handle_index)++, handle, m); | 
 | } | 
 |  | 
 | // Overload to match SerializedVar, which can contain handles. | 
 | void ScanParam(const SerializedVar& var, ScanningResults* results) { | 
 |   std::vector<SerializedHandle*> var_handles = var.GetHandles(); | 
 |   // Copy any handles and then rewrite the message. | 
 |   for (size_t i = 0; i < var_handles.size(); ++i) | 
 |     results->handles.push_back(*var_handles[i]); | 
 |   if (results->new_msg) | 
 |     var.WriteDataToMessage(results->new_msg.get(), | 
 |                            base::Bind(&HandleWriter, &results->handle_index)); | 
 | } | 
 |  | 
 | // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall, | 
 | // the handles are carried inside the ResourceMessageReplyParams. | 
 | // NOTE: We only intercept handles from host->NaCl. The only kind of | 
 | //       ResourceMessageParams that travels this direction is | 
 | //       ResourceMessageReplyParams, so that's the only one we need to handle. | 
 | void ScanParam(const ResourceMessageReplyParams& params, | 
 |                ScanningResults* results) { | 
 |   results->pp_resource = params.pp_resource(); | 
 |   // If the resource reply params don't contain handles, NULL the new message | 
 |   // pointer to cancel further rewriting. | 
 |   // NOTE: This works because only handles currently need rewriting, and we | 
 |   //       know at this point that this message has none. | 
 |   if (params.handles().empty()) { | 
 |     results->new_msg.reset(NULL); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If we need to rewrite the message, write everything before the handles | 
 |   // (there's nothing after the handles). | 
 |   if (results->new_msg) { | 
 |     params.WriteReplyHeader(results->new_msg.get()); | 
 |     // IPC writes the vector length as an int before the contents of the | 
 |     // vector. | 
 |     results->new_msg->WriteInt(static_cast<int>(params.handles().size())); | 
 |   } | 
 |   for (Handles::const_iterator iter = params.handles().begin(); | 
 |        iter != params.handles().end(); | 
 |        ++iter) { | 
 |     // ScanParam will write each handle to the new message, if necessary. | 
 |     ScanParam(*iter, results); | 
 |   } | 
 |   // Tell ResourceMessageReplyParams that we have taken the handles, so it | 
 |   // shouldn't close them. The NaCl runtime will take ownership of them. | 
 |   params.ConsumeHandles(); | 
 | } | 
 |  | 
 | // Overload to match nested messages. If we need to rewrite the message, write | 
 | // the parameter. | 
 | void ScanParam(const IPC::Message& param, ScanningResults* results) { | 
 |   if (results->pp_resource && !results->nested_msg_callback.is_null()) { | 
 |     SerializedHandle* handle = NULL; | 
 |     if (results->handles.size() == 1) | 
 |       handle = &results->handles[0]; | 
 |     results->nested_msg_callback.Run(results->pp_resource, param, handle); | 
 |   } | 
 |   if (results->new_msg) | 
 |     IPC::WriteParam(results->new_msg.get(), param); | 
 | } | 
 |  | 
 | template <class T> | 
 | void ScanParam(const std::vector<T>& vec, ScanningResults* results) { | 
 |   if (results->new_msg) | 
 |     IPC::WriteParam(results->new_msg.get(), static_cast<int>(vec.size())); | 
 |   for (const T& element : vec) { | 
 |     ScanParam(element, results); | 
 |   } | 
 | } | 
 |  | 
 | // Overload to match all other types. If we need to rewrite the message, write | 
 | // the parameter. | 
 | template <class T> | 
 | void ScanParam(const T& param, ScanningResults* results) { | 
 |   if (results->new_msg) | 
 |     IPC::WriteParam(results->new_msg.get(), param); | 
 | } | 
 |  | 
 | // These just break apart the given tuple and run ScanParam over each param. | 
 | // The idea is to scan elements in the tuple which require special handling, | 
 | // and write them into the |results| struct. | 
 | template <class A> | 
 | void ScanTuple(const std::tuple<A>& t1, ScanningResults* results) { | 
 |   ScanParam(std::get<0>(t1), results); | 
 | } | 
 | template <class A, class B> | 
 | void ScanTuple(const std::tuple<A, B>& t1, ScanningResults* results) { | 
 |   ScanParam(std::get<0>(t1), results); | 
 |   ScanParam(std::get<1>(t1), results); | 
 | } | 
 | template <class A, class B, class C> | 
 | void ScanTuple(const std::tuple<A, B, C>& t1, ScanningResults* results) { | 
 |   ScanParam(std::get<0>(t1), results); | 
 |   ScanParam(std::get<1>(t1), results); | 
 |   ScanParam(std::get<2>(t1), results); | 
 | } | 
 | template <class A, class B, class C, class D> | 
 | void ScanTuple(const std::tuple<A, B, C, D>& t1, ScanningResults* results) { | 
 |   ScanParam(std::get<0>(t1), results); | 
 |   ScanParam(std::get<1>(t1), results); | 
 |   ScanParam(std::get<2>(t1), results); | 
 |   ScanParam(std::get<3>(t1), results); | 
 | } | 
 |  | 
 | template <class MessageType> | 
 | class MessageScannerImpl { | 
 |  public: | 
 |   explicit MessageScannerImpl(const IPC::Message* msg) | 
 |       // The cast below is invalid. See https://crbug.com/520760. | 
 |       : msg_(static_cast<const MessageType*>(msg)) { | 
 |   } | 
 |   bool ScanMessage(ScanningResults* results) { | 
 |     typename MessageType::Param params; | 
 |     if (!MessageType::Read(msg_, ¶ms)) | 
 |       return false; | 
 |     ScanTuple(params, results); | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool ScanSyncMessage(ScanningResults* results) { | 
 |     typename MessageType::SendParam params; | 
 |     if (!MessageType::ReadSendParam(msg_, ¶ms)) | 
 |       return false; | 
 |     // If we need to rewrite the message, write the message id first. | 
 |     if (results->new_msg) { | 
 |       results->new_msg->set_sync(); | 
 |       int id = IPC::SyncMessage::GetMessageId(*msg_); | 
 |       results->new_msg->WriteInt(id); | 
 |     } | 
 |     ScanTuple(params, results); | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool ScanReply(ScanningResults* results) { | 
 |     typename MessageType::ReplyParam params; | 
 |     if (!MessageType::ReadReplyParam(msg_, ¶ms)) | 
 |       return false; | 
 |     // If we need to rewrite the message, write the message id first. | 
 |     if (results->new_msg) { | 
 |       results->new_msg->set_reply(); | 
 |       int id = IPC::SyncMessage::GetMessageId(*msg_); | 
 |       results->new_msg->WriteInt(id); | 
 |     } | 
 |     ScanTuple(params, results); | 
 |     return true; | 
 |   } | 
 |  | 
 |  private: | 
 |   const MessageType* msg_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \ | 
 |       case MESSAGE_TYPE::ID: { \ | 
 |         MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ | 
 |         if (rewrite_msg) \ | 
 |           results.new_msg.reset( \ | 
 |               new IPC::Message(msg.routing_id(), msg.type(), \ | 
 |                                IPC::Message::PRIORITY_NORMAL)); \ | 
 |         if (!scanner.ScanMessage(&results)) \ | 
 |           return false; \ | 
 |         break; \ | 
 |       } | 
 | #define CASE_FOR_SYNC_MESSAGE(MESSAGE_TYPE) \ | 
 |       case MESSAGE_TYPE::ID: { \ | 
 |         MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ | 
 |         if (rewrite_msg) \ | 
 |           results.new_msg.reset( \ | 
 |               new IPC::Message(msg.routing_id(), msg.type(), \ | 
 |                                IPC::Message::PRIORITY_NORMAL)); \ | 
 |         if (!scanner.ScanSyncMessage(&results)) \ | 
 |           return false; \ | 
 |         break; \ | 
 |       } | 
 | #define CASE_FOR_REPLY(MESSAGE_TYPE) \ | 
 |       case MESSAGE_TYPE::ID: { \ | 
 |         MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ | 
 |         if (rewrite_msg) \ | 
 |           results.new_msg.reset( \ | 
 |               new IPC::Message(msg.routing_id(), msg.type(), \ | 
 |                                IPC::Message::PRIORITY_NORMAL)); \ | 
 |         if (!scanner.ScanReply(&results)) \ | 
 |           return false; \ | 
 |         break; \ | 
 |       } | 
 |  | 
 | namespace ppapi { | 
 | namespace proxy { | 
 |  | 
 | class SerializedHandle; | 
 |  | 
 | NaClMessageScanner::FileSystem::FileSystem() | 
 |     : reserved_quota_(0) { | 
 | } | 
 |  | 
 | NaClMessageScanner::FileSystem::~FileSystem() { | 
 | } | 
 |  | 
 | bool NaClMessageScanner::FileSystem::UpdateReservedQuota(int64_t delta) { | 
 |   base::AutoLock lock(lock_); | 
 |   if (std::numeric_limits<int64_t>::max() - reserved_quota_ < delta) | 
 |     return false;  // reserved_quota_ + delta would overflow. | 
 |   if (reserved_quota_ + delta < 0) | 
 |     return false; | 
 |   reserved_quota_ += delta; | 
 |   return true; | 
 | } | 
 |  | 
 | NaClMessageScanner::FileIO::FileIO(FileSystem* file_system, | 
 |                                    int64_t max_written_offset) | 
 |     : file_system_(file_system), | 
 |       max_written_offset_(max_written_offset) { | 
 | } | 
 |  | 
 | NaClMessageScanner::FileIO::~FileIO() { | 
 | } | 
 |  | 
 | void NaClMessageScanner::FileIO::SetMaxWrittenOffset( | 
 |     int64_t max_written_offset) { | 
 |   base::AutoLock lock(lock_); | 
 |   max_written_offset_ = max_written_offset; | 
 | } | 
 |  | 
 | bool NaClMessageScanner::FileIO::Grow(int64_t amount) { | 
 |   base::AutoLock lock(lock_); | 
 |   DCHECK(amount > 0); | 
 |   if (!file_system_->UpdateReservedQuota(-amount)) | 
 |     return false; | 
 |   max_written_offset_ += amount; | 
 |   return true; | 
 | } | 
 |  | 
 | NaClMessageScanner::NaClMessageScanner() { | 
 | } | 
 |  | 
 | NaClMessageScanner::~NaClMessageScanner() { | 
 |   for (FileSystemMap::iterator it = file_systems_.begin(); | 
 |       it != file_systems_.end(); ++it) | 
 |     delete it->second; | 
 |   for (FileIOMap::iterator it = files_.begin(); it != files_.end(); ++it) | 
 |     delete it->second; | 
 | } | 
 |  | 
 | // Windows IPC differs from POSIX in that native handles are serialized in the | 
 | // message body, rather than passed in a separate FileDescriptorSet. Therefore, | 
 | // on Windows, any message containing handles must be rewritten in the POSIX | 
 | // format before we can send it to the NaCl plugin. | 
 | // On Mac, base::SharedMemoryHandle has a different serialization than | 
 | // base::FileDescriptor (which base::SharedMemoryHandle is typedef-ed to in | 
 | // OS_NACL). | 
 | bool NaClMessageScanner::ScanMessage( | 
 |     const IPC::Message& msg, | 
 |     uint32_t type, | 
 |     std::vector<SerializedHandle>* handles, | 
 |     std::unique_ptr<IPC::Message>* new_msg_ptr) { | 
 |   DCHECK(handles); | 
 |   DCHECK(handles->empty()); | 
 |   DCHECK(new_msg_ptr); | 
 |   DCHECK(!new_msg_ptr->get()); | 
 |  | 
 |   bool rewrite_msg = | 
 | #if defined(OS_WIN) || defined(OS_MACOSX) | 
 |       true; | 
 | #else | 
 |       false; | 
 | #endif | 
 |  | 
 |   // We can't always tell from the message ID if rewriting is needed. Therefore, | 
 |   // scan any message types that might contain a handle. If we later determine | 
 |   // that there are no handles, we can cancel the rewriting by clearing the | 
 |   // results.new_msg pointer. | 
 |   ScanningResults results; | 
 |   results.nested_msg_callback = | 
 |       base::Bind(&NaClMessageScanner::AuditNestedMessage, | 
 |                  base::Unretained(this)); | 
 |   switch (type) { | 
 |     CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated) | 
 |     CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage) | 
 |     CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply) | 
 |     CASE_FOR_SYNC_MESSAGE(PpapiMsg_PPPMessageHandler_HandleBlockingMessage) | 
 |     CASE_FOR_SYNC_MESSAGE(PpapiMsg_PnaclTranslatorCompileInit) | 
 |     CASE_FOR_SYNC_MESSAGE(PpapiMsg_PnaclTranslatorLink) | 
 |     CASE_FOR_REPLY(PpapiHostMsg_OpenResource) | 
 |     CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_Create) | 
 |     CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer) | 
 |     CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple) | 
 |     CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall) | 
 |     CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory) | 
 |     default: | 
 |       // Do nothing for messages we don't know. | 
 |       break; | 
 |   } | 
 |  | 
 |   // Only messages containing handles need to be rewritten. If no handles are | 
 |   // found, don't return the rewritten message either. This must be changed if | 
 |   // we ever add new param types that also require rewriting. | 
 |   if (!results.handles.empty()) { | 
 |     handles->swap(results.handles); | 
 |     *new_msg_ptr = std::move(results.new_msg); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | void NaClMessageScanner::ScanUntrustedMessage( | 
 |     const IPC::Message& untrusted_msg, | 
 |     std::unique_ptr<IPC::Message>* new_msg_ptr) { | 
 |   // Audit FileIO and FileSystem messages to ensure that the plugin doesn't | 
 |   // exceed its file quota. If we find the message is malformed, just pass it | 
 |   // through - we only care about well formed messages to the host. | 
 |   if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) { | 
 |     ResourceMessageCallParams params; | 
 |     IPC::Message nested_msg; | 
 |     if (!UnpackMessage<PpapiHostMsg_ResourceCall>( | 
 |             untrusted_msg, ¶ms, &nested_msg)) | 
 |       return; | 
 |  | 
 |     switch (nested_msg.type()) { | 
 |       case PpapiHostMsg_FileIO_Close::ID: { | 
 |         FileIOMap::iterator it = files_.find(params.pp_resource()); | 
 |         if (it == files_.end()) | 
 |           return; | 
 |         // Audit FileIO Close messages to make sure the plugin reports an | 
 |         // accurate file size. | 
 |         FileGrowth file_growth; | 
 |         if (!UnpackMessage<PpapiHostMsg_FileIO_Close>( | 
 |                 nested_msg, &file_growth)) | 
 |           return; | 
 |  | 
 |         int64_t trusted_max_written_offset = it->second->max_written_offset(); | 
 |         delete it->second; | 
 |         files_.erase(it); | 
 |         // If the plugin is under-reporting, rewrite the message with the | 
 |         // trusted value. | 
 |         if (trusted_max_written_offset > file_growth.max_written_offset) { | 
 |           new_msg_ptr->reset( | 
 |               new PpapiHostMsg_ResourceCall( | 
 |                   params, | 
 |                   PpapiHostMsg_FileIO_Close( | 
 |                       FileGrowth(trusted_max_written_offset, 0)))); | 
 |         } | 
 |         break; | 
 |       } | 
 |       case PpapiHostMsg_FileIO_SetLength::ID: { | 
 |         FileIOMap::iterator it = files_.find(params.pp_resource()); | 
 |         if (it == files_.end()) | 
 |           return; | 
 |         // Audit FileIO SetLength messages to make sure the plugin is within | 
 |         // the current quota reservation. In addition, deduct the file size | 
 |         // increase from the quota reservation. | 
 |         int64_t length = 0; | 
 |         if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>( | 
 |                 nested_msg, &length)) | 
 |           return; | 
 |  | 
 |         // Calculate file size increase, taking care to avoid overflows. | 
 |         if (length < 0) | 
 |           return; | 
 |         int64_t trusted_max_written_offset = it->second->max_written_offset(); | 
 |         int64_t increase = length - trusted_max_written_offset; | 
 |         if (increase <= 0) | 
 |           return; | 
 |         if (!it->second->Grow(increase)) { | 
 |           new_msg_ptr->reset( | 
 |               new PpapiHostMsg_ResourceCall( | 
 |                   params, | 
 |                   PpapiHostMsg_FileIO_SetLength(-1))); | 
 |         } | 
 |         break; | 
 |       } | 
 |       case PpapiHostMsg_FileSystem_ReserveQuota::ID: { | 
 |         // Audit FileSystem ReserveQuota messages to make sure the plugin | 
 |         // reports accurate file sizes. | 
 |         int64_t amount = 0; | 
 |         FileGrowthMap file_growths; | 
 |         if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>( | 
 |                 nested_msg, &amount, &file_growths)) | 
 |           return; | 
 |  | 
 |         bool audit_failed = false; | 
 |         for (FileGrowthMap::iterator it = file_growths.begin(); | 
 |             it != file_growths.end(); ++it) { | 
 |           FileIOMap::iterator file_it = files_.find(it->first); | 
 |           if (file_it == files_.end()) | 
 |             continue; | 
 |           int64_t trusted_max_written_offset = | 
 |               file_it->second->max_written_offset(); | 
 |           if (trusted_max_written_offset > it->second.max_written_offset) { | 
 |             audit_failed = true; | 
 |             it->second.max_written_offset = trusted_max_written_offset; | 
 |           } | 
 |           if (it->second.append_mode_write_amount < 0) { | 
 |             audit_failed = true; | 
 |             it->second.append_mode_write_amount = 0; | 
 |           } | 
 |         } | 
 |         if (audit_failed) { | 
 |           new_msg_ptr->reset( | 
 |               new PpapiHostMsg_ResourceCall( | 
 |                   params, | 
 |                   PpapiHostMsg_FileSystem_ReserveQuota( | 
 |                       amount, file_growths))); | 
 |         } | 
 |         break; | 
 |       } | 
 |       case PpapiHostMsg_ResourceDestroyed::ID: { | 
 |         // Audit resource destroyed messages to release FileSystems. | 
 |         PP_Resource resource; | 
 |         if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>( | 
 |                 nested_msg, &resource)) | 
 |           return; | 
 |         FileSystemMap::iterator fs_it = file_systems_.find(resource); | 
 |         if (fs_it != file_systems_.end()) { | 
 |           delete fs_it->second; | 
 |           file_systems_.erase(fs_it); | 
 |         } | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | NaClMessageScanner::FileIO* NaClMessageScanner::GetFile( | 
 |     PP_Resource file_io) { | 
 |   FileIOMap::iterator it = files_.find(file_io); | 
 |   DCHECK(it != files_.end()); | 
 |   return it->second; | 
 | } | 
 |  | 
 | void NaClMessageScanner::AuditNestedMessage(PP_Resource resource, | 
 |                                             const IPC::Message& msg, | 
 |                                             SerializedHandle* handle) { | 
 |   switch (msg.type()) { | 
 |     case PpapiPluginMsg_FileIO_OpenReply::ID: { | 
 |       // A file that requires quota checking was opened. | 
 |       PP_Resource quota_file_system; | 
 |       int64_t max_written_offset = 0; | 
 |       if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>( | 
 |               msg, "a_file_system, &max_written_offset)) { | 
 |         if (quota_file_system) { | 
 |           // Look up the FileSystem by inserting a new one. If it was already | 
 |           // present, get the existing one, otherwise construct it. | 
 |           FileSystem* file_system = NULL; | 
 |           std::pair<FileSystemMap::iterator, bool> insert_result = | 
 |               file_systems_.insert(std::make_pair(quota_file_system, | 
 |                                                   file_system)); | 
 |           if (insert_result.second) | 
 |             insert_result.first->second = new FileSystem(); | 
 |           file_system = insert_result.first->second; | 
 |           // Create the FileIO. | 
 |           DCHECK(files_.find(resource) == files_.end()); | 
 |           files_.insert(std::make_pair( | 
 |               resource, | 
 |               new FileIO(file_system, max_written_offset))); | 
 |         } | 
 |       } | 
 |       break; | 
 |     } | 
 |     case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: { | 
 |       // The amount of reserved quota for a FileSystem was refreshed. | 
 |       int64_t amount = 0; | 
 |       FileSizeMap file_sizes; | 
 |       if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>( | 
 |           msg, &amount, &file_sizes)) { | 
 |         FileSystemMap::iterator it = file_systems_.find(resource); | 
 |         DCHECK(it != file_systems_.end()); | 
 |         it->second->UpdateReservedQuota(amount); | 
 |  | 
 |         FileSizeMap::const_iterator offset_it = file_sizes.begin(); | 
 |         for (; offset_it != file_sizes.end(); ++offset_it) { | 
 |           FileIOMap::iterator fio_it = files_.find(offset_it->first); | 
 |           DCHECK(fio_it != files_.end()); | 
 |           if (fio_it != files_.end()) | 
 |             fio_it->second->SetMaxWrittenOffset(offset_it->second); | 
 |         } | 
 |       } | 
 |       break; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace proxy | 
 | }  // namespace ppapi |