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
IsValidDevice()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
OpenDevice()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
UnmountVolumes(const base::Closure & continuation)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