OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/storage_monitor/volume_mount_watcher_win.h" | 5 #include "chrome/browser/storage_monitor/volume_mount_watcher_win.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include <dbt.h> | 9 #include <dbt.h> |
10 #include <fileapi.h> | 10 #include <fileapi.h> |
11 #include <winioctl.h> | 11 #include <winioctl.h> |
12 | 12 |
13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
16 #include "base/string_util.h" | 16 #include "base/string_util.h" |
17 #include "base/stringprintf.h" | 17 #include "base/stringprintf.h" |
18 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
19 #include "base/task_runner_util.h" | 19 #include "base/task_runner_util.h" |
20 #include "base/time.h" | 20 #include "base/time.h" |
21 #include "base/utf_string_conversions.h" | 21 #include "base/utf_string_conversions.h" |
22 #include "base/win/scoped_handle.h" | 22 #include "base/win/scoped_handle.h" |
23 #include "chrome/browser/storage_monitor/media_storage_util.h" | 23 #include "chrome/browser/storage_monitor/media_storage_util.h" |
24 #include "chrome/browser/storage_monitor/storage_info.h" | 24 #include "chrome/browser/storage_monitor/storage_info.h" |
25 #include "content/public/browser/browser_thread.h" | 25 #include "content/public/browser/browser_thread.h" |
26 #include "content/public/browser/user_metrics.h" | 26 #include "content/public/browser/user_metrics.h" |
27 | 27 |
28 using content::BrowserThread; | 28 using content::BrowserThread; |
29 | 29 |
| 30 namespace chrome { |
| 31 |
30 namespace { | 32 namespace { |
31 | 33 |
32 const DWORD kMaxPathBufLen = MAX_PATH + 1; | 34 const DWORD kMaxPathBufLen = MAX_PATH + 1; |
33 | 35 |
34 enum DeviceType { | 36 enum DeviceType { |
35 FLOPPY, | 37 FLOPPY, |
36 REMOVABLE, | 38 REMOVABLE, |
37 FIXED, | 39 FIXED, |
38 }; | 40 }; |
39 | 41 |
40 // Histogram values for recording frequencies of eject attempts and | 42 // Histogram values for recording frequencies of eject attempts and |
41 // outcomes. | 43 // outcomes. |
42 enum EjectWinLockOutcomes { | 44 enum EjectWinLockOutcomes { |
43 LOCK_ATTEMPT, | 45 LOCK_ATTEMPT, |
44 LOCK_TIMEOUT, | 46 LOCK_TIMEOUT, |
45 LOCK_TIMEOUT2, | 47 LOCK_TIMEOUT2, |
46 NUM_LOCK_OUTCOMES, | 48 NUM_LOCK_OUTCOMES, |
47 }; | 49 }; |
48 | 50 |
49 // We are trying to figure out whether the drive is a fixed volume, | 51 // We are trying to figure out whether the drive is a fixed volume, |
50 // a removable storage, or a floppy. A "floppy" here means "a volume we | 52 // a removable storage, or a floppy. A "floppy" here means "a volume we |
51 // want to basically ignore because it won't fit media and will spin | 53 // want to basically ignore because it won't fit media and will spin |
52 // if we touch it to get volume metadata." GetDriveType returns DRIVE_REMOVABLE | 54 // if we touch it to get volume metadata." GetDriveType returns DRIVE_REMOVABLE |
53 // on either floppy or removable volumes. The DRIVE_CDROM type is handled | 55 // on either floppy or removable volumes. The DRIVE_CDROM type is handled |
54 // as a floppy, as are DRIVE_UNKNOWN and DRIVE_NO_ROOT_DIR, as there are | 56 // as a floppy, as are DRIVE_UNKNOWN and DRIVE_NO_ROOT_DIR, as there are |
55 // reports that some floppy drives don't report as DRIVE_REMOVABLE. | 57 // reports that some floppy drives don't report as DRIVE_REMOVABLE. |
56 DeviceType GetDeviceType(const string16& mount_point) { | 58 DeviceType GetDeviceType(const base::string16& mount_point) { |
57 UINT drive_type = GetDriveType(mount_point.c_str()); | 59 UINT drive_type = GetDriveType(mount_point.c_str()); |
58 if (drive_type == DRIVE_FIXED || drive_type == DRIVE_REMOTE || | 60 if (drive_type == DRIVE_FIXED || drive_type == DRIVE_REMOTE || |
59 drive_type == DRIVE_RAMDISK) { | 61 drive_type == DRIVE_RAMDISK) { |
60 return FIXED; | 62 return FIXED; |
61 } | 63 } |
62 if (drive_type != DRIVE_REMOVABLE) | 64 if (drive_type != DRIVE_REMOVABLE) |
63 return FLOPPY; | 65 return FLOPPY; |
64 | 66 |
65 // Check device strings of the form "X:" and "\\.\X:" | 67 // Check device strings of the form "X:" and "\\.\X:" |
66 // For floppy drives, these will return strings like "/Device/Floppy0" | 68 // For floppy drives, these will return strings like "/Device/Floppy0" |
67 string16 device = mount_point; | 69 base::string16 device = mount_point; |
68 if (EndsWith(mount_point, L"\\", false)) | 70 if (EndsWith(mount_point, L"\\", false)) |
69 device = mount_point.substr(0, mount_point.length() - 1); | 71 device = mount_point.substr(0, mount_point.length() - 1); |
70 string16 device_path; | 72 base::string16 device_path; |
71 string16 device_path_slash; | 73 base::string16 device_path_slash; |
72 DWORD dos_device = QueryDosDevice( | 74 DWORD dos_device = QueryDosDevice( |
73 device.c_str(), WriteInto(&device_path, kMaxPathBufLen), kMaxPathBufLen); | 75 device.c_str(), WriteInto(&device_path, kMaxPathBufLen), kMaxPathBufLen); |
74 string16 device_slash = string16(L"\\\\.\\"); | 76 base::string16 device_slash = base::string16(L"\\\\.\\"); |
75 device_slash += device; | 77 device_slash += device; |
76 DWORD dos_device_slash = QueryDosDevice( | 78 DWORD dos_device_slash = QueryDosDevice( |
77 device_slash.c_str(), WriteInto(&device_path_slash, kMaxPathBufLen), | 79 device_slash.c_str(), WriteInto(&device_path_slash, kMaxPathBufLen), |
78 kMaxPathBufLen); | 80 kMaxPathBufLen); |
79 if (dos_device == 0 && dos_device_slash == 0) | 81 if (dos_device == 0 && dos_device_slash == 0) |
80 return FLOPPY; | 82 return FLOPPY; |
81 if (device_path.find(L"Floppy") != string16::npos || | 83 if (device_path.find(L"Floppy") != base::string16::npos || |
82 device_path_slash.find(L"Floppy") != string16::npos) { | 84 device_path_slash.find(L"Floppy") != base::string16::npos) { |
83 return FLOPPY; | 85 return FLOPPY; |
84 } | 86 } |
85 | 87 |
86 return REMOVABLE; | 88 return REMOVABLE; |
87 } | 89 } |
88 | 90 |
89 // Returns 0 if the devicetype is not volume. | 91 // Returns 0 if the devicetype is not volume. |
90 uint32 GetVolumeBitMaskFromBroadcastHeader(LPARAM data) { | 92 uint32 GetVolumeBitMaskFromBroadcastHeader(LPARAM data) { |
91 DEV_BROADCAST_VOLUME* dev_broadcast_volume = | 93 DEV_BROADCAST_VOLUME* dev_broadcast_volume = |
92 reinterpret_cast<DEV_BROADCAST_VOLUME*>(data); | 94 reinterpret_cast<DEV_BROADCAST_VOLUME*>(data); |
93 if (dev_broadcast_volume->dbcv_devicetype == DBT_DEVTYP_VOLUME) | 95 if (dev_broadcast_volume->dbcv_devicetype == DBT_DEVTYP_VOLUME) |
94 return dev_broadcast_volume->dbcv_unitmask; | 96 return dev_broadcast_volume->dbcv_unitmask; |
95 return 0; | 97 return 0; |
96 } | 98 } |
97 | 99 |
98 // Returns true if |data| represents a logical volume structure. | 100 // Returns true if |data| represents a logical volume structure. |
99 bool IsLogicalVolumeStructure(LPARAM data) { | 101 bool IsLogicalVolumeStructure(LPARAM data) { |
100 DEV_BROADCAST_HDR* broadcast_hdr = | 102 DEV_BROADCAST_HDR* broadcast_hdr = |
101 reinterpret_cast<DEV_BROADCAST_HDR*>(data); | 103 reinterpret_cast<DEV_BROADCAST_HDR*>(data); |
102 return broadcast_hdr != NULL && | 104 return broadcast_hdr != NULL && |
103 broadcast_hdr->dbch_devicetype == DBT_DEVTYP_VOLUME; | 105 broadcast_hdr->dbch_devicetype == DBT_DEVTYP_VOLUME; |
104 } | 106 } |
105 | 107 |
106 // Gets the total volume of the |mount_point| in bytes. | 108 // Gets the total volume of the |mount_point| in bytes. |
107 uint64 GetVolumeSize(const string16& mount_point) { | 109 uint64 GetVolumeSize(const base::string16& mount_point) { |
108 ULARGE_INTEGER total; | 110 ULARGE_INTEGER total; |
109 if (!GetDiskFreeSpaceExW(mount_point.c_str(), NULL, &total, NULL)) | 111 if (!GetDiskFreeSpaceExW(mount_point.c_str(), NULL, &total, NULL)) |
110 return 0; | 112 return 0; |
111 return total.QuadPart; | 113 return total.QuadPart; |
112 } | 114 } |
113 | 115 |
114 // Gets mass storage device information given a |device_path|. On success, | 116 // Gets mass storage device information given a |device_path|. On success, |
115 // returns true and fills in |info|. | 117 // returns true and fills in |info|. |
116 // The following msdn blog entry is helpful for understanding disk volumes | 118 // The following msdn blog entry is helpful for understanding disk volumes |
117 // and how they are treated in Windows: | 119 // and how they are treated in Windows: |
118 // http://blogs.msdn.com/b/adioltean/archive/2005/04/16/408947.aspx. | 120 // http://blogs.msdn.com/b/adioltean/archive/2005/04/16/408947.aspx. |
119 bool GetDeviceDetails(const base::FilePath& device_path, | 121 bool GetDeviceDetails(const base::FilePath& device_path, StorageInfo* info) { |
120 chrome::StorageInfo* info) { | |
121 DCHECK(info); | 122 DCHECK(info); |
122 | 123 |
123 string16 mount_point; | 124 base::string16 mount_point; |
124 if (!GetVolumePathName(device_path.value().c_str(), | 125 if (!GetVolumePathName(device_path.value().c_str(), |
125 WriteInto(&mount_point, kMaxPathBufLen), | 126 WriteInto(&mount_point, kMaxPathBufLen), |
126 kMaxPathBufLen)) { | 127 kMaxPathBufLen)) { |
127 return false; | 128 return false; |
128 } | 129 } |
129 mount_point.resize(wcslen(mount_point.c_str())); | 130 mount_point.resize(wcslen(mount_point.c_str())); |
130 | 131 |
131 // Note: experimentally this code does not spin a floppy drive. It | 132 // Note: experimentally this code does not spin a floppy drive. It |
132 // returns a GUID associated with the device, not the volume. | 133 // returns a GUID associated with the device, not the volume. |
133 string16 guid; | 134 base::string16 guid; |
134 if (!GetVolumeNameForVolumeMountPoint(mount_point.c_str(), | 135 if (!GetVolumeNameForVolumeMountPoint(mount_point.c_str(), |
135 WriteInto(&guid, kMaxPathBufLen), | 136 WriteInto(&guid, kMaxPathBufLen), |
136 kMaxPathBufLen)) { | 137 kMaxPathBufLen)) { |
137 return false; | 138 return false; |
138 } | 139 } |
139 // In case it has two GUID's (see above mentioned blog), do it again. | 140 // In case it has two GUID's (see above mentioned blog), do it again. |
140 if (!GetVolumeNameForVolumeMountPoint(guid.c_str(), | 141 if (!GetVolumeNameForVolumeMountPoint(guid.c_str(), |
141 WriteInto(&guid, kMaxPathBufLen), | 142 WriteInto(&guid, kMaxPathBufLen), |
142 kMaxPathBufLen)) { | 143 kMaxPathBufLen)) { |
143 return false; | 144 return false; |
144 } | 145 } |
145 | 146 |
146 // If we're adding a floppy drive, return without querying any more | 147 // If we're adding a floppy drive, return without querying any more |
147 // drive metadata -- it will cause the floppy drive to seek. | 148 // drive metadata -- it will cause the floppy drive to seek. |
148 // Note: treats FLOPPY as FIXED_MASS_STORAGE. This is intentional. | 149 // Note: treats FLOPPY as FIXED_MASS_STORAGE. This is intentional. |
149 DeviceType device_type = GetDeviceType(mount_point); | 150 DeviceType device_type = GetDeviceType(mount_point); |
150 if (device_type == FLOPPY) { | 151 if (device_type == FLOPPY) { |
151 info->set_device_id(chrome::StorageInfo::MakeDeviceId( | 152 info->set_device_id(StorageInfo::MakeDeviceId( |
152 chrome::StorageInfo::FIXED_MASS_STORAGE, UTF16ToUTF8(guid))); | 153 StorageInfo::FIXED_MASS_STORAGE, UTF16ToUTF8(guid))); |
153 return true; | 154 return true; |
154 } | 155 } |
155 | 156 |
156 chrome::StorageInfo::Type type = chrome::StorageInfo::FIXED_MASS_STORAGE; | 157 StorageInfo::Type type = StorageInfo::FIXED_MASS_STORAGE; |
157 if (device_type == REMOVABLE) { | 158 if (device_type == REMOVABLE) { |
158 type = chrome::StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM; | 159 type = StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM; |
159 if (chrome::MediaStorageUtil::HasDcim(base::FilePath(mount_point))) | 160 if (MediaStorageUtil::HasDcim(base::FilePath(mount_point))) |
160 type = chrome::StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM; | 161 type = StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM; |
161 } | 162 } |
162 | 163 |
163 // NOTE: experimentally, this function returns false if there is no volume | 164 // NOTE: experimentally, this function returns false if there is no volume |
164 // name set. | 165 // name set. |
165 string16 volume_label; | 166 base::string16 volume_label; |
166 GetVolumeInformationW(device_path.value().c_str(), | 167 GetVolumeInformationW(device_path.value().c_str(), |
167 WriteInto(&volume_label, kMaxPathBufLen), | 168 WriteInto(&volume_label, kMaxPathBufLen), |
168 kMaxPathBufLen, NULL, NULL, NULL, NULL, 0); | 169 kMaxPathBufLen, NULL, NULL, NULL, NULL, 0); |
169 | 170 |
170 uint64 total_size_in_bytes = GetVolumeSize(mount_point); | 171 uint64 total_size_in_bytes = GetVolumeSize(mount_point); |
171 std::string device_id = | 172 std::string device_id = StorageInfo::MakeDeviceId(type, UTF16ToUTF8(guid)); |
172 chrome::StorageInfo::MakeDeviceId(type, UTF16ToUTF8(guid)); | |
173 | 173 |
174 // TODO(gbillock): if volume_label.empty(), get the vendor/model information | 174 // TODO(gbillock): if volume_label.empty(), get the vendor/model information |
175 // for the volume. | 175 // for the volume. |
176 *info = chrome::StorageInfo(device_id, string16(), mount_point, | 176 *info = StorageInfo(device_id, base::string16(), mount_point, |
177 volume_label, string16(), string16(), | 177 volume_label, base::string16(), base::string16(), |
178 total_size_in_bytes); | 178 total_size_in_bytes); |
179 return true; | 179 return true; |
180 } | 180 } |
181 | 181 |
182 // Returns a vector of all the removable mass storage devices that are | 182 // Returns a vector of all the removable mass storage devices that are |
183 // connected. | 183 // connected. |
184 std::vector<base::FilePath> GetAttachedDevices() { | 184 std::vector<base::FilePath> GetAttachedDevices() { |
185 std::vector<base::FilePath> result; | 185 std::vector<base::FilePath> result; |
186 string16 volume_name; | 186 base::string16 volume_name; |
187 HANDLE find_handle = FindFirstVolume(WriteInto(&volume_name, kMaxPathBufLen), | 187 HANDLE find_handle = FindFirstVolume(WriteInto(&volume_name, kMaxPathBufLen), |
188 kMaxPathBufLen); | 188 kMaxPathBufLen); |
189 if (find_handle == INVALID_HANDLE_VALUE) | 189 if (find_handle == INVALID_HANDLE_VALUE) |
190 return result; | 190 return result; |
191 | 191 |
192 while (true) { | 192 while (true) { |
193 string16 volume_path; | 193 base::string16 volume_path; |
194 DWORD return_count; | 194 DWORD return_count; |
195 if (GetVolumePathNamesForVolumeName(volume_name.c_str(), | 195 if (GetVolumePathNamesForVolumeName(volume_name.c_str(), |
196 WriteInto(&volume_path, kMaxPathBufLen), | 196 WriteInto(&volume_path, kMaxPathBufLen), |
197 kMaxPathBufLen, &return_count)) { | 197 kMaxPathBufLen, &return_count)) { |
198 result.push_back(base::FilePath(volume_path)); | 198 result.push_back(base::FilePath(volume_path)); |
199 } | 199 } |
200 if (!FindNextVolume(find_handle, WriteInto(&volume_name, kMaxPathBufLen), | 200 if (!FindNextVolume(find_handle, WriteInto(&volume_name, kMaxPathBufLen), |
201 kMaxPathBufLen)) { | 201 kMaxPathBufLen)) { |
202 if (GetLastError() != ERROR_NO_MORE_FILES) | 202 if (GetLastError() != ERROR_NO_MORE_FILES) |
203 DPLOG(ERROR); | 203 DPLOG(ERROR); |
204 break; | 204 break; |
205 } | 205 } |
206 } | 206 } |
207 | 207 |
208 FindVolumeClose(find_handle); | 208 FindVolumeClose(find_handle); |
209 return result; | 209 return result; |
210 } | 210 } |
211 | 211 |
212 // Eject a removable volume at the specified |device| path. This works by | 212 // Eject a removable volume at the specified |device| path. This works by |
213 // 1) locking the volume, | 213 // 1) locking the volume, |
214 // 2) unmounting the volume, | 214 // 2) unmounting the volume, |
215 // 3) ejecting the volume. | 215 // 3) ejecting the volume. |
216 // If the lock fails, it will re-schedule itself. | 216 // If the lock fails, it will re-schedule itself. |
217 // See http://support.microsoft.com/kb/165721 | 217 // See http://support.microsoft.com/kb/165721 |
218 void EjectDeviceInThreadPool( | 218 void EjectDeviceInThreadPool( |
219 const base::FilePath& device, | 219 const base::FilePath& device, |
220 base::Callback<void(chrome::StorageMonitor::EjectStatus)> callback, | 220 base::Callback<void(StorageMonitor::EjectStatus)> callback, |
221 scoped_refptr<base::SequencedTaskRunner> task_runner, | 221 scoped_refptr<base::SequencedTaskRunner> task_runner, |
222 int iteration) { | 222 int iteration) { |
223 base::FilePath::StringType volume_name; | 223 base::FilePath::StringType volume_name; |
224 base::FilePath::CharType drive_letter = device.value()[0]; | 224 base::FilePath::CharType drive_letter = device.value()[0]; |
225 // Don't try to eject if the path isn't a simple one -- we're not | 225 // Don't try to eject if the path isn't a simple one -- we're not |
226 // sure how to do that yet. Need to figure out how to eject volumes mounted | 226 // sure how to do that yet. Need to figure out how to eject volumes mounted |
227 // at not-just-drive-letter paths. | 227 // at not-just-drive-letter paths. |
228 if (drive_letter < L'A' || drive_letter > L'Z' || | 228 if (drive_letter < L'A' || drive_letter > L'Z' || |
229 device != device.DirName()) { | 229 device != device.DirName()) { |
230 content::BrowserThread::PostTask( | 230 BrowserThread::PostTask( |
231 content::BrowserThread::UI, FROM_HERE, | 231 BrowserThread::UI, FROM_HERE, |
232 base::Bind(callback, chrome::StorageMonitor::EJECT_FAILURE)); | 232 base::Bind(callback, StorageMonitor::EJECT_FAILURE)); |
233 return; | 233 return; |
234 } | 234 } |
235 base::SStringPrintf(&volume_name, L"\\\\.\\%lc:", drive_letter); | 235 base::SStringPrintf(&volume_name, L"\\\\.\\%lc:", drive_letter); |
236 | 236 |
237 base::win::ScopedHandle volume_handle(CreateFile( | 237 base::win::ScopedHandle volume_handle(CreateFile( |
238 volume_name.c_str(), | 238 volume_name.c_str(), |
239 GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, | 239 GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, |
240 NULL, OPEN_EXISTING, 0, NULL)); | 240 NULL, OPEN_EXISTING, 0, NULL)); |
241 | 241 |
242 if (!volume_handle.IsValid()) { | 242 if (!volume_handle.IsValid()) { |
243 content::BrowserThread::PostTask( | 243 BrowserThread::PostTask( |
244 content::BrowserThread::UI, FROM_HERE, | 244 BrowserThread::UI, FROM_HERE, |
245 base::Bind(callback, chrome::StorageMonitor::EJECT_FAILURE)); | 245 base::Bind(callback, StorageMonitor::EJECT_FAILURE)); |
246 return; | 246 return; |
247 } | 247 } |
248 | 248 |
249 DWORD bytes_returned = 0; // Unused, but necessary for ioctl's. | 249 DWORD bytes_returned = 0; // Unused, but necessary for ioctl's. |
250 | 250 |
251 // Lock the drive to be ejected (so that other processes can't open | 251 // Lock the drive to be ejected (so that other processes can't open |
252 // files on it). If this fails, it means some other process has files | 252 // files on it). If this fails, it means some other process has files |
253 // open on the device. Note that the lock is released when the volume | 253 // open on the device. Note that the lock is released when the volume |
254 // handle is closed, and this is done by the ScopedHandle above. | 254 // handle is closed, and this is done by the ScopedHandle above. |
255 BOOL locked = DeviceIoControl(volume_handle, FSCTL_LOCK_VOLUME, | 255 BOOL locked = DeviceIoControl(volume_handle, FSCTL_LOCK_VOLUME, |
256 NULL, 0, NULL, 0, &bytes_returned, NULL); | 256 NULL, 0, NULL, 0, &bytes_returned, NULL); |
257 UMA_HISTOGRAM_ENUMERATION("StorageMonitor.EjectWinLock", | 257 UMA_HISTOGRAM_ENUMERATION("StorageMonitor.EjectWinLock", |
258 LOCK_ATTEMPT, NUM_LOCK_OUTCOMES); | 258 LOCK_ATTEMPT, NUM_LOCK_OUTCOMES); |
259 if (!locked) { | 259 if (!locked) { |
260 UMA_HISTOGRAM_ENUMERATION("StorageMonitor.EjectWinLock", | 260 UMA_HISTOGRAM_ENUMERATION("StorageMonitor.EjectWinLock", |
261 iteration == 0 ? LOCK_TIMEOUT : LOCK_TIMEOUT2, | 261 iteration == 0 ? LOCK_TIMEOUT : LOCK_TIMEOUT2, |
262 NUM_LOCK_OUTCOMES); | 262 NUM_LOCK_OUTCOMES); |
263 const int kNumLockRetries = 1; | 263 const int kNumLockRetries = 1; |
264 const base::TimeDelta kLockRetryInterval = | 264 const base::TimeDelta kLockRetryInterval = |
265 base::TimeDelta::FromMilliseconds(500); | 265 base::TimeDelta::FromMilliseconds(500); |
266 if (iteration < kNumLockRetries) { | 266 if (iteration < kNumLockRetries) { |
267 // Try again -- the lock may have been a transient one. This happens on | 267 // Try again -- the lock may have been a transient one. This happens on |
268 // things like AV disk lock for some reason, or another process | 268 // things like AV disk lock for some reason, or another process |
269 // transient disk lock. | 269 // transient disk lock. |
270 task_runner->PostDelayedTask(FROM_HERE, | 270 task_runner->PostDelayedTask( |
| 271 FROM_HERE, |
271 base::Bind(&EjectDeviceInThreadPool, | 272 base::Bind(&EjectDeviceInThreadPool, |
272 device, callback, task_runner, iteration + 1), | 273 device, callback, task_runner, iteration + 1), |
273 kLockRetryInterval); | 274 kLockRetryInterval); |
274 return; | 275 return; |
275 } | 276 } |
276 | 277 |
277 content::BrowserThread::PostTask( | 278 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
278 content::BrowserThread::UI, FROM_HERE, | 279 base::Bind(callback, StorageMonitor::EJECT_IN_USE)); |
279 base::Bind(callback, chrome::StorageMonitor::EJECT_IN_USE)); | |
280 return; | 280 return; |
281 } | 281 } |
282 | 282 |
283 // Unmount the device from the filesystem -- this will remove it from | 283 // Unmount the device from the filesystem -- this will remove it from |
284 // the file picker, drive enumerations, etc. | 284 // the file picker, drive enumerations, etc. |
285 BOOL dismounted = DeviceIoControl(volume_handle, FSCTL_DISMOUNT_VOLUME, | 285 BOOL dismounted = DeviceIoControl(volume_handle, FSCTL_DISMOUNT_VOLUME, |
286 NULL, 0, NULL, 0, &bytes_returned, NULL); | 286 NULL, 0, NULL, 0, &bytes_returned, NULL); |
287 | 287 |
288 // Reached if we acquired a lock, but could not dismount. This might | 288 // Reached if we acquired a lock, but could not dismount. This might |
289 // occur if another process unmounted without locking. Call this OK, | 289 // occur if another process unmounted without locking. Call this OK, |
290 // since the volume is now unreachable. | 290 // since the volume is now unreachable. |
291 if (!dismounted) { | 291 if (!dismounted) { |
292 DeviceIoControl(volume_handle, FSCTL_UNLOCK_VOLUME, | 292 DeviceIoControl(volume_handle, FSCTL_UNLOCK_VOLUME, |
293 NULL, 0, NULL, 0, &bytes_returned, NULL); | 293 NULL, 0, NULL, 0, &bytes_returned, NULL); |
294 content::BrowserThread::PostTask( | 294 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
295 content::BrowserThread::UI, FROM_HERE, | 295 base::Bind(callback, StorageMonitor::EJECT_OK)); |
296 base::Bind(callback, chrome::StorageMonitor::EJECT_OK)); | |
297 return; | 296 return; |
298 } | 297 } |
299 | 298 |
300 PREVENT_MEDIA_REMOVAL pmr_buffer; | 299 PREVENT_MEDIA_REMOVAL pmr_buffer; |
301 pmr_buffer.PreventMediaRemoval = FALSE; | 300 pmr_buffer.PreventMediaRemoval = FALSE; |
302 // Mark the device as safe to remove. | 301 // Mark the device as safe to remove. |
303 if (!DeviceIoControl(volume_handle, IOCTL_STORAGE_MEDIA_REMOVAL, | 302 if (!DeviceIoControl(volume_handle, IOCTL_STORAGE_MEDIA_REMOVAL, |
304 &pmr_buffer, sizeof(PREVENT_MEDIA_REMOVAL), | 303 &pmr_buffer, sizeof(PREVENT_MEDIA_REMOVAL), |
305 NULL, 0, &bytes_returned, NULL)) { | 304 NULL, 0, &bytes_returned, NULL)) { |
306 content::BrowserThread::PostTask( | 305 BrowserThread::PostTask( |
307 content::BrowserThread::UI, FROM_HERE, | 306 BrowserThread::UI, FROM_HERE, |
308 base::Bind(callback, chrome::StorageMonitor::EJECT_FAILURE)); | 307 base::Bind(callback, StorageMonitor::EJECT_FAILURE)); |
309 return; | 308 return; |
310 } | 309 } |
311 | 310 |
312 // Physically eject or soft-eject the device. | 311 // Physically eject or soft-eject the device. |
313 if (!DeviceIoControl(volume_handle, IOCTL_STORAGE_EJECT_MEDIA, | 312 if (!DeviceIoControl(volume_handle, IOCTL_STORAGE_EJECT_MEDIA, |
314 NULL, 0, NULL, 0, &bytes_returned, NULL)) { | 313 NULL, 0, NULL, 0, &bytes_returned, NULL)) { |
315 content::BrowserThread::PostTask( | 314 BrowserThread::PostTask( |
316 content::BrowserThread::UI, FROM_HERE, | 315 BrowserThread::UI, FROM_HERE, |
317 base::Bind(callback, chrome::StorageMonitor::EJECT_FAILURE)); | 316 base::Bind(callback, StorageMonitor::EJECT_FAILURE)); |
318 return; | 317 return; |
319 } | 318 } |
320 | 319 |
321 content::BrowserThread::PostTask( | 320 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
322 content::BrowserThread::UI, FROM_HERE, | 321 base::Bind(callback, StorageMonitor::EJECT_OK)); |
323 base::Bind(callback, chrome::StorageMonitor::EJECT_OK)); | |
324 } | 322 } |
325 | 323 |
326 } // namespace | 324 } // namespace |
327 | 325 |
328 namespace chrome { | |
329 | |
330 const int kWorkerPoolNumThreads = 3; | 326 const int kWorkerPoolNumThreads = 3; |
331 const char* kWorkerPoolNamePrefix = "DeviceInfoPool"; | 327 const char* kWorkerPoolNamePrefix = "DeviceInfoPool"; |
332 | 328 |
333 VolumeMountWatcherWin::VolumeMountWatcherWin() | 329 VolumeMountWatcherWin::VolumeMountWatcherWin() |
334 : device_info_worker_pool_(new base::SequencedWorkerPool( | 330 : device_info_worker_pool_(new base::SequencedWorkerPool( |
335 kWorkerPoolNumThreads, kWorkerPoolNamePrefix)), | 331 kWorkerPoolNumThreads, kWorkerPoolNamePrefix)), |
336 weak_factory_(this), | 332 weak_factory_(this), |
337 notifications_(NULL) { | 333 notifications_(NULL) { |
338 task_runner_ = | 334 task_runner_ = |
339 device_info_worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior( | 335 device_info_worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior( |
340 device_info_worker_pool_->GetSequenceToken(), | 336 device_info_worker_pool_->GetSequenceToken(), |
341 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | 337 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
342 } | 338 } |
343 | 339 |
344 // static | 340 // static |
345 base::FilePath VolumeMountWatcherWin::DriveNumberToFilePath(int drive_number) { | 341 base::FilePath VolumeMountWatcherWin::DriveNumberToFilePath(int drive_number) { |
346 if (drive_number < 0 || drive_number > 25) | 342 if (drive_number < 0 || drive_number > 25) |
347 return base::FilePath(); | 343 return base::FilePath(); |
348 string16 path(L"_:\\"); | 344 base::string16 path(L"_:\\"); |
349 path[0] = L'A' + drive_number; | 345 path[0] = L'A' + drive_number; |
350 return base::FilePath(path); | 346 return base::FilePath(path); |
351 } | 347 } |
352 | 348 |
353 // In order to get all the weak pointers created on the UI thread, and doing | 349 // In order to get all the weak pointers created on the UI thread, and doing |
354 // synchronous Windows calls in the worker pool, this kicks off a chain of | 350 // synchronous Windows calls in the worker pool, this kicks off a chain of |
355 // events which will | 351 // events which will |
356 // a) Enumerate attached devices | 352 // a) Enumerate attached devices |
357 // b) Create weak pointers for which to send completion signals from | 353 // b) Create weak pointers for which to send completion signals from |
358 // c) Retrieve metadata on the volumes and then | 354 // c) Retrieve metadata on the volumes and then |
(...skipping 11 matching lines...) Expand all Loading... |
370 } | 366 } |
371 | 367 |
372 void VolumeMountWatcherWin::AddDevicesOnUIThread( | 368 void VolumeMountWatcherWin::AddDevicesOnUIThread( |
373 std::vector<base::FilePath> removable_devices) { | 369 std::vector<base::FilePath> removable_devices) { |
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
375 | 371 |
376 for (size_t i = 0; i < removable_devices.size(); i++) { | 372 for (size_t i = 0; i < removable_devices.size(); i++) { |
377 if (ContainsKey(pending_device_checks_, removable_devices[i])) | 373 if (ContainsKey(pending_device_checks_, removable_devices[i])) |
378 continue; | 374 continue; |
379 pending_device_checks_.insert(removable_devices[i]); | 375 pending_device_checks_.insert(removable_devices[i]); |
380 task_runner_->PostTask(FROM_HERE, base::Bind( | 376 task_runner_->PostTask( |
381 &VolumeMountWatcherWin::RetrieveInfoForDeviceAndAdd, | 377 FROM_HERE, |
382 removable_devices[i], GetDeviceDetailsCallback(), | 378 base::Bind(&VolumeMountWatcherWin::RetrieveInfoForDeviceAndAdd, |
383 weak_factory_.GetWeakPtr())); | 379 removable_devices[i], GetDeviceDetailsCallback(), |
| 380 weak_factory_.GetWeakPtr())); |
384 } | 381 } |
385 } | 382 } |
386 | 383 |
387 // static | 384 // static |
388 void VolumeMountWatcherWin::RetrieveInfoForDeviceAndAdd( | 385 void VolumeMountWatcherWin::RetrieveInfoForDeviceAndAdd( |
389 const base::FilePath& device_path, | 386 const base::FilePath& device_path, |
390 const GetDeviceDetailsCallbackType& get_device_details_callback, | 387 const GetDeviceDetailsCallbackType& get_device_details_callback, |
391 base::WeakPtr<chrome::VolumeMountWatcherWin> volume_watcher) { | 388 base::WeakPtr<VolumeMountWatcherWin> volume_watcher) { |
392 StorageInfo info; | 389 StorageInfo info; |
393 if (!get_device_details_callback.Run(device_path, &info)) { | 390 if (!get_device_details_callback.Run(device_path, &info)) { |
394 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 391 BrowserThread::PostTask( |
395 &chrome::VolumeMountWatcherWin::DeviceCheckComplete, | 392 BrowserThread::UI, FROM_HERE, |
396 volume_watcher, device_path)); | 393 base::Bind(&VolumeMountWatcherWin::DeviceCheckComplete, |
| 394 volume_watcher, device_path)); |
397 return; | 395 return; |
398 } | 396 } |
399 | 397 |
400 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 398 BrowserThread::PostTask( |
401 &chrome::VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread, | 399 BrowserThread::UI, FROM_HERE, |
402 volume_watcher, device_path, info)); | 400 base::Bind(&VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread, |
| 401 volume_watcher, device_path, info)); |
403 } | 402 } |
404 | 403 |
405 void VolumeMountWatcherWin::DeviceCheckComplete( | 404 void VolumeMountWatcherWin::DeviceCheckComplete( |
406 const base::FilePath& device_path) { | 405 const base::FilePath& device_path) { |
407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
408 pending_device_checks_.erase(device_path); | 407 pending_device_checks_.erase(device_path); |
409 | 408 |
410 if (pending_device_checks_.size() == 0) { | 409 if (pending_device_checks_.size() == 0) { |
411 if (notifications_) | 410 if (notifications_) |
412 notifications_->MarkInitialized(); | 411 notifications_->MarkInitialized(); |
413 } | 412 } |
414 } | 413 } |
415 | 414 |
416 VolumeMountWatcherWin::GetAttachedDevicesCallbackType | 415 VolumeMountWatcherWin::GetAttachedDevicesCallbackType |
417 VolumeMountWatcherWin::GetAttachedDevicesCallback() const { | 416 VolumeMountWatcherWin::GetAttachedDevicesCallback() const { |
418 return base::Bind(&GetAttachedDevices); | 417 return base::Bind(&GetAttachedDevices); |
419 } | 418 } |
420 | 419 |
421 VolumeMountWatcherWin::GetDeviceDetailsCallbackType | 420 VolumeMountWatcherWin::GetDeviceDetailsCallbackType |
422 VolumeMountWatcherWin::GetDeviceDetailsCallback() const { | 421 VolumeMountWatcherWin::GetDeviceDetailsCallback() const { |
423 return base::Bind(&GetDeviceDetails); | 422 return base::Bind(&GetDeviceDetails); |
424 } | 423 } |
425 | 424 |
426 bool VolumeMountWatcherWin::GetDeviceInfo(const base::FilePath& device_path, | 425 bool VolumeMountWatcherWin::GetDeviceInfo(const base::FilePath& device_path, |
427 StorageInfo* info) const { | 426 StorageInfo* info) const { |
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 428 DCHECK(info); |
429 base::FilePath path(device_path); | 429 base::FilePath path(device_path); |
430 MountPointDeviceMetadataMap::const_iterator iter = | 430 MountPointDeviceMetadataMap::const_iterator iter = |
431 device_metadata_.find(path.value()); | 431 device_metadata_.find(path.value()); |
432 while (iter == device_metadata_.end() && path.DirName() != path) { | 432 while (iter == device_metadata_.end() && path.DirName() != path) { |
433 path = path.DirName(); | 433 path = path.DirName(); |
434 iter = device_metadata_.find(path.value()); | 434 iter = device_metadata_.find(path.value()); |
435 } | 435 } |
436 | 436 |
437 if (iter == device_metadata_.end()) | 437 if (iter == device_metadata_.end()) |
438 return false; | 438 return false; |
439 | 439 |
440 if (info) | 440 *info = iter->second; |
441 *info = iter->second; | |
442 | |
443 return true; | 441 return true; |
444 } | 442 } |
445 | 443 |
446 void VolumeMountWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { | 444 void VolumeMountWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { |
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 445 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
448 switch (event_type) { | 446 switch (event_type) { |
449 case DBT_DEVICEARRIVAL: { | 447 case DBT_DEVICEARRIVAL: { |
450 if (IsLogicalVolumeStructure(data)) { | 448 if (IsLogicalVolumeStructure(data)) { |
451 DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); | 449 DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); |
452 std::vector<base::FilePath> paths; | 450 std::vector<base::FilePath> paths; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
494 | 492 |
495 // Don't call removable storage observers for fixed volumes. | 493 // Don't call removable storage observers for fixed volumes. |
496 if (!StorageInfo::IsRemovableDevice(info.device_id())) | 494 if (!StorageInfo::IsRemovableDevice(info.device_id())) |
497 return; | 495 return; |
498 | 496 |
499 if (notifications_) | 497 if (notifications_) |
500 notifications_->ProcessAttach(info); | 498 notifications_->ProcessAttach(info); |
501 } | 499 } |
502 | 500 |
503 void VolumeMountWatcherWin::HandleDeviceDetachEventOnUIThread( | 501 void VolumeMountWatcherWin::HandleDeviceDetachEventOnUIThread( |
504 const string16& device_location) { | 502 const base::string16& device_location) { |
505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
506 | 504 |
507 MountPointDeviceMetadataMap::const_iterator device_info = | 505 MountPointDeviceMetadataMap::const_iterator device_info = |
508 device_metadata_.find(device_location); | 506 device_metadata_.find(device_location); |
509 // If the device isn't type removable (like a CD), it won't be there. | 507 // If the device isn't type removable (like a CD), it won't be there. |
510 if (device_info == device_metadata_.end()) | 508 if (device_info == device_metadata_.end()) |
511 return; | 509 return; |
512 | 510 |
513 if (notifications_) | 511 if (notifications_) |
514 notifications_->ProcessDetach(device_info->second.device_id()); | 512 notifications_->ProcessDetach(device_info->second.device_id()); |
515 device_metadata_.erase(device_info); | 513 device_metadata_.erase(device_info); |
516 } | 514 } |
517 | 515 |
518 void VolumeMountWatcherWin::EjectDevice( | 516 void VolumeMountWatcherWin::EjectDevice( |
519 const std::string& device_id, | 517 const std::string& device_id, |
520 base::Callback<void(StorageMonitor::EjectStatus)> callback) { | 518 base::Callback<void(StorageMonitor::EjectStatus)> callback) { |
521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
522 base::FilePath device = | 520 base::FilePath device = MediaStorageUtil::FindDevicePathById(device_id); |
523 chrome::MediaStorageUtil::FindDevicePathById(device_id); | |
524 if (device.empty()) { | 521 if (device.empty()) { |
525 callback.Run(StorageMonitor::EJECT_FAILURE); | 522 callback.Run(StorageMonitor::EJECT_FAILURE); |
526 return; | 523 return; |
527 } | 524 } |
528 if (device_metadata_.erase(device.value()) == 0) { | 525 if (device_metadata_.erase(device.value()) == 0) { |
529 callback.Run(StorageMonitor::EJECT_FAILURE); | 526 callback.Run(StorageMonitor::EJECT_FAILURE); |
530 return; | 527 return; |
531 } | 528 } |
532 | 529 |
533 task_runner_->PostTask(FROM_HERE, | 530 task_runner_->PostTask( |
| 531 FROM_HERE, |
534 base::Bind(&EjectDeviceInThreadPool, device, callback, task_runner_, 0)); | 532 base::Bind(&EjectDeviceInThreadPool, device, callback, task_runner_, 0)); |
535 } | 533 } |
536 | 534 |
537 } // namespace chrome | 535 } // namespace chrome |
OLD | NEW |