1 // Copyright 2014 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 #include "base/files/file_proxy.h"
6
7 #include <memory>
8 #include <optional>
9 #include <utility>
10
11 #include "base/containers/heap_array.h"
12 #include "base/files/file.h"
13 #include "base/files/file_util.h"
14 #include "base/functional/bind.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/location.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/task/task_runner.h"
19
20 namespace {
21
FileDeleter(base::File file)22 void FileDeleter(base::File file) {
23 }
24
25 } // namespace
26
27 namespace base {
28
29 class FileHelper {
30 public:
FileHelper(base::WeakPtr<FileProxy> proxy,File file)31 FileHelper(base::WeakPtr<FileProxy> proxy, File file)
32 : file_(std::move(file)),
33 task_runner_(proxy->task_runner()),
34 proxy_(proxy) {}
35 FileHelper(const FileHelper&) = delete;
36 FileHelper& operator=(const FileHelper&) = delete;
37
PassFile()38 void PassFile() {
39 if (proxy_)
40 proxy_->SetFile(std::move(file_));
41 else if (file_.IsValid())
42 task_runner_->PostTask(FROM_HERE,
43 BindOnce(&FileDeleter, std::move(file_)));
44 }
45
46 protected:
47 File file_;
48 File::Error error_ = File::FILE_ERROR_FAILED;
49
50 private:
51 scoped_refptr<TaskRunner> task_runner_;
52 WeakPtr<FileProxy> proxy_;
53 };
54
55 namespace {
56
57 class GenericFileHelper : public FileHelper {
58 public:
GenericFileHelper(base::WeakPtr<FileProxy> proxy,File file)59 GenericFileHelper(base::WeakPtr<FileProxy> proxy, File file)
60 : FileHelper(std::move(proxy), std::move(file)) {}
61 GenericFileHelper(const GenericFileHelper&) = delete;
62 GenericFileHelper& operator=(const GenericFileHelper&) = delete;
63
Close()64 void Close() {
65 file_.Close();
66 error_ = File::FILE_OK;
67 }
68
SetTimes(Time last_access_time,Time last_modified_time)69 void SetTimes(Time last_access_time, Time last_modified_time) {
70 bool rv = file_.SetTimes(last_access_time, last_modified_time);
71 error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
72 }
73
SetLength(int64_t length)74 void SetLength(int64_t length) {
75 if (file_.SetLength(length))
76 error_ = File::FILE_OK;
77 }
78
Flush()79 void Flush() {
80 if (file_.Flush())
81 error_ = File::FILE_OK;
82 }
83
Reply(FileProxy::StatusCallback callback)84 void Reply(FileProxy::StatusCallback callback) {
85 PassFile();
86 if (!callback.is_null())
87 std::move(callback).Run(error_);
88 }
89 };
90
91 class CreateOrOpenHelper : public FileHelper {
92 public:
CreateOrOpenHelper(base::WeakPtr<FileProxy> proxy,File file)93 CreateOrOpenHelper(base::WeakPtr<FileProxy> proxy, File file)
94 : FileHelper(std::move(proxy), std::move(file)) {}
95 CreateOrOpenHelper(const CreateOrOpenHelper&) = delete;
96 CreateOrOpenHelper& operator=(const CreateOrOpenHelper&) = delete;
97
RunWork(const FilePath & file_path,uint32_t file_flags)98 void RunWork(const FilePath& file_path, uint32_t file_flags) {
99 file_.Initialize(file_path, file_flags);
100 error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
101 }
102
Reply(FileProxy::StatusCallback callback)103 void Reply(FileProxy::StatusCallback callback) {
104 DCHECK(!callback.is_null());
105 PassFile();
106 std::move(callback).Run(error_);
107 }
108 };
109
110 class CreateTemporaryHelper : public FileHelper {
111 public:
CreateTemporaryHelper(base::WeakPtr<FileProxy> proxy,File file)112 CreateTemporaryHelper(base::WeakPtr<FileProxy> proxy, File file)
113 : FileHelper(std::move(proxy), std::move(file)) {}
114 CreateTemporaryHelper(const CreateTemporaryHelper&) = delete;
115 CreateTemporaryHelper& operator=(const CreateTemporaryHelper&) = delete;
116
RunWork(uint32_t additional_file_flags)117 void RunWork(uint32_t additional_file_flags) {
118 // TODO(darin): file_util should have a variant of CreateTemporaryFile
119 // that returns a FilePath and a File.
120 if (!CreateTemporaryFile(&file_path_)) {
121 // TODO(davidben): base::CreateTemporaryFile should preserve the error
122 // code.
123 error_ = File::FILE_ERROR_FAILED;
124 return;
125 }
126
127 uint32_t file_flags = File::FLAG_WRITE | File::FLAG_WIN_TEMPORARY |
128 File::FLAG_CREATE_ALWAYS | additional_file_flags;
129
130 file_.Initialize(file_path_, file_flags);
131 if (file_.IsValid()) {
132 error_ = File::FILE_OK;
133 } else {
134 error_ = file_.error_details();
135 DeleteFile(file_path_);
136 file_path_.clear();
137 }
138 }
139
Reply(FileProxy::CreateTemporaryCallback callback)140 void Reply(FileProxy::CreateTemporaryCallback callback) {
141 DCHECK(!callback.is_null());
142 PassFile();
143 std::move(callback).Run(error_, file_path_);
144 }
145
146 private:
147 FilePath file_path_;
148 };
149
150 class GetInfoHelper : public FileHelper {
151 public:
GetInfoHelper(base::WeakPtr<FileProxy> proxy,File file)152 GetInfoHelper(base::WeakPtr<FileProxy> proxy, File file)
153 : FileHelper(std::move(proxy), std::move(file)) {}
154 GetInfoHelper(const GetInfoHelper&) = delete;
155 GetInfoHelper& operator=(const GetInfoHelper&) = delete;
156
RunWork()157 void RunWork() {
158 if (file_.GetInfo(&file_info_))
159 error_ = File::FILE_OK;
160 }
161
Reply(FileProxy::GetFileInfoCallback callback)162 void Reply(FileProxy::GetFileInfoCallback callback) {
163 PassFile();
164 DCHECK(!callback.is_null());
165 std::move(callback).Run(error_, file_info_);
166 }
167
168 private:
169 File::Info file_info_;
170 };
171
172 class ReadHelper : public FileHelper {
173 public:
ReadHelper(base::WeakPtr<FileProxy> proxy,File file,int bytes_to_read)174 ReadHelper(base::WeakPtr<FileProxy> proxy, File file, int bytes_to_read)
175 : FileHelper(std::move(proxy), std::move(file)),
176 // SAFETY - References to `buffer_` are provided as a span only after
177 // successfully reading some bytes.
178 buffer_(base::HeapArray<uint8_t>::Uninit(
179 static_cast<size_t>(bytes_to_read))) {}
180
181 ReadHelper(const ReadHelper&) = delete;
182 ReadHelper& operator=(const ReadHelper&) = delete;
183
RunWork(int64_t offset)184 void RunWork(int64_t offset) {
185 std::optional<size_t> result = file_.Read(offset, buffer_);
186 if (!result.has_value()) {
187 bytes_read_ = -1;
188 error_ = File::FILE_ERROR_FAILED;
189 return;
190 }
191 bytes_read_ = checked_cast<int>(result.value());
192 error_ = File::FILE_OK;
193 }
194
Reply(FileProxy::ReadCallback callback)195 void Reply(FileProxy::ReadCallback callback) {
196 PassFile();
197 DCHECK(!callback.is_null());
198 base::span<uint8_t> read_span;
199 if (error_ == File::FILE_OK) {
200 read_span = buffer_.first(checked_cast<size_t>(bytes_read_));
201 }
202 std::move(callback).Run(error_, base::as_chars(read_span));
203 }
204
205 private:
206 base::HeapArray<uint8_t> buffer_;
207 int bytes_read_ = 0;
208 };
209
210 class WriteHelper : public FileHelper {
211 public:
WriteHelper(base::WeakPtr<FileProxy> proxy,File file,base::span<const uint8_t> data)212 WriteHelper(base::WeakPtr<FileProxy> proxy,
213 File file,
214 base::span<const uint8_t> data)
215 : FileHelper(std::move(proxy), std::move(file)),
216 buffer_(base::HeapArray<uint8_t>::CopiedFrom(data)) {}
217
218 WriteHelper(const WriteHelper&) = delete;
219 WriteHelper& operator=(const WriteHelper&) = delete;
220
RunWork(int64_t offset)221 void RunWork(int64_t offset) {
222 std::optional<size_t> result = file_.Write(offset, buffer_);
223 if (!result.has_value()) {
224 bytes_written_ = -1;
225 error_ = File::FILE_ERROR_FAILED;
226 return;
227 }
228 bytes_written_ = checked_cast<int>(result.value());
229 error_ = File::FILE_OK;
230 }
231
Reply(FileProxy::WriteCallback callback)232 void Reply(FileProxy::WriteCallback callback) {
233 PassFile();
234 if (!callback.is_null())
235 std::move(callback).Run(error_, bytes_written_);
236 }
237
238 private:
239 base::HeapArray<uint8_t> buffer_;
240 int bytes_written_ = 0;
241 };
242
243 } // namespace
244
FileProxy(TaskRunner * task_runner)245 FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {}
246
~FileProxy()247 FileProxy::~FileProxy() {
248 if (file_.IsValid())
249 task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, std::move(file_)));
250 }
251
CreateOrOpen(const FilePath & file_path,uint32_t file_flags,StatusCallback callback)252 bool FileProxy::CreateOrOpen(const FilePath& file_path,
253 uint32_t file_flags,
254 StatusCallback callback) {
255 DCHECK(!file_.IsValid());
256 CreateOrOpenHelper* helper =
257 new CreateOrOpenHelper(weak_ptr_factory_.GetWeakPtr(), File());
258 return task_runner_->PostTaskAndReply(
259 FROM_HERE,
260 BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
261 file_flags),
262 BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), std::move(callback)));
263 }
264
CreateTemporary(uint32_t additional_file_flags,CreateTemporaryCallback callback)265 bool FileProxy::CreateTemporary(uint32_t additional_file_flags,
266 CreateTemporaryCallback callback) {
267 DCHECK(!file_.IsValid());
268 CreateTemporaryHelper* helper =
269 new CreateTemporaryHelper(weak_ptr_factory_.GetWeakPtr(), File());
270 return task_runner_->PostTaskAndReply(
271 FROM_HERE,
272 BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper),
273 additional_file_flags),
274 BindOnce(&CreateTemporaryHelper::Reply, Owned(helper),
275 std::move(callback)));
276 }
277
IsValid() const278 bool FileProxy::IsValid() const {
279 return file_.IsValid();
280 }
281
SetFile(File file)282 void FileProxy::SetFile(File file) {
283 DCHECK(!file_.IsValid());
284 file_ = std::move(file);
285 }
286
TakeFile()287 File FileProxy::TakeFile() {
288 return std::move(file_);
289 }
290
DuplicateFile()291 File FileProxy::DuplicateFile() {
292 return file_.Duplicate();
293 }
294
GetPlatformFile() const295 PlatformFile FileProxy::GetPlatformFile() const {
296 return file_.GetPlatformFile();
297 }
298
Close(StatusCallback callback)299 bool FileProxy::Close(StatusCallback callback) {
300 DCHECK(file_.IsValid());
301 GenericFileHelper* helper =
302 new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_));
303 return task_runner_->PostTaskAndReply(
304 FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)),
305 BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
306 }
307
GetInfo(GetFileInfoCallback callback)308 bool FileProxy::GetInfo(GetFileInfoCallback callback) {
309 DCHECK(file_.IsValid());
310 GetInfoHelper* helper =
311 new GetInfoHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_));
312 return task_runner_->PostTaskAndReply(
313 FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)),
314 BindOnce(&GetInfoHelper::Reply, Owned(helper), std::move(callback)));
315 }
316
Read(int64_t offset,int bytes_to_read,ReadCallback callback)317 bool FileProxy::Read(int64_t offset, int bytes_to_read, ReadCallback callback) {
318 DCHECK(file_.IsValid());
319 if (bytes_to_read < 0)
320 return false;
321
322 ReadHelper* helper = new ReadHelper(weak_ptr_factory_.GetWeakPtr(),
323 std::move(file_), bytes_to_read);
324 return task_runner_->PostTaskAndReply(
325 FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset),
326 BindOnce(&ReadHelper::Reply, Owned(helper), std::move(callback)));
327 }
328
Write(int64_t offset,base::span<const uint8_t> data,WriteCallback callback)329 bool FileProxy::Write(int64_t offset,
330 base::span<const uint8_t> data,
331 WriteCallback callback) {
332 DCHECK(file_.IsValid());
333 if (data.empty()) {
334 return false;
335 }
336 WriteHelper* helper =
337 new WriteHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_), data);
338
339 return task_runner_->PostTaskAndReply(
340 FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset),
341 BindOnce(&WriteHelper::Reply, Owned(helper), std::move(callback)));
342 }
343
SetTimes(Time last_access_time,Time last_modified_time,StatusCallback callback)344 bool FileProxy::SetTimes(Time last_access_time,
345 Time last_modified_time,
346 StatusCallback callback) {
347 DCHECK(file_.IsValid());
348 GenericFileHelper* helper =
349 new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_));
350 return task_runner_->PostTaskAndReply(
351 FROM_HERE,
352 BindOnce(&GenericFileHelper::SetTimes, Unretained(helper),
353 last_access_time, last_modified_time),
354 BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
355 }
356
SetLength(int64_t length,StatusCallback callback)357 bool FileProxy::SetLength(int64_t length, StatusCallback callback) {
358 DCHECK(file_.IsValid());
359 GenericFileHelper* helper =
360 new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_));
361 return task_runner_->PostTaskAndReply(
362 FROM_HERE,
363 BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length),
364 BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
365 }
366
Flush(StatusCallback callback)367 bool FileProxy::Flush(StatusCallback callback) {
368 DCHECK(file_.IsValid());
369 GenericFileHelper* helper =
370 new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_));
371 return task_runner_->PostTaskAndReply(
372 FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)),
373 BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
374 }
375
376 } // namespace base
377