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 #include "net/base/file_stream_context.h"
6
7 #include <utility>
8
9 #include "base/files/file_path.h"
10 #include "base/functional/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/task/task_runner.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "base/values.h"
16 #include "build/build_config.h"
17 #include "net/base/net_errors.h"
18
19 #if BUILDFLAG(IS_ANDROID)
20 #include "base/android/content_uri_utils.h"
21 #endif
22
23 namespace net {
24
25 namespace {
26
CallInt64ToInt(CompletionOnceCallback callback,int64_t result)27 void CallInt64ToInt(CompletionOnceCallback callback, int64_t result) {
28 std::move(callback).Run(static_cast<int>(result));
29 }
30
31 } // namespace
32
IOResult()33 FileStream::Context::IOResult::IOResult()
34 : result(OK),
35 os_error(0) {
36 }
37
IOResult(int64_t result,logging::SystemErrorCode os_error)38 FileStream::Context::IOResult::IOResult(int64_t result,
39 logging::SystemErrorCode os_error)
40 : result(result), os_error(os_error) {
41 }
42
43 // static
FromOSError(logging::SystemErrorCode os_error)44 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
45 logging::SystemErrorCode os_error) {
46 return IOResult(MapSystemError(os_error), os_error);
47 }
48
49 // ---------------------------------------------------------------------
50
51 FileStream::Context::OpenResult::OpenResult() = default;
52
OpenResult(base::File file,IOResult error_code)53 FileStream::Context::OpenResult::OpenResult(base::File file,
54 IOResult error_code)
55 : file(std::move(file)), error_code(error_code) {}
56
OpenResult(OpenResult && other)57 FileStream::Context::OpenResult::OpenResult(OpenResult&& other)
58 : file(std::move(other.file)), error_code(other.error_code) {}
59
operator =(OpenResult && other)60 FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=(
61 OpenResult&& other) {
62 file = std::move(other.file);
63 error_code = other.error_code;
64 return *this;
65 }
66
67 // ---------------------------------------------------------------------
68
Orphan()69 void FileStream::Context::Orphan() {
70 DCHECK(!orphaned_);
71
72 orphaned_ = true;
73
74 if (!async_in_progress_) {
75 CloseAndDelete();
76 } else if (file_.IsValid()) {
77 #if BUILDFLAG(IS_WIN)
78 CancelIo(file_.GetPlatformFile());
79 #endif
80 }
81 }
82
Open(const base::FilePath & path,int open_flags,CompletionOnceCallback callback)83 void FileStream::Context::Open(const base::FilePath& path,
84 int open_flags,
85 CompletionOnceCallback callback) {
86 DCHECK(!async_in_progress_);
87
88 bool posted = task_runner_->PostTaskAndReplyWithResult(
89 FROM_HERE,
90 base::BindOnce(&Context::OpenFileImpl, base::Unretained(this), path,
91 open_flags),
92 base::BindOnce(&Context::OnOpenCompleted, base::Unretained(this),
93 std::move(callback)));
94 DCHECK(posted);
95
96 async_in_progress_ = true;
97 }
98
Close(CompletionOnceCallback callback)99 void FileStream::Context::Close(CompletionOnceCallback callback) {
100 DCHECK(!async_in_progress_);
101
102 bool posted = task_runner_->PostTaskAndReplyWithResult(
103 FROM_HERE,
104 base::BindOnce(&Context::CloseFileImpl, base::Unretained(this)),
105 base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
106 IntToInt64(std::move(callback))));
107 DCHECK(posted);
108
109 async_in_progress_ = true;
110 }
111
Seek(int64_t offset,Int64CompletionOnceCallback callback)112 void FileStream::Context::Seek(int64_t offset,
113 Int64CompletionOnceCallback callback) {
114 DCHECK(!async_in_progress_);
115
116 if (offset < 0) {
117 std::move(callback).Run(net::ERR_INVALID_ARGUMENT);
118 return;
119 }
120
121 bool posted = task_runner_->PostTaskAndReplyWithResult(
122 FROM_HERE,
123 base::BindOnce(&Context::SeekFileImpl, base::Unretained(this), offset),
124 base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
125 std::move(callback)));
126 DCHECK(posted);
127
128 async_in_progress_ = true;
129 }
130
GetFileInfo(base::File::Info * file_info,CompletionOnceCallback callback)131 void FileStream::Context::GetFileInfo(base::File::Info* file_info,
132 CompletionOnceCallback callback) {
133 task_runner_->PostTaskAndReplyWithResult(
134 FROM_HERE,
135 base::BindOnce(&Context::GetFileInfoImpl, base::Unretained(this),
136 base::Unretained(file_info)),
137 base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
138 IntToInt64(std::move(callback))));
139
140 async_in_progress_ = true;
141 }
142
Flush(CompletionOnceCallback callback)143 void FileStream::Context::Flush(CompletionOnceCallback callback) {
144 DCHECK(!async_in_progress_);
145
146 bool posted = task_runner_->PostTaskAndReplyWithResult(
147 FROM_HERE,
148 base::BindOnce(&Context::FlushFileImpl, base::Unretained(this)),
149 base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
150 IntToInt64(std::move(callback))));
151 DCHECK(posted);
152
153 async_in_progress_ = true;
154 }
155
IsOpen() const156 bool FileStream::Context::IsOpen() const {
157 return file_.IsValid();
158 }
159
OpenFileImpl(const base::FilePath & path,int open_flags)160 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
161 const base::FilePath& path, int open_flags) {
162 #if BUILDFLAG(IS_POSIX)
163 // Always use blocking IO.
164 open_flags &= ~base::File::FLAG_ASYNC;
165 #endif
166 base::File file;
167 #if BUILDFLAG(IS_ANDROID)
168 if (path.IsContentUri()) {
169 // Check that only Read flags are set.
170 DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
171 base::File::FLAG_OPEN | base::File::FLAG_READ);
172 file = base::OpenContentUriForRead(path);
173 } else {
174 #endif // BUILDFLAG(IS_ANDROID)
175 // FileStream::Context actually closes the file asynchronously,
176 // independently from FileStream's destructor. It can cause problems for
177 // users wanting to delete the file right after FileStream deletion. Thus
178 // we are always adding SHARE_DELETE flag to accommodate such use case.
179 // TODO(rvargas): This sounds like a bug, as deleting the file would
180 // presumably happen on the wrong thread. There should be an async delete.
181 open_flags |= base::File::FLAG_WIN_SHARE_DELETE;
182 file.Initialize(path, open_flags);
183 #if BUILDFLAG(IS_ANDROID)
184 }
185 #endif // BUILDFLAG(IS_ANDROID)
186 if (!file.IsValid()) {
187 return OpenResult(base::File(),
188 IOResult::FromOSError(logging::GetLastSystemErrorCode()));
189 }
190
191 return OpenResult(std::move(file), IOResult(OK, 0));
192 }
193
GetFileInfoImpl(base::File::Info * file_info)194 FileStream::Context::IOResult FileStream::Context::GetFileInfoImpl(
195 base::File::Info* file_info) {
196 bool result = file_.GetInfo(file_info);
197 if (!result)
198 return IOResult::FromOSError(logging::GetLastSystemErrorCode());
199 return IOResult(OK, 0);
200 }
201
CloseFileImpl()202 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
203 file_.Close();
204 return IOResult(OK, 0);
205 }
206
FlushFileImpl()207 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
208 if (file_.Flush())
209 return IOResult(OK, 0);
210
211 return IOResult::FromOSError(logging::GetLastSystemErrorCode());
212 }
213
OnOpenCompleted(CompletionOnceCallback callback,OpenResult open_result)214 void FileStream::Context::OnOpenCompleted(CompletionOnceCallback callback,
215 OpenResult open_result) {
216 file_ = std::move(open_result.file);
217 if (file_.IsValid() && !orphaned_)
218 OnFileOpened();
219
220 OnAsyncCompleted(IntToInt64(std::move(callback)), open_result.error_code);
221 }
222
CloseAndDelete()223 void FileStream::Context::CloseAndDelete() {
224 DCHECK(!async_in_progress_);
225
226 if (file_.IsValid()) {
227 bool posted = task_runner_.get()->PostTask(
228 FROM_HERE, base::BindOnce(base::IgnoreResult(&Context::CloseFileImpl),
229 base::Owned(this)));
230 DCHECK(posted);
231 } else {
232 delete this;
233 }
234 }
235
IntToInt64(CompletionOnceCallback callback)236 Int64CompletionOnceCallback FileStream::Context::IntToInt64(
237 CompletionOnceCallback callback) {
238 return base::BindOnce(&CallInt64ToInt, std::move(callback));
239 }
240
OnAsyncCompleted(Int64CompletionOnceCallback callback,const IOResult & result)241 void FileStream::Context::OnAsyncCompleted(Int64CompletionOnceCallback callback,
242 const IOResult& result) {
243 // Reset this before Run() as Run() may issue a new async operation. Also it
244 // should be reset before Close() because it shouldn't run if any async
245 // operation is in progress.
246 async_in_progress_ = false;
247 if (orphaned_) {
248 CloseAndDelete();
249 } else {
250 std::move(callback).Run(result.result);
251 }
252 }
253
254 } // namespace net
255