1// Copyright 2013 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 <libudev.h>
6
7#include "base/files/file_util.h"
8#include "base/strings/string_number_conversions.h"
9#include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h"
10#include "content/public/browser/browser_thread.h"
11
12namespace extensions {
13// TODO(haven): Udev code may be duplicated in the Chrome codebase.
14// https://code.google.com/p/chromium/issues/detail?id=284898
15
16// Returns the integer contained in |attr|.  Returns 0 on error.
17static uint64 get_int_attr(const char* attr){
18  uint64 result = 0;
19  // In error cases, StringToInt will set result to 0
20  base::StringToUint64(attr, &result);
21  return result;
22}
23
24static int get_device_blk_size(const std::string& path) {
25  base::FilePath file_path(path);
26  std::string device = file_path.BaseName().value();
27
28  base::FilePath info_file_path = base::FilePath("/sys/block")
29                                  .Append(device)
30                                  .Append("queue/logical_block_size");
31
32  std::string file_contents;
33  int blk_size;
34
35  if (!base::ReadFileToString(info_file_path, &file_contents)) {
36    return 0;
37  }
38  // In error cases, StringToInt will set blk_size to 0
39  base::StringToInt(file_contents, &blk_size);
40
41  return blk_size;
42}
43
44bool RemovableStorageProvider::PopulateDeviceList(
45    scoped_refptr<StorageDeviceList> device_list) {
46  struct udev* udev;
47  struct udev_enumerate* enumerate;
48  struct udev_list_entry* devices, *dev_list_entry;
49  struct udev_device* dev, *parent;
50
51  udev = udev_new();
52  if (!udev) {
53    DLOG(ERROR) << "Can't create udev";
54    return false;
55  }
56
57  /* Create a list of the devices in the 'block' subsystem. */
58  enumerate = udev_enumerate_new(udev);
59
60  udev_enumerate_add_match_subsystem(enumerate, "block");
61  udev_enumerate_scan_devices(enumerate);
62  devices = udev_enumerate_get_list_entry(enumerate);
63
64  udev_list_entry_foreach(dev_list_entry, devices) {
65    const char* path = udev_list_entry_get_name(dev_list_entry);
66    dev = udev_device_new_from_syspath(udev, path);
67
68    const char* partition = udev_device_get_sysattr_value(dev, "partition");
69    if (partition && get_int_attr(partition)){
70      // This is a partition of a device, not the device itself
71      continue;
72    }
73
74    const char* removable = udev_device_get_sysattr_value(dev, "removable");
75    if (!removable || !get_int_attr(removable)) {
76      // This is not a removable storage device.
77      continue;
78    }
79
80    /* Get the parent SCSI device that contains the model
81       and manufacturer.  You can look at the hierarchy with
82       udevadm info -a -n /dev/<device> */
83    parent = udev_device_get_parent_with_subsystem_devtype(
84           dev,
85           "scsi",
86           NULL);
87    if (!parent) {
88      // this is not a usb device
89      continue;
90    }
91
92    linked_ptr<api::image_writer_private::RemovableStorageDevice> device(
93      new api::image_writer_private::RemovableStorageDevice());
94    device->vendor = udev_device_get_sysattr_value(parent, "vendor");
95    device->model = udev_device_get_sysattr_value(parent, "model");
96    // TODO (smaskell): Don't expose raw device path
97    device->storage_unit_id = udev_device_get_devnode(dev);
98    device->capacity = get_int_attr(udev_device_get_sysattr_value(dev, "size"))
99      * get_device_blk_size(device->storage_unit_id);
100    device->removable = removable;
101
102    device_list->data.push_back(device);
103
104    udev_device_unref(dev);
105  }
106  /* Free the enumerator object */
107  udev_enumerate_unref(enumerate);
108  udev_unref(udev);
109
110  return true;
111}
112
113} // namespace extensions
114