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/dbus/cros_disks_client.h" | |
6 | |
7 #include <set> | |
8 #include <vector> | |
9 | |
10 #include <sys/statvfs.h> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/memory/scoped_vector.h" | |
14 #include "base/string_util.h" | |
15 #include "chrome/browser/chromeos/system/runtime_environment.h" | |
16 #include "content/public/browser/browser_thread.h" | |
17 #include "dbus/bus.h" | |
18 #include "dbus/message.h" | |
19 #include "dbus/object_proxy.h" | |
20 #include "third_party/cros_system_api/dbus/service_constants.h" | |
21 | |
22 using content::BrowserThread; | |
23 | |
24 namespace chromeos { | |
25 | |
26 namespace { | |
27 | |
28 const char* kDeviceNotFound = "Device could not be found"; | |
29 | |
30 const char* kDefaultMountOptions[] = { | |
31 "rw", | |
32 "nodev", | |
33 "noexec", | |
34 "nosuid", | |
35 "sync" | |
36 }; | |
37 const char* kDefaultUnmountOptions[] = { | |
38 "force" | |
39 }; | |
40 | |
41 enum MountEventType { | |
42 DISK_ADDED, | |
43 DISK_REMOVED, | |
44 DISK_CHANGED, | |
45 DEVICE_ADDED, | |
46 DEVICE_REMOVED, | |
47 DEVICE_SCANNED, | |
48 FORMATTING_FINISHED, | |
49 }; | |
50 | |
51 struct DiskInfo { | |
52 public: | |
53 DiskInfo(const std::string& path, dbus::Response* response) | |
54 : path_(path), | |
55 is_drive_(false), | |
56 has_media_(false), | |
57 on_boot_device_(false), | |
58 device_type_(UNDEFINED), | |
59 total_size_(0), | |
60 is_read_only_(false), | |
61 is_hidden_(true) { | |
62 InitializeFromResponse(response); | |
63 } | |
64 | |
65 // DBus service path. | |
66 std::string path() const { return path_; } | |
satorux1
2011/11/07 23:21:55
dbus_service_path() would be clearer.
hashimoto
2011/11/08 07:31:05
Looking at cros-disks code, device_path() is more
| |
67 // Disk mount path. | |
68 std::string mount_path() const { return mount_path_; } | |
satorux1
2011/11/07 23:21:55
Let's add a blank line between accessors.
hashimoto
2011/11/08 07:31:05
Done.
| |
69 // Disk system path. | |
70 std::string system_path() const { return system_path_; } | |
satorux1
2011/11/07 23:21:55
Would be nice to add some example for the three pa
hashimoto
2011/11/08 07:31:05
Done.
| |
71 // Is disk into a drive (i.e. /dev/sdb vs, /dev/sdb1). | |
72 bool is_drive() const { return is_drive_; } | |
73 // Does the disk have media content. | |
74 bool has_media() const { return has_media_; } | |
75 // Is the disk on deveice we booted the machien from. | |
76 bool on_boot_device() const { return on_boot_device_; } | |
77 // Disk file path (e.g /dev/sdb). | |
78 std::string file_path() const { return file_path_; } | |
79 // Disk label. | |
80 std::string label() const { return label_; } | |
81 // Disk model | |
82 std::string drive_label() const { return drive_model_; } | |
83 // Partition table path of the device, if device is partition. | |
84 std::string partition_slave() const { return partition_slave_; } | |
85 // Device type. Not working well, yet. | |
86 DeviceType device_type() const { return device_type_; } | |
87 // Total size of the disk. | |
88 uint64 size() const { return total_size_; } | |
89 // Is the device read-only. | |
90 bool is_read_only() const { return is_read_only_; } | |
91 bool is_hidden() const { return is_hidden_; } | |
92 | |
93 private: | |
94 DeviceType GetDeviceType(bool is_optical, bool is_rotational) { | |
95 if (is_optical) | |
96 return OPTICAL; | |
97 if (is_rotational) | |
98 return HDD; | |
99 return FLASH; | |
100 } | |
101 | |
102 bool MaybePopValue(dbus::MessageReader* reader, bool* value) { | |
satorux1
2011/11/07 23:21:55
Function comment is missing. Please fix other plac
hashimoto
2011/11/08 07:31:05
Done.
| |
103 if (!reader) | |
104 return false; | |
105 reader->PopBool(value); | |
106 return true; | |
satorux1
2011/11/07 23:21:55
PopBool() may fail, but this function always retur
hashimoto
2011/11/08 07:31:05
Done.
| |
107 } | |
108 | |
109 bool MaybePopValue(dbus::MessageReader* reader, std::string* value) { | |
satorux1
2011/11/07 23:21:55
Function overriding like this is prohibited by the
hashimoto
2011/11/08 07:31:05
Done.
| |
110 if (!reader) | |
111 return false; | |
112 reader->PopString(value); | |
113 return true; | |
114 } | |
115 | |
116 bool MaybePopValue(dbus::MessageReader* reader, uint64* value) { | |
117 if (!reader) | |
118 return false; | |
119 reader->PopUint64(value); | |
120 return true; | |
121 } | |
122 | |
123 void InitializeFromResponse(dbus::Response* response) { | |
124 dbus::MessageReader reader(response); | |
125 dbus::MessageReader array_reader(response); | |
126 if (!reader.PopArray(&array_reader)) { | |
127 LOG(ERROR) << "Invalid response: " << response->ToString(); | |
128 return; | |
129 } | |
130 ScopedVector<dbus::MessageReader> values_owner; | |
satorux1
2011/11/07 23:21:55
readers_owner? see below
hashimoto
2011/11/08 07:31:05
Done.
| |
131 std::map<std::string, dbus::MessageReader*> properties; | |
132 while (array_reader.HasMoreData()) { | |
133 // |values_owner| is responsible to delete |value| | |
134 dbus::MessageReader* value = new dbus::MessageReader(response); | |
satorux1
2011/11/07 23:21:55
value -> reader? This is a MessageReader rather th
hashimoto
2011/11/08 07:31:05
Done.
| |
135 values_owner.push_back(value); | |
136 dbus::MessageReader dict_entry_reader(response); | |
137 std::string key; | |
138 if (!array_reader.PopDictEntry(&dict_entry_reader) || | |
139 !dict_entry_reader.PopString(&key) || | |
140 !dict_entry_reader.PopVariant(value)) { | |
141 LOG(ERROR) << "Invalid response: " << response->ToString(); | |
142 return; | |
143 } | |
144 properties[key] = value; | |
145 } | |
146 MaybePopValue(properties[cros_disks::kDeviceIsDrive], &is_drive_); | |
147 MaybePopValue(properties[cros_disks::kDeviceIsReadOnly], &is_read_only_); | |
148 MaybePopValue(properties[cros_disks::kDevicePresentationHide], &is_hidden_); | |
149 MaybePopValue(properties[cros_disks::kDeviceIsMediaAvailable], &has_media_); | |
150 MaybePopValue(properties[cros_disks::kDeviceIsOnBootDevice], | |
151 &on_boot_device_); | |
152 MaybePopValue(properties[cros_disks::kNativePath], &system_path_); | |
153 MaybePopValue(properties[cros_disks::kDeviceFile], &file_path_); | |
154 MaybePopValue(properties[cros_disks::kDriveModel], &drive_model_); | |
155 MaybePopValue(properties[cros_disks::kIdLabel], &label_); | |
156 MaybePopValue(properties[cros_disks::kDeviceSize], &total_size_); | |
satorux1
2011/11/07 23:21:55
As you know, parsing string-to-varint dictionary s
hashimoto
2011/11/08 07:31:05
Done.
| |
157 | |
158 if (properties[cros_disks::kDeviceMountPaths]) { | |
159 std::vector<std::string> mount_paths; | |
160 if (!properties[cros_disks::kDeviceMountPaths]->PopArrayOfStrings( | |
satorux1
2011/11/07 23:21:55
The map is looked up twice here. You can save one
hashimoto
2011/11/08 07:31:05
Done.
| |
161 &mount_paths)) | |
162 if (!mount_paths.empty()) | |
163 mount_path_ = mount_paths[0]; | |
164 } | |
165 bool is_rotational, is_optical; | |
166 if (MaybePopValue(properties[cros_disks::kDriveIsRotational], | |
167 &is_rotational) && | |
168 MaybePopValue(properties[cros_disks::kDeviceIsOpticalDisc], | |
169 &is_optical)) | |
170 device_type_ = GetDeviceType(is_optical, is_rotational); | |
171 } | |
satorux1
2011/11/07 23:21:55
BTW, structs are supposed be just data. Having non
hashimoto
2011/11/08 07:31:05
Changed it to a class.
| |
172 | |
173 std::string path_; | |
174 std::string mount_path_; | |
175 std::string system_path_; | |
176 bool is_drive_; | |
177 bool has_media_; | |
178 bool on_boot_device_; | |
179 | |
180 std::string file_path_; | |
181 std::string label_; | |
182 std::string drive_model_; | |
183 std::string partition_slave_; | |
184 DeviceType device_type_; | |
185 uint64 total_size_; | |
186 bool is_read_only_; | |
187 bool is_hidden_; | |
188 }; | |
189 | |
190 class CrosDisksDBusProxy { | |
satorux1
2011/11/07 23:21:55
Please add some class comment.
hashimoto
2011/11/08 07:31:05
Done.
| |
191 public: | |
192 typedef base::Callback<void()> ErrorCallback; | |
193 typedef base::Callback<void()> MountCallback; | |
194 typedef base::Callback<void(const std::string&)> UnmountCallback; | |
195 typedef base::Callback<void(const std::vector<std::string>) | |
satorux1
2011/11/07 23:21:55
Can you add '&'?
(const std::vector<std::string>&
hashimoto
2011/11/08 07:31:05
Done.
| |
196 > EnumerateAutoMountableDevicesCallback; | |
197 typedef base::Callback<void(const std::string&, bool)> FormatDeviceCallback; | |
198 typedef base::Callback<void(const DiskInfo&)> GetDevicePropertiesCallback; | |
199 typedef base::Callback<void(MountError, const std::string&, MountType, | |
200 const std::string&)> MountCompletedHandler; | |
201 typedef base::Callback<void(MountEventType, const std::string&) | |
202 > MountEventHandler; | |
203 | |
204 explicit CrosDisksDBusProxy(dbus::Bus* bus) | |
205 : proxy_(bus->GetObjectProxy(cros_disks::kCrosDisksServiceName, | |
206 cros_disks::kCrosDisksServicePath)), | |
207 weak_ptr_factory_(this) { | |
208 } | |
209 | |
210 void Mount(const std::string& source_path, MountType type, | |
211 const MountPathOptions& options, MountCallback callback, | |
212 ErrorCallback error_callback) { | |
213 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface, | |
214 cros_disks::kMount); | |
215 dbus::MessageWriter writer(&method_call); | |
216 writer.AppendString(source_path); | |
217 writer.AppendString(""); // auto detect filesystem. | |
218 std::vector<std::string> mount_options(kDefaultMountOptions, | |
219 kDefaultMountOptions + | |
220 arraysize(kDefaultMountOptions)); | |
221 writer.AppendArrayOfStrings(mount_options); | |
222 proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
223 base::Bind(&CrosDisksDBusProxy::OnMount, | |
224 weak_ptr_factory_.GetWeakPtr(), | |
225 callback, | |
226 error_callback)); | |
227 } | |
228 | |
229 void Unmount(const std::string& device_path, UnmountCallback callback, | |
230 ErrorCallback error_callback) { | |
231 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface, | |
232 cros_disks::kUnmount); | |
233 dbus::MessageWriter writer(&method_call); | |
234 writer.AppendString(device_path); | |
235 std::vector<std::string> unmount_options(kDefaultUnmountOptions, | |
236 kDefaultUnmountOptions + | |
237 arraysize(kDefaultUnmountOptions)); | |
238 writer.AppendArrayOfStrings(unmount_options); | |
239 proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
240 base::Bind(&CrosDisksDBusProxy::OnUnmount, | |
241 weak_ptr_factory_.GetWeakPtr(), | |
242 device_path, | |
243 callback, | |
244 error_callback)); | |
245 } | |
246 | |
247 void EnumerateAutoMountableDevices( | |
248 EnumerateAutoMountableDevicesCallback callback, | |
249 ErrorCallback error_callback) { | |
250 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface, | |
251 cros_disks::kEnumerateAutoMountableDevices); | |
252 proxy_->CallMethod( | |
253 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
254 base::Bind(&CrosDisksDBusProxy::OnEnumerateAutoMountableDevices, | |
255 weak_ptr_factory_.GetWeakPtr(), | |
256 callback, | |
257 error_callback)); | |
258 } | |
259 | |
260 void FormatDevice(const std::string& device_path, | |
261 const std::string& filesystem, | |
262 FormatDeviceCallback callback, | |
263 ErrorCallback error_callback) { | |
264 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface, | |
265 cros_disks::kFormatDevice); | |
266 dbus::MessageWriter writer(&method_call); | |
267 writer.AppendString(device_path); | |
268 writer.AppendString(filesystem); | |
269 proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
270 base::Bind(&CrosDisksDBusProxy::OnFormatDevice, | |
271 weak_ptr_factory_.GetWeakPtr(), | |
272 device_path, | |
273 callback, | |
274 error_callback)); | |
275 } | |
276 | |
277 void GetDeviceProperties(const std::string& device_path, | |
278 GetDevicePropertiesCallback callback, | |
279 ErrorCallback error_callback) { | |
280 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface, | |
281 cros_disks::kGetDeviceProperties); | |
282 dbus::MessageWriter writer(&method_call); | |
283 writer.AppendString(device_path); | |
284 proxy_->CallMethod(&method_call, | |
285 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
286 base::Bind(&CrosDisksDBusProxy::OnGetDeviceProperties, | |
287 weak_ptr_factory_.GetWeakPtr(), | |
288 device_path, | |
289 callback, | |
290 error_callback)); | |
291 } | |
292 | |
293 void SetUpConnections(MountEventHandler mount_event_handler, | |
294 MountCompletedHandler mount_completed_handler) { | |
295 static const SignalEventTuple kSignalEventTuples[] = { | |
296 { "DeviceAdded", DEVICE_ADDED }, | |
297 { "DeviceScanned", DEVICE_SCANNED }, | |
298 { "DeviceRemoved", DEVICE_REMOVED }, | |
299 { "DiskAdded", DISK_ADDED }, | |
300 { "DiskChanged", DISK_CHANGED }, | |
301 { "DiskRemoved", DISK_REMOVED }, | |
302 { "FormattingFinished", FORMATTING_FINISHED }, | |
303 }; | |
304 const size_t kNumSignalEventTuples = arraysize(kSignalEventTuples); | |
305 | |
306 for (size_t i = 0; i < kNumSignalEventTuples; ++i) { | |
307 proxy_->ConnectToSignal( | |
308 cros_disks::kCrosDisksInterface, | |
309 kSignalEventTuples[i].signal_name, | |
310 base::Bind(&CrosDisksDBusProxy::OnMountEvent, | |
311 weak_ptr_factory_.GetWeakPtr(), | |
312 kSignalEventTuples[i].event_type, | |
313 mount_event_handler), | |
314 base::Bind(&CrosDisksDBusProxy::OnSignalConnected, | |
315 weak_ptr_factory_.GetWeakPtr())); | |
316 } | |
317 proxy_->ConnectToSignal( | |
318 cros_disks::kCrosDisksInterface, | |
319 "MountCompleted", | |
320 base::Bind(&CrosDisksDBusProxy::OnMountCompleted, | |
321 weak_ptr_factory_.GetWeakPtr(), | |
322 mount_completed_handler ), | |
323 base::Bind(&CrosDisksDBusProxy::OnSignalConnected, | |
324 weak_ptr_factory_.GetWeakPtr())); | |
325 } | |
326 | |
327 private: | |
328 struct SignalEventTuple{ | |
satorux1
2011/11/07 23:21:55
space before {
hashimoto
2011/11/08 07:31:05
Done.
| |
329 const char *signal_name; | |
330 MountEventType event_type; | |
331 }; | |
332 | |
333 void OnMount(MountCallback callback, ErrorCallback error_callback, | |
satorux1
2011/11/07 23:21:55
Move ErrorCallback error_callback to the next lin
hashimoto
2011/11/08 07:31:05
Done.
| |
334 dbus::Response* response) { | |
335 if (!response) { | |
336 error_callback.Run(); | |
337 return; | |
338 } | |
339 callback.Run(); | |
340 } | |
341 | |
342 void OnUnmount(const std::string& device_path, UnmountCallback callback, | |
343 ErrorCallback error_callback, dbus::Response* response) { | |
344 if (!response) { | |
345 error_callback.Run(); | |
346 return; | |
347 } | |
348 callback.Run(device_path); | |
349 } | |
350 | |
351 void OnEnumerateAutoMountableDevices( | |
352 EnumerateAutoMountableDevicesCallback callback, | |
353 ErrorCallback error_callback, | |
354 dbus::Response* response) { | |
355 if (!response) { | |
356 error_callback.Run(); | |
357 return; | |
358 } | |
359 dbus::MessageReader reader(response); | |
360 std::vector<std::string> device_paths; | |
361 if (!reader.PopArrayOfStrings(&device_paths)) { | |
satorux1
2011/11/07 23:21:55
Add something like
LOG(ERROR) << "Invalid respons
hashimoto
2011/11/08 07:31:05
Done.
| |
362 error_callback.Run(); | |
363 return; | |
364 } | |
365 callback.Run(device_paths); | |
366 } | |
367 | |
368 void OnFormatDevice(const std::string& device_path, | |
369 FormatDeviceCallback callback, | |
370 ErrorCallback error_callback, | |
371 dbus::Response* response) { | |
372 if (!response) { | |
373 error_callback.Run(); | |
374 return; | |
375 } | |
376 dbus::MessageReader reader(response); | |
377 bool success = false; | |
378 if (!reader.PopBool(&success)) { | |
satorux1
2011/11/07 23:21:55
ditto.
hashimoto
2011/11/08 07:31:05
Done.
| |
379 error_callback.Run(); | |
380 return; | |
381 } | |
382 callback.Run(device_path, success); | |
383 } | |
384 | |
385 void OnGetDeviceProperties(const std::string& device_path, | |
386 GetDevicePropertiesCallback callback, | |
387 ErrorCallback error_callback, | |
388 dbus::Response* response) { | |
389 if (!response) { | |
390 error_callback.Run(); | |
391 return; | |
392 } | |
393 DiskInfo disk(device_path, response); | |
394 callback.Run(disk); | |
395 } | |
396 | |
397 void OnMountEvent(MountEventType event_type, MountEventHandler handler, | |
398 dbus::Signal* signal) { | |
399 dbus::MessageReader reader(signal); | |
400 std::string device; | |
401 if (!reader.PopString(&device)) { | |
402 LOG(ERROR) << "Invalid signal: " << signal->ToString(); | |
403 return; | |
404 } | |
405 handler.Run(event_type, device); | |
406 } | |
407 | |
408 void OnMountCompleted(MountCompletedHandler handler, dbus::Signal* signal) { | |
409 dbus::MessageReader reader(signal); | |
410 unsigned int error_code = 0; | |
411 std::string source_path; | |
412 unsigned int mount_type = 0; | |
413 std::string mount_path; | |
414 if (!reader.PopUint32(&error_code) || | |
415 !reader.PopString(&source_path) || | |
416 !reader.PopUint32(&mount_type) || | |
417 !reader.PopString(&mount_path)) { | |
418 LOG(ERROR) << "Invalid signal: " << signal->ToString(); | |
419 return; | |
420 } | |
421 handler.Run(static_cast<MountError>(error_code), source_path, | |
422 static_cast<MountType>(mount_type), mount_path); | |
423 } | |
424 | |
425 void OnSignalConnected(const std::string& interface, | |
426 const std::string& signal, bool successed) { | |
427 LOG_IF(ERROR, !successed) << "Connect to " << interface << " " << | |
428 signal << " failed."; | |
429 } | |
430 | |
431 dbus::ObjectProxy* proxy_; | |
432 base::WeakPtrFactory<CrosDisksDBusProxy> weak_ptr_factory_; | |
433 }; | |
434 | |
435 class CrosDisksClientImpl : public CrosDisksClient { | |
satorux1
2011/11/07 23:21:55
Please add some class comment.
hashimoto
2011/11/08 07:31:05
Done.
| |
436 public: | |
437 explicit CrosDisksClientImpl(dbus::Bus* bus) | |
438 : dbus_proxy_(new CrosDisksDBusProxy(bus)), | |
439 weak_ptr_factory_(this) { | |
440 dbus_proxy_->SetUpConnections( | |
441 base::Bind(&CrosDisksClientImpl::OnMountEvent, | |
442 weak_ptr_factory_.GetWeakPtr()), | |
443 base::Bind(&CrosDisksClientImpl::OnMountCompleted, | |
444 weak_ptr_factory_.GetWeakPtr())); | |
445 } | |
446 | |
447 ~CrosDisksClientImpl() { | |
448 } | |
449 | |
450 virtual void AddObserver(Observer* observer) OVERRIDE { | |
451 observers_.AddObserver(observer); | |
452 } | |
453 | |
454 virtual void RemoveObserver(Observer* observer) OVERRIDE { | |
455 observers_.RemoveObserver(observer); | |
456 } | |
457 | |
458 virtual void MountPath(const std::string& source_path, MountType type, | |
459 const MountPathOptions& options) OVERRIDE { | |
460 // Hidden and non-existent devices should not be mounted. | |
461 if (type == MOUNT_TYPE_DEVICE) { | |
462 DiskMap::const_iterator it = disks_.find(source_path); | |
463 if (it == disks_.end() || it->second->is_hidden()) { | |
464 OnMountCompleted(MOUNT_ERROR_INTERNAL, source_path, type, ""); | |
465 return; | |
466 } | |
467 } | |
468 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
469 dbus_proxy_->Mount(source_path, type, options, | |
470 // When succeeds, OnMountCompleted will be called by | |
471 // "MountCompleted" signal instead. | |
472 base::Bind(&DoNothing), | |
satorux1
2011/11/07 23:21:55
Instead of this, I think you can pass base::Closur
hashimoto
2011/11/08 07:31:05
Passing base::Closure() results in calling NULL fu
| |
473 base::Bind(&CrosDisksClientImpl::OnMountCompleted, | |
474 weak_ptr_factory_.GetWeakPtr(), | |
475 MOUNT_ERROR_INTERNAL, | |
476 source_path, | |
477 type, | |
478 "")); | |
479 } | |
480 | |
481 virtual void UnmountPath(const std::string& mount_path) OVERRIDE { | |
482 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
483 dbus_proxy_->Unmount(mount_path, | |
484 base::Bind(&CrosDisksClientImpl::OnUnmountPath, | |
485 weak_ptr_factory_.GetWeakPtr()), | |
486 base::Bind(&DoNothing)); | |
487 } | |
488 | |
489 virtual void GetSizeStatsOnFileThread(const std::string& mount_path, | |
490 size_t* total_size_kb, | |
491 size_t* remaining_size_kb) OVERRIDE { | |
492 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
493 | |
494 uint64_t total_size_uint64 = 0; | |
495 uint64_t remaining_size_uint64 = 0; | |
496 | |
497 struct statvfs stat; | |
satorux1
2011/11/07 23:21:55
Maybe zero-clear the stat?
struct statvfs stat
hashimoto
2011/11/08 07:31:05
Done.
| |
498 if (statvfs(mount_path.c_str(), &stat) == 0) { | |
499 total_size_uint64 = | |
500 static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize; | |
501 remaining_size_uint64 = | |
502 static_cast<uint64_t>(stat.f_bfree) * stat.f_frsize; | |
503 } | |
504 *total_size_kb = static_cast<size_t>(total_size_uint64 / 1024); | |
505 *remaining_size_kb = static_cast<size_t>(remaining_size_uint64 / 1024); | |
506 } | |
507 | |
508 virtual void FormatUnmountedDevice(const std::string& file_path) OVERRIDE { | |
509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
510 for (CrosDisksClient::DiskMap::iterator it = disks_.begin(); | |
511 it != disks_.end(); ++it) { | |
512 if (it->second->file_path() == file_path && | |
513 !it->second->mount_path().empty()) { | |
514 LOG(ERROR) << "Device is still mounted: " << file_path; | |
515 OnFormatDevice(file_path, false); | |
516 return; | |
517 } | |
518 } | |
519 dbus_proxy_->FormatDevice(file_path, "vfat", | |
satorux1
2011/11/07 23:21:55
"vfat" -> make it a constant like const char kForm
hashimoto
2011/11/08 07:31:05
Done.
| |
520 base::Bind(&CrosDisksClientImpl::OnFormatDevice, | |
521 weak_ptr_factory_.GetWeakPtr()), | |
522 base::Bind(&CrosDisksClientImpl::OnFormatDevice, | |
523 weak_ptr_factory_.GetWeakPtr(), | |
524 file_path, | |
525 false)); | |
526 } | |
527 | |
528 virtual void FormatMountedDevice(const std::string& mount_path) OVERRIDE { | |
529 Disk* disk = NULL; | |
530 for (CrosDisksClient::DiskMap::iterator it = disks_.begin(); | |
531 it != disks_.end(); ++it) { | |
532 if (it->second->mount_path() == mount_path) { | |
533 disk = it->second; | |
534 break; | |
535 } | |
536 } | |
537 if (!disk) { | |
538 LOG(ERROR) << "Device with this mount path not found: " << mount_path; | |
539 OnFormatDevice(mount_path, false); | |
540 return; | |
541 } | |
542 if (formatting_pending_.find(disk->device_path()) != | |
543 formatting_pending_.end()) { | |
544 LOG(ERROR) << "Formatting is already pending: " << mount_path; | |
545 OnFormatDevice(mount_path, false); | |
546 return; | |
547 } | |
548 // Formatting process continues, after unmounting. | |
549 formatting_pending_[disk->device_path()] = disk->file_path(); | |
550 UnmountPath(disk->mount_path()); | |
551 } | |
552 | |
553 virtual void UnmountDeviceRecursive( | |
554 const std::string& device_path, | |
555 UnmountDeviceRecursiveCallbackType callback, | |
556 void* user_data) OVERRIDE { | |
557 bool success = true; | |
558 std::string error_message; | |
559 std::vector<std::string> devices_to_unmount; | |
560 | |
561 // Get list of all devices to unmount. | |
562 int device_path_len = device_path.length(); | |
563 for (DiskMap::iterator it = disks_.begin(); it != disks_.end(); ++it) { | |
564 if (!it->second->mount_path().empty() && | |
565 strncmp(device_path.c_str(), it->second->device_path().c_str(), | |
566 device_path_len) == 0) { | |
567 devices_to_unmount.push_back(it->second->mount_path()); | |
568 } | |
569 } | |
570 // We should detect at least original device. | |
571 if (devices_to_unmount.size() == 0) { | |
satorux1
2011/11/07 23:21:55
devices_to_unmount.empty() ?
hashimoto
2011/11/08 07:31:05
Done.
| |
572 if (disks_.find(device_path) == disks_.end()) { | |
573 success = false; | |
574 error_message = kDeviceNotFound; | |
575 } else { | |
576 // Nothing to unmount. | |
577 callback(user_data, true); | |
578 return; | |
579 } | |
580 } | |
581 if (success) { | |
582 // We will send the same callback data object to all Unmount calls and use | |
583 // it to syncronize callbacks. | |
584 UnmountDeviceRecursiveCallbackData* cb_data = | |
585 new UnmountDeviceRecursiveCallbackData(user_data, callback, | |
586 devices_to_unmount.size()); | |
587 for (std::vector<std::string>::iterator it = devices_to_unmount.begin(); | |
588 it != devices_to_unmount.end(); | |
589 ++it) { | |
590 dbus_proxy_->Unmount( | |
591 *it, | |
592 base::Bind(&CrosDisksClientImpl::OnUnmountDeviceRecursive, | |
593 weak_ptr_factory_.GetWeakPtr(), cb_data, true), | |
594 base::Bind(&CrosDisksClientImpl::OnUnmountDeviceRecursive, | |
595 weak_ptr_factory_.GetWeakPtr(), cb_data, false, *it)); | |
596 } | |
597 } else { | |
598 LOG(WARNING) << "Unmount recursive request failed for device " | |
599 << device_path << ", with error: " << error_message; | |
600 callback(user_data, false); | |
601 } | |
602 } | |
603 | |
604 virtual void RequestMountInfoRefresh() OVERRIDE { | |
605 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
606 dbus_proxy_->EnumerateAutoMountableDevices( | |
607 base::Bind(&CrosDisksClientImpl::OnRequestMountInfo, | |
608 weak_ptr_factory_.GetWeakPtr()), | |
609 base::Bind(&DoNothing)); | |
610 } | |
611 | |
612 const DiskMap& disks() const OVERRIDE { return disks_; } | |
613 const MountPointMap& mount_points() const OVERRIDE { return mount_points_; } | |
614 | |
615 private: | |
616 struct UnmountDeviceRecursiveCallbackData { | |
617 void* user_data; | |
618 UnmountDeviceRecursiveCallbackType callback; | |
619 size_t pending_callbacks_count; | |
620 | |
621 UnmountDeviceRecursiveCallbackData(void* ud, | |
622 UnmountDeviceRecursiveCallbackType cb, | |
623 int count) | |
624 : user_data(ud), | |
625 callback(cb), | |
626 pending_callbacks_count(count) { | |
627 } | |
628 }; | |
629 | |
630 // Callback for UnmountDeviceRecursive. | |
631 void OnUnmountDeviceRecursive(UnmountDeviceRecursiveCallbackData* cb_data, | |
632 bool success, const std::string& mount_path) { | |
633 if (success) { | |
634 // Do standard processing for Unmount event. | |
635 OnUnmountPath(mount_path); | |
636 LOG(INFO) << mount_path << " unmounted."; | |
637 } | |
638 // This is safe as long as all callbacks are called on the same thread as | |
639 // UnmountDeviceRecursive. | |
640 cb_data->pending_callbacks_count--; | |
641 | |
642 if (cb_data->pending_callbacks_count == 0) { | |
643 cb_data->callback(cb_data->user_data, success); | |
644 delete cb_data; | |
645 } | |
646 } | |
647 | |
648 void OnMountCompleted(MountError error_code, const std::string& source_path, | |
649 MountType type, const std::string& mount_path) { | |
650 MountCondition mount_condition = MOUNT_CONDITION_NONE; | |
651 if (type == MOUNT_TYPE_DEVICE) { | |
652 if (error_code == MOUNT_ERROR_UNKNOWN_FILESYSTEM) | |
653 mount_condition = MOUNT_CONDITION_UNKNOWN_FILESYSTEM; | |
654 if (error_code == MOUNT_ERROR_UNSUPORTED_FILESYSTEM) | |
655 mount_condition = MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM; | |
656 } | |
657 const MountPointInfo mount_info(source_path, mount_path, type, | |
658 mount_condition); | |
659 | |
660 FireMountCompleted(MOUNTING, error_code, mount_info); | |
661 | |
662 // If the device is corrupted but it's still possible to format it, it will | |
663 // be fake mounted. | |
664 if ((error_code == MOUNT_ERROR_NONE || mount_info.mount_condition) && | |
665 mount_points_.find(mount_info.mount_path) == mount_points_.end()) { | |
666 mount_points_.insert(MountPointMap::value_type(mount_info.mount_path, | |
667 mount_info)); | |
668 } | |
669 if ((error_code == MOUNT_ERROR_NONE || mount_info.mount_condition) && | |
670 mount_info.mount_type == MOUNT_TYPE_DEVICE && | |
671 !mount_info.source_path.empty() && | |
672 !mount_info.mount_path.empty()) { | |
673 DiskMap::iterator iter = disks_.find(mount_info.source_path); | |
674 if (iter == disks_.end()) { | |
675 // disk might have been removed by now? | |
676 return; | |
677 } | |
678 Disk* disk = iter->second; | |
679 DCHECK(disk); | |
680 disk->set_mount_path(mount_info.mount_path); | |
681 FireDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk); | |
682 } | |
683 } | |
684 | |
685 void OnUnmountPath(const std::string& mount_path) { | |
686 MountPointMap::iterator mount_points_it = mount_points_.find(mount_path); | |
687 if (mount_points_it == mount_points_.end()) | |
688 return; | |
689 // TODO(tbarzic): Add separate, PathUnmounted event to Observer. | |
690 FireMountCompleted(UNMOUNTING, | |
691 MOUNT_ERROR_NONE, | |
692 MountPointInfo(mount_points_it->second.source_path, | |
693 mount_points_it->second.mount_path, | |
694 mount_points_it->second.mount_type, | |
695 mount_points_it->second.mount_condition)); | |
696 std::string path(mount_points_it->second.source_path); | |
697 mount_points_.erase(mount_points_it); | |
698 DiskMap::iterator iter = disks_.find(path); | |
699 if (iter == disks_.end()) { | |
700 // disk might have been removed by now. | |
701 return; | |
702 } | |
703 Disk* disk = iter->second; | |
704 DCHECK(disk); | |
705 disk->clear_mount_path(); | |
706 // Check if there is a formatting scheduled | |
707 PathMap::iterator it = formatting_pending_.find(disk->device_path()); | |
708 if (it != formatting_pending_.end()) { | |
709 const std::string file_path = it->second; | |
710 formatting_pending_.erase(it); | |
711 FormatUnmountedDevice(file_path); | |
712 } | |
713 } | |
714 | |
715 void OnFormatDevice(const std::string& device_path, bool success) { | |
716 if (success) { | |
717 FireDeviceStatusUpdate(MOUNT_FORMATTING_STARTED, device_path); | |
718 } else { | |
719 FireDeviceStatusUpdate(MOUNT_FORMATTING_STARTED, | |
720 std::string("!") + device_path); | |
721 LOG(WARNING) << "Format request failed for device " << device_path; | |
722 } | |
723 } | |
724 | |
725 void OnGetDeviceProperties(const DiskInfo& disk) { | |
satorux1
2011/11/07 23:21:55
disk -> disk_info ?
hashimoto
2011/11/08 07:31:05
Done.
| |
726 // TODO(zelidrag): Find a better way to filter these out before we | |
727 // fetch the properties: | |
728 // Ignore disks coming from the device we booted the system from. | |
729 if (disk.on_boot_device()) | |
730 return; | |
731 | |
732 LOG(WARNING) << "Found disk " << disk.path(); | |
733 // Delete previous disk info for this path: | |
734 bool is_new = true; | |
735 DiskMap::iterator iter = disks_.find(disk.path()); | |
736 if (iter != disks_.end()) { | |
737 delete iter->second; | |
738 disks_.erase(iter); | |
739 is_new = false; | |
740 } | |
741 Disk* new_disk = new Disk(disk.path(), | |
742 disk.mount_path(), | |
743 disk.system_path(), | |
744 disk.file_path(), | |
745 disk.label(), | |
746 disk.drive_label(), | |
747 disk.partition_slave(), | |
748 FindSystemPathPrefix(disk.system_path()), | |
749 disk.device_type(), | |
750 disk.size(), | |
751 disk.is_drive(), | |
752 disk.is_read_only(), | |
753 disk.has_media(), | |
754 disk.on_boot_device(), | |
755 disk.is_hidden()); | |
756 disks_.insert(std::pair<std::string, Disk*>(disk.path(), new_disk)); | |
757 FireDiskStatusUpdate(is_new ? MOUNT_DISK_ADDED : MOUNT_DISK_CHANGED, | |
758 new_disk); | |
759 } | |
760 | |
761 void OnRequestMountInfo(const std::vector<std::string>& devices) { | |
762 std::set<std::string> current_device_set; | |
763 if (!devices.empty()) { | |
764 // Initiate properties fetch for all removable disks, | |
765 for (size_t i = 0; i < devices.size(); i++) { | |
766 current_device_set.insert(devices[i]); | |
767 // Initiate disk property retrieval for each relevant device path. | |
768 dbus_proxy_->GetDeviceProperties( | |
769 devices[i], | |
770 base::Bind(&CrosDisksClientImpl::OnGetDeviceProperties, | |
771 weak_ptr_factory_.GetWeakPtr()), | |
772 base::Bind(&DoNothing)); | |
773 } | |
774 } | |
775 // Search and remove disks that are no longer present. | |
776 for (DiskMap::iterator iter = disks_.begin(); iter != disks_.end(); ) { | |
777 if (current_device_set.find(iter->first) == current_device_set.end()) { | |
778 Disk* disk = iter->second; | |
779 FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); | |
780 delete iter->second; | |
781 disks_.erase(iter++); | |
782 } else { | |
783 ++iter; | |
784 } | |
785 } | |
786 } | |
787 | |
788 void OnMountEvent(MountEventType evt, std::string device_path) { | |
789 CrosDisksClientEventType type = MOUNT_DEVICE_ADDED; | |
790 switch (evt) { | |
791 case DISK_ADDED: { | |
792 dbus_proxy_->GetDeviceProperties( | |
793 device_path, | |
794 base::Bind(&CrosDisksClientImpl::OnGetDeviceProperties, | |
795 weak_ptr_factory_.GetWeakPtr()), | |
796 base::Bind(&DoNothing)); | |
797 return; | |
798 } | |
799 case DISK_REMOVED: { | |
800 // Search and remove disks that are no longer present. | |
801 CrosDisksClient::DiskMap::iterator iter = disks_.find(device_path); | |
802 if (iter != disks_.end()) { | |
803 Disk* disk = iter->second; | |
804 FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); | |
805 delete iter->second; | |
806 disks_.erase(iter); | |
807 } | |
808 return; | |
809 } | |
810 case DEVICE_ADDED: { | |
811 type = MOUNT_DEVICE_ADDED; | |
812 system_path_prefixes_.insert(device_path); | |
813 break; | |
814 } | |
815 case DEVICE_REMOVED: { | |
816 type = MOUNT_DEVICE_REMOVED; | |
817 system_path_prefixes_.erase(device_path); | |
818 break; | |
819 } | |
820 case DEVICE_SCANNED: { | |
821 type = MOUNT_DEVICE_SCANNED; | |
822 break; | |
823 } | |
824 case FORMATTING_FINISHED: { | |
825 // FORMATTING_FINISHED actually returns file path instead of device | |
826 // path. | |
827 device_path = FilePathToDevicePath(device_path); | |
828 if (device_path.empty()) { | |
829 LOG(ERROR) << "Error while handling disks metadata. Cannot find " | |
830 << "device that is being formatted."; | |
831 return; | |
832 } | |
833 type = MOUNT_FORMATTING_FINISHED; | |
834 break; | |
835 } | |
836 default: { | |
837 return; | |
838 } | |
839 } | |
840 FireDeviceStatusUpdate(type, device_path); | |
841 } | |
842 | |
843 void FireDiskStatusUpdate(CrosDisksClientEventType evt, const Disk* disk) { | |
844 // Make sure we run on UI thread. | |
845 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
846 FOR_EACH_OBSERVER(Observer, observers_, DiskChanged(evt, disk)); | |
847 } | |
848 | |
849 void FireDeviceStatusUpdate(CrosDisksClientEventType evt, | |
850 const std::string& device_path) { | |
851 // Make sure we run on UI thread. | |
852 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
853 FOR_EACH_OBSERVER(Observer, observers_, DeviceChanged(evt, device_path)); | |
854 } | |
855 | |
856 void FireMountCompleted(MountEvent event_type, MountError error_code, | |
857 const MountPointInfo& mount_info) { | |
858 // Make sure we run on UI thread. | |
859 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
860 FOR_EACH_OBSERVER(Observer, observers_, | |
861 MountCompleted(event_type, error_code, mount_info)); | |
862 } | |
863 | |
864 std::string FilePathToDevicePath(const std::string& file_path) { | |
865 int failed = (file_path[0] == '!') ? 1 : 0; | |
866 for (CrosDisksClient::DiskMap::iterator it = disks_.begin(); | |
867 it != disks_.end(); ++it) { | |
868 if (it->second->file_path().compare(file_path.c_str() + failed) == 0) { | |
869 if (failed) | |
870 return std::string("!") + it->second->device_path(); | |
871 else | |
872 return it->second->device_path(); | |
873 } | |
874 } | |
875 return EmptyString(); | |
876 } | |
877 | |
878 const std::string& FindSystemPathPrefix(const std::string& system_path) { | |
879 if (system_path.empty()) | |
880 return EmptyString(); | |
881 for (SystemPathPrefixSet::const_iterator it = system_path_prefixes_.begin(); | |
882 it != system_path_prefixes_.end(); | |
883 ++it) { | |
884 if (system_path.find(*it, 0) == 0) | |
885 return *it; | |
886 } | |
887 return EmptyString(); | |
888 } | |
889 | |
890 // A function to be used as an empty callback | |
891 static void DoNothing() { | |
satorux1
2011/11/07 23:21:55
I think you don't need this. See my comment above.
| |
892 } | |
893 | |
894 // Mount event change observers. | |
895 ObserverList<Observer> observers_; | |
896 | |
897 scoped_ptr<CrosDisksDBusProxy> dbus_proxy_; | |
898 | |
899 // The list of disks found. | |
900 CrosDisksClient::DiskMap disks_; | |
901 | |
902 CrosDisksClient::MountPointMap mount_points_; | |
903 | |
904 typedef std::set<std::string> SystemPathPrefixSet; | |
905 SystemPathPrefixSet system_path_prefixes_; | |
906 | |
907 // Set of devices that are supposed to be formated, but are currently waiting | |
908 // to be unmounted. When device is in this map, the formatting process HAVEN'T | |
909 // started yet. | |
910 PathMap formatting_pending_; | |
911 | |
912 base::WeakPtrFactory<CrosDisksClientImpl> weak_ptr_factory_; | |
913 | |
914 DISALLOW_COPY_AND_ASSIGN(CrosDisksClientImpl); | |
915 }; | |
916 | |
917 class CrosDisksClientStubImpl : public CrosDisksClient { | |
918 public: | |
919 CrosDisksClientStubImpl() {} | |
920 virtual ~CrosDisksClientStubImpl() {} | |
921 | |
922 // CrosDisksClient overrides. | |
923 virtual void AddObserver(Observer* observer) OVERRIDE {} | |
924 virtual void RemoveObserver(Observer* observer) OVERRIDE {} | |
925 virtual const DiskMap& disks() const OVERRIDE { return disks_; } | |
926 virtual const MountPointMap& mount_points() const OVERRIDE { | |
927 return mount_points_; | |
928 } | |
929 virtual void RequestMountInfoRefresh() OVERRIDE {} | |
930 virtual void MountPath(const std::string& source_path, MountType type, | |
931 const MountPathOptions& options) OVERRIDE {} | |
932 virtual void UnmountPath(const std::string& mount_path) OVERRIDE {} | |
933 virtual void GetSizeStatsOnFileThread(const std::string& mount_path, | |
934 size_t* total_size_kb, | |
935 size_t* remaining_size_kb) OVERRIDE {} | |
936 virtual void FormatUnmountedDevice(const std::string& device_path) OVERRIDE {} | |
937 virtual void FormatMountedDevice(const std::string& mount_path) OVERRIDE {} | |
938 virtual void UnmountDeviceRecursive( | |
939 const std::string& device_path, | |
940 UnmountDeviceRecursiveCallbackType callback, | |
941 void* user_data) OVERRIDE {} | |
942 private: | |
943 DiskMap disks_; // The list of disks found. | |
944 MountPointMap mount_points_; | |
945 | |
946 DISALLOW_COPY_AND_ASSIGN(CrosDisksClientStubImpl); | |
947 }; | |
948 | |
949 } // namespace | |
950 | |
951 // static | |
952 std::string CrosDisksClient::MountTypeToString(MountType type) { | |
953 switch (type) { | |
954 case MOUNT_TYPE_DEVICE: | |
955 return "device"; | |
956 case MOUNT_TYPE_ARCHIVE: | |
957 return "file"; | |
958 case MOUNT_TYPE_NETWORK_STORAGE: | |
959 return "network"; | |
960 case MOUNT_TYPE_INVALID: | |
961 return "invalid"; | |
962 default: | |
963 NOTREACHED(); | |
964 } | |
965 return EmptyString(); | |
966 } | |
967 | |
968 // static | |
969 std::string CrosDisksClient::MountConditionToString(MountCondition condition) { | |
970 switch (condition) { | |
971 case MOUNT_CONDITION_NONE: | |
972 return EmptyString(); | |
973 case MOUNT_CONDITION_UNKNOWN_FILESYSTEM: | |
974 return "unknown_filesystem"; | |
975 case MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM: | |
976 return "unsupported_filesystem"; | |
977 default: | |
978 NOTREACHED(); | |
979 } | |
980 return EmptyString(); | |
981 } | |
982 | |
983 // static | |
984 MountType CrosDisksClient::MountTypeFromString(const std::string& type_str) { | |
985 if (type_str == "device") | |
986 return MOUNT_TYPE_DEVICE; | |
987 else if (type_str == "network") | |
988 return MOUNT_TYPE_NETWORK_STORAGE; | |
989 else if (type_str == "file") | |
990 return MOUNT_TYPE_ARCHIVE; | |
991 else | |
992 return MOUNT_TYPE_INVALID; | |
993 } | |
994 | |
995 CrosDisksClient::Disk::Disk(const std::string& device_path, | |
996 const std::string& mount_path, | |
997 const std::string& system_path, | |
998 const std::string& file_path, | |
999 const std::string& device_label, | |
1000 const std::string& drive_label, | |
1001 const std::string& parent_path, | |
1002 const std::string& system_path_prefix, | |
1003 DeviceType device_type, | |
1004 uint64 total_size, | |
1005 bool is_parent, | |
1006 bool is_read_only, | |
1007 bool has_media, | |
1008 bool on_boot_device, | |
1009 bool is_hidden) | |
1010 : device_path_(device_path), | |
1011 mount_path_(mount_path), | |
1012 system_path_(system_path), | |
1013 file_path_(file_path), | |
1014 device_label_(device_label), | |
1015 drive_label_(drive_label), | |
1016 parent_path_(parent_path), | |
1017 system_path_prefix_(system_path_prefix), | |
1018 device_type_(device_type), | |
1019 total_size_(total_size), | |
1020 is_parent_(is_parent), | |
1021 is_read_only_(is_read_only), | |
1022 has_media_(has_media), | |
1023 on_boot_device_(on_boot_device), | |
1024 is_hidden_(is_hidden) { | |
1025 } | |
1026 | |
1027 CrosDisksClient::Disk::~Disk() {} | |
1028 | |
1029 // static | |
1030 CrosDisksClient* CrosDisksClient::Create(dbus::Bus* bus) { | |
1031 CrosDisksClient* impl; | |
1032 if (system::runtime_environment::IsRunningOnChromeOS()) | |
1033 impl = new CrosDisksClientImpl(bus); | |
1034 else | |
1035 impl = new CrosDisksClientStubImpl(); | |
1036 return impl; | |
1037 } | |
1038 | |
1039 } // namespace chromeos | |
OLD | NEW |