OLD | NEW |
(Empty) | |
| 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 <sys/socket.h> |
| 6 #include <IOKit/storage/IOStorageProtocolCharacteristics.h> |
| 7 |
| 8 #include "base/message_loop/message_loop_proxy.h" |
| 9 #include "base/message_loop/message_pump_mac.h" |
| 10 #include "base/posix/eintr_wrapper.h" |
| 11 #include "chrome/utility/image_writer/disk_unmounter_mac.h" |
| 12 #include "chrome/utility/image_writer/error_messages.h" |
| 13 #include "chrome/utility/image_writer/image_writer.h" |
| 14 |
| 15 namespace image_writer { |
| 16 |
| 17 bool ImageWriter::IsValidDevice() { |
| 18 base::ScopedCFTypeRef<DASessionRef> session(DASessionCreate(NULL)); |
| 19 DADiskRef disk = DADiskCreateFromBSDName( |
| 20 kCFAllocatorDefault, session, device_path_.value().c_str()); |
| 21 |
| 22 if (!disk) |
| 23 return false; |
| 24 |
| 25 base::ScopedCFTypeRef<CFDictionaryRef> dict(DADiskCopyDescription(disk)); |
| 26 |
| 27 CFBooleanRef internal = base::mac::GetValueFromDictionary<CFBooleanRef>( |
| 28 dict, kDADiskDescriptionDeviceInternalKey); |
| 29 CFStringRef protocol = base::mac::GetValueFromDictionary<CFStringRef>( |
| 30 dict, kDADiskDescriptionDeviceProtocolKey); |
| 31 CFStringRef io_reg_path = base::mac::GetValueFromDictionary<CFStringRef>( |
| 32 dict, kDADiskDescriptionDevicePathKey); |
| 33 CFBooleanRef ejectable = base::mac::GetValueFromDictionary<CFBooleanRef>( |
| 34 dict, kDADiskDescriptionMediaEjectableKey); |
| 35 CFBooleanRef removable = base::mac::GetValueFromDictionary<CFBooleanRef>( |
| 36 dict, kDADiskDescriptionMediaRemovableKey); |
| 37 CFBooleanRef whole = base::mac::GetValueFromDictionary<CFBooleanRef>( |
| 38 dict, kDADiskDescriptionMediaWholeKey); |
| 39 CFStringRef kind = base::mac::GetValueFromDictionary<CFStringRef>( |
| 40 dict, kDADiskDescriptionMediaKindKey); |
| 41 |
| 42 // A drive is a USB stick iff: |
| 43 // - it is not internal |
| 44 // - it is attached to the USB bus |
| 45 // - it is ejectable (because it will be ejected after written to) |
| 46 // - it is removable |
| 47 // - it is the whole drive (although the use of |
| 48 // kDADiskDescriptionMatchMediaWhole should have ensured this) |
| 49 // - it is of type IOMedia (external DVD drives and the like are IOCDMedia |
| 50 // or |
| 51 // IODVDMedia) |
| 52 bool is_usb_stick = |
| 53 !CFBooleanGetValue(internal) && |
| 54 CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) && |
| 55 CFBooleanGetValue(ejectable) && CFBooleanGetValue(removable) && |
| 56 CFBooleanGetValue(whole) && |
| 57 CFStringCompare(kind, CFSTR("IOMedia"), 0) == kCFCompareEqualTo; |
| 58 |
| 59 // A drive is an SD card iff: |
| 60 // - it is attached to the USB bus |
| 61 // - it is ejectable (because it will be ejected after written to) |
| 62 // - it is removable |
| 63 // - it is the whole drive (although the use of |
| 64 // kDADiskDescriptionMatchMediaWhole should have ensured this) |
| 65 // - it is of type IOMedia (external DVD drives and the like are IOCDMedia |
| 66 // or |
| 67 // IODVDMedia) |
| 68 // - the IORegistry device path contains "AppleUSBCardReader" |
| 69 bool is_sd_card = |
| 70 CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) && |
| 71 CFBooleanGetValue(ejectable) && CFBooleanGetValue(removable) && |
| 72 CFBooleanGetValue(whole) && |
| 73 CFStringCompare(kind, CFSTR("IOMedia"), 0) == kCFCompareEqualTo && |
| 74 CFStringFind(io_reg_path, CFSTR("AppleUSBCardReader"), 0).location != |
| 75 kCFNotFound; |
| 76 |
| 77 return is_usb_stick || is_sd_card; |
| 78 } |
| 79 |
| 80 void ImageWriter::UnmountVolumes(const base::Closure& continuation) { |
| 81 if (unmounter_ == NULL) { |
| 82 unmounter_.reset(new DiskUnmounterMac(AsWeakPtr())); |
| 83 } |
| 84 |
| 85 unmounter_->Unmount(device_path_.value(), continuation); |
| 86 } |
| 87 |
| 88 bool ImageWriter::OpenDevice() { |
| 89 int sockets[2]; // [parent's end, child's end] |
| 90 int result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); |
| 91 if (result == -1) { |
| 92 LOG(ERROR) << "Unable to allocate socket pair."; |
| 93 return false; |
| 94 } |
| 95 |
| 96 char rdwr[10]; |
| 97 snprintf(rdwr, sizeof(rdwr), "%d", O_RDWR); |
| 98 |
| 99 pid_t childPid = fork(); |
| 100 if (childPid == -1) { |
| 101 LOG(ERROR) << "Fork failed."; |
| 102 return false; |
| 103 } |
| 104 |
| 105 if (childPid == 0) { // child |
| 106 HANDLE_EINTR(dup2(sockets[1], STDOUT_FILENO)); |
| 107 close(sockets[0]); |
| 108 close(sockets[1]); |
| 109 |
| 110 base::FilePath real_device_path; |
| 111 if (device_path_.IsAbsolute()) { |
| 112 real_device_path = device_path_; |
| 113 } else { |
| 114 real_device_path = base::FilePath("/dev").Append(device_path_); |
| 115 } |
| 116 |
| 117 const char authopenPath[] = "/usr/libexec/authopen"; |
| 118 execl(authopenPath, |
| 119 authopenPath, |
| 120 "-stdoutpipe", |
| 121 "-o", |
| 122 rdwr, |
| 123 real_device_path.value().c_str(), |
| 124 NULL); |
| 125 _exit(errno); |
| 126 } else { // parent |
| 127 close(sockets[1]); |
| 128 int fd = -1; |
| 129 |
| 130 msghdr message = {0}; |
| 131 const size_t kDataBufferSize = 1024; |
| 132 char dataBuffer[kDataBufferSize]; |
| 133 iovec ioVec[1]; |
| 134 ioVec[0].iov_base = dataBuffer; |
| 135 ioVec[0].iov_len = kDataBufferSize; |
| 136 message.msg_iov = ioVec; |
| 137 message.msg_iovlen = 1; |
| 138 const socklen_t kCmsgSocketSize = (socklen_t)CMSG_SPACE(sizeof(int)); |
| 139 char cmsgSocket[kCmsgSocketSize]; |
| 140 message.msg_control = cmsgSocket; |
| 141 message.msg_controllen = kCmsgSocketSize; |
| 142 ssize_t size = HANDLE_EINTR(recvmsg(sockets[0], &message, 0)); |
| 143 if (size > 0) { |
| 144 cmsghdr* cmsgSocketHeader = CMSG_FIRSTHDR(&message); |
| 145 // Paranoia. |
| 146 if (cmsgSocketHeader && cmsgSocketHeader->cmsg_level == SOL_SOCKET && |
| 147 cmsgSocketHeader->cmsg_type == SCM_RIGHTS) |
| 148 fd = *((int*)CMSG_DATA(cmsgSocketHeader)); |
| 149 } |
| 150 |
| 151 int childStat; |
| 152 result = HANDLE_EINTR(waitpid(childPid, &childStat, 0)); |
| 153 close(sockets[0]); |
| 154 |
| 155 if (result != -1 && WIFEXITED(childStat)) { |
| 156 int exitStatus = WEXITSTATUS(childStat); |
| 157 if (exitStatus) { |
| 158 LOG(ERROR) << "Child process returned failure."; |
| 159 return false; |
| 160 } |
| 161 } |
| 162 |
| 163 device_file_ = base::File(fd); |
| 164 |
| 165 return device_file_.IsValid(); |
| 166 } |
| 167 } |
| 168 |
| 169 } // namespace image_writer |
OLD | NEW |