nacl_message_scanner.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/nacl_message_scanner.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ipc/ipc_message.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ipc/ipc_message_macros.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/ppapi_messages.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/resource_message_params.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/serialized_handle.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/serialized_var.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NaClDescImcShm;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace IPC {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Message;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ppapi::proxy::ResourceMessageReplyParams;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ppapi::proxy::SerializedHandle;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ppapi::proxy::SerializedVar;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef std::vector<SerializedHandle> Handles;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct ScanningResults {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanningResults() : handle_index(0), pp_resource(0) {}
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Vector to hold handles found in the message.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Handles handles;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Current handle index in the rewritten message. During the scan, it will be
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // be less than or equal to handles.size(). After the scan it should be equal.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int handle_index;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The rewritten message. This may be NULL, so all ScanParam overloads should
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // check for NULL before writing to it. In some cases, a ScanParam overload
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // may set this to NULL when it can determine that there are no parameters
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // that need conversion. (See the ResourceMessageReplyParams overload.)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<IPC::Message> new_msg;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Resource id for resource messages. Save this when scanning resource replies
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so when we audit the nested message, we know which resource it is for.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PP_Resource pp_resource;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Callback to receive the nested message in a resource message or reply.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Callback<void(PP_Resource, const IPC::Message&, SerializedHandle*)>
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      nested_msg_callback;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteHandle(int handle_index,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const SerializedHandle& handle,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 IPC::Message* msg) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SerializedHandle::WriteHeader(handle.header(), msg);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (handle.type() != SerializedHandle::INVALID) {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Now write the handle itself in POSIX style.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // See ParamTraits<FileDescriptor>::Read for where these values are read.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    msg->WriteBool(true);  // valid == true
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    msg->WriteInt(handle_index);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Define overloads for each kind of message parameter that requires special
65// handling. See ScanTuple for how these get used.
66
67// Overload to match SerializedHandle.
68void ScanParam(const SerializedHandle& handle, ScanningResults* results) {
69  results->handles.push_back(handle);
70  if (results->new_msg)
71    WriteHandle(results->handle_index++, handle, results->new_msg.get());
72}
73
74void HandleWriter(int* handle_index,
75                  IPC::Message* m,
76                  const SerializedHandle& handle) {
77  WriteHandle((*handle_index)++, handle, m);
78}
79
80// Overload to match SerializedVar, which can contain handles.
81void ScanParam(const SerializedVar& var, ScanningResults* results) {
82  std::vector<SerializedHandle*> var_handles = var.GetHandles();
83  // Copy any handles and then rewrite the message.
84  for (size_t i = 0; i < var_handles.size(); ++i)
85    results->handles.push_back(*var_handles[i]);
86  if (results->new_msg)
87    var.WriteDataToMessage(results->new_msg.get(),
88                           base::Bind(&HandleWriter, &results->handle_index));
89}
90
91// For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall,
92// the handles are carried inside the ResourceMessageReplyParams.
93// NOTE: We only intercept handles from host->NaCl. The only kind of
94//       ResourceMessageParams that travels this direction is
95//       ResourceMessageReplyParams, so that's the only one we need to handle.
96void ScanParam(const ResourceMessageReplyParams& params,
97               ScanningResults* results) {
98  results->pp_resource = params.pp_resource();
99  // If the resource reply params don't contain handles, NULL the new message
100  // pointer to cancel further rewriting.
101  // NOTE: This works because only handles currently need rewriting, and we
102  //       know at this point that this message has none.
103  if (params.handles().empty()) {
104    results->new_msg.reset(NULL);
105    return;
106  }
107
108  // If we need to rewrite the message, write everything before the handles
109  // (there's nothing after the handles).
110  if (results->new_msg) {
111    params.WriteReplyHeader(results->new_msg.get());
112    // IPC writes the vector length as an int before the contents of the
113    // vector.
114    results->new_msg->WriteInt(static_cast<int>(params.handles().size()));
115  }
116  for (Handles::const_iterator iter = params.handles().begin();
117       iter != params.handles().end();
118       ++iter) {
119    // ScanParam will write each handle to the new message, if necessary.
120    ScanParam(*iter, results);
121  }
122  // Tell ResourceMessageReplyParams that we have taken the handles, so it
123  // shouldn't close them. The NaCl runtime will take ownership of them.
124  params.ConsumeHandles();
125}
126
127// Overload to match nested messages. If we need to rewrite the message, write
128// the parameter.
129void ScanParam(const IPC::Message& param, ScanningResults* results) {
130  if (results->pp_resource && !results->nested_msg_callback.is_null()) {
131    SerializedHandle* handle = NULL;
132    if (results->handles.size() == 1)
133      handle = &results->handles[0];
134    results->nested_msg_callback.Run(results->pp_resource, param, handle);
135  }
136  if (results->new_msg)
137    IPC::WriteParam(results->new_msg.get(), param);
138}
139
140// Overload to match all other types. If we need to rewrite the message, write
141// the parameter.
142template <class T>
143void ScanParam(const T& param, ScanningResults* results) {
144  if (results->new_msg)
145    IPC::WriteParam(results->new_msg.get(), param);
146}
147
148// These just break apart the given tuple and run ScanParam over each param.
149// The idea is to scan elements in the tuple which require special handling,
150// and write them into the |results| struct.
151template <class A>
152void ScanTuple(const Tuple1<A>& t1, ScanningResults* results) {
153  ScanParam(t1.a, results);
154}
155template <class A, class B>
156void ScanTuple(const Tuple2<A, B>& t1, ScanningResults* results) {
157  ScanParam(t1.a, results);
158  ScanParam(t1.b, results);
159}
160template <class A, class B, class C>
161void ScanTuple(const Tuple3<A, B, C>& t1, ScanningResults* results) {
162  ScanParam(t1.a, results);
163  ScanParam(t1.b, results);
164  ScanParam(t1.c, results);
165}
166template <class A, class B, class C, class D>
167void ScanTuple(const Tuple4<A, B, C, D>& t1, ScanningResults* results) {
168  ScanParam(t1.a, results);
169  ScanParam(t1.b, results);
170  ScanParam(t1.c, results);
171  ScanParam(t1.d, results);
172}
173
174template <class MessageType>
175class MessageScannerImpl {
176 public:
177  explicit MessageScannerImpl(const IPC::Message* msg)
178      : msg_(static_cast<const MessageType*>(msg)) {
179  }
180  bool ScanMessage(ScanningResults* results) {
181    typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params;
182    if (!MessageType::Read(msg_, &params))
183      return false;
184    ScanTuple(params, results);
185    return true;
186  }
187
188  bool ScanReply(ScanningResults* results) {
189    typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple
190        params;
191    if (!MessageType::ReadReplyParam(msg_, &params))
192      return false;
193    // If we need to rewrite the message, write the message id first.
194    if (results->new_msg) {
195      results->new_msg->set_reply();
196      int id = IPC::SyncMessage::GetMessageId(*msg_);
197      results->new_msg->WriteInt(id);
198    }
199    ScanTuple(params, results);
200    return true;
201  }
202  // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever
203  //                 need to scan those.
204
205 private:
206  const MessageType* msg_;
207};
208
209}  // namespace
210
211#define CASE_FOR_MESSAGE(MESSAGE_TYPE) \
212      case MESSAGE_TYPE::ID: { \
213        MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
214        if (rewrite_msg) \
215          results.new_msg.reset( \
216              new IPC::Message(msg.routing_id(), msg.type(), \
217                               IPC::Message::PRIORITY_NORMAL)); \
218        if (!scanner.ScanMessage(&results)) \
219          return false; \
220        break; \
221      }
222#define CASE_FOR_REPLY(MESSAGE_TYPE) \
223      case MESSAGE_TYPE::ID: { \
224        MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
225        if (rewrite_msg) \
226          results.new_msg.reset( \
227              new IPC::Message(msg.routing_id(), msg.type(), \
228                               IPC::Message::PRIORITY_NORMAL)); \
229        if (!scanner.ScanReply(&results)) \
230          return false; \
231        break; \
232      }
233
234namespace ppapi {
235namespace proxy {
236
237class SerializedHandle;
238
239NaClMessageScanner::FileSystem::FileSystem()
240    : reserved_quota_(0) {
241}
242
243NaClMessageScanner::FileSystem::~FileSystem() {
244}
245
246bool NaClMessageScanner::FileSystem::UpdateReservedQuota(int64_t delta) {
247  base::AutoLock lock(lock_);
248  if (std::numeric_limits<int64_t>::max() - reserved_quota_ < delta)
249    return false;  // reserved_quota_ + delta would overflow.
250  if (reserved_quota_ + delta < 0)
251    return false;
252  reserved_quota_ += delta;
253  return true;
254}
255
256NaClMessageScanner::FileIO::FileIO(FileSystem* file_system,
257                                   int64_t max_written_offset)
258    : file_system_(file_system),
259      max_written_offset_(max_written_offset) {
260}
261
262NaClMessageScanner::FileIO::~FileIO() {
263}
264
265void NaClMessageScanner::FileIO::SetMaxWrittenOffset(
266    int64_t max_written_offset) {
267  base::AutoLock lock(lock_);
268  max_written_offset_ = max_written_offset;
269}
270
271bool NaClMessageScanner::FileIO::Grow(int64_t amount) {
272  base::AutoLock lock(lock_);
273  DCHECK(amount > 0);
274  if (!file_system_->UpdateReservedQuota(-amount))
275    return false;
276  max_written_offset_ += amount;
277  return true;
278}
279
280NaClMessageScanner::NaClMessageScanner() {
281}
282
283NaClMessageScanner::~NaClMessageScanner() {
284  for (FileSystemMap::iterator it = file_systems_.begin();
285      it != file_systems_.end(); ++it)
286    delete it->second;
287  for (FileIOMap::iterator it = files_.begin(); it != files_.end(); ++it)
288    delete it->second;
289}
290
291// Windows IPC differs from POSIX in that native handles are serialized in the
292// message body, rather than passed in a separate FileDescriptorSet. Therefore,
293// on Windows, any message containing handles must be rewritten in the POSIX
294// format before we can send it to the NaCl plugin.
295bool NaClMessageScanner::ScanMessage(
296    const IPC::Message& msg,
297    uint32_t type,
298    std::vector<SerializedHandle>* handles,
299    scoped_ptr<IPC::Message>* new_msg_ptr) {
300  DCHECK(handles);
301  DCHECK(handles->empty());
302  DCHECK(new_msg_ptr);
303  DCHECK(!new_msg_ptr->get());
304
305  bool rewrite_msg =
306#if defined(OS_WIN)
307      true;
308#else
309      false;
310#endif
311
312  // We can't always tell from the message ID if rewriting is needed. Therefore,
313  // scan any message types that might contain a handle. If we later determine
314  // that there are no handles, we can cancel the rewriting by clearing the
315  // results.new_msg pointer.
316  ScanningResults results;
317  results.nested_msg_callback =
318      base::Bind(&NaClMessageScanner::AuditNestedMessage,
319                 base::Unretained(this));
320  switch (type) {
321    CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
322    CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage)
323    CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply)
324    CASE_FOR_REPLY(PpapiHostMsg_OpenResource)
325    CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_Create)
326    CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer)
327    CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple)
328    CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall)
329    CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory)
330    default:
331      // Do nothing for messages we don't know.
332      break;
333  }
334
335  // Only messages containing handles need to be rewritten. If no handles are
336  // found, don't return the rewritten message either. This must be changed if
337  // we ever add new param types that also require rewriting.
338  if (!results.handles.empty()) {
339    handles->swap(results.handles);
340    *new_msg_ptr = results.new_msg.Pass();
341  }
342  return true;
343}
344
345void NaClMessageScanner::ScanUntrustedMessage(
346    const IPC::Message& untrusted_msg,
347    scoped_ptr<IPC::Message>* new_msg_ptr) {
348  // Audit FileIO and FileSystem messages to ensure that the plugin doesn't
349  // exceed its file quota. If we find the message is malformed, just pass it
350  // through - we only care about well formed messages to the host.
351  if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) {
352    ResourceMessageCallParams params;
353    IPC::Message nested_msg;
354    if (!UnpackMessage<PpapiHostMsg_ResourceCall>(
355            untrusted_msg, &params, &nested_msg))
356      return;
357
358    switch (nested_msg.type()) {
359      case PpapiHostMsg_FileIO_Close::ID: {
360        FileIOMap::iterator it = files_.find(params.pp_resource());
361        if (it == files_.end())
362          return;
363        // Audit FileIO Close messages to make sure the plugin reports an
364        // accurate file size.
365        FileGrowth file_growth;
366        if (!UnpackMessage<PpapiHostMsg_FileIO_Close>(
367                nested_msg, &file_growth))
368          return;
369
370        int64_t trusted_max_written_offset = it->second->max_written_offset();
371        delete it->second;
372        files_.erase(it);
373        // If the plugin is under-reporting, rewrite the message with the
374        // trusted value.
375        if (trusted_max_written_offset > file_growth.max_written_offset) {
376          new_msg_ptr->reset(
377              new PpapiHostMsg_ResourceCall(
378                  params,
379                  PpapiHostMsg_FileIO_Close(
380                      FileGrowth(trusted_max_written_offset, 0))));
381        }
382        break;
383      }
384      case PpapiHostMsg_FileIO_SetLength::ID: {
385        FileIOMap::iterator it = files_.find(params.pp_resource());
386        if (it == files_.end())
387          return;
388        // Audit FileIO SetLength messages to make sure the plugin is within
389        // the current quota reservation. In addition, deduct the file size
390        // increase from the quota reservation.
391        int64_t length = 0;
392        if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
393                nested_msg, &length))
394          return;
395
396        // Calculate file size increase, taking care to avoid overflows.
397        if (length < 0)
398          return;
399        int64_t trusted_max_written_offset = it->second->max_written_offset();
400        int64_t increase = length - trusted_max_written_offset;
401        if (increase <= 0)
402          return;
403        if (!it->second->Grow(increase)) {
404          new_msg_ptr->reset(
405              new PpapiHostMsg_ResourceCall(
406                  params,
407                  PpapiHostMsg_FileIO_SetLength(-1)));
408        }
409        break;
410      }
411      case PpapiHostMsg_FileSystem_ReserveQuota::ID: {
412        // Audit FileSystem ReserveQuota messages to make sure the plugin
413        // reports accurate file sizes.
414        int64_t amount = 0;
415        FileGrowthMap file_growths;
416        if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
417                nested_msg, &amount, &file_growths))
418          return;
419
420        bool audit_failed = false;
421        for (FileGrowthMap::iterator it = file_growths.begin();
422            it != file_growths.end(); ++it) {
423          FileIOMap::iterator file_it = files_.find(it->first);
424          if (file_it == files_.end())
425            continue;
426          int64_t trusted_max_written_offset =
427              file_it->second->max_written_offset();
428          if (trusted_max_written_offset > it->second.max_written_offset) {
429            audit_failed = true;
430            it->second.max_written_offset = trusted_max_written_offset;
431          }
432          if (it->second.append_mode_write_amount < 0) {
433            audit_failed = true;
434            it->second.append_mode_write_amount = 0;
435          }
436        }
437        if (audit_failed) {
438          new_msg_ptr->reset(
439              new PpapiHostMsg_ResourceCall(
440                  params,
441                  PpapiHostMsg_FileSystem_ReserveQuota(
442                      amount, file_growths)));
443        }
444        break;
445      }
446      case PpapiHostMsg_ResourceDestroyed::ID: {
447        // Audit resource destroyed messages to release FileSystems.
448        PP_Resource resource;
449        if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>(
450                nested_msg, &resource))
451          return;
452        FileSystemMap::iterator fs_it = file_systems_.find(resource);
453        if (fs_it != file_systems_.end()) {
454          delete fs_it->second;
455          file_systems_.erase(fs_it);
456        }
457        break;
458      }
459    }
460  }
461}
462
463NaClMessageScanner::FileIO* NaClMessageScanner::GetFile(
464    PP_Resource file_io) {
465  FileIOMap::iterator it = files_.find(file_io);
466  DCHECK(it != files_.end());
467  return it->second;
468}
469
470void NaClMessageScanner::AuditNestedMessage(PP_Resource resource,
471                                            const IPC::Message& msg,
472                                            SerializedHandle* handle) {
473  switch (msg.type()) {
474    case PpapiPluginMsg_FileIO_OpenReply::ID: {
475      // A file that requires quota checking was opened.
476      PP_Resource quota_file_system;
477      int64_t max_written_offset = 0;
478      if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>(
479              msg, &quota_file_system, &max_written_offset)) {
480        if (quota_file_system) {
481          // Look up the FileSystem by inserting a new one. If it was already
482          // present, get the existing one, otherwise construct it.
483          FileSystem* file_system = NULL;
484          std::pair<FileSystemMap::iterator, bool> insert_result =
485              file_systems_.insert(std::make_pair(quota_file_system,
486                                                  file_system));
487          if (insert_result.second)
488            insert_result.first->second = new FileSystem();
489          file_system = insert_result.first->second;
490          // Create the FileIO.
491          DCHECK(files_.find(resource) == files_.end());
492          files_.insert(std::make_pair(
493              resource,
494              new FileIO(file_system, max_written_offset)));
495        }
496      }
497      break;
498    }
499    case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: {
500      // The amount of reserved quota for a FileSystem was refreshed.
501      int64_t amount = 0;
502      FileSizeMap file_sizes;
503      if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(
504          msg, &amount, &file_sizes)) {
505        FileSystemMap::iterator it = file_systems_.find(resource);
506        DCHECK(it != file_systems_.end());
507        it->second->UpdateReservedQuota(amount);
508
509        FileSizeMap::const_iterator offset_it = file_sizes.begin();
510        for (; offset_it != file_sizes.end(); ++offset_it) {
511          FileIOMap::iterator fio_it = files_.find(offset_it->first);
512          DCHECK(fio_it != files_.end());
513          if (fio_it != files_.end())
514            fio_it->second->SetMaxWrittenOffset(offset_it->second);
515        }
516      }
517      break;
518    }
519  }
520}
521
522}  // namespace proxy
523}  // namespace ppapi
524