15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/storage_monitor/volume_mount_watcher_win.h" 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <windows.h> 8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <dbt.h> 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <fileapi.h> 115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <shlobj.h> 12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include <winioctl.h> 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind_helpers.h" 15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/metrics/histogram.h" 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/stl_util.h" 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h" 20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/task_runner_util.h" 22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/win/scoped_handle.h" 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/storage_monitor/media_storage_util.h" 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/storage_monitor/storage_info.h" 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h" 27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "content/public/browser/user_metrics.h" 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread; 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace storage_monitor { 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace { 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const DWORD kMaxPathBufLen = MAX_PATH + 1; 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)enum DeviceType { 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FLOPPY, 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) REMOVABLE, 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FIXED, 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Histogram values for recording frequencies of eject attempts and 44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// outcomes. 45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)enum EjectWinLockOutcomes { 46b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) LOCK_ATTEMPT, 47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) LOCK_TIMEOUT, 48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) LOCK_TIMEOUT2, 49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NUM_LOCK_OUTCOMES, 50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}; 51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// We are trying to figure out whether the drive is a fixed volume, 53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// a removable storage, or a floppy. A "floppy" here means "a volume we 54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// want to basically ignore because it won't fit media and will spin 55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// if we touch it to get volume metadata." GetDriveType returns DRIVE_REMOVABLE 56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// on either floppy or removable volumes. The DRIVE_CDROM type is handled 57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// as a floppy, as are DRIVE_UNKNOWN and DRIVE_NO_ROOT_DIR, as there are 58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// reports that some floppy drives don't report as DRIVE_REMOVABLE. 59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)DeviceType GetDeviceType(const base::string16& mount_point) { 60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) UINT drive_type = GetDriveType(mount_point.c_str()); 61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (drive_type == DRIVE_FIXED || drive_type == DRIVE_REMOTE || 62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) drive_type == DRIVE_RAMDISK) { 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return FIXED; 64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (drive_type != DRIVE_REMOVABLE) 66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return FLOPPY; 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 6890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Check device strings of the form "X:" and "\\.\X:" 6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // For floppy drives, these will return strings like "/Device/Floppy0" 70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 device = mount_point; 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (EndsWith(mount_point, L"\\", false)) 7290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) device = mount_point.substr(0, mount_point.length() - 1); 73868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 device_path; 74868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 device_path_slash; 7590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) DWORD dos_device = QueryDosDevice( 7690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) device.c_str(), WriteInto(&device_path, kMaxPathBufLen), kMaxPathBufLen); 77868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 device_slash = base::string16(L"\\\\.\\"); 7890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) device_slash += device; 7990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) DWORD dos_device_slash = QueryDosDevice( 8090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) device_slash.c_str(), WriteInto(&device_path_slash, kMaxPathBufLen), 8190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) kMaxPathBufLen); 8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (dos_device == 0 && dos_device_slash == 0) 8390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return FLOPPY; 84868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (device_path.find(L"Floppy") != base::string16::npos || 85868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) device_path_slash.find(L"Floppy") != base::string16::npos) { 8690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return FLOPPY; 8790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 8890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 8990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return REMOVABLE; 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns 0 if the devicetype is not volume. 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)uint32 GetVolumeBitMaskFromBroadcastHeader(LPARAM data) { 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DEV_BROADCAST_VOLUME* dev_broadcast_volume = 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) reinterpret_cast<DEV_BROADCAST_VOLUME*>(data); 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (dev_broadcast_volume->dbcv_devicetype == DBT_DEVTYP_VOLUME) 972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return dev_broadcast_volume->dbcv_unitmask; 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 0; 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns true if |data| represents a logical volume structure. 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsLogicalVolumeStructure(LPARAM data) { 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DEV_BROADCAST_HDR* broadcast_hdr = 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) reinterpret_cast<DEV_BROADCAST_HDR*>(data); 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return broadcast_hdr != NULL && 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) broadcast_hdr->dbch_devicetype == DBT_DEVTYP_VOLUME; 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Gets the total volume of the |mount_point| in bytes. 110868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)uint64 GetVolumeSize(const base::string16& mount_point) { 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ULARGE_INTEGER total; 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!GetDiskFreeSpaceExW(mount_point.c_str(), NULL, &total, NULL)) 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 0; 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return total.QuadPart; 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Gets mass storage device information given a |device_path|. On success, 118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// returns true and fills in |info|. 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The following msdn blog entry is helpful for understanding disk volumes 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// and how they are treated in Windows: 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// http://blogs.msdn.com/b/adioltean/archive/2005/04/16/408947.aspx. 122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool GetDeviceDetails(const base::FilePath& device_path, StorageInfo* info) { 12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) DCHECK(info); 12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 mount_point; 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!GetVolumePathName(device_path.value().c_str(), 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) WriteInto(&mount_point, kMaxPathBufLen), 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) kMaxPathBufLen)) { 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 13190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) mount_point.resize(wcslen(mount_point.c_str())); 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Note: experimentally this code does not spin a floppy drive. It 134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // returns a GUID associated with the device, not the volume. 135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 guid; 13690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!GetVolumeNameForVolumeMountPoint(mount_point.c_str(), 13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) WriteInto(&guid, kMaxPathBufLen), 13890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) kMaxPathBufLen)) { 13990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return false; 14090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 14190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // In case it has two GUID's (see above mentioned blog), do it again. 14290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!GetVolumeNameForVolumeMountPoint(guid.c_str(), 14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) WriteInto(&guid, kMaxPathBufLen), 14490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) kMaxPathBufLen)) { 14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return false; 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If we're adding a floppy drive, return without querying any more 149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // drive metadata -- it will cause the floppy drive to seek. 150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Note: treats FLOPPY as FIXED_MASS_STORAGE. This is intentional. 151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DeviceType device_type = GetDeviceType(mount_point); 152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (device_type == FLOPPY) { 153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) info->set_device_id(StorageInfo::MakeDeviceId( 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) StorageInfo::FIXED_MASS_STORAGE, base::UTF16ToUTF8(guid))); 155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return true; 156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) StorageInfo::Type type = StorageInfo::FIXED_MASS_STORAGE; 159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (device_type == REMOVABLE) { 160868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) type = StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM; 161868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (MediaStorageUtil::HasDcim(base::FilePath(mount_point))) 162868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) type = StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM; 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // NOTE: experimentally, this function returns false if there is no volume 166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // name set. 167868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 volume_label; 168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) GetVolumeInformationW(device_path.value().c_str(), 169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) WriteInto(&volume_label, kMaxPathBufLen), 170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) kMaxPathBufLen, NULL, NULL, NULL, NULL, 0); 171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 17290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) uint64 total_size_in_bytes = GetVolumeSize(mount_point); 1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string device_id = 1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) StorageInfo::MakeDeviceId(type, base::UTF16ToUTF8(guid)); 175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // TODO(gbillock): if volume_label.empty(), get the vendor/model information 177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // for the volume. 178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *info = StorageInfo(device_id, mount_point, volume_label, base::string16(), 179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::string16(), total_size_in_bytes); 1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns a vector of all the removable mass storage devices that are 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// connected. 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<base::FilePath> GetAttachedDevices() { 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<base::FilePath> result; 187868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 volume_name; 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) HANDLE find_handle = FindFirstVolume(WriteInto(&volume_name, kMaxPathBufLen), 1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) kMaxPathBufLen); 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (find_handle == INVALID_HANDLE_VALUE) 1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return result; 1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while (true) { 194868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 volume_path; 1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DWORD return_count; 1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (GetVolumePathNamesForVolumeName(volume_name.c_str(), 1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) WriteInto(&volume_path, kMaxPathBufLen), 1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) kMaxPathBufLen, &return_count)) { 1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result.push_back(base::FilePath(volume_path)); 2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!FindNextVolume(find_handle, WriteInto(&volume_name, kMaxPathBufLen), 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) kMaxPathBufLen)) { 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (GetLastError() != ERROR_NO_MORE_FILES) 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DPLOG(ERROR); 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FindVolumeClose(find_handle); 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return result; 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 213b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Eject a removable volume at the specified |device| path. This works by 214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// 1) locking the volume, 215b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// 2) unmounting the volume, 216b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// 3) ejecting the volume. 217b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// If the lock fails, it will re-schedule itself. 218b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// See http://support.microsoft.com/kb/165721 219b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void EjectDeviceInThreadPool( 220b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const base::FilePath& device, 221868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Callback<void(StorageMonitor::EjectStatus)> callback, 222b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) scoped_refptr<base::SequencedTaskRunner> task_runner, 223b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) int iteration) { 224b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::FilePath::StringType volume_name; 225b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::FilePath::CharType drive_letter = device.value()[0]; 226b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Don't try to eject if the path isn't a simple one -- we're not 227b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // sure how to do that yet. Need to figure out how to eject volumes mounted 228b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // at not-just-drive-letter paths. 229b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (drive_letter < L'A' || drive_letter > L'Z' || 230b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) device != device.DirName()) { 231868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask( 232868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::UI, FROM_HERE, 233868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_FAILURE)); 234b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 235b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 236b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::SStringPrintf(&volume_name, L"\\\\.\\%lc:", drive_letter); 237b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 238b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::win::ScopedHandle volume_handle(CreateFile( 239b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) volume_name.c_str(), 240b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 241b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NULL, OPEN_EXISTING, 0, NULL)); 242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 243b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!volume_handle.IsValid()) { 244868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask( 245868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::UI, FROM_HERE, 246868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_FAILURE)); 247b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 248b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 250b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) DWORD bytes_returned = 0; // Unused, but necessary for ioctl's. 251b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 252b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Lock the drive to be ejected (so that other processes can't open 253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // files on it). If this fails, it means some other process has files 254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // open on the device. Note that the lock is released when the volume 255b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // handle is closed, and this is done by the ScopedHandle above. 256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) BOOL locked = DeviceIoControl(volume_handle, FSCTL_LOCK_VOLUME, 257b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NULL, 0, NULL, 0, &bytes_returned, NULL); 258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("StorageMonitor.EjectWinLock", 259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) LOCK_ATTEMPT, NUM_LOCK_OUTCOMES); 260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!locked) { 261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("StorageMonitor.EjectWinLock", 262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) iteration == 0 ? LOCK_TIMEOUT : LOCK_TIMEOUT2, 263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NUM_LOCK_OUTCOMES); 264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const int kNumLockRetries = 1; 265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const base::TimeDelta kLockRetryInterval = 266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::TimeDelta::FromMilliseconds(500); 267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (iteration < kNumLockRetries) { 268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Try again -- the lock may have been a transient one. This happens on 269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // things like AV disk lock for some reason, or another process 270b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // transient disk lock. 271868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) task_runner->PostDelayedTask( 272868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) FROM_HERE, 273b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::Bind(&EjectDeviceInThreadPool, 274b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) device, callback, task_runner, iteration + 1), 275b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) kLockRetryInterval); 276b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 277b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 278b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 279868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 280868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_IN_USE)); 281b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 282b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 283b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 284b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Unmount the device from the filesystem -- this will remove it from 285b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // the file picker, drive enumerations, etc. 286b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) BOOL dismounted = DeviceIoControl(volume_handle, FSCTL_DISMOUNT_VOLUME, 287b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NULL, 0, NULL, 0, &bytes_returned, NULL); 288b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 289b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Reached if we acquired a lock, but could not dismount. This might 290b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // occur if another process unmounted without locking. Call this OK, 291b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // since the volume is now unreachable. 292b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!dismounted) { 293b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) DeviceIoControl(volume_handle, FSCTL_UNLOCK_VOLUME, 294b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NULL, 0, NULL, 0, &bytes_returned, NULL); 295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 296868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_OK)); 297b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 298b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 299b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 300b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) PREVENT_MEDIA_REMOVAL pmr_buffer; 301b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) pmr_buffer.PreventMediaRemoval = FALSE; 302b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Mark the device as safe to remove. 303b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!DeviceIoControl(volume_handle, IOCTL_STORAGE_MEDIA_REMOVAL, 304b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) &pmr_buffer, sizeof(PREVENT_MEDIA_REMOVAL), 305b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NULL, 0, &bytes_returned, NULL)) { 306868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask( 307868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::UI, FROM_HERE, 308868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_FAILURE)); 309b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 310b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 311b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 312b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Physically eject or soft-eject the device. 313b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!DeviceIoControl(volume_handle, IOCTL_STORAGE_EJECT_MEDIA, 314b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) NULL, 0, NULL, 0, &bytes_returned, NULL)) { 315868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask( 316868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::UI, FROM_HERE, 317868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_FAILURE)); 318b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 319b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 320b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 321868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 322868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(callback, StorageMonitor::EJECT_OK)); 323b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)} 324b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace 3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kWorkerPoolNumThreads = 3; 3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char* kWorkerPoolNamePrefix = "DeviceInfoPool"; 3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)VolumeMountWatcherWin::VolumeMountWatcherWin() 3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) : device_info_worker_pool_(new base::SequencedWorkerPool( 3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) kWorkerPoolNumThreads, kWorkerPoolNamePrefix)), 3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci notifications_(NULL), 3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci weak_factory_(this) { 3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) task_runner_ = 3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) device_info_worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior( 3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) device_info_worker_pool_->GetSequenceToken(), 3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath VolumeMountWatcherWin::DriveNumberToFilePath(int drive_number) { 3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (drive_number < 0 || drive_number > 25) 3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(); 345868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::string16 path(L"_:\\"); 3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) path[0] = L'A' + drive_number; 3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(path); 3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// In order to get all the weak pointers created on the UI thread, and doing 3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// synchronous Windows calls in the worker pool, this kicks off a chain of 3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// events which will 3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// a) Enumerate attached devices 3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// b) Create weak pointers for which to send completion signals from 3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// c) Retrieve metadata on the volumes and then 3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// d) Notify that metadata to listeners. 3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::Init() { 3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // When VolumeMountWatcherWin is created, the message pumps are not running 3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // so a posted task from the constructor would never run. Therefore, do all 3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // the initializations here. 363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::PostTaskAndReplyWithResult(task_runner_, FROM_HERE, 364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) GetAttachedDevicesCallback(), 365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::Bind(&VolumeMountWatcherWin::AddDevicesOnUIThread, 366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) weak_factory_.GetWeakPtr())); 3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::AddDevicesOnUIThread( 3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<base::FilePath> removable_devices) { 3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (size_t i = 0; i < removable_devices.size(); i++) { 3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (ContainsKey(pending_device_checks_, removable_devices[i])) 3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pending_device_checks_.insert(removable_devices[i]); 377868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) task_runner_->PostTask( 378868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) FROM_HERE, 379868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(&VolumeMountWatcherWin::RetrieveInfoForDeviceAndAdd, 380868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) removable_devices[i], GetDeviceDetailsCallback(), 381868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) weak_factory_.GetWeakPtr())); 3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::RetrieveInfoForDeviceAndAdd( 3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& device_path, 3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const GetDeviceDetailsCallbackType& get_device_details_callback, 389868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::WeakPtr<VolumeMountWatcherWin> volume_watcher) { 390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) StorageInfo info; 391c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!get_device_details_callback.Run(device_path, &info)) { 392868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask( 393868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::UI, FROM_HERE, 394868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(&VolumeMountWatcherWin::DeviceCheckComplete, 395868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) volume_watcher, device_path)); 3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 399868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask( 400868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::UI, FROM_HERE, 401868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::Bind(&VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread, 402868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) volume_watcher, device_path, info)); 4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::DeviceCheckComplete( 4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& device_path) { 4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pending_device_checks_.erase(device_path); 409c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 410c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (pending_device_checks_.size() == 0) { 411c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (notifications_) 412c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) notifications_->MarkInitialized(); 413c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)VolumeMountWatcherWin::GetAttachedDevicesCallbackType 4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) VolumeMountWatcherWin::GetAttachedDevicesCallback() const { 4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::Bind(&GetAttachedDevices); 4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)VolumeMountWatcherWin::GetDeviceDetailsCallbackType 4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) VolumeMountWatcherWin::GetDeviceDetailsCallback() const { 4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::Bind(&GetDeviceDetails); 4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool VolumeMountWatcherWin::GetDeviceInfo(const base::FilePath& device_path, 427c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) StorageInfo* info) const { 4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 429868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(info); 4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath path(device_path); 4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MountPointDeviceMetadataMap::const_iterator iter = 432868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) device_metadata_.find(path); 4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while (iter == device_metadata_.end() && path.DirName() != path) { 4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) path = path.DirName(); 435868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) iter = device_metadata_.find(path); 4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (iter == device_metadata_.end()) 439c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return false; 4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 441868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) *info = iter->second; 4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { 4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) switch (event_type) { 4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case DBT_DEVICEARRIVAL: { 4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (IsLogicalVolumeStructure(data)) { 4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); 4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<base::FilePath> paths; 4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (int i = 0; unitmask; ++i, unitmask >>= 1) { 4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!(unitmask & 0x01)) 4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) paths.push_back(DriveNumberToFilePath(i)); 4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) AddDevicesOnUIThread(paths); 4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case DBT_DEVICEREMOVECOMPLETE: { 4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (IsLogicalVolumeStructure(data)) { 4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); 4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (int i = 0; unitmask; ++i, unitmask >>= 1) { 4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!(unitmask & 0x01)) 4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) HandleDeviceDetachEventOnUIThread(DriveNumberToFilePath(i).value()); 4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid VolumeMountWatcherWin::OnMediaChange(WPARAM wparam, LPARAM lparam) { 4765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (lparam == SHCNE_MEDIAINSERTED || lparam == SHCNE_MEDIAREMOVED) { 4775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu struct _ITEMIDLIST* pidl = *reinterpret_cast<struct _ITEMIDLIST**>( 4785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu wparam); 4795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu wchar_t sPath[MAX_PATH]; 4805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!SHGetPathFromIDList(pidl, sPath)) { 4815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu DVLOG(1) << "MediaInserted: SHGetPathFromIDList failed"; 4825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 4835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu switch (lparam) { 4855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case SHCNE_MEDIAINSERTED: { 4865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu std::vector<base::FilePath> paths; 4875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu paths.push_back(base::FilePath(sPath)); 4885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu AddDevicesOnUIThread(paths); 4895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 4905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case SHCNE_MEDIAREMOVED: { 4925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu HandleDeviceDetachEventOnUIThread(sPath); 4935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 4945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 4985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::SetNotifications( 5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) StorageMonitor::Receiver* notifications) { 5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifications_ = notifications; 5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)VolumeMountWatcherWin::~VolumeMountWatcherWin() { 5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) weak_factory_.InvalidateWeakPtrs(); 506c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) device_info_worker_pool_->Shutdown(); 5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread( 5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& device_path, 511c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const StorageInfo& info) { 5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 514868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) device_metadata_[device_path] = info; 5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 516c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (notifications_) 517c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) notifications_->ProcessAttach(info); 5184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 5194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DeviceCheckComplete(device_path); 5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void VolumeMountWatcherWin::HandleDeviceDetachEventOnUIThread( 523868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const base::string16& device_location) { 5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MountPointDeviceMetadataMap::const_iterator device_info = 527868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) device_metadata_.find(base::FilePath(device_location)); 5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If the device isn't type removable (like a CD), it won't be there. 5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (device_info == device_metadata_.end()) 5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (notifications_) 53390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) notifications_->ProcessDetach(device_info->second.device_id()); 5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) device_metadata_.erase(device_info); 5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 537b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void VolumeMountWatcherWin::EjectDevice( 538b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const std::string& device_id, 539b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::Callback<void(StorageMonitor::EjectStatus)> callback) { 540b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 541868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::FilePath device = MediaStorageUtil::FindDevicePathById(device_id); 542b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (device.empty()) { 543b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) callback.Run(StorageMonitor::EJECT_FAILURE); 544b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 545b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 546868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (device_metadata_.erase(device) == 0) { 547b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) callback.Run(StorageMonitor::EJECT_FAILURE); 548b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 549b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 550b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 551868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) task_runner_->PostTask( 552868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) FROM_HERE, 553b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) base::Bind(&EjectDeviceInThreadPool, device, callback, task_runner_, 0)); 554b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)} 555a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 556a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} // namespace storage_monitor 557