1 // Copyright 2013 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/file_manager/volume_manager.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/command_line.h"
15 #include "base/feature_list.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/metrics/histogram_functions.h"
21 #include "base/metrics/histogram_macros.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/system/sys_info.h"
27 #include "base/task/thread_pool.h"
28 #include "chrome/browser/chromeos/arc/arc_util.h"
29 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_map.h"
30 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h"
31 #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h"
32 #include "chrome/browser/chromeos/arc/fileapi/arc_media_view_util.h"
33 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
34 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
35 #include "chrome/browser/chromeos/crostini/crostini_util.h"
36 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
37 #include "chrome/browser/chromeos/drive/file_system_util.h"
38 #include "chrome/browser/chromeos/file_manager/path_util.h"
39 #include "chrome/browser/chromeos/file_manager/snapshot_manager.h"
40 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
41 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
42 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
43 #include "chrome/browser/chromeos/profiles/profile_helper.h"
44 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
45 #include "chrome/browser/profiles/profile.h"
46 #include "chrome/common/pref_names.h"
47 #include "chromeos/constants/chromeos_switches.h"
48 #include "chromeos/disks/disk.h"
49 #include "chromeos/disks/disk_mount_manager.h"
50 #include "components/prefs/pref_service.h"
51 #include "components/storage_monitor/storage_monitor.h"
52 #include "content/public/browser/browser_context.h"
53 #include "content/public/browser/browser_task_traits.h"
54 #include "content/public/browser/browser_thread.h"
55 #include "services/device/public/mojom/mtp_manager.mojom.h"
56 #include "services/device/public/mojom/mtp_storage_info.mojom.h"
57 #include "storage/browser/file_system/external_mount_points.h"
58 #include "ui/base/l10n/l10n_util.h"
59 #include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
60
61 namespace file_manager {
62 namespace {
63
64 const uint32_t kAccessCapabilityReadWrite = 0;
65 const uint32_t kFilesystemTypeGenericHierarchical = 2;
66 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
67 const char kMtpVolumeIdPrefix[] = "mtp:";
68 const char kRootPath[] = "/";
69
70 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
71 // If another folder is already mounted. It revokes and overrides the old one.
RegisterDownloadsMountPoint(Profile * profile,const base::FilePath & path)72 bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) {
73 // Although we show only profile's own "Downloads" folder in the Files app,
74 // in the backend we need to mount all profile's download directory globally.
75 // Otherwise, the Files app cannot support cross-profile file copies, etc.
76 // For this reason, we need to register to the global GetSystemInstance().
77 const std::string mount_point_name =
78 file_manager::util::GetDownloadsMountPointName(profile);
79 storage::ExternalMountPoints* const mount_points =
80 storage::ExternalMountPoints::GetSystemInstance();
81
82 // In some tests we want to override existing Downloads mount point, so we
83 // first revoke the existing mount point (if any).
84 mount_points->RevokeFileSystem(mount_point_name);
85 return mount_points->RegisterFileSystem(mount_point_name,
86 storage::kFileSystemTypeNativeLocal,
87 storage::FileSystemMountOption(),
88 path);
89 }
90
91 // Registers a mount point for Android files to ExternalMountPoints.
RegisterAndroidFilesMountPoint()92 bool RegisterAndroidFilesMountPoint() {
93 storage::ExternalMountPoints* const mount_points =
94 storage::ExternalMountPoints::GetSystemInstance();
95 return mount_points->RegisterFileSystem(
96 file_manager::util::GetAndroidFilesMountPointName(),
97 storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
98 base::FilePath(util::kAndroidFilesPath));
99 }
100
101 // Finds the path register as the "Downloads" folder to FileSystem API backend.
102 // Returns false if it is not registered.
FindDownloadsMountPointPath(Profile * profile,base::FilePath * path)103 bool FindDownloadsMountPointPath(Profile* profile, base::FilePath* path) {
104 const std::string mount_point_name =
105 util::GetDownloadsMountPointName(profile);
106 storage::ExternalMountPoints* const mount_points =
107 storage::ExternalMountPoints::GetSystemInstance();
108
109 return mount_points->GetRegisteredPath(mount_point_name, path);
110 }
111
MountTypeToVolumeType(chromeos::MountType type)112 VolumeType MountTypeToVolumeType(chromeos::MountType type) {
113 switch (type) {
114 case chromeos::MOUNT_TYPE_INVALID:
115 // We don't expect this value, but list here, so that when any value
116 // is added to the enum definition but this is not edited, the compiler
117 // warns it.
118 break;
119 case chromeos::MOUNT_TYPE_DEVICE:
120 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION;
121 case chromeos::MOUNT_TYPE_ARCHIVE:
122 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE;
123 case chromeos::MOUNT_TYPE_NETWORK_STORAGE:
124 // Network storage mounts are handled by their mounters so
125 // MOUNT_TYPE_NETWORK_STORAGE should never need to be handled here.
126 break;
127 }
128
129 NOTREACHED();
130 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
131 }
132
133 // Returns a string representation of the given volume type.
VolumeTypeToString(VolumeType type)134 std::string VolumeTypeToString(VolumeType type) {
135 switch (type) {
136 case VOLUME_TYPE_GOOGLE_DRIVE:
137 return "drive";
138 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
139 return "downloads";
140 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
141 return "removable";
142 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
143 return "archive";
144 case VOLUME_TYPE_PROVIDED:
145 return "provided";
146 case VOLUME_TYPE_MTP:
147 return "mtp";
148 case VOLUME_TYPE_MEDIA_VIEW:
149 return "media_view";
150 case VOLUME_TYPE_ANDROID_FILES:
151 return "android_files";
152 case VOLUME_TYPE_DOCUMENTS_PROVIDER:
153 return "documents_provider";
154 case VOLUME_TYPE_TESTING:
155 return "testing";
156 case VOLUME_TYPE_CROSTINI:
157 return "crostini";
158 case VOLUME_TYPE_SMB:
159 return "smb";
160 case NUM_VOLUME_TYPE:
161 break;
162 }
163 NOTREACHED();
164 return "";
165 }
166
167 // Generates a unique volume ID for the given volume info.
GenerateVolumeId(const Volume & volume)168 std::string GenerateVolumeId(const Volume& volume) {
169 // For the same volume type, base names are unique, as mount points are
170 // flat for the same volume type.
171 return (VolumeTypeToString(volume.type()) + ":" +
172 volume.mount_path().BaseName().AsUTF8Unsafe());
173 }
174
GetMountPointNameForMediaStorage(const storage_monitor::StorageInfo & info)175 std::string GetMountPointNameForMediaStorage(
176 const storage_monitor::StorageInfo& info) {
177 std::string name(kFileManagerMTPMountNamePrefix);
178 name += info.device_id();
179 return name;
180 }
181
GetExternalStorageAccessMode(const Profile * profile)182 chromeos::MountAccessMode GetExternalStorageAccessMode(const Profile* profile) {
183 return profile->GetPrefs()->GetBoolean(prefs::kExternalStorageReadOnly)
184 ? chromeos::MOUNT_ACCESS_MODE_READ_ONLY
185 : chromeos::MOUNT_ACCESS_MODE_READ_WRITE;
186 }
187
RecordDownloadsDiskUsageStats(base::FilePath downloads_path)188 void RecordDownloadsDiskUsageStats(base::FilePath downloads_path) {
189 constexpr int64_t kOneMiB = 1024 * 1024;
190 // For now assume a maximum bucket size of 512GB, which exceeds all current
191 // chromeOS hard disk sizes.
192 constexpr int64_t k512GiBInMiB = 512 * 1024;
193
194 int64_t download_directory_size_in_bytes =
195 base::ComputeDirectorySize(downloads_path);
196
197 base::UmaHistogramCustomCounts(
198 "FileBrowser.Downloads.DirectorySizeMiB",
199 static_cast<int>(download_directory_size_in_bytes / kOneMiB), 1,
200 k512GiBInMiB, 100);
201
202 int64_t total_disk_space_in_bytes =
203 base::SysInfo::AmountOfTotalDiskSpace(downloads_path);
204
205 // total_disk_space_in_bytes can be -1 on error.
206 if (total_disk_space_in_bytes > 0) {
207 int percentage_space_used = std::lround(
208 (download_directory_size_in_bytes * 100.0) / total_disk_space_in_bytes);
209
210 base::UmaHistogramPercentageObsoleteDoNotUse(
211 "FileBrowser.Downloads.DirectoryPercentageOfDiskUsage",
212 percentage_space_used);
213 }
214 }
215
216 } // namespace
217
Volume()218 Volume::Volume()
219 : source_(SOURCE_FILE),
220 type_(VOLUME_TYPE_GOOGLE_DRIVE),
221 device_type_(chromeos::DEVICE_TYPE_UNKNOWN),
222 mount_condition_(chromeos::disks::MOUNT_CONDITION_NONE),
223 mount_context_(MOUNT_CONTEXT_UNKNOWN),
224 is_parent_(false),
225 is_read_only_(false),
226 is_read_only_removable_device_(false),
227 has_media_(false),
228 configurable_(false),
229 watchable_(false) {
230 }
231
232 Volume::~Volume() = default;
233
234 // static
CreateForDrive(const base::FilePath & drive_path)235 std::unique_ptr<Volume> Volume::CreateForDrive(
236 const base::FilePath& drive_path) {
237 std::unique_ptr<Volume> volume(new Volume());
238 volume->type_ = VOLUME_TYPE_GOOGLE_DRIVE;
239 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
240 volume->source_path_ = drive_path;
241 volume->source_ = SOURCE_NETWORK;
242 volume->mount_path_ = drive_path;
243 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
244 volume->volume_id_ = GenerateVolumeId(*volume);
245 volume->watchable_ = true;
246 return volume;
247 }
248
249 // static
CreateForDownloads(const base::FilePath & downloads_path)250 std::unique_ptr<Volume> Volume::CreateForDownloads(
251 const base::FilePath& downloads_path) {
252 std::unique_ptr<Volume> volume(new Volume());
253 volume->type_ = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
254 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
255 // Keep source_path empty.
256 volume->source_ = SOURCE_SYSTEM;
257 volume->mount_path_ = downloads_path;
258 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
259 volume->volume_id_ = GenerateVolumeId(*volume);
260 volume->watchable_ = true;
261 return volume;
262 }
263
264 // static
CreateForRemovable(const chromeos::disks::DiskMountManager::MountPointInfo & mount_point,const chromeos::disks::Disk * disk)265 std::unique_ptr<Volume> Volume::CreateForRemovable(
266 const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
267 const chromeos::disks::Disk* disk) {
268 std::unique_ptr<Volume> volume(new Volume());
269 volume->type_ = MountTypeToVolumeType(mount_point.mount_type);
270 volume->source_path_ = base::FilePath(mount_point.source_path);
271 volume->source_ = mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE
272 ? SOURCE_FILE
273 : SOURCE_DEVICE;
274 volume->mount_path_ = base::FilePath(mount_point.mount_path);
275 volume->mount_condition_ = mount_point.mount_condition;
276
277 if (disk) {
278 volume->file_system_type_ = disk->file_system_type();
279 volume->volume_label_ = disk->device_label();
280 volume->device_type_ = disk->device_type();
281 volume->storage_device_path_ = base::FilePath(disk->storage_device_path());
282 volume->is_parent_ = disk->is_parent();
283 volume->is_read_only_ = disk->is_read_only();
284 volume->is_read_only_removable_device_ = disk->is_read_only_hardware();
285 volume->has_media_ = disk->has_media();
286 volume->drive_label_ = disk->drive_label();
287 } else {
288 volume->volume_label_ = volume->mount_path().BaseName().AsUTF8Unsafe();
289 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
290 volume->is_read_only_ =
291 (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
292 }
293 volume->volume_id_ = GenerateVolumeId(*volume);
294 volume->watchable_ = true;
295 return volume;
296 }
297
298 // static
CreateForProvidedFileSystem(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,MountContext mount_context)299 std::unique_ptr<Volume> Volume::CreateForProvidedFileSystem(
300 const chromeos::file_system_provider::ProvidedFileSystemInfo&
301 file_system_info,
302 MountContext mount_context) {
303 std::unique_ptr<Volume> volume(new Volume());
304 volume->file_system_id_ = file_system_info.file_system_id();
305 volume->provider_id_ = file_system_info.provider_id();
306 switch (file_system_info.source()) {
307 case extensions::SOURCE_FILE:
308 volume->source_ = SOURCE_FILE;
309 break;
310 case extensions::SOURCE_DEVICE:
311 volume->source_ = SOURCE_DEVICE;
312 break;
313 case extensions::SOURCE_NETWORK:
314 volume->source_ = SOURCE_NETWORK;
315 break;
316 }
317 volume->volume_label_ = file_system_info.display_name();
318 volume->type_ = VOLUME_TYPE_PROVIDED;
319 volume->mount_path_ = file_system_info.mount_path();
320 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
321 volume->mount_context_ = mount_context;
322 volume->is_parent_ = true;
323 volume->is_read_only_ = !file_system_info.writable();
324 volume->configurable_ = file_system_info.configurable();
325 volume->watchable_ = file_system_info.watchable();
326 volume->volume_id_ = GenerateVolumeId(*volume);
327 volume->icon_set_ = file_system_info.icon_set();
328 return volume;
329 }
330
331 // static
CreateForMTP(const base::FilePath & mount_path,const std::string & label,bool read_only)332 std::unique_ptr<Volume> Volume::CreateForMTP(const base::FilePath& mount_path,
333 const std::string& label,
334 bool read_only) {
335 std::unique_ptr<Volume> volume(new Volume());
336 volume->type_ = VOLUME_TYPE_MTP;
337 volume->mount_path_ = mount_path;
338 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
339 volume->is_parent_ = true;
340 volume->is_read_only_ = read_only;
341 volume->volume_id_ = kMtpVolumeIdPrefix + label;
342 volume->volume_label_ = label;
343 volume->source_path_ = mount_path;
344 volume->source_ = SOURCE_DEVICE;
345 volume->device_type_ = chromeos::DEVICE_TYPE_MOBILE;
346 return volume;
347 }
348
349 // static
CreateForMediaView(const std::string & root_document_id)350 std::unique_ptr<Volume> Volume::CreateForMediaView(
351 const std::string& root_document_id) {
352 std::unique_ptr<Volume> volume(new Volume());
353 volume->type_ = VOLUME_TYPE_MEDIA_VIEW;
354 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
355 volume->source_ = SOURCE_SYSTEM;
356 volume->mount_path_ = arc::GetDocumentsProviderMountPath(
357 arc::kMediaDocumentsProviderAuthority, root_document_id);
358 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
359 volume->volume_label_ = root_document_id;
360 volume->is_read_only_ = true;
361 volume->watchable_ = false;
362 volume->volume_id_ = arc::GetMediaViewVolumeId(root_document_id);
363 return volume;
364 }
365
366 // static
CreateForSshfsCrostini(const base::FilePath & sshfs_mount_path,const base::FilePath & remote_mount_path)367 std::unique_ptr<Volume> Volume::CreateForSshfsCrostini(
368 const base::FilePath& sshfs_mount_path,
369 const base::FilePath& remote_mount_path) {
370 std::unique_ptr<Volume> volume(new Volume());
371 volume->type_ = VOLUME_TYPE_CROSTINI;
372 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
373 // Keep source_path empty.
374 volume->source_ = SOURCE_SYSTEM;
375 volume->mount_path_ = sshfs_mount_path;
376 volume->remote_mount_path_ = remote_mount_path;
377 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
378 volume->volume_id_ = GenerateVolumeId(*volume);
379 volume->watchable_ = false;
380 return volume;
381 }
382
383 // static
CreateForAndroidFiles(const base::FilePath & mount_path)384 std::unique_ptr<Volume> Volume::CreateForAndroidFiles(
385 const base::FilePath& mount_path) {
386 std::unique_ptr<Volume> volume(new Volume());
387 volume->type_ = VOLUME_TYPE_ANDROID_FILES;
388 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
389 // Keep source_path empty.
390 volume->source_ = SOURCE_SYSTEM;
391 volume->mount_path_ = mount_path;
392 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
393 volume->volume_id_ = GenerateVolumeId(*volume);
394 volume->watchable_ = true;
395 return volume;
396 }
397
398 // static
CreateForDocumentsProvider(const std::string & authority,const std::string & root_id,const std::string & document_id,const std::string & title,const std::string & summary,const GURL & icon_url,bool read_only)399 std::unique_ptr<Volume> Volume::CreateForDocumentsProvider(
400 const std::string& authority,
401 const std::string& root_id,
402 const std::string& document_id,
403 const std::string& title,
404 const std::string& summary,
405 const GURL& icon_url,
406 bool read_only) {
407 std::unique_ptr<Volume> volume(new Volume());
408 volume->type_ = VOLUME_TYPE_DOCUMENTS_PROVIDER;
409 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
410 // Keep source_path empty.
411 volume->source_ = SOURCE_SYSTEM;
412 volume->mount_path_ =
413 arc::GetDocumentsProviderMountPath(authority, document_id);
414 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
415 if (summary.empty()) {
416 volume->volume_label_ = title;
417 } else {
418 volume->volume_label_ = l10n_util::GetStringFUTF8(
419 IDS_FILE_BROWSER_DOCPROVIDER_ROOT_LABEL_WITH_SUMMARY,
420 base::UTF8ToUTF16(title), base::UTF8ToUTF16(summary));
421 }
422 volume->is_read_only_ = read_only;
423 volume->watchable_ = false;
424 volume->volume_id_ = arc::GetDocumentsProviderVolumeId(authority, root_id);
425 if (!icon_url.is_empty()) {
426 chromeos::file_system_provider::IconSet icon_set;
427 icon_set.SetIcon(
428 chromeos::file_system_provider::IconSet::IconSize::SIZE_32x32,
429 icon_url);
430 volume->icon_set_ = icon_set;
431 }
432 return volume;
433 }
434
435 // static
CreateForSmb(const base::FilePath & mount_point,const std::string display_name)436 std::unique_ptr<Volume> Volume::CreateForSmb(const base::FilePath& mount_point,
437 const std::string display_name) {
438 std::unique_ptr<Volume> volume(new Volume());
439 volume->type_ = VOLUME_TYPE_SMB;
440 volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
441 // Keep source_path empty.
442 volume->source_ = SOURCE_NETWORK;
443 volume->mount_path_ = mount_point;
444 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
445 volume->volume_id_ = GenerateVolumeId(*volume);
446 volume->volume_label_ = display_name;
447 volume->watchable_ = false;
448 volume->is_read_only_ = false;
449 return volume;
450 }
451
452 // static
CreateForTesting(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type,bool read_only,const base::FilePath & device_path,const std::string & drive_label,const std::string & file_system_type)453 std::unique_ptr<Volume> Volume::CreateForTesting(
454 const base::FilePath& path,
455 VolumeType volume_type,
456 chromeos::DeviceType device_type,
457 bool read_only,
458 const base::FilePath& device_path,
459 const std::string& drive_label,
460 const std::string& file_system_type) {
461 std::unique_ptr<Volume> volume(new Volume());
462 volume->type_ = volume_type;
463 volume->device_type_ = device_type;
464 // Keep source_path empty.
465 volume->source_ = SOURCE_DEVICE;
466 volume->mount_path_ = path;
467 volume->storage_device_path_ = device_path;
468 volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
469 volume->is_read_only_ = read_only;
470 volume->volume_id_ = GenerateVolumeId(*volume);
471 volume->drive_label_ = drive_label;
472 if (volume_type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
473 volume->file_system_type_ = file_system_type;
474 }
475 return volume;
476 }
477
478 // static
CreateForTesting(const base::FilePath & device_path,const base::FilePath & mount_path)479 std::unique_ptr<Volume> Volume::CreateForTesting(
480 const base::FilePath& device_path,
481 const base::FilePath& mount_path) {
482 std::unique_ptr<Volume> volume(new Volume());
483 volume->storage_device_path_ = device_path;
484 volume->mount_path_ = mount_path;
485 return volume;
486 }
487
VolumeManager(Profile * profile,drive::DriveIntegrationService * drive_integration_service,chromeos::PowerManagerClient * power_manager_client,chromeos::disks::DiskMountManager * disk_mount_manager,chromeos::file_system_provider::Service * file_system_provider_service,GetMtpStorageInfoCallback get_mtp_storage_info_callback)488 VolumeManager::VolumeManager(
489 Profile* profile,
490 drive::DriveIntegrationService* drive_integration_service,
491 chromeos::PowerManagerClient* power_manager_client,
492 chromeos::disks::DiskMountManager* disk_mount_manager,
493 chromeos::file_system_provider::Service* file_system_provider_service,
494 GetMtpStorageInfoCallback get_mtp_storage_info_callback)
495 : profile_(profile),
496 drive_integration_service_(drive_integration_service),
497 disk_mount_manager_(disk_mount_manager),
498 file_system_provider_service_(file_system_provider_service),
499 get_mtp_storage_info_callback_(get_mtp_storage_info_callback),
500 snapshot_manager_(new SnapshotManager(profile_)),
501 documents_provider_root_manager_(
502 std::make_unique<DocumentsProviderRootManager>(
503 profile_,
504 arc::ArcFileSystemOperationRunner::GetForBrowserContext(
505 profile_))) {
506 DCHECK(disk_mount_manager);
507 }
508
509 VolumeManager::~VolumeManager() = default;
510
Get(content::BrowserContext * context)511 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
512 return VolumeManagerFactory::Get(context);
513 }
514
Initialize()515 void VolumeManager::Initialize() {
516 // If in the Sign in profile pr the lock screen app profile, skip mounting
517 // and listening for mount events.
518 if (chromeos::ProfileHelper::IsSigninProfile(profile_) ||
519 chromeos::ProfileHelper::IsLockScreenAppProfile(profile_)) {
520 return;
521 }
522
523 const base::FilePath localVolume =
524 file_manager::util::GetMyFilesFolderForProfile(profile_);
525 const bool success = RegisterDownloadsMountPoint(profile_, localVolume);
526 DCHECK(success);
527
528 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
529 Volume::CreateForDownloads(localVolume));
530
531 // Asyncrhonously record the disk usage for the downloads path
532 base::ThreadPool::PostTask(
533 FROM_HERE,
534 {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
535 base::TaskPriority::BEST_EFFORT},
536 base::BindOnce(&RecordDownloadsDiskUsageStats, std::move(localVolume)));
537
538 // Subscribe to DriveIntegrationService.
539 drive_integration_service_->AddObserver(this);
540 if (drive_integration_service_->IsMounted()) {
541 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
542 Volume::CreateForDrive(GetDriveMountPointPath()));
543 }
544
545 // Subscribe to DiskMountManager.
546 disk_mount_manager_->AddObserver(this);
547 disk_mount_manager_->EnsureMountInfoRefreshed(
548 base::BindOnce(&VolumeManager::OnDiskMountManagerRefreshed,
549 weak_ptr_factory_.GetWeakPtr()),
550 false /* force */);
551
552 // Subscribe to ARC DocumentsProvider events about roots.
553 documents_provider_root_manager_->AddObserver(this);
554
555 // Subscribe to FileSystemProviderService and register currently mounted
556 // volumes for the profile.
557 if (file_system_provider_service_) {
558 using chromeos::file_system_provider::ProvidedFileSystemInfo;
559 file_system_provider_service_->AddObserver(this);
560
561 std::vector<ProvidedFileSystemInfo> file_system_info_list =
562 file_system_provider_service_->GetProvidedFileSystemInfoList();
563 for (size_t i = 0; i < file_system_info_list.size(); ++i) {
564 std::unique_ptr<Volume> volume = Volume::CreateForProvidedFileSystem(
565 file_system_info_list[i], MOUNT_CONTEXT_AUTO);
566 DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume));
567 }
568 }
569
570 // Subscribe to Profile Preference change.
571 pref_change_registrar_.Init(profile_->GetPrefs());
572 pref_change_registrar_.Add(
573 prefs::kExternalStorageDisabled,
574 base::BindRepeating(&VolumeManager::OnExternalStorageDisabledChanged,
575 weak_ptr_factory_.GetWeakPtr()));
576 pref_change_registrar_.Add(
577 prefs::kExternalStorageReadOnly,
578 base::BindRepeating(&VolumeManager::OnExternalStorageReadOnlyChanged,
579 weak_ptr_factory_.GetWeakPtr()));
580
581 // Subscribe to storage monitor for MTP notifications.
582 if (storage_monitor::StorageMonitor::GetInstance()) {
583 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
584 base::BindOnce(&VolumeManager::OnStorageMonitorInitialized,
585 weak_ptr_factory_.GetWeakPtr()));
586 }
587
588 // Subscribe to ARC file system events.
589 if (base::FeatureList::IsEnabled(arc::kMediaViewFeature) &&
590 arc::IsArcAllowedForProfile(profile_)) {
591 // Registers a mount point for Android files only when the flag is enabled.
592 RegisterAndroidFilesMountPoint();
593
594 arc::ArcSessionManager::Get()->AddObserver(this);
595 OnArcPlayStoreEnabledChanged(
596 arc::IsArcPlayStoreEnabledForProfile(profile_));
597 }
598 }
599
Shutdown()600 void VolumeManager::Shutdown() {
601 weak_ptr_factory_.InvalidateWeakPtrs();
602
603 snapshot_manager_.reset();
604 pref_change_registrar_.RemoveAll();
605 disk_mount_manager_->RemoveObserver(this);
606 documents_provider_root_manager_->RemoveObserver(this);
607 documents_provider_root_manager_.reset();
608 if (storage_monitor::StorageMonitor::GetInstance())
609 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
610
611 if (drive_integration_service_)
612 drive_integration_service_->RemoveObserver(this);
613
614 if (file_system_provider_service_)
615 file_system_provider_service_->RemoveObserver(this);
616
617 // Unsubscribe from ARC file system events.
618 if (base::FeatureList::IsEnabled(arc::kMediaViewFeature) &&
619 arc::IsArcAllowedForProfile(profile_)) {
620 auto* session_manager = arc::ArcSessionManager::Get();
621 // TODO(crbug.com/672829): We need nullptr check here because
622 // ArcSessionManager may or may not be alive at this point.
623 if (session_manager)
624 session_manager->RemoveObserver(this);
625 }
626 }
627
AddObserver(VolumeManagerObserver * observer)628 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
629 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
630 DCHECK(observer);
631 observers_.AddObserver(observer);
632 }
633
RemoveObserver(VolumeManagerObserver * observer)634 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
635 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
636 DCHECK(observer);
637 observers_.RemoveObserver(observer);
638 }
639
GetVolumeList()640 std::vector<base::WeakPtr<Volume>> VolumeManager::GetVolumeList() {
641 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
642
643 std::vector<base::WeakPtr<Volume>> result;
644 result.reserve(mounted_volumes_.size());
645 for (const auto& pair : mounted_volumes_) {
646 result.push_back(pair.second->AsWeakPtr());
647 }
648 return result;
649 }
650
FindVolumeById(const std::string & volume_id)651 base::WeakPtr<Volume> VolumeManager::FindVolumeById(
652 const std::string& volume_id) {
653 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
654
655 const auto it = mounted_volumes_.find(volume_id);
656 if (it != mounted_volumes_.end())
657 return it->second->AsWeakPtr();
658 return base::WeakPtr<Volume>();
659 }
660
AddSshfsCrostiniVolume(const base::FilePath & sshfs_mount_path,const base::FilePath & remote_mount_path)661 void VolumeManager::AddSshfsCrostiniVolume(
662 const base::FilePath& sshfs_mount_path,
663 const base::FilePath& remote_mount_path) {
664 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
665 std::unique_ptr<Volume> volume =
666 Volume::CreateForSshfsCrostini(sshfs_mount_path, remote_mount_path);
667 // Ignore if volume already exists.
668 if (mounted_volumes_.find(volume->volume_id()) != mounted_volumes_.end())
669 return;
670 DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume));
671
672 // Listen for crostini container shutdown and remove volume.
673 crostini::CrostiniManager::GetForProfile(profile_)
674 ->AddShutdownContainerCallback(
675 crostini::ContainerId::GetDefault(),
676 base::BindOnce(&VolumeManager::RemoveSshfsCrostiniVolume,
677 weak_ptr_factory_.GetWeakPtr(), sshfs_mount_path,
678 base::BindOnce([](bool result) {
679 if (!result)
680 LOG(ERROR) << "Failed to remove sshfs mount";
681 })));
682 }
683
RemoveSshfsCrostiniVolume(const base::FilePath & sshfs_mount_path,RemoveSshfsCrostiniVolumeCallback callback)684 void VolumeManager::RemoveSshfsCrostiniVolume(
685 const base::FilePath& sshfs_mount_path,
686 RemoveSshfsCrostiniVolumeCallback callback) {
687 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
688 disk_mount_manager_->UnmountPath(
689 sshfs_mount_path.value(),
690 base::BindOnce(&VolumeManager::OnSshfsCrostiniUnmountCallback,
691 base::Unretained(this), sshfs_mount_path,
692 std::move(callback)));
693 }
694
RegisterAndroidFilesDirectoryForTesting(const base::FilePath & path)695 bool VolumeManager::RegisterAndroidFilesDirectoryForTesting(
696 const base::FilePath& path) {
697 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
698 bool result =
699 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
700 file_manager::util::GetAndroidFilesMountPointName(),
701 storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
702 path);
703 DCHECK(result);
704 DoMountEvent(chromeos::MOUNT_ERROR_NONE, Volume::CreateForAndroidFiles(path));
705 return true;
706 }
707
RegisterMediaViewForTesting(const std::string & root_document_id)708 bool VolumeManager::RegisterMediaViewForTesting(
709 const std::string& root_document_id) {
710 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
711 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
712 Volume::CreateForMediaView(root_document_id));
713 return true;
714 }
715
RemoveAndroidFilesDirectoryForTesting(const base::FilePath & path)716 bool VolumeManager::RemoveAndroidFilesDirectoryForTesting(
717 const base::FilePath& path) {
718 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
719 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
720 *Volume::CreateForAndroidFiles(path));
721 return true;
722 }
723
RemoveDownloadsDirectoryForTesting()724 void VolumeManager::RemoveDownloadsDirectoryForTesting() {
725 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
726 base::FilePath path;
727 if (FindDownloadsMountPointPath(profile_, &path)) {
728 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
729 *Volume::CreateForDownloads(path));
730 }
731 }
732
RegisterDownloadsDirectoryForTesting(const base::FilePath & path)733 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
734 const base::FilePath& path) {
735 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
736
737 base::FilePath old_path;
738 if (FindDownloadsMountPointPath(profile_, &old_path)) {
739 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
740 *Volume::CreateForDownloads(old_path));
741 }
742
743 bool success = RegisterDownloadsMountPoint(profile_, path);
744 DoMountEvent(
745 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
746 Volume::CreateForDownloads(path));
747 return success;
748 }
749
RegisterCrostiniDirectoryForTesting(const base::FilePath & path)750 bool VolumeManager::RegisterCrostiniDirectoryForTesting(
751 const base::FilePath& path) {
752 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
753
754 bool success =
755 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
756 file_manager::util::GetCrostiniMountPointName(profile_),
757 storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
758 path);
759 DoMountEvent(
760 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
761 Volume::CreateForSshfsCrostini(path, base::FilePath("/home/testuser")));
762 return true;
763 }
764
AddVolumeForTesting(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type,bool read_only,const base::FilePath & device_path,const std::string & drive_label,const std::string & file_system_type)765 void VolumeManager::AddVolumeForTesting(const base::FilePath& path,
766 VolumeType volume_type,
767 chromeos::DeviceType device_type,
768 bool read_only,
769 const base::FilePath& device_path,
770 const std::string& drive_label,
771 const std::string& file_system_type) {
772 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
773 DoMountEvent(
774 chromeos::MOUNT_ERROR_NONE,
775 Volume::CreateForTesting(path, volume_type, device_type, read_only,
776 device_path, drive_label, file_system_type));
777 }
778
AddVolumeForTesting(std::unique_ptr<Volume> volume)779 void VolumeManager::AddVolumeForTesting(std::unique_ptr<Volume> volume) {
780 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
781 DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume));
782 }
783
RemoveVolumeForTesting(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type,bool read_only,const base::FilePath & device_path,const std::string & drive_label,const std::string & file_system_type)784 void VolumeManager::RemoveVolumeForTesting(
785 const base::FilePath& path,
786 VolumeType volume_type,
787 chromeos::DeviceType device_type,
788 bool read_only,
789 const base::FilePath& device_path,
790 const std::string& drive_label,
791 const std::string& file_system_type) {
792 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
793 DoUnmountEvent(
794 chromeos::MOUNT_ERROR_NONE,
795 *Volume::CreateForTesting(path, volume_type, device_type, read_only,
796 device_path, drive_label, file_system_type));
797 }
798
OnFileSystemMounted()799 void VolumeManager::OnFileSystemMounted() {
800 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
801
802 // Raise mount event.
803 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
804 // or network is unreachable. These two errors will be handled later.
805 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
806 Volume::CreateForDrive(GetDriveMountPointPath()));
807 }
808
OnFileSystemBeingUnmounted()809 void VolumeManager::OnFileSystemBeingUnmounted() {
810 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
811
812 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
813 *Volume::CreateForDrive(GetDriveMountPointPath()));
814 }
815
OnAutoMountableDiskEvent(chromeos::disks::DiskMountManager::DiskEvent event,const chromeos::disks::Disk & disk)816 void VolumeManager::OnAutoMountableDiskEvent(
817 chromeos::disks::DiskMountManager::DiskEvent event,
818 const chromeos::disks::Disk& disk) {
819 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
820
821 // Disregard hidden devices.
822 if (disk.is_hidden())
823 return;
824
825 switch (event) {
826 case chromeos::disks::DiskMountManager::DISK_ADDED:
827 case chromeos::disks::DiskMountManager::DISK_CHANGED: {
828 if (disk.device_path().empty()) {
829 DVLOG(1) << "Empty system path for " << disk.device_path();
830 return;
831 }
832
833 bool mounting = false;
834 if (disk.mount_path().empty() && disk.has_media() &&
835 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
836 // TODO(crbug.com/774890): Remove |mount_label| when the issue gets
837 // resolved. Currently we suggest a mount point name, because in case
838 // when disk's name contains '#', content will not load in Files App.
839 std::string mount_label = disk.device_label();
840 std::replace(mount_label.begin(), mount_label.end(), '#', '_');
841
842 // If disk is not mounted yet and it has media and there is no policy
843 // forbidding external storage, give it a try.
844 // Initiate disk mount operation. MountPath auto-detects the filesystem
845 // format if the second argument is empty. The third argument (mount
846 // label) is not used in a disk mount operation.
847 disk_mount_manager_->MountPath(disk.device_path(), std::string(),
848 mount_label, {},
849 chromeos::MOUNT_TYPE_DEVICE,
850 GetExternalStorageAccessMode(profile_));
851 mounting = true;
852 }
853
854 // Notify to observers.
855 for (auto& observer : observers_)
856 observer.OnDiskAdded(disk, mounting);
857 return;
858 }
859
860 case chromeos::disks::DiskMountManager::DISK_REMOVED:
861 // If the disk is already mounted, unmount it.
862 if (!disk.mount_path().empty()) {
863 disk_mount_manager_->UnmountPath(
864 disk.mount_path(),
865 chromeos::disks::DiskMountManager::UnmountPathCallback());
866 }
867
868 // Notify to observers.
869 for (auto& observer : observers_)
870 observer.OnDiskRemoved(disk);
871 return;
872 }
873 NOTREACHED();
874 }
875
OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,const std::string & device_path)876 void VolumeManager::OnDeviceEvent(
877 chromeos::disks::DiskMountManager::DeviceEvent event,
878 const std::string& device_path) {
879 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
880
881 DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
882 switch (event) {
883 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
884 for (auto& observer : observers_)
885 observer.OnDeviceAdded(device_path);
886 return;
887 case chromeos::disks::DiskMountManager::DEVICE_REMOVED: {
888 for (auto& observer : observers_)
889 observer.OnDeviceRemoved(device_path);
890 return;
891 }
892 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
893 DVLOG(1) << "Ignore SCANNED event: " << device_path;
894 return;
895 }
896 NOTREACHED();
897 }
898
OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,chromeos::MountError error_code,const chromeos::disks::DiskMountManager::MountPointInfo & mount_info)899 void VolumeManager::OnMountEvent(
900 chromeos::disks::DiskMountManager::MountEvent event,
901 chromeos::MountError error_code,
902 const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
903 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
904 switch (mount_info.mount_type) {
905 case chromeos::MOUNT_TYPE_ARCHIVE:
906 case chromeos::MOUNT_TYPE_DEVICE: {
907 // Notify a mounting/unmounting event to observers.
908 const chromeos::disks::Disk* const disk =
909 disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
910 std::unique_ptr<Volume> volume =
911 Volume::CreateForRemovable(mount_info, disk);
912 switch (event) {
913 case chromeos::disks::DiskMountManager::MOUNTING: {
914 DoMountEvent(error_code, std::move(volume));
915 return;
916 }
917 case chromeos::disks::DiskMountManager::UNMOUNTING:
918 DoUnmountEvent(error_code, *volume);
919 return;
920 }
921 NOTREACHED();
922 }
923
924 // Network storage is responsible for doing its own mounting.
925 case chromeos::MOUNT_TYPE_NETWORK_STORAGE: {
926 break;
927 }
928
929 case chromeos::MOUNT_TYPE_INVALID: {
930 NOTREACHED();
931 break;
932 }
933 }
934 }
935
OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,chromeos::FormatError error_code,const std::string & device_path,const std::string & device_label)936 void VolumeManager::OnFormatEvent(
937 chromeos::disks::DiskMountManager::FormatEvent event,
938 chromeos::FormatError error_code,
939 const std::string& device_path,
940 const std::string& device_label) {
941 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
942 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
943 << ", " << device_path;
944
945 switch (event) {
946 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
947 for (auto& observer : observers_) {
948 observer.OnFormatStarted(device_path, device_label,
949 error_code == chromeos::FORMAT_ERROR_NONE);
950 }
951 return;
952 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
953 // Even if format did not complete successfully, try to mount the device
954 // so the user can retry.
955 // MountPath auto-detects filesystem format if second argument is
956 // empty. The third argument (mount label) is not used in a disk mount
957 // operation.
958 disk_mount_manager_->MountPath(device_path, std::string(), std::string(),
959 {}, chromeos::MOUNT_TYPE_DEVICE,
960 GetExternalStorageAccessMode(profile_));
961
962 for (auto& observer : observers_) {
963 observer.OnFormatCompleted(device_path, device_label,
964 error_code == chromeos::FORMAT_ERROR_NONE);
965 }
966
967 return;
968 }
969 NOTREACHED();
970 }
971
OnPartitionEvent(chromeos::disks::DiskMountManager::PartitionEvent event,chromeos::PartitionError error_code,const std::string & device_path,const std::string & device_label)972 void VolumeManager::OnPartitionEvent(
973 chromeos::disks::DiskMountManager::PartitionEvent event,
974 chromeos::PartitionError error_code,
975 const std::string& device_path,
976 const std::string& device_label) {
977 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
978 DVLOG(1) << "OnPartitionEvent: " << event << ", " << error_code << ", "
979 << device_path;
980
981 switch (event) {
982 case chromeos::disks::DiskMountManager::PARTITION_STARTED:
983 for (auto& observer : observers_) {
984 observer.OnPartitionStarted(
985 device_path, device_label,
986 error_code == chromeos::PARTITION_ERROR_NONE);
987 }
988 return;
989 case chromeos::disks::DiskMountManager::PARTITION_COMPLETED:
990 // If partitioning failed, try to mount the device so the user can retry.
991 // MountPath auto-detects filesystem format if second argument is
992 // empty. The third argument (mount label) is not used in a disk mount
993 // operation.
994 if (error_code != chromeos::PARTITION_ERROR_NONE) {
995 disk_mount_manager_->MountPath(device_path, std::string(),
996 std::string(), {},
997 chromeos::MOUNT_TYPE_DEVICE,
998 GetExternalStorageAccessMode(profile_));
999 }
1000
1001 for (auto& observer : observers_) {
1002 observer.OnPartitionCompleted(
1003 device_path, device_label,
1004 error_code == chromeos::PARTITION_ERROR_NONE);
1005 }
1006 return;
1007 }
1008 NOTREACHED();
1009 }
1010
OnRenameEvent(chromeos::disks::DiskMountManager::RenameEvent event,chromeos::RenameError error_code,const std::string & device_path,const std::string & device_label)1011 void VolumeManager::OnRenameEvent(
1012 chromeos::disks::DiskMountManager::RenameEvent event,
1013 chromeos::RenameError error_code,
1014 const std::string& device_path,
1015 const std::string& device_label) {
1016 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1017 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code << ", "
1018 << device_path;
1019
1020 switch (event) {
1021 case chromeos::disks::DiskMountManager::RENAME_STARTED:
1022 for (auto& observer : observers_) {
1023 observer.OnRenameStarted(device_path, device_label,
1024 error_code == chromeos::RENAME_ERROR_NONE);
1025 }
1026 return;
1027 case chromeos::disks::DiskMountManager::RENAME_COMPLETED:
1028 // Find previous mount point label if it exists
1029 std::string mount_label;
1030 auto disk_map_iter = disk_mount_manager_->disks().find(device_path);
1031 if (disk_map_iter != disk_mount_manager_->disks().end() &&
1032 !disk_map_iter->second->base_mount_path().empty()) {
1033 mount_label = base::FilePath(disk_map_iter->second->base_mount_path())
1034 .BaseName()
1035 .AsUTF8Unsafe();
1036 }
1037
1038 // Try to mount the device. MountPath auto-detects filesystem format if
1039 // second argument is empty. Third argument is a mount point name of the
1040 // disk when it was first time mounted (to preserve mount point regardless
1041 // of the volume name).
1042 disk_mount_manager_->MountPath(device_path, std::string(), mount_label,
1043 {}, chromeos::MOUNT_TYPE_DEVICE,
1044 GetExternalStorageAccessMode(profile_));
1045
1046 bool successfully_renamed = error_code == chromeos::RENAME_ERROR_NONE;
1047 for (auto& observer : observers_)
1048 observer.OnRenameCompleted(device_path, device_label,
1049 successfully_renamed);
1050
1051 return;
1052 }
1053 NOTREACHED();
1054 }
1055
OnProvidedFileSystemMount(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,chromeos::file_system_provider::MountContext context,base::File::Error error)1056 void VolumeManager::OnProvidedFileSystemMount(
1057 const chromeos::file_system_provider::ProvidedFileSystemInfo&
1058 file_system_info,
1059 chromeos::file_system_provider::MountContext context,
1060 base::File::Error error) {
1061 MountContext volume_context = MOUNT_CONTEXT_UNKNOWN;
1062 switch (context) {
1063 case chromeos::file_system_provider::MOUNT_CONTEXT_USER:
1064 volume_context = MOUNT_CONTEXT_USER;
1065 break;
1066 case chromeos::file_system_provider::MOUNT_CONTEXT_RESTORE:
1067 volume_context = MOUNT_CONTEXT_AUTO;
1068 break;
1069 }
1070
1071 std::unique_ptr<Volume> volume =
1072 Volume::CreateForProvidedFileSystem(file_system_info, volume_context);
1073
1074 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
1075 // since it is related to cros disks only.
1076 chromeos::MountError mount_error;
1077 switch (error) {
1078 case base::File::FILE_OK:
1079 mount_error = chromeos::MOUNT_ERROR_NONE;
1080 break;
1081 case base::File::FILE_ERROR_EXISTS:
1082 mount_error = chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED;
1083 break;
1084 default:
1085 mount_error = chromeos::MOUNT_ERROR_UNKNOWN;
1086 break;
1087 }
1088
1089 DoMountEvent(mount_error, std::move(volume));
1090 }
1091
OnProvidedFileSystemUnmount(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,base::File::Error error)1092 void VolumeManager::OnProvidedFileSystemUnmount(
1093 const chromeos::file_system_provider::ProvidedFileSystemInfo&
1094 file_system_info,
1095 base::File::Error error) {
1096 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
1097 // since it is related to cros disks only.
1098 const chromeos::MountError mount_error = error == base::File::FILE_OK
1099 ? chromeos::MOUNT_ERROR_NONE
1100 : chromeos::MOUNT_ERROR_UNKNOWN;
1101 std::unique_ptr<Volume> volume = Volume::CreateForProvidedFileSystem(
1102 file_system_info, MOUNT_CONTEXT_UNKNOWN);
1103 DoUnmountEvent(mount_error, *volume);
1104 }
1105
OnExternalStorageDisabledChangedUnmountCallback(std::vector<std::string> remaining_mount_paths,chromeos::MountError error_code)1106 void VolumeManager::OnExternalStorageDisabledChangedUnmountCallback(
1107 std::vector<std::string> remaining_mount_paths,
1108 chromeos::MountError error_code) {
1109 LOG_IF(ERROR, error_code != chromeos::MOUNT_ERROR_NONE)
1110 << "Unmount on ExternalStorageDisabled policy change failed: "
1111 << error_code;
1112
1113 while (!remaining_mount_paths.empty()) {
1114 std::string mount_path = remaining_mount_paths.back();
1115 remaining_mount_paths.pop_back();
1116 if (!base::Contains(disk_mount_manager_->mount_points(), mount_path)) {
1117 // The mount point could have already been removed for another reason
1118 // (i.e. the disk was removed by the user).
1119 continue;
1120 }
1121
1122 disk_mount_manager_->UnmountPath(
1123 mount_path,
1124 base::BindOnce(
1125 &VolumeManager::OnExternalStorageDisabledChangedUnmountCallback,
1126 weak_ptr_factory_.GetWeakPtr(), std::move(remaining_mount_paths)));
1127 return;
1128 }
1129 }
1130
OnArcPlayStoreEnabledChanged(bool enabled)1131 void VolumeManager::OnArcPlayStoreEnabledChanged(bool enabled) {
1132 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1133 DCHECK(base::FeatureList::IsEnabled(arc::kMediaViewFeature));
1134 DCHECK(arc::IsArcAllowedForProfile(profile_));
1135
1136 if (enabled == arc_volumes_mounted_)
1137 return;
1138
1139 if (enabled) {
1140 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
1141 Volume::CreateForMediaView(arc::kImagesRootDocumentId));
1142 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
1143 Volume::CreateForMediaView(arc::kVideosRootDocumentId));
1144 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
1145 Volume::CreateForMediaView(arc::kAudioRootDocumentId));
1146 DoMountEvent(
1147 chromeos::MOUNT_ERROR_NONE,
1148 Volume::CreateForAndroidFiles(base::FilePath(util::kAndroidFilesPath)));
1149 } else {
1150 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
1151 *Volume::CreateForMediaView(arc::kImagesRootDocumentId));
1152 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
1153 *Volume::CreateForMediaView(arc::kVideosRootDocumentId));
1154 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
1155 *Volume::CreateForMediaView(arc::kAudioRootDocumentId));
1156 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
1157 *Volume::CreateForAndroidFiles(
1158 base::FilePath(util::kAndroidFilesPath)));
1159 }
1160
1161 documents_provider_root_manager_->SetEnabled(enabled);
1162 arc_volumes_mounted_ = enabled;
1163 }
1164
OnExternalStorageDisabledChanged()1165 void VolumeManager::OnExternalStorageDisabledChanged() {
1166 // If the policy just got disabled we have to unmount every device currently
1167 // mounted. The opposite is fine - we can let the user re-plug their device to
1168 // make it available.
1169 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
1170 // We do not iterate on mount_points directly, because mount_points can
1171 // be changed by UnmountPath(). Also, a failing unmount shouldn't be retried
1172 // indefinitely. So make a set of all the mount points that should be
1173 // unmounted (all external media mounts), and iterate through them.
1174 std::vector<std::string> remaining_mount_paths;
1175 for (auto& mount_point : disk_mount_manager_->mount_points()) {
1176 if (mount_point.second.mount_type == chromeos::MOUNT_TYPE_DEVICE) {
1177 remaining_mount_paths.push_back(mount_point.first);
1178 }
1179 }
1180 if (remaining_mount_paths.empty()) {
1181 return;
1182 }
1183
1184 std::string mount_path = remaining_mount_paths.back();
1185 remaining_mount_paths.pop_back();
1186 disk_mount_manager_->UnmountPath(
1187 mount_path,
1188 base::BindOnce(
1189 &VolumeManager::OnExternalStorageDisabledChangedUnmountCallback,
1190 weak_ptr_factory_.GetWeakPtr(), std::move(remaining_mount_paths)));
1191 }
1192 }
1193
OnExternalStorageReadOnlyChanged()1194 void VolumeManager::OnExternalStorageReadOnlyChanged() {
1195 disk_mount_manager_->RemountAllRemovableDrives(
1196 GetExternalStorageAccessMode(profile_));
1197 }
1198
OnRemovableStorageAttached(const storage_monitor::StorageInfo & info)1199 void VolumeManager::OnRemovableStorageAttached(
1200 const storage_monitor::StorageInfo& info) {
1201 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
1202 return;
1203 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled))
1204 return;
1205
1206 // Resolve mtp storage name and get MtpStorageInfo.
1207 std::string storage_name;
1208 base::RemoveChars(info.location(), kRootPath, &storage_name);
1209 DCHECK(!storage_name.empty());
1210 if (get_mtp_storage_info_callback_.is_null()) {
1211 storage_monitor::StorageMonitor::GetInstance()
1212 ->media_transfer_protocol_manager()
1213 ->GetStorageInfo(storage_name,
1214 base::BindOnce(&VolumeManager::DoAttachMtpStorage,
1215 weak_ptr_factory_.GetWeakPtr(), info));
1216 } else {
1217 get_mtp_storage_info_callback_.Run(
1218 storage_name, base::BindOnce(&VolumeManager::DoAttachMtpStorage,
1219 weak_ptr_factory_.GetWeakPtr(), info));
1220 }
1221 }
1222
DoAttachMtpStorage(const storage_monitor::StorageInfo & info,device::mojom::MtpStorageInfoPtr mtp_storage_info)1223 void VolumeManager::DoAttachMtpStorage(
1224 const storage_monitor::StorageInfo& info,
1225 device::mojom::MtpStorageInfoPtr mtp_storage_info) {
1226 if (!mtp_storage_info) {
1227 // |mtp_storage_info| can be null. e.g. As OnRemovableStorageAttached and
1228 // DoAttachMtpStorage are called asynchronously, there can be a race
1229 // condition where the storage has been already removed in
1230 // MediaTransferProtocolManager at the time when this method is called.
1231 return;
1232 }
1233
1234 // Mtp write is enabled only when the device is writable, supports generic
1235 // hierarchical file system, and writing to external storage devices is not
1236 // prohibited by the preference.
1237 const bool read_only =
1238 mtp_storage_info->access_capability != kAccessCapabilityReadWrite ||
1239 mtp_storage_info->filesystem_type !=
1240 kFilesystemTypeGenericHierarchical ||
1241 GetExternalStorageAccessMode(profile_) ==
1242 chromeos::MOUNT_ACCESS_MODE_READ_ONLY;
1243
1244 const base::FilePath path = base::FilePath::FromUTF8Unsafe(info.location());
1245 const std::string fsid = GetMountPointNameForMediaStorage(info);
1246 const std::string base_name = base::UTF16ToUTF8(info.model_name());
1247
1248 // Assign a fresh volume ID based on the volume name.
1249 std::string label = base_name;
1250 for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label); ++i)
1251 label = base_name + base::StringPrintf(" (%d)", i);
1252
1253 bool result =
1254 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
1255 fsid, storage::kFileSystemTypeDeviceMediaAsFileStorage,
1256 storage::FileSystemMountOption(), path);
1257 DCHECK(result);
1258
1259 content::GetIOThreadTaskRunner({})->PostTask(
1260 FROM_HERE,
1261 base::BindOnce(&MTPDeviceMapService::RegisterMTPFileSystem,
1262 base::Unretained(MTPDeviceMapService::GetInstance()),
1263 info.location(), fsid, read_only));
1264
1265 std::unique_ptr<Volume> volume = Volume::CreateForMTP(path, label, read_only);
1266 DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume));
1267 }
1268
OnRemovableStorageDetached(const storage_monitor::StorageInfo & info)1269 void VolumeManager::OnRemovableStorageDetached(
1270 const storage_monitor::StorageInfo& info) {
1271 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
1272 return;
1273
1274 for (const auto& mounted_volume : mounted_volumes_) {
1275 if (mounted_volume.second->source_path().value() == info.location()) {
1276 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, *mounted_volume.second.get());
1277
1278 const std::string fsid = GetMountPointNameForMediaStorage(info);
1279 storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fsid);
1280 content::GetIOThreadTaskRunner({})->PostTask(
1281 FROM_HERE,
1282 base::BindOnce(&MTPDeviceMapService::RevokeMTPFileSystem,
1283 base::Unretained(MTPDeviceMapService::GetInstance()),
1284 fsid));
1285 return;
1286 }
1287 }
1288 }
1289
OnDocumentsProviderRootAdded(const std::string & authority,const std::string & root_id,const std::string & document_id,const std::string & title,const std::string & summary,const GURL & icon_url,bool read_only,const std::vector<std::string> & mime_types)1290 void VolumeManager::OnDocumentsProviderRootAdded(
1291 const std::string& authority,
1292 const std::string& root_id,
1293 const std::string& document_id,
1294 const std::string& title,
1295 const std::string& summary,
1296 const GURL& icon_url,
1297 bool read_only,
1298 const std::vector<std::string>& mime_types) {
1299 arc::ArcDocumentsProviderRootMap::GetForArcBrowserContext()->RegisterRoot(
1300 authority, document_id, root_id, read_only, mime_types);
1301 DoMountEvent(
1302 chromeos::MOUNT_ERROR_NONE,
1303 Volume::CreateForDocumentsProvider(authority, root_id, document_id, title,
1304 summary, icon_url, read_only));
1305 }
1306
OnDocumentsProviderRootRemoved(const std::string & authority,const std::string & root_id,const std::string & document_id)1307 void VolumeManager::OnDocumentsProviderRootRemoved(
1308 const std::string& authority,
1309 const std::string& root_id,
1310 const std::string& document_id) {
1311 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
1312 *Volume::CreateForDocumentsProvider(
1313 authority, root_id, std::string(), std::string(),
1314 std::string(), GURL(), false));
1315 arc::ArcDocumentsProviderRootMap::GetForArcBrowserContext()->UnregisterRoot(
1316 authority, document_id);
1317 }
1318
AddSmbFsVolume(const base::FilePath & mount_point,const std::string & display_name)1319 void VolumeManager::AddSmbFsVolume(const base::FilePath& mount_point,
1320 const std::string& display_name) {
1321 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
1322 Volume::CreateForSmb(mount_point, display_name));
1323 }
1324
RemoveSmbFsVolume(const base::FilePath & mount_point)1325 void VolumeManager::RemoveSmbFsVolume(const base::FilePath& mount_point) {
1326 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1327
1328 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
1329 *Volume::CreateForSmb(mount_point, ""));
1330 }
1331
OnDiskMountManagerRefreshed(bool success)1332 void VolumeManager::OnDiskMountManagerRefreshed(bool success) {
1333 if (!success) {
1334 LOG(ERROR) << "Failed to refresh disk mount manager";
1335 return;
1336 }
1337
1338 std::vector<std::unique_ptr<Volume>> archives;
1339
1340 const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
1341 disk_mount_manager_->mount_points();
1342 for (const auto& mount_point : mount_points) {
1343 switch (mount_point.second.mount_type) {
1344 case chromeos::MOUNT_TYPE_ARCHIVE: {
1345 // Archives are mounted after other types of volume. See below.
1346 archives.push_back(
1347 Volume::CreateForRemovable(mount_point.second, nullptr));
1348 break;
1349 }
1350 case chromeos::MOUNT_TYPE_DEVICE: {
1351 DoMountEvent(
1352 chromeos::MOUNT_ERROR_NONE,
1353 Volume::CreateForRemovable(
1354 mount_point.second, disk_mount_manager_->FindDiskBySourcePath(
1355 mount_point.second.source_path)));
1356 break;
1357 }
1358 case chromeos::MOUNT_TYPE_NETWORK_STORAGE: {
1359 break;
1360 }
1361 case chromeos::MOUNT_TYPE_INVALID: {
1362 NOTREACHED();
1363 }
1364 }
1365 }
1366
1367 // We mount archives only if they are opened from currently mounted volumes.
1368 // To check the condition correctly in DoMountEvent, we care about the order.
1369 std::vector<bool> done(archives.size(), false);
1370 for (size_t i = 0; i < archives.size(); ++i) {
1371 if (done[i])
1372 continue;
1373
1374 std::vector<std::unique_ptr<Volume>> chain;
1375 // done[x] = true means archives[x] is null and that volume is in |chain|.
1376 done[i] = true;
1377 chain.push_back(std::move(archives[i]));
1378
1379 // If archives[i]'s source_path is in another archive, mount it first.
1380 for (size_t parent = i + 1; parent < archives.size(); ++parent) {
1381 if (!done[parent] &&
1382 archives[parent]->mount_path().IsParent(
1383 chain.back()->source_path())) {
1384 // done[parent] started false, so archives[parent] is non-null.
1385 done[parent] = true;
1386 chain.push_back(std::move(archives[parent]));
1387 parent = i + 1; // Search archives[parent]'s parent from the beginning.
1388 }
1389 }
1390
1391 // Mount from the tail of chain.
1392 for (size_t i = chain.size(); i > 0; --i) {
1393 DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(chain[i - 1]));
1394 }
1395 }
1396 }
1397
OnStorageMonitorInitialized()1398 void VolumeManager::OnStorageMonitorInitialized() {
1399 std::vector<storage_monitor::StorageInfo> storages =
1400 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
1401 for (size_t i = 0; i < storages.size(); ++i)
1402 OnRemovableStorageAttached(storages[i]);
1403 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
1404 }
1405
DoMountEvent(chromeos::MountError error_code,std::unique_ptr<Volume> volume)1406 void VolumeManager::DoMountEvent(chromeos::MountError error_code,
1407 std::unique_ptr<Volume> volume) {
1408 // Archive files are mounted globally in system. We however don't want to show
1409 // archives from profile-specific folders (Drive/Downloads) of other users in
1410 // multi-profile session. To this end, we filter out archives not on the
1411 // volumes already mounted on this VolumeManager instance.
1412 if (volume->type() == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
1413 // Source may be in Drive cache folder under the current profile directory.
1414 bool from_current_profile =
1415 profile_->GetPath().IsParent(volume->source_path());
1416 for (const auto& mounted_volume : mounted_volumes_) {
1417 if (mounted_volume.second->mount_path().IsParent(volume->source_path())) {
1418 from_current_profile = true;
1419 break;
1420 }
1421 }
1422 if (!from_current_profile)
1423 return;
1424 }
1425
1426 // Filter out removable disks if forbidden by policy for this profile.
1427 if (volume->type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
1428 profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
1429 return;
1430 }
1431
1432 Volume* raw_volume = volume.get();
1433 if (error_code == chromeos::MOUNT_ERROR_NONE || volume->mount_condition()) {
1434 mounted_volumes_[volume->volume_id()] = std::move(volume);
1435 UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", raw_volume->type(),
1436 NUM_VOLUME_TYPE);
1437 }
1438
1439 for (auto& observer : observers_)
1440 observer.OnVolumeMounted(error_code, *raw_volume);
1441 }
1442
DoUnmountEvent(chromeos::MountError error_code,const Volume & volume)1443 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code,
1444 const Volume& volume) {
1445 auto iter = mounted_volumes_.find(volume.volume_id());
1446 if (iter == mounted_volumes_.end())
1447 return;
1448 std::unique_ptr<Volume> volume_ref;
1449 if (error_code == chromeos::MOUNT_ERROR_NONE) {
1450 // It is important to hold a reference to the removed Volume from
1451 // |mounted_volumes_|, because OnVolumeMounted() will access it.
1452 volume_ref = std::move(iter->second);
1453 mounted_volumes_.erase(iter);
1454 }
1455
1456 for (auto& observer : observers_)
1457 observer.OnVolumeUnmounted(error_code, volume);
1458 }
1459
GetDriveMountPointPath() const1460 base::FilePath VolumeManager::GetDriveMountPointPath() const {
1461 return drive_integration_service_->GetMountPointPath();
1462 }
1463
OnSshfsCrostiniUnmountCallback(const base::FilePath & sshfs_mount_path,RemoveSshfsCrostiniVolumeCallback callback,chromeos::MountError error_code)1464 void VolumeManager::OnSshfsCrostiniUnmountCallback(
1465 const base::FilePath& sshfs_mount_path,
1466 RemoveSshfsCrostiniVolumeCallback callback,
1467 chromeos::MountError error_code) {
1468 if ((error_code == chromeos::MOUNT_ERROR_NONE) ||
1469 (error_code == chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED)) {
1470 // Remove metadata associated with the mount. It will be a no-op if it
1471 // wasn't mounted or unmounted out of band.
1472 DoUnmountEvent(
1473 chromeos::MOUNT_ERROR_NONE,
1474 *Volume::CreateForSshfsCrostini(sshfs_mount_path, base::FilePath()));
1475 if (callback)
1476 std::move(callback).Run(true);
1477 return;
1478 }
1479
1480 LOG(ERROR) << "Unmounting " << sshfs_mount_path.value() << " failed";
1481 if (callback)
1482 std::move(callback).Run(false);
1483 }
1484
1485 } // namespace file_manager
1486