chromium/chrome/utility/image_writer/disk_unmounter_mac.cc

// Copyright 2014 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/utility/image_writer/disk_unmounter_mac.h"

#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include <sys/socket.h>

#include "base/message_loop/message_pump_apple.h"
#include "base/message_loop/message_pump_type.h"
#include "base/posix/eintr_wrapper.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/utility/image_writer/error_message_strings.h"
#include "chrome/utility/image_writer/image_writer.h"

namespace image_writer {

DiskUnmounterMac::DiskUnmounterMac() : cf_thread_("ImageWriterDiskArb") {
  base::Thread::Options options;
  options.message_pump_type = base::MessagePumpType::UI;

  cf_thread_.StartWithOptions(std::move(options));
}

DiskUnmounterMac::~DiskUnmounterMac() {
  if (disk_)
    DADiskUnclaim(disk_.get());
}

void DiskUnmounterMac::Unmount(const std::string& device_path,
                               base::OnceClosure success_continuation,
                               base::OnceClosure failure_continuation) {
  // Should only be used once.
  DCHECK(!original_thread_.get());
  DCHECK(!success_continuation_);
  DCHECK(!failure_continuation_);

  DCHECK(success_continuation);
  DCHECK(failure_continuation);

  original_thread_ = base::SingleThreadTaskRunner::GetCurrentDefault();
  success_continuation_ = std::move(success_continuation);
  failure_continuation_ = std::move(failure_continuation);

  cf_thread_.task_runner()->PostTask(
      FROM_HERE, base::BindOnce(&DiskUnmounterMac::UnmountOnWorker,
                                base::Unretained(this), device_path));
}

// static
void DiskUnmounterMac::DiskClaimed(DADiskRef disk,
                                   DADissenterRef dissenter,
                                   void* context) {
  DiskUnmounterMac* disk_unmounter = static_cast<DiskUnmounterMac*>(context);

  if (dissenter) {
    LOG(ERROR) << "Unable to claim disk.";
    disk_unmounter->Error();
    return;
  }

  DADiskUnmount(disk,
                kDADiskUnmountOptionForce | kDADiskUnmountOptionWhole,
                DiskUnmounted,
                disk_unmounter);
}

// static
DADissenterRef DiskUnmounterMac::DiskClaimRevoked(DADiskRef disk,
                                                  void* context) {
  CFStringRef reason = CFSTR(
      "Hi. Sorry to bother you, but I'm busy overwriting the entire disk "
      "here. There's nothing to claim but the smoldering ruins of bytes "
      "that were in flash memory. Trust me, it's nothing that you want. "
      "All the best. Toodles!");
  return DADissenterCreate(kCFAllocatorDefault, kDAReturnBusy, reason);
}

// static
void DiskUnmounterMac::DiskUnmounted(DADiskRef disk,
                                     DADissenterRef dissenter,
                                     void* context) {
  DiskUnmounterMac* disk_unmounter = static_cast<DiskUnmounterMac*>(context);

  if (dissenter) {
    LOG(ERROR) << "Unable to unmount disk.";
    disk_unmounter->Error();
    return;
  }

  disk_unmounter->original_thread_->PostTask(
      FROM_HERE, std::move(disk_unmounter->success_continuation_));
}

void DiskUnmounterMac::UnmountOnWorker(const std::string& device_path) {
  DCHECK(cf_thread_.task_runner()->BelongsToCurrentThread());

  session_.reset(DASessionCreate(NULL));

  DASessionScheduleWithRunLoop(session_.get(), CFRunLoopGetCurrent(),
                               kCFRunLoopCommonModes);

  disk_.reset(DADiskCreateFromBSDName(kCFAllocatorDefault, session_.get(),
                                      device_path.c_str()));

  if (!disk_) {
    LOG(ERROR) << "Unable to get disk reference.";
    Error();
    return;
  }

  DADiskClaim(disk_.get(), kDADiskClaimOptionDefault, DiskClaimRevoked, this,
              DiskClaimed, this);
}

void DiskUnmounterMac::Error() {
  original_thread_->PostTask(FROM_HERE, std::move(failure_continuation_));
}

}  // namespace image_writer