chromium/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace {

// Prepares the file for analysis and returns the result on the UI thread with
// either `success_callback` or `failure_callback`.
void PrepareFileToAnalyze(
    base::FilePath file_path,
    uint64_t max_size,
    base::OnceCallback<void(SandboxedDMGAnalyzer::WrappedFilePtr)>
        success_callback,
    base::OnceCallback<void(safe_browsing::ArchiveAnalysisResult)>
        failure_callback) {
  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  SandboxedDMGAnalyzer::WrappedFilePtr file(
      new base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
                                    base::File::FLAG_WIN_SHARE_DELETE),
      base::OnTaskRunnerDeleter(
          base::SequencedTaskRunner::GetCurrentDefault()));

  if (!file->IsValid()) {
    DLOG(ERROR) << "Could not open file: " << file_path.value();
    content::GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(
            std::move(failure_callback),
            std::move(safe_browsing::ArchiveAnalysisResult::kFailedToOpen)));
    return;
  }

  uint64_t size = file->GetLength();

  bool too_big_to_unpack = base::checked_cast<uint64_t>(size) > max_size;
  if (too_big_to_unpack) {
    DLOG(ERROR) << "File is too big: " << file_path.value();
    content::GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(
            std::move(failure_callback),
            std::move(safe_browsing::ArchiveAnalysisResult::kTooLarge)));
    return;
  }

  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(std::move(success_callback), std::move(file)));
}

}  // namespace

// static
std::unique_ptr<SandboxedDMGAnalyzer, base::OnTaskRunnerDeleter>
SandboxedDMGAnalyzer::CreateAnalyzer(
    const base::FilePath& dmg_file,
    const uint64_t max_size,
    ResultCallback callback,
    mojo::PendingRemote<chrome::mojom::FileUtilService> service) {
  return std::unique_ptr<SandboxedDMGAnalyzer, base::OnTaskRunnerDeleter>(
      new SandboxedDMGAnalyzer(dmg_file, max_size, std::move(callback),
                               std::move(service)),
      base::OnTaskRunnerDeleter(content::GetUIThreadTaskRunner({})));
}

SandboxedDMGAnalyzer::SandboxedDMGAnalyzer(
    const base::FilePath& dmg_file,
    const uint64_t max_size,
    ResultCallback callback,
    mojo::PendingRemote<chrome::mojom::FileUtilService> service)
    : file_path_(dmg_file),
      max_size_(max_size),
      callback_(std::move(callback)),
      service_(std::move(service)),
      file_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
  DCHECK(callback_);
  service_->BindSafeArchiveAnalyzer(
      remote_analyzer_.BindNewPipeAndPassReceiver());
  remote_analyzer_.set_disconnect_handler(
      base::BindOnce(&SandboxedDMGAnalyzer::AnalyzeFileDone, GetWeakPtr(),
                     safe_browsing::ArchiveAnalyzerResults()));
}

void SandboxedDMGAnalyzer::Start() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  file_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &PrepareFileToAnalyze, file_path_, max_size_,
          base::BindOnce(&SandboxedDMGAnalyzer::AnalyzeFile, GetWeakPtr()),
          base::BindOnce(&SandboxedDMGAnalyzer::ReportFileFailure,
                         GetWeakPtr())));
}

SandboxedDMGAnalyzer::~SandboxedDMGAnalyzer() = default;

void SandboxedDMGAnalyzer::ReportFileFailure(
    safe_browsing::ArchiveAnalysisResult reason) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  if (callback_) {
    safe_browsing::ArchiveAnalyzerResults results;
    results.analysis_result = reason;
    std::move(callback_).Run(results);
  }
}

void SandboxedDMGAnalyzer::AnalyzeFile(
    SandboxedDMGAnalyzer::WrappedFilePtr file) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (remote_analyzer_) {
    mojo::PendingRemote<chrome::mojom::TemporaryFileGetter>
        temp_file_getter_remote =
            temp_file_getter_.GetRemoteTemporaryFileGetter();
    remote_analyzer_->AnalyzeDmgFile(
        std::move(*file), std::move(temp_file_getter_remote),
        base::BindOnce(&SandboxedDMGAnalyzer::AnalyzeFileDone, GetWeakPtr()));
  } else {
    AnalyzeFileDone(safe_browsing::ArchiveAnalyzerResults());
  }
}

void SandboxedDMGAnalyzer::AnalyzeFileDone(
    const safe_browsing::ArchiveAnalyzerResults& results) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  remote_analyzer_.reset();
  std::move(callback_).Run(results);
}

base::WeakPtr<SandboxedDMGAnalyzer> SandboxedDMGAnalyzer::GetWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}