• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_MAC)
20 #include "net/base/apple/guarded_fd.h"
21 #endif  // BUILDFLAG(IS_MAC)
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   // FileStream::Context actually closes the file asynchronously,
167   // independently from FileStream's destructor. It can cause problems for
168   // users wanting to delete the file right after FileStream deletion. Thus
169   // we are always adding SHARE_DELETE flag to accommodate such use case.
170   // TODO(rvargas): This sounds like a bug, as deleting the file would
171   // presumably happen on the wrong thread. There should be an async delete.
172 #if BUILDFLAG(IS_WIN)
173   open_flags |= base::File::FLAG_WIN_SHARE_DELETE;
174 #endif
175   base::File file(path, open_flags);
176   if (!file.IsValid()) {
177     return OpenResult(base::File(),
178                       IOResult::FromOSError(logging::GetLastSystemErrorCode()));
179   }
180 
181   return OpenResult(std::move(file), IOResult(OK, 0));
182 }
183 
GetFileInfoImpl(base::File::Info * file_info)184 FileStream::Context::IOResult FileStream::Context::GetFileInfoImpl(
185     base::File::Info* file_info) {
186   bool result = file_.GetInfo(file_info);
187   if (!result)
188     return IOResult::FromOSError(logging::GetLastSystemErrorCode());
189   return IOResult(OK, 0);
190 }
191 
CloseFileImpl()192 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
193 #if BUILDFLAG(IS_MAC)
194   // https://crbug.com/330771755: Guard against a file descriptor being closed
195   // out from underneath the file.
196   if (file_.IsValid()) {
197     guardid_t guardid = reinterpret_cast<guardid_t>(this);
198     PCHECK(change_fdguard_np(file_.GetPlatformFile(), &guardid,
199                              GUARD_CLOSE | GUARD_DUP,
200                              /*nguard=*/nullptr, /*nguardflags=*/0,
201                              /*fdflagsp=*/nullptr) == 0);
202   }
203 #endif
204   file_.Close();
205   return IOResult(OK, 0);
206 }
207 
FlushFileImpl()208 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
209   if (file_.Flush())
210     return IOResult(OK, 0);
211 
212   return IOResult::FromOSError(logging::GetLastSystemErrorCode());
213 }
214 
OnOpenCompleted(CompletionOnceCallback callback,OpenResult open_result)215 void FileStream::Context::OnOpenCompleted(CompletionOnceCallback callback,
216                                           OpenResult open_result) {
217   file_ = std::move(open_result.file);
218   if (file_.IsValid() && !orphaned_)
219     OnFileOpened();
220 
221 #if BUILDFLAG(IS_MAC)
222   // https://crbug.com/330771755: Guard against a file descriptor being closed
223   // out from underneath the file.
224   if (file_.IsValid()) {
225     guardid_t guardid = reinterpret_cast<guardid_t>(this);
226     PCHECK(change_fdguard_np(file_.GetPlatformFile(), /*guard=*/nullptr,
227                              /*guardflags=*/0, &guardid,
228                              GUARD_CLOSE | GUARD_DUP,
229                              /*fdflagsp=*/nullptr) == 0);
230   }
231 #endif
232 
233   OnAsyncCompleted(IntToInt64(std::move(callback)), open_result.error_code);
234 }
235 
CloseAndDelete()236 void FileStream::Context::CloseAndDelete() {
237   DCHECK(!async_in_progress_);
238 
239   if (file_.IsValid()) {
240     bool posted = task_runner_.get()->PostTask(
241         FROM_HERE, base::BindOnce(base::IgnoreResult(&Context::CloseFileImpl),
242                                   base::Owned(this)));
243     DCHECK(posted);
244   } else {
245     delete this;
246   }
247 }
248 
IntToInt64(CompletionOnceCallback callback)249 Int64CompletionOnceCallback FileStream::Context::IntToInt64(
250     CompletionOnceCallback callback) {
251   return base::BindOnce(&CallInt64ToInt, std::move(callback));
252 }
253 
OnAsyncCompleted(Int64CompletionOnceCallback callback,const IOResult & result)254 void FileStream::Context::OnAsyncCompleted(Int64CompletionOnceCallback callback,
255                                            const IOResult& result) {
256   // Reset this before Run() as Run() may issue a new async operation. Also it
257   // should be reset before Close() because it shouldn't run if any async
258   // operation is in progress.
259   async_in_progress_ = false;
260   if (orphaned_) {
261     CloseAndDelete();
262   } else {
263     std::move(callback).Run(result.result);
264   }
265 }
266 
267 }  // namespace net
268