]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/env/mock_env.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / env / mock_env.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
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).
7c673cae
FG
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"
20effc67 11
7c673cae
FG
12#include <algorithm>
13#include <chrono>
20effc67
TL
14
15#include "file/filename.h"
7c673cae 16#include "port/sys_time.h"
20effc67 17#include "rocksdb/file_system.h"
11fdf7f2 18#include "util/cast_util.h"
20effc67 19#include "util/hash.h"
7c673cae
FG
20#include "util/random.h"
21#include "util/rate_limiter.h"
22
f67539c2 23namespace ROCKSDB_NAMESPACE {
7c673cae
FG
24
25class 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()),
20effc67 35 rnd_(Lower32of64(GetSliceNPHash64(fn))),
7c673cae 36 fsynced_bytes_(0) {}
f67539c2
TL
37 // No copying allowed.
38 MemFile(const MemFile&) = delete;
39 void operator=(const MemFile&) = delete;
7c673cae
FG
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
11fdf7f2 81 uint64_t Size() const { return size_; }
7c673cae 82
20effc67
TL
83 void Truncate(size_t size, const IOOptions& /*options*/,
84 IODebugContext* /*dbg*/) {
7c673cae
FG
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) {
11fdf7f2 102 data_[static_cast<size_t>(pos)] = static_cast<char>(rnd_.Uniform(256));
7c673cae
FG
103 }
104 }
105
20effc67
TL
106 IOStatus Read(uint64_t offset, size_t n, const IOOptions& /*options*/,
107 Slice* result, char* scratch, IODebugContext* /*dbg*/) const {
7c673cae
FG
108 MutexLock lock(&mutex_);
109 const uint64_t available = Size() - std::min(Size(), offset);
11fdf7f2 110 size_t offset_ = static_cast<size_t>(offset);
7c673cae 111 if (n > available) {
11fdf7f2 112 n = static_cast<size_t>(available);
7c673cae
FG
113 }
114 if (n == 0) {
115 *result = Slice();
20effc67 116 return IOStatus::OK();
7c673cae
FG
117 }
118 if (scratch) {
11fdf7f2 119 memcpy(scratch, &(data_[offset_]), n);
7c673cae
FG
120 *result = Slice(scratch, n);
121 } else {
11fdf7f2 122 *result = Slice(&(data_[offset_]), n);
7c673cae 123 }
20effc67 124 return IOStatus::OK();
7c673cae
FG
125 }
126
20effc67
TL
127 IOStatus Write(uint64_t offset, const Slice& data,
128 const IOOptions& /*options*/, IODebugContext* /*dbg*/) {
7c673cae 129 MutexLock lock(&mutex_);
11fdf7f2 130 size_t offset_ = static_cast<size_t>(offset);
7c673cae 131 if (offset + data.size() > data_.size()) {
11fdf7f2 132 data_.resize(offset_ + data.size());
7c673cae 133 }
11fdf7f2 134 data_.replace(offset_, data.size(), data.data(), data.size());
7c673cae
FG
135 size_ = data_.size();
136 modified_time_ = Now();
20effc67 137 return IOStatus::OK();
7c673cae
FG
138 }
139
20effc67
TL
140 IOStatus Append(const Slice& data, const IOOptions& /*options*/,
141 IODebugContext* /*dbg*/) {
7c673cae
FG
142 MutexLock lock(&mutex_);
143 data_.append(data.data(), data.size());
144 size_ = data_.size();
145 modified_time_ = Now();
20effc67 146 return IOStatus::OK();
7c673cae
FG
147 }
148
20effc67 149 IOStatus Fsync(const IOOptions& /*options*/, IODebugContext* /*dbg*/) {
7c673cae 150 fsynced_bytes_ = size_.load();
20effc67 151 return IOStatus::OK();
7c673cae
FG
152 }
153
11fdf7f2 154 uint64_t ModifiedTime() const { return modified_time_; }
7c673cae
FG
155
156 private:
157 uint64_t Now() {
11fdf7f2 158 int64_t unix_time = 0;
7c673cae
FG
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.
11fdf7f2 165 ~MemFile() { assert(refs_ == 0); }
7c673cae 166
7c673cae
FG
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
184namespace {
185
20effc67 186class MockSequentialFile : public FSSequentialFile {
7c673cae 187 public:
20effc67
TL
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) {
7c673cae
FG
193 file_->Ref();
194 }
195
494da23a 196 ~MockSequentialFile() override { file_->Unref(); }
7c673cae 197
20effc67
TL
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);
7c673cae
FG
202 if (s.ok()) {
203 pos_ += result->size();
204 }
205 return s;
206 }
207
20effc67
TL
208 bool use_direct_io() const override { return use_direct_io_; }
209 IOStatus Skip(uint64_t n) override {
7c673cae 210 if (pos_ > file_->Size()) {
20effc67 211 return IOStatus::IOError("pos_ > file_->Size()");
7c673cae 212 }
11fdf7f2 213 const uint64_t available = file_->Size() - pos_;
7c673cae
FG
214 if (n > available) {
215 n = available;
216 }
11fdf7f2 217 pos_ += static_cast<size_t>(n);
20effc67 218 return IOStatus::OK();
7c673cae
FG
219 }
220
221 private:
222 MemFile* file_;
20effc67
TL
223 bool use_direct_io_;
224 bool use_mmap_read_;
7c673cae
FG
225 size_t pos_;
226};
227
20effc67 228class MockRandomAccessFile : public FSRandomAccessFile {
7c673cae 229 public:
20effc67
TL
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 }
7c673cae 236
494da23a 237 ~MockRandomAccessFile() override { file_->Unref(); }
7c673cae 238
20effc67
TL
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 }
7c673cae
FG
255 }
256
257 private:
258 MemFile* file_;
20effc67
TL
259 bool use_direct_io_;
260 bool use_mmap_read_;
7c673cae
FG
261};
262
20effc67 263class MockRandomRWFile : public FSRandomRWFile {
7c673cae
FG
264 public:
265 explicit MockRandomRWFile(MemFile* file) : file_(file) { file_->Ref(); }
266
494da23a 267 ~MockRandomRWFile() override { file_->Unref(); }
7c673cae 268
20effc67
TL
269 IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options,
270 IODebugContext* dbg) override {
271 return file_->Write(offset, data, options, dbg);
7c673cae
FG
272 }
273
20effc67
TL
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);
7c673cae
FG
278 }
279
20effc67
TL
280 IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
281 return file_->Fsync(options, dbg);
282 }
7c673cae 283
20effc67
TL
284 IOStatus Flush(const IOOptions& /*options*/,
285 IODebugContext* /*dbg*/) override {
286 return IOStatus::OK();
287 }
7c673cae 288
20effc67
TL
289 IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
290 return file_->Fsync(options, dbg);
291 }
7c673cae
FG
292
293 private:
294 MemFile* file_;
295};
296
20effc67 297class MockWritableFile : public FSWritableFile {
7c673cae 298 public:
20effc67
TL
299 MockWritableFile(MemFile* file, const FileOptions& opts)
300 : file_(file),
301 use_direct_io_(opts.use_direct_writes),
302 rate_limiter_(opts.rate_limiter) {
7c673cae
FG
303 file_->Ref();
304 }
305
494da23a 306 ~MockWritableFile() override { file_->Unref(); }
7c673cae 307
20effc67
TL
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 {
11fdf7f2 313 size_t bytes_written = 0;
7c673cae
FG
314 while (bytes_written < data.size()) {
315 auto bytes = RequestToken(data.size() - bytes_written);
20effc67
TL
316 IOStatus s = file_->Append(Slice(data.data() + bytes_written, bytes),
317 options, dbg);
7c673cae
FG
318 if (!s.ok()) {
319 return s;
320 }
321 bytes_written += bytes;
322 }
20effc67
TL
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();
7c673cae 338 }
20effc67
TL
339 IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
340 return file_->Fsync(options, dbg);
7c673cae 341 }
7c673cae 342
20effc67
TL
343 IOStatus Flush(const IOOptions& /*options*/,
344 IODebugContext* /*dbg*/) override {
345 return IOStatus::OK();
346 }
7c673cae 347
20effc67
TL
348 IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
349 return file_->Fsync(options, dbg);
350 }
7c673cae 351
20effc67
TL
352 uint64_t GetFileSize(const IOOptions& /*options*/,
353 IODebugContext* /*dbg*/) override {
354 return file_->Size();
355 }
7c673cae
FG
356
357 private:
358 inline size_t RequestToken(size_t bytes) {
359 if (rate_limiter_ && io_priority_ < Env::IO_TOTAL) {
11fdf7f2
TL
360 bytes = std::min(
361 bytes, static_cast<size_t>(rate_limiter_->GetSingleBurstBytes()));
7c673cae
FG
362 rate_limiter_->Request(bytes, io_priority_);
363 }
364 return bytes;
365 }
366
367 MemFile* file_;
20effc67 368 bool use_direct_io_;
7c673cae
FG
369 RateLimiter* rate_limiter_;
370};
371
20effc67 372class MockEnvDirectory : public FSDirectory {
7c673cae 373 public:
20effc67
TL
374 IOStatus Fsync(const IOOptions& /*options*/,
375 IODebugContext* /*dbg*/) override {
376 return IOStatus::OK();
377 }
7c673cae
FG
378};
379
380class MockEnvFileLock : public FileLock {
381 public:
11fdf7f2 382 explicit MockEnvFileLock(const std::string& fname) : fname_(fname) {}
7c673cae 383
11fdf7f2 384 std::string FileName() const { return fname_; }
7c673cae
FG
385
386 private:
387 const std::string fname_;
388};
389
390class TestMemLogger : public Logger {
391 private:
20effc67 392 std::unique_ptr<FSWritableFile> file_;
7c673cae
FG
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_;
20effc67
TL
397 IOOptions options_;
398 IODebugContext* dbg_;
494da23a 399 std::atomic<bool> flush_pending_;
7c673cae
FG
400
401 public:
20effc67
TL
402 TestMemLogger(std::unique_ptr<FSWritableFile> f, Env* env,
403 const IOOptions& options, IODebugContext* dbg,
7c673cae
FG
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),
20effc67
TL
410 options_(options),
411 dbg_(dbg),
7c673cae 412 flush_pending_(false) {}
494da23a 413 ~TestMemLogger() override {}
7c673cae 414
494da23a 415 void Flush() override {
7c673cae
FG
416 if (flush_pending_) {
417 flush_pending_ = false;
418 }
419 last_flush_micros_ = env_->NowMicros();
420 }
421
422 using Logger::Logv;
494da23a 423 void Logv(const char* format, va_list ap) override {
7c673cae
FG
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;
11fdf7f2
TL
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));
7c673cae
FG
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) {
11fdf7f2 463 continue; // Try again with larger buffer
7c673cae
FG
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
20effc67
TL
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 }
11fdf7f2
TL
482 uint64_t now_micros =
483 static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
7c673cae
FG
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
20effc67
TL
497class MockFileSystem : public FileSystem {
498 public:
499 explicit MockFileSystem(Env* env, bool supports_direct_io = true)
500 : env_(env), supports_direct_io_(supports_direct_io) {}
7c673cae 501
20effc67
TL
502 ~MockFileSystem() override {
503 for (auto i = file_map_.begin(); i != file_map_.end(); ++i) {
504 i->second->Unref();
505 }
506 }
7c673cae 507
20effc67
TL
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 }
7c673cae 586 }
20effc67
TL
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};
7c673cae 616
20effc67 617} // Anonymous namespace
11fdf7f2 618// Partial implementation of the Env interface.
20effc67
TL
619IOStatus 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
7c673cae
FG
624 MutexLock lock(&mutex_);
625 if (file_map_.find(fn) == file_map_.end()) {
11fdf7f2 626 *result = nullptr;
20effc67 627 return IOStatus::PathNotFound(fn);
7c673cae
FG
628 }
629 auto* f = file_map_[fn];
630 if (f->is_lock_file()) {
20effc67
TL
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();
7c673cae 637 }
7c673cae
FG
638}
639
20effc67
TL
640IOStatus MockFileSystem::NewRandomAccessFile(
641 const std::string& fname, const FileOptions& file_opts,
642 std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* /*dbg*/) {
643 auto fn = NormalizeMockPath(fname);
7c673cae
FG
644 MutexLock lock(&mutex_);
645 if (file_map_.find(fn) == file_map_.end()) {
11fdf7f2 646 *result = nullptr;
20effc67 647 return IOStatus::PathNotFound(fn);
7c673cae
FG
648 }
649 auto* f = file_map_[fn];
650 if (f->is_lock_file()) {
20effc67
TL
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();
7c673cae 657 }
7c673cae
FG
658}
659
20effc67
TL
660IOStatus MockFileSystem::NewRandomRWFile(
661 const std::string& fname, const FileOptions& /*file_opts*/,
662 std::unique_ptr<FSRandomRWFile>* result, IODebugContext* /*dbg*/) {
663 auto fn = NormalizeMockPath(fname);
7c673cae
FG
664 MutexLock lock(&mutex_);
665 if (file_map_.find(fn) == file_map_.end()) {
11fdf7f2 666 *result = nullptr;
20effc67 667 return IOStatus::PathNotFound(fn);
7c673cae
FG
668 }
669 auto* f = file_map_[fn];
670 if (f->is_lock_file()) {
20effc67 671 return IOStatus::InvalidArgument(fn, "Cannot open a lock file.");
7c673cae
FG
672 }
673 result->reset(new MockRandomRWFile(f));
20effc67 674 return IOStatus::OK();
7c673cae
FG
675}
676
20effc67
TL
677IOStatus 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);
7c673cae
FG
682 if (!s.ok()) {
683 return s;
20effc67
TL
684 } else {
685 result->reset();
686 return NewWritableFile(fname, options, result, dbg);
7c673cae 687 }
7c673cae
FG
688}
689
20effc67
TL
690IOStatus MockFileSystem::NewWritableFile(
691 const std::string& fname, const FileOptions& file_opts,
692 std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) {
693 auto fn = NormalizeMockPath(fname);
7c673cae
FG
694 MutexLock lock(&mutex_);
695 if (file_map_.find(fn) != file_map_.end()) {
696 DeleteFileInternal(fn);
697 }
20effc67 698 MemFile* file = new MemFile(env_, fn, false);
7c673cae
FG
699 file->Ref();
700 file_map_[fn] = file;
20effc67
TL
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}
7c673cae 708
20effc67
TL
709IOStatus 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 }
7c673cae
FG
728}
729
20effc67
TL
730IOStatus MockFileSystem::NewDirectory(const std::string& /*name*/,
731 const IOOptions& /*io_opts*/,
732 std::unique_ptr<FSDirectory>* result,
733 IODebugContext* /*dbg*/) {
7c673cae 734 result->reset(new MockEnvDirectory());
20effc67 735 return IOStatus::OK();
7c673cae
FG
736}
737
20effc67
TL
738IOStatus MockFileSystem::FileExists(const std::string& fname,
739 const IOOptions& /*io_opts*/,
740 IODebugContext* /*dbg*/) {
741 auto fn = NormalizeMockPath(fname);
7c673cae
FG
742 MutexLock lock(&mutex_);
743 if (file_map_.find(fn) != file_map_.end()) {
744 // File exists
20effc67 745 return IOStatus::OK();
7c673cae
FG
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;
11fdf7f2 750 if (filename.size() >= fn.size() + 1 && filename[fn.size()] == '/' &&
7c673cae 751 Slice(filename).starts_with(Slice(fn))) {
20effc67 752 return IOStatus::OK();
7c673cae
FG
753 }
754 }
20effc67 755 return IOStatus::NotFound();
7c673cae
FG
756}
757
20effc67
TL
758bool MockFileSystem::GetChildrenInternal(const std::string& dir,
759 std::vector<std::string>* result) {
760 auto d = NormalizeMockPath(dir);
7c673cae 761 bool found_dir = false;
20effc67
TL
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));
7c673cae
FG
777 }
778 }
779 }
780 result->erase(std::unique(result->begin(), result->end()), result->end());
20effc67 781 return found_dir;
7c673cae
FG
782}
783
20effc67
TL
784IOStatus 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
793void MockFileSystem::DeleteFileInternal(const std::string& fname) {
794 assert(fname == NormalizeMockPath(fname));
7c673cae
FG
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
20effc67
TL
802IOStatus MockFileSystem::DeleteFile(const std::string& fname,
803 const IOOptions& /*options*/,
804 IODebugContext* /*dbg*/) {
805 auto fn = NormalizeMockPath(fname);
7c673cae
FG
806 MutexLock lock(&mutex_);
807 if (file_map_.find(fn) == file_map_.end()) {
20effc67 808 return IOStatus::PathNotFound(fn);
7c673cae
FG
809 }
810
811 DeleteFileInternal(fn);
20effc67 812 return IOStatus::OK();
7c673cae
FG
813}
814
20effc67
TL
815IOStatus MockFileSystem::Truncate(const std::string& fname, size_t size,
816 const IOOptions& options,
817 IODebugContext* dbg) {
818 auto fn = NormalizeMockPath(fname);
11fdf7f2
TL
819 MutexLock lock(&mutex_);
820 auto iter = file_map_.find(fn);
821 if (iter == file_map_.end()) {
20effc67 822 return IOStatus::PathNotFound(fn);
11fdf7f2 823 }
20effc67
TL
824 iter->second->Truncate(size, options, dbg);
825 return IOStatus::OK();
11fdf7f2
TL
826}
827
20effc67
TL
828IOStatus MockFileSystem::CreateDir(const std::string& dirname,
829 const IOOptions& /*options*/,
830 IODebugContext* /*dbg*/) {
831 auto dn = NormalizeMockPath(dirname);
832 MutexLock lock(&mutex_);
7c673cae 833 if (file_map_.find(dn) == file_map_.end()) {
20effc67 834 MemFile* file = new MemFile(env_, dn, false);
7c673cae
FG
835 file->Ref();
836 file_map_[dn] = file;
837 } else {
20effc67 838 return IOStatus::IOError();
7c673cae 839 }
20effc67 840 return IOStatus::OK();
7c673cae
FG
841}
842
20effc67
TL
843IOStatus MockFileSystem::CreateDirIfMissing(const std::string& dirname,
844 const IOOptions& options,
845 IODebugContext* dbg) {
846 CreateDir(dirname, options, dbg).PermitUncheckedError();
847 return IOStatus::OK();
7c673cae
FG
848}
849
20effc67
TL
850IOStatus 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 }
7c673cae
FG
867}
868
20effc67
TL
869IOStatus MockFileSystem::GetFileSize(const std::string& fname,
870 const IOOptions& /*options*/,
871 uint64_t* file_size,
872 IODebugContext* /*dbg*/) {
873 auto fn = NormalizeMockPath(fname);
7c673cae
FG
874 MutexLock lock(&mutex_);
875 auto iter = file_map_.find(fn);
876 if (iter == file_map_.end()) {
20effc67 877 return IOStatus::PathNotFound(fn);
7c673cae
FG
878 }
879
880 *file_size = iter->second->Size();
20effc67 881 return IOStatus::OK();
7c673cae
FG
882}
883
20effc67
TL
884IOStatus MockFileSystem::GetFileModificationTime(const std::string& fname,
885 const IOOptions& /*options*/,
886 uint64_t* time,
887 IODebugContext* /*dbg*/) {
888 auto fn = NormalizeMockPath(fname);
7c673cae
FG
889 MutexLock lock(&mutex_);
890 auto iter = file_map_.find(fn);
891 if (iter == file_map_.end()) {
20effc67 892 return IOStatus::PathNotFound(fn);
7c673cae
FG
893 }
894 *time = iter->second->ModifiedTime();
20effc67 895 return IOStatus::OK();
7c673cae
FG
896}
897
20effc67
TL
898bool 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;
7c673cae 913 }
20effc67 914}
7c673cae 915
20effc67
TL
916IOStatus 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 }
7c673cae
FG
929}
930
20effc67
TL
931IOStatus 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);
7c673cae
FG
937 MutexLock lock(&mutex_);
938 if (file_map_.find(s) == file_map_.end()) {
20effc67 939 return IOStatus::PathNotFound(s);
7c673cae
FG
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
20effc67 945 return IOStatus::OK();
7c673cae
FG
946}
947
20effc67
TL
948IOStatus 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);
7c673cae
FG
953 MutexLock lock(&mutex_);
954 auto iter = file_map_.find(fn);
955 MemFile* file = nullptr;
956 if (iter == file_map_.end()) {
20effc67 957 file = new MemFile(env_, fn, false);
7c673cae
FG
958 file->Ref();
959 file_map_[fn] = file;
960 } else {
961 file = iter->second;
962 }
20effc67
TL
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();
7c673cae
FG
966}
967
20effc67
TL
968IOStatus MockFileSystem::LockFile(const std::string& fname,
969 const IOOptions& /*options*/,
970 FileLock** flock, IODebugContext* /*dbg*/) {
971 auto fn = NormalizeMockPath(fname);
7c673cae
FG
972 {
973 MutexLock lock(&mutex_);
974 if (file_map_.find(fn) != file_map_.end()) {
975 if (!file_map_[fn]->is_lock_file()) {
20effc67 976 return IOStatus::InvalidArgument(fname, "Not a lock file.");
7c673cae
FG
977 }
978 if (!file_map_[fn]->Lock()) {
20effc67 979 return IOStatus::IOError(fn, "lock is already held.");
7c673cae
FG
980 }
981 } else {
20effc67 982 auto* file = new MemFile(env_, fn, true);
7c673cae
FG
983 file->Ref();
984 file->Lock();
985 file_map_[fn] = file;
986 }
987 }
988 *flock = new MockEnvFileLock(fn);
20effc67 989 return IOStatus::OK();
7c673cae
FG
990}
991
20effc67
TL
992IOStatus MockFileSystem::UnlockFile(FileLock* flock,
993 const IOOptions& /*options*/,
994 IODebugContext* /*dbg*/) {
995 std::string fn = static_cast_with_check<MockEnvFileLock>(flock)->FileName();
7c673cae
FG
996 {
997 MutexLock lock(&mutex_);
998 if (file_map_.find(fn) != file_map_.end()) {
999 if (!file_map_[fn]->is_lock_file()) {
20effc67 1000 return IOStatus::InvalidArgument(fn, "Not a lock file.");
7c673cae
FG
1001 }
1002 file_map_[fn]->Unlock();
1003 }
1004 }
1005 delete flock;
20effc67 1006 return IOStatus::OK();
7c673cae
FG
1007}
1008
20effc67
TL
1009IOStatus MockFileSystem::GetTestDirectory(const IOOptions& /*options*/,
1010 std::string* path,
1011 IODebugContext* /*dbg*/) {
7c673cae 1012 *path = "/test";
20effc67
TL
1013 return IOStatus::OK();
1014}
1015
1016Status 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();
7c673cae
FG
1024 return Status::OK();
1025}
1026
20effc67
TL
1027MockEnv::MockEnv(Env* base_env)
1028 : CompositeEnvWrapper(base_env, std::make_shared<MockFileSystem>(this)),
1029 fake_sleep_micros_(0) {}
1030
7c673cae 1031Status MockEnv::GetCurrentTime(int64_t* unix_time) {
20effc67 1032 auto s = CompositeEnvWrapper::GetCurrentTime(unix_time);
11fdf7f2
TL
1033 if (s.ok()) {
1034 *unix_time += fake_sleep_micros_.load() / (1000 * 1000);
1035 }
7c673cae
FG
1036 return s;
1037}
1038
1039uint64_t MockEnv::NowMicros() {
20effc67 1040 return CompositeEnvWrapper::NowMicros() + fake_sleep_micros_.load();
7c673cae
FG
1041}
1042
1043uint64_t MockEnv::NowNanos() {
20effc67 1044 return CompositeEnvWrapper::NowNanos() + fake_sleep_micros_.load() * 1000;
7c673cae
FG
1045}
1046
7c673cae 1047Status MockEnv::CorruptBuffer(const std::string& fname) {
20effc67
TL
1048 auto mock = static_cast_with_check<MockFileSystem>(GetFileSystem().get());
1049 return mock->CorruptBuffer(fname);
7c673cae
FG
1050}
1051
1052void MockEnv::FakeSleepForMicroseconds(int64_t micros) {
1053 fake_sleep_micros_.fetch_add(micros);
1054}
1055
11fdf7f2
TL
1056#ifndef ROCKSDB_LITE
1057// This is to maintain the behavior before swithcing from InMemoryEnv to MockEnv
1058Env* NewMemEnv(Env* base_env) { return new MockEnv(base_env); }
1059
1060#else // ROCKSDB_LITE
1061
1062Env* NewMemEnv(Env* /*base_env*/) { return nullptr; }
1063
1064#endif // !ROCKSDB_LITE
1065
f67539c2 1066} // namespace ROCKSDB_NAMESPACE