Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
4 : // Use of this source code is governed by a BSD-style license that can be
5 : // found in the LICENSE file.
6 :
7 : #include "base/shared_memory.h"
8 :
9 : #include <errno.h>
10 : #include <fcntl.h>
11 : #include <sys/mman.h>
12 : #include <sys/stat.h>
13 : #include <unistd.h>
14 :
15 : #include "base/file_util.h"
16 : #include "base/logging.h"
17 : #include "base/platform_thread.h"
18 : #include "base/string_util.h"
19 : #include "mozilla/UniquePtr.h"
20 :
21 : namespace base {
22 :
23 50 : SharedMemory::SharedMemory()
24 : : mapped_file_(-1),
25 : inode_(0),
26 : memory_(NULL),
27 : read_only_(false),
28 50 : max_size_(0) {
29 50 : }
30 :
31 74 : SharedMemory::~SharedMemory() {
32 37 : Close();
33 37 : }
34 :
35 37 : bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
36 37 : DCHECK(mapped_file_ == -1);
37 :
38 : struct stat st;
39 37 : if (fstat(handle.fd, &st) < 0) {
40 0 : return false;
41 : }
42 :
43 37 : mapped_file_ = handle.fd;
44 37 : inode_ = st.st_ino;
45 37 : read_only_ = read_only;
46 37 : return true;
47 : }
48 :
49 : // static
50 37 : bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
51 37 : return handle.fd >= 0;
52 : }
53 :
54 : // static
55 35 : SharedMemoryHandle SharedMemory::NULLHandle() {
56 35 : return SharedMemoryHandle();
57 : }
58 :
59 13 : bool SharedMemory::Create(const std::string &cname, bool read_only,
60 : bool open_existing, size_t size) {
61 13 : read_only_ = read_only;
62 :
63 26 : std::wstring name = UTF8ToWide(cname);
64 :
65 13 : int posix_flags = 0;
66 13 : posix_flags |= read_only ? O_RDONLY : O_RDWR;
67 13 : if (!open_existing || mapped_file_ <= 0)
68 13 : posix_flags |= O_CREAT;
69 :
70 13 : if (!CreateOrOpen(name, posix_flags, size))
71 0 : return false;
72 :
73 13 : max_size_ = size;
74 13 : return true;
75 : }
76 :
77 : // Our current implementation of shmem is with mmap()ing of files.
78 : // These files need to be deleted explicitly.
79 : // In practice this call is only needed for unit tests.
80 0 : bool SharedMemory::Delete(const std::wstring& name) {
81 0 : std::wstring mem_filename;
82 0 : if (FilenameForMemoryName(name, &mem_filename) == false)
83 0 : return false;
84 :
85 0 : FilePath path(WideToUTF8(mem_filename));
86 0 : if (file_util::PathExists(path)) {
87 0 : return file_util::Delete(path);
88 : }
89 :
90 : // Doesn't exist, so success.
91 0 : return true;
92 : }
93 :
94 0 : bool SharedMemory::Open(const std::wstring &name, bool read_only) {
95 0 : read_only_ = read_only;
96 :
97 0 : int posix_flags = 0;
98 0 : posix_flags |= read_only ? O_RDONLY : O_RDWR;
99 :
100 0 : return CreateOrOpen(name, posix_flags, 0);
101 : }
102 :
103 : // For the given shmem named |memname|, return a filename to mmap()
104 : // (and possibly create). Modifies |filename|. Return false on
105 : // error, or true of we are happy.
106 0 : bool SharedMemory::FilenameForMemoryName(const std::wstring &memname,
107 : std::wstring *filename) {
108 0 : std::wstring mem_filename;
109 :
110 : // mem_name will be used for a filename; make sure it doesn't
111 : // contain anything which will confuse us.
112 0 : DCHECK(memname.find_first_of(L"/") == std::string::npos);
113 0 : DCHECK(memname.find_first_of(L"\0") == std::string::npos);
114 :
115 0 : FilePath temp_dir;
116 0 : if (file_util::GetShmemTempDir(&temp_dir) == false)
117 0 : return false;
118 :
119 0 : mem_filename = UTF8ToWide(temp_dir.value());
120 0 : file_util::AppendToPath(&mem_filename, L"com.google.chrome.shmem." + memname);
121 0 : *filename = mem_filename;
122 0 : return true;
123 : }
124 :
125 : namespace {
126 :
127 : // A class to handle auto-closing of FILE*'s.
128 : class ScopedFILEClose {
129 : public:
130 13 : inline void operator()(FILE* x) const {
131 13 : if (x) {
132 13 : fclose(x);
133 : }
134 13 : }
135 : };
136 :
137 : typedef mozilla::UniquePtr<FILE, ScopedFILEClose> ScopedFILE;
138 :
139 : }
140 :
141 : // Chromium mostly only use the unique/private shmem as specified by
142 : // "name == L"". The exception is in the StatsTable.
143 : // TODO(jrg): there is no way to "clean up" all unused named shmem if
144 : // we restart from a crash. (That isn't a new problem, but it is a problem.)
145 : // In case we want to delete it later, it may be useful to save the value
146 : // of mem_filename after FilenameForMemoryName().
147 13 : bool SharedMemory::CreateOrOpen(const std::wstring &name,
148 : int posix_flags, size_t size) {
149 13 : DCHECK(mapped_file_ == -1);
150 :
151 26 : ScopedFILE file_closer;
152 : FILE *fp;
153 :
154 13 : if (name == L"") {
155 : // It doesn't make sense to have a read-only private piece of shmem
156 13 : DCHECK(posix_flags & (O_RDWR | O_WRONLY));
157 :
158 26 : FilePath path;
159 13 : fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
160 :
161 : // Deleting the file prevents anyone else from mapping it in
162 : // (making it private), and prevents the need for cleanup (once
163 : // the last fd is closed, it is truly freed).
164 13 : file_util::Delete(path);
165 : } else {
166 0 : std::wstring mem_filename;
167 0 : if (FilenameForMemoryName(name, &mem_filename) == false)
168 0 : return false;
169 :
170 0 : std::string mode;
171 0 : switch (posix_flags) {
172 : case (O_RDWR | O_CREAT):
173 : // Careful: "w+" will truncate if it already exists.
174 0 : mode = "a+";
175 0 : break;
176 : case O_RDWR:
177 0 : mode = "r+";
178 0 : break;
179 : case O_RDONLY:
180 0 : mode = "r";
181 0 : break;
182 : default:
183 0 : NOTIMPLEMENTED();
184 0 : break;
185 : }
186 :
187 0 : fp = file_util::OpenFile(mem_filename, mode.c_str());
188 : }
189 :
190 13 : if (fp == NULL)
191 0 : return false;
192 13 : file_closer.reset(fp); // close when we go out of scope
193 :
194 : // Make sure the (new) file is the right size.
195 : // According to the man page, "Use of truncate() to extend a file is
196 : // not portable."
197 13 : if (size && (posix_flags & (O_RDWR | O_CREAT))) {
198 : // Get current size.
199 : struct stat stat;
200 13 : if (fstat(fileno(fp), &stat) != 0)
201 0 : return false;
202 13 : size_t current_size = stat.st_size;
203 13 : if (current_size != size) {
204 13 : if (ftruncate(fileno(fp), size) != 0)
205 0 : return false;
206 13 : if (fseeko(fp, size, SEEK_SET) != 0)
207 0 : return false;
208 : }
209 : }
210 :
211 13 : mapped_file_ = dup(fileno(fp));
212 13 : DCHECK(mapped_file_ >= 0);
213 :
214 : struct stat st;
215 13 : if (fstat(mapped_file_, &st))
216 0 : NOTREACHED();
217 13 : inode_ = st.st_ino;
218 :
219 13 : return true;
220 : }
221 :
222 50 : bool SharedMemory::Map(size_t bytes) {
223 50 : if (mapped_file_ == -1)
224 0 : return false;
225 :
226 50 : memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
227 : MAP_SHARED, mapped_file_, 0);
228 :
229 50 : if (memory_)
230 50 : max_size_ = bytes;
231 :
232 50 : bool mmap_succeeded = (memory_ != (void*)-1);
233 50 : DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
234 50 : return mmap_succeeded;
235 : }
236 :
237 37 : bool SharedMemory::Unmap() {
238 37 : if (memory_ == NULL)
239 0 : return false;
240 :
241 37 : munmap(memory_, max_size_);
242 37 : memory_ = NULL;
243 37 : max_size_ = 0;
244 37 : return true;
245 : }
246 :
247 38 : bool SharedMemory::ShareToProcessCommon(ProcessId processId,
248 : SharedMemoryHandle *new_handle,
249 : bool close_self) {
250 38 : const int new_fd = dup(mapped_file_);
251 38 : DCHECK(new_fd >= -1);
252 38 : new_handle->fd = new_fd;
253 38 : new_handle->auto_close = true;
254 :
255 38 : if (close_self)
256 0 : Close();
257 :
258 38 : return true;
259 : }
260 :
261 :
262 42 : void SharedMemory::Close(bool unmap_view) {
263 42 : if (unmap_view) {
264 37 : Unmap();
265 : }
266 :
267 42 : if (mapped_file_ >= 0) {
268 42 : close(mapped_file_);
269 42 : mapped_file_ = -1;
270 : }
271 42 : }
272 :
273 : #ifdef ANDROID
274 : void SharedMemory::LockOrUnlockCommon(int function) {
275 : DCHECK(mapped_file_ >= 0);
276 : struct flock lockreq;
277 : lockreq.l_type = function;
278 : lockreq.l_whence = SEEK_SET;
279 : lockreq.l_start = 0;
280 : lockreq.l_len = 0;
281 : while (fcntl(mapped_file_, F_SETLKW, &lockreq) < 0) {
282 : if (errno == EINTR) {
283 : continue;
284 : } else if (errno == ENOLCK) {
285 : // temporary kernel resource exaustion
286 : PlatformThread::Sleep(500);
287 : continue;
288 : } else {
289 : NOTREACHED() << "lockf() failed."
290 : << " function:" << function
291 : << " fd:" << mapped_file_
292 : << " errno:" << errno
293 : << " msg:" << strerror(errno);
294 : }
295 : }
296 : }
297 :
298 : void SharedMemory::Lock() {
299 : LockOrUnlockCommon(F_WRLCK);
300 : }
301 :
302 : void SharedMemory::Unlock() {
303 : LockOrUnlockCommon(F_UNLCK);
304 : }
305 : #else
306 0 : void SharedMemory::LockOrUnlockCommon(int function) {
307 0 : DCHECK(mapped_file_ >= 0);
308 0 : while (lockf(mapped_file_, function, 0) < 0) {
309 0 : if (errno == EINTR) {
310 0 : continue;
311 0 : } else if (errno == ENOLCK) {
312 : // temporary kernel resource exaustion
313 0 : PlatformThread::Sleep(500);
314 0 : continue;
315 : } else {
316 0 : NOTREACHED() << "lockf() failed."
317 : << " function:" << function
318 : << " fd:" << mapped_file_
319 0 : << " errno:" << errno
320 0 : << " msg:" << strerror(errno);
321 : }
322 : }
323 0 : }
324 :
325 0 : void SharedMemory::Lock() {
326 0 : LockOrUnlockCommon(F_LOCK);
327 0 : }
328 :
329 0 : void SharedMemory::Unlock() {
330 0 : LockOrUnlockCommon(F_ULOCK);
331 0 : }
332 : #endif
333 :
334 0 : SharedMemoryHandle SharedMemory::handle() const {
335 0 : return FileDescriptor(mapped_file_, false);
336 : }
337 :
338 : } // namespace base
|