1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 "content/browser/file_system_access/native_file_system_file_writer_impl.h"
6 
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/task/post_task.h"
10 #include "base/task/thread_pool.h"
11 #include "build/build_config.h"
12 #include "components/services/quarantine/quarantine.h"
13 #include "content/browser/file_system_access/native_file_system_error.h"
14 #include "content/browser/file_system_access/native_file_system_manager_impl.h"
15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/common/content_client.h"
17 #include "crypto/secure_hash.h"
18 #include "mojo/public/cpp/bindings/callback_helpers.h"
19 #include "storage/browser/blob/blob_storage_context.h"
20 #include "storage/browser/file_system/file_stream_reader.h"
21 #include "storage/browser/file_system/file_system_operation_runner.h"
22 #include "third_party/blink/public/common/blob/blob_utils.h"
23 #include "third_party/blink/public/mojom/blob/blob.mojom.h"
24 #include "third_party/blink/public/mojom/file_system_access/native_file_system_error.mojom.h"
25 
26 using blink::mojom::NativeFileSystemStatus;
27 using storage::BlobDataHandle;
28 using storage::FileSystemOperation;
29 using storage::FileSystemOperationRunner;
30 
31 namespace content {
32 
33 namespace {
34 
35 // For after write checks we need the hash and size of the file. That data is
36 // calculated on the IO thread by this class.
37 // This class is ref-counted to make it easier to integrate with the
38 // FileStreamReader API where methods either return synchronously or invoke
39 // their callback asynchronously.
40 class HashCalculator : public base::RefCounted<HashCalculator> {
41  public:
42   // Must be called on the FileSystemContext's IO runner.
CreateAndStart(scoped_refptr<storage::FileSystemContext> context,NativeFileSystemFileWriterImpl::HashCallback callback,const storage::FileSystemURL & swap_url,storage::FileSystemOperationRunner *)43   static void CreateAndStart(
44       scoped_refptr<storage::FileSystemContext> context,
45       NativeFileSystemFileWriterImpl::HashCallback callback,
46       const storage::FileSystemURL& swap_url,
47       storage::FileSystemOperationRunner*) {
48     auto calculator = base::MakeRefCounted<HashCalculator>(std::move(context),
49                                                            std::move(callback));
50     calculator->Start(swap_url);
51   }
52 
HashCalculator(scoped_refptr<storage::FileSystemContext> context,NativeFileSystemFileWriterImpl::HashCallback callback)53   HashCalculator(scoped_refptr<storage::FileSystemContext> context,
54                  NativeFileSystemFileWriterImpl::HashCallback callback)
55       : context_(std::move(context)), callback_(std::move(callback)) {
56     DCHECK(context_);
57   }
58 
59  private:
60   friend class base::RefCounted<HashCalculator>;
61   ~HashCalculator() = default;
62 
Start(const storage::FileSystemURL & swap_url)63   void Start(const storage::FileSystemURL& swap_url) {
64     reader_ = context_->CreateFileStreamReader(
65         swap_url, 0, storage::kMaximumLength, base::Time());
66     int64_t length =
67         reader_->GetLength(base::BindOnce(&HashCalculator::GotLength, this));
68     if (length == net::ERR_IO_PENDING)
69       return;
70     GotLength(length);
71   }
72 
GotLength(int64_t length)73   void GotLength(int64_t length) {
74     if (length < 0) {
75       std::move(callback_).Run(storage::NetErrorToFileError(length),
76                                std::string(), -1);
77       return;
78     }
79 
80     file_size_ = length;
81     ReadMore();
82   }
83 
ReadMore()84   void ReadMore() {
85     DCHECK_GE(file_size_, 0);
86     int read_result =
87         reader_->Read(buffer_.get(), buffer_->size(),
88                       base::BindOnce(&HashCalculator::DidRead, this));
89     if (read_result == net::ERR_IO_PENDING)
90       return;
91     DidRead(read_result);
92   }
93 
DidRead(int bytes_read)94   void DidRead(int bytes_read) {
95     DCHECK_GE(file_size_, 0);
96     if (bytes_read < 0) {
97       std::move(callback_).Run(storage::NetErrorToFileError(bytes_read),
98                                std::string(), -1);
99       return;
100     }
101     if (bytes_read == 0) {
102       std::string hash_str(hash_->GetHashLength(), 0);
103       hash_->Finish(base::data(hash_str), hash_str.size());
104       std::move(callback_).Run(base::File::FILE_OK, hash_str, file_size_);
105       return;
106     }
107 
108     hash_->Update(buffer_->data(), bytes_read);
109     ReadMore();
110   }
111 
112   const scoped_refptr<storage::FileSystemContext> context_;
113   NativeFileSystemFileWriterImpl::HashCallback callback_;
114 
115   const scoped_refptr<net::IOBufferWithSize> buffer_{
116       base::MakeRefCounted<net::IOBufferWithSize>(8 * 1024)};
117 
118   const std::unique_ptr<crypto::SecureHash> hash_{
119       crypto::SecureHash::Create(crypto::SecureHash::SHA256)};
120 
121   std::unique_ptr<storage::FileStreamReader> reader_;
122   int64_t file_size_ = -1;
123 };
124 
RemoveSwapFile(const storage::FileSystemURL & swap_url,storage::FileSystemOperationRunner * runner)125 void RemoveSwapFile(const storage::FileSystemURL& swap_url,
126                     storage::FileSystemOperationRunner* runner) {
127   runner->Remove(swap_url, /*recursive=*/false, base::DoNothing());
128 }
129 
130 }  // namespace
131 
132 struct NativeFileSystemFileWriterImpl::WriteState {
133   WriteCallback callback;
134   uint64_t bytes_written = 0;
135 };
136 
NativeFileSystemFileWriterImpl(NativeFileSystemManagerImpl * manager,const BindingContext & context,const storage::FileSystemURL & url,const storage::FileSystemURL & swap_url,const SharedHandleState & handle_state,bool has_transient_user_activation,download::QuarantineConnectionCallback quarantine_connection_callback)137 NativeFileSystemFileWriterImpl::NativeFileSystemFileWriterImpl(
138     NativeFileSystemManagerImpl* manager,
139     const BindingContext& context,
140     const storage::FileSystemURL& url,
141     const storage::FileSystemURL& swap_url,
142     const SharedHandleState& handle_state,
143     bool has_transient_user_activation,
144     download::QuarantineConnectionCallback quarantine_connection_callback)
145     : NativeFileSystemHandleBase(manager, context, url, handle_state),
146       swap_url_(swap_url),
147       quarantine_connection_callback_(
148           std::move(quarantine_connection_callback)),
149       has_transient_user_activation_(has_transient_user_activation) {
150   DCHECK_EQ(swap_url.type(), url.type());
151 }
152 
~NativeFileSystemFileWriterImpl()153 NativeFileSystemFileWriterImpl::~NativeFileSystemFileWriterImpl() {
154   if (can_purge()) {
155     DoFileSystemOperation(FROM_HERE, &FileSystemOperationRunner::RemoveFile,
156                           base::BindOnce(
157                               [](const storage::FileSystemURL& swap_url,
158                                  base::File::Error result) {
159                                 if (result != base::File::FILE_OK) {
160                                   DLOG(ERROR)
161                                       << "Error Deleting Swap File, status: "
162                                       << base::File::ErrorToString(result)
163                                       << " path: " << swap_url.path();
164                                 }
165                               },
166                               swap_url()),
167                           swap_url());
168   }
169 }
170 
Write(uint64_t offset,mojo::PendingRemote<blink::mojom::Blob> data,WriteCallback callback)171 void NativeFileSystemFileWriterImpl::Write(
172     uint64_t offset,
173     mojo::PendingRemote<blink::mojom::Blob> data,
174     WriteCallback callback) {
175   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
176 
177   RunWithWritePermission(
178       base::BindOnce(&NativeFileSystemFileWriterImpl::WriteImpl,
179                      weak_factory_.GetWeakPtr(), offset, std::move(data)),
180       base::BindOnce([](blink::mojom::NativeFileSystemErrorPtr result,
181                         WriteCallback callback) {
182         std::move(callback).Run(std::move(result),
183                                 /*bytes_written=*/0);
184       }),
185       std::move(callback));
186 }
187 
WriteStream(uint64_t offset,mojo::ScopedDataPipeConsumerHandle stream,WriteStreamCallback callback)188 void NativeFileSystemFileWriterImpl::WriteStream(
189     uint64_t offset,
190     mojo::ScopedDataPipeConsumerHandle stream,
191     WriteStreamCallback callback) {
192   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193 
194   RunWithWritePermission(
195       base::BindOnce(&NativeFileSystemFileWriterImpl::WriteStreamImpl,
196                      weak_factory_.GetWeakPtr(), offset, std::move(stream)),
197       base::BindOnce([](blink::mojom::NativeFileSystemErrorPtr result,
198                         WriteStreamCallback callback) {
199         std::move(callback).Run(std::move(result),
200                                 /*bytes_written=*/0);
201       }),
202       std::move(callback));
203 }
204 
Truncate(uint64_t length,TruncateCallback callback)205 void NativeFileSystemFileWriterImpl::Truncate(uint64_t length,
206                                               TruncateCallback callback) {
207   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
208 
209   RunWithWritePermission(
210       base::BindOnce(&NativeFileSystemFileWriterImpl::TruncateImpl,
211                      weak_factory_.GetWeakPtr(), length),
212       base::BindOnce([](blink::mojom::NativeFileSystemErrorPtr result,
213                         TruncateCallback callback) {
214         std::move(callback).Run(std::move(result));
215       }),
216       std::move(callback));
217 }
218 
Close(CloseCallback callback)219 void NativeFileSystemFileWriterImpl::Close(CloseCallback callback) {
220   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
221 
222   RunWithWritePermission(
223       base::BindOnce(&NativeFileSystemFileWriterImpl::CloseImpl,
224                      weak_factory_.GetWeakPtr()),
225       base::BindOnce([](blink::mojom::NativeFileSystemErrorPtr result,
226                         CloseCallback callback) {
227         std::move(callback).Run(std::move(result));
228       }),
229       std::move(callback));
230 }
231 
232 namespace {
233 
234 // Writing a blob to a file consists of three operations:
235 // 1) The Blob reads its data, and writes it out to a mojo data pipe producer
236 //    handle. It calls BlobReaderClient::OnComplete when all data has been
237 //    written.
238 // 2) WriteStream reads data from the associated mojo data pipe consumer handle
239 //    as long as data is available.
240 // 3) All the read data is written to disk. Signalled by calling WriteCompleted.
241 //
242 // All of these steps are done in parallel, operating on chunks. Furthermore the
243 // OnComplete call from step 1) is done over a different mojo pipe than where
244 // the data is sent, making it possible for this to arrive either before or
245 // after step 3) completes. To make sure we report an error when any of these
246 // steps fail, this helper class waits for both the OnComplete call to arrive
247 // and for the write to finish before considering the entire write operation a
248 // success.
249 //
250 // On the other hand, as soon as we're aware of any of these steps failing, that
251 // error can be propagated, as that means the operation failed.
252 // In other words, this is like Promise.all, which resolves when all
253 // operations succeed, or rejects as soon as any operation fails.
254 //
255 // This class deletes itself after calling its callback.
256 class BlobReaderClient : public base::SupportsWeakPtr<BlobReaderClient>,
257                          public blink::mojom::BlobReaderClient {
258  public:
BlobReaderClient(NativeFileSystemFileWriterImpl::WriteCallback callback,mojo::PendingReceiver<blink::mojom::BlobReaderClient> receiver)259   BlobReaderClient(
260       NativeFileSystemFileWriterImpl::WriteCallback callback,
261       mojo::PendingReceiver<blink::mojom::BlobReaderClient> receiver)
262       : callback_(std::move(callback)), receiver_(this, std::move(receiver)) {
263     receiver_.set_disconnect_handler(
264         base::BindOnce(&BlobReaderClient::OnDisconnect, AsWeakPtr()));
265   }
266 
OnCalculatedSize(uint64_t total_size,uint64_t expected_content_size)267   void OnCalculatedSize(uint64_t total_size,
268                         uint64_t expected_content_size) override {}
OnComplete(int32_t status,uint64_t data_length)269   void OnComplete(int32_t status, uint64_t data_length) override {
270     DCHECK(!read_result_.has_value());
271     read_result_ = status;
272     MaybeCallCallbackAndDeleteThis();
273   }
274 
WriteCompleted(blink::mojom::NativeFileSystemErrorPtr result,uint64_t bytes_written)275   void WriteCompleted(blink::mojom::NativeFileSystemErrorPtr result,
276                       uint64_t bytes_written) {
277     DCHECK(!write_result_);
278     write_result_ = std::move(result);
279     bytes_written_ = bytes_written;
280     MaybeCallCallbackAndDeleteThis();
281   }
282 
283  private:
284   friend class base::RefCounted<BlobReaderClient>;
285   ~BlobReaderClient() override = default;
286 
OnDisconnect()287   void OnDisconnect() {
288     if (!read_result_.has_value()) {
289       // Disconnected without getting a read result, treat this as read failure.
290       read_result_ = net::ERR_ABORTED;
291       MaybeCallCallbackAndDeleteThis();
292     }
293   }
294 
MaybeCallCallbackAndDeleteThis()295   void MaybeCallCallbackAndDeleteThis() {
296     // |this| is deleted right after invoking |callback_|, so |callback_| should
297     // always be valid here.
298     DCHECK(callback_);
299 
300     if (read_result_.has_value() && *read_result_ != net::Error::OK) {
301       // Reading from the blob failed, report that error.
302       std::move(callback_).Run(native_file_system_error::FromFileError(
303                                    storage::NetErrorToFileError(*read_result_)),
304                                0);
305       delete this;
306       return;
307     }
308     if (!write_result_.is_null() &&
309         write_result_->status != blink::mojom::NativeFileSystemStatus::kOk) {
310       // Writing failed, report that error.
311       std::move(callback_).Run(std::move(write_result_), 0);
312       delete this;
313       return;
314     }
315     if (read_result_.has_value() && !write_result_.is_null()) {
316       // Both reading and writing succeeded, report success.
317       std::move(callback_).Run(std::move(write_result_), bytes_written_);
318       delete this;
319       return;
320     }
321     // Still waiting for the other operation to complete, so don't call the
322     // callback yet.
323   }
324 
325   NativeFileSystemFileWriterImpl::WriteCallback callback_;
326   mojo::Receiver<blink::mojom::BlobReaderClient> receiver_;
327 
328   base::Optional<int32_t> read_result_;
329   blink::mojom::NativeFileSystemErrorPtr write_result_;
330   uint64_t bytes_written_ = 0;
331 };
332 
333 }  // namespace
334 
WriteImpl(uint64_t offset,mojo::PendingRemote<blink::mojom::Blob> data,WriteCallback callback)335 void NativeFileSystemFileWriterImpl::WriteImpl(
336     uint64_t offset,
337     mojo::PendingRemote<blink::mojom::Blob> data,
338     WriteCallback callback) {
339   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
340   DCHECK_EQ(GetWritePermissionStatus(),
341             blink::mojom::PermissionStatus::GRANTED);
342 
343   if (is_closed()) {
344     std::move(callback).Run(
345         native_file_system_error::FromStatus(
346             NativeFileSystemStatus::kInvalidState,
347             "An attempt was made to write to a closed writer."),
348         /*bytes_written=*/0);
349     return;
350   }
351 
352   MojoCreateDataPipeOptions options;
353   options.struct_size = sizeof(MojoCreateDataPipeOptions);
354   options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
355   options.element_num_bytes = 1;
356   options.capacity_num_bytes =
357       blink::BlobUtils::GetDataPipeCapacity(blink::BlobUtils::kUnknownSize);
358 
359   mojo::ScopedDataPipeProducerHandle producer_handle;
360   mojo::ScopedDataPipeConsumerHandle consumer_handle;
361   MojoResult rv =
362       mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle);
363   if (rv != MOJO_RESULT_OK) {
364     std::move(callback).Run(
365         native_file_system_error::FromStatus(
366             NativeFileSystemStatus::kOperationFailed,
367             "Internal read error: failed to create mojo data pipe."),
368         /*bytes_written=*/0);
369     return;
370   }
371 
372   // TODO(mek): We can do this transformation from Blob to DataPipe in the
373   // renderer, and simplify the mojom exposed interface.
374   mojo::Remote<blink::mojom::Blob> blob(std::move(data));
375   mojo::PendingRemote<blink::mojom::BlobReaderClient> reader_client;
376   auto* client = new BlobReaderClient(
377       std::move(callback), reader_client.InitWithNewPipeAndPassReceiver());
378   blob->ReadAll(std::move(producer_handle), std::move(reader_client));
379   WriteStreamImpl(
380       offset, std::move(consumer_handle),
381       base::BindOnce(&BlobReaderClient::WriteCompleted, client->AsWeakPtr()));
382 }
383 
WriteStreamImpl(uint64_t offset,mojo::ScopedDataPipeConsumerHandle stream,WriteStreamCallback callback)384 void NativeFileSystemFileWriterImpl::WriteStreamImpl(
385     uint64_t offset,
386     mojo::ScopedDataPipeConsumerHandle stream,
387     WriteStreamCallback callback) {
388   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
389   DCHECK_EQ(GetWritePermissionStatus(),
390             blink::mojom::PermissionStatus::GRANTED);
391 
392   if (is_closed()) {
393     std::move(callback).Run(
394         native_file_system_error::FromStatus(
395             NativeFileSystemStatus::kInvalidState,
396             "An attempt was made to write to a closed writer."),
397         /*bytes_written=*/0);
398     return;
399   }
400 
401   DoFileSystemOperation(
402       FROM_HERE, &FileSystemOperationRunner::WriteStream,
403       base::BindRepeating(&NativeFileSystemFileWriterImpl::DidWrite,
404                           weak_factory_.GetWeakPtr(),
405                           base::Owned(new WriteState{std::move(callback)})),
406       swap_url(), std::move(stream), offset);
407 }
408 
DidWrite(WriteState * state,base::File::Error result,int64_t bytes,bool complete)409 void NativeFileSystemFileWriterImpl::DidWrite(WriteState* state,
410                                               base::File::Error result,
411                                               int64_t bytes,
412                                               bool complete) {
413   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
414 
415   DCHECK(state);
416   state->bytes_written += bytes;
417   if (complete) {
418     std::move(state->callback)
419         .Run(native_file_system_error::FromFileError(result),
420              state->bytes_written);
421   }
422 }
423 
TruncateImpl(uint64_t length,TruncateCallback callback)424 void NativeFileSystemFileWriterImpl::TruncateImpl(uint64_t length,
425                                                   TruncateCallback callback) {
426   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
427   DCHECK_EQ(GetWritePermissionStatus(),
428             blink::mojom::PermissionStatus::GRANTED);
429 
430   if (is_closed()) {
431     std::move(callback).Run(native_file_system_error::FromStatus(
432         NativeFileSystemStatus::kInvalidState,
433         "An attempt was made to write to a closed writer."));
434     return;
435   }
436 
437   DoFileSystemOperation(
438       FROM_HERE, &FileSystemOperationRunner::Truncate,
439       base::BindOnce(
440           [](TruncateCallback callback, base::File::Error result) {
441             std::move(callback).Run(
442                 native_file_system_error::FromFileError(result));
443           },
444           std::move(callback)),
445       swap_url(), length);
446 }
447 
CloseImpl(CloseCallback callback)448 void NativeFileSystemFileWriterImpl::CloseImpl(CloseCallback callback) {
449   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
450   DCHECK_EQ(GetWritePermissionStatus(),
451             blink::mojom::PermissionStatus::GRANTED);
452   if (is_closed()) {
453     std::move(callback).Run(native_file_system_error::FromStatus(
454         NativeFileSystemStatus::kInvalidState,
455         "An attempt was made to close an already closed writer."));
456     return;
457   }
458 
459   // Should the writer be destructed at this point, we want to allow the
460   // close operation to run its course, so we should not purge the swap file.
461   // If the after write check fails, the callback for that will clean up the
462   // swap file even if the writer was destroyed at that point.
463   state_ = State::kClosePending;
464 
465   if (!RequireSecurityChecks() || !manager()->permission_context()) {
466     DidPassAfterWriteCheck(std::move(callback));
467     return;
468   }
469 
470   ComputeHashForSwapFile(base::BindOnce(
471       &NativeFileSystemFileWriterImpl::DoAfterWriteCheck,
472       weak_factory_.GetWeakPtr(), base::WrapRefCounted(manager()), swap_url(),
473       std::move(callback)));
474 }
475 
476 // static
DoAfterWriteCheck(base::WeakPtr<NativeFileSystemFileWriterImpl> file_writer,scoped_refptr<NativeFileSystemManagerImpl> manager,const storage::FileSystemURL & swap_url,NativeFileSystemFileWriterImpl::CloseCallback callback,base::File::Error hash_result,const std::string & hash,int64_t size)477 void NativeFileSystemFileWriterImpl::DoAfterWriteCheck(
478     base::WeakPtr<NativeFileSystemFileWriterImpl> file_writer,
479     scoped_refptr<NativeFileSystemManagerImpl> manager,
480     const storage::FileSystemURL& swap_url,
481     NativeFileSystemFileWriterImpl::CloseCallback callback,
482     base::File::Error hash_result,
483     const std::string& hash,
484     int64_t size) {
485   if (!file_writer || hash_result != base::File::FILE_OK) {
486     // If writer was deleted, or calculating the hash failed try deleting the
487     // swap file and invoke the callback.
488     manager->operation_runner().PostTaskWithThisObject(
489         FROM_HERE, base::BindOnce(&RemoveSwapFile, swap_url));
490     std::move(callback).Run(native_file_system_error::FromStatus(
491         NativeFileSystemStatus::kOperationAborted,
492         "Failed to perform Safe Browsing check."));
493     return;
494   }
495 
496   DCHECK_CALLED_ON_VALID_SEQUENCE(file_writer->sequence_checker_);
497 
498   auto item = std::make_unique<NativeFileSystemWriteItem>();
499   item->target_file_path = file_writer->url().path();
500   item->full_path = file_writer->swap_url().path();
501   item->sha256_hash = hash;
502   item->size = size;
503   item->frame_url = file_writer->context().url;
504   item->has_user_gesture = file_writer->has_transient_user_activation_;
505   file_writer->manager()->permission_context()->PerformAfterWriteChecks(
506       std::move(item), file_writer->context().frame_id,
507       base::BindOnce(&NativeFileSystemFileWriterImpl::DidAfterWriteCheck,
508                      file_writer, std::move(manager), swap_url,
509                      std::move(callback)));
510 }
511 
512 // static
DidAfterWriteCheck(base::WeakPtr<NativeFileSystemFileWriterImpl> file_writer,scoped_refptr<NativeFileSystemManagerImpl> manager,const storage::FileSystemURL & swap_url,NativeFileSystemFileWriterImpl::CloseCallback callback,NativeFileSystemPermissionContext::AfterWriteCheckResult result)513 void NativeFileSystemFileWriterImpl::DidAfterWriteCheck(
514     base::WeakPtr<NativeFileSystemFileWriterImpl> file_writer,
515     scoped_refptr<NativeFileSystemManagerImpl> manager,
516     const storage::FileSystemURL& swap_url,
517     NativeFileSystemFileWriterImpl::CloseCallback callback,
518     NativeFileSystemPermissionContext::AfterWriteCheckResult result) {
519   if (file_writer &&
520       result ==
521           NativeFileSystemPermissionContext::AfterWriteCheckResult::kAllow) {
522     file_writer->DidPassAfterWriteCheck(std::move(callback));
523     return;
524   }
525 
526   // Writer is gone, or safe browsing check failed. In this case we should
527   // try deleting the swap file and call the callback to report that close
528   // failed.
529   manager->operation_runner().PostTaskWithThisObject(
530       FROM_HERE, base::BindOnce(&RemoveSwapFile, swap_url));
531   std::move(callback).Run(native_file_system_error::FromStatus(
532       NativeFileSystemStatus::kOperationAborted,
533       "Write operation blocked by Safe Browsing."));
534   return;
535 }
536 
DidPassAfterWriteCheck(CloseCallback callback)537 void NativeFileSystemFileWriterImpl::DidPassAfterWriteCheck(
538     CloseCallback callback) {
539   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
540   // If the move operation succeeds, the path pointing to the swap file
541   // will not exist anymore.
542   // In case of error, the swap file URL will point to a valid filesystem
543   // location. The file at this URL will be deleted when the mojo pipe closes.
544   base::OnceCallback<void(base::File::Error)> result_callback;
545   if (RequireSecurityChecks()) {
546     GURL referrer_url = manager()->is_off_the_record() ? GURL() : context().url;
547     mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote;
548     if (quarantine_connection_callback_) {
549       quarantine_connection_callback_.Run(
550           quarantine_remote.BindNewPipeAndPassReceiver());
551     }
552     result_callback =
553         base::BindOnce(&NativeFileSystemFileWriterImpl::DidSwapFileDoQuarantine,
554                        weak_factory_.GetWeakPtr(), url(), referrer_url,
555                        std::move(quarantine_remote), std::move(callback));
556   } else {
557     result_callback = base::BindOnce(
558         &NativeFileSystemFileWriterImpl::DidSwapFileSkipQuarantine,
559         weak_factory_.GetWeakPtr(), std::move(callback));
560   }
561   DoFileSystemOperation(
562       FROM_HERE, &FileSystemOperationRunner::MoveFileLocal,
563       std::move(result_callback), swap_url(), url(),
564       storage::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED);
565 }
566 
DidSwapFileSkipQuarantine(CloseCallback callback,base::File::Error result)567 void NativeFileSystemFileWriterImpl::DidSwapFileSkipQuarantine(
568     CloseCallback callback,
569     base::File::Error result) {
570   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
571   if (result != base::File::FILE_OK) {
572     state_ = State::kCloseError;
573     DLOG(ERROR) << "Swap file move operation failed source: "
574                 << swap_url().path() << " dest: " << url().path()
575                 << " error: " << base::File::ErrorToString(result);
576     std::move(callback).Run(native_file_system_error::FromFileError(result));
577     return;
578   }
579 
580   state_ = State::kClosed;
581   std::move(callback).Run(native_file_system_error::Ok());
582 }
583 
584 // static
DidSwapFileDoQuarantine(base::WeakPtr<NativeFileSystemFileWriterImpl> file_writer,const storage::FileSystemURL & target_url,const GURL & referrer_url,mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote,CloseCallback callback,base::File::Error result)585 void NativeFileSystemFileWriterImpl::DidSwapFileDoQuarantine(
586     base::WeakPtr<NativeFileSystemFileWriterImpl> file_writer,
587     const storage::FileSystemURL& target_url,
588     const GURL& referrer_url,
589     mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote,
590     CloseCallback callback,
591     base::File::Error result) {
592   if (file_writer)
593     DCHECK_CALLED_ON_VALID_SEQUENCE(file_writer->sequence_checker_);
594 
595   if (result != base::File::FILE_OK) {
596     if (file_writer)
597       file_writer->state_ = State::kCloseError;
598     DLOG(ERROR) << "Swap file move operation failed dest: " << target_url.path()
599                 << " error: " << base::File::ErrorToString(result);
600     std::move(callback).Run(native_file_system_error::FromFileError(result));
601     return;
602   }
603 
604   // The quarantine service operates on files identified by a base::FilePath. As
605   // such we can only quarantine files that are actual local files.
606   // On ChromeOS on the other hand anything that isn't in the sandboxed file
607   // system is also uniquely identifiable by its FileSystemURL::path(), and
608   // thus we accept all other FileSystemURL types.
609 #if defined(OS_CHROMEOS)
610   DCHECK(target_url.type() != storage::kFileSystemTypeTemporary &&
611          target_url.type() != storage::kFileSystemTypePersistent)
612       << target_url.type();
613 #else
614   DCHECK(target_url.type() == storage::kFileSystemTypeNativeLocal ||
615          target_url.type() == storage::kFileSystemTypeTest)
616       << target_url.type();
617 #endif
618 
619   GURL authority_url =
620       referrer_url.is_valid() && referrer_url.SchemeIsHTTPOrHTTPS()
621           ? referrer_url
622           : GURL();
623 
624   if (quarantine_remote) {
625     quarantine::mojom::Quarantine* raw_quarantine = quarantine_remote.get();
626     raw_quarantine->QuarantineFile(
627         target_url.path(), authority_url, referrer_url,
628         GetContentClient()
629             ->browser()
630             ->GetApplicationClientGUIDForQuarantineCheck(),
631         mojo::WrapCallbackWithDefaultInvokeIfNotRun(
632             base::BindOnce(&NativeFileSystemFileWriterImpl::DidAnnotateFile,
633                            std::move(file_writer), std::move(callback),
634                            std::move(quarantine_remote)),
635             quarantine::mojom::QuarantineFileResult::ANNOTATION_FAILED));
636   } else {
637 #if defined(OS_WIN)
638     base::ThreadPool::PostTaskAndReplyWithResult(
639         FROM_HERE, {base::MayBlock()},
640         base::BindOnce(&quarantine::SetInternetZoneIdentifierDirectly,
641                        target_url.path(), authority_url, referrer_url),
642         base::BindOnce(&NativeFileSystemFileWriterImpl::DidAnnotateFile,
643                        std::move(file_writer), std::move(callback),
644                        std::move(quarantine_remote)));
645 #else
646     if (file_writer) {
647       file_writer->DidAnnotateFile(
648           std::move(callback), std::move(quarantine_remote),
649           quarantine::mojom::QuarantineFileResult::ANNOTATION_FAILED);
650     }
651 #endif
652   }
653 }
654 
DidAnnotateFile(CloseCallback callback,mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote,quarantine::mojom::QuarantineFileResult result)655 void NativeFileSystemFileWriterImpl::DidAnnotateFile(
656     CloseCallback callback,
657     mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote,
658     quarantine::mojom::QuarantineFileResult result) {
659   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
660   state_ = State::kClosed;
661 
662   if (result != quarantine::mojom::QuarantineFileResult::OK &&
663       result != quarantine::mojom::QuarantineFileResult::ANNOTATION_FAILED) {
664     // If malware was detected, or the file referrer was blocked by policy, the
665     // file will be deleted at this point by AttachmentServices on Windows.
666     // There is nothing to do except to return the error message to the
667     // application.
668     std::move(callback).Run(native_file_system_error::FromStatus(
669         NativeFileSystemStatus::kOperationAborted,
670         "Write operation aborted due to security policy."));
671     return;
672   }
673 
674   std::move(callback).Run(native_file_system_error::Ok());
675 }
676 
ComputeHashForSwapFile(HashCallback callback)677 void NativeFileSystemFileWriterImpl::ComputeHashForSwapFile(
678     HashCallback callback) {
679   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
680 
681   auto wrapped_callback = base::BindOnce(
682       [](scoped_refptr<base::SequencedTaskRunner> runner, HashCallback callback,
683          base::File::Error error, const std::string& hash, int64_t size) {
684         runner->PostTask(
685             FROM_HERE, base::BindOnce(std::move(callback), error, hash, size));
686       },
687       base::SequencedTaskRunnerHandle::Get(), std::move(callback));
688 
689   manager()->operation_runner().PostTaskWithThisObject(
690       FROM_HERE, base::BindOnce(&HashCalculator::CreateAndStart,
691                                 base::WrapRefCounted(file_system_context()),
692                                 std::move(wrapped_callback), swap_url()));
693 }
694 
695 base::WeakPtr<NativeFileSystemHandleBase>
AsWeakPtr()696 NativeFileSystemFileWriterImpl::AsWeakPtr() {
697   return weak_factory_.GetWeakPtr();
698 }
699 
700 }  // namespace content
701