OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/shared_memory.h" | 5 #include "base/shared_memory.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <sys/mman.h> | 9 #include <sys/mman.h> |
10 | 10 |
| 11 #include "base/file_util.h" |
11 #include "base/logging.h" | 12 #include "base/logging.h" |
12 #include "base/string_util.h" | 13 #include "base/string_util.h" |
13 | 14 |
14 namespace base { | 15 namespace base { |
15 | 16 |
16 namespace { | 17 namespace { |
17 // Paranoia. Semaphores and shared memory segments should live in different | 18 // Paranoia. Semaphores and shared memory segments should live in different |
18 // namespaces, but who knows what's out there. | 19 // namespaces, but who knows what's out there. |
19 const char kSemaphoreSuffix[] = "-sem"; | 20 const char kSemaphoreSuffix[] = "-sem"; |
20 } | 21 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 | 56 |
56 bool SharedMemory::Create(const std::wstring &name, bool read_only, | 57 bool SharedMemory::Create(const std::wstring &name, bool read_only, |
57 bool open_existing, size_t size) { | 58 bool open_existing, size_t size) { |
58 read_only_ = read_only; | 59 read_only_ = read_only; |
59 | 60 |
60 int posix_flags = 0; | 61 int posix_flags = 0; |
61 posix_flags |= read_only ? O_RDONLY : O_RDWR; | 62 posix_flags |= read_only ? O_RDONLY : O_RDWR; |
62 if (!open_existing || mapped_file_ <= 0) | 63 if (!open_existing || mapped_file_ <= 0) |
63 posix_flags |= O_CREAT; | 64 posix_flags |= O_CREAT; |
64 | 65 |
65 if (!CreateOrOpen(name, posix_flags)) | 66 if (!CreateOrOpen(name, posix_flags, size)) |
66 return false; | 67 return false; |
67 | 68 |
68 if (ftruncate(mapped_file_, size) != 0) { | |
69 Close(); | |
70 return false; | |
71 } | |
72 | |
73 max_size_ = size; | 69 max_size_ = size; |
74 return true; | 70 return true; |
75 } | 71 } |
76 | 72 |
| 73 // Our current implementation of shmem is with mmap()ing of files. |
| 74 // These files need to be deleted explicitly. |
| 75 // In practice this call is only needed for unit tests. |
| 76 bool SharedMemory::Delete(const std::wstring& name) { |
| 77 std::wstring mem_filename; |
| 78 if (FilenameForMemoryName(name, &mem_filename) == false) |
| 79 return false; |
| 80 |
| 81 FilePath path(WideToUTF8(mem_filename)); |
| 82 if (file_util::PathExists(path)) { |
| 83 return file_util::Delete(path, false); |
| 84 } |
| 85 |
| 86 // Doesn't exist, so success. |
| 87 return true; |
| 88 } |
| 89 |
77 bool SharedMemory::Open(const std::wstring &name, bool read_only) { | 90 bool SharedMemory::Open(const std::wstring &name, bool read_only) { |
78 read_only_ = read_only; | 91 read_only_ = read_only; |
79 | 92 |
80 int posix_flags = 0; | 93 int posix_flags = 0; |
81 posix_flags |= read_only ? O_RDONLY : O_RDWR; | 94 posix_flags |= read_only ? O_RDONLY : O_RDWR; |
82 | 95 |
83 return CreateOrOpen(name, posix_flags); | 96 return CreateOrOpen(name, posix_flags, 0); |
84 } | 97 } |
85 | 98 |
86 bool SharedMemory::CreateOrOpen(const std::wstring &name, int posix_flags) { | 99 // For the given shmem named |memname|, return a filename to mmap() |
| 100 // (and possibly create). Modifies |filename|. Return false on |
| 101 // error, or true of we are happy. |
| 102 bool SharedMemory::FilenameForMemoryName(const std::wstring &memname, |
| 103 std::wstring *filename) { |
| 104 std::wstring mem_filename; |
| 105 |
| 106 // mem_name will be used for a filename; make sure it doesn't |
| 107 // contain anything which will confuse us. |
| 108 DCHECK(memname.find_first_of(L"/") == std::string::npos); |
| 109 DCHECK(memname.find_first_of(L"\0") == std::string::npos); |
| 110 |
| 111 FilePath temp_dir; |
| 112 if (file_util::GetShmemTempDir(&temp_dir) == false) |
| 113 return false; |
| 114 |
| 115 mem_filename = UTF8ToWide(temp_dir.value()); |
| 116 file_util::AppendToPath(&mem_filename, L"com.google.chrome.shmem." + memname); |
| 117 *filename = mem_filename; |
| 118 return true; |
| 119 } |
| 120 |
| 121 // Current expectation is that Cromium only really needs |
| 122 // unique/private shmem as specified by "name == L"". |
| 123 // TODO(port): confirm that assumption. |
| 124 // TODO(jrg): there is no way to "clean up" all unused named shmem if |
| 125 // we restart from a crash. (That isn't a new problem, but it is a problem.) |
| 126 bool SharedMemory::CreateOrOpen(const std::wstring &name, |
| 127 int posix_flags, size_t size) { |
87 DCHECK(mapped_file_ == -1); | 128 DCHECK(mapped_file_ == -1); |
88 | 129 |
89 name_ = L"/" + name; | 130 file_util::ScopedFILE file_closer; |
| 131 FILE *fp; |
90 | 132 |
91 mode_t posix_mode = S_IRUSR | S_IWUSR; // owner read/write | 133 if (name == L"") { |
92 std::string posix_name(WideToUTF8(name_)); | 134 // It doesn't make sense to have a read-only private piece of shmem |
93 mapped_file_ = shm_open(posix_name.c_str(), posix_flags, posix_mode); | 135 DCHECK(posix_flags & (O_RDWR | O_WRONLY)); |
94 if (mapped_file_ < 0) | 136 |
| 137 FilePath path; |
| 138 fp = file_util::CreateAndOpenTemporaryShmemFile(&path); |
| 139 name_ = UTF8ToWide(path.value()); |
| 140 |
| 141 // Deleting the file prevents anyone else from mapping it in |
| 142 // (making it private), and prevents the need for cleanup (once |
| 143 // the last fd is closed, it is truly freed). |
| 144 file_util::Delete(path, false); |
| 145 } else { |
| 146 std::wstring mem_filename; |
| 147 if (FilenameForMemoryName(name, &mem_filename) == false) |
| 148 return false; |
| 149 |
| 150 name_ = mem_filename; |
| 151 std::string mode; |
| 152 switch (posix_flags) { |
| 153 case (O_RDWR | O_CREAT): |
| 154 // Careful: "w+" will truncate if it already exists. |
| 155 mode = "a+"; |
| 156 break; |
| 157 case O_RDWR: |
| 158 mode = "r+"; |
| 159 break; |
| 160 case O_RDONLY: |
| 161 mode = "r"; |
| 162 break; |
| 163 default: |
| 164 NOTIMPLEMENTED(); |
| 165 break; |
| 166 } |
| 167 |
| 168 fp = file_util::OpenFile(mem_filename, mode.c_str()); |
| 169 } |
| 170 |
| 171 if (fp == NULL) |
95 return false; | 172 return false; |
| 173 file_closer.reset(fp); // close when we go out of scope |
96 | 174 |
97 posix_name += kSemaphoreSuffix; | 175 // Make sure the (new) file is the right size. |
98 lock_ = sem_open(posix_name.c_str(), O_CREAT, posix_mode, 1); | 176 // According to the man page, "Use of truncate() to extend a file is |
| 177 // not portable." |
| 178 if (size && (posix_flags & (O_RDWR | O_CREAT))) { |
| 179 // Get current size. |
| 180 size_t current_size = 0; |
| 181 struct stat stat; |
| 182 if (fstat(fileno(fp), &stat) != 0) |
| 183 return false; |
| 184 current_size = stat.st_size; |
| 185 // Possibly grow. |
| 186 if (current_size < size) { |
| 187 if (fseeko(fp, current_size, SEEK_SET) != 0) |
| 188 return false; |
| 189 size_t writesize = size - current_size; |
| 190 scoped_array<char> buf(new char[writesize]); |
| 191 memset(buf.get(), 0, writesize); |
| 192 if (fwrite(buf.get(), 1, writesize, fp) != writesize) { |
| 193 return false; |
| 194 } |
| 195 if (fflush(fp) != 0) |
| 196 return false; |
| 197 } else if (current_size > size) { |
| 198 // possibly shrink. |
| 199 if ((ftruncate(fileno(fp), size) != 0) || |
| 200 (fflush(fp) != 0)) { |
| 201 return false; |
| 202 } |
| 203 } |
| 204 } |
| 205 |
| 206 mapped_file_ = dup(fileno(fp)); |
| 207 DCHECK(mapped_file_ >= 0); |
| 208 |
| 209 mode_t posix_mode = S_IRUSR | S_IWUSR; // owner read/write |
| 210 // name_ that includes a tmpdir easily exceeds SEM_NAME_LEN, |
| 211 // so we cannot use it directly as the basis for a sem_name_. |
| 212 sem_name_ = WideToUTF8(name) + kSemaphoreSuffix; |
| 213 lock_ = sem_open(sem_name_.c_str(), O_CREAT, posix_mode, 1); |
99 if (lock_ == SEM_FAILED) { | 214 if (lock_ == SEM_FAILED) { |
100 close(mapped_file_); | 215 close(mapped_file_); |
101 mapped_file_ = -1; | 216 mapped_file_ = -1; |
102 shm_unlink(posix_name.c_str()); | |
103 lock_ = NULL; | 217 lock_ = NULL; |
104 return false; | 218 return false; |
105 } | 219 } |
106 | 220 |
107 return true; | 221 return true; |
108 } | 222 } |
109 | 223 |
110 bool SharedMemory::Map(size_t bytes) { | 224 bool SharedMemory::Map(size_t bytes) { |
111 if (mapped_file_ == -1) | 225 if (mapped_file_ == -1) |
112 return false; | 226 return false; |
(...skipping 27 matching lines...) Expand all Loading... |
140 } | 254 } |
141 | 255 |
142 | 256 |
143 void SharedMemory::Close() { | 257 void SharedMemory::Close() { |
144 | 258 |
145 Unmap(); | 259 Unmap(); |
146 | 260 |
147 std::string posix_name(WideToUTF8(name_)); | 261 std::string posix_name(WideToUTF8(name_)); |
148 if (mapped_file_ > 0) { | 262 if (mapped_file_ > 0) { |
149 close(mapped_file_); | 263 close(mapped_file_); |
150 shm_unlink(posix_name.c_str()); | |
151 mapped_file_ = -1; | 264 mapped_file_ = -1; |
152 } | 265 } |
153 | 266 |
154 if (lock_) { | 267 if (lock_) { |
155 posix_name += kSemaphoreSuffix; | 268 sem_unlink(sem_name_.c_str()); |
156 sem_unlink(posix_name.c_str()); | |
157 lock_ = NULL; | 269 lock_ = NULL; |
158 } | 270 } |
159 } | 271 } |
160 | 272 |
161 void SharedMemory::Lock() { | 273 void SharedMemory::Lock() { |
162 DCHECK(lock_ != NULL); | 274 DCHECK(lock_ != NULL); |
163 while(sem_wait(lock_) < 0) { | 275 while(sem_wait(lock_) < 0) { |
164 DCHECK(errno == EAGAIN || errno == EINTR); | 276 DCHECK(errno == EAGAIN || errno == EINTR); |
165 } | 277 } |
166 } | 278 } |
167 | 279 |
168 void SharedMemory::Unlock() { | 280 void SharedMemory::Unlock() { |
169 DCHECK(lock_ != NULL); | 281 DCHECK(lock_ != NULL); |
170 int result = sem_post(lock_); | 282 int result = sem_post(lock_); |
171 DCHECK(result == 0); | 283 DCHECK(result == 0); |
172 } | 284 } |
173 | 285 |
174 } // namespace base | 286 } // namespace base |
OLD | NEW |