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 2014 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 // This test uses a custom FileSystem to keep track of the state of a file
11 // system the last "Sync". The data being written is cached in a "buffer".
12 // Only when "Sync" is called, the data will be persistent. It can similate
13 // file data loss (or entire files) not protected by a "Sync". For any of the
14 // FileSystem related operations, by specify the "IOStatus Error", a specific
15 // error can be returned when file system is not activated.
16 
17 #pragma once
18 
19 #include <map>
20 #include <set>
21 #include <string>
22 
23 #include "db/version_set.h"
24 #include "env/mock_env.h"
25 #include "file/filename.h"
26 #include "include/rocksdb/file_system.h"
27 #include "rocksdb/db.h"
28 #include "rocksdb/env.h"
29 #include "util/mutexlock.h"
30 #include "util/random.h"
31 
32 namespace ROCKSDB_NAMESPACE {
33 
34 class TestFSWritableFile;
35 class FaultInjectionTestFS;
36 
37 struct FSFileState {
38   std::string filename_;
39   ssize_t pos_;
40   ssize_t pos_at_last_sync_;
41   ssize_t pos_at_last_flush_;
42   std::string buffer_;
43 
FSFileStateFSFileState44   explicit FSFileState(const std::string& filename)
45       : filename_(filename),
46         pos_(-1),
47         pos_at_last_sync_(-1),
48         pos_at_last_flush_(-1) {}
49 
FSFileStateFSFileState50   FSFileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
51 
IsFullySyncedFSFileState52   bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }
53 
54   IOStatus DropUnsyncedData();
55 
56   IOStatus DropRandomUnsyncedData(Random* rand);
57 };
58 
59 // A wrapper around WritableFileWriter* file
60 // is written to or sync'ed.
61 class TestFSWritableFile : public FSWritableFile {
62  public:
63   explicit TestFSWritableFile(const std::string& fname,
64                               std::unique_ptr<FSWritableFile>&& f,
65                               FaultInjectionTestFS* fs);
66   virtual ~TestFSWritableFile();
67   virtual IOStatus Append(const Slice& data, const IOOptions&,
68                           IODebugContext*) override;
Truncate(uint64_t size,const IOOptions & options,IODebugContext * dbg)69   virtual IOStatus Truncate(uint64_t size, const IOOptions& options,
70                             IODebugContext* dbg) override {
71     return target_->Truncate(size, options, dbg);
72   }
73   virtual IOStatus Close(const IOOptions& options,
74                          IODebugContext* dbg) override;
75   virtual IOStatus Flush(const IOOptions&, IODebugContext*) override;
76   virtual IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override;
IsSyncThreadSafe()77   virtual bool IsSyncThreadSafe() const override { return true; }
PositionedAppend(const Slice & data,uint64_t offset,const IOOptions & options,IODebugContext * dbg)78   virtual IOStatus PositionedAppend(const Slice& data, uint64_t offset,
79                                     const IOOptions& options,
80                                     IODebugContext* dbg) override {
81     return target_->PositionedAppend(data, offset, options, dbg);
82   }
use_direct_io()83   virtual bool use_direct_io() const override {
84     return target_->use_direct_io();
85   };
86 
87  private:
88   FSFileState state_;
89   std::unique_ptr<FSWritableFile> target_;
90   bool writable_file_opened_;
91   FaultInjectionTestFS* fs_;
92   port::Mutex mutex_;
93 };
94 
95 // A wrapper around WritableFileWriter* file
96 // is written to or sync'ed.
97 class TestFSRandomRWFile : public FSRandomRWFile {
98  public:
99   explicit TestFSRandomRWFile(const std::string& fname,
100                               std::unique_ptr<FSRandomRWFile>&& f,
101                               FaultInjectionTestFS* fs);
102   virtual ~TestFSRandomRWFile();
103   IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options,
104                  IODebugContext* dbg) override;
105   IOStatus Read(uint64_t offset, size_t n, const IOOptions& options,
106                 Slice* result, char* scratch,
107                 IODebugContext* dbg) const override;
108   IOStatus Close(const IOOptions& options, IODebugContext* dbg) override;
109   IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override;
110   IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override;
GetRequiredBufferAlignment()111   size_t GetRequiredBufferAlignment() const override {
112     return target_->GetRequiredBufferAlignment();
113   }
use_direct_io()114   bool use_direct_io() const override { return target_->use_direct_io(); };
115 
116  private:
117   std::unique_ptr<FSRandomRWFile> target_;
118   bool file_opened_;
119   FaultInjectionTestFS* fs_;
120 };
121 
122 class TestFSDirectory : public FSDirectory {
123  public:
TestFSDirectory(FaultInjectionTestFS * fs,std::string dirname,FSDirectory * dir)124   explicit TestFSDirectory(FaultInjectionTestFS* fs, std::string dirname,
125                            FSDirectory* dir)
126       : fs_(fs), dirname_(dirname), dir_(dir) {}
~TestFSDirectory()127   ~TestFSDirectory() {}
128 
129   virtual IOStatus Fsync(const IOOptions& options,
130                          IODebugContext* dbg) override;
131 
132  private:
133   FaultInjectionTestFS* fs_;
134   std::string dirname_;
135   std::unique_ptr<FSDirectory> dir_;
136 };
137 
138 class FaultInjectionTestFS : public FileSystemWrapper {
139  public:
FaultInjectionTestFS(std::shared_ptr<FileSystem> base)140   explicit FaultInjectionTestFS(std::shared_ptr<FileSystem> base)
141       : FileSystemWrapper(base), filesystem_active_(true) {}
~FaultInjectionTestFS()142   virtual ~FaultInjectionTestFS() {}
143 
Name()144   const char* Name() const override { return "FaultInjectionTestFS"; }
145 
146   IOStatus NewDirectory(const std::string& name, const IOOptions& options,
147                         std::unique_ptr<FSDirectory>* result,
148                         IODebugContext* dbg) override;
149 
150   IOStatus NewWritableFile(const std::string& fname,
151                            const FileOptions& file_opts,
152                            std::unique_ptr<FSWritableFile>* result,
153                            IODebugContext* dbg) override;
154 
155   IOStatus ReopenWritableFile(const std::string& fname,
156                               const FileOptions& file_opts,
157                               std::unique_ptr<FSWritableFile>* result,
158                               IODebugContext* dbg) override;
159 
160   IOStatus NewRandomRWFile(const std::string& fname,
161                            const FileOptions& file_opts,
162                            std::unique_ptr<FSRandomRWFile>* result,
163                            IODebugContext* dbg) override;
164 
165   IOStatus NewRandomAccessFile(const std::string& fname,
166                                const FileOptions& file_opts,
167                                std::unique_ptr<FSRandomAccessFile>* result,
168                                IODebugContext* dbg) override;
169 
170   virtual IOStatus DeleteFile(const std::string& f, const IOOptions& options,
171                               IODebugContext* dbg) override;
172 
173   virtual IOStatus RenameFile(const std::string& s, const std::string& t,
174                               const IOOptions& options,
175                               IODebugContext* dbg) override;
176 
177 // Undef to eliminate clash on Windows
178 #undef GetFreeSpace
GetFreeSpace(const std::string & path,const IOOptions & options,uint64_t * disk_free,IODebugContext * dbg)179   virtual IOStatus GetFreeSpace(const std::string& path,
180                                 const IOOptions& options, uint64_t* disk_free,
181                                 IODebugContext* dbg) override {
182     if (!IsFilesystemActive() && error_ == IOStatus::NoSpace()) {
183       *disk_free = 0;
184       return IOStatus::OK();
185     } else {
186       return target()->GetFreeSpace(path, options, disk_free, dbg);
187     }
188   }
189 
190   void WritableFileClosed(const FSFileState& state);
191 
192   void WritableFileSynced(const FSFileState& state);
193 
194   void WritableFileAppended(const FSFileState& state);
195 
196   IOStatus DropUnsyncedFileData();
197 
198   IOStatus DropRandomUnsyncedFileData(Random* rnd);
199 
200   IOStatus DeleteFilesCreatedAfterLastDirSync(const IOOptions& options,
201                                               IODebugContext* dbg);
202 
203   void ResetState();
204 
205   void UntrackFile(const std::string& f);
206 
SyncDir(const std::string & dirname)207   void SyncDir(const std::string& dirname) {
208     MutexLock l(&mutex_);
209     dir_to_new_files_since_last_sync_.erase(dirname);
210   }
211 
212   // Setting the filesystem to inactive is the test equivalent to simulating a
213   // system reset. Setting to inactive will freeze our saved filesystem state so
214   // that it will stop being recorded. It can then be reset back to the state at
215   // the time of the reset.
IsFilesystemActive()216   bool IsFilesystemActive() {
217     MutexLock l(&mutex_);
218     return filesystem_active_;
219   }
220   void SetFilesystemActiveNoLock(
221       bool active, IOStatus error = IOStatus::Corruption("Not active")) {
222     filesystem_active_ = active;
223     if (!active) {
224       error_ = error;
225     }
226   }
227   void SetFilesystemActive(
228       bool active, IOStatus error = IOStatus::Corruption("Not active")) {
229     MutexLock l(&mutex_);
230     SetFilesystemActiveNoLock(active, error);
231   }
AssertNoOpenFile()232   void AssertNoOpenFile() { assert(open_files_.empty()); }
233 
GetError()234   IOStatus GetError() { return error_; }
235 
SetFileSystemIOError(IOStatus io_error)236   void SetFileSystemIOError(IOStatus io_error) {
237     MutexLock l(&mutex_);
238     error_ = io_error;
239   }
240 
241  private:
242   port::Mutex mutex_;
243   std::map<std::string, FSFileState> db_file_state_;
244   std::set<std::string> open_files_;
245   std::unordered_map<std::string, std::set<std::string>>
246       dir_to_new_files_since_last_sync_;
247   bool filesystem_active_;  // Record flushes, syncs, writes
248   IOStatus error_;
249 };
250 
251 }  // namespace ROCKSDB_NAMESPACE
252