Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium OS 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 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 5 | #include "cros-disks/udev_device.h" |
Ben Chan | beefd0d | 2011-07-25 09:31:34 -0700 | [diff] [blame] | 6 | |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 7 | #include <fcntl.h> |
| 8 | #include <libudev.h> |
Ben Chan | da410a0 | 2011-08-24 23:37:53 -0700 | [diff] [blame] | 9 | #include <stdlib.h> |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 10 | #include <sys/statvfs.h> |
Ben Chan | bdc3974 | 2011-05-11 17:51:26 -0700 | [diff] [blame] | 11 | |
Chris Masone | ec4761f | 2011-05-12 14:04:13 -0700 | [diff] [blame] | 12 | #include <base/logging.h> |
Ben Chan | 0e1b550 | 2013-07-24 23:39:29 -0700 | [diff] [blame] | 13 | #include <base/sha1.h> |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 14 | #include <base/strings/string_number_conversions.h> |
| 15 | #include <base/strings/string_split.h> |
| 16 | #include <base/strings/string_util.h> |
Ben Chan | bdc3974 | 2011-05-11 17:51:26 -0700 | [diff] [blame] | 17 | #include <rootdev/rootdev.h> |
| 18 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 19 | #include "cros-disks/mount_info.h" |
| 20 | #include "cros-disks/usb_device_info.h" |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 21 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 22 | using std::string; |
| 23 | using std::vector; |
| 24 | |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 25 | namespace { |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 26 | |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 27 | const char kNullDeviceFile[] = "/dev/null"; |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 28 | const char kAttributeIdProduct[] = "idProduct"; |
| 29 | const char kAttributeIdVendor[] = "idVendor"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 30 | const char kAttributePartition[] = "partition"; |
| 31 | const char kAttributeRange[] = "range"; |
| 32 | const char kAttributeReadOnly[] = "ro"; |
| 33 | const char kAttributeRemovable[] = "removable"; |
| 34 | const char kAttributeSize[] = "size"; |
Ben Chan | 58aad1d | 2013-02-26 16:09:34 -0800 | [diff] [blame] | 35 | const char kDriverMMCBlock[] = "mmcblk"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 36 | const char kPropertyBlkIdFilesystemType[] = "TYPE"; |
| 37 | const char kPropertyBlkIdFilesystemLabel[] = "LABEL"; |
| 38 | const char kPropertyBlkIdFilesystemUUID[] = "UUID"; |
| 39 | const char kPropertyCDROM[] = "ID_CDROM"; |
Ben Chan | 7d8f9f7 | 2012-05-02 10:02:20 -0700 | [diff] [blame] | 40 | const char kPropertyCDROMDVD[] = "ID_CDROM_DVD"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 41 | const char kPropertyCDROMMedia[] = "ID_CDROM_MEDIA"; |
Ben Chan | a0fe0ef | 2012-09-06 23:39:56 -0700 | [diff] [blame] | 42 | const char kPropertyCDROMMediaTrackCountData[] = |
| 43 | "ID_CDROM_MEDIA_TRACK_COUNT_DATA"; |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 44 | const char kPropertyDeviceType[] = "DEVTYPE"; |
| 45 | const char kPropertyDeviceTypeUSBDevice[] = "usb_device"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 46 | const char kPropertyFilesystemUsage[] = "ID_FS_USAGE"; |
Ben Chan | 1f41838 | 2013-06-17 23:33:28 -0700 | [diff] [blame] | 47 | const char kPropertyMistSupportedDevice[] = "MIST_SUPPORTED_DEVICE"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 48 | const char kPropertyModel[] = "ID_MODEL"; |
Ben Chan | cac2033 | 2014-02-13 23:21:17 -0800 | [diff] [blame] | 49 | const char kPropertyPartitionEntryType[] = "ID_PART_ENTRY_TYPE"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 50 | const char kPropertyPartitionSize[] = "UDISKS_PARTITION_SIZE"; |
| 51 | const char kPropertyPresentationHide[] = "UDISKS_PRESENTATION_HIDE"; |
| 52 | const char kPropertyRotationRate[] = "ID_ATA_ROTATION_RATE_RPM"; |
Ben Chan | 0e1b550 | 2013-07-24 23:39:29 -0700 | [diff] [blame] | 53 | const char kPropertySerial[] = "ID_SERIAL"; |
Ben Chan | 1f41838 | 2013-06-17 23:33:28 -0700 | [diff] [blame] | 54 | const char kSubsystemUsb[] = "usb"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 55 | const char kVirtualDevicePathPrefix[] = "/sys/devices/virtual/"; |
Ben Chan | 2923ed7 | 2013-07-29 15:52:29 -0700 | [diff] [blame] | 56 | const char kLoopDevicePathPrefix[] = "/sys/devices/virtual/block/loop"; |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 57 | const char kUSBDeviceInfoFile[] = "/opt/google/cros-disks/usb-device-info"; |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 58 | const char kUSBIdentifierDatabase[] = "/usr/share/misc/usb.ids"; |
Ben Chan | cac2033 | 2014-02-13 23:21:17 -0800 | [diff] [blame] | 59 | const char* kPartitionTypesToHide[] = { |
| 60 | "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", // EFI system partition |
| 61 | "fe3a2a5d-4f32-41a7-b725-accc3285a309", // Chrome OS kernel |
| 62 | "3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec", // Chrome OS root filesystem |
| 63 | "cab6e88e-abf3-4102-a07a-d4bb9be3c1d3", // Chrome OS firmware |
| 64 | "2e0a753d-9e48-43b0-8337-b15192cb1b5e", // Chrome OS reserved |
Ben Chan | 1c2d425 | 2011-06-03 13:33:49 -0700 | [diff] [blame] | 65 | }; |
| 66 | |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 67 | } // namespace |
| 68 | |
| 69 | namespace cros_disks { |
| 70 | |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 71 | UdevDevice::UdevDevice(struct udev_device *dev) |
Ben Chan | da410a0 | 2011-08-24 23:37:53 -0700 | [diff] [blame] | 72 | : dev_(dev), |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame^] | 73 | blkid_cache_(nullptr) { |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 74 | CHECK(dev_) << "Invalid udev device"; |
| 75 | udev_device_ref(dev_); |
| 76 | } |
| 77 | |
| 78 | UdevDevice::~UdevDevice() { |
Ben Chan | da410a0 | 2011-08-24 23:37:53 -0700 | [diff] [blame] | 79 | if (blkid_cache_) { |
| 80 | // It needs to call blkid_put_cache to deallocate the blkid cache. |
| 81 | blkid_put_cache(blkid_cache_); |
| 82 | } |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 83 | udev_device_unref(dev_); |
| 84 | } |
| 85 | |
Ben Chan | 1f41838 | 2013-06-17 23:33:28 -0700 | [diff] [blame] | 86 | // static |
Ben Chan | 22c0e60 | 2012-02-13 12:37:34 -0800 | [diff] [blame] | 87 | string UdevDevice::EnsureUTF8String(const string& str) { |
Ben Chan | af25ddb | 2014-05-21 18:32:47 -0700 | [diff] [blame] | 88 | return base::IsStringUTF8(str) ? str : ""; |
Ben Chan | 22c0e60 | 2012-02-13 12:37:34 -0800 | [diff] [blame] | 89 | } |
| 90 | |
Ben Chan | 1f41838 | 2013-06-17 23:33:28 -0700 | [diff] [blame] | 91 | // static |
| 92 | bool UdevDevice::IsValueBooleanTrue(const char *value) { |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 93 | return value && strcmp(value, "1") == 0; |
| 94 | } |
| 95 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 96 | string UdevDevice::GetAttribute(const char *key) const { |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 97 | const char *value = udev_device_get_sysattr_value(dev_, key); |
| 98 | return (value) ? value : ""; |
| 99 | } |
| 100 | |
| 101 | bool UdevDevice::IsAttributeTrue(const char *key) const { |
| 102 | const char *value = udev_device_get_sysattr_value(dev_, key); |
| 103 | return IsValueBooleanTrue(value); |
| 104 | } |
| 105 | |
| 106 | bool UdevDevice::HasAttribute(const char *key) const { |
| 107 | const char *value = udev_device_get_sysattr_value(dev_, key); |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame^] | 108 | return value != nullptr; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 109 | } |
| 110 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 111 | string UdevDevice::GetProperty(const char *key) const { |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 112 | const char *value = udev_device_get_property_value(dev_, key); |
| 113 | return (value) ? value : ""; |
| 114 | } |
| 115 | |
| 116 | bool UdevDevice::IsPropertyTrue(const char *key) const { |
| 117 | const char *value = udev_device_get_property_value(dev_, key); |
| 118 | return IsValueBooleanTrue(value); |
| 119 | } |
| 120 | |
| 121 | bool UdevDevice::HasProperty(const char *key) const { |
| 122 | const char *value = udev_device_get_property_value(dev_, key); |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame^] | 123 | return value != nullptr; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 124 | } |
| 125 | |
Ben Chan | da410a0 | 2011-08-24 23:37:53 -0700 | [diff] [blame] | 126 | string UdevDevice::GetPropertyFromBlkId(const char *key) { |
| 127 | string value; |
| 128 | const char *dev_file = udev_device_get_devnode(dev_); |
| 129 | if (dev_file) { |
| 130 | // No cache file is used as it should always query information from |
| 131 | // the device, i.e. setting cache file to /dev/null. |
| 132 | if (blkid_cache_ || blkid_get_cache(&blkid_cache_, kNullDeviceFile) == 0) { |
| 133 | blkid_dev dev = blkid_get_dev(blkid_cache_, dev_file, BLKID_DEV_NORMAL); |
| 134 | if (dev) { |
| 135 | char *tag_value = blkid_get_tag_value(blkid_cache_, key, dev_file); |
| 136 | if (tag_value) { |
| 137 | value = tag_value; |
| 138 | free(tag_value); |
| 139 | } |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | return value; |
| 144 | } |
| 145 | |
Ben Chan | a169b0a | 2014-08-06 17:22:40 -0700 | [diff] [blame] | 146 | void UdevDevice::GetSizeInfo(uint64_t *total_size, |
| 147 | uint64_t *remaining_size) const { |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 148 | static const int kSectorSize = 512; |
Ben Chan | a169b0a | 2014-08-06 17:22:40 -0700 | [diff] [blame] | 149 | uint64_t total = 0, remaining = 0; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 150 | |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 151 | // If the device is mounted, obtain the total and remaining size in bytes |
| 152 | // using statvfs. |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 153 | vector<string> mount_paths = GetMountPaths(); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 154 | if (!mount_paths.empty()) { |
| 155 | struct statvfs stat; |
| 156 | if (statvfs(mount_paths[0].c_str(), &stat) == 0) { |
| 157 | total = stat.f_blocks * stat.f_frsize; |
| 158 | remaining = stat.f_bfree * stat.f_frsize; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 159 | } |
| 160 | } |
| 161 | |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 162 | // If the UDISKS_PARTITION_SIZE property is set, use it as the total size |
| 163 | // instead. If the UDISKS_PARTITION_SIZE property is not set but sysfs |
| 164 | // provides a size value, which is the actual size in bytes divided by 512, |
| 165 | // use that as the total size instead. |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 166 | const char *partition_size = |
| 167 | udev_device_get_property_value(dev_, kPropertyPartitionSize); |
Ben Chan | a169b0a | 2014-08-06 17:22:40 -0700 | [diff] [blame] | 168 | int64_t size = 0; |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 169 | if (partition_size) { |
| 170 | base::StringToInt64(partition_size, &size); |
| 171 | total = size; |
| 172 | } else { |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 173 | const char *size_attr = udev_device_get_sysattr_value(dev_, kAttributeSize); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 174 | if (size_attr) { |
| 175 | base::StringToInt64(size_attr, &size); |
| 176 | total = size * kSectorSize; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | if (total_size) |
| 181 | *total_size = total; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 182 | if (remaining_size) |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 183 | *remaining_size = remaining; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 184 | } |
| 185 | |
Ben Chan | 53e12a2 | 2014-02-13 23:34:09 -0800 | [diff] [blame] | 186 | size_t UdevDevice::GetPartitionCount() const { |
Ben Chan | ca36994 | 2011-09-10 17:03:05 -0700 | [diff] [blame] | 187 | size_t partition_count = 0; |
| 188 | const char *dev_file = udev_device_get_devnode(dev_); |
| 189 | if (dev_file) { |
Ben Chan | 53e12a2 | 2014-02-13 23:34:09 -0800 | [diff] [blame] | 190 | blkid_probe probe = blkid_new_probe_from_filename(dev_file); |
| 191 | if (probe) { |
| 192 | blkid_partlist partitions = blkid_probe_get_partitions(probe); |
| 193 | if (partitions) { |
| 194 | partition_count = blkid_partlist_numof_partitions(partitions); |
Ben Chan | ca36994 | 2011-09-10 17:03:05 -0700 | [diff] [blame] | 195 | } |
Ben Chan | 53e12a2 | 2014-02-13 23:34:09 -0800 | [diff] [blame] | 196 | blkid_free_probe(probe); |
Ben Chan | ca36994 | 2011-09-10 17:03:05 -0700 | [diff] [blame] | 197 | } |
| 198 | } |
| 199 | return partition_count; |
| 200 | } |
| 201 | |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 202 | DeviceMediaType UdevDevice::GetDeviceMediaType() const { |
Ben Chan | 7d8f9f7 | 2012-05-02 10:02:20 -0700 | [diff] [blame] | 203 | if (IsPropertyTrue(kPropertyCDROMDVD)) |
| 204 | return DEVICE_MEDIA_DVD; |
| 205 | |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 206 | if (IsPropertyTrue(kPropertyCDROM)) |
Ben Chan | 6d0b272 | 2011-11-18 08:24:14 -0800 | [diff] [blame] | 207 | return DEVICE_MEDIA_OPTICAL_DISC; |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 208 | |
Ben Chan | 58aad1d | 2013-02-26 16:09:34 -0800 | [diff] [blame] | 209 | if (IsOnMMCDevice()) |
| 210 | return DEVICE_MEDIA_SD; |
| 211 | |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 212 | string vendor_id, product_id; |
| 213 | if (GetVendorAndProductId(&vendor_id, &product_id)) { |
| 214 | USBDeviceInfo info; |
| 215 | info.RetrieveFromFile(kUSBDeviceInfoFile); |
| 216 | return info.GetDeviceMediaType(vendor_id, product_id); |
| 217 | } |
| 218 | return DEVICE_MEDIA_UNKNOWN; |
| 219 | } |
| 220 | |
| 221 | bool UdevDevice::GetVendorAndProductId( |
| 222 | string* vendor_id, string* product_id) const { |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 223 | // Search up the parent device tree to obtain the vendor and product ID |
| 224 | // of the first device with a device type "usb_device". Then look up the |
| 225 | // media type based on the vendor and product ID from a USB device info file. |
| 226 | for (struct udev_device *dev = dev_; dev; dev = udev_device_get_parent(dev)) { |
| 227 | const char *device_type = |
| 228 | udev_device_get_property_value(dev, kPropertyDeviceType); |
| 229 | if (device_type && strcmp(device_type, kPropertyDeviceTypeUSBDevice) == 0) { |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 230 | const char *vendor_id_attr = |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 231 | udev_device_get_sysattr_value(dev, kAttributeIdVendor); |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 232 | const char *product_id_attr = |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 233 | udev_device_get_sysattr_value(dev, kAttributeIdProduct); |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 234 | if (vendor_id_attr && product_id_attr) { |
| 235 | *vendor_id = vendor_id_attr; |
| 236 | *product_id = product_id_attr; |
| 237 | return true; |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 238 | } |
| 239 | } |
| 240 | } |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 241 | return false; |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 242 | } |
| 243 | |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 244 | bool UdevDevice::IsMediaAvailable() const { |
| 245 | bool is_media_available = true; |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 246 | if (IsAttributeTrue(kAttributeRemovable)) { |
| 247 | if (IsPropertyTrue(kPropertyCDROM)) { |
| 248 | is_media_available = IsPropertyTrue(kPropertyCDROMMedia); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 249 | } else { |
| 250 | const char *dev_file = udev_device_get_devnode(dev_); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 251 | if (dev_file) { |
| 252 | int fd = open(dev_file, O_RDONLY); |
| 253 | if (fd < 0) { |
| 254 | is_media_available = false; |
| 255 | } else { |
| 256 | close(fd); |
| 257 | } |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 258 | } |
| 259 | } |
| 260 | } |
| 261 | return is_media_available; |
| 262 | } |
| 263 | |
Ben Chan | 1f41838 | 2013-06-17 23:33:28 -0700 | [diff] [blame] | 264 | bool UdevDevice::IsMobileBroadbandDevice() const { |
| 265 | // Check if a parent device, which belongs to the "usb" subsystem and has a |
| 266 | // device type "usb_device", has a property "MIST_SUPPORTED_DEVICE=1". If so, |
| 267 | // it is a mobile broadband device supported by mist. |
| 268 | struct udev_device* parent = udev_device_get_parent_with_subsystem_devtype( |
| 269 | dev_, kSubsystemUsb, kPropertyDeviceTypeUSBDevice); |
| 270 | if (!parent) |
| 271 | return false; |
| 272 | |
| 273 | const char* value = |
| 274 | udev_device_get_property_value(parent, kPropertyMistSupportedDevice); |
| 275 | return IsValueBooleanTrue(value); |
| 276 | } |
| 277 | |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 278 | bool UdevDevice::IsAutoMountable() const { |
Ben Chan | 9b42570 | 2011-09-07 10:57:09 -0700 | [diff] [blame] | 279 | // TODO(benchan): Find a reliable way to detect if a device is a removable |
| 280 | // storage as the removable attribute in sysfs does not always tell the truth. |
Ben Chan | a4b9651 | 2012-04-10 17:37:42 -0700 | [diff] [blame] | 281 | return !IsOnBootDevice() && !IsVirtual(); |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 282 | } |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 283 | |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 284 | bool UdevDevice::IsHidden() { |
| 285 | if (IsPropertyTrue(kPropertyPresentationHide)) |
| 286 | return true; |
| 287 | |
Ben Chan | a0fe0ef | 2012-09-06 23:39:56 -0700 | [diff] [blame] | 288 | // Hide an optical disc without any data track. |
| 289 | // udev/cdrom_id only sets ID_CDROM_MEDIA_TRACK_COUNT_DATA when there is at |
| 290 | // least one data track. |
| 291 | if (IsPropertyTrue(kPropertyCDROM) && |
| 292 | !HasProperty(kPropertyCDROMMediaTrackCountData)) { |
| 293 | return true; |
| 294 | } |
| 295 | |
Ben Chan | 1f41838 | 2013-06-17 23:33:28 -0700 | [diff] [blame] | 296 | // Hide a mobile broadband device, which may initially expose itself as a USB |
| 297 | // mass storage device and later be switched to a modem by mist. |
| 298 | if (IsMobileBroadbandDevice()) |
| 299 | return true; |
| 300 | |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 301 | // Hide a device that is neither marked as a partition nor a filesystem, |
Ben Chan | ca36994 | 2011-09-10 17:03:05 -0700 | [diff] [blame] | 302 | // unless it has no valid partitions (e.g. the device is unformatted or |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 303 | // corrupted). An unformatted or corrupted device is visible in the file |
| 304 | // the file browser so that we can provide a way to format it. |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 305 | if (!HasAttribute(kAttributePartition) && |
Ben Chan | ca36994 | 2011-09-10 17:03:05 -0700 | [diff] [blame] | 306 | !HasProperty(kPropertyFilesystemUsage) && |
Ben Chan | 53e12a2 | 2014-02-13 23:34:09 -0800 | [diff] [blame] | 307 | (GetPartitionCount() > 0)) |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 308 | return true; |
Ben Chan | 1c2d425 | 2011-06-03 13:33:49 -0700 | [diff] [blame] | 309 | |
Ben Chan | cac2033 | 2014-02-13 23:21:17 -0800 | [diff] [blame] | 310 | // Hide special partitions based on partition type. |
| 311 | string partition_type = GetProperty(kPropertyPartitionEntryType); |
| 312 | if (!partition_type.empty()) { |
| 313 | for (size_t i = 0; i < arraysize(kPartitionTypesToHide); ++i) { |
| 314 | if (partition_type == kPartitionTypesToHide[i]) |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 315 | return true; |
Ben Chan | 1c2d425 | 2011-06-03 13:33:49 -0700 | [diff] [blame] | 316 | } |
| 317 | } |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 318 | return false; |
Ben Chan | 1c2d425 | 2011-06-03 13:33:49 -0700 | [diff] [blame] | 319 | } |
| 320 | |
Ben Chan | 2923ed7 | 2013-07-29 15:52:29 -0700 | [diff] [blame] | 321 | bool UdevDevice::IsIgnored() const { |
| 322 | return IsVirtual() && !IsLoopDevice(); |
| 323 | } |
| 324 | |
Ben Chan | 490319f | 2011-05-06 14:00:42 -0700 | [diff] [blame] | 325 | bool UdevDevice::IsOnBootDevice() const { |
| 326 | // Obtain the boot device path, e.g. /dev/sda |
| 327 | char boot_device_path[PATH_MAX]; |
| 328 | if (rootdev(boot_device_path, sizeof(boot_device_path), true, true)) { |
| 329 | LOG(ERROR) << "Could not determine root device"; |
| 330 | // Assume it is on the boot device when there is any uncertainty. |
| 331 | // This is to prevent a device, which is potentially on the boot device, |
| 332 | // from being auto mounted and exposed to users. |
| 333 | // TODO(benchan): Find a way to eliminate the uncertainty. |
| 334 | return true; |
| 335 | } |
| 336 | |
| 337 | // Compare the device file path of the current device and all its parents |
| 338 | // with the boot device path. Any match indicates that the current device |
| 339 | // is on the boot device. |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 340 | for (struct udev_device *dev = dev_; dev; dev = udev_device_get_parent(dev)) { |
Ben Chan | 490319f | 2011-05-06 14:00:42 -0700 | [diff] [blame] | 341 | const char *dev_file = udev_device_get_devnode(dev); |
| 342 | if (dev_file) { |
| 343 | if (strncmp(boot_device_path, dev_file, PATH_MAX) == 0) { |
| 344 | return true; |
| 345 | } |
| 346 | } |
| 347 | } |
| 348 | return false; |
| 349 | } |
| 350 | |
Ben Chan | 58aad1d | 2013-02-26 16:09:34 -0800 | [diff] [blame] | 351 | bool UdevDevice::IsOnMMCDevice() const { |
| 352 | for (struct udev_device *dev = dev_; dev; dev = udev_device_get_parent(dev)) { |
| 353 | const char *driver = udev_device_get_driver(dev); |
| 354 | if (driver && strcmp(driver, kDriverMMCBlock) == 0) { |
| 355 | return true; |
| 356 | } |
| 357 | } |
| 358 | return false; |
| 359 | } |
| 360 | |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 361 | bool UdevDevice::IsOnRemovableDevice() const { |
| 362 | for (struct udev_device *dev = dev_; dev; dev = udev_device_get_parent(dev)) { |
| 363 | const char *value = udev_device_get_sysattr_value(dev, kAttributeRemovable); |
| 364 | if (IsValueBooleanTrue(value)) |
| 365 | return true; |
| 366 | } |
| 367 | return false; |
| 368 | } |
| 369 | |
Ben Chan | 9cda93a | 2011-05-24 13:31:04 -0700 | [diff] [blame] | 370 | bool UdevDevice::IsVirtual() const { |
| 371 | const char *sys_path = udev_device_get_syspath(dev_); |
| 372 | if (sys_path) { |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 373 | return StartsWithASCII(sys_path, kVirtualDevicePathPrefix, true); |
Ben Chan | 9cda93a | 2011-05-24 13:31:04 -0700 | [diff] [blame] | 374 | } |
| 375 | // To be safe, mark it as virtual device if sys path cannot be determined. |
| 376 | return true; |
| 377 | } |
| 378 | |
Ben Chan | 2923ed7 | 2013-07-29 15:52:29 -0700 | [diff] [blame] | 379 | bool UdevDevice::IsLoopDevice() const { |
| 380 | const char *sys_path = udev_device_get_syspath(dev_); |
| 381 | if (sys_path) { |
| 382 | return StartsWithASCII(sys_path, kLoopDevicePathPrefix, true); |
| 383 | } |
| 384 | return false; |
| 385 | } |
| 386 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 387 | string UdevDevice::NativePath() const { |
Ben Chan | 4288836 | 2011-06-09 18:16:24 -0700 | [diff] [blame] | 388 | const char *sys_path = udev_device_get_syspath(dev_); |
| 389 | return sys_path ? sys_path : ""; |
| 390 | } |
| 391 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 392 | vector<string> UdevDevice::GetMountPaths() const { |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 393 | const char *device_path = udev_device_get_devnode(dev_); |
| 394 | if (device_path) { |
| 395 | return GetMountPaths(device_path); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 396 | } |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 397 | return vector<string>(); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 398 | } |
| 399 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 400 | vector<string> UdevDevice::GetMountPaths(const string& device_path) { |
Ben Chan | beefd0d | 2011-07-25 09:31:34 -0700 | [diff] [blame] | 401 | MountInfo mount_info; |
| 402 | if (mount_info.RetrieveFromCurrentProcess()) { |
| 403 | return mount_info.GetMountPaths(device_path); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 404 | } |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 405 | return vector<string>(); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 406 | } |
| 407 | |
Ben Chan | da410a0 | 2011-08-24 23:37:53 -0700 | [diff] [blame] | 408 | Disk UdevDevice::ToDisk() { |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 409 | Disk disk; |
| 410 | |
Ben Chan | 1c2d425 | 2011-06-03 13:33:49 -0700 | [diff] [blame] | 411 | disk.set_is_auto_mountable(IsAutoMountable()); |
Ben Chan | 5434262 | 2011-08-18 00:57:11 -0700 | [diff] [blame] | 412 | disk.set_is_read_only(IsAttributeTrue(kAttributeReadOnly)); |
| 413 | disk.set_is_drive(HasAttribute(kAttributeRange)); |
| 414 | disk.set_is_rotational(HasProperty(kPropertyRotationRate)); |
Ben Chan | 0255db6 | 2011-09-19 12:14:28 -0700 | [diff] [blame] | 415 | disk.set_is_hidden(IsHidden()); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 416 | disk.set_is_media_available(IsMediaAvailable()); |
Ben Chan | 490319f | 2011-05-06 14:00:42 -0700 | [diff] [blame] | 417 | disk.set_is_on_boot_device(IsOnBootDevice()); |
Ben Chan | e80629f | 2014-07-11 14:31:20 -0700 | [diff] [blame] | 418 | disk.set_is_on_removable_device(IsOnRemovableDevice()); |
Ben Chan | 9cda93a | 2011-05-24 13:31:04 -0700 | [diff] [blame] | 419 | disk.set_is_virtual(IsVirtual()); |
Ben Chan | 0a389a6 | 2011-10-03 15:02:34 -0700 | [diff] [blame] | 420 | disk.set_media_type(GetDeviceMediaType()); |
Ben Chan | da410a0 | 2011-08-24 23:37:53 -0700 | [diff] [blame] | 421 | disk.set_filesystem_type(GetPropertyFromBlkId(kPropertyBlkIdFilesystemType)); |
Ben Chan | 4288836 | 2011-06-09 18:16:24 -0700 | [diff] [blame] | 422 | disk.set_native_path(NativePath()); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 423 | |
Ben Chan | 22c0e60 | 2012-02-13 12:37:34 -0800 | [diff] [blame] | 424 | // Drive model and filesystem label may not be UTF-8 encoded, so we |
| 425 | // need to ensure that they are either set to a valid UTF-8 string or |
| 426 | // an empty string before later passed to a DBus message iterator. |
| 427 | disk.set_drive_model(EnsureUTF8String(GetProperty(kPropertyModel))); |
| 428 | disk.set_label( |
| 429 | EnsureUTF8String(GetPropertyFromBlkId(kPropertyBlkIdFilesystemLabel))); |
| 430 | |
Ben Chan | 120c11c | 2012-09-10 23:10:18 -0700 | [diff] [blame] | 431 | string vendor_id, product_id; |
| 432 | if (GetVendorAndProductId(&vendor_id, &product_id)) { |
| 433 | disk.set_vendor_id(vendor_id); |
| 434 | disk.set_product_id(product_id); |
| 435 | |
| 436 | string vendor_name, product_name; |
| 437 | USBDeviceInfo info; |
| 438 | if (info.GetVendorAndProductName(kUSBIdentifierDatabase, |
| 439 | vendor_id, product_id, |
| 440 | &vendor_name, &product_name)) { |
| 441 | disk.set_vendor_name(EnsureUTF8String(vendor_name)); |
| 442 | disk.set_product_name(EnsureUTF8String(product_name)); |
| 443 | } |
| 444 | } |
| 445 | |
Ben Chan | 0e1b550 | 2013-07-24 23:39:29 -0700 | [diff] [blame] | 446 | // TODO(benchan): Add a proper unit test when fixing crbug.com/221380. |
| 447 | string uuid_hash = base::SHA1HashString( |
| 448 | vendor_id + product_id + GetProperty(kPropertySerial) + |
| 449 | GetPropertyFromBlkId(kPropertyBlkIdFilesystemUUID)); |
| 450 | disk.set_uuid(base::HexEncode(uuid_hash.data(), uuid_hash.size())); |
| 451 | |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 452 | const char *dev_file = udev_device_get_devnode(dev_); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 453 | if (dev_file) |
| 454 | disk.set_device_file(dev_file); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 455 | |
Ben Chan | 190d3cf | 2011-07-07 09:38:48 -0700 | [diff] [blame] | 456 | vector<string> mount_paths = GetMountPaths(); |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 457 | disk.set_is_mounted(!mount_paths.empty()); |
| 458 | disk.set_mount_paths(mount_paths); |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 459 | |
Ben Chan | a169b0a | 2014-08-06 17:22:40 -0700 | [diff] [blame] | 460 | uint64_t total_size, remaining_size; |
Ben Chan | ce7ee54 | 2011-04-12 17:02:49 -0700 | [diff] [blame] | 461 | GetSizeInfo(&total_size, &remaining_size); |
| 462 | disk.set_device_capacity(total_size); |
| 463 | disk.set_bytes_remaining(remaining_size); |
| 464 | |
| 465 | return disk; |
| 466 | } |
| 467 | |
| 468 | } // namespace cros_disks |