OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "chrome/browser/chromeos/cros/mount_library.h" | |
6 | |
7 #include <set> | |
8 #include <sys/statvfs.h> | |
9 #include <vector> | |
10 | |
11 #include "base/message_loop.h" | |
12 #include "base/string_util.h" | |
13 #include "chrome/browser/chromeos/cros/cros_library.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 | |
16 using content::BrowserThread; | |
17 | |
18 const char* kLibraryNotLoaded = "Cros Library not loaded"; | |
19 const char* kDeviceNotFound = "Device could not be found"; | |
20 | |
21 namespace chromeos { | |
22 | |
23 // static | |
24 std::string MountLibrary::MountTypeToString(MountType type) { | |
25 switch (type) { | |
26 case MOUNT_TYPE_DEVICE: | |
27 return "device"; | |
28 case MOUNT_TYPE_ARCHIVE: | |
29 return "file"; | |
30 case MOUNT_TYPE_NETWORK_STORAGE: | |
31 return "network"; | |
32 case MOUNT_TYPE_INVALID: | |
33 return "invalid"; | |
34 default: | |
35 NOTREACHED(); | |
36 } | |
37 return ""; | |
38 } | |
39 | |
40 std::string MountLibrary::MountConditionToString(MountCondition condition) { | |
41 switch (condition) { | |
42 case MOUNT_CONDITION_NONE: | |
43 return ""; | |
44 case MOUNT_CONDITION_UNKNOWN_FILESYSTEM: | |
45 return "unknown_filesystem"; | |
46 case MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM: | |
47 return "unsupported_filesystem"; | |
48 default: | |
49 NOTREACHED(); | |
50 } | |
51 return ""; | |
52 } | |
53 | |
54 // static | |
55 MountType MountLibrary::MountTypeFromString( | |
56 const std::string& type_str) { | |
57 if (type_str == "device") { | |
58 return MOUNT_TYPE_DEVICE; | |
59 } else if (type_str == "network") { | |
60 return MOUNT_TYPE_NETWORK_STORAGE; | |
61 } else if (type_str == "file") { | |
62 return MOUNT_TYPE_ARCHIVE; | |
63 } else { | |
64 return MOUNT_TYPE_INVALID; | |
65 } | |
66 } | |
67 | |
68 MountLibrary::Disk::Disk(const std::string& device_path, | |
69 const std::string& mount_path, | |
70 const std::string& system_path, | |
71 const std::string& file_path, | |
72 const std::string& device_label, | |
73 const std::string& drive_label, | |
74 const std::string& parent_path, | |
75 const std::string& system_path_prefix, | |
76 DeviceType device_type, | |
77 uint64 total_size, | |
78 bool is_parent, | |
79 bool is_read_only, | |
80 bool has_media, | |
81 bool on_boot_device, | |
82 bool is_hidden) | |
83 : device_path_(device_path), | |
84 mount_path_(mount_path), | |
85 system_path_(system_path), | |
86 file_path_(file_path), | |
87 device_label_(device_label), | |
88 drive_label_(drive_label), | |
89 parent_path_(parent_path), | |
90 system_path_prefix_(system_path_prefix), | |
91 device_type_(device_type), | |
92 total_size_(total_size), | |
93 is_parent_(is_parent), | |
94 is_read_only_(is_read_only), | |
95 has_media_(has_media), | |
96 on_boot_device_(on_boot_device), | |
97 is_hidden_(is_hidden) { | |
98 } | |
99 | |
100 MountLibrary::Disk::~Disk() {} | |
101 | |
102 class MountLibcrosProxyImpl : public MountLibcrosProxy { | |
103 public: | |
104 virtual void CallMountPath(const char* source_path, | |
105 MountType type, | |
106 const MountPathOptions& options, | |
107 MountCompletedMonitor callback, | |
108 void* object) OVERRIDE { | |
109 chromeos::MountSourcePath(source_path, type, options, callback, object); | |
110 } | |
111 | |
112 virtual void CallUnmountPath(const char* path, | |
113 UnmountRequestCallback callback, | |
114 void* object) OVERRIDE { | |
115 chromeos::UnmountMountPoint(path, callback, object); | |
116 } | |
117 | |
118 virtual void CallRequestMountInfo(RequestMountInfoCallback callback, | |
119 void* object) OVERRIDE { | |
120 chromeos::RequestMountInfo(callback, object); | |
121 } | |
122 | |
123 virtual void CallFormatDevice(const char* file_path, | |
124 const char* filesystem, | |
125 FormatRequestCallback callback, | |
126 void* object) OVERRIDE { | |
127 chromeos::FormatDevice(file_path, filesystem, callback, object); | |
128 } | |
129 | |
130 virtual void CallGetDiskProperties(const char* device_path, | |
131 GetDiskPropertiesCallback callback, | |
132 void* object) OVERRIDE { | |
133 chromeos::GetDiskProperties(device_path, callback, object); | |
134 } | |
135 | |
136 virtual MountEventConnection MonitorCrosDisks( | |
137 MountEventMonitor monitor, | |
138 MountCompletedMonitor mount_completed_monitor, | |
139 void* object) OVERRIDE { | |
140 return chromeos::MonitorAllMountEvents( | |
141 monitor, mount_completed_monitor, object); | |
142 } | |
143 | |
144 virtual void DisconnectCrosDisksMonitorIfSet( | |
145 MountEventConnection conn) OVERRIDE { | |
146 if (conn) | |
147 chromeos::DisconnectMountEventMonitor(conn); | |
148 } | |
149 }; | |
150 | |
151 class MountLibraryImpl : public MountLibrary { | |
152 | |
153 struct UnmountDeviceRecursiveCallbackData { | |
154 MountLibraryImpl* const object; | |
155 void* user_data; | |
156 UnmountDeviceRecursiveCallbackType callback; | |
157 size_t pending_callbacks_count; | |
158 bool success; | |
159 | |
160 UnmountDeviceRecursiveCallbackData(MountLibraryImpl* const o, void* ud, | |
161 UnmountDeviceRecursiveCallbackType cb, int count) | |
162 : object(o), | |
163 user_data(ud), | |
164 callback(cb), | |
165 pending_callbacks_count(count), | |
166 success(true) { | |
167 } | |
168 }; | |
169 | |
170 public: | |
171 MountLibraryImpl() : libcros_proxy_(new MountLibcrosProxyImpl()), | |
172 mount_status_connection_(NULL) { | |
173 } | |
174 | |
175 virtual ~MountLibraryImpl() { | |
176 libcros_proxy_->DisconnectCrosDisksMonitorIfSet( | |
177 mount_status_connection_); | |
178 } | |
179 | |
180 // MountLibrary overrides. | |
181 virtual void Init() OVERRIDE { | |
182 DCHECK(CrosLibrary::Get()->libcros_loaded()); | |
183 // Getting the monitor status so that the daemon starts up. | |
184 mount_status_connection_ = libcros_proxy_->MonitorCrosDisks( | |
185 &MonitorMountEventsHandler, &MountCompletedHandler, this); | |
186 } | |
187 | |
188 virtual void AddObserver(Observer* observer) OVERRIDE { | |
189 observers_.AddObserver(observer); | |
190 } | |
191 | |
192 virtual void RemoveObserver(Observer* observer) OVERRIDE { | |
193 observers_.RemoveObserver(observer); | |
194 } | |
195 | |
196 virtual void MountPath(const char* source_path, | |
197 MountType type, | |
198 const MountPathOptions& options) OVERRIDE { | |
199 // Hidden and non-existent devices should not be mounted. | |
200 if (type == MOUNT_TYPE_DEVICE) { | |
201 DiskMap::const_iterator it = disks_.find(source_path); | |
202 if (it == disks_.end() || it->second->is_hidden()) { | |
203 MountCompletedHandler(this, MOUNT_ERROR_INTERNAL, source_path, type, | |
204 NULL); | |
205 return; | |
206 } | |
207 } | |
208 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
209 libcros_proxy_->CallMountPath(source_path, type, options, | |
210 &MountCompletedHandler, this); | |
211 } | |
212 | |
213 virtual void UnmountPath(const char* mount_path) OVERRIDE { | |
214 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
215 libcros_proxy_->CallUnmountPath(mount_path, &UnmountMountPointCallback, | |
216 this); | |
217 } | |
218 | |
219 virtual void GetSizeStatsOnFileThread(const char* mount_path, | |
220 size_t* total_size_kb, size_t* remaining_size_kb) OVERRIDE { | |
221 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
222 | |
223 uint64_t total_size_uint64 = 0; | |
224 uint64_t remaining_size_uint64 = 0; | |
225 | |
226 struct statvfs stat; | |
227 if (statvfs(mount_path, &stat) == 0) { | |
228 total_size_uint64 = | |
229 static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize; | |
230 remaining_size_uint64 = | |
231 static_cast<uint64_t>(stat.f_bfree) * stat.f_frsize; | |
232 } | |
233 | |
234 *total_size_kb = static_cast<size_t>(total_size_uint64 / 1024); | |
235 *remaining_size_kb = static_cast<size_t>(remaining_size_uint64 / 1024); | |
236 } | |
237 | |
238 virtual void FormatUnmountedDevice(const char* file_path) OVERRIDE { | |
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
240 for (MountLibrary::DiskMap::iterator it = disks_.begin(); | |
241 it != disks_.end(); ++it) { | |
242 if (it->second->file_path().compare(file_path) == 0 && | |
243 !it->second->mount_path().empty()) { | |
244 OnFormatDevice(file_path, | |
245 false, | |
246 MOUNT_METHOD_ERROR_LOCAL, | |
247 "Device is still mounted."); | |
248 return; | |
249 } | |
250 } | |
251 libcros_proxy_->CallFormatDevice(file_path, "vfat", &FormatDeviceCallback, | |
252 this); | |
253 } | |
254 | |
255 virtual void FormatMountedDevice(const char* mount_path) OVERRIDE { | |
256 DCHECK(mount_path); | |
257 Disk* disk = NULL; | |
258 for (MountLibrary::DiskMap::iterator it = disks_.begin(); | |
259 it != disks_.end(); ++it) { | |
260 if (it->second->mount_path().compare(mount_path) == 0) { | |
261 disk = it->second; | |
262 break; | |
263 } | |
264 } | |
265 if (!disk) { | |
266 OnFormatDevice(mount_path, | |
267 false, | |
268 MOUNT_METHOD_ERROR_LOCAL, | |
269 "Device with this mount path not found."); | |
270 return; | |
271 } | |
272 | |
273 if (formatting_pending_.find(disk->device_path()) != | |
274 formatting_pending_.end()) { | |
275 OnFormatDevice(mount_path, | |
276 false, | |
277 MOUNT_METHOD_ERROR_LOCAL, | |
278 "Formatting is already pending."); | |
279 return; | |
280 } | |
281 // Formatting process continues, after unmounting. | |
282 formatting_pending_[disk->device_path()] = disk->file_path(); | |
283 UnmountPath(disk->mount_path().c_str()); | |
284 } | |
285 | |
286 virtual void UnmountDeviceRecursive(const char* device_path, | |
287 UnmountDeviceRecursiveCallbackType callback, void* user_data) | |
288 OVERRIDE { | |
289 bool success = true; | |
290 const char* error_message = NULL; | |
291 std::vector<const char*> devices_to_unmount; | |
292 | |
293 // Get list of all devices to unmount. | |
294 int device_path_len = strlen(device_path); | |
295 for (DiskMap::iterator it = disks_.begin(); it != disks_.end(); ++it) { | |
296 if (!it->second->mount_path().empty() && | |
297 strncmp(device_path, it->second->device_path().c_str(), | |
298 device_path_len) == 0) { | |
299 devices_to_unmount.push_back(it->second->mount_path().c_str()); | |
300 } | |
301 } | |
302 | |
303 // We should detect at least original device. | |
304 if (devices_to_unmount.size() == 0) { | |
305 if (disks_.find(device_path) == disks_.end()) { | |
306 success = false; | |
307 error_message = kDeviceNotFound; | |
308 } else { | |
309 // Nothing to unmount. | |
310 callback(user_data, true); | |
311 return; | |
312 } | |
313 } | |
314 | |
315 if (success) { | |
316 // We will send the same callback data object to all Unmount calls and use | |
317 // it to syncronize callbacks. | |
318 UnmountDeviceRecursiveCallbackData* | |
319 cb_data = new UnmountDeviceRecursiveCallbackData(this, user_data, | |
320 callback, devices_to_unmount.size()); | |
321 for (std::vector<const char*>::iterator it = devices_to_unmount.begin(); | |
322 it != devices_to_unmount.end(); | |
323 ++it) { | |
324 libcros_proxy_->CallUnmountPath(*it, &UnmountDeviceRecursiveCallback, | |
325 cb_data); | |
326 } | |
327 } else { | |
328 LOG(WARNING) << "Unmount recursive request failed for device " | |
329 << device_path << ", with error: " << error_message; | |
330 callback(user_data, false); | |
331 } | |
332 } | |
333 | |
334 virtual void RequestMountInfoRefresh() OVERRIDE { | |
335 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
336 libcros_proxy_->CallRequestMountInfo(RequestMountInfoCallback, this); | |
337 } | |
338 | |
339 const DiskMap& disks() const OVERRIDE { return disks_; } | |
340 const MountPointMap& mount_points() const OVERRIDE { return mount_points_; } | |
341 | |
342 virtual void SetLibcrosProxy(MountLibcrosProxy* proxy) OVERRIDE { | |
343 libcros_proxy_->DisconnectCrosDisksMonitorIfSet(mount_status_connection_); | |
344 libcros_proxy_.reset(proxy); | |
345 mount_status_connection_ = libcros_proxy_->MonitorCrosDisks( | |
346 &MonitorMountEventsHandler, &MountCompletedHandler, this); | |
347 } | |
348 private: | |
349 // Callback for MountComplete signal and MountSourcePath method. | |
350 static void MountCompletedHandler(void* object, | |
351 MountError error_code, | |
352 const char* source_path, | |
353 MountType type, | |
354 const char* mount_path) { | |
355 DCHECK(object); | |
356 MountCondition mount_condition = MOUNT_CONDITION_NONE; | |
357 if (type == MOUNT_TYPE_DEVICE) { | |
358 if (error_code == MOUNT_ERROR_UNKNOWN_FILESYSTEM) | |
359 mount_condition = MOUNT_CONDITION_UNKNOWN_FILESYSTEM; | |
360 if (error_code == MOUNT_ERROR_UNSUPORTED_FILESYSTEM) | |
361 mount_condition = MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM; | |
362 } | |
363 | |
364 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); | |
365 self->OnMountCompleted(static_cast<MountError>(error_code), | |
366 MountPointInfo(source_path, | |
367 mount_path, | |
368 type, | |
369 mount_condition)); | |
370 } | |
371 | |
372 // Callback for UnmountRemovableDevice method. | |
373 static void UnmountMountPointCallback(void* object, | |
374 const char* mount_path, | |
375 MountMethodErrorType error, | |
376 const char* error_message) { | |
377 DCHECK(object); | |
378 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); | |
379 self->OnUnmountPath(mount_path, error, error_message); | |
380 } | |
381 | |
382 // Callback for FormatRemovableDevice method. | |
383 static void FormatDeviceCallback(void* object, | |
384 const char* file_path, | |
385 bool success, | |
386 MountMethodErrorType error, | |
387 const char* error_message) { | |
388 DCHECK(object); | |
389 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); | |
390 const char* device_path = self->FilePathToDevicePath(file_path); | |
391 if (!device_path) { | |
392 LOG(ERROR) << "Error while handling disks metadata. Cannot find " | |
393 << "device that is being formatted."; | |
394 return; | |
395 } | |
396 self->OnFormatDevice(device_path, success, error, error_message); | |
397 } | |
398 | |
399 // Callback for UnmountDeviceRecursive. | |
400 static void UnmountDeviceRecursiveCallback(void* object, | |
401 const char* mount_path, | |
402 MountMethodErrorType error, | |
403 const char* error_message) { | |
404 DCHECK(object); | |
405 UnmountDeviceRecursiveCallbackData* cb_data = | |
406 static_cast<UnmountDeviceRecursiveCallbackData*>(object); | |
407 | |
408 // Do standard processing for Unmount event. | |
409 cb_data->object->OnUnmountPath(mount_path, | |
410 error, | |
411 error_message); | |
412 if (error == MOUNT_METHOD_ERROR_LOCAL) { | |
413 cb_data->success = false; | |
414 } else if (error == MOUNT_METHOD_ERROR_NONE) { | |
415 LOG(INFO) << mount_path << " unmounted."; | |
416 } | |
417 | |
418 // This is safe as long as all callbacks are called on the same thread as | |
419 // UnmountDeviceRecursive. | |
420 cb_data->pending_callbacks_count--; | |
421 | |
422 if (cb_data->pending_callbacks_count == 0) { | |
423 cb_data->callback(cb_data->user_data, cb_data->success); | |
424 delete cb_data; | |
425 } | |
426 } | |
427 | |
428 // Callback for disk information retrieval calls. | |
429 static void GetDiskPropertiesCallback(void* object, | |
430 const char* device_path, | |
431 const DiskInfo* disk, | |
432 MountMethodErrorType error, | |
433 const char* error_message) { | |
434 DCHECK(object); | |
435 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); | |
436 self->OnGetDiskProperties(device_path, | |
437 disk, | |
438 error, | |
439 error_message); | |
440 } | |
441 | |
442 // Callback for RequestMountInfo call. | |
443 static void RequestMountInfoCallback(void* object, | |
444 const char** devices, | |
445 size_t device_len, | |
446 MountMethodErrorType error, | |
447 const char* error_message) { | |
448 DCHECK(object); | |
449 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); | |
450 self->OnRequestMountInfo(devices, | |
451 device_len, | |
452 error, | |
453 error_message); | |
454 } | |
455 | |
456 // This method will receive events that are caused by drive status changes. | |
457 static void MonitorMountEventsHandler(void* object, | |
458 MountEventType evt, | |
459 const char* device_path) { | |
460 DCHECK(object); | |
461 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); | |
462 self->OnMountEvent(evt, device_path); | |
463 } | |
464 | |
465 | |
466 void OnMountCompleted(MountError error_code, | |
467 const MountPointInfo& mount_info) { | |
468 DCHECK(!mount_info.source_path.empty()); | |
469 | |
470 FireMountCompleted(MOUNTING, error_code, mount_info); | |
471 | |
472 // If the device is corrupted but it's still possible to format it, it will | |
473 // be fake mounted. | |
474 if ((error_code == MOUNT_ERROR_NONE || mount_info.mount_condition) && | |
475 mount_points_.find(mount_info.mount_path) == mount_points_.end()) { | |
476 mount_points_.insert(MountPointMap::value_type( | |
477 mount_info.mount_path.c_str(), | |
478 mount_info)); | |
479 } | |
480 | |
481 if ((error_code == MOUNT_ERROR_NONE || mount_info.mount_condition) && | |
482 mount_info.mount_type == MOUNT_TYPE_DEVICE && | |
483 !mount_info.source_path.empty() && | |
484 !mount_info.mount_path.empty()) { | |
485 DiskMap::iterator iter = disks_.find(mount_info.source_path); | |
486 if (iter == disks_.end()) { | |
487 // disk might have been removed by now? | |
488 return; | |
489 } | |
490 Disk* disk = iter->second; | |
491 DCHECK(disk); | |
492 disk->set_mount_path(mount_info.mount_path.c_str()); | |
493 FireDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk); | |
494 } | |
495 } | |
496 | |
497 void OnUnmountPath(const char* mount_path, | |
498 MountMethodErrorType error, | |
499 const char* error_message) { | |
500 DCHECK(mount_path); | |
501 if (error == MOUNT_METHOD_ERROR_NONE && mount_path) { | |
502 MountPointMap::iterator mount_points_it = mount_points_.find(mount_path); | |
503 if (mount_points_it == mount_points_.end()) | |
504 return; | |
505 // TODO(tbarzic): Add separate, PathUnmounted event to Observer. | |
506 FireMountCompleted( | |
507 UNMOUNTING, | |
508 MOUNT_ERROR_NONE, | |
509 MountPointInfo(mount_points_it->second.source_path.c_str(), | |
510 mount_points_it->second.mount_path.c_str(), | |
511 mount_points_it->second.mount_type, | |
512 mount_points_it->second.mount_condition)); | |
513 std::string path(mount_points_it->second.source_path); | |
514 mount_points_.erase(mount_points_it); | |
515 DiskMap::iterator iter = disks_.find(path); | |
516 if (iter == disks_.end()) { | |
517 // disk might have been removed by now. | |
518 return; | |
519 } | |
520 Disk* disk = iter->second; | |
521 DCHECK(disk); | |
522 disk->clear_mount_path(); | |
523 // Check if there is a formatting scheduled | |
524 PathMap::iterator it = formatting_pending_.find(disk->device_path()); | |
525 if (it != formatting_pending_.end()) { | |
526 const std::string file_path = it->second; | |
527 formatting_pending_.erase(it); | |
528 FormatUnmountedDevice(file_path.c_str()); | |
529 } | |
530 } else { | |
531 LOG(WARNING) << "Unmount request failed for device " | |
532 << mount_path << ", with error: " | |
533 << (error_message ? error_message : "Unknown"); | |
534 } | |
535 } | |
536 | |
537 void OnFormatDevice(const char* device_path, | |
538 bool success, | |
539 MountMethodErrorType error, | |
540 const char* error_message) { | |
541 DCHECK(device_path); | |
542 if (error == MOUNT_METHOD_ERROR_NONE && device_path && success) { | |
543 FireDeviceStatusUpdate(MOUNT_FORMATTING_STARTED, device_path); | |
544 } else { | |
545 FireDeviceStatusUpdate(MOUNT_FORMATTING_STARTED, | |
546 std::string("!") + device_path); | |
547 LOG(WARNING) << "Format request failed for device " | |
548 << device_path << ", with error: " | |
549 << (error_message ? error_message : "Unknown"); | |
550 } | |
551 } | |
552 | |
553 void OnGetDiskProperties(const char* device_path, | |
554 const DiskInfo* disk, | |
555 MountMethodErrorType error, | |
556 const char* error_message) { | |
557 DCHECK(device_path); | |
558 if (error == MOUNT_METHOD_ERROR_NONE && device_path) { | |
559 // TODO(zelidrag): Find a better way to filter these out before we | |
560 // fetch the properties: | |
561 // Ignore disks coming from the device we booted the system from. | |
562 | |
563 // This cast is temporal solution, until we merge DiskInfo and | |
564 // DiskInfoAdvanced into single interface. | |
565 if (disk->on_boot_device()) | |
566 return; | |
567 | |
568 LOG(WARNING) << "Found disk " << device_path; | |
569 // Delete previous disk info for this path: | |
570 bool is_new = true; | |
571 std::string device_path_string(device_path); | |
572 DiskMap::iterator iter = disks_.find(device_path_string); | |
573 if (iter != disks_.end()) { | |
574 delete iter->second; | |
575 disks_.erase(iter); | |
576 is_new = false; | |
577 } | |
578 | |
579 std::string path; | |
580 std::string mountpath; | |
581 std::string systempath; | |
582 std::string filepath; | |
583 std::string devicelabel; | |
584 std::string drivelabel; | |
585 std::string parentpath; | |
586 | |
587 if (disk->path() != NULL) | |
588 path = disk->path(); | |
589 | |
590 if (disk->mount_path() != NULL) | |
591 mountpath = disk->mount_path(); | |
592 | |
593 if (disk->system_path() != NULL) | |
594 systempath = disk->system_path(); | |
595 | |
596 if (disk->file_path() != NULL) | |
597 filepath = disk->file_path(); | |
598 | |
599 if (disk->label() != NULL) | |
600 devicelabel = disk->label(); | |
601 | |
602 if (disk->drive_label() != NULL) | |
603 drivelabel = disk->drive_label(); | |
604 | |
605 if (disk->partition_slave() != NULL) | |
606 parentpath = disk->partition_slave(); | |
607 | |
608 Disk* new_disk = new Disk(path, | |
609 mountpath, | |
610 systempath, | |
611 filepath, | |
612 devicelabel, | |
613 drivelabel, | |
614 parentpath, | |
615 FindSystemPathPrefix(systempath), | |
616 disk->device_type(), | |
617 disk->size(), | |
618 disk->is_drive(), | |
619 disk->is_read_only(), | |
620 disk->has_media(), | |
621 disk->on_boot_device(), | |
622 disk->is_hidden()); | |
623 disks_.insert( | |
624 std::pair<std::string, Disk*>(device_path_string, new_disk)); | |
625 FireDiskStatusUpdate(is_new ? MOUNT_DISK_ADDED : MOUNT_DISK_CHANGED, | |
626 new_disk); | |
627 } else { | |
628 LOG(WARNING) << "Property retrieval request failed for device " | |
629 << device_path << ", with error: " | |
630 << (error_message ? error_message : "Unknown"); | |
631 } | |
632 } | |
633 | |
634 void OnRequestMountInfo(const char** devices, | |
635 size_t devices_len, | |
636 MountMethodErrorType error, | |
637 const char* error_message) { | |
638 std::set<std::string> current_device_set; | |
639 if (error == MOUNT_METHOD_ERROR_NONE && devices && devices_len) { | |
640 // Initiate properties fetch for all removable disks, | |
641 for (size_t i = 0; i < devices_len; i++) { | |
642 if (!devices[i]) { | |
643 NOTREACHED(); | |
644 continue; | |
645 } | |
646 current_device_set.insert(std::string(devices[i])); | |
647 // Initiate disk property retrieval for each relevant device path. | |
648 libcros_proxy_->CallGetDiskProperties(devices[i], | |
649 &GetDiskPropertiesCallback, this); | |
650 } | |
651 } else if (error != MOUNT_METHOD_ERROR_NONE) { | |
652 LOG(WARNING) << "Request mount info retrieval request failed with error: " | |
653 << (error_message ? error_message : "Unknown"); | |
654 } | |
655 // Search and remove disks that are no longer present. | |
656 for (MountLibrary::DiskMap::iterator iter = disks_.begin(); | |
657 iter != disks_.end(); ) { | |
658 if (current_device_set.find(iter->first) == current_device_set.end()) { | |
659 Disk* disk = iter->second; | |
660 FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); | |
661 delete iter->second; | |
662 disks_.erase(iter++); | |
663 } else { | |
664 ++iter; | |
665 } | |
666 } | |
667 } | |
668 | |
669 void OnMountEvent(MountEventType evt, | |
670 const char* device_path) { | |
671 if (!device_path) | |
672 return; | |
673 MountLibraryEventType type = MOUNT_DEVICE_ADDED; | |
674 switch (evt) { | |
675 case DISK_ADDED: { | |
676 libcros_proxy_->CallGetDiskProperties(device_path, | |
677 &MountLibraryImpl::GetDiskPropertiesCallback, this); | |
678 return; | |
679 } | |
680 case DISK_REMOVED: { | |
681 // Search and remove disks that are no longer present. | |
682 MountLibrary::DiskMap::iterator iter = | |
683 disks_.find(std::string(device_path)); | |
684 if (iter != disks_.end()) { | |
685 Disk* disk = iter->second; | |
686 FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); | |
687 delete iter->second; | |
688 disks_.erase(iter); | |
689 } | |
690 return; | |
691 } | |
692 case DEVICE_ADDED: { | |
693 type = MOUNT_DEVICE_ADDED; | |
694 system_path_prefixes_.insert(device_path); | |
695 break; | |
696 } | |
697 case DEVICE_REMOVED: { | |
698 type = MOUNT_DEVICE_REMOVED; | |
699 system_path_prefixes_.erase(device_path); | |
700 break; | |
701 } | |
702 case DEVICE_SCANNED: { | |
703 type = MOUNT_DEVICE_SCANNED; | |
704 break; | |
705 } | |
706 case FORMATTING_FINISHED: { | |
707 // FORMATTING_FINISHED actually returns file path instead of device | |
708 // path. | |
709 device_path = FilePathToDevicePath(device_path); | |
710 if (!device_path) { | |
711 LOG(ERROR) << "Error while handling disks metadata. Cannot find " | |
712 << "device that is being formatted."; | |
713 return; | |
714 } | |
715 type = MOUNT_FORMATTING_FINISHED; | |
716 break; | |
717 } | |
718 default: { | |
719 return; | |
720 } | |
721 } | |
722 FireDeviceStatusUpdate(type, std::string(device_path)); | |
723 } | |
724 | |
725 void FireDiskStatusUpdate(MountLibraryEventType evt, | |
726 const Disk* disk) { | |
727 // Make sure we run on UI thread. | |
728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
729 FOR_EACH_OBSERVER( | |
730 Observer, observers_, DiskChanged(evt, disk)); | |
731 } | |
732 | |
733 void FireDeviceStatusUpdate(MountLibraryEventType evt, | |
734 const std::string& device_path) { | |
735 // Make sure we run on UI thread. | |
736 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
737 FOR_EACH_OBSERVER( | |
738 Observer, observers_, DeviceChanged(evt, device_path)); | |
739 } | |
740 | |
741 void FireMountCompleted(MountEvent event_type, | |
742 MountError error_code, | |
743 const MountPointInfo& mount_info) { | |
744 // Make sure we run on UI thread. | |
745 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
746 FOR_EACH_OBSERVER( | |
747 Observer, observers_, MountCompleted(event_type, | |
748 error_code, | |
749 mount_info)); | |
750 } | |
751 | |
752 const char* FilePathToDevicePath(const char* file_path) { | |
753 int failed = (file_path[0] == '!') ? 1 : 0; | |
754 for (MountLibrary::DiskMap::iterator it = disks_.begin(); | |
755 it != disks_.end(); ++it) { | |
756 if (it->second->file_path().compare(file_path + failed) == 0) { | |
757 if (failed) { | |
758 return (std::string("!") + it->second->device_path()).c_str(); | |
759 } else { | |
760 return it->second->device_path().c_str(); | |
761 } | |
762 } | |
763 } | |
764 return NULL; | |
765 } | |
766 | |
767 const std::string& FindSystemPathPrefix(const std::string& system_path) { | |
768 if (system_path.empty()) | |
769 return EmptyString(); | |
770 for (SystemPathPrefixSet::const_iterator it = system_path_prefixes_.begin(); | |
771 it != system_path_prefixes_.end(); | |
772 ++it) { | |
773 if (system_path.find(*it, 0) == 0) | |
774 return *it; | |
775 } | |
776 return EmptyString(); | |
777 } | |
778 | |
779 // Mount event change observers. | |
780 ObserverList<Observer> observers_; | |
781 | |
782 scoped_ptr<MountLibcrosProxy> libcros_proxy_; | |
783 | |
784 // A reference to the mount api, to allow callbacks when the mount | |
785 // status changes. | |
786 MountEventConnection mount_status_connection_; | |
787 | |
788 // The list of disks found. | |
789 MountLibrary::DiskMap disks_; | |
790 | |
791 MountLibrary::MountPointMap mount_points_; | |
792 | |
793 typedef std::set<std::string> SystemPathPrefixSet; | |
794 SystemPathPrefixSet system_path_prefixes_; | |
795 | |
796 // Set of devices that are supposed to be formated, but are currently waiting | |
797 // to be unmounted. When device is in this map, the formatting process HAVEN'T | |
798 // started yet. | |
799 PathMap formatting_pending_; | |
800 | |
801 DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl); | |
802 }; | |
803 | |
804 class MountLibraryStubImpl : public MountLibrary { | |
805 public: | |
806 MountLibraryStubImpl() {} | |
807 virtual ~MountLibraryStubImpl() {} | |
808 | |
809 // MountLibrary overrides. | |
810 virtual void Init() OVERRIDE {} | |
811 virtual void AddObserver(Observer* observer) OVERRIDE {} | |
812 virtual void RemoveObserver(Observer* observer) OVERRIDE {} | |
813 virtual const DiskMap& disks() const OVERRIDE { return disks_; } | |
814 virtual const MountPointMap& mount_points() const OVERRIDE { | |
815 return mount_points_; | |
816 } | |
817 virtual void RequestMountInfoRefresh() OVERRIDE {} | |
818 virtual void MountPath(const char* source_path, MountType type, | |
819 const MountPathOptions& options) OVERRIDE {} | |
820 virtual void UnmountPath(const char* mount_path) OVERRIDE {} | |
821 virtual void GetSizeStatsOnFileThread(const char* mount_path, | |
822 size_t* total_size_kb, size_t* remaining_size_kb) OVERRIDE {} | |
823 virtual void FormatUnmountedDevice(const char* device_path) OVERRIDE {} | |
824 virtual void FormatMountedDevice(const char* mount_path) OVERRIDE {} | |
825 virtual void UnmountDeviceRecursive(const char* device_path, | |
826 UnmountDeviceRecursiveCallbackType callback, void* user_data) | |
827 OVERRIDE {} | |
828 private: | |
829 // The list of disks found. | |
830 DiskMap disks_; | |
831 MountPointMap mount_points_; | |
832 | |
833 DISALLOW_COPY_AND_ASSIGN(MountLibraryStubImpl); | |
834 }; | |
835 | |
836 // static | |
837 MountLibrary* MountLibrary::GetImpl(bool stub) { | |
838 MountLibrary* impl; | |
839 if (stub) | |
840 impl = new MountLibraryStubImpl(); | |
841 else | |
842 impl = new MountLibraryImpl(); | |
843 impl->Init(); | |
844 return impl; | |
845 } | |
846 | |
847 } // namespace chromeos | |
OLD | NEW |