1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_FILES_IMPORTANT_FILE_WRITER_H_ 6 #define BASE_FILES_IMPORTANT_FILE_WRITER_H_ 7 8 #include <memory> 9 #include <optional> 10 #include <string> 11 #include <string_view> 12 13 #include "base/base_export.h" 14 #include "base/compiler_specific.h" 15 #include "base/files/file.h" 16 #include "base/files/file_path.h" 17 #include "base/functional/callback.h" 18 #include "base/memory/raw_ptr.h" 19 #include "base/sequence_checker.h" 20 #include "base/time/time.h" 21 #include "base/timer/timer.h" 22 #include "third_party/abseil-cpp/absl/types/variant.h" 23 24 namespace base { 25 26 class SequencedTaskRunner; 27 28 // Helper for atomically writing a file to ensure that it won't be corrupted by 29 // *application* crash during write (implemented as create, flush, rename). 30 // 31 // As an added benefit, ImportantFileWriter makes it less likely that the file 32 // is corrupted by *system* crash, though even if the ImportantFileWriter call 33 // has already returned at the time of the crash it is not specified which 34 // version of the file (old or new) is preserved. And depending on system 35 // configuration (hardware and software) a significant likelihood of file 36 // corruption may remain, thus using ImportantFileWriter is not a valid 37 // substitute for file integrity checks and recovery codepaths for malformed 38 // files. 39 // 40 // Also note that ImportantFileWriter can be *really* slow (cf. File::Flush() 41 // for details) and thus please don't block shutdown on ImportantFileWriter. 42 class BASE_EXPORT ImportantFileWriter { 43 public: 44 // Promise-like callback that returns (via output parameter) the serialized 45 // data to be written. This callback is invoked on the sequence where I/O 46 // operations are executed. Returning false indicates an error. 47 using BackgroundDataProducerCallback = 48 base::OnceCallback<std::optional<std::string>()>; 49 50 // Used by ScheduleSave to lazily provide the data to be saved. Allows us 51 // to also batch data serializations. 52 class BASE_EXPORT DataSerializer { 53 public: 54 // Returns a string for serialisation when successful, or a nullopt in case 55 // it failed to generate the data. Will be called on the same thread on 56 // which ImportantFileWriter has been created. 57 virtual std::optional<std::string> SerializeData() = 0; 58 59 protected: 60 virtual ~DataSerializer() = default; 61 }; 62 63 // Same as DataSerializer but allows the caller to move some of the 64 // serialization logic to the sequence where I/O operations are executed. 65 class BASE_EXPORT BackgroundDataSerializer { 66 public: 67 // Returns a promise-like callback that, when invoked, will produce the 68 // serialized string. This getter itself will be called on the same thread 69 // on which ImportantFileWriter has been created, but the callback will be 70 // invoked from the sequence where I/O operations are executed. 71 virtual BackgroundDataProducerCallback 72 GetSerializedDataProducerForBackgroundSequence() = 0; 73 74 protected: 75 virtual ~BackgroundDataSerializer() = default; 76 }; 77 78 // Save |data| to |path| in an atomic manner. Blocks and writes data on the 79 // current thread. Does not guarantee file integrity across system crash (see 80 // the class comment above). 81 static bool WriteFileAtomically( 82 const FilePath& path, 83 std::string_view data, 84 std::string_view histogram_suffix = std::string_view()); 85 86 // Initialize the writer. 87 // |path| is the name of file to write. 88 // |task_runner| is the SequencedTaskRunner instance where on which we will 89 // execute file I/O operations. 90 // All non-const methods, ctor and dtor must be called on the same thread. 91 ImportantFileWriter(const FilePath& path, 92 scoped_refptr<SequencedTaskRunner> task_runner, 93 std::string_view histogram_suffix = std::string_view()); 94 95 // Same as above, but with a custom commit interval. 96 ImportantFileWriter(const FilePath& path, 97 scoped_refptr<SequencedTaskRunner> task_runner, 98 TimeDelta interval, 99 std::string_view histogram_suffix = std::string_view()); 100 101 ImportantFileWriter(const ImportantFileWriter&) = delete; 102 ImportantFileWriter& operator=(const ImportantFileWriter&) = delete; 103 104 // You have to ensure that there are no pending writes at the moment 105 // of destruction. 106 ~ImportantFileWriter(); 107 path()108 const FilePath& path() const LIFETIME_BOUND { return path_; } 109 110 // Returns true if there is a scheduled write pending which has not yet 111 // been started. 112 bool HasPendingWrite() const; 113 114 // Save |data| to target filename. Does not block. If there is a pending write 115 // scheduled by ScheduleWrite(), it is cancelled. 116 void WriteNow(std::string data); 117 118 // Schedule a save to target filename. Data will be serialized and saved 119 // to disk after the commit interval. If another ScheduleWrite is issued 120 // before that, only one serialization and write to disk will happen, and 121 // the most recent |serializer| will be used. This operation does not block. 122 // |serializer| should remain valid through the lifetime of 123 // ImportantFileWriter. 124 void ScheduleWrite(DataSerializer* serializer); 125 126 // Same as above but uses the BackgroundDataSerializer API. 127 void ScheduleWriteWithBackgroundDataSerializer( 128 BackgroundDataSerializer* serializer); 129 130 // Serialize data pending to be saved and execute write on background thread. 131 void DoScheduledWrite(); 132 133 // Registers |before_next_write_callback| and |after_next_write_callback| to 134 // be synchronously invoked from WriteFileAtomically() before its next write 135 // and after its next write, respectively. The boolean passed to 136 // |after_next_write_callback| indicates whether the write was successful. 137 // Both callbacks must be thread safe as they will be called on |task_runner_| 138 // and may be called during Chrome shutdown. 139 // If called more than once before a write is scheduled on |task_runner|, the 140 // latest callbacks clobber the others. 141 void RegisterOnNextWriteCallbacks( 142 OnceClosure before_next_write_callback, 143 OnceCallback<void(bool success)> after_next_write_callback); 144 commit_interval()145 TimeDelta commit_interval() const { 146 return commit_interval_; 147 } 148 149 // Overrides the timer to use for scheduling writes with |timer_override|. 150 void SetTimerForTesting(OneShotTimer* timer_override); 151 152 #if defined(UNIT_TEST) previous_data_size()153 size_t previous_data_size() const { return previous_data_size_; } 154 #endif set_previous_data_size(size_t previous_data_size)155 void set_previous_data_size(size_t previous_data_size) { 156 previous_data_size_ = previous_data_size; 157 } 158 159 // Allows tests to call the given callback instead of ReplaceFile(). 160 using ReplaceFileCallback = 161 RepeatingCallback<bool(const FilePath&, const FilePath&, File::Error*)>; 162 void SetReplaceFileCallbackForTesting(ReplaceFileCallback callback); 163 164 private: timer()165 const OneShotTimer& timer() const LIFETIME_BOUND { 166 return timer_override_ ? *timer_override_ : timer_; 167 } timer()168 OneShotTimer& timer() LIFETIME_BOUND { 169 return timer_override_ ? *timer_override_ : timer_; 170 } 171 172 // Same as WriteNow() but it uses a promise-like signature that allows running 173 // custom logic in the background sequence. 174 void WriteNowWithBackgroundDataProducer( 175 BackgroundDataProducerCallback background_producer); 176 177 // Helper function to call WriteFileAtomically() with a promise-like callback 178 // producing a std::string. 179 static void ProduceAndWriteStringToFileAtomically( 180 const FilePath& path, 181 BackgroundDataProducerCallback data_producer_for_background_sequence, 182 OnceClosure before_write_callback, 183 OnceCallback<void(bool success)> after_write_callback, 184 ReplaceFileCallback replace_file_callback, 185 const std::string& histogram_suffix); 186 187 // Writes |data| to |path|, recording histograms with an optional 188 // |histogram_suffix|. |from_instance| indicates whether the call originates 189 // from an instance of ImportantFileWriter or a direct call to 190 // WriteFileAtomically. When false, the directory containing |path| is added 191 // to the set cleaned by the ImportantFileWriterCleaner (Windows only). 192 static bool WriteFileAtomicallyImpl( 193 const FilePath& path, 194 std::string_view data, 195 std::string_view histogram_suffix, 196 bool from_instance, 197 ReplaceFileCallback replace_file_callback); 198 199 void ClearPendingWrite(); 200 201 // Invoked synchronously on the next write event. 202 OnceClosure before_next_write_callback_; 203 OnceCallback<void(bool success)> after_next_write_callback_; 204 205 // Path being written to. 206 const FilePath path_; 207 208 // TaskRunner for the thread on which file I/O can be done. 209 const scoped_refptr<SequencedTaskRunner> task_runner_; 210 211 // Timer used to schedule commit after ScheduleWrite. 212 OneShotTimer timer_; 213 214 // An override for |timer_| used for testing. 215 raw_ptr<OneShotTimer> timer_override_ = nullptr; 216 217 // Serializer which will provide the data to be saved. 218 absl::variant<absl::monostate, DataSerializer*, BackgroundDataSerializer*> 219 serializer_; 220 221 // Time delta after which scheduled data will be written to disk. 222 const TimeDelta commit_interval_; 223 224 // Custom histogram suffix. 225 const std::string histogram_suffix_; 226 227 // Memorizes the amount of data written on the previous write. This helps 228 // preallocating memory for the data serialization. It is only used for 229 // scheduled writes. 230 size_t previous_data_size_ = 0; 231 232 ReplaceFileCallback replace_file_callback_; 233 234 SEQUENCE_CHECKER(sequence_checker_); 235 236 WeakPtrFactory<ImportantFileWriter> weak_factory_{this}; 237 }; 238 239 } // namespace base 240 241 #endif // BASE_FILES_IMPORTANT_FILE_WRITER_H_ 242