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