xref: /aosp_15_r20/external/cronet/net/base/file_stream_context.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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