OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // MediaDeviceNotificationsLinux implementation. | 5 // RemovableDeviceNotificationsLinux implementation. |
6 | 6 |
7 #include "chrome/browser/media_gallery/media_device_notifications_linux.h" | 7 #include "chrome/browser/media_gallery/removable_device_notifications_linux.h" |
8 | 8 |
9 #include <libudev.h> | 9 #include <libudev.h> |
10 #include <mntent.h> | 10 #include <mntent.h> |
11 #include <stdio.h> | 11 #include <stdio.h> |
12 | 12 |
13 #include <vector> | 13 #include <list> |
14 | 14 |
15 #include "base/bind.h" | 15 #include "base/bind.h" |
16 #include "base/file_path.h" | 16 #include "base/file_path.h" |
17 #include "base/memory/scoped_generic_obj.h" | 17 #include "base/memory/scoped_generic_obj.h" |
18 #include "base/metrics/histogram.h" | 18 #include "base/metrics/histogram.h" |
19 #include "base/stl_util.h" | 19 #include "base/stl_util.h" |
20 #include "base/string_number_conversions.h" | 20 #include "base/string_number_conversions.h" |
21 #include "base/string_util.h" | 21 #include "base/string_util.h" |
22 #include "base/stringprintf.h" | 22 #include "base/stringprintf.h" |
23 #include "base/system_monitor/system_monitor.h" | 23 #include "base/system_monitor/system_monitor.h" |
24 #include "base/utf_string_conversions.h" | 24 #include "base/utf_string_conversions.h" |
25 #include "chrome/browser/media_gallery/media_device_notifications_utils.h" | 25 #include "chrome/browser/media_gallery/media_device_notifications_utils.h" |
26 #include "chrome/browser/media_gallery/media_gallery_constants.h" | 26 #include "chrome/browser/media_gallery/media_gallery_constants.h" |
27 #include "chrome/browser/media_gallery/media_storage_util.h" | 27 #include "chrome/browser/media_gallery/media_storage_util.h" |
28 | 28 |
29 namespace chrome { | 29 namespace chrome { |
30 | 30 |
31 using base::SystemMonitor; | 31 using base::SystemMonitor; |
32 using content::BrowserThread; | 32 using content::BrowserThread; |
33 | 33 |
34 namespace { | 34 namespace { |
35 | 35 |
36 static RemovableDeviceNotificationsLinux* | |
37 g_removable_device_notifications_linux = NULL; | |
38 | |
36 // List of file systems we care about. | 39 // List of file systems we care about. |
37 const char* const kKnownFileSystems[] = { | 40 const char* const kKnownFileSystems[] = { |
38 "ext2", | 41 "ext2", |
39 "ext3", | 42 "ext3", |
40 "ext4", | 43 "ext4", |
41 "fat", | 44 "fat", |
42 "hfsplus", | 45 "hfsplus", |
43 "iso9660", | 46 "iso9660", |
44 "msdos", | 47 "msdos", |
45 "ntfs", | 48 "ntfs", |
46 "udf", | 49 "udf", |
47 "vfat", | 50 "vfat", |
48 }; | 51 }; |
49 | 52 |
50 // udev device property constants. | 53 // udev device property constants. |
54 const char kBlockSubsystemKey[] = "block"; | |
51 const char kDevName[] = "DEVNAME"; | 55 const char kDevName[] = "DEVNAME"; |
56 const char kDiskDeviceTypeKey[] = "disk"; | |
52 const char kFsUUID[] = "ID_FS_UUID"; | 57 const char kFsUUID[] = "ID_FS_UUID"; |
53 const char kLabel[] = "ID_FS_LABEL"; | 58 const char kLabel[] = "ID_FS_LABEL"; |
54 const char kModel[] = "ID_MODEL"; | 59 const char kModel[] = "ID_MODEL"; |
55 const char kModelID[] = "ID_MODEL_ID"; | 60 const char kModelID[] = "ID_MODEL_ID"; |
61 const char kRemovableSysAttr[] = "removable"; | |
56 const char kSerial[] = "ID_SERIAL"; | 62 const char kSerial[] = "ID_SERIAL"; |
57 const char kSerialShort[] = "ID_SERIAL_SHORT"; | 63 const char kSerialShort[] = "ID_SERIAL_SHORT"; |
58 const char kVendor[] = "ID_VENDOR"; | 64 const char kVendor[] = "ID_VENDOR"; |
59 const char kVendorID[] = "ID_VENDOR_ID"; | 65 const char kVendorID[] = "ID_VENDOR_ID"; |
60 | 66 |
61 // Device mount point details. | 67 // (mount point, mount device) |
62 struct MountPointEntryInfo { | 68 // A mapping from mount point to mount device, as extracted from the mtab |
63 std::string mount_point; | 69 // file. |
64 int entry_pos; | 70 typedef std::map<FilePath, FilePath> MountPointDeviceMap; |
65 }; | 71 |
72 // Reads mtab file entries into |mtab|. | |
73 void ReadMtab(const FilePath& mtab_path, | |
74 const std::set<std::string>& interesting_file_systems, | |
75 MountPointDeviceMap* mtab) { | |
76 mtab->clear(); | |
77 | |
78 FILE* fp = setmntent(mtab_path.value().c_str(), "r"); | |
79 if (!fp) | |
80 return; | |
81 | |
82 mntent entry; | |
83 char buf[512]; | |
84 | |
85 // We return the same device mounted to multiple locations, but hide | |
86 // devices that have been mounted over. | |
87 while (getmntent_r(fp, &entry, buf, sizeof(buf))) { | |
88 // We only care about real file systems. | |
89 if (!ContainsKey(interesting_file_systems, entry.mnt_type)) | |
90 continue; | |
91 | |
92 (*mtab)[FilePath(entry.mnt_dir)] = FilePath(entry.mnt_fsname); | |
93 } | |
94 endmntent(fp); | |
95 } | |
66 | 96 |
67 // ScopedGenericObj functor for UdevObjectRelease(). | 97 // ScopedGenericObj functor for UdevObjectRelease(). |
68 class ScopedReleaseUdevObject { | 98 class ScopedReleaseUdevObject { |
69 public: | 99 public: |
70 void operator()(struct udev* udev) const { | 100 void operator()(struct udev* udev) const { |
71 udev_unref(udev); | 101 udev_unref(udev); |
72 } | 102 } |
73 }; | 103 }; |
74 typedef ScopedGenericObj<struct udev*, | 104 typedef ScopedGenericObj<struct udev*, |
75 ScopedReleaseUdevObject> ScopedUdevObject; | 105 ScopedReleaseUdevObject> ScopedUdevObject; |
(...skipping 11 matching lines...) Expand all Loading... | |
87 // Wrapper function for udev_device_get_property_value() that also checks for | 117 // Wrapper function for udev_device_get_property_value() that also checks for |
88 // valid but empty values. | 118 // valid but empty values. |
89 std::string GetUdevDevicePropertyValue(struct udev_device* udev_device, | 119 std::string GetUdevDevicePropertyValue(struct udev_device* udev_device, |
90 const char* key) { | 120 const char* key) { |
91 const char* value = udev_device_get_property_value(udev_device, key); | 121 const char* value = udev_device_get_property_value(udev_device, key); |
92 if (!value) | 122 if (!value) |
93 return std::string(); | 123 return std::string(); |
94 return (strlen(value) > 0) ? value : std::string(); | 124 return (strlen(value) > 0) ? value : std::string(); |
95 } | 125 } |
96 | 126 |
127 // Construct a device id using label or manufacturer (vendor and model) details. | |
128 std::string MakeDeviceUniqueId(struct udev_device* device) { | |
129 std::string uuid = GetUdevDevicePropertyValue(device, kFsUUID); | |
130 if (!uuid.empty()) | |
131 return kFSUniqueIdPrefix + uuid; | |
132 | |
133 // If one of the vendor, model, serial information is missing, its value | |
134 // in the string is empty. | |
135 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialShortInfo | |
136 // E.g.: VendorModelSerial:Kn:DataTravel_12.10:8000000000006CB02CDB | |
137 std::string vendor = GetUdevDevicePropertyValue(device, kVendorID); | |
138 std::string model = GetUdevDevicePropertyValue(device, kModelID); | |
139 std::string serial_short = GetUdevDevicePropertyValue(device, | |
140 kSerialShort); | |
141 if (vendor.empty() && model.empty() && serial_short.empty()) | |
142 return std::string(); | |
143 | |
144 return base::StringPrintf("%s%s%s%s%s%s", | |
145 kVendorModelSerialPrefix, | |
146 vendor.c_str(), kNonSpaceDelim, | |
147 model.c_str(), kNonSpaceDelim, | |
148 serial_short.c_str()); | |
149 } | |
150 | |
97 // Get the device information using udev library. | 151 // Get the device information using udev library. |
98 // On success, returns true and fill in |id| and |name|. | 152 // On success, returns true and fill in |unique_id|, |name|, and |removable|. |
99 bool GetDeviceInfo(const std::string& device_path, std::string* id, | 153 bool GetDeviceInfo(const FilePath& device_path, std::string* unique_id, |
100 string16* name) { | 154 string16* name, bool* removable) { |
101 DCHECK(!device_path.empty()); | 155 DCHECK(!device_path.empty()); |
102 | 156 |
103 ScopedUdevObject udev_obj(udev_new()); | 157 ScopedUdevObject udev_obj(udev_new()); |
104 if (!udev_obj.get()) | 158 if (!udev_obj.get()) |
105 return false; | 159 return false; |
106 | 160 |
107 struct stat device_stat; | 161 struct stat device_stat; |
108 if (stat(device_path.c_str(), &device_stat) < 0) | 162 if (stat(device_path.value().c_str(), &device_stat) < 0) |
109 return false; | 163 return false; |
110 | 164 |
111 char device_type; | 165 char device_type; |
112 if (S_ISCHR(device_stat.st_mode)) | 166 if (S_ISCHR(device_stat.st_mode)) |
113 device_type = 'c'; | 167 device_type = 'c'; |
114 else if (S_ISBLK(device_stat.st_mode)) | 168 else if (S_ISBLK(device_stat.st_mode)) |
115 device_type = 'b'; | 169 device_type = 'b'; |
116 else | 170 else |
117 return false; // Not a supported type. | 171 return false; // Not a supported type. |
118 | 172 |
(...skipping 21 matching lines...) Expand all Loading... | |
140 else if (model_name.empty()) | 194 else if (model_name.empty()) |
141 device_label = vendor_name; | 195 device_label = vendor_name; |
142 else | 196 else |
143 device_label = vendor_name + kSpaceDelim + model_name; | 197 device_label = vendor_name + kSpaceDelim + model_name; |
144 } | 198 } |
145 | 199 |
146 if (IsStringUTF8(device_label)) | 200 if (IsStringUTF8(device_label)) |
147 *name = UTF8ToUTF16(device_label); | 201 *name = UTF8ToUTF16(device_label); |
148 } | 202 } |
149 | 203 |
150 // Construct a device id using label or manufacturer (vendor and model) | 204 if (unique_id) { |
151 // details. | 205 *unique_id = MakeDeviceUniqueId(device); |
152 if (id) { | 206 if (unique_id->empty()) |
153 std::string unique_id = GetUdevDevicePropertyValue(device, kFsUUID); | 207 return false; |
154 if (unique_id.empty()) { | 208 } |
155 // If one of the vendor, model, serial information is missing, its value | |
156 // in the string is empty. | |
157 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialShortInfo | |
158 // E.g.: VendorModelSerial:Kn:DataTravel_12.10:8000000000006CB02CDB | |
159 std::string vendor = GetUdevDevicePropertyValue(device, kVendorID); | |
160 std::string model = GetUdevDevicePropertyValue(device, kModelID); | |
161 std::string serial_short = GetUdevDevicePropertyValue(device, | |
162 kSerialShort); | |
163 if (vendor.empty() && model.empty() && serial_short.empty()) | |
164 return false; | |
165 | 209 |
166 unique_id = base::StringPrintf("%s%s%s%s%s%s", | 210 if (removable) { |
167 kVendorModelSerialPrefix, | 211 // |parent_device| is owned by |device| and does not need to be cleaned up. |
168 vendor.c_str(), | 212 struct udev_device* parent_device = |
169 kNonSpaceDelim, | 213 udev_device_get_parent_with_subsystem_devtype(device, |
170 model.c_str(), | 214 kBlockSubsystemKey, |
171 kNonSpaceDelim, | 215 kDiskDeviceTypeKey); |
172 serial_short.c_str()); | 216 const char* value = udev_device_get_sysattr_value(parent_device, |
Lei Zhang
2012/08/28 07:55:30
Try this on |device| first, in case |device| is /d
vandebo (ex-Chrome)
2012/08/28 18:59:30
Done.
| |
173 } else { | 217 kRemovableSysAttr); |
174 unique_id = kFSUniqueIdPrefix + unique_id; | 218 *removable = (value && atoi(value) == 1); |
175 } | |
176 *id = MediaStorageUtil::MakeDeviceId( | |
177 MediaStorageUtil::USB_MASS_STORAGE_WITH_DCIM, unique_id); | |
178 } | 219 } |
179 return true; | 220 return true; |
180 } | 221 } |
181 | 222 |
182 } // namespace | 223 } // namespace |
183 | 224 |
184 MediaDeviceNotificationsLinux::MediaDeviceNotificationsLinux( | 225 RemovableDeviceNotificationsLinux::RemovableDeviceNotificationsLinux( |
185 const FilePath& path) | 226 const FilePath& path) |
186 : initialized_(false), | 227 : initialized_(false), |
187 mtab_path_(path), | 228 mtab_path_(path), |
188 get_device_info_func_(&GetDeviceInfo) { | 229 get_device_info_func_(&GetDeviceInfo) { |
230 DCHECK(!g_removable_device_notifications_linux); | |
231 g_removable_device_notifications_linux = this; | |
189 } | 232 } |
190 | 233 |
191 MediaDeviceNotificationsLinux::MediaDeviceNotificationsLinux( | 234 RemovableDeviceNotificationsLinux::RemovableDeviceNotificationsLinux( |
192 const FilePath& path, | 235 const FilePath& path, |
193 GetDeviceInfoFunc get_device_info_func) | 236 GetDeviceInfoFunc get_device_info_func) |
194 : initialized_(false), | 237 : initialized_(false), |
195 mtab_path_(path), | 238 mtab_path_(path), |
196 get_device_info_func_(get_device_info_func) { | 239 get_device_info_func_(get_device_info_func) { |
240 DCHECK(!g_removable_device_notifications_linux); | |
241 g_removable_device_notifications_linux = this; | |
197 } | 242 } |
198 | 243 |
199 MediaDeviceNotificationsLinux::~MediaDeviceNotificationsLinux() { | 244 RemovableDeviceNotificationsLinux::~RemovableDeviceNotificationsLinux() { |
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
246 DCHECK_EQ(this, g_removable_device_notifications_linux); | |
247 g_removable_device_notifications_linux = NULL; | |
201 } | 248 } |
202 | 249 |
203 void MediaDeviceNotificationsLinux::Init() { | 250 // static |
251 RemovableDeviceNotificationsLinux* | |
252 RemovableDeviceNotificationsLinux::GetInstance() { | |
253 return g_removable_device_notifications_linux; | |
254 } | |
255 | |
256 void RemovableDeviceNotificationsLinux::Init() { | |
204 DCHECK(!mtab_path_.empty()); | 257 DCHECK(!mtab_path_.empty()); |
205 | 258 |
206 // Put |kKnownFileSystems| in std::set to get O(log N) access time. | 259 // Put |kKnownFileSystems| in std::set to get O(log N) access time. |
207 for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i) | 260 for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i) |
208 known_file_systems_.insert(kKnownFileSystems[i]); | 261 known_file_systems_.insert(kKnownFileSystems[i]); |
209 | 262 |
210 BrowserThread::PostTask( | 263 BrowserThread::PostTask( |
211 BrowserThread::FILE, FROM_HERE, | 264 BrowserThread::FILE, FROM_HERE, |
212 base::Bind(&MediaDeviceNotificationsLinux::InitOnFileThread, this)); | 265 base::Bind(&RemovableDeviceNotificationsLinux::InitOnFileThread, this)); |
213 } | 266 } |
214 | 267 |
215 void MediaDeviceNotificationsLinux::OnFilePathChanged(const FilePath& path, | 268 FilePath RemovableDeviceNotificationsLinux::GetDeviceMountPoint( |
216 bool error) { | 269 const std::string& device_id) const { |
270 | |
271 MediaStorageUtil::Type type; | |
272 MediaStorageUtil::CrackDeviceId(device_id, &type, NULL); | |
273 if (type == MediaStorageUtil::USB_MTP) | |
Lei Zhang
2012/08/28 07:55:30
Do you think we'll be adding more storage types in
vandebo (ex-Chrome)
2012/08/28 18:59:30
Added a DCHECK
Lei Zhang
2012/08/28 20:49:04
I was thinking of:
switch (type) {
case MediaSt
vandebo (ex-Chrome)
2012/08/28 21:25:26
It seems a bit overkill for the protection that we
| |
274 return FilePath(); | |
275 | |
276 FilePath mount_device; | |
277 for (MountMap::const_iterator it = mount_info_map_.begin(); | |
278 it != mount_info_map_.end(); | |
279 ++it) { | |
280 if (it->second.device_id == device_id) { | |
281 mount_device = it->second.mount_device; | |
282 break; | |
283 } | |
284 } | |
285 if (mount_device.empty()) | |
286 return mount_device; | |
287 | |
288 const ReferencedMountPoint& referenced_info = | |
289 mount_priority_map_.find(mount_device)->second; | |
290 for (ReferencedMountPoint::const_iterator it = referenced_info.begin(); | |
291 it != referenced_info.end(); | |
292 ++it) { | |
293 if (it->second) | |
294 return it->first; | |
295 } | |
296 // If none of them are default, just return the first. | |
297 return FilePath( | |
298 mount_priority_map_.find(mount_device)->second.begin()->first); | |
Lei Zhang
2012/08/28 07:55:30
Isn't this just: referenced_info.begin()->first ?
vandebo (ex-Chrome)
2012/08/28 18:59:30
Done.
| |
299 } | |
300 | |
301 std::string RemovableDeviceNotificationsLinux::GetDeviceIdForPath( | |
302 const FilePath& path, FilePath* mount_point) const { | |
303 if (!path.IsAbsolute()) | |
304 return std::string(); | |
305 | |
306 FilePath current = path; | |
307 for (current = path; | |
Lei Zhang
2012/08/28 07:55:30
I think you want a while loop here.
vandebo (ex-Chrome)
2012/08/28 18:59:30
Done.
| |
308 mount_info_map_.find(current) == mount_info_map_.end() && | |
309 current != current.DirName(); | |
310 current = current.DirName()) {} | |
311 | |
312 if (mount_info_map_.find(current) == mount_info_map_.end()) | |
Lei Zhang
2012/08/28 07:55:30
Save the iterator so you don't have to call find()
vandebo (ex-Chrome)
2012/08/28 18:59:30
Done.
| |
313 return std::string(); | |
314 | |
315 if (mount_point) { | |
316 *mount_point = FilePath(); | |
317 current.AppendRelativePath(path, mount_point); | |
Lei Zhang
2012/08/28 07:55:30
I may be understanding this wrong, but this doesn'
vandebo (ex-Chrome)
2012/08/28 18:59:30
Indeed. The caller will also want the relative pa
| |
318 } | |
319 | |
320 return mount_info_map_.find(current)->second.device_id; | |
321 } | |
322 | |
323 void RemovableDeviceNotificationsLinux::OnFilePathChanged(const FilePath& path, | |
324 bool error) { | |
217 if (path != mtab_path_) { | 325 if (path != mtab_path_) { |
218 // This cannot happen unless FilePathWatcher is buggy. Just ignore this | 326 // This cannot happen unless FilePathWatcher is buggy. Just ignore this |
219 // notification and do nothing. | 327 // notification and do nothing. |
220 NOTREACHED(); | 328 NOTREACHED(); |
221 return; | 329 return; |
222 } | 330 } |
223 if (error) { | 331 if (error) { |
224 LOG(ERROR) << "Error watching " << mtab_path_.value(); | 332 LOG(ERROR) << "Error watching " << mtab_path_.value(); |
225 return; | 333 return; |
226 } | 334 } |
227 | 335 |
228 UpdateMtab(); | 336 UpdateMtab(); |
229 } | 337 } |
230 | 338 |
231 void MediaDeviceNotificationsLinux::InitOnFileThread() { | 339 void RemovableDeviceNotificationsLinux::InitOnFileThread() { |
232 DCHECK(!initialized_); | 340 DCHECK(!initialized_); |
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
234 initialized_ = true; | 342 initialized_ = true; |
235 | 343 |
236 // The callback passed to Watch() has to be unretained. Otherwise | 344 // The callback passed to Watch() has to be unretained. Otherwise |
237 // MediaDeviceNotificationsLinux will live longer than expected, and | 345 // RemovableDeviceNotificationsLinux will live longer than expected, and |
238 // FilePathWatcher will get in trouble at shutdown time. | 346 // FilePathWatcher will get in trouble at shutdown time. |
239 bool ret = file_watcher_.Watch( | 347 bool ret = file_watcher_.Watch( |
240 mtab_path_, | 348 mtab_path_, |
241 base::Bind(&MediaDeviceNotificationsLinux::OnFilePathChanged, | 349 base::Bind(&RemovableDeviceNotificationsLinux::OnFilePathChanged, |
242 base::Unretained(this))); | 350 base::Unretained(this))); |
243 if (!ret) { | 351 if (!ret) { |
244 LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed"; | 352 LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed"; |
245 return; | 353 return; |
246 } | 354 } |
247 | 355 |
248 UpdateMtab(); | 356 UpdateMtab(); |
249 } | 357 } |
250 | 358 |
251 void MediaDeviceNotificationsLinux::UpdateMtab() { | 359 void RemovableDeviceNotificationsLinux::UpdateMtab() { |
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
253 | 361 |
254 MountPointDeviceMap new_mtab; | 362 MountPointDeviceMap new_mtab; |
255 ReadMtab(&new_mtab); | 363 ReadMtab(mtab_path_, known_file_systems_, &new_mtab); |
256 | 364 |
257 // Check existing mtab entries for unaccounted mount points. | 365 // Check existing mtab entries for unaccounted mount points. |
258 // These mount points must have been removed in the new mtab. | 366 // These mount points must have been removed in the new mtab. |
259 std::vector<std::string> mount_points_to_erase; | 367 std::list<FilePath> mount_points_to_erase; |
368 std::list<FilePath> multiple_mounted_devices_needing_reattachment; | |
260 for (MountMap::const_iterator old_iter = mount_info_map_.begin(); | 369 for (MountMap::const_iterator old_iter = mount_info_map_.begin(); |
261 old_iter != mount_info_map_.end(); ++old_iter) { | 370 old_iter != mount_info_map_.end(); ++old_iter) { |
262 const std::string& mount_point = old_iter->first; | 371 const FilePath& mount_point = old_iter->first; |
263 const std::string& mount_device = old_iter->second.mount_device; | 372 const FilePath& mount_device = old_iter->second.mount_device; |
264 MountPointDeviceMap::iterator new_iter = new_mtab.find(mount_point); | 373 MountPointDeviceMap::iterator new_iter = new_mtab.find(mount_point); |
265 // |mount_point| not in |new_mtab| or |mount_device| is no longer mounted at | 374 // |mount_point| not in |new_mtab| or |mount_device| is no longer mounted at |
266 // |mount_point|. | 375 // |mount_point|. |
267 if (new_iter == new_mtab.end() || (new_iter->second != mount_device)) { | 376 if (new_iter == new_mtab.end() || (new_iter->second != mount_device)) { |
268 RemoveOldDevice(old_iter->second.device_id); | 377 MountPriorityMap::iterator priority = |
378 mount_priority_map_.find(mount_device); | |
379 DCHECK(priority != mount_priority_map_.end()); | |
380 ReferencedMountPoint::const_iterator has_priority = | |
381 priority->second.find(mount_point); | |
382 if (old_iter->second.has_dcim) { | |
383 DCHECK(has_priority != priority->second.end()); | |
384 if (has_priority->second) | |
385 RemoveMediaMount(old_iter->second.device_id); | |
386 if (mount_priority_map_.find(mount_device)->second.size() > 1) | |
Lei Zhang
2012/08/28 07:55:30
no need to call find() again? ditto below.
vandebo (ex-Chrome)
2012/08/28 18:59:30
Done.
| |
387 multiple_mounted_devices_needing_reattachment.push_back(mount_device); | |
388 } | |
389 priority->second.erase(mount_point); | |
390 if (mount_priority_map_.find(mount_device)->second.empty()) | |
391 mount_priority_map_.erase(mount_device); | |
269 mount_points_to_erase.push_back(mount_point); | 392 mount_points_to_erase.push_back(mount_point); |
270 } | 393 } |
271 } | 394 } |
395 | |
272 // Erase the |mount_info_map_| entries afterwards. Erasing in the loop above | 396 // Erase the |mount_info_map_| entries afterwards. Erasing in the loop above |
273 // using the iterator is slightly more efficient, but more tricky, since | 397 // using the iterator is slightly more efficient, but more tricky, since |
274 // calling std::map::erase() on an iterator invalidates it. | 398 // calling std::map::erase() on an iterator invalidates it. |
275 for (size_t i = 0; i < mount_points_to_erase.size(); ++i) | 399 for (std::list<FilePath>::const_iterator it = mount_points_to_erase.begin(); |
276 mount_info_map_.erase(mount_points_to_erase[i]); | 400 it != mount_points_to_erase.end(); |
401 ++it) { | |
402 mount_info_map_.erase(*it); | |
403 } | |
404 | |
405 // For any multiply mounted device where the mount that we had notified | |
406 // got detached, send a notification of attachment for one of the other | |
407 // mount points. | |
408 for (std::list<FilePath>::const_iterator it = | |
409 multiple_mounted_devices_needing_reattachment.begin(); | |
410 it != multiple_mounted_devices_needing_reattachment.end(); | |
411 ++it) { | |
412 const FilePath& mount_point = mount_priority_map_[*it].begin()->first; | |
Lei Zhang
2012/08/28 07:55:30
If there was ever a bug in the code, you may end u
vandebo (ex-Chrome)
2012/08/28 18:59:30
I can see how this might be a little fragile. Add
Lei Zhang
2012/08/28 20:49:04
Reading from a map using operator[] still sucks, b
vandebo (ex-Chrome)
2012/08/28 21:25:26
I mostly agree with what you've said. But what wo
Lei Zhang
2012/08/28 23:22:33
We can't fix everyone's code, but we can at least
vandebo (ex-Chrome)
2012/08/29 00:20:12
Changed all instances (here and AddNewMount) of op
| |
413 mount_priority_map_[*it].begin()->second = true; | |
414 DCHECK(mount_info_map_[mount_point].has_dcim); | |
415 base::SystemMonitor::Get()->ProcessRemovableStorageAttached( | |
416 mount_info_map_[mount_point].device_id, | |
417 mount_info_map_[mount_point].device_name, | |
418 mount_point.value()); | |
419 } | |
277 | 420 |
278 // Check new mtab entries against existing ones. | 421 // Check new mtab entries against existing ones. |
279 for (MountPointDeviceMap::iterator new_iter = new_mtab.begin(); | 422 for (MountPointDeviceMap::iterator new_iter = new_mtab.begin(); |
280 new_iter != new_mtab.end(); ++new_iter) { | 423 new_iter != new_mtab.end(); ++new_iter) { |
281 const std::string& mount_point = new_iter->first; | 424 const FilePath& mount_point = new_iter->first; |
282 const std::string& mount_device = new_iter->second; | 425 const FilePath& mount_device = new_iter->second; |
283 MountMap::iterator old_iter = mount_info_map_.find(mount_point); | 426 MountMap::iterator old_iter = mount_info_map_.find(mount_point); |
284 if (old_iter == mount_info_map_.end() || | 427 if (old_iter == mount_info_map_.end() || |
285 old_iter->second.mount_device != mount_device) { | 428 old_iter->second.mount_device != mount_device) { |
286 // New mount point found or an existing mount point found with a new | 429 // New mount point found or an existing mount point found with a new |
287 // device. | 430 // device. |
288 CheckAndAddMediaDevice(mount_device, mount_point); | 431 AddNewMount(mount_device, mount_point); |
289 } | 432 } |
290 } | 433 } |
291 } | 434 } |
292 | 435 |
293 void MediaDeviceNotificationsLinux::ReadMtab(MountPointDeviceMap* mtab) { | 436 void RemovableDeviceNotificationsLinux::AddNewMount( |
294 FILE* fp = setmntent(mtab_path_.value().c_str(), "r"); | 437 const FilePath& mount_device, const FilePath& mount_point) { |
295 if (!fp) | 438 if (mount_priority_map_.find(mount_device) != mount_priority_map_.end()) { |
Lei Zhang
2012/08/28 07:55:30
You can easily save the iterator from find() and n
vandebo (ex-Chrome)
2012/08/28 18:59:30
Done.
| |
439 const FilePath& other_mount_point = | |
440 mount_priority_map_[mount_device].begin()->first; | |
441 mount_priority_map_[mount_device][mount_point] = false; | |
442 mount_info_map_[mount_point] = mount_info_map_[other_mount_point]; | |
296 return; | 443 return; |
444 } | |
297 | 445 |
298 mntent entry; | 446 std::string unique_id; |
299 char buf[512]; | |
300 | |
301 // Keep track of mount point entry positions in mtab file. | |
302 int entry_pos = 0; | |
303 | |
304 // Helper map to store the device mount point details. | |
305 // (mount device, MountPointEntryInfo) | |
306 typedef std::map<std::string, MountPointEntryInfo> DeviceMap; | |
307 DeviceMap device_map; | |
308 while (getmntent_r(fp, &entry, buf, sizeof(buf))) { | |
309 // We only care about real file systems. | |
310 if (!ContainsKey(known_file_systems_, entry.mnt_type)) | |
311 continue; | |
312 | |
313 // Add entries, but overwrite entries for the same mount device. Keep track | |
314 // of the entry positions in |entry_info| and use that below to resolve | |
315 // multiple devices mounted at the same mount point. | |
316 MountPointEntryInfo entry_info; | |
317 entry_info.mount_point = entry.mnt_dir; | |
318 entry_info.entry_pos = entry_pos++; | |
319 device_map[entry.mnt_fsname] = entry_info; | |
320 } | |
321 endmntent(fp); | |
322 | |
323 // Helper map to store mount point entries. | |
324 // (mount point, entry position in mtab file) | |
325 typedef std::map<std::string, int> MountPointsInfoMap; | |
326 MountPointsInfoMap mount_points_info_map; | |
327 MountPointDeviceMap& new_mtab = *mtab; | |
328 for (DeviceMap::const_iterator device_it = device_map.begin(); | |
329 device_it != device_map.end(); | |
330 ++device_it) { | |
331 // Add entries, but overwrite entries for the same mount point. Keep track | |
332 // of the entry positions and use that information to resolve multiple | |
333 // devices mounted at the same mount point. | |
334 const std::string& mount_device = device_it->first; | |
335 const std::string& mount_point = device_it->second.mount_point; | |
336 const int entry_pos = device_it->second.entry_pos; | |
337 MountPointDeviceMap::iterator new_it = new_mtab.find(mount_point); | |
338 MountPointsInfoMap::iterator mount_point_info_map_it = | |
339 mount_points_info_map.find(mount_point); | |
340 | |
341 // New mount point entry found or there is already a device mounted at | |
342 // |mount_point| and the existing mount entry is older than the current one. | |
343 if (new_it == new_mtab.end() || | |
344 (mount_point_info_map_it != mount_points_info_map.end() && | |
345 mount_point_info_map_it->second < entry_pos)) { | |
346 new_mtab[mount_point] = mount_device; | |
347 mount_points_info_map[mount_point] = entry_pos; | |
348 } | |
349 } | |
350 } | |
351 | |
352 void MediaDeviceNotificationsLinux::CheckAndAddMediaDevice( | |
353 const std::string& mount_device, | |
354 const std::string& mount_point) { | |
355 if (!IsMediaDevice(mount_point)) | |
356 return; | |
357 | |
358 std::string id; | |
359 string16 name; | 447 string16 name; |
360 bool result = get_device_info_func_(mount_device, &id, &name); | 448 bool removable; |
449 bool result = get_device_info_func_(mount_device, &unique_id, &name, | |
450 &removable); | |
361 | 451 |
362 // Keep track of GetDeviceInfo result, to see how often we fail to get device | 452 // Keep track of GetDeviceInfo result, to see how often we fail to get device |
363 // details. | 453 // details. |
364 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_info_available", | 454 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_info_available", |
365 result); | 455 result); |
366 if (!result) | 456 if (!result) |
367 return; | 457 return; |
368 | 458 |
369 // Keep track of device uuid, to see how often we receive empty values. | 459 // Keep track of device uuid, to see how often we receive empty values. |
370 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_uuid_available", | 460 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_uuid_available", |
371 !id.empty()); | 461 !unique_id.empty()); |
372 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_name_available", | 462 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_name_available", |
373 !name.empty()); | 463 !name.empty()); |
374 | 464 |
375 if (id.empty() || name.empty()) | 465 if (unique_id.empty() || name.empty()) |
376 return; | 466 return; |
377 | 467 |
378 MountDeviceAndId mount_device_and_id; | 468 bool has_dcim = IsMediaDevice(mount_point.value()); |
379 mount_device_and_id.mount_device = mount_device; | 469 MediaStorageUtil::Type type; |
380 mount_device_and_id.device_id = id; | 470 if (removable) { |
381 mount_info_map_[mount_point] = mount_device_and_id; | 471 if (has_dcim) { |
472 type = MediaStorageUtil::USB_MASS_STORAGE_WITH_DCIM; | |
473 } else { | |
474 type = MediaStorageUtil::USB_MASS_STORAGE_NO_DCIM; | |
475 } | |
476 } else { | |
477 type = MediaStorageUtil::OTHER_MASS_STORAGE; | |
478 } | |
479 std::string device_id = MediaStorageUtil::MakeDeviceId(type, unique_id); | |
382 | 480 |
383 SystemMonitor::Get()->ProcessRemovableStorageAttached(id, name, mount_point); | 481 MountPointInfo mount_point_info; |
482 mount_point_info.mount_device = mount_device; | |
483 mount_point_info.device_id = device_id; | |
484 mount_point_info.device_name = name; | |
485 mount_point_info.has_dcim = has_dcim; | |
486 | |
487 mount_info_map_[mount_point] = mount_point_info; | |
488 mount_priority_map_[mount_device][mount_point] = has_dcim; | |
489 | |
490 if (mount_point_info.has_dcim) { | |
491 SystemMonitor::Get()->ProcessRemovableStorageAttached(device_id, name, | |
492 mount_point.value()); | |
493 } | |
384 } | 494 } |
385 | 495 |
386 void MediaDeviceNotificationsLinux::RemoveOldDevice( | 496 void RemovableDeviceNotificationsLinux::RemoveMediaMount( |
387 const std::string& device_id) { | 497 const std::string& device_id) { |
388 SystemMonitor::Get()->ProcessRemovableStorageDetached(device_id); | 498 SystemMonitor::Get()->ProcessRemovableStorageDetached(device_id); |
389 } | 499 } |
390 | 500 |
391 } // namespace chrome | 501 } // namespace chrome |
OLD | NEW |