• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "mojo/embedder/simple_platform_shared_buffer.h"
6 
7 #include <stdint.h>
8 #include <stdio.h>     // For |fileno()|.
9 #include <sys/mman.h>  // For |mmap()|/|munmap()|.
10 #include <sys/stat.h>
11 #include <sys/types.h>  // For |off_t|.
12 #include <unistd.h>
13 
14 #include <limits>
15 
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/files/scoped_file.h"
19 #include "base/logging.h"
20 #include "base/macros.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/sys_info.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "mojo/embedder/platform_handle.h"
25 
26 // We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
27 // |uint64_t|.
28 static_assert(sizeof(size_t) <= sizeof(uint64_t), "size_t too big");
29 static_assert(sizeof(off_t) <= sizeof(uint64_t), "off_t too big");
30 
31 namespace mojo {
32 namespace embedder {
33 
34 // SimplePlatformSharedBuffer --------------------------------------------------
35 
Init()36 bool SimplePlatformSharedBuffer::Init() {
37   DCHECK(!handle_.is_valid());
38 
39   base::ThreadRestrictions::ScopedAllowIO allow_io;
40 
41   if (static_cast<uint64_t>(num_bytes_) >
42       static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
43     return false;
44   }
45 
46   // TODO(vtl): This is stupid. The implementation of
47   // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
48   // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
49   // can own. (base/memory/shared_memory_posix.cc does this too, with more
50   // |fstat()|s thrown in for good measure.)
51   base::FilePath shared_buffer_dir;
52   if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
53     LOG(ERROR) << "Failed to get temporary directory for shared memory";
54     return false;
55   }
56   base::FilePath shared_buffer_file;
57   base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
58       shared_buffer_dir, &shared_buffer_file));
59   if (!fp) {
60     LOG(ERROR) << "Failed to create/open temporary file for shared memory";
61     return false;
62   }
63   // Note: |unlink()| is not interruptible.
64   if (unlink(shared_buffer_file.value().c_str()) != 0) {
65     PLOG(WARNING) << "unlink";
66     // This isn't "fatal" (e.g., someone else may have unlinked the file first),
67     // so we may as well continue.
68   }
69 
70   // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
71   base::ScopedFD fd(dup(fileno(fp.get())));
72   if (!fd.is_valid()) {
73     PLOG(ERROR) << "dup";
74     return false;
75   }
76 
77   if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(num_bytes_))) != 0) {
78     PLOG(ERROR) << "ftruncate";
79     return false;
80   }
81 
82   handle_.reset(PlatformHandle(fd.release()));
83   return true;
84 }
85 
InitFromPlatformHandle(ScopedPlatformHandle platform_handle)86 bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
87     ScopedPlatformHandle platform_handle) {
88   DCHECK(!handle_.is_valid());
89 
90   if (static_cast<uint64_t>(num_bytes_) >
91       static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
92     return false;
93   }
94 
95   struct stat sb = {};
96   // Note: |fstat()| isn't interruptible.
97   if (fstat(platform_handle.get().fd, &sb) != 0) {
98     PLOG(ERROR) << "fstat";
99     return false;
100   }
101 
102   if (!S_ISREG(sb.st_mode)) {
103     LOG(ERROR) << "Platform handle not to a regular file";
104     return false;
105   }
106 
107   if (sb.st_size != static_cast<off_t>(num_bytes_)) {
108     LOG(ERROR) << "Shared memory file has the wrong size";
109     return false;
110   }
111 
112   // TODO(vtl): More checks?
113 
114   handle_ = platform_handle.Pass();
115   return true;
116 }
117 
MapImpl(size_t offset,size_t length)118 scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapImpl(
119     size_t offset,
120     size_t length) {
121   size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
122   size_t real_offset = offset - offset_rounding;
123   size_t real_length = length + offset_rounding;
124 
125   // This should hold (since we checked |num_bytes| versus the maximum value of
126   // |off_t| on creation, but it never hurts to be paranoid.
127   DCHECK_LE(static_cast<uint64_t>(real_offset),
128             static_cast<uint64_t>(std::numeric_limits<off_t>::max()));
129 
130   void* real_base = mmap(nullptr,
131                          real_length,
132                          PROT_READ | PROT_WRITE,
133                          MAP_SHARED,
134                          handle_.get().fd,
135                          static_cast<off_t>(real_offset));
136   // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
137   // return null either.
138   if (real_base == MAP_FAILED || !real_base) {
139     PLOG(ERROR) << "mmap";
140     return scoped_ptr<PlatformSharedBufferMapping>();
141   }
142 
143   void* base = static_cast<char*>(real_base) + offset_rounding;
144   return scoped_ptr<PlatformSharedBufferMapping>(
145       new SimplePlatformSharedBufferMapping(
146           base, length, real_base, real_length));
147 }
148 
149 // SimplePlatformSharedBufferMapping -------------------------------------------
150 
Unmap()151 void SimplePlatformSharedBufferMapping::Unmap() {
152   int result = munmap(real_base_, real_length_);
153   PLOG_IF(ERROR, result != 0) << "munmap";
154 }
155 
156 }  // namespace embedder
157 }  // namespace mojo
158