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