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.
5 #include "mojo/embedder/simple_platform_shared_buffer.h"
8 #include <stdio.h> // For |fileno()|.
9 #include <sys/mman.h> // For |mmap()|/|munmap()|.
11 #include <sys/types.h> // For |off_t|.
16 #include "base/file_util.h"
17 #include "base/files/file_path.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"
26 // We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
28 COMPILE_ASSERT(sizeof(size_t) <= sizeof(uint64_t), size_t_too_big
);
29 COMPILE_ASSERT(sizeof(off_t
) <= sizeof(uint64_t), off_t_too_big
);
34 // SimplePlatformSharedBuffer --------------------------------------------------
36 bool SimplePlatformSharedBuffer::Init() {
37 DCHECK(!handle_
.is_valid());
39 base::ThreadRestrictions::ScopedAllowIO allow_io
;
41 if (static_cast<uint64_t>(num_bytes_
) >
42 static_cast<uint64_t>(std::numeric_limits
<off_t
>::max())) {
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";
56 base::FilePath shared_buffer_file
;
57 base::ScopedFILE
fp(base::CreateAndOpenTemporaryFileInDir(
58 shared_buffer_dir
, &shared_buffer_file
));
60 LOG(ERROR
) << "Failed to create/open temporary file for shared memory";
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.
70 // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
71 base::ScopedFD
fd(dup(fileno(fp
.get())));
77 if (HANDLE_EINTR(ftruncate(fd
.get(), static_cast<off_t
>(num_bytes_
))) != 0) {
78 PLOG(ERROR
) << "ftruncate";
82 handle_
.reset(PlatformHandle(fd
.release()));
86 bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
87 ScopedPlatformHandle platform_handle
) {
88 DCHECK(!handle_
.is_valid());
90 if (static_cast<uint64_t>(num_bytes_
) >
91 static_cast<uint64_t>(std::numeric_limits
<off_t
>::max())) {
96 // Note: |fstat()| isn't interruptible.
97 if (fstat(platform_handle
.get().fd
, &sb
) != 0) {
98 PLOG(ERROR
) << "fstat";
102 if (!S_ISREG(sb
.st_mode
)) {
103 LOG(ERROR
) << "Platform handle not to a regular file";
107 if (sb
.st_size
!= static_cast<off_t
>(num_bytes_
)) {
108 LOG(ERROR
) << "Shared memory file has the wrong size";
112 // TODO(vtl): More checks?
114 handle_
= platform_handle
.Pass();
118 scoped_ptr
<PlatformSharedBufferMapping
> SimplePlatformSharedBuffer::MapImpl(
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
;
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()));
130 void* real_base
= mmap(NULL
,
132 PROT_READ
| PROT_WRITE
,
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
>();
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
));
149 // SimplePlatformSharedBufferMapping -------------------------------------------
151 void SimplePlatformSharedBufferMapping::Unmap() {
152 int result
= munmap(real_base_
, real_length_
);
153 PLOG_IF(ERROR
, result
!= 0) << "munmap";
156 } // namespace embedder