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 9 #include "base/message_loop.h" 10 #include "base/string_util.h" 11 #include "chrome/browser/chromeos/cros/cros_library.h" 12 #include "content/browser/browser_thread.h" 13 14 const char* kLibraryNotLoaded = "Cros Library not loaded"; 15 16 namespace chromeos { 17 18 MountLibrary::Disk::Disk(const std::string& device_path, 19 const std::string& mount_path, 20 const std::string& system_path, 21 const std::string& file_path, 22 const std::string& device_label, 23 const std::string& drive_label, 24 const std::string& parent_path, 25 DeviceType device_type, 26 uint64 total_size, 27 bool is_parent, 28 bool is_read_only, 29 bool has_media, 30 bool on_boot_device) 31 : device_path_(device_path), 32 mount_path_(mount_path), 33 system_path_(system_path), 34 file_path_(file_path), 35 device_label_(device_label), 36 drive_label_(drive_label), 37 parent_path_(parent_path), 38 device_type_(device_type), 39 total_size_(total_size), 40 is_parent_(is_parent), 41 is_read_only_(is_read_only), 42 has_media_(has_media), 43 on_boot_device_(on_boot_device) { 44 // Add trailing slash to mount path. 45 if (mount_path_.length() && mount_path_.at(mount_path_.length() -1) != '/') 46 mount_path_ = mount_path_.append("/"); 47 } 48 49 class MountLibraryImpl : public MountLibrary { 50 public: 51 MountLibraryImpl() : mount_status_connection_(NULL) { 52 if (CrosLibrary::Get()->EnsureLoaded()) 53 Init(); 54 else 55 LOG(ERROR) << kLibraryNotLoaded; 56 } 57 58 virtual ~MountLibraryImpl() { 59 if (mount_status_connection_) 60 DisconnectMountEventMonitor(mount_status_connection_); 61 } 62 63 // MountLibrary overrides. 64 virtual void AddObserver(Observer* observer) OVERRIDE { 65 observers_.AddObserver(observer); 66 } 67 68 virtual void RemoveObserver(Observer* observer) OVERRIDE { 69 observers_.RemoveObserver(observer); 70 } 71 72 virtual void MountPath(const char* device_path) OVERRIDE { 73 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 74 if (!CrosLibrary::Get()->EnsureLoaded()) { 75 OnMountRemovableDevice(device_path, 76 NULL, 77 MOUNT_METHOD_ERROR_LOCAL, 78 kLibraryNotLoaded); 79 return; 80 } 81 MountRemovableDevice(device_path, 82 &MountLibraryImpl::MountRemovableDeviceCallback, 83 this); 84 } 85 86 virtual void UnmountPath(const char* device_path) OVERRIDE { 87 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 88 if (!CrosLibrary::Get()->EnsureLoaded()) { 89 OnUnmountRemovableDevice(device_path, 90 MOUNT_METHOD_ERROR_LOCAL, 91 kLibraryNotLoaded); 92 return; 93 } 94 UnmountRemovableDevice(device_path, 95 &MountLibraryImpl::UnmountRemovableDeviceCallback, 96 this); 97 } 98 99 virtual void RequestMountInfoRefresh() OVERRIDE { 100 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 101 if (!CrosLibrary::Get()->EnsureLoaded()) { 102 OnRequestMountInfo(NULL, 103 0, 104 MOUNT_METHOD_ERROR_LOCAL, 105 kLibraryNotLoaded); 106 return; 107 } 108 RequestMountInfo(&MountLibraryImpl::RequestMountInfoCallback, 109 this); 110 } 111 112 virtual void RefreshDiskProperties(const Disk* disk) OVERRIDE { 113 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 114 DCHECK(disk); 115 if (!CrosLibrary::Get()->EnsureLoaded()) { 116 OnGetDiskProperties(disk->device_path().c_str(), 117 NULL, 118 MOUNT_METHOD_ERROR_LOCAL, 119 kLibraryNotLoaded); 120 return; 121 } 122 GetDiskProperties(disk->device_path().c_str(), 123 &MountLibraryImpl::GetDiskPropertiesCallback, 124 this); 125 } 126 127 const DiskMap& disks() const OVERRIDE { return disks_; } 128 129 private: 130 131 // Callback for MountRemovableDevice method. 132 static void MountRemovableDeviceCallback(void* object, 133 const char* device_path, 134 const char* mount_path, 135 MountMethodErrorType error, 136 const char* error_message) { 137 DCHECK(object); 138 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); 139 self->OnMountRemovableDevice(device_path, 140 mount_path, 141 error, 142 error_message); 143 } 144 145 // Callback for UnmountRemovableDevice method. 146 static void UnmountRemovableDeviceCallback(void* object, 147 const char* device_path, 148 const char* mount_path, 149 MountMethodErrorType error, 150 const char* error_message) { 151 DCHECK(object); 152 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); 153 self->OnUnmountRemovableDevice(device_path, 154 error, 155 error_message); 156 } 157 158 // Callback for disk information retrieval calls. 159 static void GetDiskPropertiesCallback(void* object, 160 const char* device_path, 161 const DiskInfo* disk, 162 MountMethodErrorType error, 163 const char* error_message) { 164 DCHECK(object); 165 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); 166 self->OnGetDiskProperties(device_path, 167 disk, 168 error, 169 error_message); 170 } 171 172 // Callback for RequestMountInfo call. 173 static void RequestMountInfoCallback(void* object, 174 const char** devices, 175 size_t device_len, 176 MountMethodErrorType error, 177 const char* error_message) { 178 DCHECK(object); 179 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); 180 self->OnRequestMountInfo(devices, 181 device_len, 182 error, 183 error_message); 184 } 185 186 // This method will receive events that are caused by drive status changes. 187 static void MonitorMountEventsHandler(void* object, 188 MountEventType evt, 189 const char* device_path) { 190 DCHECK(object); 191 MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); 192 self->OnMountEvent(evt, device_path); 193 } 194 195 196 void OnMountRemovableDevice(const char* device_path, 197 const char* mount_path, 198 MountMethodErrorType error, 199 const char* error_message) { 200 DCHECK(device_path); 201 202 if (error == MOUNT_METHOD_ERROR_NONE && device_path && mount_path) { 203 std::string path(device_path); 204 DiskMap::iterator iter = disks_.find(path); 205 if (iter == disks_.end()) { 206 // disk might have been removed by now? 207 return; 208 } 209 Disk* disk = iter->second; 210 DCHECK(disk); 211 disk->set_mount_path(mount_path); 212 FireDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk); 213 } else { 214 LOG(WARNING) << "Mount request failed for device " 215 << device_path << ", with error: " 216 << (error_message ? error_message : "Unknown"); 217 } 218 } 219 220 void OnUnmountRemovableDevice(const char* device_path, 221 MountMethodErrorType error, 222 const char* error_message) { 223 DCHECK(device_path); 224 if (error == MOUNT_METHOD_ERROR_NONE && device_path) { 225 std::string path(device_path); 226 DiskMap::iterator iter = disks_.find(path); 227 if (iter == disks_.end()) { 228 // disk might have been removed by now? 229 return; 230 } 231 Disk* disk = iter->second; 232 DCHECK(disk); 233 disk->clear_mount_path(); 234 FireDiskStatusUpdate(MOUNT_DISK_UNMOUNTED, disk); 235 } else { 236 LOG(WARNING) << "Unmount request failed for device " 237 << device_path << ", with error: " 238 << (error_message ? error_message : "Unknown"); 239 } 240 } 241 242 void OnGetDiskProperties(const char* device_path, 243 const DiskInfo* disk1, 244 MountMethodErrorType error, 245 const char* error_message) { 246 DCHECK(device_path); 247 if (error == MOUNT_METHOD_ERROR_NONE && device_path) { 248 // TODO(zelidrag): Find a better way to filter these out before we 249 // fetch the properties: 250 // Ignore disks coming from the device we booted the system from. 251 252 // This cast is temporal solution, until we merge DiskInfo and 253 // DiskInfoAdvanced into single interface. 254 const DiskInfoAdvanced* disk = 255 reinterpret_cast<const DiskInfoAdvanced*>(disk1); 256 if (disk->on_boot_device()) 257 return; 258 259 LOG(WARNING) << "Found disk " << device_path; 260 // Delete previous disk info for this path: 261 bool is_new = true; 262 std::string device_path_string(device_path); 263 DiskMap::iterator iter = disks_.find(device_path_string); 264 if (iter != disks_.end()) { 265 delete iter->second; 266 disks_.erase(iter); 267 is_new = false; 268 } 269 std::string path; 270 std::string mountpath; 271 std::string systempath; 272 std::string filepath; 273 std::string devicelabel; 274 std::string drivelabel; 275 std::string parentpath; 276 277 if (disk->path() != NULL) 278 path = disk->path(); 279 280 if (disk->mount_path() != NULL) 281 mountpath = disk->mount_path(); 282 283 if (disk->system_path() != NULL) 284 systempath = disk->system_path(); 285 286 if (disk->file_path() != NULL) 287 filepath = disk->file_path(); 288 289 if (disk->label() != NULL) 290 devicelabel = disk->label(); 291 292 if (disk->drive_label() != NULL) 293 drivelabel = disk->drive_label(); 294 295 if (disk->partition_slave() != NULL) 296 parentpath = disk->partition_slave(); 297 298 Disk* new_disk = new Disk(path, 299 mountpath, 300 systempath, 301 filepath, 302 devicelabel, 303 drivelabel, 304 parentpath, 305 disk->device_type(), 306 disk->size(), 307 disk->is_drive(), 308 disk->is_read_only(), 309 disk->has_media(), 310 disk->on_boot_device()); 311 disks_.insert( 312 std::pair<std::string, Disk*>(device_path_string, new_disk)); 313 FireDiskStatusUpdate(is_new ? MOUNT_DISK_ADDED : MOUNT_DISK_CHANGED, 314 new_disk); 315 } else { 316 LOG(WARNING) << "Property retrieval request failed for device " 317 << device_path << ", with error: " 318 << (error_message ? error_message : "Unknown"); 319 } 320 } 321 322 void OnRequestMountInfo(const char** devices, 323 size_t devices_len, 324 MountMethodErrorType error, 325 const char* error_message) { 326 std::set<std::string> current_device_set; 327 if (error == MOUNT_METHOD_ERROR_NONE && devices && devices_len) { 328 // Initiate properties fetch for all removable disks, 329 bool found_disk = false; 330 for (size_t i = 0; i < devices_len; i++) { 331 if (!devices[i]) { 332 NOTREACHED(); 333 continue; 334 } 335 current_device_set.insert(std::string(devices[i])); 336 found_disk = true; 337 // Initiate disk property retrieval for each relevant device path. 338 GetDiskProperties(devices[i], 339 &MountLibraryImpl::GetDiskPropertiesCallback, 340 this); 341 } 342 } else if (error != MOUNT_METHOD_ERROR_NONE) { 343 LOG(WARNING) << "Request mount info retrieval request failed with error: " 344 << (error_message ? error_message : "Unknown"); 345 } 346 // Search and remove disks that are no longer present. 347 for (MountLibrary::DiskMap::iterator iter = disks_.begin(); 348 iter != disks_.end(); ) { 349 if (current_device_set.find(iter->first) == current_device_set.end()) { 350 Disk* disk = iter->second; 351 FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); 352 delete iter->second; 353 disks_.erase(iter++); 354 } else { 355 ++iter; 356 } 357 } 358 } 359 360 void OnMountEvent(MountEventType evt, 361 const char* device_path) { 362 if (!device_path) 363 return; 364 MountLibraryEventType type = MOUNT_DEVICE_ADDED; 365 switch (evt) { 366 case DISK_ADDED: 367 case DISK_CHANGED: { 368 GetDiskProperties(device_path, 369 &MountLibraryImpl::GetDiskPropertiesCallback, 370 this); 371 return; 372 } 373 case DISK_REMOVED: { 374 // Search and remove disks that are no longer present. 375 MountLibrary::DiskMap::iterator iter = 376 disks_.find(std::string(device_path)); 377 if (iter != disks_.end()) { 378 Disk* disk = iter->second; 379 FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); 380 delete iter->second; 381 disks_.erase(iter); 382 } 383 return; 384 } 385 case DEVICE_ADDED: { 386 type = MOUNT_DEVICE_ADDED; 387 break; 388 } 389 case DEVICE_REMOVED: { 390 type = MOUNT_DEVICE_REMOVED; 391 break; 392 } 393 case DEVICE_SCANNED: { 394 type = MOUNT_DEVICE_SCANNED; 395 break; 396 } 397 } 398 FireDeviceStatusUpdate(type, std::string(device_path)); 399 } 400 401 void Init() { 402 // Getting the monitor status so that the daemon starts up. 403 mount_status_connection_ = MonitorMountEvents( 404 &MonitorMountEventsHandler, this); 405 } 406 407 void FireDiskStatusUpdate(MountLibraryEventType evt, 408 const Disk* disk) { 409 // Make sure we run on UI thread. 410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 411 FOR_EACH_OBSERVER( 412 Observer, observers_, DiskChanged(evt, disk)); 413 } 414 415 void FireDeviceStatusUpdate(MountLibraryEventType evt, 416 const std::string& device_path) { 417 // Make sure we run on UI thread. 418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 419 FOR_EACH_OBSERVER( 420 Observer, observers_, DeviceChanged(evt, device_path)); 421 } 422 423 // Mount event change observers. 424 ObserverList<Observer> observers_; 425 426 // A reference to the mount api, to allow callbacks when the mount 427 // status changes. 428 MountEventConnection mount_status_connection_; 429 430 // The list of disks found. 431 MountLibrary::DiskMap disks_; 432 433 DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl); 434 }; 435 436 class MountLibraryStubImpl : public MountLibrary { 437 public: 438 MountLibraryStubImpl() {} 439 virtual ~MountLibraryStubImpl() {} 440 441 // MountLibrary overrides. 442 virtual void AddObserver(Observer* observer) OVERRIDE {} 443 virtual void RemoveObserver(Observer* observer) OVERRIDE {} 444 virtual const DiskMap& disks() const OVERRIDE { return disks_; } 445 virtual void RequestMountInfoRefresh() OVERRIDE {} 446 virtual void MountPath(const char* device_path) OVERRIDE {} 447 virtual void UnmountPath(const char* device_path) OVERRIDE {} 448 virtual bool IsBootPath(const char* device_path) OVERRIDE { return true; } 449 450 private: 451 // The list of disks found. 452 DiskMap disks_; 453 454 DISALLOW_COPY_AND_ASSIGN(MountLibraryStubImpl); 455 }; 456 457 // static 458 MountLibrary* MountLibrary::GetImpl(bool stub) { 459 if (stub) 460 return new MountLibraryStubImpl(); 461 else 462 return new MountLibraryImpl(); 463 } 464 465 } // namespace chromeos 466 467 // Allows InvokeLater without adding refcounting. This class is a Singleton and 468 // won't be deleted until it's last InvokeLater is run. 469 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::MountLibraryImpl); 470 471