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