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