Branch data Line data Source code
1 : : // Copyright (c) 2011 The LevelDB 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. See the AUTHORS file for names of contributors.
4 : :
5 : : #include <dirent.h>
6 : : #include <fcntl.h>
7 : : #include <pthread.h>
8 : : #include <sys/mman.h>
9 : : #include <sys/resource.h>
10 : : #include <sys/stat.h>
11 : : #include <sys/time.h>
12 : : #include <sys/types.h>
13 : : #include <unistd.h>
14 : :
15 : : #include <atomic>
16 : : #include <cerrno>
17 : : #include <cstddef>
18 : : #include <cstdint>
19 : : #include <cstdio>
20 : : #include <cstdlib>
21 : : #include <cstring>
22 : : #include <limits>
23 : : #include <queue>
24 : : #include <set>
25 : : #include <string>
26 : : #include <thread>
27 : : #include <type_traits>
28 : : #include <utility>
29 : :
30 : : #include "leveldb/env.h"
31 : : #include "leveldb/slice.h"
32 : : #include "leveldb/status.h"
33 : : #include "port/port.h"
34 : : #include "port/thread_annotations.h"
35 : : #include "util/env_posix_test_helper.h"
36 : : #include "util/posix_logger.h"
37 : :
38 : : namespace leveldb {
39 : :
40 : : namespace {
41 : :
42 : : // Set by EnvPosixTestHelper::SetReadOnlyMMapLimit() and MaxOpenFiles().
43 : : int g_open_read_only_file_limit = -1;
44 : :
45 : : // Up to 4096 mmap regions for 64-bit binaries; none for 32-bit.
46 : : constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 4096 : 0;
47 : :
48 : : // Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit().
49 : : int g_mmap_limit = kDefaultMmapLimit;
50 : :
51 : : // Common flags defined for all posix open operations
52 : : #if HAVE_O_CLOEXEC
53 : : constexpr const int kOpenBaseFlags = O_CLOEXEC;
54 : : #else
55 : : constexpr const int kOpenBaseFlags = 0;
56 : : #endif // defined(HAVE_O_CLOEXEC)
57 : :
58 : : constexpr const size_t kWritableFileBufferSize = 65536;
59 : :
60 : 2487 : Status PosixError(const std::string& context, int error_number) {
61 [ + + ]: 2487 : if (error_number == ENOENT) {
62 [ - + ]: 5 : return Status::NotFound(context, std::strerror(error_number));
63 : : } else {
64 [ - + ]: 2482 : return Status::IOError(context, std::strerror(error_number));
65 : : }
66 : : }
67 : :
68 : : // Helper class to limit resource usage to avoid exhaustion.
69 : : // Currently used to limit read-only file descriptors and mmap file usage
70 : : // so that we do not run out of file descriptors or virtual memory, or run into
71 : : // kernel performance problems for very large databases.
72 : : class Limiter {
73 : : public:
74 : : // Limit maximum number of resources to |max_acquires|.
75 : 2234 : Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
76 : :
77 : : Limiter(const Limiter&) = delete;
78 : : Limiter operator=(const Limiter&) = delete;
79 : :
80 : : // If another resource is available, acquire it and return true.
81 : : // Else return false.
82 : 4144 : bool Acquire() {
83 : 4144 : int old_acquires_allowed =
84 [ - + ]: 4144 : acquires_allowed_.fetch_sub(1, std::memory_order_relaxed);
85 : :
86 [ - + ]: 4144 : if (old_acquires_allowed > 0) return true;
87 : :
88 : 0 : acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
89 : 0 : return false;
90 : : }
91 : :
92 : : // Release a resource acquired by a previous call to Acquire() that returned
93 : : // true.
94 : 4142 : void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
95 : :
96 : : private:
97 : : // The number of available resources.
98 : : //
99 : : // This is a counter and is not tied to the invariants of any other class, so
100 : : // it can be operated on safely using std::memory_order_relaxed.
101 : : std::atomic<int> acquires_allowed_;
102 : : };
103 : :
104 : : // Implements sequential read access in a file using read().
105 : : //
106 : : // Instances of this class are thread-friendly but not thread-safe, as required
107 : : // by the SequentialFile API.
108 : : class PosixSequentialFile final : public SequentialFile {
109 : : public:
110 : 6715 : PosixSequentialFile(std::string filename, int fd)
111 [ - + ]: 13430 : : fd_(fd), filename_(filename) {}
112 : 13430 : ~PosixSequentialFile() override { close(fd_); }
113 : :
114 : 9369 : Status Read(size_t n, Slice* result, char* scratch) override {
115 : 9369 : Status status;
116 : 9369 : while (true) {
117 [ + - ]: 9369 : ::ssize_t read_size = ::read(fd_, scratch, n);
118 [ - + ]: 9369 : if (read_size < 0) { // Read error.
119 [ # # ]: 0 : if (errno == EINTR) {
120 : 0 : continue; // Retry
121 : : }
122 [ # # # # ]: 0 : status = PosixError(filename_, errno);
123 : 0 : break;
124 : : }
125 : 9369 : *result = Slice(scratch, read_size);
126 : 9369 : break;
127 : 0 : }
128 : 9369 : return status;
129 : 0 : }
130 : :
131 : 0 : Status Skip(uint64_t n) override {
132 [ # # ]: 0 : if (::lseek(fd_, n, SEEK_CUR) == static_cast<off_t>(-1)) {
133 : 0 : return PosixError(filename_, errno);
134 : : }
135 : 0 : return Status::OK();
136 : : }
137 : :
138 [ - + ]: 3 : virtual std::string GetName() const override { return filename_; }
139 : :
140 : : private:
141 : : const int fd_;
142 : : const std::string filename_;
143 : : };
144 : :
145 : : // Implements random read access in a file using pread().
146 : : //
147 : : // Instances of this class are thread-safe, as required by the RandomAccessFile
148 : : // API. Instances are immutable and Read() only calls thread-safe library
149 : : // functions.
150 : : class PosixRandomAccessFile final : public RandomAccessFile {
151 : : public:
152 : : // The new instance takes ownership of |fd|. |fd_limiter| must outlive this
153 : : // instance, and will be used to determine if .
154 : 0 : PosixRandomAccessFile(std::string filename, int fd, Limiter* fd_limiter)
155 : 0 : : has_permanent_fd_(fd_limiter->Acquire()),
156 [ # # ]: 0 : fd_(has_permanent_fd_ ? fd : -1),
157 : 0 : fd_limiter_(fd_limiter),
158 : 0 : filename_(std::move(filename)) {
159 [ # # ]: 0 : if (!has_permanent_fd_) {
160 [ # # ]: 0 : assert(fd_ == -1);
161 [ # # ]: 0 : ::close(fd); // The file will be opened on every read.
162 : : }
163 : 0 : }
164 : :
165 : 0 : ~PosixRandomAccessFile() override {
166 [ # # ]: 0 : if (has_permanent_fd_) {
167 [ # # ]: 0 : assert(fd_ != -1);
168 : 0 : ::close(fd_);
169 : 0 : fd_limiter_->Release();
170 : : }
171 : 0 : }
172 : :
173 : 0 : Status Read(uint64_t offset, size_t n, Slice* result,
174 : : char* scratch) const override {
175 : 0 : int fd = fd_;
176 [ # # ]: 0 : if (!has_permanent_fd_) {
177 : 0 : fd = ::open(filename_.c_str(), O_RDONLY | kOpenBaseFlags);
178 [ # # ]: 0 : if (fd < 0) {
179 : 0 : return PosixError(filename_, errno);
180 : : }
181 : : }
182 : :
183 [ # # ]: 0 : assert(fd != -1);
184 : :
185 [ # # ]: 0 : Status status;
186 [ # # ]: 0 : ssize_t read_size = ::pread(fd, scratch, n, static_cast<off_t>(offset));
187 [ # # ]: 0 : *result = Slice(scratch, (read_size < 0) ? 0 : read_size);
188 [ # # ]: 0 : if (read_size < 0) {
189 : : // An error: return a non-ok status.
190 [ # # ]: 0 : status = PosixError(filename_, errno);
191 : : }
192 [ # # ]: 0 : if (!has_permanent_fd_) {
193 : : // Close the temporary file descriptor opened earlier.
194 [ # # ]: 0 : assert(fd != fd_);
195 [ # # ]: 0 : ::close(fd);
196 : : }
197 : 0 : return status;
198 : 0 : }
199 : :
200 [ # # ]: 0 : virtual std::string GetName() const override { return filename_; }
201 : :
202 : : private:
203 : : const bool has_permanent_fd_; // If false, the file is opened on every read.
204 : : const int fd_; // -1 if has_permanent_fd_ is false.
205 : : Limiter* const fd_limiter_;
206 : : const std::string filename_;
207 : : };
208 : :
209 : : // Implements random read access in a file using mmap().
210 : : //
211 : : // Instances of this class are thread-safe, as required by the RandomAccessFile
212 : : // API. Instances are immutable and Read() only calls thread-safe library
213 : : // functions.
214 : : class PosixMmapReadableFile final : public RandomAccessFile {
215 : : public:
216 : : // mmap_base[0, length-1] points to the memory-mapped contents of the file. It
217 : : // must be the result of a successful call to mmap(). This instances takes
218 : : // over the ownership of the region.
219 : : //
220 : : // |mmap_limiter| must outlive this instance. The caller must have already
221 : : // acquired the right to use one mmap region, which will be released when this
222 : : // instance is destroyed.
223 : 4144 : PosixMmapReadableFile(std::string filename, char* mmap_base, size_t length,
224 : : Limiter* mmap_limiter)
225 : 4144 : : mmap_base_(mmap_base),
226 : 4144 : length_(length),
227 : 4144 : mmap_limiter_(mmap_limiter),
228 : 4144 : filename_(std::move(filename)) {}
229 : :
230 : 8284 : ~PosixMmapReadableFile() override {
231 : 4142 : ::munmap(static_cast<void*>(mmap_base_), length_);
232 : 4142 : mmap_limiter_->Release();
233 : 8284 : }
234 : :
235 : 60784 : Status Read(uint64_t offset, size_t n, Slice* result,
236 : : char* scratch) const override {
237 [ - + ]: 60784 : if (offset + n > length_) {
238 : 0 : *result = Slice();
239 : 0 : return PosixError(filename_, EINVAL);
240 : : }
241 : :
242 : 60784 : *result = Slice(mmap_base_ + offset, n);
243 : 60784 : return Status::OK();
244 : : }
245 : :
246 [ - + ]: 2 : virtual std::string GetName() const override { return filename_; }
247 : :
248 : : private:
249 : : char* const mmap_base_;
250 : : const size_t length_;
251 : : Limiter* const mmap_limiter_;
252 : : const std::string filename_;
253 : : };
254 : :
255 : : class PosixWritableFile final : public WritableFile {
256 : : public:
257 : 10934 : PosixWritableFile(std::string filename, int fd)
258 : 10934 : : pos_(0),
259 : 10934 : fd_(fd),
260 : 10934 : is_manifest_(IsManifest(filename)),
261 : 10934 : filename_(std::move(filename)),
262 [ + - ]: 21868 : dirname_(Dirname(filename_)) {}
263 : :
264 : 21864 : ~PosixWritableFile() override {
265 [ + + ]: 10932 : if (fd_ >= 0) {
266 : : // Ignoring any potential errors
267 [ - + ]: 4992 : Close();
268 : : }
269 : 21864 : }
270 : :
271 : 131574 : Status Append(const Slice& data) override {
272 [ + - ]: 131574 : size_t write_size = data.size();
273 [ + - ]: 131574 : const char* write_data = data.data();
274 : :
275 : : // Fit as much as possible into buffer.
276 [ + - ]: 131574 : size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_);
277 [ + - ]: 131574 : std::memcpy(buf_ + pos_, write_data, copy_size);
278 : 131574 : write_data += copy_size;
279 : 131574 : write_size -= copy_size;
280 : 131574 : pos_ += copy_size;
281 [ + - ]: 131574 : if (write_size == 0) {
282 : 131574 : return Status::OK();
283 : : }
284 : :
285 : : // Can't fit in buffer, so need to do at least one write.
286 : 0 : Status status = FlushBuffer();
287 : 0 : if (!status.ok()) {
288 : 0 : return status;
289 : : }
290 : :
291 : : // Small writes go to buffer, large writes are written directly.
292 [ # # ]: 0 : if (write_size < kWritableFileBufferSize) {
293 : 0 : std::memcpy(buf_, write_data, write_size);
294 : 0 : pos_ = write_size;
295 : 0 : return Status::OK();
296 : : }
297 [ # # ]: 0 : return WriteUnbuffered(write_data, write_size);
298 : 0 : }
299 : :
300 : 10932 : Status Close() override {
301 : 21864 : Status status = FlushBuffer();
302 : 21864 : const int close_result = ::close(fd_);
303 [ - + - - ]: 10932 : if (close_result < 0 && status.ok()) {
304 [ # # # # ]: 0 : status = PosixError(filename_, errno);
305 : : }
306 : 10932 : fd_ = -1;
307 : 10932 : return status;
308 : 0 : }
309 : :
310 : 114188 : Status Flush() override { return FlushBuffer(); }
311 : :
312 : 11511 : Status Sync() override {
313 : : // Ensure new files referred to by the manifest are in the filesystem.
314 : : //
315 : : // This needs to happen before the manifest file is flushed to disk, to
316 : : // avoid crashing in a state where the manifest refers to files that are not
317 : : // yet on disk.
318 : 11511 : Status status = SyncDirIfManifest();
319 [ - + ]: 11511 : if (!status.ok()) {
320 : 0 : return status;
321 : : }
322 : :
323 : 34533 : status = FlushBuffer();
324 [ - + ]: 11511 : if (!status.ok()) {
325 : 0 : return status;
326 : : }
327 : :
328 [ + - ]: 11511 : return SyncFd(fd_, filename_, false);
329 : 11511 : }
330 : :
331 : : private:
332 : 79537 : Status FlushBuffer() {
333 [ + - ]: 79537 : Status status = WriteUnbuffered(buf_, pos_);
334 : 79537 : pos_ = 0;
335 [ - + + - : 22443 : return status;
- - ]
336 : : }
337 : :
338 : 79537 : Status WriteUnbuffered(const char* data, size_t size) {
339 [ + + ]: 141851 : while (size > 0) {
340 : 62314 : ssize_t write_result = ::write(fd_, data, size);
341 [ - + ]: 62314 : if (write_result < 0) {
342 [ # # ]: 0 : if (errno == EINTR) {
343 : 0 : continue; // Retry
344 : : }
345 : 0 : return PosixError(filename_, errno);
346 : : }
347 : 62314 : data += write_result;
348 : 62314 : size -= write_result;
349 : : }
350 : 79537 : return Status::OK();
351 : : }
352 : :
353 : 11511 : Status SyncDirIfManifest() {
354 [ + + ]: 11511 : Status status;
355 [ + + ]: 11511 : if (!is_manifest_) {
356 : : return status;
357 : : }
358 : :
359 [ + - ]: 2806 : int fd = ::open(dirname_.c_str(), O_RDONLY | kOpenBaseFlags);
360 [ - + ]: 2806 : if (fd < 0) {
361 [ # # # # ]: 0 : status = PosixError(dirname_, errno);
362 : : } else {
363 [ + - - + ]: 2806 : status = SyncFd(fd, dirname_, true);
364 [ + - ]: 2806 : ::close(fd);
365 : : }
366 : : return status;
367 : 0 : }
368 : :
369 : : // Ensures that all the caches associated with the given file descriptor's
370 : : // data are flushed all the way to durable media, and can withstand power
371 : : // failures.
372 : : //
373 : : // The path argument is only used to populate the description string in the
374 : : // returned Status if an error occurs.
375 : 14317 : static Status SyncFd(int fd, const std::string& fd_path, bool syncing_dir) {
376 : : #if HAVE_FULLFSYNC
377 : : // On macOS and iOS, fsync() doesn't guarantee durability past power
378 : : // failures. fcntl(F_FULLFSYNC) is required for that purpose. Some
379 : : // filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to
380 : : // fsync().
381 : : if (::fcntl(fd, F_FULLFSYNC) == 0) {
382 : : return Status::OK();
383 : : }
384 : : #endif // HAVE_FULLFSYNC
385 : :
386 : : #if HAVE_FDATASYNC
387 : 14317 : bool sync_success = ::fdatasync(fd) == 0;
388 : : #else
389 : : bool sync_success = ::fsync(fd) == 0;
390 : : #endif // HAVE_FDATASYNC
391 : :
392 [ + - ]: 14317 : if (sync_success) {
393 : 14317 : return Status::OK();
394 : : }
395 : : // Do not crash if filesystem can't fsync directories
396 : : // (see https://github.com/bitcoin/bitcoin/pull/10000)
397 [ # # # # ]: 0 : if (syncing_dir && errno == EINVAL) {
398 : 0 : return Status::OK();
399 : : }
400 : 0 : return PosixError(fd_path, errno);
401 : : }
402 : :
403 : : // Returns the directory name in a path pointing to a file.
404 : : //
405 : : // Returns "." if the path does not contain any directory separator.
406 : 10934 : static std::string Dirname(const std::string& filename) {
407 : 10934 : std::string::size_type separator_pos = filename.rfind('/');
408 [ - + ]: 10934 : if (separator_pos == std::string::npos) {
409 : 0 : return std::string(".");
410 : : }
411 : : // The filename component should not contain a path separator. If it does,
412 : : // the splitting was done incorrectly.
413 [ - + ]: 10934 : assert(filename.find('/', separator_pos + 1) == std::string::npos);
414 : :
415 : 10934 : return filename.substr(0, separator_pos);
416 : : }
417 : :
418 : : // Extracts the file name from a path pointing to a file.
419 : : //
420 : : // The returned Slice points to |filename|'s data buffer, so it is only valid
421 : : // while |filename| is alive and unchanged.
422 : 10934 : static Slice Basename(const std::string& filename) {
423 : 10934 : std::string::size_type separator_pos = filename.rfind('/');
424 [ - + ]: 10934 : if (separator_pos == std::string::npos) {
425 [ # # ]: 0 : return Slice(filename);
426 : : }
427 : : // The filename component should not contain a path separator. If it does,
428 : : // the splitting was done incorrectly.
429 [ - + ]: 10934 : assert(filename.find('/', separator_pos + 1) == std::string::npos);
430 : :
431 [ - + ]: 10934 : return Slice(filename.data() + separator_pos + 1,
432 : 10934 : filename.length() - separator_pos - 1);
433 : : }
434 : :
435 : : // True if the given file is a manifest file.
436 : 10934 : static bool IsManifest(const std::string& filename) {
437 : 10934 : return Basename(filename).starts_with("MANIFEST");
438 : : }
439 : :
440 [ # # ]: 0 : virtual std::string GetName() const override { return filename_; }
441 : :
442 : : // buf_[0, pos_ - 1] contains data to be written to fd_.
443 : : char buf_[kWritableFileBufferSize];
444 : : size_t pos_;
445 : : int fd_;
446 : :
447 : : const bool is_manifest_; // True if the file's name starts with MANIFEST.
448 : : const std::string filename_;
449 : : const std::string dirname_; // The directory of filename_.
450 : : };
451 : :
452 : 5137 : int LockOrUnlock(int fd, bool lock) {
453 : 5137 : errno = 0;
454 : 5137 : struct ::flock file_lock_info;
455 [ + + ]: 5137 : std::memset(&file_lock_info, 0, sizeof(file_lock_info));
456 [ + + ]: 5137 : file_lock_info.l_type = (lock ? F_WRLCK : F_UNLCK);
457 : 5137 : file_lock_info.l_whence = SEEK_SET;
458 : 5137 : file_lock_info.l_start = 0;
459 : 5137 : file_lock_info.l_len = 0; // Lock/unlock entire file.
460 : 5137 : return ::fcntl(fd, F_SETLK, &file_lock_info);
461 : : }
462 : :
463 : : // Instances are thread-safe because they are immutable.
464 : : class PosixFileLock : public FileLock {
465 : : public:
466 : 2569 : PosixFileLock(int fd, std::string filename)
467 : 2569 : : fd_(fd), filename_(std::move(filename)) {}
468 : :
469 : 5136 : int fd() const { return fd_; }
470 : 2568 : const std::string& filename() const { return filename_; }
471 : :
472 : : private:
473 : : const int fd_;
474 : : const std::string filename_;
475 : : };
476 : :
477 : : // Tracks the files locked by PosixEnv::LockFile().
478 : : //
479 : : // We maintain a separate set instead of relying on fcntl(F_SETLK) because
480 : : // fcntl(F_SETLK) does not provide any protection against multiple uses from the
481 : : // same process.
482 : : //
483 : : // Instances are thread-safe because all member data is guarded by a mutex.
484 : 1117 : class PosixLockTable {
485 : : public:
486 : 2569 : bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) {
487 : 2569 : mu_.Lock();
488 : 2569 : bool succeeded = locked_files_.insert(fname).second;
489 : 2569 : mu_.Unlock();
490 : 2569 : return succeeded;
491 : : }
492 : 2568 : void Remove(const std::string& fname) LOCKS_EXCLUDED(mu_) {
493 : 2568 : mu_.Lock();
494 : 2568 : locked_files_.erase(fname);
495 : 2568 : mu_.Unlock();
496 : 2568 : }
497 : :
498 : : private:
499 : : port::Mutex mu_;
500 : : std::set<std::string> locked_files_ GUARDED_BY(mu_);
501 : : };
502 : :
503 : : class PosixEnv : public Env {
504 : : public:
505 : : PosixEnv();
506 : 0 : ~PosixEnv() override {
507 : 0 : static const char msg[] =
508 : : "PosixEnv singleton destroyed. Unsupported behavior!\n";
509 : 0 : std::fwrite(msg, 1, sizeof(msg), stderr);
510 : 0 : std::abort();
511 : 0 : }
512 : :
513 : 6716 : Status NewSequentialFile(const std::string& filename,
514 : : SequentialFile** result) override {
515 : 6716 : int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags);
516 [ + + ]: 6716 : if (fd < 0) {
517 : 1 : *result = nullptr;
518 : 1 : return PosixError(filename, errno);
519 : : }
520 : :
521 [ - + + - ]: 13430 : *result = new PosixSequentialFile(filename, fd);
522 : 6715 : return Status::OK();
523 : : }
524 : :
525 : 4144 : Status NewRandomAccessFile(const std::string& filename,
526 : : RandomAccessFile** result) override {
527 : 4144 : *result = nullptr;
528 : 4144 : int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags);
529 [ - + ]: 4144 : if (fd < 0) {
530 : 0 : return PosixError(filename, errno);
531 : : }
532 : :
533 [ - + ]: 4144 : if (!mmap_limiter_.Acquire()) {
534 [ # # # # ]: 0 : *result = new PosixRandomAccessFile(filename, fd, &fd_limiter_);
535 : 0 : return Status::OK();
536 : : }
537 : :
538 : 4144 : uint64_t file_size;
539 : 4144 : Status status = GetFileSize(filename, &file_size);
540 [ + - ]: 4144 : if (status.ok()) {
541 : 4144 : void* mmap_base =
542 : 4144 : ::mmap(/*addr=*/nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
543 [ + - ]: 4144 : if (mmap_base != MAP_FAILED) {
544 : 8288 : *result = new PosixMmapReadableFile(filename,
545 : : reinterpret_cast<char*>(mmap_base),
546 [ + - - + ]: 12432 : file_size, &mmap_limiter_);
547 : : } else {
548 [ # # # # ]: 0 : status = PosixError(filename, errno);
549 : : }
550 : : }
551 [ + - ]: 4144 : ::close(fd);
552 [ - + ]: 4144 : if (!status.ok()) {
553 : 0 : mmap_limiter_.Release();
554 : : }
555 : 4144 : return status;
556 : 4144 : }
557 : :
558 : 10934 : Status NewWritableFile(const std::string& filename,
559 : : WritableFile** result) override {
560 : 10934 : int fd = ::open(filename.c_str(),
561 : : O_TRUNC | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
562 [ - + ]: 10934 : if (fd < 0) {
563 : 0 : *result = nullptr;
564 : 0 : return PosixError(filename, errno);
565 : : }
566 : :
567 [ - + + - ]: 21868 : *result = new PosixWritableFile(filename, fd);
568 : 10934 : return Status::OK();
569 : : }
570 : :
571 : 0 : Status NewAppendableFile(const std::string& filename,
572 : : WritableFile** result) override {
573 : 0 : int fd = ::open(filename.c_str(),
574 : : O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
575 [ # # ]: 0 : if (fd < 0) {
576 : 0 : *result = nullptr;
577 : 0 : return PosixError(filename, errno);
578 : : }
579 : :
580 [ # # # # ]: 0 : *result = new PosixWritableFile(filename, fd);
581 : 0 : return Status::OK();
582 : : }
583 : :
584 : 2480 : bool FileExists(const std::string& filename) override {
585 : 2480 : return ::access(filename.c_str(), F_OK) == 0;
586 : : }
587 : :
588 : 5377 : Status GetChildren(const std::string& directory_path,
589 : : std::vector<std::string>* result) override {
590 : 5377 : result->clear();
591 : 5377 : ::DIR* dir = ::opendir(directory_path.c_str());
592 [ + + ]: 5377 : if (dir == nullptr) {
593 : 4 : return PosixError(directory_path, errno);
594 : : }
595 : : struct ::dirent* entry;
596 [ + + ]: 49111 : while ((entry = ::readdir(dir)) != nullptr) {
597 : 43738 : result->emplace_back(entry->d_name);
598 : : }
599 : 5373 : ::closedir(dir);
600 : 5373 : return Status::OK();
601 : : }
602 : :
603 : 5811 : Status DeleteFile(const std::string& filename) override {
604 [ - + ]: 5811 : if (::unlink(filename.c_str()) != 0) {
605 : 0 : return PosixError(filename, errno);
606 : : }
607 : 5811 : return Status::OK();
608 : : }
609 : :
610 : 2480 : Status CreateDir(const std::string& dirname) override {
611 [ + - ]: 2480 : if (::mkdir(dirname.c_str(), 0755) != 0) {
612 : 2480 : return PosixError(dirname, errno);
613 : : }
614 : 0 : return Status::OK();
615 : : }
616 : :
617 : 89 : Status DeleteDir(const std::string& dirname) override {
618 [ + + ]: 89 : if (::rmdir(dirname.c_str()) != 0) {
619 : 2 : return PosixError(dirname, errno);
620 : : }
621 : 87 : return Status::OK();
622 : : }
623 : :
624 : 4144 : Status GetFileSize(const std::string& filename, uint64_t* size) override {
625 : 4144 : struct ::stat file_stat;
626 [ - + ]: 4144 : if (::stat(filename.c_str(), &file_stat) != 0) {
627 : 0 : *size = 0;
628 : 0 : return PosixError(filename, errno);
629 : : }
630 : 4144 : *size = file_stat.st_size;
631 : 4144 : return Status::OK();
632 : : }
633 : :
634 : 3193 : Status RenameFile(const std::string& from, const std::string& to) override {
635 [ - + ]: 3193 : if (std::rename(from.c_str(), to.c_str()) != 0) {
636 : 0 : return PosixError(from, errno);
637 : : }
638 : 3193 : return Status::OK();
639 : : }
640 : :
641 : 2569 : Status LockFile(const std::string& filename, FileLock** lock) override {
642 : 2569 : *lock = nullptr;
643 : :
644 : 2569 : int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | kOpenBaseFlags, 0644);
645 [ - + ]: 2569 : if (fd < 0) {
646 : 0 : return PosixError(filename, errno);
647 : : }
648 : :
649 [ - + ]: 2569 : if (!locks_.Insert(filename)) {
650 : 0 : ::close(fd);
651 [ # # # # ]: 0 : return Status::IOError("lock " + filename, "already held by process");
652 : : }
653 : :
654 [ - + ]: 2569 : if (LockOrUnlock(fd, true) == -1) {
655 : 0 : int lock_errno = errno;
656 : 0 : ::close(fd);
657 : 0 : locks_.Remove(filename);
658 [ # # ]: 0 : return PosixError("lock " + filename, lock_errno);
659 : : }
660 : :
661 [ - + ]: 5138 : *lock = new PosixFileLock(fd, filename);
662 : 2569 : return Status::OK();
663 : : }
664 : :
665 : 2568 : Status UnlockFile(FileLock* lock) override {
666 : 2568 : PosixFileLock* posix_file_lock = static_cast<PosixFileLock*>(lock);
667 [ - + ]: 2568 : if (LockOrUnlock(posix_file_lock->fd(), false) == -1) {
668 [ # # ]: 0 : return PosixError("unlock " + posix_file_lock->filename(), errno);
669 : : }
670 : 2568 : locks_.Remove(posix_file_lock->filename());
671 : 2568 : ::close(posix_file_lock->fd());
672 [ + - ]: 2568 : delete posix_file_lock;
673 : 2568 : return Status::OK();
674 : : }
675 : :
676 : : void Schedule(void (*background_work_function)(void* background_work_arg),
677 : : void* background_work_arg) override;
678 : :
679 : 0 : void StartThread(void (*thread_main)(void* thread_main_arg),
680 : : void* thread_main_arg) override {
681 : 0 : std::thread new_thread(thread_main, thread_main_arg);
682 [ # # ]: 0 : new_thread.detach();
683 : 0 : }
684 : :
685 : 0 : Status GetTestDirectory(std::string* result) override {
686 : 0 : const char* env = std::getenv("TEST_TMPDIR");
687 [ # # # # ]: 0 : if (env && env[0] != '\0') {
688 : 0 : *result = env;
689 : : } else {
690 : 0 : char buf[100];
691 : 0 : std::snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d",
692 : 0 : static_cast<int>(::geteuid()));
693 : 0 : *result = buf;
694 : : }
695 : :
696 : : // The CreateDir status is ignored because the directory may already exist.
697 [ # # ]: 0 : CreateDir(*result);
698 : :
699 : 0 : return Status::OK();
700 : : }
701 : :
702 : 0 : Status NewLogger(const std::string& filename, Logger** result) override {
703 : 0 : int fd = ::open(filename.c_str(),
704 : : O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
705 [ # # ]: 0 : if (fd < 0) {
706 : 0 : *result = nullptr;
707 : 0 : return PosixError(filename, errno);
708 : : }
709 : :
710 : 0 : std::FILE* fp = ::fdopen(fd, "w");
711 [ # # ]: 0 : if (fp == nullptr) {
712 : 0 : ::close(fd);
713 : 0 : *result = nullptr;
714 : 0 : return PosixError(filename, errno);
715 : : } else {
716 : 0 : *result = new PosixLogger(fp);
717 : 0 : return Status::OK();
718 : : }
719 : : }
720 : :
721 : 4058 : uint64_t NowMicros() override {
722 : 4058 : static constexpr uint64_t kUsecondsPerSecond = 1000000;
723 : 4058 : struct ::timeval tv;
724 : 4058 : ::gettimeofday(&tv, nullptr);
725 : 4058 : return static_cast<uint64_t>(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec;
726 : : }
727 : :
728 : 0 : void SleepForMicroseconds(int micros) override {
729 : 0 : std::this_thread::sleep_for(std::chrono::microseconds(micros));
730 : 0 : }
731 : :
732 : : private:
733 : : void BackgroundThreadMain();
734 : :
735 : 206 : static void BackgroundThreadEntryPoint(PosixEnv* env) {
736 : 206 : env->BackgroundThreadMain();
737 : : }
738 : :
739 : : // Stores the work item data in a Schedule() call.
740 : : //
741 : : // Instances are constructed on the thread calling Schedule() and used on the
742 : : // background thread.
743 : : //
744 : : // This structure is thread-safe because it is immutable.
745 : : struct BackgroundWorkItem {
746 : 341 : explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
747 : 341 : : function(function), arg(arg) {}
748 : :
749 : : void (*const function)(void*);
750 : : void* const arg;
751 : : };
752 : :
753 : : port::Mutex background_work_mutex_;
754 : : port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_);
755 : : bool started_background_thread_ GUARDED_BY(background_work_mutex_);
756 : :
757 : : std::queue<BackgroundWorkItem> background_work_queue_
758 : : GUARDED_BY(background_work_mutex_);
759 : :
760 : : PosixLockTable locks_; // Thread-safe.
761 : : Limiter mmap_limiter_; // Thread-safe.
762 : : Limiter fd_limiter_; // Thread-safe.
763 : : };
764 : :
765 : : // Return the maximum number of concurrent mmaps.
766 : 1117 : int MaxMmaps() { return g_mmap_limit; }
767 : :
768 : : // Return the maximum number of read-only files to keep open.
769 : 1117 : int MaxOpenFiles() {
770 [ + - ]: 1117 : if (g_open_read_only_file_limit >= 0) {
771 : : return g_open_read_only_file_limit;
772 : : }
773 : 1117 : struct ::rlimit rlim;
774 [ - + ]: 1117 : if (::getrlimit(RLIMIT_NOFILE, &rlim)) {
775 : : // getrlimit failed, fallback to hard-coded default.
776 : 0 : g_open_read_only_file_limit = 50;
777 [ - + ]: 1117 : } else if (rlim.rlim_cur == RLIM_INFINITY) {
778 : 0 : g_open_read_only_file_limit = std::numeric_limits<int>::max();
779 : : } else {
780 : : // Allow use of 20% of available file descriptors for read-only files.
781 : 1117 : g_open_read_only_file_limit = rlim.rlim_cur / 5;
782 : : }
783 : 1117 : return g_open_read_only_file_limit;
784 : : }
785 : :
786 : : } // namespace
787 : :
788 : 1117 : PosixEnv::PosixEnv()
789 : 1117 : : background_work_cv_(&background_work_mutex_),
790 : 1117 : started_background_thread_(false),
791 : 1117 : mmap_limiter_(MaxMmaps()),
792 [ + - ]: 1117 : fd_limiter_(MaxOpenFiles()) {}
793 : :
794 : 341 : void PosixEnv::Schedule(
795 : : void (*background_work_function)(void* background_work_arg),
796 : : void* background_work_arg) {
797 : 341 : background_work_mutex_.Lock();
798 : :
799 : : // Start the background thread, if we haven't done so already.
800 [ + + ]: 341 : if (!started_background_thread_) {
801 : 206 : started_background_thread_ = true;
802 : 206 : std::thread background_thread(PosixEnv::BackgroundThreadEntryPoint, this);
803 [ + - ]: 206 : background_thread.detach();
804 : 206 : }
805 : :
806 : : // If the queue is empty, the background thread may be waiting for work.
807 [ + - ]: 341 : if (background_work_queue_.empty()) {
808 : 341 : background_work_cv_.Signal();
809 : : }
810 : :
811 : 341 : background_work_queue_.emplace(background_work_function, background_work_arg);
812 : 341 : background_work_mutex_.Unlock();
813 : 341 : }
814 : :
815 : 206 : void PosixEnv::BackgroundThreadMain() {
816 : 888 : while (true) {
817 : 547 : background_work_mutex_.Lock();
818 : :
819 : : // Wait until there is work to be done.
820 [ + + ]: 682 : while (background_work_queue_.empty()) {
821 : 341 : background_work_cv_.Wait();
822 : : }
823 : :
824 : 341 : assert(!background_work_queue_.empty());
825 : 341 : auto background_work_function = background_work_queue_.front().function;
826 : 341 : void* background_work_arg = background_work_queue_.front().arg;
827 : 341 : background_work_queue_.pop();
828 : :
829 : 341 : background_work_mutex_.Unlock();
830 : 341 : background_work_function(background_work_arg);
831 : 341 : }
832 : : }
833 : :
834 : : namespace {
835 : :
836 : : // Wraps an Env instance whose destructor is never created.
837 : : //
838 : : // Intended usage:
839 : : // using PlatformSingletonEnv = SingletonEnv<PlatformEnv>;
840 : : // void ConfigurePosixEnv(int param) {
841 : : // PlatformSingletonEnv::AssertEnvNotInitialized();
842 : : // // set global configuration flags.
843 : : // }
844 : : // Env* Env::Default() {
845 : : // static PlatformSingletonEnv default_env;
846 : : // return default_env.env();
847 : : // }
848 : : template <typename EnvType>
849 : : class SingletonEnv {
850 : : public:
851 : 1117 : SingletonEnv() {
852 : : #if !defined(NDEBUG)
853 : 1117 : env_initialized_.store(true, std::memory_order_relaxed);
854 : : #endif // !defined(NDEBUG)
855 : : static_assert(sizeof(env_storage_) >= sizeof(EnvType),
856 : : "env_storage_ will not fit the Env");
857 : : static_assert(std::is_standard_layout_v<SingletonEnv<EnvType>>);
858 : : static_assert(
859 : : offsetof(SingletonEnv<EnvType>, env_storage_) % alignof(EnvType) == 0,
860 : : "env_storage_ does not meet the Env's alignment needs");
861 : : static_assert(alignof(SingletonEnv<EnvType>) % alignof(EnvType) == 0,
862 : : "env_storage_ does not meet the Env's alignment needs");
863 : 1117 : new (env_storage_) EnvType();
864 : 1117 : }
865 : : ~SingletonEnv() = default;
866 : :
867 : : SingletonEnv(const SingletonEnv&) = delete;
868 : : SingletonEnv& operator=(const SingletonEnv&) = delete;
869 : :
870 : 10255 : Env* env() { return reinterpret_cast<Env*>(&env_storage_); }
871 : :
872 [ # # ]: 0 : static void AssertEnvNotInitialized() {
873 : : #if !defined(NDEBUG)
874 [ # # ]: 0 : assert(!env_initialized_.load(std::memory_order_relaxed));
875 : : #endif // !defined(NDEBUG)
876 : 0 : }
877 : :
878 : : private:
879 : : alignas(EnvType) char env_storage_[sizeof(EnvType)];
880 : : #if !defined(NDEBUG)
881 : : static std::atomic<bool> env_initialized_;
882 : : #endif // !defined(NDEBUG)
883 : : };
884 : :
885 : : #if !defined(NDEBUG)
886 : : template <typename EnvType>
887 : : std::atomic<bool> SingletonEnv<EnvType>::env_initialized_;
888 : : #endif // !defined(NDEBUG)
889 : :
890 : : using PosixDefaultEnv = SingletonEnv<PosixEnv>;
891 : :
892 : : } // namespace
893 : :
894 : 0 : void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) {
895 : 0 : PosixDefaultEnv::AssertEnvNotInitialized();
896 : 0 : g_open_read_only_file_limit = limit;
897 : 0 : }
898 : :
899 : 0 : void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) {
900 : 0 : PosixDefaultEnv::AssertEnvNotInitialized();
901 : 0 : g_mmap_limit = limit;
902 : 0 : }
903 : :
904 : 10255 : Env* Env::Default() {
905 [ + + + - : 10255 : static PosixDefaultEnv env_container;
+ - ]
906 : 10255 : return env_container.env();
907 : : }
908 : :
909 : : } // namespace leveldb
|