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