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).
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.
10 #include "env/mock_env.h"
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"
23 namespace ROCKSDB_NAMESPACE
{
27 explicit MemFile(Env
* env
, const std::string
& fn
, bool _is_lock_file
= false)
31 is_lock_file_(_is_lock_file
),
34 modified_time_(Now()),
35 rnd_(Lower32of64(GetSliceNPHash64(fn
))),
37 // No copying allowed.
38 MemFile(const MemFile
&) = delete;
39 void operator=(const MemFile
&) = delete;
42 MutexLock
lock(&mutex_
);
46 bool is_lock_file() const { return is_lock_file_
; }
49 assert(is_lock_file_
);
50 MutexLock
lock(&mutex_
);
60 assert(is_lock_file_
);
61 MutexLock
lock(&mutex_
);
66 bool do_delete
= false;
68 MutexLock
lock(&mutex_
);
81 uint64_t Size() const { return size_
; }
83 void Truncate(size_t size
, const IOOptions
& /*options*/,
84 IODebugContext
* /*dbg*/) {
85 MutexLock
lock(&mutex_
);
92 void CorruptBuffer() {
93 if (fsynced_bytes_
>= size_
) {
96 uint64_t buffered_bytes
= size_
- fsynced_bytes_
;
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));
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
);
112 n
= static_cast<size_t>(available
);
116 return IOStatus::OK();
119 memcpy(scratch
, &(data_
[offset_
]), n
);
120 *result
= Slice(scratch
, n
);
122 *result
= Slice(&(data_
[offset_
]), n
);
124 return IOStatus::OK();
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());
134 data_
.replace(offset_
, data
.size(), data
.data(), data
.size());
135 size_
= data_
.size();
136 modified_time_
= Now();
137 return IOStatus::OK();
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();
149 IOStatus
Fsync(const IOOptions
& /*options*/, IODebugContext
* /*dbg*/) {
150 fsynced_bytes_
= size_
.load();
151 return IOStatus::OK();
154 uint64_t ModifiedTime() const { return modified_time_
; }
158 int64_t unix_time
= 0;
159 auto s
= env_
->GetCurrentTime(&unix_time
);
161 return static_cast<uint64_t>(unix_time
);
164 // Private since only Unref() should be used to delete it.
165 ~MemFile() { assert(refs_
== 0); }
168 const std::string fn_
;
169 mutable port::Mutex mutex_
;
174 // Data written into this file, all bytes before fsynced_bytes are
177 std::atomic
<uint64_t> size_
;
178 std::atomic
<uint64_t> modified_time_
;
181 std::atomic
<uint64_t> fsynced_bytes_
;
186 class MockSequentialFile
: public FSSequentialFile
{
188 explicit MockSequentialFile(MemFile
* file
, const FileOptions
& opts
)
190 use_direct_io_(opts
.use_direct_reads
),
191 use_mmap_read_(opts
.use_mmap_reads
),
196 ~MockSequentialFile() override
{ file_
->Unref(); }
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
);
203 pos_
+= result
->size();
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()");
213 const uint64_t available
= file_
->Size() - pos_
;
217 pos_
+= static_cast<size_t>(n
);
218 return IOStatus::OK();
228 class MockRandomAccessFile
: public FSRandomAccessFile
{
230 explicit MockRandomAccessFile(MemFile
* file
, const FileOptions
& opts
)
232 use_direct_io_(opts
.use_direct_reads
),
233 use_mmap_read_(opts
.use_mmap_reads
) {
237 ~MockRandomAccessFile() override
{ file_
->Unref(); }
239 bool use_direct_io() const override
{ return use_direct_io_
; }
241 IOStatus
Prefetch(uint64_t /*offset*/, size_t /*n*/,
242 const IOOptions
& /*options*/,
243 IODebugContext
* /*dbg*/) override
{
244 return IOStatus::OK();
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
);
253 return file_
->Read(offset
, n
, options
, result
, scratch
, dbg
);
263 class MockRandomRWFile
: public FSRandomRWFile
{
265 explicit MockRandomRWFile(MemFile
* file
) : file_(file
) { file_
->Ref(); }
267 ~MockRandomRWFile() override
{ file_
->Unref(); }
269 IOStatus
Write(uint64_t offset
, const Slice
& data
, const IOOptions
& options
,
270 IODebugContext
* dbg
) override
{
271 return file_
->Write(offset
, data
, options
, dbg
);
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
);
280 IOStatus
Close(const IOOptions
& options
, IODebugContext
* dbg
) override
{
281 return file_
->Fsync(options
, dbg
);
284 IOStatus
Flush(const IOOptions
& /*options*/,
285 IODebugContext
* /*dbg*/) override
{
286 return IOStatus::OK();
289 IOStatus
Sync(const IOOptions
& options
, IODebugContext
* dbg
) override
{
290 return file_
->Fsync(options
, dbg
);
297 class MockWritableFile
: public FSWritableFile
{
299 MockWritableFile(MemFile
* file
, const FileOptions
& opts
)
301 use_direct_io_(opts
.use_direct_writes
),
302 rate_limiter_(opts
.rate_limiter
) {
306 ~MockWritableFile() override
{ file_
->Unref(); }
308 bool use_direct_io() const override
{ return false && use_direct_io_
; }
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
),
321 bytes_written
+= bytes
;
323 return IOStatus::OK();
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
);
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();
339 IOStatus
Close(const IOOptions
& options
, IODebugContext
* dbg
) override
{
340 return file_
->Fsync(options
, dbg
);
343 IOStatus
Flush(const IOOptions
& /*options*/,
344 IODebugContext
* /*dbg*/) override
{
345 return IOStatus::OK();
348 IOStatus
Sync(const IOOptions
& options
, IODebugContext
* dbg
) override
{
349 return file_
->Fsync(options
, dbg
);
352 uint64_t GetFileSize(const IOOptions
& /*options*/,
353 IODebugContext
* /*dbg*/) override
{
354 return file_
->Size();
358 inline size_t RequestToken(size_t bytes
) {
359 if (rate_limiter_
&& io_priority_
< Env::IO_TOTAL
) {
361 bytes
, static_cast<size_t>(rate_limiter_
->GetSingleBurstBytes()));
362 rate_limiter_
->Request(bytes
, io_priority_
);
369 RateLimiter
* rate_limiter_
;
372 class MockEnvDirectory
: public FSDirectory
{
374 IOStatus
Fsync(const IOOptions
& /*options*/,
375 IODebugContext
* /*dbg*/) override
{
376 return IOStatus::OK();
380 class MockEnvFileLock
: public FileLock
{
382 explicit MockEnvFileLock(const std::string
& fname
) : fname_(fname
) {}
384 std::string
FileName() const { return fname_
; }
387 const std::string fname_
;
390 class TestMemLogger
: public Logger
{
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_
;
398 IODebugContext
* dbg_
;
399 std::atomic
<bool> flush_pending_
;
402 TestMemLogger(std::unique_ptr
<FSWritableFile
> f
, Env
* env
,
403 const IOOptions
& options
, IODebugContext
* dbg
,
404 const InfoLogLevel log_level
= InfoLogLevel::ERROR_LEVEL
)
408 last_flush_micros_(0),
412 flush_pending_(false) {}
413 ~TestMemLogger() override
{}
415 void Flush() override
{
416 if (flush_pending_
) {
417 flush_pending_
= false;
419 last_flush_micros_
= env_
->NowMicros();
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.
427 for (int iter
= 0; iter
< 2; iter
++) {
431 bufsize
= sizeof(buffer
);
435 base
= new char[bufsize
];
438 char* limit
= base
+ bufsize
;
440 struct timeval now_tv
;
441 gettimeofday(&now_tv
, nullptr);
442 const time_t seconds
= now_tv
.tv_sec
;
444 memset(&t
, 0, sizeof(t
));
445 struct tm
* ret
__attribute__((__unused__
));
446 ret
= localtime_r(&seconds
, &t
);
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
));
455 va_copy(backup_ap
, ap
);
456 p
+= vsnprintf(p
, limit
- p
, format
, backup_ap
);
460 // Truncate to available space if necessary
463 continue; // Try again with larger buffer
469 // Add newline if necessary
470 if (p
== base
|| p
[-1] != '\n') {
475 const size_t write_size
= p
- base
;
477 Status s
= file_
->Append(Slice(base
, write_size
), options_
, dbg_
);
479 flush_pending_
= true;
480 log_size_
+= write_size
;
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
;
488 if (base
!= buffer
) {
494 size_t GetLogFileSize() const override
{ return log_size_
; }
497 class MockFileSystem
: public FileSystem
{
499 explicit MockFileSystem(Env
* env
, bool supports_direct_io
= true)
500 : env_(env
), supports_direct_io_(supports_direct_io
) {}
502 ~MockFileSystem() override
{
503 for (auto i
= file_map_
.begin(); i
!= file_map_
.end(); ++i
) {
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
;
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
;
554 IOStatus
GetFileSize(const std::string
& fname
, const IOOptions
& options
,
555 uint64_t* file_size
, IODebugContext
* dbg
) override
;
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");
584 return IOStatus::OK();
587 IOStatus
IsDirectory(const std::string
& /*path*/,
588 const IOOptions
& /*options*/, bool* /*is_dir*/,
589 IODebugContext
* /*dgb*/) override
{
590 return IOStatus::NotSupported("IsDirectory");
593 Status
CorruptBuffer(const std::string
& fname
);
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
);
601 std::string
NormalizeMockPath(const std::string
& path
) {
602 std::string p
= NormalizePath(path
);
603 if (p
.back() == kFilePathSeparator
&& p
.size() > 1) {
610 // Map from filenames to MemFile objects, representing a simple file system.
612 std::map
<std::string
, MemFile
*> file_map_
; // Protected by mutex_.
614 bool supports_direct_io_
;
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
);
624 MutexLock
lock(&mutex_
);
625 if (file_map_
.find(fn
) == file_map_
.end()) {
627 return IOStatus::PathNotFound(fn
);
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");
635 result
->reset(new MockSequentialFile(f
, file_opts
));
636 return IOStatus::OK();
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()) {
647 return IOStatus::PathNotFound(fn
);
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");
655 result
->reset(new MockRandomAccessFile(f
, file_opts
));
656 return IOStatus::OK();
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()) {
667 return IOStatus::PathNotFound(fn
);
669 auto* f
= file_map_
[fn
];
670 if (f
->is_lock_file()) {
671 return IOStatus::InvalidArgument(fn
, "Cannot open a lock file.");
673 result
->reset(new MockRandomRWFile(f
));
674 return IOStatus::OK();
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
);
686 return NewWritableFile(fname
, options
, result
, dbg
);
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
);
698 MemFile
* file
= new MemFile(env_
, fn
, false);
700 file_map_
[fn
] = file
;
701 if (file_opts
.use_direct_writes
&& !supports_direct_io_
) {
702 return IOStatus::NotSupported("Direct I/O Not Supported");
704 result
->reset(new MockWritableFile(file
, file_opts
));
705 return IOStatus::OK();
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
;
719 file
= file_map_
[fn
];
722 if (file_opts
.use_direct_writes
&& !supports_direct_io_
) {
723 return IOStatus::NotSupported("Direct I/O Not Supported");
725 result
->reset(new MockWritableFile(file
, file_opts
));
726 return IOStatus::OK();
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();
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()) {
745 return IOStatus::OK();
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();
755 return IOStatus::NotFound();
758 bool MockFileSystem::GetChildrenInternal(const std::string
& dir
,
759 std::vector
<std::string
>* result
) {
760 auto d
= NormalizeMockPath(dir
);
761 bool found_dir
= false;
763 for (const auto& iter
: file_map_
) {
764 const std::string
& filename
= iter
.first
;
768 } else if (filename
.size() >= d
.size() + 1 && filename
[d
.size()] == '/' &&
769 Slice(filename
).starts_with(Slice(d
))) {
771 size_t next_slash
= filename
.find('/', d
.size() + 1);
772 if (next_slash
!= std::string::npos
) {
774 filename
.substr(d
.size() + 1, next_slash
- d
.size() - 1));
776 result
->push_back(filename
.substr(d
.size() + 1));
780 result
->erase(std::unique(result
->begin(), result
->end()), result
->end());
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
);
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
);
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
);
811 DeleteFileInternal(fn
);
812 return IOStatus::OK();
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
);
824 iter
->second
->Truncate(size
, options
, dbg
);
825 return IOStatus::OK();
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);
836 file_map_
[dn
] = file
;
838 return IOStatus::IOError();
840 return IOStatus::OK();
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();
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
);
858 std::vector
<std::string
> children
;
859 if (GetChildrenInternal(dir
, &children
)) {
860 for (const auto& child
: children
) {
861 DeleteFileInternal(child
);
864 DeleteFileInternal(dir
);
865 return IOStatus::OK();
869 IOStatus
MockFileSystem::GetFileSize(const std::string
& fname
,
870 const IOOptions
& /*options*/,
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
);
880 *file_size
= iter
->second
->Size();
881 return IOStatus::OK();
884 IOStatus
MockFileSystem::GetFileModificationTime(const std::string
& fname
,
885 const IOOptions
& /*options*/,
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
);
894 *time
= iter
->second
->ModifiedTime();
895 return IOStatus::OK();
898 bool MockFileSystem::RenameFileInternal(const std::string
& src
,
899 const std::string
& dest
) {
900 if (file_map_
.find(src
) == file_map_
.end()) {
903 std::vector
<std::string
> children
;
904 if (GetChildrenInternal(src
, &children
)) {
905 for (const auto& child
: children
) {
906 RenameFileInternal(src
+ "/" + child
, dest
+ "/" + child
);
909 DeleteFileInternal(dest
);
910 file_map_
[dest
] = file_map_
[src
];
911 file_map_
.erase(src
);
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
);
925 return IOStatus::PathNotFound(s
);
927 return IOStatus::OK();
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
);
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();
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);
959 file_map_
[fn
] = file
;
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();
968 IOStatus
MockFileSystem::LockFile(const std::string
& fname
,
969 const IOOptions
& /*options*/,
970 FileLock
** flock
, IODebugContext
* /*dbg*/) {
971 auto fn
= NormalizeMockPath(fname
);
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.");
978 if (!file_map_
[fn
]->Lock()) {
979 return IOStatus::IOError(fn
, "lock is already held.");
982 auto* file
= new MemFile(env_
, fn
, true);
985 file_map_
[fn
] = file
;
988 *flock
= new MockEnvFileLock(fn
);
989 return IOStatus::OK();
992 IOStatus
MockFileSystem::UnlockFile(FileLock
* flock
,
993 const IOOptions
& /*options*/,
994 IODebugContext
* /*dbg*/) {
995 std::string fn
= static_cast_with_check
<MockEnvFileLock
>(flock
)->FileName();
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.");
1002 file_map_
[fn
]->Unlock();
1006 return IOStatus::OK();
1009 IOStatus
MockFileSystem::GetTestDirectory(const IOOptions
& /*options*/,
1011 IODebugContext
* /*dbg*/) {
1013 return IOStatus::OK();
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");
1023 iter
->second
->CorruptBuffer();
1024 return Status::OK();
1027 MockEnv::MockEnv(Env
* base_env
)
1028 : CompositeEnvWrapper(base_env
, std::make_shared
<MockFileSystem
>(this)),
1029 fake_sleep_micros_(0) {}
1031 Status
MockEnv::GetCurrentTime(int64_t* unix_time
) {
1032 auto s
= CompositeEnvWrapper::GetCurrentTime(unix_time
);
1034 *unix_time
+= fake_sleep_micros_
.load() / (1000 * 1000);
1039 uint64_t MockEnv::NowMicros() {
1040 return CompositeEnvWrapper::NowMicros() + fake_sleep_micros_
.load();
1043 uint64_t MockEnv::NowNanos() {
1044 return CompositeEnvWrapper::NowNanos() + fake_sleep_micros_
.load() * 1000;
1047 Status
MockEnv::CorruptBuffer(const std::string
& fname
) {
1048 auto mock
= static_cast_with_check
<MockFileSystem
>(GetFileSystem().get());
1049 return mock
->CorruptBuffer(fname
);
1052 void MockEnv::FakeSleepForMicroseconds(int64_t micros
) {
1053 fake_sleep_micros_
.fetch_add(micros
);
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
); }
1060 #else // ROCKSDB_LITE
1062 Env
* NewMemEnv(Env
* /*base_env*/) { return nullptr; }
1064 #endif // !ROCKSDB_LITE
1066 } // namespace ROCKSDB_NAMESPACE