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 <windows.h> 6 #include <setupapi.h> 7 #include <winioctl.h> 8 9 #include "chrome/utility/image_writer/error_messages.h" 10 #include "chrome/utility/image_writer/image_writer.h" 11 12 namespace image_writer { 13 14 const size_t kStorageQueryBufferSize = 1024; 15 16 bool ImageWriter::IsValidDevice() { 17 base::win::ScopedHandle device_handle( 18 CreateFile(device_path_.value().c_str(), 19 GENERIC_READ | GENERIC_WRITE, 20 FILE_SHARE_READ | FILE_SHARE_WRITE, 21 NULL, 22 OPEN_EXISTING, 23 FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, 24 NULL)); 25 if (device_handle == INVALID_HANDLE_VALUE) { 26 Error(error::kOpenDevice); 27 return false; 28 } 29 30 STORAGE_PROPERTY_QUERY query = STORAGE_PROPERTY_QUERY(); 31 query.PropertyId = StorageDeviceProperty; 32 query.QueryType = PropertyStandardQuery; 33 DWORD bytes_returned; 34 35 scoped_ptr<char[]> output_buf(new char[kStorageQueryBufferSize]); 36 BOOL status = DeviceIoControl( 37 device_handle, // Device handle. 38 IOCTL_STORAGE_QUERY_PROPERTY, // Flag to request device properties. 39 &query, // Query parameters. 40 sizeof(STORAGE_PROPERTY_QUERY), // query parameters size. 41 output_buf.get(), // output buffer. 42 kStorageQueryBufferSize, // Size of buffer. 43 &bytes_returned, // Number of bytes returned. 44 // Must not be null. 45 NULL); // Optional unused overlapped perameter. 46 47 if (!status) { 48 PLOG(ERROR) << "Storage property query failed"; 49 return false; 50 } 51 52 STORAGE_DEVICE_DESCRIPTOR* device_descriptor = 53 reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(output_buf.get()); 54 55 return device_descriptor->RemovableMedia == TRUE; 56 } 57 58 bool ImageWriter::OpenDevice() { 59 // Windows requires that device files be opened with FILE_FLAG_NO_BUFFERING 60 // and FILE_FLAG_WRITE_THROUGH. These two flags are not part of base::File. 61 device_file_ = 62 base::File(CreateFile(device_path_.value().c_str(), 63 GENERIC_READ | GENERIC_WRITE, 64 FILE_SHARE_READ | FILE_SHARE_WRITE, 65 NULL, 66 OPEN_EXISTING, 67 FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, 68 NULL)); 69 return device_file_.IsValid(); 70 } 71 72 void ImageWriter::UnmountVolumes(const base::Closure& continuation) { 73 if (!InitializeFiles()) { 74 return; 75 } 76 77 STORAGE_DEVICE_NUMBER sdn = {0}; 78 DWORD bytes_returned; 79 80 BOOL status = DeviceIoControl( 81 device_file_.GetPlatformFile(), 82 IOCTL_STORAGE_GET_DEVICE_NUMBER, 83 NULL, // Unused, must be NULL. 84 0, // Unused, must be 0. 85 &sdn, // An input buffer to hold the STORAGE_DEVICE_NUMBER 86 sizeof(sdn), // The size of the input buffer. 87 &bytes_returned, // the actual number of bytes returned. 88 NULL); // Unused overlap. 89 if (!status) { 90 PLOG(ERROR) << "Unable to get device number."; 91 return; 92 } 93 94 ULONG device_number = sdn.DeviceNumber; 95 96 TCHAR volume_path[MAX_PATH + 1]; 97 HANDLE volume_finder = FindFirstVolume(volume_path, MAX_PATH + 1); 98 if (volume_finder == INVALID_HANDLE_VALUE) { 99 return; 100 } 101 102 HANDLE volume_handle; 103 bool first_volume = true; 104 bool success = true; 105 106 while (first_volume || 107 FindNextVolume(volume_finder, volume_path, MAX_PATH + 1)) { 108 first_volume = false; 109 110 size_t length = wcsnlen(volume_path, MAX_PATH + 1); 111 if (length < 1) { 112 continue; 113 } 114 volume_path[length - 1] = L'\0'; 115 116 volume_handle = CreateFile(volume_path, 117 GENERIC_READ | GENERIC_WRITE, 118 FILE_SHARE_READ | FILE_SHARE_WRITE, 119 NULL, 120 OPEN_EXISTING, 121 0, 122 NULL); 123 if (volume_handle == INVALID_HANDLE_VALUE) { 124 PLOG(ERROR) << "Opening volume handle failed."; 125 success = false; 126 break; 127 } 128 129 volume_handles_.push_back(volume_handle); 130 131 VOLUME_DISK_EXTENTS disk_extents = {0}; 132 status = DeviceIoControl(volume_handle, 133 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 134 NULL, 135 0, 136 &disk_extents, 137 sizeof(disk_extents), 138 &bytes_returned, 139 NULL); 140 141 if (!status) { 142 DWORD error = GetLastError(); 143 if (error == ERROR_MORE_DATA || error == ERROR_INVALID_FUNCTION || 144 error == ERROR_NOT_READY) { 145 continue; 146 } else { 147 PLOG(ERROR) << "Unable to get volume disk extents."; 148 success = false; 149 break; 150 } 151 } 152 153 if (disk_extents.NumberOfDiskExtents != 1 || 154 disk_extents.Extents[0].DiskNumber != device_number) { 155 continue; 156 } 157 158 status = DeviceIoControl(volume_handle, 159 FSCTL_LOCK_VOLUME, 160 NULL, 161 0, 162 NULL, 163 0, 164 &bytes_returned, 165 NULL); 166 if (!status) { 167 PLOG(ERROR) << "Unable to lock volume."; 168 success = false; 169 break; 170 } 171 172 status = DeviceIoControl(volume_handle, 173 FSCTL_DISMOUNT_VOLUME, 174 NULL, 175 0, 176 NULL, 177 0, 178 &bytes_returned, 179 NULL); 180 if (!status) { 181 DWORD error = GetLastError(); 182 if (error != ERROR_NOT_SUPPORTED) { 183 PLOG(ERROR) << "Unable to dismount volume."; 184 success = false; 185 break; 186 } 187 } 188 } 189 190 if (volume_finder != INVALID_HANDLE_VALUE) { 191 FindVolumeClose(volume_finder); 192 } 193 194 if (success) 195 continuation.Run(); 196 } 197 198 } // namespace image_writer 199