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.
5 #include <CoreFoundation/CoreFoundation.h>
6 #include <IOKit/IOBSD.h>
7 #include <IOKit/IOKitLib.h>
8 #include <IOKit/storage/IOBlockStorageDevice.h>
9 #include <IOKit/storage/IOMedia.h>
10 #include <IOKit/storage/IOStorageProtocolCharacteristics.h>
11 #include <sys/socket.h>
13 #include "base/command_line.h"
14 #include "base/files/scoped_file.h"
15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/mac/scoped_ioobject.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/process/kill.h"
19 #include "base/process/launch.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/sys_string_conversions.h"
22 #include "chrome/common/extensions/image_writer/image_writer_util_mac.h"
23 #include "chrome/utility/image_writer/disk_unmounter_mac.h"
24 #include "chrome/utility/image_writer/error_messages.h"
25 #include "chrome/utility/image_writer/image_writer.h"
27 namespace image_writer
{
29 static const char kAuthOpenPath
[] = "/usr/libexec/authopen";
31 bool ImageWriter::IsValidDevice() {
32 base::ScopedCFTypeRef
<CFStringRef
> cf_bsd_name(
33 base::SysUTF8ToCFStringRef(device_path_
.value()));
34 CFMutableDictionaryRef matching
= IOServiceMatching(kIOMediaClass
);
35 CFDictionaryAddValue(matching
, CFSTR(kIOMediaWholeKey
), kCFBooleanTrue
);
36 CFDictionaryAddValue(matching
, CFSTR(kIOMediaWritableKey
), kCFBooleanTrue
);
37 CFDictionaryAddValue(matching
, CFSTR(kIOBSDNameKey
), cf_bsd_name
);
39 io_service_t disk_obj
=
40 IOServiceGetMatchingService(kIOMasterPortDefault
, matching
);
41 base::mac::ScopedIOObject
<io_service_t
> iterator_ref(disk_obj
);
44 CFMutableDictionaryRef dict
;
45 if (IORegistryEntryCreateCFProperties(
46 disk_obj
, &dict
, kCFAllocatorDefault
, 0) != KERN_SUCCESS
) {
47 LOG(ERROR
) << "Unable to get properties of disk object.";
50 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> dict_ref(dict
);
52 CFBooleanRef cf_removable
= base::mac::GetValueFromDictionary
<CFBooleanRef
>(
53 dict
, CFSTR(kIOMediaRemovableKey
));
54 bool removable
= CFBooleanGetValue(cf_removable
);
56 bool is_usb
= extensions::IsUsbDevice(disk_obj
);
58 return removable
|| is_usb
;
64 void ImageWriter::UnmountVolumes(const base::Closure
& continuation
) {
65 if (unmounter_
== NULL
) {
66 unmounter_
.reset(new DiskUnmounterMac());
73 &ImageWriter::Error
, base::Unretained(this), error::kUnmountVolumes
));
76 bool ImageWriter::OpenDevice() {
77 base::LaunchOptions options
= base::LaunchOptions();
80 // Create a socket pair for communication.
82 int result
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sockets
);
84 PLOG(ERROR
) << "Unable to allocate socket pair.";
87 base::ScopedFD
parent_socket(sockets
[0]);
88 base::ScopedFD
child_socket(sockets
[1]);
90 // Map the client socket to the client's STDOUT.
91 base::FileHandleMappingVector fd_map
;
92 fd_map
.push_back(std::pair
<int, int>(child_socket
.get(), STDOUT_FILENO
));
93 options
.fds_to_remap
= &fd_map
;
95 // Find the file path to open.
96 base::FilePath real_device_path
;
97 if (device_path_
.IsAbsolute()) {
98 real_device_path
= device_path_
;
100 real_device_path
= base::FilePath("/dev").Append(device_path_
);
103 // Build the command line.
104 std::string rdwr
= base::StringPrintf("%d", O_RDWR
);
106 base::CommandLine cmd_line
= base::CommandLine(base::FilePath(kAuthOpenPath
));
107 cmd_line
.AppendSwitch("-stdoutpipe");
108 // Using AppendSwitchNative will use an equal-symbol which we don't want.
109 cmd_line
.AppendArg("-o");
110 cmd_line
.AppendArg(rdwr
);
111 cmd_line
.AppendArgPath(real_device_path
);
113 // Launch the process.
114 base::ProcessHandle process_handle
;
115 if (!base::LaunchProcess(cmd_line
, options
, &process_handle
)) {
116 LOG(ERROR
) << "Failed to launch authopen process.";
120 // Receive a file descriptor from authopen which sends a single FD via
121 // sendmsg and the SCM_RIGHTS extension.
123 const size_t kDataBufferSize
= sizeof(struct cmsghdr
) + sizeof(int);
124 char data_buffer
[kDataBufferSize
];
126 struct iovec io_vec
[1];
127 io_vec
[0].iov_base
= data_buffer
;
128 io_vec
[0].iov_len
= kDataBufferSize
;
130 const socklen_t kCmsgSocketSize
=
131 static_cast<socklen_t
>(CMSG_SPACE(sizeof(int)));
132 char cmsg_socket
[kCmsgSocketSize
];
134 struct msghdr message
= {0};
135 message
.msg_iov
= io_vec
;
136 message
.msg_iovlen
= 1;
137 message
.msg_control
= cmsg_socket
;
138 message
.msg_controllen
= kCmsgSocketSize
;
140 ssize_t size
= HANDLE_EINTR(recvmsg(parent_socket
.get(), &message
, 0));
142 struct cmsghdr
* cmsg_socket_header
= CMSG_FIRSTHDR(&message
);
144 if (cmsg_socket_header
&& cmsg_socket_header
->cmsg_level
== SOL_SOCKET
&&
145 cmsg_socket_header
->cmsg_type
== SCM_RIGHTS
) {
146 fd
= *reinterpret_cast<int*>(CMSG_DATA(cmsg_socket_header
));
150 device_file_
= base::File(fd
);
152 // Wait for the child.
153 int child_exit_status
;
154 if (!base::WaitForExitCode(process_handle
, &child_exit_status
)) {
155 LOG(ERROR
) << "Unable to wait for child.";
159 if (child_exit_status
) {
160 LOG(ERROR
) << "Child process returned failure.";
164 return device_file_
.IsValid();
167 } // namespace image_writer