Index: chrome/browser/storage_monitor/removable_device_notifications_linux.cc |
diff --git a/chrome/browser/storage_monitor/removable_device_notifications_linux.cc b/chrome/browser/storage_monitor/removable_device_notifications_linux.cc |
deleted file mode 100644 |
index db6ef69c67d59aeb18271830f24244a9dd3a3ac4..0000000000000000000000000000000000000000 |
--- a/chrome/browser/storage_monitor/removable_device_notifications_linux.cc |
+++ /dev/null |
@@ -1,464 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-// RemovableDeviceNotificationsLinux implementation. |
- |
-#include "chrome/browser/storage_monitor/removable_device_notifications_linux.h" |
- |
-#include <mntent.h> |
-#include <stdio.h> |
- |
-#include <list> |
- |
-#include "base/basictypes.h" |
-#include "base/bind.h" |
-#include "base/files/file_path.h" |
-#include "base/metrics/histogram.h" |
-#include "base/stl_util.h" |
-#include "base/string_util.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/utf_string_conversions.h" |
-#include "chrome/browser/storage_monitor/media_device_notifications_utils.h" |
-#include "chrome/browser/storage_monitor/media_storage_util.h" |
-#include "chrome/browser/storage_monitor/removable_device_constants.h" |
-#include "chrome/browser/storage_monitor/udev_util_linux.h" |
- |
-namespace chrome { |
- |
-using content::BrowserThread; |
- |
-namespace { |
- |
-// List of file systems we care about. |
-const char* const kKnownFileSystems[] = { |
- "ext2", |
- "ext3", |
- "ext4", |
- "fat", |
- "hfsplus", |
- "iso9660", |
- "msdos", |
- "ntfs", |
- "udf", |
- "vfat", |
-}; |
- |
-// udev device property constants. |
-const char kBlockSubsystemKey[] = "block"; |
-const char kDiskDeviceTypeKey[] = "disk"; |
-const char kFsUUID[] = "ID_FS_UUID"; |
-const char kLabel[] = "ID_FS_LABEL"; |
-const char kModel[] = "ID_MODEL"; |
-const char kModelID[] = "ID_MODEL_ID"; |
-const char kRemovableSysAttr[] = "removable"; |
-const char kSerialShort[] = "ID_SERIAL_SHORT"; |
-const char kSizeSysAttr[] = "size"; |
-const char kVendor[] = "ID_VENDOR"; |
-const char kVendorID[] = "ID_VENDOR_ID"; |
- |
-// (mount point, mount device) |
-// A mapping from mount point to mount device, as extracted from the mtab |
-// file. |
-typedef std::map<base::FilePath, base::FilePath> MountPointDeviceMap; |
- |
-// Reads mtab file entries into |mtab|. |
-void ReadMtab(const base::FilePath& mtab_path, |
- const std::set<std::string>& interesting_file_systems, |
- MountPointDeviceMap* mtab) { |
- mtab->clear(); |
- |
- FILE* fp = setmntent(mtab_path.value().c_str(), "r"); |
- if (!fp) |
- return; |
- |
- mntent entry; |
- char buf[512]; |
- |
- // We return the same device mounted to multiple locations, but hide |
- // devices that have been mounted over. |
- while (getmntent_r(fp, &entry, buf, sizeof(buf))) { |
- // We only care about real file systems. |
- if (!ContainsKey(interesting_file_systems, entry.mnt_type)) |
- continue; |
- |
- (*mtab)[base::FilePath(entry.mnt_dir)] = base::FilePath(entry.mnt_fsname); |
- } |
- endmntent(fp); |
-} |
- |
-// Construct a device id using label or manufacturer (vendor and model) details. |
-std::string MakeDeviceUniqueId(struct udev_device* device) { |
- std::string uuid = GetUdevDevicePropertyValue(device, kFsUUID); |
- if (!uuid.empty()) |
- return kFSUniqueIdPrefix + uuid; |
- |
- // If one of the vendor, model, serial information is missing, its value |
- // in the string is empty. |
- // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialShortInfo |
- // E.g.: VendorModelSerial:Kn:DataTravel_12.10:8000000000006CB02CDB |
- std::string vendor = GetUdevDevicePropertyValue(device, kVendorID); |
- std::string model = GetUdevDevicePropertyValue(device, kModelID); |
- std::string serial_short = GetUdevDevicePropertyValue(device, |
- kSerialShort); |
- if (vendor.empty() && model.empty() && serial_short.empty()) |
- return std::string(); |
- |
- return kVendorModelSerialPrefix + vendor + ":" + model + ":" + serial_short; |
-} |
- |
-// Records GetDeviceInfo result, to see how often we fail to get device details. |
-void RecordGetDeviceInfoResult(bool result) { |
- UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.UdevRequestSuccess", result); |
-} |
- |
-// Returns the storage partition size of the device specified by |device_path|. |
-// If the requested information is unavailable, returns 0. |
-uint64 GetDeviceStorageSize(const base::FilePath& device_path, |
- struct udev_device* device) { |
- // sysfs provides the device size in units of 512-byte blocks. |
- const std::string partition_size = udev_device_get_sysattr_value( |
- device, kSizeSysAttr); |
- |
- // Keep track of device size, to see how often this information is |
- // unavailable. |
- UMA_HISTOGRAM_BOOLEAN( |
- "RemovableDeviceNotificationsLinux.device_partition_size_available", |
- !partition_size.empty()); |
- |
- uint64 total_size_in_bytes = 0; |
- if (!base::StringToUint64(partition_size, &total_size_in_bytes)) |
- return 0; |
- return (total_size_in_bytes <= kuint64max / 512) ? |
- total_size_in_bytes * 512 : 0; |
-} |
- |
-// Constructs the device name from the device properties. If the device details |
-// are unavailable, returns an empty string. |
-string16 GetDeviceName(struct udev_device* device) { |
- std::string device_label = GetUdevDevicePropertyValue(device, kLabel); |
- if (!device_label.empty() && IsStringUTF8(device_label)) |
- return UTF8ToUTF16(device_label); |
- |
- device_label = GetUdevDevicePropertyValue(device, kFsUUID); |
- // Keep track of device uuid, to see how often we receive empty uuid values. |
- UMA_HISTOGRAM_BOOLEAN( |
- "RemovableDeviceNotificationsLinux.device_file_system_uuid_available", |
- !device_label.empty()); |
- |
- const string16 name = GetFullProductName( |
- GetUdevDevicePropertyValue(device, kVendor), |
- GetUdevDevicePropertyValue(device, kModel)); |
- |
- const string16 device_label_utf16 = |
- (!device_label.empty() && IsStringUTF8(device_label)) ? |
- UTF8ToUTF16(device_label) : string16(); |
- if (!name.empty() && !device_label_utf16.empty()) |
- return device_label_utf16 + ASCIIToUTF16(" ") + name; |
- return name.empty() ? device_label_utf16 : name; |
-} |
- |
-// Get the device information using udev library. |
-// On success, returns true and fill in |unique_id|, |name|, |removable| and |
-// |partition_size_in_bytes|. |
-void GetDeviceInfo(const base::FilePath& device_path, |
- std::string* unique_id, |
- string16* name, |
- bool* removable, |
- uint64* partition_size_in_bytes) { |
- DCHECK(!device_path.empty()); |
- ScopedUdevObject udev_obj(udev_new()); |
- if (!udev_obj.get()) { |
- RecordGetDeviceInfoResult(false); |
- return; |
- } |
- |
- struct stat device_stat; |
- if (stat(device_path.value().c_str(), &device_stat) < 0) { |
- RecordGetDeviceInfoResult(false); |
- return; |
- } |
- |
- char device_type; |
- if (S_ISCHR(device_stat.st_mode)) { |
- device_type = 'c'; |
- } else if (S_ISBLK(device_stat.st_mode)) { |
- device_type = 'b'; |
- } else { |
- RecordGetDeviceInfoResult(false); |
- return; // Not a supported type. |
- } |
- |
- ScopedUdevDeviceObject device( |
- udev_device_new_from_devnum(udev_obj, device_type, device_stat.st_rdev)); |
- if (!device.get()) { |
- RecordGetDeviceInfoResult(false); |
- return; |
- } |
- |
- if (name) |
- *name = GetDeviceName(device); |
- |
- if (unique_id) |
- *unique_id = MakeDeviceUniqueId(device); |
- |
- if (removable) { |
- const char* value = udev_device_get_sysattr_value(device, |
- kRemovableSysAttr); |
- if (!value) { |
- // |parent_device| is owned by |device| and does not need to be cleaned |
- // up. |
- struct udev_device* parent_device = |
- udev_device_get_parent_with_subsystem_devtype(device, |
- kBlockSubsystemKey, |
- kDiskDeviceTypeKey); |
- value = udev_device_get_sysattr_value(parent_device, kRemovableSysAttr); |
- } |
- *removable = (value && atoi(value) == 1); |
- } |
- |
- if (partition_size_in_bytes) |
- *partition_size_in_bytes = GetDeviceStorageSize(device_path, device); |
- RecordGetDeviceInfoResult(true); |
-} |
- |
-} // namespace |
- |
-RemovableDeviceNotificationsLinux::RemovableDeviceNotificationsLinux( |
- const base::FilePath& path) |
- : initialized_(false), |
- mtab_path_(path), |
- get_device_info_func_(&GetDeviceInfo) { |
-} |
- |
-RemovableDeviceNotificationsLinux::RemovableDeviceNotificationsLinux( |
- const base::FilePath& path, |
- GetDeviceInfoFunc get_device_info_func) |
- : initialized_(false), |
- mtab_path_(path), |
- get_device_info_func_(get_device_info_func) { |
-} |
- |
-RemovableDeviceNotificationsLinux::~RemovableDeviceNotificationsLinux() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
-} |
- |
-void RemovableDeviceNotificationsLinux::Init() { |
- DCHECK(!mtab_path_.empty()); |
- |
- // Put |kKnownFileSystems| in std::set to get O(log N) access time. |
- for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i) |
- known_file_systems_.insert(kKnownFileSystems[i]); |
- |
- BrowserThread::PostTask( |
- BrowserThread::FILE, FROM_HERE, |
- base::Bind(&RemovableDeviceNotificationsLinux::InitOnFileThread, this)); |
-} |
- |
-bool RemovableDeviceNotificationsLinux::GetStorageInfoForPath( |
- const base::FilePath& path, |
- StorageInfo* device_info) const { |
- if (!path.IsAbsolute()) |
- return false; |
- |
- base::FilePath current = path; |
- while (!ContainsKey(mount_info_map_, current) && current != current.DirName()) |
- current = current.DirName(); |
- |
- MountMap::const_iterator mount_info = mount_info_map_.find(current); |
- if (mount_info == mount_info_map_.end()) |
- return false; |
- |
- if (device_info) { |
- device_info->device_id = mount_info->second.device_id; |
- device_info->name = mount_info->second.device_name; |
- device_info->location = current.value(); |
- } |
- return true; |
-} |
- |
-uint64 RemovableDeviceNotificationsLinux::GetStorageSize( |
- const std::string& location) const { |
- MountMap::const_iterator mount_info = mount_info_map_.find( |
- base::FilePath(location)); |
- return (mount_info != mount_info_map_.end()) ? |
- mount_info->second.partition_size_in_bytes : 0; |
-} |
- |
-void RemovableDeviceNotificationsLinux::OnFilePathChanged( |
- const base::FilePath& path, |
- bool error) { |
- if (path != mtab_path_) { |
- // This cannot happen unless FilePathWatcher is buggy. Just ignore this |
- // notification and do nothing. |
- NOTREACHED(); |
- return; |
- } |
- if (error) { |
- LOG(ERROR) << "Error watching " << mtab_path_.value(); |
- return; |
- } |
- |
- UpdateMtab(); |
-} |
- |
-RemovableDeviceNotificationsLinux::MountPointInfo::MountPointInfo() |
- : partition_size_in_bytes(0) { |
-} |
- |
-void RemovableDeviceNotificationsLinux::InitOnFileThread() { |
- DCHECK(!initialized_); |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- initialized_ = true; |
- |
- // The callback passed to Watch() has to be unretained. Otherwise |
- // RemovableDeviceNotificationsLinux will live longer than expected, and |
- // FilePathWatcher will get in trouble at shutdown time. |
- bool ret = file_watcher_.Watch( |
- mtab_path_, false, |
- base::Bind(&RemovableDeviceNotificationsLinux::OnFilePathChanged, |
- base::Unretained(this))); |
- if (!ret) { |
- LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed"; |
- return; |
- } |
- |
- UpdateMtab(); |
-} |
- |
-void RemovableDeviceNotificationsLinux::UpdateMtab() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- |
- MountPointDeviceMap new_mtab; |
- ReadMtab(mtab_path_, known_file_systems_, &new_mtab); |
- |
- // Check existing mtab entries for unaccounted mount points. |
- // These mount points must have been removed in the new mtab. |
- std::list<base::FilePath> mount_points_to_erase; |
- std::list<base::FilePath> multiple_mounted_devices_needing_reattachment; |
- for (MountMap::const_iterator old_iter = mount_info_map_.begin(); |
- old_iter != mount_info_map_.end(); ++old_iter) { |
- const base::FilePath& mount_point = old_iter->first; |
- const base::FilePath& mount_device = old_iter->second.mount_device; |
- MountPointDeviceMap::iterator new_iter = new_mtab.find(mount_point); |
- // |mount_point| not in |new_mtab| or |mount_device| is no longer mounted at |
- // |mount_point|. |
- if (new_iter == new_mtab.end() || (new_iter->second != mount_device)) { |
- MountPriorityMap::iterator priority = |
- mount_priority_map_.find(mount_device); |
- DCHECK(priority != mount_priority_map_.end()); |
- ReferencedMountPoint::const_iterator has_priority = |
- priority->second.find(mount_point); |
- if (MediaStorageUtil::IsRemovableDevice(old_iter->second.device_id)) { |
- DCHECK(has_priority != priority->second.end()); |
- if (has_priority->second) { |
- receiver()->ProcessDetach(old_iter->second.device_id); |
- } |
- if (priority->second.size() > 1) |
- multiple_mounted_devices_needing_reattachment.push_back(mount_device); |
- } |
- priority->second.erase(mount_point); |
- if (priority->second.empty()) |
- mount_priority_map_.erase(mount_device); |
- mount_points_to_erase.push_back(mount_point); |
- } |
- } |
- |
- // Erase the |mount_info_map_| entries afterwards. Erasing in the loop above |
- // using the iterator is slightly more efficient, but more tricky, since |
- // calling std::map::erase() on an iterator invalidates it. |
- for (std::list<base::FilePath>::const_iterator it = |
- mount_points_to_erase.begin(); |
- it != mount_points_to_erase.end(); |
- ++it) { |
- mount_info_map_.erase(*it); |
- } |
- |
- // For any multiply mounted device where the mount that we had notified |
- // got detached, send a notification of attachment for one of the other |
- // mount points. |
- for (std::list<base::FilePath>::const_iterator it = |
- multiple_mounted_devices_needing_reattachment.begin(); |
- it != multiple_mounted_devices_needing_reattachment.end(); |
- ++it) { |
- ReferencedMountPoint::iterator first_mount_point_info = |
- mount_priority_map_.find(*it)->second.begin(); |
- const base::FilePath& mount_point = first_mount_point_info->first; |
- first_mount_point_info->second = true; |
- |
- const MountPointInfo& mount_info = |
- mount_info_map_.find(mount_point)->second; |
- DCHECK(MediaStorageUtil::IsRemovableDevice(mount_info.device_id)); |
- receiver()->ProcessAttach(StorageInfo( |
- mount_info.device_id, mount_info.device_name, mount_point.value())); |
- } |
- |
- // Check new mtab entries against existing ones. |
- for (MountPointDeviceMap::iterator new_iter = new_mtab.begin(); |
- new_iter != new_mtab.end(); ++new_iter) { |
- const base::FilePath& mount_point = new_iter->first; |
- const base::FilePath& mount_device = new_iter->second; |
- MountMap::iterator old_iter = mount_info_map_.find(mount_point); |
- if (old_iter == mount_info_map_.end() || |
- old_iter->second.mount_device != mount_device) { |
- // New mount point found or an existing mount point found with a new |
- // device. |
- AddNewMount(mount_device, mount_point); |
- } |
- } |
-} |
- |
-void RemovableDeviceNotificationsLinux::AddNewMount( |
- const base::FilePath& mount_device, const base::FilePath& mount_point) { |
- MountPriorityMap::iterator priority = |
- mount_priority_map_.find(mount_device); |
- if (priority != mount_priority_map_.end()) { |
- const base::FilePath& other_mount_point = priority->second.begin()->first; |
- priority->second[mount_point] = false; |
- mount_info_map_[mount_point] = |
- mount_info_map_.find(other_mount_point)->second; |
- return; |
- } |
- |
- std::string unique_id; |
- string16 name; |
- bool removable; |
- uint64 partition_size_in_bytes; |
- get_device_info_func_(mount_device, &unique_id, &name, &removable, |
- &partition_size_in_bytes); |
- |
- // Keep track of device info details to see how often we get invalid values. |
- MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id, name); |
- if (unique_id.empty() || name.empty()) |
- return; |
- |
- bool has_dcim = IsMediaDevice(mount_point.value()); |
- MediaStorageUtil::Type type; |
- if (removable) { |
- if (has_dcim) { |
- type = MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM; |
- } else { |
- type = MediaStorageUtil::REMOVABLE_MASS_STORAGE_NO_DCIM; |
- } |
- } else { |
- type = MediaStorageUtil::FIXED_MASS_STORAGE; |
- } |
- std::string device_id = MediaStorageUtil::MakeDeviceId(type, unique_id); |
- |
- MountPointInfo mount_point_info; |
- mount_point_info.mount_device = mount_device; |
- mount_point_info.device_id = device_id; |
- mount_point_info.device_name = name; |
- mount_point_info.partition_size_in_bytes = partition_size_in_bytes; |
- |
- mount_info_map_[mount_point] = mount_point_info; |
- mount_priority_map_[mount_device][mount_point] = removable; |
- |
- if (removable) { |
- receiver()->ProcessAttach(StorageInfo( |
- device_id, GetDisplayNameForDevice(partition_size_in_bytes, name), |
- mount_point.value())); |
- } |
-} |
- |
-} // namespace chrome |