| Index: chrome/utility/image_writer/image_writer_mac.cc
|
| diff --git a/chrome/utility/image_writer/image_writer_mac.cc b/chrome/utility/image_writer/image_writer_mac.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..99277d90e78bc9e5f68da6b539beca9ea8f5a5d2
|
| --- /dev/null
|
| +++ b/chrome/utility/image_writer/image_writer_mac.cc
|
| @@ -0,0 +1,169 @@
|
| +// Copyright 2014 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 <sys/socket.h>
|
| +#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
|
| +
|
| +#include "base/message_loop/message_loop_proxy.h"
|
| +#include "base/message_loop/message_pump_mac.h"
|
| +#include "base/posix/eintr_wrapper.h"
|
| +#include "chrome/utility/image_writer/disk_unmounter_mac.h"
|
| +#include "chrome/utility/image_writer/error_messages.h"
|
| +#include "chrome/utility/image_writer/image_writer.h"
|
| +
|
| +namespace image_writer {
|
| +
|
| +bool ImageWriter::IsValidDevice() {
|
| + base::ScopedCFTypeRef<DASessionRef> session(DASessionCreate(NULL));
|
| + DADiskRef disk = DADiskCreateFromBSDName(
|
| + kCFAllocatorDefault, session, device_path_.value().c_str());
|
| +
|
| + if (!disk)
|
| + return false;
|
| +
|
| + base::ScopedCFTypeRef<CFDictionaryRef> dict(DADiskCopyDescription(disk));
|
| +
|
| + CFBooleanRef internal = base::mac::GetValueFromDictionary<CFBooleanRef>(
|
| + dict, kDADiskDescriptionDeviceInternalKey);
|
| + CFStringRef protocol = base::mac::GetValueFromDictionary<CFStringRef>(
|
| + dict, kDADiskDescriptionDeviceProtocolKey);
|
| + CFStringRef io_reg_path = base::mac::GetValueFromDictionary<CFStringRef>(
|
| + dict, kDADiskDescriptionDevicePathKey);
|
| + CFBooleanRef ejectable = base::mac::GetValueFromDictionary<CFBooleanRef>(
|
| + dict, kDADiskDescriptionMediaEjectableKey);
|
| + CFBooleanRef removable = base::mac::GetValueFromDictionary<CFBooleanRef>(
|
| + dict, kDADiskDescriptionMediaRemovableKey);
|
| + CFBooleanRef whole = base::mac::GetValueFromDictionary<CFBooleanRef>(
|
| + dict, kDADiskDescriptionMediaWholeKey);
|
| + CFStringRef kind = base::mac::GetValueFromDictionary<CFStringRef>(
|
| + dict, kDADiskDescriptionMediaKindKey);
|
| +
|
| + // A drive is a USB stick iff:
|
| + // - it is not internal
|
| + // - it is attached to the USB bus
|
| + // - it is ejectable (because it will be ejected after written to)
|
| + // - it is removable
|
| + // - it is the whole drive (although the use of
|
| + // kDADiskDescriptionMatchMediaWhole should have ensured this)
|
| + // - it is of type IOMedia (external DVD drives and the like are IOCDMedia
|
| + // or
|
| + // IODVDMedia)
|
| + bool is_usb_stick =
|
| + !CFBooleanGetValue(internal) &&
|
| + CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) &&
|
| + CFBooleanGetValue(ejectable) && CFBooleanGetValue(removable) &&
|
| + CFBooleanGetValue(whole) &&
|
| + CFStringCompare(kind, CFSTR("IOMedia"), 0) == kCFCompareEqualTo;
|
| +
|
| + // A drive is an SD card iff:
|
| + // - it is attached to the USB bus
|
| + // - it is ejectable (because it will be ejected after written to)
|
| + // - it is removable
|
| + // - it is the whole drive (although the use of
|
| + // kDADiskDescriptionMatchMediaWhole should have ensured this)
|
| + // - it is of type IOMedia (external DVD drives and the like are IOCDMedia
|
| + // or
|
| + // IODVDMedia)
|
| + // - the IORegistry device path contains "AppleUSBCardReader"
|
| + bool is_sd_card =
|
| + CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) &&
|
| + CFBooleanGetValue(ejectable) && CFBooleanGetValue(removable) &&
|
| + CFBooleanGetValue(whole) &&
|
| + CFStringCompare(kind, CFSTR("IOMedia"), 0) == kCFCompareEqualTo &&
|
| + CFStringFind(io_reg_path, CFSTR("AppleUSBCardReader"), 0).location !=
|
| + kCFNotFound;
|
| +
|
| + return is_usb_stick || is_sd_card;
|
| +}
|
| +
|
| +void ImageWriter::UnmountVolumes(const base::Closure& continuation) {
|
| + if (unmounter_ == NULL) {
|
| + unmounter_.reset(new DiskUnmounterMac(AsWeakPtr()));
|
| + }
|
| +
|
| + unmounter_->Unmount(device_path_.value(), continuation);
|
| +}
|
| +
|
| +bool ImageWriter::OpenDevice() {
|
| + int sockets[2]; // [parent's end, child's end]
|
| + int result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
|
| + if (result == -1) {
|
| + LOG(ERROR) << "Unable to allocate socket pair.";
|
| + return false;
|
| + }
|
| +
|
| + char rdwr[10];
|
| + snprintf(rdwr, sizeof(rdwr), "%d", O_RDWR);
|
| +
|
| + pid_t childPid = fork();
|
| + if (childPid == -1) {
|
| + LOG(ERROR) << "Fork failed.";
|
| + return false;
|
| + }
|
| +
|
| + if (childPid == 0) { // child
|
| + HANDLE_EINTR(dup2(sockets[1], STDOUT_FILENO));
|
| + close(sockets[0]);
|
| + close(sockets[1]);
|
| +
|
| + base::FilePath real_device_path;
|
| + if (device_path_.IsAbsolute()) {
|
| + real_device_path = device_path_;
|
| + } else {
|
| + real_device_path = base::FilePath("/dev").Append(device_path_);
|
| + }
|
| +
|
| + const char authopenPath[] = "/usr/libexec/authopen";
|
| + execl(authopenPath,
|
| + authopenPath,
|
| + "-stdoutpipe",
|
| + "-o",
|
| + rdwr,
|
| + real_device_path.value().c_str(),
|
| + NULL);
|
| + _exit(errno);
|
| + } else { // parent
|
| + close(sockets[1]);
|
| + int fd = -1;
|
| +
|
| + msghdr message = {0};
|
| + const size_t kDataBufferSize = 1024;
|
| + char dataBuffer[kDataBufferSize];
|
| + iovec ioVec[1];
|
| + ioVec[0].iov_base = dataBuffer;
|
| + ioVec[0].iov_len = kDataBufferSize;
|
| + message.msg_iov = ioVec;
|
| + message.msg_iovlen = 1;
|
| + const socklen_t kCmsgSocketSize = (socklen_t)CMSG_SPACE(sizeof(int));
|
| + char cmsgSocket[kCmsgSocketSize];
|
| + message.msg_control = cmsgSocket;
|
| + message.msg_controllen = kCmsgSocketSize;
|
| + ssize_t size = HANDLE_EINTR(recvmsg(sockets[0], &message, 0));
|
| + if (size > 0) {
|
| + cmsghdr* cmsgSocketHeader = CMSG_FIRSTHDR(&message);
|
| + // Paranoia.
|
| + if (cmsgSocketHeader && cmsgSocketHeader->cmsg_level == SOL_SOCKET &&
|
| + cmsgSocketHeader->cmsg_type == SCM_RIGHTS)
|
| + fd = *((int*)CMSG_DATA(cmsgSocketHeader));
|
| + }
|
| +
|
| + int childStat;
|
| + result = HANDLE_EINTR(waitpid(childPid, &childStat, 0));
|
| + close(sockets[0]);
|
| +
|
| + if (result != -1 && WIFEXITED(childStat)) {
|
| + int exitStatus = WEXITSTATUS(childStat);
|
| + if (exitStatus) {
|
| + LOG(ERROR) << "Child process returned failure.";
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + device_file_ = base::File(fd);
|
| +
|
| + return device_file_.IsValid();
|
| + }
|
| +}
|
| +
|
| +} // namespace image_writer
|
|
|