// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "storage/browser/blob/blob_impl.h" #include #include #include #include #include "base/bind.h" #include "base/containers/span.h" #include "base/files/file_util.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/io_buffer.h" #include "storage/browser/blob/blob_data_handle.h" #include "storage/browser/blob/blob_data_item.h" #include "storage/browser/blob/blob_data_snapshot.h" #include "storage/browser/blob/blob_url_loader.h" #include "storage/browser/blob/mojo_blob_reader.h" namespace storage { namespace { class ReaderDelegate : public MojoBlobReader::Delegate { public: ReaderDelegate(mojo::PendingRemote client) : client_(std::move(client)) {} MojoBlobReader::Delegate::RequestSideData DidCalculateSize( uint64_t total_size, uint64_t content_size) override { if (client_) client_->OnCalculatedSize(total_size, content_size); return MojoBlobReader::Delegate::DONT_REQUEST_SIDE_DATA; } void OnComplete(net::Error result, uint64_t total_written_bytes) override { if (client_) client_->OnComplete(result, total_written_bytes); } private: mojo::Remote client_; DISALLOW_COPY_AND_ASSIGN(ReaderDelegate); }; class DataPipeGetterReaderDelegate : public MojoBlobReader::Delegate { public: DataPipeGetterReaderDelegate( network::mojom::DataPipeGetter::ReadCallback callback) : callback_(std::move(callback)) {} MojoBlobReader::Delegate::RequestSideData DidCalculateSize( uint64_t total_size, uint64_t content_size) override { // Check if null since it's conceivable OnComplete() was already called // with error. if (!callback_.is_null()) std::move(callback_).Run(net::OK, content_size); return MojoBlobReader::Delegate::DONT_REQUEST_SIDE_DATA; } void OnComplete(net::Error result, uint64_t total_written_bytes) override { // Check if null since DidCalculateSize() may have already been called // and an error occurred later. if (!callback_.is_null() && result != net::OK) { // On error, signal failure immediately. On success, OnCalculatedSize() // is guaranteed to be called, and the result will be signaled from // there. std::move(callback_).Run(result, 0); } } private: network::mojom::DataPipeGetter::ReadCallback callback_; DISALLOW_COPY_AND_ASSIGN(DataPipeGetterReaderDelegate); }; } // namespace // static base::WeakPtr BlobImpl::Create( std::unique_ptr handle, mojo::PendingReceiver receiver) { return (new BlobImpl(std::move(handle), std::move(receiver))) ->weak_ptr_factory_.GetWeakPtr(); } void BlobImpl::UpdateHandle(std::unique_ptr new_handle) { DCHECK_EQ(handle_->uuid(), new_handle->uuid()); handle_ = std::move(new_handle); } void BlobImpl::Clone(mojo::PendingReceiver receiver) { receivers_.Add(this, std::move(receiver)); } void BlobImpl::AsDataPipeGetter( mojo::PendingReceiver receiver) { data_pipe_getter_receivers_.Add(this, std::move(receiver)); } void BlobImpl::ReadRange( uint64_t offset, uint64_t length, mojo::ScopedDataPipeProducerHandle handle, mojo::PendingRemote client) { MojoBlobReader::Create( handle_.get(), (length == std::numeric_limits::max()) ? net::HttpByteRange::RightUnbounded(offset) : net::HttpByteRange::Bounded(offset, offset + length - 1), std::make_unique(std::move(client)), std::move(handle)); } void BlobImpl::ReadAll( mojo::ScopedDataPipeProducerHandle handle, mojo::PendingRemote client) { MojoBlobReader::Create(handle_.get(), net::HttpByteRange(), std::make_unique(std::move(client)), std::move(handle)); } void BlobImpl::Load( mojo::PendingReceiver loader, const std::string& method, const net::HttpRequestHeaders& headers, mojo::PendingRemote client) { BlobURLLoader::CreateAndStart(std::move(loader), method, headers, std::move(client), std::make_unique(*handle_)); } void BlobImpl::ReadSideData(ReadSideDataCallback callback) { handle_->RunOnConstructionComplete(base::BindOnce( [](BlobDataHandle handle, ReadSideDataCallback callback, BlobStatus status) { if (status != BlobStatus::DONE) { DCHECK(BlobStatusIsError(status)); std::move(callback).Run(base::nullopt); return; } auto snapshot = handle.CreateSnapshot(); // Currently side data is supported only for blobs with a single entry. const auto& items = snapshot->items(); if (items.size() != 1) { std::move(callback).Run(base::nullopt); return; } const auto& item = items[0]; if (item->type() != BlobDataItem::Type::kReadableDataHandle) { std::move(callback).Run(base::nullopt); return; } int32_t body_size = item->data_handle()->GetSideDataSize(); if (body_size == 0) { std::move(callback).Run(base::nullopt); return; } item->data_handle()->ReadSideData(base::BindOnce( [](ReadSideDataCallback callback, int result, mojo_base::BigBuffer buffer) { if (result < 0) { std::move(callback).Run(base::nullopt); return; } std::move(callback).Run(std::move(buffer)); }, std::move(callback))); }, *handle_, std::move(callback))); } void BlobImpl::CaptureSnapshot(CaptureSnapshotCallback callback) { handle_->RunOnConstructionComplete(base::BindOnce( [](base::WeakPtr blob_impl, CaptureSnapshotCallback callback, BlobStatus status) { if (!blob_impl) { // No need to call callback, since blob_impl is only destroyed if the // mojo pipe is disconnected. return; } auto* handle = blob_impl->handle_.get(); if (status != BlobStatus::DONE) { DCHECK(BlobStatusIsError(status)); std::move(callback).Run(0, base::nullopt); return; } auto snapshot = handle->CreateSnapshot(); // Only blobs consisting of a single file can have a modification // time. const auto& items = snapshot->items(); if (items.size() != 1) { std::move(callback).Run(handle->size(), base::nullopt); return; } const auto& item = items[0]; if (item->type() != BlobDataItem::Type::kFile) { std::move(callback).Run(handle->size(), base::nullopt); return; } base::Time modification_time = item->expected_modification_time(); if (!modification_time.is_null() && handle->size() != BlobDataHandle::kUnknownSize) { std::move(callback).Run(handle->size(), modification_time); return; } struct SizeAndTime { uint64_t size; base::Optional time; }; base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, base::BindOnce( [](const base::FilePath& path) { base::File::Info info; if (!base::GetFileInfo(path, &info)) return SizeAndTime{0, base::nullopt}; return SizeAndTime{info.size, info.last_modified}; }, item->path()), base::BindOnce( [](CaptureSnapshotCallback callback, const SizeAndTime& result) { std::move(callback).Run(result.size, result.time); }, std::move(callback))); }, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } void BlobImpl::GetInternalUUID(GetInternalUUIDCallback callback) { std::move(callback).Run(handle_->uuid()); } void BlobImpl::Clone( mojo::PendingReceiver receiver) { data_pipe_getter_receivers_.Add(this, std::move(receiver)); } void BlobImpl::Read(mojo::ScopedDataPipeProducerHandle handle, ReadCallback callback) { MojoBlobReader::Create( handle_.get(), net::HttpByteRange(), std::make_unique(std::move(callback)), std::move(handle)); } void BlobImpl::FlushForTesting() { auto weak_self = weak_ptr_factory_.GetWeakPtr(); receivers_.FlushForTesting(); if (!weak_self) return; data_pipe_getter_receivers_.FlushForTesting(); if (!weak_self) return; if (receivers_.empty() && data_pipe_getter_receivers_.empty()) delete this; } BlobImpl::BlobImpl(std::unique_ptr handle, mojo::PendingReceiver receiver) : handle_(std::move(handle)) { DCHECK(handle_); receivers_.Add(this, std::move(receiver)); receivers_.set_disconnect_handler( base::BindRepeating(&BlobImpl::OnMojoDisconnect, base::Unretained(this))); data_pipe_getter_receivers_.set_disconnect_handler( base::BindRepeating(&BlobImpl::OnMojoDisconnect, base::Unretained(this))); } BlobImpl::~BlobImpl() = default; void BlobImpl::OnMojoDisconnect() { if (!receivers_.empty()) return; if (!data_pipe_getter_receivers_.empty()) return; delete this; } } // namespace storage