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 "chrome/browser/extensions/api/music_manager_private/device_id.h"
6
7#include <CoreFoundation/CoreFoundation.h>
8#include <DiskArbitration/DASession.h>
9#include <DiskArbitration/DADisk.h>
10#include <sys/mount.h>
11
12#include "base/bind.h"
13#include "base/mac/foundation_util.h"
14#include "base/mac/scoped_cftyperef.h"
15#include "base/strings/sys_string_conversions.h"
16#include "content/public/browser/browser_thread.h"
17
18namespace {
19
20const char kRootDirectory[] = "/";
21
22// Return the BSD name (e.g. '/dev/disk1') of the root directory by enumerating
23// through the mounted volumes .
24// Return "" if an error occured.
25std::string FindBSDNameOfSystemDisk() {
26  struct statfs* mounted_volumes;
27  int num_volumes = getmntinfo(&mounted_volumes, 0);
28  if (num_volumes == 0) {
29    VLOG(1) << "Cannot enumerate list of mounted volumes.";
30    return std::string();
31  }
32
33  for (int i = 0; i < num_volumes; i++) {
34    struct statfs* vol = &mounted_volumes[i];
35    if (std::string(vol->f_mntonname) == kRootDirectory) {
36      return std::string(vol->f_mntfromname);
37    }
38  }
39
40  VLOG(1) << "Cannot find disk mounted as '" << kRootDirectory << "'.";
41  return std::string();
42}
43
44// Return the Volume UUID property of a BSD disk name (e.g. '/dev/disk1').
45// Return "" if an error occured.
46std::string GetVolumeUUIDFromBSDName(const std::string& bsd_name) {
47  const CFAllocatorRef allocator = NULL;
48
49  base::ScopedCFTypeRef<DASessionRef> session(DASessionCreate(allocator));
50  if (session.get() == NULL) {
51    VLOG(1) << "Error creating DA Session.";
52    return std::string();
53  }
54
55  base::ScopedCFTypeRef<DADiskRef> disk(
56      DADiskCreateFromBSDName(allocator, session, bsd_name.c_str()));
57  if (disk.get() == NULL) {
58    VLOG(1) << "Error creating DA disk from BSD disk name.";
59    return std::string();
60  }
61
62  base::ScopedCFTypeRef<CFDictionaryRef> disk_description(
63      DADiskCopyDescription(disk));
64  if (disk_description.get() == NULL) {
65    VLOG(1) << "Error getting disk description.";
66    return std::string();
67  }
68
69  CFUUIDRef volume_uuid = base::mac::GetValueFromDictionary<CFUUIDRef>(
70      disk_description,
71      kDADiskDescriptionVolumeUUIDKey);
72  if (volume_uuid == NULL) {
73    VLOG(1) << "Error getting volume UUID of disk.";
74    return std::string();
75  }
76
77  base::ScopedCFTypeRef<CFStringRef> volume_uuid_string(
78      CFUUIDCreateString(allocator, volume_uuid));
79  if (volume_uuid_string.get() == NULL) {
80    VLOG(1) << "Error creating string from CSStringRef.";
81    return std::string();
82  }
83
84  return base::SysCFStringRefToUTF8(volume_uuid_string.get());
85}
86
87// Return Volume UUID property of disk mounted as "/".
88void GetVolumeUUID(const extensions::api::DeviceId::IdCallback& callback) {
89  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
90
91  std::string result;
92  std::string bsd_name = FindBSDNameOfSystemDisk();
93  if (!bsd_name.empty()) {
94    VLOG(4) << "BSD name of root directory: '" << bsd_name << "'";
95    result = GetVolumeUUIDFromBSDName(bsd_name);
96  }
97  content::BrowserThread::PostTask(
98      content::BrowserThread::UI,
99      FROM_HERE,
100      base::Bind(callback, result));
101}
102
103}
104
105namespace extensions {
106namespace api {
107
108// MacOS: Return Volume UUID property of disk mounted as "/".
109/* static */
110void DeviceId::GetMachineId(const IdCallback& callback) {
111  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
112
113  content::BrowserThread::PostTask(
114      content::BrowserThread::FILE,
115      FROM_HERE,
116      base::Bind(GetVolumeUUID, callback));
117}
118
119}  // namespace api
120}  // namespace extensions
121