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
Disk(const std::string & device_path,const std::string & mount_path,const std::string & system_path,const std::string & file_path,const std::string & device_label,const std::string & drive_label,const std::string & parent_path,DeviceType device_type,uint64 total_size,bool is_parent,bool is_read_only,bool has_media,bool on_boot_device)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:
MountLibraryImpl()51 MountLibraryImpl() : mount_status_connection_(NULL) {
52 if (CrosLibrary::Get()->EnsureLoaded())
53 Init();
54 else
55 LOG(ERROR) << kLibraryNotLoaded;
56 }
57
~MountLibraryImpl()58 virtual ~MountLibraryImpl() {
59 if (mount_status_connection_)
60 DisconnectMountEventMonitor(mount_status_connection_);
61 }
62
63 // MountLibrary overrides.
AddObserver(Observer * observer)64 virtual void AddObserver(Observer* observer) OVERRIDE {
65 observers_.AddObserver(observer);
66 }
67
RemoveObserver(Observer * observer)68 virtual void RemoveObserver(Observer* observer) OVERRIDE {
69 observers_.RemoveObserver(observer);
70 }
71
MountPath(const char * device_path)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
UnmountPath(const char * device_path)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
RequestMountInfoRefresh()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
RefreshDiskProperties(const Disk * disk)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
disks() const127 const DiskMap& disks() const OVERRIDE { return disks_; }
128
129 private:
130
131 // Callback for MountRemovableDevice method.
MountRemovableDeviceCallback(void * object,const char * device_path,const char * mount_path,MountMethodErrorType error,const char * error_message)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.
UnmountRemovableDeviceCallback(void * object,const char * device_path,const char * mount_path,MountMethodErrorType error,const char * error_message)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.
GetDiskPropertiesCallback(void * object,const char * device_path,const DiskInfo * disk,MountMethodErrorType error,const char * error_message)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.
RequestMountInfoCallback(void * object,const char ** devices,size_t device_len,MountMethodErrorType error,const char * error_message)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.
MonitorMountEventsHandler(void * object,MountEventType evt,const char * device_path)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
OnMountRemovableDevice(const char * device_path,const char * mount_path,MountMethodErrorType error,const char * error_message)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
OnUnmountRemovableDevice(const char * device_path,MountMethodErrorType error,const char * error_message)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
OnGetDiskProperties(const char * device_path,const DiskInfo * disk1,MountMethodErrorType error,const char * error_message)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
OnRequestMountInfo(const char ** devices,size_t devices_len,MountMethodErrorType error,const char * error_message)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
OnMountEvent(MountEventType evt,const char * device_path)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
Init()401 void Init() {
402 // Getting the monitor status so that the daemon starts up.
403 mount_status_connection_ = MonitorMountEvents(
404 &MonitorMountEventsHandler, this);
405 }
406
FireDiskStatusUpdate(MountLibraryEventType evt,const Disk * disk)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
FireDeviceStatusUpdate(MountLibraryEventType evt,const std::string & device_path)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:
MountLibraryStubImpl()438 MountLibraryStubImpl() {}
~MountLibraryStubImpl()439 virtual ~MountLibraryStubImpl() {}
440
441 // MountLibrary overrides.
AddObserver(Observer * observer)442 virtual void AddObserver(Observer* observer) OVERRIDE {}
RemoveObserver(Observer * observer)443 virtual void RemoveObserver(Observer* observer) OVERRIDE {}
disks() const444 virtual const DiskMap& disks() const OVERRIDE { return disks_; }
RequestMountInfoRefresh()445 virtual void RequestMountInfoRefresh() OVERRIDE {}
MountPath(const char * device_path)446 virtual void MountPath(const char* device_path) OVERRIDE {}
UnmountPath(const char * device_path)447 virtual void UnmountPath(const char* device_path) OVERRIDE {}
IsBootPath(const char * device_path)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
GetImpl(bool stub)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