summaryrefslogtreecommitdiffstats
path: root/mojo/system/raw_shared_buffer_posix.cc
blob: 3ce50f040b707c4d823d2dca731af36671c5a2b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2014 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 "mojo/system/raw_shared_buffer.h"

#include <stdint.h>
#include <stdio.h>  // For |fileno()|.
#include <sys/mman.h>  // For |mmap()|/|munmap()|.
#include <sys/types.h>  // For |off_t|.
#include <unistd.h>

#include <limits>

#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sys_info.h"
#include "base/threading/thread_restrictions.h"
#include "mojo/embedder/platform_handle.h"

// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
// |uint64_t|.
COMPILE_ASSERT(sizeof(size_t) <= sizeof(uint64_t), size_t_too_big);
COMPILE_ASSERT(sizeof(off_t) <= sizeof(uint64_t), off_t_too_big);

namespace mojo {
namespace system {

// RawSharedBuffer -------------------------------------------------------------

bool RawSharedBuffer::InitNoLock() {
  DCHECK(!handle_.is_valid());

  base::ThreadRestrictions::ScopedAllowIO allow_io;

  if (static_cast<uint64_t>(num_bytes_) >
          static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
    return false;
  }

  // TODO(vtl): This is stupid. The implementation of
  // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
  // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
  // can own. (base/memory/shared_memory_posix.cc does this too, with more
  // |fstat()|s thrown in for good measure.)
  base::FilePath shared_buffer_dir;
  if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
    LOG(ERROR) << "Failed to get temporary directory for shared memory";
    return false;
  }
  base::FilePath shared_buffer_file;
  base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
      shared_buffer_dir, &shared_buffer_file));
  if (!fp) {
    LOG(ERROR) << "Failed to create/open temporary file for shared memory";
    return false;
  }
  // Note: |unlink()| is not interruptible.
  if (unlink(shared_buffer_file.value().c_str()) != 0) {
    PLOG(WARNING) << "unlink";
    // This isn't "fatal" (e.g., someone else may have unlinked the file first),
    // so we may as well continue.
  }

  // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
  base::ScopedFD fd(dup(fileno(fp.get())));
  if (!fd.is_valid()) {
    PLOG(ERROR) << "dup";
    return false;
  }

  if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(num_bytes_))) != 0) {
    PLOG(ERROR) << "ftruncate";
    return false;
  }

  handle_.reset(embedder::PlatformHandle(fd.release()));
  return true;
}

scoped_ptr<RawSharedBufferMapping> RawSharedBuffer::MapImplNoLock(
    size_t offset,
    size_t length) {
  lock_.AssertAcquired();

  size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
  size_t real_offset = offset - offset_rounding;
  size_t real_length = length + offset_rounding;

  // This should hold (since we checked |num_bytes| versus the maximum value of
  // |off_t| on creation, but it never hurts to be paranoid.
  DCHECK_LE(static_cast<uint64_t>(real_offset),
            static_cast<uint64_t>(std::numeric_limits<off_t>::max()));

  void* real_base = mmap(NULL, real_length, PROT_READ | PROT_WRITE, MAP_SHARED,
                         handle_.get().fd, static_cast<off_t>(real_offset));
  // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
  // return null either.
  if (real_base == MAP_FAILED || !real_base) {
    PLOG(ERROR) << "mmap";
    return scoped_ptr<RawSharedBufferMapping>();
  }

  void* base = static_cast<char*>(real_base) + offset_rounding;
  return make_scoped_ptr(
      new RawSharedBufferMapping(base, length, real_base, real_length));
}

// RawSharedBufferMapping ------------------------------------------------------

void RawSharedBufferMapping::Unmap() {
  int result = munmap(real_base_, real_length_);
  PLOG_IF(ERROR, result != 0) << "munmap";
}

}  // namespace system
}  // namespace mojo