]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/env/mock_env.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / env / mock_env.cc
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10 #include "env/mock_env.h"
11
12 #include <algorithm>
13 #include <chrono>
14
15 #include "file/filename.h"
16 #include "port/sys_time.h"
17 #include "rocksdb/file_system.h"
18 #include "util/cast_util.h"
19 #include "util/hash.h"
20 #include "util/random.h"
21 #include "util/rate_limiter.h"
22
23 namespace ROCKSDB_NAMESPACE {
24
25 class MemFile {
26 public:
27 explicit MemFile(Env* env, const std::string& fn, bool _is_lock_file = false)
28 : env_(env),
29 fn_(fn),
30 refs_(0),
31 is_lock_file_(_is_lock_file),
32 locked_(false),
33 size_(0),
34 modified_time_(Now()),
35 rnd_(Lower32of64(GetSliceNPHash64(fn))),
36 fsynced_bytes_(0) {}
37 // No copying allowed.
38 MemFile(const MemFile&) = delete;
39 void operator=(const MemFile&) = delete;
40
41 void Ref() {
42 MutexLock lock(&mutex_);
43 ++refs_;
44 }
45
46 bool is_lock_file() const { return is_lock_file_; }
47
48 bool Lock() {
49 assert(is_lock_file_);
50 MutexLock lock(&mutex_);
51 if (locked_) {
52 return false;
53 } else {
54 locked_ = true;
55 return true;
56 }
57 }
58
59 void Unlock() {
60 assert(is_lock_file_);
61 MutexLock lock(&mutex_);
62 locked_ = false;
63 }
64
65 void Unref() {
66 bool do_delete = false;
67 {
68 MutexLock lock(&mutex_);
69 --refs_;
70 assert(refs_ >= 0);
71 if (refs_ <= 0) {
72 do_delete = true;
73 }
74 }
75
76 if (do_delete) {
77 delete this;
78 }
79 }
80
81 uint64_t Size() const { return size_; }
82
83 void Truncate(size_t size, const IOOptions& /*options*/,
84 IODebugContext* /*dbg*/) {
85 MutexLock lock(&mutex_);
86 if (size < size_) {
87 data_.resize(size);
88 size_ = size;
89 }
90 }
91
92 void CorruptBuffer() {
93 if (fsynced_bytes_ >= size_) {
94 return;
95 }
96 uint64_t buffered_bytes = size_ - fsynced_bytes_;
97 uint64_t start =
98 fsynced_bytes_ + rnd_.Uniform(static_cast<int>(buffered_bytes));
99 uint64_t end = std::min(start + 512, size_.load());
100 MutexLock lock(&mutex_);
101 for (uint64_t pos = start; pos < end; ++pos) {
102 data_[static_cast<size_t>(pos)] = static_cast<char>(rnd_.Uniform(256));
103 }
104 }
105
106 IOStatus Read(uint64_t offset, size_t n, const IOOptions& /*options*/,
107 Slice* result, char* scratch, IODebugContext* /*dbg*/) const {
108 MutexLock lock(&mutex_);
109 const uint64_t available = Size() - std::min(Size(), offset);
110 size_t offset_ = static_cast<size_t>(offset);
111 if (n > available) {
112 n = static_cast<size_t>(available);
113 }
114 if (n == 0) {
115 *result = Slice();
116 return IOStatus::OK();
117 }
118 if (scratch) {
119 memcpy(scratch, &(data_[offset_]), n);
120 *result = Slice(scratch, n);
121 } else {
122 *result = Slice(&(data_[offset_]), n);
123 }
124 return IOStatus::OK();
125 }
126
127 IOStatus Write(uint64_t offset, const Slice& data,
128 const IOOptions& /*options*/, IODebugContext* /*dbg*/) {
129 MutexLock lock(&mutex_);
130 size_t offset_ = static_cast<size_t>(offset);
131 if (offset + data.size() > data_.size()) {
132 data_.resize(offset_ + data.size());
133 }
134 data_.replace(offset_, data.size(), data.data(), data.size());
135 size_ = data_.size();
136 modified_time_ = Now();
137 return IOStatus::OK();
138 }
139
140 IOStatus Append(const Slice& data, const IOOptions& /*options*/,
141 IODebugContext* /*dbg*/) {
142 MutexLock lock(&mutex_);
143 data_.append(data.data(), data.size());
144 size_ = data_.size();
145 modified_time_ = Now();
146 return IOStatus::OK();
147 }
148
149 IOStatus Fsync(const IOOptions& /*options*/, IODebugContext* /*dbg*/) {
150 fsynced_bytes_ = size_.load();
151 return IOStatus::OK();
152 }
153
154 uint64_t ModifiedTime() const { return modified_time_; }
155
156 private:
157 uint64_t Now() {
158 int64_t unix_time = 0;
159 auto s = env_->GetCurrentTime(&unix_time);
160 assert(s.ok());
161 return static_cast<uint64_t>(unix_time);
162 }
163
164 // Private since only Unref() should be used to delete it.
165 ~MemFile() { assert(refs_ == 0); }
166
167 Env* env_;
168 const std::string fn_;
169 mutable port::Mutex mutex_;
170 int refs_;
171 bool is_lock_file_;
172 bool locked_;
173
174 // Data written into this file, all bytes before fsynced_bytes are
175 // persistent.
176 std::string data_;
177 std::atomic<uint64_t> size_;
178 std::atomic<uint64_t> modified_time_;
179
180 Random rnd_;
181 std::atomic<uint64_t> fsynced_bytes_;
182 };
183
184 namespace {
185
186 class MockSequentialFile : public FSSequentialFile {
187 public:
188 explicit MockSequentialFile(MemFile* file, const FileOptions& opts)
189 : file_(file),
190 use_direct_io_(opts.use_direct_reads),
191 use_mmap_read_(opts.use_mmap_reads),
192 pos_(0) {
193 file_->Ref();
194 }
195
196 ~MockSequentialFile() override { file_->Unref(); }
197
198 IOStatus Read(size_t n, const IOOptions& options, Slice* result,
199 char* scratch, IODebugContext* dbg) override {
200 IOStatus s = file_->Read(pos_, n, options, result,
201 (use_mmap_read_) ? nullptr : scratch, dbg);
202 if (s.ok()) {
203 pos_ += result->size();
204 }
205 return s;
206 }
207
208 bool use_direct_io() const override { return use_direct_io_; }
209 IOStatus Skip(uint64_t n) override {
210 if (pos_ > file_->Size()) {
211 return IOStatus::IOError("pos_ > file_->Size()");
212 }
213 const uint64_t available = file_->Size() - pos_;
214 if (n > available) {
215 n = available;
216 }
217 pos_ += static_cast<size_t>(n);
218 return IOStatus::OK();
219 }
220
221 private:
222 MemFile* file_;
223 bool use_direct_io_;
224 bool use_mmap_read_;
225 size_t pos_;
226 };
227
228 class MockRandomAccessFile : public FSRandomAccessFile {
229 public:
230 explicit MockRandomAccessFile(MemFile* file, const FileOptions& opts)
231 : file_(file),
232 use_direct_io_(opts.use_direct_reads),
233 use_mmap_read_(opts.use_mmap_reads) {
234 file_->Ref();
235 }
236
237 ~MockRandomAccessFile() override { file_->Unref(); }
238
239 bool use_direct_io() const override { return use_direct_io_; }
240
241 IOStatus Prefetch(uint64_t /*offset*/, size_t /*n*/,
242 const IOOptions& /*options*/,
243 IODebugContext* /*dbg*/) override {
244 return IOStatus::OK();
245 }
246
247 IOStatus Read(uint64_t offset, size_t n, const IOOptions& options,
248 Slice* result, char* scratch,
249 IODebugContext* dbg) const override {
250 if (use_mmap_read_) {
251 return file_->Read(offset, n, options, result, nullptr, dbg);
252 } else {
253 return file_->Read(offset, n, options, result, scratch, dbg);
254 }
255 }
256
257 private:
258 MemFile* file_;
259 bool use_direct_io_;
260 bool use_mmap_read_;
261 };
262
263 class MockRandomRWFile : public FSRandomRWFile {
264 public:
265 explicit MockRandomRWFile(MemFile* file) : file_(file) { file_->Ref(); }
266
267 ~MockRandomRWFile() override { file_->Unref(); }
268
269 IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options,
270 IODebugContext* dbg) override {
271 return file_->Write(offset, data, options, dbg);
272 }
273
274 IOStatus Read(uint64_t offset, size_t n, const IOOptions& options,
275 Slice* result, char* scratch,
276 IODebugContext* dbg) const override {
277 return file_->Read(offset, n, options, result, scratch, dbg);
278 }
279
280 IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
281 return file_->Fsync(options, dbg);
282 }
283
284 IOStatus Flush(const IOOptions& /*options*/,
285 IODebugContext* /*dbg*/) override {
286 return IOStatus::OK();
287 }
288
289 IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
290 return file_->Fsync(options, dbg);
291 }
292
293 private:
294 MemFile* file_;
295 };
296
297 class MockWritableFile : public FSWritableFile {
298 public:
299 MockWritableFile(MemFile* file, const FileOptions& opts)
300 : file_(file),
301 use_direct_io_(opts.use_direct_writes),
302 rate_limiter_(opts.rate_limiter) {
303 file_->Ref();
304 }
305
306 ~MockWritableFile() override { file_->Unref(); }
307
308 bool use_direct_io() const override { return false && use_direct_io_; }
309
310 using FSWritableFile::Append;
311 IOStatus Append(const Slice& data, const IOOptions& options,
312 IODebugContext* dbg) override {
313 size_t bytes_written = 0;
314 while (bytes_written < data.size()) {
315 auto bytes = RequestToken(data.size() - bytes_written);
316 IOStatus s = file_->Append(Slice(data.data() + bytes_written, bytes),
317 options, dbg);
318 if (!s.ok()) {
319 return s;
320 }
321 bytes_written += bytes;
322 }
323 return IOStatus::OK();
324 }
325
326 using FSWritableFile::PositionedAppend;
327 IOStatus PositionedAppend(const Slice& data, uint64_t /*offset*/,
328 const IOOptions& options,
329 IODebugContext* dbg) override {
330 assert(use_direct_io_);
331 return Append(data, options, dbg);
332 }
333
334 IOStatus Truncate(uint64_t size, const IOOptions& options,
335 IODebugContext* dbg) override {
336 file_->Truncate(static_cast<size_t>(size), options, dbg);
337 return IOStatus::OK();
338 }
339 IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
340 return file_->Fsync(options, dbg);
341 }
342
343 IOStatus Flush(const IOOptions& /*options*/,
344 IODebugContext* /*dbg*/) override {
345 return IOStatus::OK();
346 }
347
348 IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
349 return file_->Fsync(options, dbg);
350 }
351
352 uint64_t GetFileSize(const IOOptions& /*options*/,
353 IODebugContext* /*dbg*/) override {
354 return file_->Size();
355 }
356
357 private:
358 inline size_t RequestToken(size_t bytes) {
359 if (rate_limiter_ && io_priority_ < Env::IO_TOTAL) {
360 bytes = std::min(
361 bytes, static_cast<size_t>(rate_limiter_->GetSingleBurstBytes()));
362 rate_limiter_->Request(bytes, io_priority_);
363 }
364 return bytes;
365 }
366
367 MemFile* file_;
368 bool use_direct_io_;
369 RateLimiter* rate_limiter_;
370 };
371
372 class MockEnvDirectory : public FSDirectory {
373 public:
374 IOStatus Fsync(const IOOptions& /*options*/,
375 IODebugContext* /*dbg*/) override {
376 return IOStatus::OK();
377 }
378 };
379
380 class MockEnvFileLock : public FileLock {
381 public:
382 explicit MockEnvFileLock(const std::string& fname) : fname_(fname) {}
383
384 std::string FileName() const { return fname_; }
385
386 private:
387 const std::string fname_;
388 };
389
390 class TestMemLogger : public Logger {
391 private:
392 std::unique_ptr<FSWritableFile> file_;
393 std::atomic_size_t log_size_;
394 static const uint64_t flush_every_seconds_ = 5;
395 std::atomic_uint_fast64_t last_flush_micros_;
396 Env* env_;
397 IOOptions options_;
398 IODebugContext* dbg_;
399 std::atomic<bool> flush_pending_;
400
401 public:
402 TestMemLogger(std::unique_ptr<FSWritableFile> f, Env* env,
403 const IOOptions& options, IODebugContext* dbg,
404 const InfoLogLevel log_level = InfoLogLevel::ERROR_LEVEL)
405 : Logger(log_level),
406 file_(std::move(f)),
407 log_size_(0),
408 last_flush_micros_(0),
409 env_(env),
410 options_(options),
411 dbg_(dbg),
412 flush_pending_(false) {}
413 ~TestMemLogger() override {}
414
415 void Flush() override {
416 if (flush_pending_) {
417 flush_pending_ = false;
418 }
419 last_flush_micros_ = env_->NowMicros();
420 }
421
422 using Logger::Logv;
423 void Logv(const char* format, va_list ap) override {
424 // We try twice: the first time with a fixed-size stack allocated buffer,
425 // and the second time with a much larger dynamically allocated buffer.
426 char buffer[500];
427 for (int iter = 0; iter < 2; iter++) {
428 char* base;
429 int bufsize;
430 if (iter == 0) {
431 bufsize = sizeof(buffer);
432 base = buffer;
433 } else {
434 bufsize = 30000;
435 base = new char[bufsize];
436 }
437 char* p = base;
438 char* limit = base + bufsize;
439
440 struct timeval now_tv;
441 gettimeofday(&now_tv, nullptr);
442 const time_t seconds = now_tv.tv_sec;
443 struct tm t;
444 memset(&t, 0, sizeof(t));
445 struct tm* ret __attribute__((__unused__));
446 ret = localtime_r(&seconds, &t);
447 assert(ret);
448 p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d ",
449 t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
450 t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec));
451
452 // Print the message
453 if (p < limit) {
454 va_list backup_ap;
455 va_copy(backup_ap, ap);
456 p += vsnprintf(p, limit - p, format, backup_ap);
457 va_end(backup_ap);
458 }
459
460 // Truncate to available space if necessary
461 if (p >= limit) {
462 if (iter == 0) {
463 continue; // Try again with larger buffer
464 } else {
465 p = limit - 1;
466 }
467 }
468
469 // Add newline if necessary
470 if (p == base || p[-1] != '\n') {
471 *p++ = '\n';
472 }
473
474 assert(p <= limit);
475 const size_t write_size = p - base;
476
477 Status s = file_->Append(Slice(base, write_size), options_, dbg_);
478 if (s.ok()) {
479 flush_pending_ = true;
480 log_size_ += write_size;
481 }
482 uint64_t now_micros =
483 static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
484 if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
485 flush_pending_ = false;
486 last_flush_micros_ = now_micros;
487 }
488 if (base != buffer) {
489 delete[] base;
490 }
491 break;
492 }
493 }
494 size_t GetLogFileSize() const override { return log_size_; }
495 };
496
497 class MockFileSystem : public FileSystem {
498 public:
499 explicit MockFileSystem(Env* env, bool supports_direct_io = true)
500 : env_(env), supports_direct_io_(supports_direct_io) {}
501
502 ~MockFileSystem() override {
503 for (auto i = file_map_.begin(); i != file_map_.end(); ++i) {
504 i->second->Unref();
505 }
506 }
507
508 const char* Name() const override { return "Memory"; }
509 IOStatus NewSequentialFile(const std::string& f, const FileOptions& file_opts,
510 std::unique_ptr<FSSequentialFile>* r,
511 IODebugContext* dbg) override;
512 IOStatus NewRandomAccessFile(const std::string& f,
513 const FileOptions& file_opts,
514 std::unique_ptr<FSRandomAccessFile>* r,
515 IODebugContext* dbg) override;
516
517 IOStatus NewRandomRWFile(const std::string& fname,
518 const FileOptions& file_opts,
519 std::unique_ptr<FSRandomRWFile>* result,
520 IODebugContext* dbg) override;
521 IOStatus ReuseWritableFile(const std::string& fname,
522 const std::string& old_fname,
523 const FileOptions& file_opts,
524 std::unique_ptr<FSWritableFile>* result,
525 IODebugContext* dbg) override;
526 IOStatus NewWritableFile(const std::string& fname,
527 const FileOptions& file_opts,
528 std::unique_ptr<FSWritableFile>* result,
529 IODebugContext* dbg) override;
530 IOStatus ReopenWritableFile(const std::string& fname,
531 const FileOptions& options,
532 std::unique_ptr<FSWritableFile>* result,
533 IODebugContext* dbg) override;
534 IOStatus NewDirectory(const std::string& /*name*/, const IOOptions& io_opts,
535 std::unique_ptr<FSDirectory>* result,
536 IODebugContext* dbg) override;
537 IOStatus FileExists(const std::string& fname, const IOOptions& /*io_opts*/,
538 IODebugContext* /*dbg*/) override;
539 IOStatus GetChildren(const std::string& dir, const IOOptions& options,
540 std::vector<std::string>* result,
541 IODebugContext* dbg) override;
542 IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
543 IODebugContext* dbg) override;
544 IOStatus Truncate(const std::string& fname, size_t size,
545 const IOOptions& options, IODebugContext* dbg) override;
546 IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
547 IODebugContext* dbg) override;
548 IOStatus CreateDirIfMissing(const std::string& dirname,
549 const IOOptions& options,
550 IODebugContext* dbg) override;
551 IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
552 IODebugContext* dbg) override;
553
554 IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
555 uint64_t* file_size, IODebugContext* dbg) override;
556
557 IOStatus GetFileModificationTime(const std::string& fname,
558 const IOOptions& options,
559 uint64_t* file_mtime,
560 IODebugContext* dbg) override;
561 IOStatus RenameFile(const std::string& src, const std::string& target,
562 const IOOptions& options, IODebugContext* dbg) override;
563 IOStatus LinkFile(const std::string& /*src*/, const std::string& /*target*/,
564 const IOOptions& /*options*/,
565 IODebugContext* /*dbg*/) override;
566 IOStatus LockFile(const std::string& fname, const IOOptions& options,
567 FileLock** lock, IODebugContext* dbg) override;
568 IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
569 IODebugContext* dbg) override;
570 IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
571 IODebugContext* dbg) override;
572 IOStatus NewLogger(const std::string& fname, const IOOptions& io_opts,
573 std::shared_ptr<Logger>* result,
574 IODebugContext* dbg) override;
575 // Get full directory name for this db.
576 IOStatus GetAbsolutePath(const std::string& db_path,
577 const IOOptions& /*options*/,
578 std::string* output_path,
579 IODebugContext* /*dbg*/) override {
580 *output_path = NormalizeMockPath(db_path);
581 if (output_path->at(0) != '/') {
582 return IOStatus::NotSupported("GetAbsolutePath");
583 } else {
584 return IOStatus::OK();
585 }
586 }
587 IOStatus IsDirectory(const std::string& /*path*/,
588 const IOOptions& /*options*/, bool* /*is_dir*/,
589 IODebugContext* /*dgb*/) override {
590 return IOStatus::NotSupported("IsDirectory");
591 }
592
593 Status CorruptBuffer(const std::string& fname);
594
595 private:
596 bool RenameFileInternal(const std::string& src, const std::string& dest);
597 void DeleteFileInternal(const std::string& fname);
598 bool GetChildrenInternal(const std::string& fname,
599 std::vector<std::string>* results);
600
601 std::string NormalizeMockPath(const std::string& path) {
602 std::string p = NormalizePath(path);
603 if (p.back() == kFilePathSeparator && p.size() > 1) {
604 p.pop_back();
605 }
606 return p;
607 }
608
609 private:
610 // Map from filenames to MemFile objects, representing a simple file system.
611 port::Mutex mutex_;
612 std::map<std::string, MemFile*> file_map_; // Protected by mutex_.
613 Env* env_;
614 bool supports_direct_io_;
615 };
616
617 } // Anonymous namespace
618 // Partial implementation of the Env interface.
619 IOStatus MockFileSystem::NewSequentialFile(
620 const std::string& fname, const FileOptions& file_opts,
621 std::unique_ptr<FSSequentialFile>* result, IODebugContext* /*dbg*/) {
622 auto fn = NormalizeMockPath(fname);
623
624 MutexLock lock(&mutex_);
625 if (file_map_.find(fn) == file_map_.end()) {
626 *result = nullptr;
627 return IOStatus::PathNotFound(fn);
628 }
629 auto* f = file_map_[fn];
630 if (f->is_lock_file()) {
631 return IOStatus::InvalidArgument(fn, "Cannot open a lock file.");
632 } else if (file_opts.use_direct_reads && !supports_direct_io_) {
633 return IOStatus::NotSupported("Direct I/O Not Supported");
634 } else {
635 result->reset(new MockSequentialFile(f, file_opts));
636 return IOStatus::OK();
637 }
638 }
639
640 IOStatus MockFileSystem::NewRandomAccessFile(
641 const std::string& fname, const FileOptions& file_opts,
642 std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* /*dbg*/) {
643 auto fn = NormalizeMockPath(fname);
644 MutexLock lock(&mutex_);
645 if (file_map_.find(fn) == file_map_.end()) {
646 *result = nullptr;
647 return IOStatus::PathNotFound(fn);
648 }
649 auto* f = file_map_[fn];
650 if (f->is_lock_file()) {
651 return IOStatus::InvalidArgument(fn, "Cannot open a lock file.");
652 } else if (file_opts.use_direct_reads && !supports_direct_io_) {
653 return IOStatus::NotSupported("Direct I/O Not Supported");
654 } else {
655 result->reset(new MockRandomAccessFile(f, file_opts));
656 return IOStatus::OK();
657 }
658 }
659
660 IOStatus MockFileSystem::NewRandomRWFile(
661 const std::string& fname, const FileOptions& /*file_opts*/,
662 std::unique_ptr<FSRandomRWFile>* result, IODebugContext* /*dbg*/) {
663 auto fn = NormalizeMockPath(fname);
664 MutexLock lock(&mutex_);
665 if (file_map_.find(fn) == file_map_.end()) {
666 *result = nullptr;
667 return IOStatus::PathNotFound(fn);
668 }
669 auto* f = file_map_[fn];
670 if (f->is_lock_file()) {
671 return IOStatus::InvalidArgument(fn, "Cannot open a lock file.");
672 }
673 result->reset(new MockRandomRWFile(f));
674 return IOStatus::OK();
675 }
676
677 IOStatus MockFileSystem::ReuseWritableFile(
678 const std::string& fname, const std::string& old_fname,
679 const FileOptions& options, std::unique_ptr<FSWritableFile>* result,
680 IODebugContext* dbg) {
681 auto s = RenameFile(old_fname, fname, IOOptions(), dbg);
682 if (!s.ok()) {
683 return s;
684 } else {
685 result->reset();
686 return NewWritableFile(fname, options, result, dbg);
687 }
688 }
689
690 IOStatus MockFileSystem::NewWritableFile(
691 const std::string& fname, const FileOptions& file_opts,
692 std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) {
693 auto fn = NormalizeMockPath(fname);
694 MutexLock lock(&mutex_);
695 if (file_map_.find(fn) != file_map_.end()) {
696 DeleteFileInternal(fn);
697 }
698 MemFile* file = new MemFile(env_, fn, false);
699 file->Ref();
700 file_map_[fn] = file;
701 if (file_opts.use_direct_writes && !supports_direct_io_) {
702 return IOStatus::NotSupported("Direct I/O Not Supported");
703 } else {
704 result->reset(new MockWritableFile(file, file_opts));
705 return IOStatus::OK();
706 }
707 }
708
709 IOStatus MockFileSystem::ReopenWritableFile(
710 const std::string& fname, const FileOptions& file_opts,
711 std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) {
712 auto fn = NormalizeMockPath(fname);
713 MutexLock lock(&mutex_);
714 MemFile* file = nullptr;
715 if (file_map_.find(fn) == file_map_.end()) {
716 file = new MemFile(env_, fn, false);
717 file_map_[fn] = file;
718 } else {
719 file = file_map_[fn];
720 }
721 file->Ref();
722 if (file_opts.use_direct_writes && !supports_direct_io_) {
723 return IOStatus::NotSupported("Direct I/O Not Supported");
724 } else {
725 result->reset(new MockWritableFile(file, file_opts));
726 return IOStatus::OK();
727 }
728 }
729
730 IOStatus MockFileSystem::NewDirectory(const std::string& /*name*/,
731 const IOOptions& /*io_opts*/,
732 std::unique_ptr<FSDirectory>* result,
733 IODebugContext* /*dbg*/) {
734 result->reset(new MockEnvDirectory());
735 return IOStatus::OK();
736 }
737
738 IOStatus MockFileSystem::FileExists(const std::string& fname,
739 const IOOptions& /*io_opts*/,
740 IODebugContext* /*dbg*/) {
741 auto fn = NormalizeMockPath(fname);
742 MutexLock lock(&mutex_);
743 if (file_map_.find(fn) != file_map_.end()) {
744 // File exists
745 return IOStatus::OK();
746 }
747 // Now also check if fn exists as a dir
748 for (const auto& iter : file_map_) {
749 const std::string& filename = iter.first;
750 if (filename.size() >= fn.size() + 1 && filename[fn.size()] == '/' &&
751 Slice(filename).starts_with(Slice(fn))) {
752 return IOStatus::OK();
753 }
754 }
755 return IOStatus::NotFound();
756 }
757
758 bool MockFileSystem::GetChildrenInternal(const std::string& dir,
759 std::vector<std::string>* result) {
760 auto d = NormalizeMockPath(dir);
761 bool found_dir = false;
762 result->clear();
763 for (const auto& iter : file_map_) {
764 const std::string& filename = iter.first;
765
766 if (filename == d) {
767 found_dir = true;
768 } else if (filename.size() >= d.size() + 1 && filename[d.size()] == '/' &&
769 Slice(filename).starts_with(Slice(d))) {
770 found_dir = true;
771 size_t next_slash = filename.find('/', d.size() + 1);
772 if (next_slash != std::string::npos) {
773 result->push_back(
774 filename.substr(d.size() + 1, next_slash - d.size() - 1));
775 } else {
776 result->push_back(filename.substr(d.size() + 1));
777 }
778 }
779 }
780 result->erase(std::unique(result->begin(), result->end()), result->end());
781 return found_dir;
782 }
783
784 IOStatus MockFileSystem::GetChildren(const std::string& dir,
785 const IOOptions& /*options*/,
786 std::vector<std::string>* result,
787 IODebugContext* /*dbg*/) {
788 MutexLock lock(&mutex_);
789 bool found_dir = GetChildrenInternal(dir, result);
790 return found_dir ? IOStatus::OK() : IOStatus::NotFound(dir);
791 }
792
793 void MockFileSystem::DeleteFileInternal(const std::string& fname) {
794 assert(fname == NormalizeMockPath(fname));
795 const auto& pair = file_map_.find(fname);
796 if (pair != file_map_.end()) {
797 pair->second->Unref();
798 file_map_.erase(fname);
799 }
800 }
801
802 IOStatus MockFileSystem::DeleteFile(const std::string& fname,
803 const IOOptions& /*options*/,
804 IODebugContext* /*dbg*/) {
805 auto fn = NormalizeMockPath(fname);
806 MutexLock lock(&mutex_);
807 if (file_map_.find(fn) == file_map_.end()) {
808 return IOStatus::PathNotFound(fn);
809 }
810
811 DeleteFileInternal(fn);
812 return IOStatus::OK();
813 }
814
815 IOStatus MockFileSystem::Truncate(const std::string& fname, size_t size,
816 const IOOptions& options,
817 IODebugContext* dbg) {
818 auto fn = NormalizeMockPath(fname);
819 MutexLock lock(&mutex_);
820 auto iter = file_map_.find(fn);
821 if (iter == file_map_.end()) {
822 return IOStatus::PathNotFound(fn);
823 }
824 iter->second->Truncate(size, options, dbg);
825 return IOStatus::OK();
826 }
827
828 IOStatus MockFileSystem::CreateDir(const std::string& dirname,
829 const IOOptions& /*options*/,
830 IODebugContext* /*dbg*/) {
831 auto dn = NormalizeMockPath(dirname);
832 MutexLock lock(&mutex_);
833 if (file_map_.find(dn) == file_map_.end()) {
834 MemFile* file = new MemFile(env_, dn, false);
835 file->Ref();
836 file_map_[dn] = file;
837 } else {
838 return IOStatus::IOError();
839 }
840 return IOStatus::OK();
841 }
842
843 IOStatus MockFileSystem::CreateDirIfMissing(const std::string& dirname,
844 const IOOptions& options,
845 IODebugContext* dbg) {
846 CreateDir(dirname, options, dbg).PermitUncheckedError();
847 return IOStatus::OK();
848 }
849
850 IOStatus MockFileSystem::DeleteDir(const std::string& dirname,
851 const IOOptions& /*options*/,
852 IODebugContext* /*dbg*/) {
853 auto dir = NormalizeMockPath(dirname);
854 MutexLock lock(&mutex_);
855 if (file_map_.find(dir) == file_map_.end()) {
856 return IOStatus::PathNotFound(dir);
857 } else {
858 std::vector<std::string> children;
859 if (GetChildrenInternal(dir, &children)) {
860 for (const auto& child : children) {
861 DeleteFileInternal(child);
862 }
863 }
864 DeleteFileInternal(dir);
865 return IOStatus::OK();
866 }
867 }
868
869 IOStatus MockFileSystem::GetFileSize(const std::string& fname,
870 const IOOptions& /*options*/,
871 uint64_t* file_size,
872 IODebugContext* /*dbg*/) {
873 auto fn = NormalizeMockPath(fname);
874 MutexLock lock(&mutex_);
875 auto iter = file_map_.find(fn);
876 if (iter == file_map_.end()) {
877 return IOStatus::PathNotFound(fn);
878 }
879
880 *file_size = iter->second->Size();
881 return IOStatus::OK();
882 }
883
884 IOStatus MockFileSystem::GetFileModificationTime(const std::string& fname,
885 const IOOptions& /*options*/,
886 uint64_t* time,
887 IODebugContext* /*dbg*/) {
888 auto fn = NormalizeMockPath(fname);
889 MutexLock lock(&mutex_);
890 auto iter = file_map_.find(fn);
891 if (iter == file_map_.end()) {
892 return IOStatus::PathNotFound(fn);
893 }
894 *time = iter->second->ModifiedTime();
895 return IOStatus::OK();
896 }
897
898 bool MockFileSystem::RenameFileInternal(const std::string& src,
899 const std::string& dest) {
900 if (file_map_.find(src) == file_map_.end()) {
901 return false;
902 } else {
903 std::vector<std::string> children;
904 if (GetChildrenInternal(src, &children)) {
905 for (const auto& child : children) {
906 RenameFileInternal(src + "/" + child, dest + "/" + child);
907 }
908 }
909 DeleteFileInternal(dest);
910 file_map_[dest] = file_map_[src];
911 file_map_.erase(src);
912 return true;
913 }
914 }
915
916 IOStatus MockFileSystem::RenameFile(const std::string& src,
917 const std::string& dest,
918 const IOOptions& /*options*/,
919 IODebugContext* /*dbg*/) {
920 auto s = NormalizeMockPath(src);
921 auto t = NormalizeMockPath(dest);
922 MutexLock lock(&mutex_);
923 bool found = RenameFileInternal(s, t);
924 if (!found) {
925 return IOStatus::PathNotFound(s);
926 } else {
927 return IOStatus::OK();
928 }
929 }
930
931 IOStatus MockFileSystem::LinkFile(const std::string& src,
932 const std::string& dest,
933 const IOOptions& /*options*/,
934 IODebugContext* /*dbg*/) {
935 auto s = NormalizeMockPath(src);
936 auto t = NormalizeMockPath(dest);
937 MutexLock lock(&mutex_);
938 if (file_map_.find(s) == file_map_.end()) {
939 return IOStatus::PathNotFound(s);
940 }
941
942 DeleteFileInternal(t);
943 file_map_[t] = file_map_[s];
944 file_map_[t]->Ref(); // Otherwise it might get deleted when noone uses s
945 return IOStatus::OK();
946 }
947
948 IOStatus MockFileSystem::NewLogger(const std::string& fname,
949 const IOOptions& io_opts,
950 std::shared_ptr<Logger>* result,
951 IODebugContext* dbg) {
952 auto fn = NormalizeMockPath(fname);
953 MutexLock lock(&mutex_);
954 auto iter = file_map_.find(fn);
955 MemFile* file = nullptr;
956 if (iter == file_map_.end()) {
957 file = new MemFile(env_, fn, false);
958 file->Ref();
959 file_map_[fn] = file;
960 } else {
961 file = iter->second;
962 }
963 std::unique_ptr<FSWritableFile> f(new MockWritableFile(file, FileOptions()));
964 result->reset(new TestMemLogger(std::move(f), env_, io_opts, dbg));
965 return IOStatus::OK();
966 }
967
968 IOStatus MockFileSystem::LockFile(const std::string& fname,
969 const IOOptions& /*options*/,
970 FileLock** flock, IODebugContext* /*dbg*/) {
971 auto fn = NormalizeMockPath(fname);
972 {
973 MutexLock lock(&mutex_);
974 if (file_map_.find(fn) != file_map_.end()) {
975 if (!file_map_[fn]->is_lock_file()) {
976 return IOStatus::InvalidArgument(fname, "Not a lock file.");
977 }
978 if (!file_map_[fn]->Lock()) {
979 return IOStatus::IOError(fn, "lock is already held.");
980 }
981 } else {
982 auto* file = new MemFile(env_, fn, true);
983 file->Ref();
984 file->Lock();
985 file_map_[fn] = file;
986 }
987 }
988 *flock = new MockEnvFileLock(fn);
989 return IOStatus::OK();
990 }
991
992 IOStatus MockFileSystem::UnlockFile(FileLock* flock,
993 const IOOptions& /*options*/,
994 IODebugContext* /*dbg*/) {
995 std::string fn = static_cast_with_check<MockEnvFileLock>(flock)->FileName();
996 {
997 MutexLock lock(&mutex_);
998 if (file_map_.find(fn) != file_map_.end()) {
999 if (!file_map_[fn]->is_lock_file()) {
1000 return IOStatus::InvalidArgument(fn, "Not a lock file.");
1001 }
1002 file_map_[fn]->Unlock();
1003 }
1004 }
1005 delete flock;
1006 return IOStatus::OK();
1007 }
1008
1009 IOStatus MockFileSystem::GetTestDirectory(const IOOptions& /*options*/,
1010 std::string* path,
1011 IODebugContext* /*dbg*/) {
1012 *path = "/test";
1013 return IOStatus::OK();
1014 }
1015
1016 Status MockFileSystem::CorruptBuffer(const std::string& fname) {
1017 auto fn = NormalizeMockPath(fname);
1018 MutexLock lock(&mutex_);
1019 auto iter = file_map_.find(fn);
1020 if (iter == file_map_.end()) {
1021 return Status::IOError(fn, "File not found");
1022 }
1023 iter->second->CorruptBuffer();
1024 return Status::OK();
1025 }
1026
1027 MockEnv::MockEnv(Env* base_env)
1028 : CompositeEnvWrapper(base_env, std::make_shared<MockFileSystem>(this)),
1029 fake_sleep_micros_(0) {}
1030
1031 Status MockEnv::GetCurrentTime(int64_t* unix_time) {
1032 auto s = CompositeEnvWrapper::GetCurrentTime(unix_time);
1033 if (s.ok()) {
1034 *unix_time += fake_sleep_micros_.load() / (1000 * 1000);
1035 }
1036 return s;
1037 }
1038
1039 uint64_t MockEnv::NowMicros() {
1040 return CompositeEnvWrapper::NowMicros() + fake_sleep_micros_.load();
1041 }
1042
1043 uint64_t MockEnv::NowNanos() {
1044 return CompositeEnvWrapper::NowNanos() + fake_sleep_micros_.load() * 1000;
1045 }
1046
1047 Status MockEnv::CorruptBuffer(const std::string& fname) {
1048 auto mock = static_cast_with_check<MockFileSystem>(GetFileSystem().get());
1049 return mock->CorruptBuffer(fname);
1050 }
1051
1052 void MockEnv::FakeSleepForMicroseconds(int64_t micros) {
1053 fake_sleep_micros_.fetch_add(micros);
1054 }
1055
1056 #ifndef ROCKSDB_LITE
1057 // This is to maintain the behavior before swithcing from InMemoryEnv to MockEnv
1058 Env* NewMemEnv(Env* base_env) { return new MockEnv(base_env); }
1059
1060 #else // ROCKSDB_LITE
1061
1062 Env* NewMemEnv(Env* /*base_env*/) { return nullptr; }
1063
1064 #endif // !ROCKSDB_LITE
1065
1066 } // namespace ROCKSDB_NAMESPACE