1effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
2effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// found in the LICENSE file.
4effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/metrics/machine_id_provider.h"
6effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
7effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include <windows.h>
8effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include <winioctl.h>
9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
10effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/base_paths.h"
11effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/files/file_path.h"
12effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/path_service.h"
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/win/scoped_handle.h"
15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
16effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochnamespace metrics {
17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)MachineIdProvider::MachineIdProvider() {
19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)MachineIdProvider::~MachineIdProvider() {
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
23effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
24effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// On windows, the machine id is based on the serial number of the drive Chrome
25effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// is running from.
26effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochstd::string MachineIdProvider::GetMachineId() {
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::ThreadRestrictions::AssertIOAllowed();
28effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
29effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Use the program's path to get the drive used for the machine id. This means
30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // that whenever the underlying drive changes, it's considered a new machine.
31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // This is fine as we do not support migrating Chrome installs to new drives.
32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  base::FilePath executable_path;
33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!PathService::Get(base::FILE_EXE, &executable_path)) {
35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    NOTREACHED();
36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return std::string();
37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  std::vector<base::FilePath::StringType> path_components;
40effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  executable_path.GetComponents(&path_components);
41effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (path_components.empty()) {
42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    NOTREACHED();
43effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return std::string();
44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  base::FilePath::StringType drive_name = L"\\\\.\\" + path_components[0];
46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::win::ScopedHandle drive_handle(
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      CreateFile(drive_name.c_str(),
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 0,
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 FILE_SHARE_READ | FILE_SHARE_WRITE,
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 NULL,
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 OPEN_EXISTING,
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 0,
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 NULL));
55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  STORAGE_PROPERTY_QUERY query = {};
57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  query.PropertyId = StorageDeviceProperty;
58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  query.QueryType = PropertyStandardQuery;
59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Perform an initial query to get the number of bytes being returned.
61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DWORD bytes_returned;
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  STORAGE_DESCRIPTOR_HEADER header = {};
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  BOOL status = DeviceIoControl(drive_handle,
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                IOCTL_STORAGE_QUERY_PROPERTY,
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                &query,
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                sizeof(STORAGE_PROPERTY_QUERY),
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                &header,
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                sizeof(STORAGE_DESCRIPTOR_HEADER),
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                &bytes_returned,
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                NULL);
71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!status)
73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return std::string();
74effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Query for the actual serial number.
76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  std::vector<int8> output_buf(header.Size);
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  status = DeviceIoControl(drive_handle,
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           IOCTL_STORAGE_QUERY_PROPERTY,
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           &query,
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           sizeof(STORAGE_PROPERTY_QUERY),
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           &output_buf[0],
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           output_buf.size(),
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           &bytes_returned,
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           NULL);
85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!status)
87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return std::string();
88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const STORAGE_DEVICE_DESCRIPTOR* device_descriptor =
90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(&output_buf[0]);
91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
92effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // The serial number is stored in the |output_buf| as a null-terminated
93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // string starting at the specified offset.
94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const DWORD offset = device_descriptor->SerialNumberOffset;
95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (offset >= output_buf.size())
96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return std::string();
97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
98effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Make sure that the null-terminator exists.
99effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const std::vector<int8>::iterator serial_number_begin =
100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      output_buf.begin() + offset;
101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const std::vector<int8>::iterator null_location =
102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      std::find(serial_number_begin, output_buf.end(), '\0');
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (null_location == output_buf.end())
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return std::string();
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const char* serial_number =
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      reinterpret_cast<const char*>(&output_buf[offset]);
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return std::string(serial_number);
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// static
113effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochMachineIdProvider* MachineIdProvider::CreateInstance() {
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return new MachineIdProvider();
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}  //  namespace metrics
118