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