1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/disks/disk_mount_manager.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <map>
11 #include <set>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "base/barrier_closure.h"
17 #include "base/bind.h"
18 #include "base/callback_helpers.h"
19 #include "base/logging.h"
20 #include "base/macros.h"
21 #include "base/memory/ptr_util.h"
22 #include "base/memory/weak_ptr.h"
23 #include "base/metrics/histogram_functions.h"
24 #include "base/observer_list.h"
25 #include "base/strings/string_util.h"
26 #include "chromeos/constants/chromeos_features.h"
27 #include "chromeos/dbus/cros_disks_client.h"
28 #include "chromeos/dbus/dbus_thread_manager.h"
29 #include "chromeos/disks/disk.h"
30 #include "chromeos/disks/suspend_unmount_manager.h"
31
32 namespace chromeos {
33 namespace disks {
34
35 namespace {
36
37 constexpr char kDeviceNotFound[] = "Device could not be found";
38 DiskMountManager* g_disk_mount_manager = NULL;
39
40 struct UnmountDeviceRecursivelyCallbackData {
UnmountDeviceRecursivelyCallbackDatachromeos::disks::__anon9c9ac18c0111::UnmountDeviceRecursivelyCallbackData41 UnmountDeviceRecursivelyCallbackData(
42 DiskMountManager::UnmountDeviceRecursivelyCallbackType in_callback)
43 : callback(std::move(in_callback)) {}
44
45 DiskMountManager::UnmountDeviceRecursivelyCallbackType callback;
46 MountError error_code = MOUNT_ERROR_NONE;
47 };
48
OnAllUnmountDeviceRecursively(std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data)49 void OnAllUnmountDeviceRecursively(
50 std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data) {
51 std::move(cb_data->callback).Run(cb_data->error_code);
52 }
53
FormatFileSystemTypeToString(FormatFileSystemType filesystem)54 std::string FormatFileSystemTypeToString(FormatFileSystemType filesystem) {
55 switch (filesystem) {
56 case FormatFileSystemType::kUnknown:
57 return "";
58 case FormatFileSystemType::kVfat:
59 return "vfat";
60 case FormatFileSystemType::kExfat:
61 return "exfat";
62 case FormatFileSystemType::kNtfs:
63 return "ntfs";
64 }
65 NOTREACHED() << "Unknown filesystem type " << static_cast<int>(filesystem);
66 return "";
67 }
68
69 // The DiskMountManager implementation.
70 class DiskMountManagerImpl : public DiskMountManager,
71 public CrosDisksClient::Observer {
72 public:
DiskMountManagerImpl()73 DiskMountManagerImpl() : already_refreshed_(false) {
74 DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
75 cros_disks_client_ = dbus_thread_manager->GetCrosDisksClient();
76 suspend_unmount_manager_.reset(new SuspendUnmountManager(this));
77
78 cros_disks_client_->AddObserver(this);
79 }
80
~DiskMountManagerImpl()81 ~DiskMountManagerImpl() override { cros_disks_client_->RemoveObserver(this); }
82
83 // DiskMountManager override.
AddObserver(DiskMountManager::Observer * observer)84 void AddObserver(DiskMountManager::Observer* observer) override {
85 observers_.AddObserver(observer);
86 }
87
88 // DiskMountManager override.
RemoveObserver(DiskMountManager::Observer * observer)89 void RemoveObserver(DiskMountManager::Observer* observer) override {
90 observers_.RemoveObserver(observer);
91 }
92
93 // DiskMountManager override.
MountPath(const std::string & source_path,const std::string & source_format,const std::string & mount_label,const std::vector<std::string> & mount_options,MountType type,MountAccessMode access_mode)94 void MountPath(const std::string& source_path,
95 const std::string& source_format,
96 const std::string& mount_label,
97 const std::vector<std::string>& mount_options,
98 MountType type,
99 MountAccessMode access_mode) override {
100 // Hidden and non-existent devices should not be mounted.
101 if (type == MOUNT_TYPE_DEVICE) {
102 DiskMap::const_iterator it = disks_.find(source_path);
103 if (it == disks_.end() || it->second->is_hidden()) {
104 OnMountCompleted(MountEntry(MOUNT_ERROR_INTERNAL, source_path, type,
105 ""));
106 return;
107 }
108 }
109 std::vector<std::string> options = mount_options;
110 if (base::FeatureList::IsEnabled(chromeos::features::kFsNosymfollow))
111 options.push_back("nosymfollow");
112 cros_disks_client_->Mount(
113 source_path, source_format, mount_label, options, access_mode,
114 REMOUNT_OPTION_MOUNT_NEW_DEVICE,
115 base::BindOnce(&DiskMountManagerImpl::OnMount,
116 weak_ptr_factory_.GetWeakPtr(), source_path, type));
117
118 // Record the access mode option passed to CrosDisks.
119 // This is needed because CrosDisks service methods doesn't return the info
120 // via DBus.
121 access_modes_.insert(std::make_pair(source_path, access_mode));
122 }
123
124 // DiskMountManager override.
UnmountPath(const std::string & mount_path,UnmountPathCallback callback)125 void UnmountPath(const std::string& mount_path,
126 UnmountPathCallback callback) override {
127 UnmountChildMounts(mount_path);
128 cros_disks_client_->Unmount(
129 mount_path, base::BindOnce(&DiskMountManagerImpl::OnUnmountPath,
130 weak_ptr_factory_.GetWeakPtr(),
131 std::move(callback), mount_path));
132 }
133
RemountAllRemovableDrives(MountAccessMode mode)134 void RemountAllRemovableDrives(MountAccessMode mode) override {
135 // TODO(yamaguchi): Retry for tentative remount failures. crbug.com/661455
136 for (const auto& device_path_and_disk : disks_) {
137 const Disk& disk = *device_path_and_disk.second;
138 if (disk.is_read_only_hardware()) {
139 // Read-only devices can be mounted in RO mode only. No need to remount.
140 continue;
141 }
142 if (!disk.is_mounted()) {
143 continue;
144 }
145 RemountRemovableDrive(disk, mode);
146 }
147 }
148
149 // DiskMountManager override.
FormatMountedDevice(const std::string & mount_path,FormatFileSystemType filesystem,const std::string & label)150 void FormatMountedDevice(const std::string& mount_path,
151 FormatFileSystemType filesystem,
152 const std::string& label) override {
153 MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
154 if (mount_point == mount_points_.end()) {
155 LOG(ERROR) << "Mount point with path \"" << mount_path << "\" not found.";
156 // We can't call OnFormatCompleted until |pending_format_changes_| has
157 // been populated.
158 NotifyFormatStatusUpdate(FORMAT_COMPLETED, FORMAT_ERROR_UNKNOWN,
159 mount_path, label);
160 return;
161 }
162
163 std::string device_path = mount_point->second.source_path;
164 const std::string filesystem_str = FormatFileSystemTypeToString(filesystem);
165 pending_format_changes_[device_path] = {filesystem_str, label};
166
167 DiskMap::const_iterator disk = disks_.find(device_path);
168 if (disk == disks_.end()) {
169 LOG(ERROR) << "Device with path \"" << device_path << "\" not found.";
170 OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
171 return;
172 }
173 if (disk->second->is_read_only()) {
174 LOG(ERROR) << "Device with path \"" << device_path << "\" is read-only.";
175 OnFormatCompleted(FORMAT_ERROR_DEVICE_NOT_ALLOWED, device_path);
176 return;
177 }
178
179 if (filesystem == FormatFileSystemType::kUnknown) {
180 LOG(ERROR) << "Unknown filesystem passed to FormatMountedDevice";
181 OnFormatCompleted(FORMAT_ERROR_UNSUPPORTED_FILESYSTEM, device_path);
182 return;
183 }
184
185 UnmountPath(disk->second->mount_path(),
186 base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
187 weak_ptr_factory_.GetWeakPtr(), device_path,
188 filesystem, label));
189 }
190
191 // DiskMountManager override.
SinglePartitionFormatDevice(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label)192 void SinglePartitionFormatDevice(const std::string& device_path,
193 FormatFileSystemType filesystem,
194 const std::string& label) override {
195 DiskMap::const_iterator disk_iter = disks_.find(device_path);
196 if (disk_iter == disks_.end()) {
197 LOG(ERROR) << "Device with path \"" << device_path << "\" not found.";
198 OnPartitionCompleted(device_path, filesystem, label,
199 PARTITION_ERROR_INVALID_DEVICE_PATH);
200 return;
201 }
202
203 UnmountDeviceRecursively(
204 device_path,
205 base::BindOnce(
206 &DiskMountManagerImpl::OnUnmountDeviceForSinglePartitionFormat,
207 weak_ptr_factory_.GetWeakPtr(), device_path, filesystem, label));
208 }
209
RenameMountedDevice(const std::string & mount_path,const std::string & volume_name)210 void RenameMountedDevice(const std::string& mount_path,
211 const std::string& volume_name) override {
212 MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
213 if (mount_point == mount_points_.end()) {
214 LOG(ERROR) << "Mount point with path '" << mount_path << "' not found.";
215 // We can't call OnRenameCompleted until |pending_rename_changes_| has
216 // been populated.
217 NotifyRenameStatusUpdate(RENAME_COMPLETED, RENAME_ERROR_UNKNOWN,
218 mount_path, volume_name);
219 return;
220 }
221
222 std::string device_path = mount_point->second.source_path;
223 pending_rename_changes_[device_path] = volume_name;
224
225 DiskMap::const_iterator iter = disks_.find(device_path);
226 if (iter == disks_.end()) {
227 LOG(ERROR) << "Device with path '" << device_path << "' not found.";
228 OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
229 return;
230 }
231 if (iter->second->is_read_only()) {
232 LOG(ERROR) << "Device with path '" << device_path << "' is read-only.";
233 OnRenameCompleted(RENAME_ERROR_DEVICE_NOT_ALLOWED, device_path);
234 return;
235 }
236
237 UnmountPath(iter->second->mount_path(),
238 base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForRename,
239 weak_ptr_factory_.GetWeakPtr(), device_path,
240 volume_name));
241 }
242
243 // DiskMountManager override.
UnmountDeviceRecursively(const std::string & device_path,UnmountDeviceRecursivelyCallbackType callback)244 void UnmountDeviceRecursively(
245 const std::string& device_path,
246 UnmountDeviceRecursivelyCallbackType callback) override {
247 std::vector<std::string> devices_to_unmount;
248
249 // Get list of all devices to unmount.
250 int device_path_len = device_path.length();
251 for (DiskMap::iterator it = disks_.begin(); it != disks_.end(); ++it) {
252 if (!it->second->mount_path().empty() &&
253 strncmp(device_path.c_str(), it->second->device_path().c_str(),
254 device_path_len) == 0) {
255 devices_to_unmount.push_back(it->second->mount_path());
256 }
257 }
258
259 // We should detect at least original device.
260 if (devices_to_unmount.empty()) {
261 if (disks_.find(device_path) == disks_.end()) {
262 LOG(WARNING) << "Unmount recursive request failed for device "
263 << device_path << ", with error: " << kDeviceNotFound;
264 std::move(callback).Run(MOUNT_ERROR_INVALID_DEVICE_PATH);
265 return;
266 }
267
268 // Nothing to unmount.
269 std::move(callback).Run(MOUNT_ERROR_NONE);
270 return;
271 }
272
273 std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data =
274 std::make_unique<UnmountDeviceRecursivelyCallbackData>(
275 std::move(callback));
276 UnmountDeviceRecursivelyCallbackData* raw_cb_data = cb_data.get();
277 base::RepeatingClosure done_callback = base::BarrierClosure(
278 devices_to_unmount.size(),
279 base::BindOnce(&OnAllUnmountDeviceRecursively, std::move(cb_data)));
280
281 for (size_t i = 0; i < devices_to_unmount.size(); ++i) {
282 cros_disks_client_->Unmount(
283 devices_to_unmount[i],
284 base::BindOnce(&DiskMountManagerImpl::OnUnmountDeviceRecursively,
285 weak_ptr_factory_.GetWeakPtr(), raw_cb_data,
286 devices_to_unmount[i], done_callback));
287 }
288 }
289
290 // DiskMountManager override.
EnsureMountInfoRefreshed(EnsureMountInfoRefreshedCallback callback,bool force)291 void EnsureMountInfoRefreshed(EnsureMountInfoRefreshedCallback callback,
292 bool force) override {
293 if (!force && already_refreshed_) {
294 std::move(callback).Run(true);
295 return;
296 }
297
298 refresh_callbacks_.push_back(std::move(callback));
299 if (refresh_callbacks_.size() == 1) {
300 // If there's no in-flight refreshing task, start it.
301 cros_disks_client_->EnumerateDevices(
302 base::BindOnce(&DiskMountManagerImpl::RefreshAfterEnumerateDevices,
303 weak_ptr_factory_.GetWeakPtr()),
304 base::BindOnce(&DiskMountManagerImpl::RefreshCompleted,
305 weak_ptr_factory_.GetWeakPtr(), false));
306 }
307 }
308
309 // DiskMountManager override.
disks() const310 const DiskMap& disks() const override { return disks_; }
311
312 // DiskMountManager override.
FindDiskBySourcePath(const std::string & source_path) const313 const Disk* FindDiskBySourcePath(
314 const std::string& source_path) const override {
315 DiskMap::const_iterator disk_it = disks_.find(source_path);
316 return disk_it == disks_.end() ? NULL : disk_it->second.get();
317 }
318
319 // DiskMountManager override.
mount_points() const320 const MountPointMap& mount_points() const override { return mount_points_; }
321
322 // DiskMountManager override.
AddDiskForTest(std::unique_ptr<Disk> disk)323 bool AddDiskForTest(std::unique_ptr<Disk> disk) override {
324 if (disks_.find(disk->device_path()) != disks_.end()) {
325 LOG(ERROR) << "Attempt to add a duplicate disk";
326 return false;
327 }
328
329 disks_.insert(std::make_pair(disk->device_path(), std::move(disk)));
330 return true;
331 }
332
333 // DiskMountManager override.
334 // Corresponding disk should be added to the manager before this is called.
AddMountPointForTest(const MountPointInfo & mount_point)335 bool AddMountPointForTest(const MountPointInfo& mount_point) override {
336 if (mount_points_.find(mount_point.mount_path) != mount_points_.end()) {
337 LOG(ERROR) << "Attempt to add a duplicate mount point";
338 return false;
339 }
340 if (mount_point.mount_type == chromeos::MOUNT_TYPE_DEVICE &&
341 disks_.find(mount_point.source_path) == disks_.end()) {
342 LOG(ERROR) << "Device mount points must have a disk entry.";
343 return false;
344 }
345
346 mount_points_.insert(std::make_pair(mount_point.mount_path, mount_point));
347 return true;
348 }
349
350 private:
351 // A struct to represent information about a format changes.
352 struct FormatChange {
353 // new file system type
354 std::string file_system_type;
355 // New volume name
356 std::string volume_name;
357 };
358
359 // Stores new volume name and file system type for a device on which
360 // formatting is invoked on, so that OnFormatCompleted can set it back to
361 // |disks_|. The key is a device_path and the value is a FormatChange.
362 std::map<std::string, FormatChange> pending_format_changes_;
363
364 // Stores device path are being partitioning.
365 // It allows preventing auto-mount of the disks in this set.
366 std::set<std::string> pending_partitioning_disks_;
367
368 // Stores new volume name for a device on which renaming is invoked on, so
369 // that OnRenameCompleted can set it back to |disks_|. The key is a
370 // device_path and the value is new volume_name.
371 std::map<std::string, std::string> pending_rename_changes_;
372
373 // Called on D-Bus CrosDisksClient::Mount() is done.
OnMount(const std::string & source_path,MountType type,bool result)374 void OnMount(const std::string& source_path, MountType type, bool result) {
375 // When succeeds, OnMountCompleted will be called by "MountCompleted",
376 // signal instead. Do nothing now.
377 if (result)
378 return;
379
380 OnMountCompleted(
381 MountEntry(MOUNT_ERROR_INTERNAL, source_path, type, std::string()));
382 }
383
RemountRemovableDrive(const Disk & disk,MountAccessMode access_mode)384 void RemountRemovableDrive(const Disk& disk,
385 MountAccessMode access_mode) {
386 const std::string& mount_path = disk.mount_path();
387 MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
388 if (mount_point == mount_points_.end()) {
389 // Not in mount_points_. This happens when the mount_points and disks_ are
390 // inconsistent.
391 LOG(ERROR) << "Mount point with path \"" << mount_path << "\" not found.";
392 OnMountCompleted(
393 MountEntry(MOUNT_ERROR_PATH_NOT_MOUNTED, disk.device_path(),
394 MOUNT_TYPE_DEVICE, mount_path));
395 return;
396 }
397 const std::string& source_path = mount_point->second.source_path;
398
399 // Update the access mode option passed to CrosDisks.
400 // This is needed because CrosDisks service methods doesn't return the info
401 // via DBus, and must be updated before issuing Mount command as it'll be
402 // read by the handler of MountCompleted signal.
403 access_modes_[source_path] = access_mode;
404
405 cros_disks_client_->Mount(
406 mount_point->second.source_path, std::string(), std::string(), {},
407 access_mode, REMOUNT_OPTION_REMOUNT_EXISTING_DEVICE,
408 base::BindOnce(&DiskMountManagerImpl::OnMount,
409 weak_ptr_factory_.GetWeakPtr(), source_path,
410 mount_point->second.mount_type));
411 }
412
413 // Unmounts all mount points whose source path is transitively parented by
414 // |mount_path|.
UnmountChildMounts(const std::string & mount_path_in)415 void UnmountChildMounts(const std::string& mount_path_in) {
416 std::string mount_path = mount_path_in;
417 // Let's make sure mount path has trailing slash.
418 if (mount_path.back() != '/')
419 mount_path += '/';
420
421 for (MountPointMap::iterator it = mount_points_.begin();
422 it != mount_points_.end();
423 ++it) {
424 if (base::StartsWith(it->second.source_path, mount_path,
425 base::CompareCase::SENSITIVE)) {
426 // TODO(tbarzic): Handle the case where this fails.
427 UnmountPath(it->second.mount_path, UnmountPathCallback());
428 }
429 }
430 }
431
432 // Callback for UnmountDeviceRecursively.
OnUnmountDeviceRecursively(UnmountDeviceRecursivelyCallbackData * cb_data,const std::string & mount_path,base::OnceClosure done_callback,MountError error_code)433 void OnUnmountDeviceRecursively(UnmountDeviceRecursivelyCallbackData* cb_data,
434 const std::string& mount_path,
435 base::OnceClosure done_callback,
436 MountError error_code) {
437 if (error_code == MOUNT_ERROR_PATH_NOT_MOUNTED ||
438 error_code == MOUNT_ERROR_INVALID_PATH) {
439 // The path was already unmounted by something else.
440 error_code = MOUNT_ERROR_NONE;
441 }
442
443 if (error_code == MOUNT_ERROR_NONE) {
444 // Do standard processing for Unmount event.
445 OnUnmountPath(UnmountPathCallback(), mount_path, MOUNT_ERROR_NONE);
446 VLOG(1) << mount_path << " unmounted.";
447 } else {
448 // This causes the last non-success error to be reported.
449 // TODO(amistry): We could ignore certain errors such as
450 // MOUNT_ERROR_PATH_NOT_MOUNTED, or prioritise more important ones.
451 cb_data->error_code = error_code;
452 }
453
454 std::move(done_callback).Run();
455 }
456
457 // CrosDisksClient::Observer override.
OnMountCompleted(const MountEntry & entry)458 void OnMountCompleted(const MountEntry& entry) override {
459 MountCondition mount_condition = MOUNT_CONDITION_NONE;
460 if (entry.mount_type() == MOUNT_TYPE_DEVICE) {
461 if (entry.error_code() == MOUNT_ERROR_UNKNOWN_FILESYSTEM) {
462 mount_condition = MOUNT_CONDITION_UNKNOWN_FILESYSTEM;
463 }
464 if (entry.error_code() == MOUNT_ERROR_UNSUPPORTED_FILESYSTEM) {
465 mount_condition = MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
466 }
467 }
468 const MountPointInfo mount_info(entry.source_path(),
469 entry.mount_path(),
470 entry.mount_type(),
471 mount_condition);
472
473 // If the device is corrupted but it's still possible to format it, it will
474 // be fake mounted.
475 if ((entry.error_code() == MOUNT_ERROR_NONE ||
476 mount_info.mount_condition) &&
477 mount_points_.find(mount_info.mount_path) == mount_points_.end()) {
478 mount_points_.insert(MountPointMap::value_type(mount_info.mount_path,
479 mount_info));
480 }
481
482 Disk* disk = nullptr;
483 if ((entry.error_code() == MOUNT_ERROR_NONE ||
484 mount_info.mount_condition) &&
485 mount_info.mount_type == MOUNT_TYPE_DEVICE &&
486 !mount_info.source_path.empty() &&
487 !mount_info.mount_path.empty()) {
488 DiskMap::iterator iter = disks_.find(mount_info.source_path);
489 if (iter != disks_.end()) { // disk might have been removed by now?
490 disk = iter->second.get();
491 DCHECK(disk);
492 // Currently the MountCompleted signal doesn't tell whether the device
493 // is mounted in read-only mode or not. Instead use the mount option
494 // recorded by DiskMountManagerImpl::MountPath().
495 // |source_path| should be same as |disk->device_path| because
496 // |VolumeManager::OnDiskEvent()| passes the latter to cros-disks as a
497 // source path when mounting a device.
498 AccessModeMap::iterator it = access_modes_.find(entry.source_path());
499
500 // Store whether the disk was mounted in read-only mode due to a policy.
501 disk->set_write_disabled_by_policy(
502 it != access_modes_.end() && !disk->is_read_only_hardware()
503 && it->second == MOUNT_ACCESS_MODE_READ_ONLY);
504 disk->SetMountPath(mount_info.mount_path);
505 // Only set the mount path if the disk is actually mounted. Right now, a
506 // number of code paths (format, rename, unmount) rely on the mount path
507 // being set even if the disk isn't mounted. cros-disks also does some
508 // tracking of non-mounted mount paths. Making this change is
509 // non-trivial.
510 // TODO(amistry): Change these code paths to use device path instead of
511 // mount path.
512 disk->set_mounted(entry.error_code() == MOUNT_ERROR_NONE);
513 }
514 }
515 // Observers may read the values of disks_. So notify them after tweaking
516 // values of disks_.
517 NotifyMountStatusUpdate(MOUNTING, entry.error_code(), mount_info);
518 if (disk) {
519 disk->set_is_first_mount(false);
520 }
521 }
522
523 // Callback for UnmountPath.
OnUnmountPath(UnmountPathCallback callback,const std::string & mount_path,MountError error_code)524 void OnUnmountPath(UnmountPathCallback callback,
525 const std::string& mount_path,
526 MountError error_code) {
527 MountPointMap::iterator mount_points_it = mount_points_.find(mount_path);
528 if (mount_points_it == mount_points_.end()) {
529 // The path was unmounted, but not as a result of this unmount request,
530 // so return error.
531 if (!callback.is_null())
532 std::move(callback).Run(MOUNT_ERROR_INTERNAL);
533 return;
534 }
535
536 if (error_code == MOUNT_ERROR_PATH_NOT_MOUNTED ||
537 error_code == MOUNT_ERROR_INVALID_PATH) {
538 // The path was already unmounted by something else.
539 error_code = MOUNT_ERROR_NONE;
540 }
541
542 NotifyMountStatusUpdate(
543 UNMOUNTING, error_code,
544 MountPointInfo(mount_points_it->second.source_path,
545 mount_points_it->second.mount_path,
546 mount_points_it->second.mount_type,
547 mount_points_it->second.mount_condition));
548
549 std::string path(mount_points_it->second.source_path);
550
551 if (error_code == MOUNT_ERROR_NONE)
552 mount_points_.erase(mount_points_it);
553
554 DiskMap::iterator disk_iter = disks_.find(path);
555 if (disk_iter != disks_.end()) {
556 DCHECK(disk_iter->second);
557 if (error_code == MOUNT_ERROR_NONE) {
558 disk_iter->second->clear_mount_path();
559 disk_iter->second->set_mounted(false);
560 }
561 }
562
563 if (!callback.is_null())
564 std::move(callback).Run(error_code);
565 }
566
OnUnmountPathForFormat(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,MountError error_code)567 void OnUnmountPathForFormat(const std::string& device_path,
568 FormatFileSystemType filesystem,
569 const std::string& label,
570 MountError error_code) {
571 if (error_code == MOUNT_ERROR_NONE &&
572 disks_.find(device_path) != disks_.end()) {
573 FormatUnmountedDevice(device_path, filesystem, label);
574 } else {
575 OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
576 }
577 }
578
OnUnmountDeviceForSinglePartitionFormat(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,MountError error_code)579 void OnUnmountDeviceForSinglePartitionFormat(const std::string& device_path,
580 FormatFileSystemType filesystem,
581 const std::string& label,
582 MountError error_code) {
583 if (error_code != MOUNT_ERROR_NONE ||
584 disks_.find(device_path) == disks_.end()) {
585 OnPartitionCompleted(device_path, filesystem, label,
586 PARTITION_ERROR_UNKNOWN);
587 return;
588 }
589
590 SinglePartitionFormatUnmountedDevice(device_path, filesystem, label);
591 }
592
593 // Starts device formatting.
FormatUnmountedDevice(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label)594 void FormatUnmountedDevice(const std::string& device_path,
595 FormatFileSystemType filesystem,
596 const std::string& label) {
597 DiskMap::const_iterator disk = disks_.find(device_path);
598 DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
599
600 base::UmaHistogramEnumeration("FileBrowser.FormatFileSystemType",
601 filesystem);
602
603 cros_disks_client_->Format(
604 device_path, FormatFileSystemTypeToString(filesystem), label,
605 base::BindOnce(&DiskMountManagerImpl::OnFormatStarted,
606 weak_ptr_factory_.GetWeakPtr(), device_path, label));
607 }
608
609 // Callback for Format.
OnFormatStarted(const std::string & device_path,const std::string & device_label,bool success)610 void OnFormatStarted(const std::string& device_path,
611 const std::string& device_label,
612 bool success) {
613 if (!success) {
614 OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
615 return;
616 }
617
618 NotifyFormatStatusUpdate(FORMAT_STARTED, FORMAT_ERROR_NONE, device_path,
619 device_label);
620 }
621
622 // CrosDisksClient::Observer override.
OnFormatCompleted(FormatError error_code,const std::string & device_path)623 void OnFormatCompleted(FormatError error_code,
624 const std::string& device_path) override {
625 std::string device_label;
626 auto pending_change = pending_format_changes_.find(device_path);
627 if (pending_change != pending_format_changes_.end()) {
628 device_label = pending_change->second.volume_name;
629 }
630
631 auto iter = disks_.find(device_path);
632
633 // disk might have been removed by now?
634 if (iter != disks_.end()) {
635 Disk* disk = iter->second.get();
636 DCHECK(disk);
637
638 if (pending_change != pending_format_changes_.end() &&
639 error_code == FORMAT_ERROR_NONE) {
640 disk->set_device_label(pending_change->second.volume_name);
641 disk->set_file_system_type(pending_change->second.file_system_type);
642 }
643 }
644
645 pending_format_changes_.erase(device_path);
646
647 EnsureMountInfoRefreshed(base::DoNothing(), true /* force */);
648
649 NotifyFormatStatusUpdate(FORMAT_COMPLETED, error_code, device_path,
650 device_label);
651 }
652
SinglePartitionFormatUnmountedDevice(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label)653 void SinglePartitionFormatUnmountedDevice(const std::string& device_path,
654 FormatFileSystemType filesystem,
655 const std::string& label) {
656 DiskMap::const_iterator disk = disks_.find(device_path);
657 DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
658
659 pending_partitioning_disks_.insert(disk->second->device_path());
660
661 NotifyPartitionStatusUpdate(PARTITION_STARTED, PARTITION_ERROR_NONE,
662 device_path, label);
663
664 cros_disks_client_->SinglePartitionFormat(
665 disk->second->file_path(),
666 base::BindOnce(&DiskMountManagerImpl::OnPartitionCompleted,
667 weak_ptr_factory_.GetWeakPtr(), device_path, filesystem,
668 label));
669 }
670
OnPartitionCompleted(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,PartitionError error_code)671 void OnPartitionCompleted(const std::string& device_path,
672 FormatFileSystemType filesystem,
673 const std::string& label,
674 PartitionError error_code) {
675 auto iter = disks_.find(device_path);
676
677 // disk might have been removed by now?
678 if (iter != disks_.end()) {
679 Disk* disk = iter->second.get();
680 DCHECK(disk);
681
682 if (error_code == PARTITION_ERROR_NONE) {
683 EnsureMountInfoRefreshed(
684 base::BindOnce(&DiskMountManagerImpl::OnRefreshAfterPartition,
685 weak_ptr_factory_.GetWeakPtr(), device_path,
686 filesystem, label),
687 true /* force */);
688 }
689
690 } else {
691 // Remove disk from pending partitioning list if disk removed.
692 pending_partitioning_disks_.erase(iter->second->device_path());
693 }
694
695 NotifyPartitionStatusUpdate(PARTITION_COMPLETED, error_code, device_path,
696 label);
697 }
698
OnRefreshAfterPartition(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,bool success)699 void OnRefreshAfterPartition(const std::string& device_path,
700 FormatFileSystemType filesystem,
701 const std::string& label,
702 bool success) {
703 DiskMap::const_iterator device_disk = disks_.find(device_path);
704 if (device_disk == disks_.end()) {
705 LOG(ERROR) << "Device not found, maybe ejected";
706 pending_partitioning_disks_.erase(device_path);
707 NotifyPartitionStatusUpdate(PARTITION_COMPLETED,
708 PARTITION_ERROR_INVALID_DEVICE_PATH,
709 device_path, label);
710 return;
711 }
712
713 std::string new_partition_device_path;
714 // Find new partition using common storage path with parent device.
715 for (DiskMountManager::DiskMap::const_iterator it = disks_.begin();
716 it != disks_.end(); ++it) {
717 if (it->second->storage_device_path() ==
718 device_disk->second->storage_device_path() &&
719 !it->second->is_parent()) {
720 new_partition_device_path = it->second->device_path();
721 break;
722 }
723 }
724
725 if (new_partition_device_path.empty()) {
726 LOG(ERROR) << "New partition couldn't be found";
727 pending_partitioning_disks_.erase(device_path);
728 NotifyPartitionStatusUpdate(PARTITION_COMPLETED,
729 PARTITION_ERROR_INVALID_DEVICE_PATH,
730 device_path, label);
731 return;
732 }
733
734 const std::string filesystem_str = FormatFileSystemTypeToString(filesystem);
735 pending_format_changes_[new_partition_device_path] = {filesystem_str,
736 label};
737
738 // It's expected the disks (parent device and new partition) are not
739 // mounted, but try unmounting before starting format if it got
740 // mounted through another flow.
741 UnmountDeviceRecursively(
742 device_path,
743 base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
744 weak_ptr_factory_.GetWeakPtr(),
745 new_partition_device_path, filesystem, label));
746
747 // It's ok to remove it from pending partitioning as format flow started.
748 pending_partitioning_disks_.erase(device_path);
749 }
750
OnUnmountPathForRename(const std::string & device_path,const std::string & volume_name,MountError error_code)751 void OnUnmountPathForRename(const std::string& device_path,
752 const std::string& volume_name,
753 MountError error_code) {
754 if (error_code != MOUNT_ERROR_NONE ||
755 disks_.find(device_path) == disks_.end()) {
756 OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
757 return;
758 }
759
760 RenameUnmountedDevice(device_path, volume_name);
761 }
762
763 // Start device renaming
RenameUnmountedDevice(const std::string & device_path,const std::string & volume_name)764 void RenameUnmountedDevice(const std::string& device_path,
765 const std::string& volume_name) {
766 DiskMap::const_iterator disk = disks_.find(device_path);
767 DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
768
769 cros_disks_client_->Rename(
770 device_path, volume_name,
771 base::BindOnce(&DiskMountManagerImpl::OnRenameStarted,
772 weak_ptr_factory_.GetWeakPtr(), device_path,
773 volume_name));
774 }
775
776 // Callback for Rename.
OnRenameStarted(const std::string & device_path,const std::string & volume_name,bool success)777 void OnRenameStarted(const std::string& device_path,
778 const std::string& volume_name,
779 bool success) {
780 if (!success) {
781 OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
782 return;
783 }
784
785 NotifyRenameStatusUpdate(RENAME_STARTED, RENAME_ERROR_NONE, device_path,
786 volume_name);
787 }
788
789 // CrosDisksClient::Observer override.
OnRenameCompleted(RenameError error_code,const std::string & device_path)790 void OnRenameCompleted(RenameError error_code,
791 const std::string& device_path) override {
792 std::string device_label;
793 auto pending_change = pending_rename_changes_.find(device_path);
794 if (pending_change != pending_rename_changes_.end()) {
795 device_label = pending_change->second;
796 }
797
798 auto iter = disks_.find(device_path);
799
800 // disk might have been removed by now?
801 if (iter != disks_.end()) {
802 Disk* disk = iter->second.get();
803 DCHECK(disk);
804
805 if (pending_change != pending_rename_changes_.end() &&
806 error_code == RENAME_ERROR_NONE)
807 disk->set_device_label(pending_change->second);
808 }
809
810 pending_rename_changes_.erase(device_path);
811
812 NotifyRenameStatusUpdate(RENAME_COMPLETED, error_code, device_path,
813 device_label);
814 }
815
816 // Callback for GetDeviceProperties.
OnGetDeviceProperties(const DiskInfo & disk_info)817 void OnGetDeviceProperties(const DiskInfo& disk_info) {
818 if (disk_info.is_virtual())
819 return;
820
821 DVLOG(1) << "Found disk " << disk_info.device_path();
822 // Delete previous disk info for this path:
823 bool is_new = true;
824 bool is_first_mount = false;
825 std::string base_mount_path = std::string();
826 DiskMap::iterator iter = disks_.find(disk_info.device_path());
827 if (iter != disks_.end()) {
828 is_first_mount = iter->second->is_first_mount();
829 base_mount_path = iter->second->base_mount_path();
830 disks_.erase(iter);
831 is_new = false;
832 }
833
834 // If the device was mounted by the instance, apply recorded parameter.
835 // Otherwise, default to false.
836 // Lookup by |device_path| which we pass to cros-disks when mounting a
837 // device in |VolumeManager::OnDiskEvent()|.
838 auto access_mode = access_modes_.find(disk_info.device_path());
839 bool write_disabled_by_policy = access_mode != access_modes_.end()
840 && access_mode->second == chromeos::MOUNT_ACCESS_MODE_READ_ONLY;
841 Disk* disk = new Disk(disk_info, write_disabled_by_policy,
842 base_mount_path);
843 if (!is_new) {
844 disk->set_is_first_mount(is_first_mount);
845 }
846 disks_.insert(
847 std::make_pair(disk_info.device_path(), base::WrapUnique(disk)));
848 NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, *disk);
849 }
850
851 // Part of EnsureMountInfoRefreshed(). Called after the list of devices are
852 // enumerated.
RefreshAfterEnumerateDevices(const std::vector<std::string> & devices)853 void RefreshAfterEnumerateDevices(const std::vector<std::string>& devices) {
854 std::set<std::string> current_device_set(devices.begin(), devices.end());
855 for (DiskMap::iterator iter = disks_.begin(); iter != disks_.end(); ) {
856 if (current_device_set.find(iter->first) == current_device_set.end()) {
857 disks_.erase(iter++);
858 } else {
859 ++iter;
860 }
861 }
862 RefreshDeviceAtIndex(devices, 0);
863 }
864
865 // Part of EnsureMountInfoRefreshed(). Called for each device to refresh info.
RefreshDeviceAtIndex(const std::vector<std::string> & devices,size_t index)866 void RefreshDeviceAtIndex(const std::vector<std::string>& devices,
867 size_t index) {
868 if (index == devices.size()) {
869 // All devices info retrieved. Proceed to enumerate mount point info.
870 cros_disks_client_->EnumerateMountEntries(
871 base::BindOnce(
872 &DiskMountManagerImpl::RefreshAfterEnumerateMountEntries,
873 weak_ptr_factory_.GetWeakPtr()),
874 base::BindOnce(&DiskMountManagerImpl::RefreshCompleted,
875 weak_ptr_factory_.GetWeakPtr(), false));
876 return;
877 }
878
879 cros_disks_client_->GetDeviceProperties(
880 devices[index],
881 base::BindOnce(&DiskMountManagerImpl::RefreshAfterGetDeviceProperties,
882 weak_ptr_factory_.GetWeakPtr(), devices, index + 1),
883 base::BindOnce(&DiskMountManagerImpl::RefreshDeviceAtIndex,
884 weak_ptr_factory_.GetWeakPtr(), devices, index + 1));
885 }
886
887 // Part of EnsureMountInfoRefreshed().
RefreshAfterGetDeviceProperties(const std::vector<std::string> & devices,size_t next_index,const DiskInfo & disk_info)888 void RefreshAfterGetDeviceProperties(const std::vector<std::string>& devices,
889 size_t next_index,
890 const DiskInfo& disk_info) {
891 OnGetDeviceProperties(disk_info);
892 RefreshDeviceAtIndex(devices, next_index);
893 }
894
895 // Part of EnsureMountInfoRefreshed(). Called after mount entries are listed.
RefreshAfterEnumerateMountEntries(const std::vector<MountEntry> & entries)896 void RefreshAfterEnumerateMountEntries(
897 const std::vector<MountEntry>& entries) {
898 for (size_t i = 0; i < entries.size(); ++i)
899 OnMountCompleted(entries[i]);
900 RefreshCompleted(true);
901 }
902
903 // Part of EnsureMountInfoRefreshed(). Called when the refreshing is done.
RefreshCompleted(bool success)904 void RefreshCompleted(bool success) {
905 already_refreshed_ = true;
906 for (size_t i = 0; i < refresh_callbacks_.size(); ++i)
907 std::move(refresh_callbacks_[i]).Run(success);
908 refresh_callbacks_.clear();
909 }
910
911 // CrosDisksClient::Observer override.
OnMountEvent(MountEventType event,const std::string & device_path_arg)912 void OnMountEvent(MountEventType event,
913 const std::string& device_path_arg) override {
914 // Take a copy of the argument so we can modify it below.
915 std::string device_path = device_path_arg;
916 switch (event) {
917 case CROS_DISKS_DISK_ADDED: {
918 cros_disks_client_->GetDeviceProperties(
919 device_path,
920 base::BindOnce(&DiskMountManagerImpl::OnGetDeviceProperties,
921 weak_ptr_factory_.GetWeakPtr()),
922 base::DoNothing());
923 break;
924 }
925 case CROS_DISKS_DISK_REMOVED: {
926 // Search and remove disks that are no longer present.
927 DiskMountManager::DiskMap::iterator iter = disks_.find(device_path);
928 if (iter != disks_.end()) {
929 Disk* disk = iter->second.get();
930 NotifyDiskStatusUpdate(DISK_REMOVED, *disk);
931 disks_.erase(iter);
932 }
933 break;
934 }
935 case CROS_DISKS_DEVICE_ADDED: {
936 NotifyDeviceStatusUpdate(DEVICE_ADDED, device_path);
937 break;
938 }
939 case CROS_DISKS_DEVICE_REMOVED: {
940 NotifyDeviceStatusUpdate(DEVICE_REMOVED, device_path);
941 break;
942 }
943 case CROS_DISKS_DEVICE_SCANNED: {
944 NotifyDeviceStatusUpdate(DEVICE_SCANNED, device_path);
945 break;
946 }
947 default: {
948 LOG(ERROR) << "Unknown event: " << event;
949 }
950 }
951 }
952
953 // Notifies all observers about disk status update.
NotifyDiskStatusUpdate(DiskEvent event,const Disk & disk)954 void NotifyDiskStatusUpdate(DiskEvent event, const Disk& disk) {
955 for (auto& observer : observers_) {
956 // Skip mounting of new partitioned disks while waiting for the format.
957 if (IsPendingPartitioningDisk(disk.device_path())) {
958 continue;
959 }
960 disk.is_auto_mountable() ? observer.OnAutoMountableDiskEvent(event, disk)
961 : observer.OnBootDeviceDiskEvent(event, disk);
962 }
963 }
964
965 // Notifies all observers about device status update.
NotifyDeviceStatusUpdate(DeviceEvent event,const std::string & device_path)966 void NotifyDeviceStatusUpdate(DeviceEvent event,
967 const std::string& device_path) {
968 for (auto& observer : observers_)
969 observer.OnDeviceEvent(event, device_path);
970 }
971
972 // Notifies all observers about mount completion.
NotifyMountStatusUpdate(MountEvent event,MountError error_code,const MountPointInfo & mount_info)973 void NotifyMountStatusUpdate(MountEvent event,
974 MountError error_code,
975 const MountPointInfo& mount_info) {
976 for (auto& observer : observers_)
977 observer.OnMountEvent(event, error_code, mount_info);
978 }
979
NotifyFormatStatusUpdate(FormatEvent event,FormatError error_code,const std::string & device_path,const std::string & device_label)980 void NotifyFormatStatusUpdate(FormatEvent event,
981 FormatError error_code,
982 const std::string& device_path,
983 const std::string& device_label) {
984 for (auto& observer : observers_)
985 observer.OnFormatEvent(event, error_code, device_path, device_label);
986 }
987
NotifyPartitionStatusUpdate(PartitionEvent event,PartitionError error_code,const std::string & device_path,const std::string & device_label)988 void NotifyPartitionStatusUpdate(PartitionEvent event,
989 PartitionError error_code,
990 const std::string& device_path,
991 const std::string& device_label) {
992 for (auto& observer : observers_)
993 observer.OnPartitionEvent(event, error_code, device_path, device_label);
994 }
995
NotifyRenameStatusUpdate(RenameEvent event,RenameError error_code,const std::string & device_path,const std::string & device_label)996 void NotifyRenameStatusUpdate(RenameEvent event,
997 RenameError error_code,
998 const std::string& device_path,
999 const std::string& device_label) {
1000 for (auto& observer : observers_)
1001 observer.OnRenameEvent(event, error_code, device_path, device_label);
1002 }
1003
IsPendingPartitioningDisk(const std::string & device_path)1004 bool IsPendingPartitioningDisk(const std::string& device_path) {
1005 if (pending_partitioning_disks_.find(device_path) !=
1006 pending_partitioning_disks_.end()) {
1007 return true;
1008 }
1009
1010 // If device path doesn't match check whether if it's a child path.
1011 for (auto it = pending_partitioning_disks_.begin();
1012 it != pending_partitioning_disks_.end(); ++it) {
1013 if (base::StartsWith(device_path, *it, base::CompareCase::SENSITIVE)) {
1014 return true;
1015 }
1016 }
1017 return false;
1018 }
1019
1020 // Mount event change observers.
1021 base::ObserverList<DiskMountManager::Observer> observers_;
1022
1023 CrosDisksClient* cros_disks_client_;
1024
1025 // The list of disks found.
1026 DiskMountManager::DiskMap disks_;
1027
1028 DiskMountManager::MountPointMap mount_points_;
1029
1030 bool already_refreshed_;
1031 std::vector<EnsureMountInfoRefreshedCallback> refresh_callbacks_;
1032
1033 std::unique_ptr<SuspendUnmountManager> suspend_unmount_manager_;
1034
1035 // Whether the instance attempted to mount a device in read-only mode for
1036 // each source path.
1037 typedef std::map<std::string, chromeos::MountAccessMode> AccessModeMap;
1038 AccessModeMap access_modes_;
1039
1040 base::WeakPtrFactory<DiskMountManagerImpl> weak_ptr_factory_{this};
1041
1042 DISALLOW_COPY_AND_ASSIGN(DiskMountManagerImpl);
1043 };
1044
1045 } // namespace
1046
~Observer()1047 DiskMountManager::Observer::~Observer() {
1048 DCHECK(!IsInObserverList());
1049 }
1050
AddDiskForTest(std::unique_ptr<Disk> disk)1051 bool DiskMountManager::AddDiskForTest(std::unique_ptr<Disk> disk) {
1052 return false;
1053 }
1054
AddMountPointForTest(const MountPointInfo & mount_point)1055 bool DiskMountManager::AddMountPointForTest(const MountPointInfo& mount_point) {
1056 return false;
1057 }
1058
1059 // static
MountConditionToString(MountCondition condition)1060 std::string DiskMountManager::MountConditionToString(MountCondition condition) {
1061 switch (condition) {
1062 case MOUNT_CONDITION_NONE:
1063 return "";
1064 case MOUNT_CONDITION_UNKNOWN_FILESYSTEM:
1065 return "unknown_filesystem";
1066 case MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM:
1067 return "unsupported_filesystem";
1068 default:
1069 NOTREACHED();
1070 }
1071 return "";
1072 }
1073
1074 // static
DeviceTypeToString(DeviceType type)1075 std::string DiskMountManager::DeviceTypeToString(DeviceType type) {
1076 switch (type) {
1077 case DEVICE_TYPE_USB:
1078 return "usb";
1079 case DEVICE_TYPE_SD:
1080 return "sd";
1081 case DEVICE_TYPE_OPTICAL_DISC:
1082 return "optical";
1083 case DEVICE_TYPE_MOBILE:
1084 return "mobile";
1085 default:
1086 return "unknown";
1087 }
1088 }
1089
1090 // static
Initialize()1091 void DiskMountManager::Initialize() {
1092 if (g_disk_mount_manager) {
1093 LOG(WARNING) << "DiskMountManager was already initialized";
1094 return;
1095 }
1096 g_disk_mount_manager = new DiskMountManagerImpl();
1097 VLOG(1) << "DiskMountManager initialized";
1098 }
1099
1100 // static
InitializeForTesting(DiskMountManager * disk_mount_manager)1101 void DiskMountManager::InitializeForTesting(
1102 DiskMountManager* disk_mount_manager) {
1103 if (g_disk_mount_manager) {
1104 LOG(WARNING) << "DiskMountManager was already initialized";
1105 return;
1106 }
1107 g_disk_mount_manager = disk_mount_manager;
1108 VLOG(1) << "DiskMountManager initialized";
1109 }
1110
1111 // static
Shutdown()1112 void DiskMountManager::Shutdown() {
1113 if (!g_disk_mount_manager) {
1114 LOG(WARNING) << "DiskMountManager::Shutdown() called with NULL manager";
1115 return;
1116 }
1117 delete g_disk_mount_manager;
1118 g_disk_mount_manager = NULL;
1119 VLOG(1) << "DiskMountManager Shutdown completed";
1120 }
1121
1122 // static
GetInstance()1123 DiskMountManager* DiskMountManager::GetInstance() {
1124 return g_disk_mount_manager;
1125 }
1126
1127 } // namespace disks
1128 } // namespace chromeos
1129