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 Env to keep track of the state of a filesystem as of
11 // the last "sync". It then checks for data loss errors by purposely dropping
12 // file data (or entire files) not protected by a "sync".
13 
14 #pragma once
15 
16 #include <map>
17 #include <set>
18 #include <string>
19 
20 #include "db/version_set.h"
21 #include "env/mock_env.h"
22 #include "file/filename.h"
23 #include "rocksdb/db.h"
24 #include "rocksdb/env.h"
25 #include "util/mutexlock.h"
26 #include "util/random.h"
27 
28 namespace ROCKSDB_NAMESPACE {
29 
30 class TestWritableFile;
31 class FaultInjectionTestEnv;
32 
33 struct FileState {
34   std::string filename_;
35   ssize_t pos_;
36   ssize_t pos_at_last_sync_;
37   ssize_t pos_at_last_flush_;
38 
FileStateFileState39   explicit FileState(const std::string& filename)
40       : filename_(filename),
41         pos_(-1),
42         pos_at_last_sync_(-1),
43         pos_at_last_flush_(-1) {}
44 
FileStateFileState45   FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
46 
IsFullySyncedFileState47   bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }
48 
49   Status DropUnsyncedData(Env* env) const;
50 
51   Status DropRandomUnsyncedData(Env* env, Random* rand) const;
52 };
53 
54 // A wrapper around WritableFileWriter* file
55 // is written to or sync'ed.
56 class TestWritableFile : public WritableFile {
57  public:
58   explicit TestWritableFile(const std::string& fname,
59                             std::unique_ptr<WritableFile>&& f,
60                             FaultInjectionTestEnv* env);
61   virtual ~TestWritableFile();
62   virtual Status Append(const Slice& data) override;
Truncate(uint64_t size)63   virtual Status Truncate(uint64_t size) override {
64     return target_->Truncate(size);
65   }
66   virtual Status Close() override;
67   virtual Status Flush() override;
68   virtual Status Sync() override;
IsSyncThreadSafe()69   virtual bool IsSyncThreadSafe() const override { return true; }
PositionedAppend(const Slice & data,uint64_t offset)70   virtual Status PositionedAppend(const Slice& data,
71                                   uint64_t offset) override {
72     return target_->PositionedAppend(data, offset);
73   }
use_direct_io()74   virtual bool use_direct_io() const override {
75     return target_->use_direct_io();
76   };
77 
78  private:
79   FileState state_;
80   std::unique_ptr<WritableFile> target_;
81   bool writable_file_opened_;
82   FaultInjectionTestEnv* env_;
83 };
84 
85 // A wrapper around WritableFileWriter* file
86 // is written to or sync'ed.
87 class TestRandomRWFile : public RandomRWFile {
88  public:
89   explicit TestRandomRWFile(const std::string& fname,
90                             std::unique_ptr<RandomRWFile>&& f,
91                             FaultInjectionTestEnv* env);
92   virtual ~TestRandomRWFile();
93   Status Write(uint64_t offset, const Slice& data) override;
94   Status Read(uint64_t offset, size_t n, Slice* result,
95               char* scratch) const override;
96   Status Close() override;
97   Status Flush() override;
98   Status Sync() override;
GetRequiredBufferAlignment()99   size_t GetRequiredBufferAlignment() const override {
100     return target_->GetRequiredBufferAlignment();
101   }
use_direct_io()102   bool use_direct_io() const override { return target_->use_direct_io(); };
103 
104  private:
105   std::unique_ptr<RandomRWFile> target_;
106   bool file_opened_;
107   FaultInjectionTestEnv* env_;
108 };
109 
110 class TestDirectory : public Directory {
111  public:
TestDirectory(FaultInjectionTestEnv * env,std::string dirname,Directory * dir)112   explicit TestDirectory(FaultInjectionTestEnv* env, std::string dirname,
113                          Directory* dir)
114       : env_(env), dirname_(dirname), dir_(dir) {}
~TestDirectory()115   ~TestDirectory() {}
116 
117   virtual Status Fsync() override;
118 
119  private:
120   FaultInjectionTestEnv* env_;
121   std::string dirname_;
122   std::unique_ptr<Directory> dir_;
123 };
124 
125 class FaultInjectionTestEnv : public EnvWrapper {
126  public:
FaultInjectionTestEnv(Env * base)127   explicit FaultInjectionTestEnv(Env* base)
128       : EnvWrapper(base), filesystem_active_(true) {}
~FaultInjectionTestEnv()129   virtual ~FaultInjectionTestEnv() {}
130 
131   Status NewDirectory(const std::string& name,
132                       std::unique_ptr<Directory>* result) override;
133 
134   Status NewWritableFile(const std::string& fname,
135                          std::unique_ptr<WritableFile>* result,
136                          const EnvOptions& soptions) override;
137 
138   Status ReopenWritableFile(const std::string& fname,
139                             std::unique_ptr<WritableFile>* result,
140                             const EnvOptions& soptions) override;
141 
142   Status NewRandomRWFile(const std::string& fname,
143                          std::unique_ptr<RandomRWFile>* result,
144                          const EnvOptions& soptions) override;
145 
146   Status NewRandomAccessFile(const std::string& fname,
147                              std::unique_ptr<RandomAccessFile>* result,
148                              const EnvOptions& soptions) override;
149 
150   virtual Status DeleteFile(const std::string& f) override;
151 
152   virtual Status RenameFile(const std::string& s,
153                             const std::string& t) override;
154 
155 // Undef to eliminate clash on Windows
156 #undef GetFreeSpace
GetFreeSpace(const std::string & path,uint64_t * disk_free)157   virtual Status GetFreeSpace(const std::string& path,
158                               uint64_t* disk_free) override {
159     if (!IsFilesystemActive() && error_ == Status::NoSpace()) {
160       *disk_free = 0;
161       return Status::OK();
162     } else {
163       return target()->GetFreeSpace(path, disk_free);
164     }
165   }
166 
167   void WritableFileClosed(const FileState& state);
168 
169   void WritableFileSynced(const FileState& state);
170 
171   void WritableFileAppended(const FileState& state);
172 
173   // For every file that is not fully synced, make a call to `func` with
174   // FileState of the file as the parameter.
175   Status DropFileData(std::function<Status(Env*, FileState)> func);
176 
177   Status DropUnsyncedFileData();
178 
179   Status DropRandomUnsyncedFileData(Random* rnd);
180 
181   Status DeleteFilesCreatedAfterLastDirSync();
182 
183   void ResetState();
184 
185   void UntrackFile(const std::string& f);
186 
SyncDir(const std::string & dirname)187   void SyncDir(const std::string& dirname) {
188     MutexLock l(&mutex_);
189     dir_to_new_files_since_last_sync_.erase(dirname);
190   }
191 
192   // Setting the filesystem to inactive is the test equivalent to simulating a
193   // system reset. Setting to inactive will freeze our saved filesystem state so
194   // that it will stop being recorded. It can then be reset back to the state at
195   // the time of the reset.
IsFilesystemActive()196   bool IsFilesystemActive() {
197     MutexLock l(&mutex_);
198     return filesystem_active_;
199   }
200   void SetFilesystemActiveNoLock(bool active,
201       Status error = Status::Corruption("Not active")) {
202     filesystem_active_ = active;
203     if (!active) {
204       error_ = error;
205     }
206   }
207   void SetFilesystemActive(bool active,
208       Status error = Status::Corruption("Not active")) {
209     MutexLock l(&mutex_);
210     SetFilesystemActiveNoLock(active, error);
211   }
AssertNoOpenFile()212   void AssertNoOpenFile() { assert(open_files_.empty()); }
GetError()213   Status GetError() { return error_; }
214 
215  private:
216   port::Mutex mutex_;
217   std::map<std::string, FileState> db_file_state_;
218   std::set<std::string> open_files_;
219   std::unordered_map<std::string, std::set<std::string>>
220       dir_to_new_files_since_last_sync_;
221   bool filesystem_active_;  // Record flushes, syncs, writes
222   Status error_;
223 };
224 
225 }  // namespace ROCKSDB_NAMESPACE
226