1// Copyright 2014 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 "chrome/utility/image_writer/disk_unmounter_mac.h"
6
7#include <sys/socket.h>
8#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
9
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/message_loop/message_pump_mac.h"
12#include "base/posix/eintr_wrapper.h"
13#include "chrome/utility/image_writer/error_messages.h"
14#include "chrome/utility/image_writer/image_writer.h"
15
16namespace image_writer {
17
18DiskUnmounterMac::DiskUnmounterMac() : cf_thread_("ImageWriterDiskArb") {
19  base::Thread::Options options;
20  options.message_pump_factory = base::Bind(&CreateMessagePump);
21
22  cf_thread_.StartWithOptions(options);
23}
24
25DiskUnmounterMac::~DiskUnmounterMac() {
26  if (disk_)
27    DADiskUnclaim(disk_);
28}
29
30void DiskUnmounterMac::Unmount(const std::string& device_path,
31                               const base::Closure& success_continuation,
32                               const base::Closure& failure_continuation) {
33  // Should only be used once.
34  DCHECK(!original_thread_.get());
35  original_thread_ = base::MessageLoopProxy::current();
36  success_continuation_ = success_continuation;
37  failure_continuation_ = failure_continuation;
38
39  cf_thread_.message_loop()->PostTask(
40      FROM_HERE,
41      base::Bind(&DiskUnmounterMac::UnmountOnWorker,
42                 base::Unretained(this),
43                 device_path));
44}
45
46// static
47void DiskUnmounterMac::DiskClaimed(DADiskRef disk,
48                                   DADissenterRef dissenter,
49                                   void* context) {
50  DiskUnmounterMac* disk_unmounter = static_cast<DiskUnmounterMac*>(context);
51
52  if (dissenter) {
53    LOG(ERROR) << "Unable to claim disk.";
54    disk_unmounter->Error();
55    return;
56  }
57
58  DADiskUnmount(disk,
59                kDADiskUnmountOptionForce | kDADiskUnmountOptionWhole,
60                DiskUnmounted,
61                disk_unmounter);
62}
63
64// static
65DADissenterRef DiskUnmounterMac::DiskClaimRevoked(DADiskRef disk,
66                                                  void* context) {
67  CFStringRef reason = CFSTR(
68      "Hi. Sorry to bother you, but I'm busy overwriting the entire disk "
69      "here. There's nothing to claim but the smoldering ruins of bytes "
70      "that were in flash memory. Trust me, it's nothing that you want. "
71      "All the best. Toodles!");
72  return DADissenterCreate(kCFAllocatorDefault, kDAReturnBusy, reason);
73}
74
75// static
76void DiskUnmounterMac::DiskUnmounted(DADiskRef disk,
77                                     DADissenterRef dissenter,
78                                     void* context) {
79  DiskUnmounterMac* disk_unmounter = static_cast<DiskUnmounterMac*>(context);
80
81  if (dissenter) {
82    LOG(ERROR) << "Unable to unmount disk.";
83    disk_unmounter->Error();
84    return;
85  }
86
87  disk_unmounter->original_thread_->PostTask(
88      FROM_HERE, disk_unmounter->success_continuation_);
89}
90
91// static
92scoped_ptr<base::MessagePump> DiskUnmounterMac::CreateMessagePump() {
93  return scoped_ptr<base::MessagePump>(new base::MessagePumpCFRunLoop);
94}
95
96void DiskUnmounterMac::UnmountOnWorker(const std::string& device_path) {
97  DCHECK(cf_thread_.message_loop() == base::MessageLoop::current());
98
99  session_.reset(DASessionCreate(NULL));
100
101  DASessionScheduleWithRunLoop(
102      session_, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
103
104  disk_.reset(DADiskCreateFromBSDName(
105      kCFAllocatorDefault, session_, device_path.c_str()));
106
107  if (!disk_) {
108    LOG(ERROR) << "Unable to get disk reference.";
109    Error();
110    return;
111  }
112
113  DADiskClaim(disk_,
114              kDADiskClaimOptionDefault,
115              DiskClaimRevoked,
116              this,
117              DiskClaimed,
118              this);
119}
120
121void DiskUnmounterMac::Error() {
122  original_thread_->PostTask(FROM_HERE, failure_continuation_);
123}
124
125}  // namespace image_writer
126