• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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