Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(387)

Side by Side Diff: chrome/browser/media_gallery/removable_device_notifications_linux.cc

Issue 10882039: Make the Linux System Monitor implementation track all devices (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix move status Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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.
51 const char kDevName[] = "DEVNAME"; 54 const char kDevName[] = "DEVNAME";
52 const char kFsUUID[] = "ID_FS_UUID"; 55 const char kFsUUID[] = "ID_FS_UUID";
53 const char kLabel[] = "ID_FS_LABEL"; 56 const char kLabel[] = "ID_FS_LABEL";
54 const char kModel[] = "ID_MODEL"; 57 const char kModel[] = "ID_MODEL";
55 const char kModelID[] = "ID_MODEL_ID"; 58 const char kModelID[] = "ID_MODEL_ID";
59 const char kRemovableSysAttr[] = "removable";
56 const char kSerial[] = "ID_SERIAL"; 60 const char kSerial[] = "ID_SERIAL";
57 const char kSerialShort[] = "ID_SERIAL_SHORT"; 61 const char kSerialShort[] = "ID_SERIAL_SHORT";
58 const char kVendor[] = "ID_VENDOR"; 62 const char kVendor[] = "ID_VENDOR";
59 const char kVendorID[] = "ID_VENDOR_ID"; 63 const char kVendorID[] = "ID_VENDOR_ID";
60 64
61 // Device mount point details. 65 // (mount point, mount device)
62 struct MountPointEntryInfo { 66 // A mapping from mount point to mount device, as extracted from the mtab
63 std::string mount_point; 67 // file.
64 int entry_pos; 68 typedef std::map<FilePath, FilePath> MountPointDeviceMap;
65 }; 69
70 // Reads mtab file entries into |mtab|.
71 void ReadMtab(FilePath mtab_path,
Lei Zhang 2012/08/28 00:05:04 Why not just keep this as a class method? If you
vandebo (ex-Chrome) 2012/08/28 00:33:32 The type declarations in the header are pretty con
72 std::set<std::string> interesting_file_systems,
73 MountPointDeviceMap* mtab) {
74 FILE* fp = setmntent(mtab_path.value().c_str(), "r");
75 if (!fp)
76 return;
77
78 mtab->clear();
Lei Zhang 2012/08/28 00:05:04 probably should do this first
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
79
80 mntent entry;
81 char buf[512];
82
83 // We return the same device mounted to multiple locations, but hide
84 // devices that have been mounted over.
85 while (getmntent_r(fp, &entry, buf, sizeof(buf))) {
86 // We only care about real file systems.
87 if (!ContainsKey(interesting_file_systems, entry.mnt_type))
88 continue;
89
90 (*mtab)[FilePath(entry.mnt_dir)] = FilePath(entry.mnt_fsname);
91 }
92 endmntent(fp);
93 }
66 94
67 // ScopedGenericObj functor for UdevObjectRelease(). 95 // ScopedGenericObj functor for UdevObjectRelease().
68 class ScopedReleaseUdevObject { 96 class ScopedReleaseUdevObject {
69 public: 97 public:
70 void operator()(struct udev* udev) const { 98 void operator()(struct udev* udev) const {
71 udev_unref(udev); 99 udev_unref(udev);
72 } 100 }
73 }; 101 };
74 typedef ScopedGenericObj<struct udev*, 102 typedef ScopedGenericObj<struct udev*,
75 ScopedReleaseUdevObject> ScopedUdevObject; 103 ScopedReleaseUdevObject> ScopedUdevObject;
(...skipping 12 matching lines...) Expand all
88 // valid but empty values. 116 // valid but empty values.
89 std::string GetUdevDevicePropertyValue(struct udev_device* udev_device, 117 std::string GetUdevDevicePropertyValue(struct udev_device* udev_device,
90 const char* key) { 118 const char* key) {
91 const char* value = udev_device_get_property_value(udev_device, key); 119 const char* value = udev_device_get_property_value(udev_device, key);
92 if (!value) 120 if (!value)
93 return std::string(); 121 return std::string();
94 return (strlen(value) > 0) ? value : std::string(); 122 return (strlen(value) > 0) ? value : std::string();
95 } 123 }
96 124
97 // Get the device information using udev library. 125 // Get the device information using udev library.
98 // On success, returns true and fill in |id| and |name|. 126 // On success, returns true and fill in |id| and |name|.
Lei Zhang 2012/08/28 00:05:04 nit: comment is out of date
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
99 bool GetDeviceInfo(const std::string& device_path, std::string* id, 127 bool GetDeviceInfo(const FilePath& device_path, std::string* unique_id,
100 string16* name) { 128 string16* name, bool* removable) {
101 DCHECK(!device_path.empty()); 129 DCHECK(!device_path.empty());
102 130
103 ScopedUdevObject udev_obj(udev_new()); 131 ScopedUdevObject udev_obj(udev_new());
104 if (!udev_obj.get()) 132 if (!udev_obj.get())
105 return false; 133 return false;
106 134
107 struct stat device_stat; 135 struct stat device_stat;
108 if (stat(device_path.c_str(), &device_stat) < 0) 136 if (stat(device_path.value().c_str(), &device_stat) < 0)
109 return false; 137 return false;
110 138
111 char device_type; 139 char device_type;
112 if (S_ISCHR(device_stat.st_mode)) 140 if (S_ISCHR(device_stat.st_mode))
113 device_type = 'c'; 141 device_type = 'c';
114 else if (S_ISBLK(device_stat.st_mode)) 142 else if (S_ISBLK(device_stat.st_mode))
115 device_type = 'b'; 143 device_type = 'b';
116 else 144 else
117 return false; // Not a supported type. 145 return false; // Not a supported type.
118 146
(...skipping 23 matching lines...) Expand all
142 else 170 else
143 device_label = vendor_name + kSpaceDelim + model_name; 171 device_label = vendor_name + kSpaceDelim + model_name;
144 } 172 }
145 173
146 if (IsStringUTF8(device_label)) 174 if (IsStringUTF8(device_label))
147 *name = UTF8ToUTF16(device_label); 175 *name = UTF8ToUTF16(device_label);
148 } 176 }
149 177
150 // Construct a device id using label or manufacturer (vendor and model) 178 // Construct a device id using label or manufacturer (vendor and model)
151 // details. 179 // details.
152 if (id) { 180 if (unique_id) {
153 std::string unique_id = GetUdevDevicePropertyValue(device, kFsUUID); 181 std::string uuid = GetUdevDevicePropertyValue(device, kFsUUID);
154 if (unique_id.empty()) { 182 *unique_id = kFSUniqueIdPrefix + uuid;
Lei Zhang 2012/08/28 00:05:04 I have a feeling you are doing this to make the co
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
183 if (uuid.empty()) {
155 // If one of the vendor, model, serial information is missing, its value 184 // If one of the vendor, model, serial information is missing, its value
156 // in the string is empty. 185 // in the string is empty.
157 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialShortInfo 186 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialShortInfo
158 // E.g.: VendorModelSerial:Kn:DataTravel_12.10:8000000000006CB02CDB 187 // E.g.: VendorModelSerial:Kn:DataTravel_12.10:8000000000006CB02CDB
159 std::string vendor = GetUdevDevicePropertyValue(device, kVendorID); 188 std::string vendor = GetUdevDevicePropertyValue(device, kVendorID);
160 std::string model = GetUdevDevicePropertyValue(device, kModelID); 189 std::string model = GetUdevDevicePropertyValue(device, kModelID);
161 std::string serial_short = GetUdevDevicePropertyValue(device, 190 std::string serial_short = GetUdevDevicePropertyValue(device,
162 kSerialShort); 191 kSerialShort);
163 if (vendor.empty() && model.empty() && serial_short.empty()) 192 if (vendor.empty() && model.empty() && serial_short.empty())
164 return false; 193 return false;
165 194
166 unique_id = base::StringPrintf("%s%s%s%s%s%s", 195 *unique_id = base::StringPrintf("%s%s%s%s%s%s",
167 kVendorModelSerialPrefix, 196 kVendorModelSerialPrefix,
168 vendor.c_str(), 197 vendor.c_str(),
169 kNonSpaceDelim, 198 kNonSpaceDelim,
170 model.c_str(), 199 model.c_str(),
171 kNonSpaceDelim, 200 kNonSpaceDelim,
172 serial_short.c_str()); 201 serial_short.c_str());
173 } else {
174 unique_id = kFSUniqueIdPrefix + unique_id;
175 } 202 }
176 *id = MediaStorageUtil::MakeDeviceId( 203 }
177 MediaStorageUtil::USB_MASS_STORAGE_WITH_DCIM, unique_id); 204
205 if (removable) {
206 // |parent_device| is owned by |device| and does not need to be cleaned up.
207 struct udev_device* parent_device =
208 udev_device_get_parent_with_subsystem_devtype(device, "block", "disk");
Lei Zhang 2012/08/28 00:05:04 nit: make block and disk constants or use remove b
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
209 const char* value = udev_device_get_sysattr_value(parent_device,
210 kRemovableSysAttr);
211 *removable = false;
212 if (value && atoi(value) == 1)
Lei Zhang 2012/08/28 00:05:04 *removable = (value && atoi(value) == 1)
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
213 *removable = true;
178 } 214 }
179 return true; 215 return true;
180 } 216 }
181 217
182 } // namespace 218 } // namespace
183 219
184 MediaDeviceNotificationsLinux::MediaDeviceNotificationsLinux( 220 RemovableDeviceNotificationsLinux::RemovableDeviceNotificationsLinux(
185 const FilePath& path) 221 const FilePath& path)
186 : initialized_(false), 222 : initialized_(false),
187 mtab_path_(path), 223 mtab_path_(path),
188 get_device_info_func_(&GetDeviceInfo) { 224 get_device_info_func_(&GetDeviceInfo) {
225 DCHECK(g_removable_device_notifications_linux == NULL);
Lei Zhang 2012/08/28 00:05:04 DCHECK(!g_removable_device_notifications_linux);
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done. And below.
226 g_removable_device_notifications_linux = this;
189 } 227 }
190 228
191 MediaDeviceNotificationsLinux::MediaDeviceNotificationsLinux( 229 RemovableDeviceNotificationsLinux::RemovableDeviceNotificationsLinux(
192 const FilePath& path, 230 const FilePath& path,
193 GetDeviceInfoFunc get_device_info_func) 231 GetDeviceInfoFunc get_device_info_func)
194 : initialized_(false), 232 : initialized_(false),
195 mtab_path_(path), 233 mtab_path_(path),
196 get_device_info_func_(get_device_info_func) { 234 get_device_info_func_(get_device_info_func) {
235 DCHECK(g_removable_device_notifications_linux == NULL);
236 g_removable_device_notifications_linux = this;
197 } 237 }
198 238
199 MediaDeviceNotificationsLinux::~MediaDeviceNotificationsLinux() { 239 RemovableDeviceNotificationsLinux::~RemovableDeviceNotificationsLinux() {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
241 DCHECK(g_removable_device_notifications_linux != NULL);
Lei Zhang 2012/08/28 00:05:04 DCHECK_EQ(this, g_removable_device_notifications_l
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
242 g_removable_device_notifications_linux = NULL;
201 } 243 }
202 244
203 void MediaDeviceNotificationsLinux::Init() { 245 // static
246 RemovableDeviceNotificationsLinux*
247 RemovableDeviceNotificationsLinux::GetInstance() {
248 return g_removable_device_notifications_linux;
249 }
250
251 void RemovableDeviceNotificationsLinux::Init() {
204 DCHECK(!mtab_path_.empty()); 252 DCHECK(!mtab_path_.empty());
205 253
206 // Put |kKnownFileSystems| in std::set to get O(log N) access time. 254 // Put |kKnownFileSystems| in std::set to get O(log N) access time.
207 for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i) 255 for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i)
208 known_file_systems_.insert(kKnownFileSystems[i]); 256 known_file_systems_.insert(kKnownFileSystems[i]);
209 257
210 BrowserThread::PostTask( 258 BrowserThread::PostTask(
211 BrowserThread::FILE, FROM_HERE, 259 BrowserThread::FILE, FROM_HERE,
212 base::Bind(&MediaDeviceNotificationsLinux::InitOnFileThread, this)); 260 base::Bind(&RemovableDeviceNotificationsLinux::InitOnFileThread, this));
213 } 261 }
214 262
215 void MediaDeviceNotificationsLinux::OnFilePathChanged(const FilePath& path, 263 FilePath RemovableDeviceNotificationsLinux::GetDeviceMountPoint(
216 bool error) { 264 const std::string& device_id) const {
265
266 MediaStorageUtil::Type type;
267 MediaStorageUtil::CrackDeviceId(device_id, &type, NULL);
268 if (type == MediaStorageUtil::USB_MTP)
269 return FilePath();
270
271 FilePath mount_device = FilePath();
Lei Zhang 2012/08/28 00:05:04 you don't need to init a FilePath to an empty File
vandebo (ex-Chrome) 2012/08/28 00:33:32 Done.
272 for (MountMap::const_iterator it = mount_info_map_.begin();
273 it != mount_info_map_.end();
274 ++it) {
275 if (it->second.device_id == device_id) {
276 mount_device = it->second.mount_device;
277 break;
278 }
279 }
280 if (mount_device.empty())
281 return mount_device;
282
283 const ReferencedMountPoint& referenced_info =
284 mount_priority_map_.find(mount_device)->second;
285 for (ReferencedMountPoint::const_iterator it = referenced_info.begin();
286 it != referenced_info.end();
287 ++it) {
288 if (it->second)
289 return it->first;
290 }
291 // If none of them are default, just return the first.
292 return FilePath(
293 mount_priority_map_.find(mount_device)->second.begin()->first);
294 }
295
296 std::string RemovableDeviceNotificationsLinux::GetDeviceIdForPath(
297 const FilePath& path, FilePath* mount_point) const {
298 if (!path.IsAbsolute())
299 return std::string();
300
301 FilePath current = path;
302 for (current = path;
303 mount_info_map_.find(current) == mount_info_map_.end() &&
304 current != current.DirName();
305 current = current.DirName()) {}
306
307 if (mount_info_map_.find(current) == mount_info_map_.end())
308 return std::string();
309
310 if (mount_point) {
311 *mount_point = FilePath();
312 current.AppendRelativePath(path, mount_point);
313 }
314
315 return mount_info_map_.find(current)->second.device_id;
316 }
317
318 void RemovableDeviceNotificationsLinux::OnFilePathChanged(const FilePath& path,
319 bool error) {
217 if (path != mtab_path_) { 320 if (path != mtab_path_) {
218 // This cannot happen unless FilePathWatcher is buggy. Just ignore this 321 // This cannot happen unless FilePathWatcher is buggy. Just ignore this
219 // notification and do nothing. 322 // notification and do nothing.
220 NOTREACHED(); 323 NOTREACHED();
221 return; 324 return;
222 } 325 }
223 if (error) { 326 if (error) {
224 LOG(ERROR) << "Error watching " << mtab_path_.value(); 327 LOG(ERROR) << "Error watching " << mtab_path_.value();
225 return; 328 return;
226 } 329 }
227 330
228 UpdateMtab(); 331 UpdateMtab();
229 } 332 }
230 333
231 void MediaDeviceNotificationsLinux::InitOnFileThread() { 334 void RemovableDeviceNotificationsLinux::InitOnFileThread() {
232 DCHECK(!initialized_); 335 DCHECK(!initialized_);
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
234 initialized_ = true; 337 initialized_ = true;
235 338
236 // The callback passed to Watch() has to be unretained. Otherwise 339 // The callback passed to Watch() has to be unretained. Otherwise
237 // MediaDeviceNotificationsLinux will live longer than expected, and 340 // RemovableDeviceNotificationsLinux will live longer than expected, and
238 // FilePathWatcher will get in trouble at shutdown time. 341 // FilePathWatcher will get in trouble at shutdown time.
239 bool ret = file_watcher_.Watch( 342 bool ret = file_watcher_.Watch(
240 mtab_path_, 343 mtab_path_,
241 base::Bind(&MediaDeviceNotificationsLinux::OnFilePathChanged, 344 base::Bind(&RemovableDeviceNotificationsLinux::OnFilePathChanged,
242 base::Unretained(this))); 345 base::Unretained(this)));
243 if (!ret) { 346 if (!ret) {
244 LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed"; 347 LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed";
245 return; 348 return;
246 } 349 }
247 350
248 UpdateMtab(); 351 UpdateMtab();
249 } 352 }
250 353
251 void MediaDeviceNotificationsLinux::UpdateMtab() { 354 void RemovableDeviceNotificationsLinux::UpdateMtab() {
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
253 356
254 MountPointDeviceMap new_mtab; 357 MountPointDeviceMap new_mtab;
255 ReadMtab(&new_mtab); 358 ReadMtab(mtab_path_, known_file_systems_, &new_mtab);
256 359
257 // Check existing mtab entries for unaccounted mount points. 360 // Check existing mtab entries for unaccounted mount points.
258 // These mount points must have been removed in the new mtab. 361 // These mount points must have been removed in the new mtab.
259 std::vector<std::string> mount_points_to_erase; 362 std::list<FilePath> mount_points_to_erase;
363 std::list<FilePath> multiple_mounted_devices_needing_reattachment;
260 for (MountMap::const_iterator old_iter = mount_info_map_.begin(); 364 for (MountMap::const_iterator old_iter = mount_info_map_.begin();
261 old_iter != mount_info_map_.end(); ++old_iter) { 365 old_iter != mount_info_map_.end(); ++old_iter) {
262 const std::string& mount_point = old_iter->first; 366 const FilePath& mount_point = old_iter->first;
263 const std::string& mount_device = old_iter->second.mount_device; 367 const FilePath& mount_device = old_iter->second.mount_device;
264 MountPointDeviceMap::iterator new_iter = new_mtab.find(mount_point); 368 MountPointDeviceMap::iterator new_iter = new_mtab.find(mount_point);
265 // |mount_point| not in |new_mtab| or |mount_device| is no longer mounted at 369 // |mount_point| not in |new_mtab| or |mount_device| is no longer mounted at
266 // |mount_point|. 370 // |mount_point|.
267 if (new_iter == new_mtab.end() || (new_iter->second != mount_device)) { 371 if (new_iter == new_mtab.end() || (new_iter->second != mount_device)) {
268 RemoveOldDevice(old_iter->second.device_id); 372 MountPriorityMap::iterator priority =
373 mount_priority_map_.find(mount_device);
374 DCHECK(priority != mount_priority_map_.end());
375 ReferencedMountPoint::const_iterator has_priority =
376 priority->second.find(mount_point);
377 if (old_iter->second.has_dcim) {
378 DCHECK(has_priority != priority->second.end());
379 if (has_priority->second)
380 RemoveMediaMount(old_iter->second.device_id);
381 if (mount_priority_map_.find(mount_device)->second.size() > 1)
382 multiple_mounted_devices_needing_reattachment.push_back(mount_device);
383 }
384 priority->second.erase(mount_point);
385 if (mount_priority_map_.find(mount_device)->second.empty())
386 mount_priority_map_.erase(mount_device);
269 mount_points_to_erase.push_back(mount_point); 387 mount_points_to_erase.push_back(mount_point);
270 } 388 }
271 } 389 }
390
272 // Erase the |mount_info_map_| entries afterwards. Erasing in the loop above 391 // Erase the |mount_info_map_| entries afterwards. Erasing in the loop above
273 // using the iterator is slightly more efficient, but more tricky, since 392 // using the iterator is slightly more efficient, but more tricky, since
274 // calling std::map::erase() on an iterator invalidates it. 393 // calling std::map::erase() on an iterator invalidates it.
275 for (size_t i = 0; i < mount_points_to_erase.size(); ++i) 394 for (std::list<FilePath>::const_iterator it = mount_points_to_erase.begin();
276 mount_info_map_.erase(mount_points_to_erase[i]); 395 it != mount_points_to_erase.end();
396 ++it) {
397 mount_info_map_.erase(*it);
398 }
399
400 // For any multiply mounted device where the mount that we had notified
401 // got detached, send a notification of attachment for one of the other
402 // mount points.
403 for (std::list<FilePath>::const_iterator it =
404 multiple_mounted_devices_needing_reattachment.begin();
405 it != multiple_mounted_devices_needing_reattachment.end();
406 ++it) {
407 const FilePath& mount_point = mount_priority_map_[*it].begin()->first;
408 mount_priority_map_[*it].begin()->second = true;
409 DCHECK(mount_info_map_[mount_point].has_dcim);
410 base::SystemMonitor::Get()->ProcessRemovableStorageAttached(
411 mount_info_map_[mount_point].device_id,
412 mount_info_map_[mount_point].device_name,
413 mount_point.value());
414 }
277 415
278 // Check new mtab entries against existing ones. 416 // Check new mtab entries against existing ones.
279 for (MountPointDeviceMap::iterator new_iter = new_mtab.begin(); 417 for (MountPointDeviceMap::iterator new_iter = new_mtab.begin();
280 new_iter != new_mtab.end(); ++new_iter) { 418 new_iter != new_mtab.end(); ++new_iter) {
281 const std::string& mount_point = new_iter->first; 419 const FilePath& mount_point = new_iter->first;
282 const std::string& mount_device = new_iter->second; 420 const FilePath& mount_device = new_iter->second;
283 MountMap::iterator old_iter = mount_info_map_.find(mount_point); 421 MountMap::iterator old_iter = mount_info_map_.find(mount_point);
284 if (old_iter == mount_info_map_.end() || 422 if (old_iter == mount_info_map_.end() ||
285 old_iter->second.mount_device != mount_device) { 423 old_iter->second.mount_device != mount_device) {
286 // New mount point found or an existing mount point found with a new 424 // New mount point found or an existing mount point found with a new
287 // device. 425 // device.
288 CheckAndAddMediaDevice(mount_device, mount_point); 426 AddNewMount(mount_device, mount_point);
289 } 427 }
290 } 428 }
291 } 429 }
292 430
293 void MediaDeviceNotificationsLinux::ReadMtab(MountPointDeviceMap* mtab) { 431 void RemovableDeviceNotificationsLinux::AddNewMount(
294 FILE* fp = setmntent(mtab_path_.value().c_str(), "r"); 432 const FilePath& mount_device, const FilePath& mount_point) {
295 if (!fp) 433 if (mount_priority_map_.find(mount_device) != mount_priority_map_.end()) {
434 const FilePath& other_mount_point =
435 mount_priority_map_[mount_device].begin()->first;
436 mount_priority_map_[mount_device][mount_point] = false;
437 mount_info_map_[mount_point] = mount_info_map_[other_mount_point];
296 return; 438 return;
439 }
297 440
298 mntent entry; 441 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; 442 string16 name;
360 bool result = get_device_info_func_(mount_device, &id, &name); 443 bool removable;
444 bool result = get_device_info_func_(mount_device, &unique_id, &name,
445 &removable);
361 446
362 // Keep track of GetDeviceInfo result, to see how often we fail to get device 447 // Keep track of GetDeviceInfo result, to see how often we fail to get device
363 // details. 448 // details.
364 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_info_available", 449 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_info_available",
365 result); 450 result);
366 if (!result) 451 if (!result)
367 return; 452 return;
368 453
369 // Keep track of device uuid, to see how often we receive empty values. 454 // Keep track of device uuid, to see how often we receive empty values.
370 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_uuid_available", 455 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_uuid_available",
371 !id.empty()); 456 !unique_id.empty());
372 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_name_available", 457 UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_name_available",
373 !name.empty()); 458 !name.empty());
374 459
375 if (id.empty() || name.empty()) 460 if (unique_id.empty() || name.empty())
376 return; 461 return;
377 462
378 MountDeviceAndId mount_device_and_id; 463 bool has_dcim = IsMediaDevice(mount_point.value());
379 mount_device_and_id.mount_device = mount_device; 464 MediaStorageUtil::Type type;
380 mount_device_and_id.device_id = id; 465 if (removable) {
381 mount_info_map_[mount_point] = mount_device_and_id; 466 if (has_dcim) {
467 type = MediaStorageUtil::USB_MASS_STORAGE_WITH_DCIM;
468 } else {
469 type = MediaStorageUtil::USB_MASS_STORAGE_NO_DCIM;
470 }
471 } else {
472 type = MediaStorageUtil::OTHER_MASS_STORAGE;
473 }
474 std::string device_id = MediaStorageUtil::MakeDeviceId(type, unique_id);
382 475
383 SystemMonitor::Get()->ProcessRemovableStorageAttached(id, name, mount_point); 476 MountPointInfo mount_point_info;
477 mount_point_info.mount_device = mount_device;
478 mount_point_info.device_id = device_id;
479 mount_point_info.device_name = name;
480 mount_point_info.has_dcim = has_dcim;
481
482 mount_info_map_[mount_point] = mount_point_info;
483 mount_priority_map_[mount_device][mount_point] = has_dcim;
484
485 if (mount_point_info.has_dcim) {
486 SystemMonitor::Get()->ProcessRemovableStorageAttached(device_id, name,
487 mount_point.value());
488 }
384 } 489 }
385 490
386 void MediaDeviceNotificationsLinux::RemoveOldDevice( 491 void RemovableDeviceNotificationsLinux::RemoveMediaMount(
387 const std::string& device_id) { 492 const std::string& device_id) {
388 SystemMonitor::Get()->ProcessRemovableStorageDetached(device_id); 493 SystemMonitor::Get()->ProcessRemovableStorageDetached(device_id);
389 } 494 }
390 495
391 } // namespace chrome 496 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698