1// Copyright 2014 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 "ppapi/proxy/file_mapping_resource.h"
6
7#include "base/bind.h"
8#include "base/numerics/safe_conversions.h"
9#include "base/task_runner_util.h"
10#include "ppapi/c/pp_errors.h"
11#include "ppapi/shared_impl/tracked_callback.h"
12#include "ppapi/shared_impl/var.h"
13#include "ppapi/thunk/enter.h"
14#include "ppapi/thunk/ppb_file_io_api.h"
15
16namespace ppapi {
17namespace proxy {
18
19FileMappingResource::FileMappingResource(Connection connection,
20                                         PP_Instance instance)
21    : PluginResource(connection, instance) {
22}
23
24FileMappingResource::~FileMappingResource() {
25}
26
27thunk::PPB_FileMapping_API* FileMappingResource::AsPPB_FileMapping_API() {
28  return this;
29}
30
31int32_t FileMappingResource::Map(PP_Instance /* instance */,
32                                 PP_Resource file_io,
33                                 int64_t length,
34                                 uint32_t protection,
35                                 uint32_t flags,
36                                 int64_t offset,
37                                 void** address,
38                                 scoped_refptr<TrackedCallback> callback) {
39  thunk::EnterResourceNoLock<thunk::PPB_FileIO_API> enter(file_io, true);
40  if (enter.failed())
41    return PP_ERROR_BADARGUMENT;
42  FileIOResource* file_io_resource =
43      static_cast<FileIOResource*>(enter.object());
44  scoped_refptr<FileIOResource::FileHolder> file_holder =
45      file_io_resource->file_holder();
46  if (!FileIOResource::FileHolder::IsValid(file_holder))
47    return PP_ERROR_FAILED;
48  if (length < 0 || offset < 0 ||
49      !base::IsValueInRangeForNumericType<off_t>(offset)) {
50    return PP_ERROR_BADARGUMENT;
51  }
52  if (!base::IsValueInRangeForNumericType<size_t>(length)) {
53    return PP_ERROR_NOMEMORY;
54  }
55
56  // Ensure any bits we don't recognize are zero.
57  if (protection &
58      ~(PP_FILEMAPPROTECTION_READ | PP_FILEMAPPROTECTION_WRITE)) {
59    return PP_ERROR_BADARGUMENT;
60  }
61  if (flags &
62      ~(PP_FILEMAPFLAG_SHARED | PP_FILEMAPFLAG_PRIVATE |
63        PP_FILEMAPFLAG_FIXED)) {
64    return PP_ERROR_BADARGUMENT;
65  }
66  // Ensure at least one of SHARED and PRIVATE is set.
67  if (!(flags & (PP_FILEMAPFLAG_SHARED | PP_FILEMAPFLAG_PRIVATE)))
68    return PP_ERROR_BADARGUMENT;
69  // Ensure at most one of SHARED and PRIVATE is set.
70  if ((flags & PP_FILEMAPFLAG_SHARED) &&
71      (flags & PP_FILEMAPFLAG_PRIVATE)) {
72    return PP_ERROR_BADARGUMENT;
73  }
74  if (!address)
75    return PP_ERROR_BADARGUMENT;
76
77  base::Callback<MapResult()> map_cb(
78      base::Bind(&FileMappingResource::DoMapBlocking, file_holder, *address,
79                 length, protection, flags, offset));
80  if (callback->is_blocking()) {
81    // The plugin could release its reference to this instance when we release
82    // the proxy lock below.
83    scoped_refptr<FileMappingResource> protect(this);
84    MapResult map_result;
85    {
86      // Release the proxy lock while making a potentially slow file call.
87      ProxyAutoUnlock unlock;
88      map_result = map_cb.Run();
89    }
90    OnMapCompleted(address, length, callback, map_result);
91    return map_result.result;
92  } else {
93    base::PostTaskAndReplyWithResult(
94      PpapiGlobals::Get()->GetFileTaskRunner(),
95      FROM_HERE,
96      map_cb,
97      RunWhileLocked(Bind(&FileMappingResource::OnMapCompleted,
98                          this,
99                          base::Unretained(address),
100                          length,
101                          callback)));
102    return PP_OK_COMPLETIONPENDING;
103  }
104}
105
106int32_t FileMappingResource::Unmap(PP_Instance /* instance */,
107                                   const void* address,
108                                   int64_t length,
109                                   scoped_refptr<TrackedCallback> callback) {
110  if (!address)
111    return PP_ERROR_BADARGUMENT;
112  if (!base::IsValueInRangeForNumericType<size_t>(length))
113    return PP_ERROR_BADARGUMENT;
114
115  base::Callback<int32_t()> unmap_cb(
116      base::Bind(&FileMappingResource::DoUnmapBlocking, address, length));
117  if (callback->is_blocking()) {
118    // Release the proxy lock while making a potentially slow file call.
119    ProxyAutoUnlock unlock;
120    return unmap_cb.Run();
121  } else {
122    base::PostTaskAndReplyWithResult(
123      PpapiGlobals::Get()->GetFileTaskRunner(),
124      FROM_HERE,
125      unmap_cb,
126      RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
127    return PP_OK_COMPLETIONPENDING;
128  }
129}
130
131int64_t FileMappingResource::GetMapPageSize(PP_Instance /* instance */) {
132  return DoGetMapPageSize();
133}
134
135void FileMappingResource::OnMapCompleted(
136    void** mapped_address_out_param,
137    int64_t length,
138    scoped_refptr<TrackedCallback> callback,
139    const MapResult& map_result) {
140  if (callback->aborted()) {
141    if (map_result.result == PP_OK) {
142      // If the Map operation was successful, we need to Unmap to avoid leaks.
143      // The plugin won't get the address, so doesn't have a chance to do the
144      // Unmap.
145      PpapiGlobals::Get()->GetFileTaskRunner()->PostTask(
146          FROM_HERE,
147          base::Bind(base::IgnoreResult(&FileMappingResource::DoUnmapBlocking),
148                     map_result.address,
149                     length));
150    }
151    return;
152  }
153  if (map_result.result == PP_OK)
154    *mapped_address_out_param = map_result.address;
155  if (!callback->is_blocking())
156    callback->Run(map_result.result);
157}
158
159}  // namespace proxy
160}  // namespace ppapi
161