1 // Copyright 2014 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 "components/storage_monitor/media_storage_util.h" 6 7 #include <vector> 8 9 #include "base/callback.h" 10 #include "base/files/file_util.h" 11 #include "base/logging.h" 12 #include "base/metrics/histogram.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "components/storage_monitor/removable_device_constants.h" 16 #include "components/storage_monitor/storage_monitor.h" 17 #include "content/public/browser/browser_thread.h" 18 19 using content::BrowserThread; 20 21 namespace storage_monitor { 22 23 namespace { 24 25 // MediaDeviceNotification.DeviceInfo histogram values. 26 enum DeviceInfoHistogramBuckets { 27 MASS_STORAGE_DEVICE_NAME_AND_UUID_AVAILABLE, 28 MASS_STORAGE_DEVICE_UUID_MISSING, 29 MASS_STORAGE_DEVICE_NAME_MISSING, 30 MASS_STORAGE_DEVICE_NAME_AND_UUID_MISSING, 31 MTP_STORAGE_DEVICE_NAME_AND_UUID_AVAILABLE, 32 MTP_STORAGE_DEVICE_UUID_MISSING, 33 MTP_STORAGE_DEVICE_NAME_MISSING, 34 MTP_STORAGE_DEVICE_NAME_AND_UUID_MISSING, 35 DEVICE_INFO_BUCKET_BOUNDARY 36 }; 37 38 #if !defined(OS_WIN) 39 const char kRootPath[] = "/"; 40 #endif 41 42 typedef std::vector<StorageInfo> StorageInfoList; 43 44 base::FilePath::StringType FindRemovableStorageLocationById( 45 const std::string& device_id) { 46 StorageInfoList devices = 47 StorageMonitor::GetInstance()->GetAllAvailableStorages(); 48 for (StorageInfoList::const_iterator it = devices.begin(); 49 it != devices.end(); ++it) { 50 if (it->device_id() == device_id 51 && StorageInfo::IsRemovableDevice(device_id)) 52 return it->location(); 53 } 54 return base::FilePath::StringType(); 55 } 56 57 void FilterAttachedDevicesOnFileThread(MediaStorageUtil::DeviceIdSet* devices) { 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 59 MediaStorageUtil::DeviceIdSet missing_devices; 60 61 for (MediaStorageUtil::DeviceIdSet::const_iterator it = devices->begin(); 62 it != devices->end(); 63 ++it) { 64 StorageInfo::Type type; 65 std::string unique_id; 66 if (!StorageInfo::CrackDeviceId(*it, &type, &unique_id)) { 67 missing_devices.insert(*it); 68 continue; 69 } 70 71 if (type == StorageInfo::FIXED_MASS_STORAGE || 72 type == StorageInfo::ITUNES || 73 type == StorageInfo::IPHOTO || 74 type == StorageInfo::PICASA) { 75 if (!base::PathExists(base::FilePath::FromUTF8Unsafe(unique_id))) 76 missing_devices.insert(*it); 77 continue; 78 } 79 80 if (!MediaStorageUtil::IsRemovableStorageAttached(*it)) 81 missing_devices.insert(*it); 82 } 83 84 for (MediaStorageUtil::DeviceIdSet::const_iterator it = 85 missing_devices.begin(); 86 it != missing_devices.end(); 87 ++it) { 88 devices->erase(*it); 89 } 90 } 91 92 } // namespace 93 94 // static 95 bool MediaStorageUtil::HasDcim(const base::FilePath& mount_point) { 96 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 97 98 base::FilePath::StringType dcim_dir(kDCIMDirectoryName); 99 if (!base::DirectoryExists(mount_point.Append(dcim_dir))) { 100 // Check for lowercase 'dcim' as well. 101 base::FilePath dcim_path_lower( 102 mount_point.Append(base::StringToLowerASCII(dcim_dir))); 103 if (!base::DirectoryExists(dcim_path_lower)) 104 return false; 105 } 106 return true; 107 } 108 109 // static 110 bool MediaStorageUtil::CanCreateFileSystem(const std::string& device_id, 111 const base::FilePath& path) { 112 StorageInfo::Type type; 113 if (!StorageInfo::CrackDeviceId(device_id, &type, NULL)) 114 return false; 115 116 if (type == StorageInfo::MAC_IMAGE_CAPTURE) 117 return true; 118 119 return !path.empty() && path.IsAbsolute() && !path.ReferencesParent(); 120 } 121 122 // static 123 void MediaStorageUtil::FilterAttachedDevices(DeviceIdSet* devices, 124 const base::Closure& done) { 125 if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) { 126 FilterAttachedDevicesOnFileThread(devices); 127 done.Run(); 128 return; 129 } 130 BrowserThread::PostTaskAndReply(BrowserThread::FILE, 131 FROM_HERE, 132 base::Bind(&FilterAttachedDevicesOnFileThread, 133 devices), 134 done); 135 } 136 137 // TODO(kmadhusu) Write unit tests for GetDeviceInfoFromPath(). 138 bool MediaStorageUtil::GetDeviceInfoFromPath(const base::FilePath& path, 139 StorageInfo* device_info, 140 base::FilePath* relative_path) { 141 DCHECK(device_info); 142 DCHECK(relative_path); 143 144 if (!path.IsAbsolute()) 145 return false; 146 147 StorageInfo info; 148 StorageMonitor* monitor = StorageMonitor::GetInstance(); 149 bool found_device = monitor->GetStorageInfoForPath(path, &info); 150 151 if (found_device && StorageInfo::IsRemovableDevice(info.device_id())) { 152 base::FilePath sub_folder_path; 153 base::FilePath device_path(info.location()); 154 if (path != device_path) { 155 bool success = device_path.AppendRelativePath(path, &sub_folder_path); 156 DCHECK(success); 157 } 158 159 *device_info = info; 160 *relative_path = sub_folder_path; 161 return true; 162 } 163 164 // On Posix systems, there's one root so any absolute path could be valid. 165 // TODO(gbillock): Delete this stanza? Posix systems should have the root 166 // volume information. If not, we should move the below into the 167 // right GetStorageInfoForPath implementations. 168 #if !defined(OS_POSIX) 169 if (!found_device) 170 return false; 171 #endif 172 173 // Handle non-removable devices. Note: this is just overwriting 174 // good values from StorageMonitor. 175 // TODO(gbillock): Make sure return values from that class are definitive, 176 // and don't do this here. 177 info.set_device_id( 178 StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE, 179 path.AsUTF8Unsafe())); 180 *device_info = info; 181 *relative_path = base::FilePath(); 182 return true; 183 } 184 185 // static 186 base::FilePath MediaStorageUtil::FindDevicePathById( 187 const std::string& device_id) { 188 StorageInfo::Type type; 189 std::string unique_id; 190 if (!StorageInfo::CrackDeviceId(device_id, &type, &unique_id)) 191 return base::FilePath(); 192 193 if (type == StorageInfo::FIXED_MASS_STORAGE || 194 type == StorageInfo::ITUNES || 195 type == StorageInfo::IPHOTO || 196 type == StorageInfo::PICASA) { 197 // For this type, the unique_id is the path. 198 return base::FilePath::FromUTF8Unsafe(unique_id); 199 } 200 201 // For ImageCapture, the synthetic filesystem will be rooted at a fake 202 // top-level directory which is the device_id. 203 if (type == StorageInfo::MAC_IMAGE_CAPTURE) { 204 #if !defined(OS_WIN) 205 return base::FilePath(kRootPath + device_id); 206 #endif 207 } 208 209 DCHECK(type == StorageInfo::MTP_OR_PTP || 210 type == StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM || 211 type == StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM); 212 return base::FilePath(FindRemovableStorageLocationById(device_id)); 213 } 214 215 // static 216 void MediaStorageUtil::RecordDeviceInfoHistogram( 217 bool mass_storage, 218 const std::string& device_uuid, 219 const base::string16& device_label) { 220 unsigned int event_number = 0; 221 if (!mass_storage) 222 event_number = 4; 223 224 if (device_label.empty()) 225 event_number += 2; 226 227 if (device_uuid.empty()) 228 event_number += 1; 229 enum DeviceInfoHistogramBuckets event = 230 static_cast<enum DeviceInfoHistogramBuckets>(event_number); 231 if (event >= DEVICE_INFO_BUCKET_BOUNDARY) { 232 NOTREACHED(); 233 return; 234 } 235 UMA_HISTOGRAM_ENUMERATION("MediaDeviceNotifications.DeviceInfo", event, 236 DEVICE_INFO_BUCKET_BOUNDARY); 237 } 238 239 bool MediaStorageUtil::IsRemovableStorageAttached(const std::string& id) { 240 StorageInfoList devices = 241 StorageMonitor::GetInstance()->GetAllAvailableStorages(); 242 for (StorageInfoList::const_iterator it = devices.begin(); 243 it != devices.end(); ++it) { 244 if (StorageInfo::IsRemovableDevice(id) && it->device_id() == id) 245 return true; 246 } 247 return false; 248 } 249 250 } // namespace storage_monitor 251