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
3 // found in the LICENSE file.
5 #include "chrome/browser/system_monitor/volume_mount_watcher_win.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_util.h"
13 #include "base/utf_string_conversions.h"
14 #include "chrome/browser/system_monitor/media_device_notifications_utils.h"
15 #include "chrome/browser/system_monitor/media_storage_util.h"
16 #include "content/public/browser/browser_thread.h"
18 using base::SystemMonitor
;
19 using content::BrowserThread
;
23 const DWORD kMaxPathBufLen
= MAX_PATH
+ 1;
25 bool IsRemovable(const char16
* mount_point
) {
26 if (GetDriveType(mount_point
) != DRIVE_REMOVABLE
)
29 // We don't consider floppy disks as removable, so check for that.
30 string16 device
= mount_point
;
31 if (EndsWith(device
, L
"\\", false))
32 device
= device
.substr(0, device
.length() - 1);
33 char16 device_path
[kMaxPathBufLen
];
34 if (!QueryDosDevice(device
.c_str(), device_path
, kMaxPathBufLen
))
36 return string16(device_path
).find(L
"Floppy") == string16::npos
;
39 // Returns 0 if the devicetype is not volume.
40 uint32
GetVolumeBitMaskFromBroadcastHeader(LPARAM data
) {
41 DEV_BROADCAST_VOLUME
* dev_broadcast_volume
=
42 reinterpret_cast<DEV_BROADCAST_VOLUME
*>(data
);
43 if (dev_broadcast_volume
->dbcv_devicetype
== DBT_DEVTYP_VOLUME
)
44 return dev_broadcast_volume
->dbcv_unitmask
;
48 FilePath
DriveNumberToFilePath(int drive_number
) {
49 DCHECK_LT(drive_number
, 26);
50 string16
path(L
"_:\\");
51 path
[0] = L
'A' + drive_number
;
52 return FilePath(path
);
55 // Returns true if |data| represents a logical volume structure.
56 bool IsLogicalVolumeStructure(LPARAM data
) {
57 DEV_BROADCAST_HDR
* broadcast_hdr
=
58 reinterpret_cast<DEV_BROADCAST_HDR
*>(data
);
59 return broadcast_hdr
!= NULL
&&
60 broadcast_hdr
->dbch_devicetype
== DBT_DEVTYP_VOLUME
;
63 // Gets mass storage device information given a |device_path|. On success,
64 // returns true and fills in |device_location|, |unique_id|, |name| and
66 // The following msdn blog entry is helpful for understanding disk volumes
67 // and how they are treated in Windows:
68 // http://blogs.msdn.com/b/adioltean/archive/2005/04/16/408947.aspx.
69 bool GetDeviceDetails(const FilePath
& device_path
, string16
* device_location
,
70 std::string
* unique_id
, string16
* name
, bool* removable
) {
71 char16 mount_point
[kMaxPathBufLen
];
72 if (!GetVolumePathName(device_path
.value().c_str(), mount_point
,
77 *device_location
= string16(mount_point
);
80 char16 guid
[kMaxPathBufLen
];
81 if (!GetVolumeNameForVolumeMountPoint(mount_point
, guid
, kMaxPathBufLen
))
83 // In case it has two GUID's (see above mentioned blog), do it again.
84 if (!GetVolumeNameForVolumeMountPoint(guid
, guid
, kMaxPathBufLen
))
86 WideToUTF8(guid
, wcslen(guid
), unique_id
);
90 *name
= device_path
.LossyDisplayName();
93 *removable
= IsRemovable(mount_point
);
102 VolumeMountWatcherWin::VolumeMountWatcherWin() {
105 void VolumeMountWatcherWin::Init() {
106 // When VolumeMountWatcherWin is created, the message pumps are not running
107 // so a posted task from the constructor would never run. Therefore, do all
108 // the initializations here.
110 // This should call AddExistingDevicesOnFileThread. The call is disabled
111 // until a fix for http://crbug.com/155910 can land.
112 // BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
113 // &VolumeMountWatcherWin::AddExistingDevicesOnFileThread, this));
116 bool VolumeMountWatcherWin::GetDeviceInfo(const FilePath
& device_path
,
117 string16
* device_location
, std::string
* unique_id
, string16
* name
,
119 return GetDeviceDetails(device_path
, device_location
, unique_id
, name
,
123 void VolumeMountWatcherWin::OnWindowMessage(UINT event_type
, LPARAM data
) {
124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
125 switch (event_type
) {
126 case DBT_DEVICEARRIVAL
: {
127 if (IsLogicalVolumeStructure(data
)) {
128 DWORD unitmask
= GetVolumeBitMaskFromBroadcastHeader(data
);
129 for (int i
= 0; unitmask
; ++i
, unitmask
>>= 1) {
130 if (!(unitmask
& 0x01))
132 AddNewDevice(DriveNumberToFilePath(i
));
137 case DBT_DEVICEREMOVECOMPLETE
: {
138 if (IsLogicalVolumeStructure(data
)) {
139 DWORD unitmask
= GetVolumeBitMaskFromBroadcastHeader(data
);
140 for (int i
= 0; unitmask
; ++i
, unitmask
>>= 1) {
141 if (!(unitmask
& 0x01))
143 HandleDeviceDetachEventOnUIThread(DriveNumberToFilePath(i
).value());
151 VolumeMountWatcherWin::~VolumeMountWatcherWin() {
154 std::vector
<FilePath
> VolumeMountWatcherWin::GetAttachedDevices() {
155 std::vector
<FilePath
> result
;
156 char16 volume_name
[kMaxPathBufLen
];
157 HANDLE find_handle
= FindFirstVolume(volume_name
, kMaxPathBufLen
);
158 if (find_handle
== INVALID_HANDLE_VALUE
)
162 char16 volume_path
[kMaxPathBufLen
];
164 if (GetVolumePathNamesForVolumeName(volume_name
, volume_path
,
165 kMaxPathBufLen
, &return_count
)) {
166 if (IsRemovable(volume_path
))
167 result
.push_back(FilePath(volume_path
));
171 if (!FindNextVolume(find_handle
, volume_name
, kMaxPathBufLen
)) {
172 if (GetLastError() != ERROR_NO_MORE_FILES
)
178 FindVolumeClose(find_handle
);
182 void VolumeMountWatcherWin::AddNewDevice(const FilePath
& device_path
) {
183 std::string unique_id
;
184 string16 device_name
;
186 if (!GetDeviceInfo(device_path
, NULL
, &unique_id
, &device_name
, &removable
))
192 BrowserThread::PostTask(
193 BrowserThread::FILE, FROM_HERE
,
194 base::Bind(&VolumeMountWatcherWin::CheckDeviceTypeOnFileThread
, this,
195 unique_id
, device_name
, device_path
));
198 void VolumeMountWatcherWin::AddExistingDevicesOnFileThread() {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
200 std::vector
<FilePath
> removable_devices
= GetAttachedDevices();
201 for (size_t i
= 0; i
< removable_devices
.size(); i
++)
202 AddNewDevice(removable_devices
[i
]);
205 void VolumeMountWatcherWin::CheckDeviceTypeOnFileThread(
206 const std::string
& unique_id
, const string16
& device_name
,
207 const FilePath
& device
) {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
210 MediaStorageUtil::Type type
=
211 MediaStorageUtil::REMOVABLE_MASS_STORAGE_NO_DCIM
;
212 if (IsMediaDevice(device
.value()))
213 type
= MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM
;
214 std::string device_id
= MediaStorageUtil::MakeDeviceId(type
, unique_id
);
216 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
217 &VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread
, this,
218 device_id
, device_name
, device
.value()));
221 void VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread(
222 const std::string
& device_id
, const string16
& device_name
,
223 const string16
& device_location
) {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
226 device_ids_
[device_location
] = device_id
;
227 SystemMonitor
* monitor
= SystemMonitor::Get();
229 monitor
->ProcessRemovableStorageAttached(device_id
, device_name
,
234 void VolumeMountWatcherWin::HandleDeviceDetachEventOnUIThread(
235 const string16
& device_location
) {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
237 MountPointDeviceIdMap::const_iterator device_info
=
238 device_ids_
.find(device_location
);
239 // If the devices isn't type removable (like a CD), it won't be there.
240 if (device_info
== device_ids_
.end())
243 SystemMonitor
* monitor
= SystemMonitor::Get();
245 monitor
->ProcessRemovableStorageDetached(device_info
->second
);
246 device_ids_
.erase(device_info
);
249 } // namespace chrome