| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/arc/fileapi/arc_file_system_bridge.h" |
| |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h" |
| #include "ash/components/arc/session/arc_bridge_service.h" |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/strings/escape.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "chrome/browser/ash/arc/fileapi/arc_select_files_handler.h" |
| #include "chrome/browser/ash/arc/fileapi/chrome_content_provider_url_util.h" |
| #include "chrome/browser/ash/arc/fileapi/file_stream_forwarder.h" |
| #include "chrome/browser/ash/drive/drive_integration_service.h" |
| #include "chrome/browser/ash/drive/file_system_util.h" |
| #include "chrome/browser/ash/file_manager/fileapi_util.h" |
| #include "chrome/browser/ash/file_manager/path_util.h" |
| #include "chrome/browser/ash/fileapi/external_file_url_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chromeos/ash/components/dbus/virtual_file_provider/virtual_file_provider_client.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/url_constants.h" |
| #include "extensions/browser/api/file_handlers/mime_util.h" |
| #include "mojo/public/cpp/platform/platform_handle.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "storage/browser/file_system/external_mount_points.h" |
| #include "storage/browser/file_system/file_system_context.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| constexpr char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK"; |
| constexpr char kTestImageRelease[] = "testimage-channel"; |
| } // namespace |
| |
| namespace arc { |
| |
| namespace { |
| |
| // Returns true if it's OK to allow ARC apps to read the given URL. |
| bool IsUrlAllowed(const GURL& url) { |
| // Currently, only externalfile URLs are allowed. |
| return url.SchemeIs(content::kExternalFileScheme); |
| } |
| |
| // Returns true if this is a testimage build. |
| bool IsTestImageBuild() { |
| std::string track; |
| return base::SysInfo::GetLsbReleaseValue(kChromeOSReleaseTrack, &track) && |
| track.find(kTestImageRelease) != std::string::npos; |
| } |
| |
| // Returns true if `download` passes validation. |
| bool IsMediaStoreDownloadMetadataValid( |
| const mojom::MediaStoreDownloadMetadataPtr& download) { |
| // Download should have non-empty display name and owner package name. |
| if (download->display_name.empty() || download->owner_package_name.empty()) |
| return false; |
| |
| // Download should have path relative to "Download/" which is the download |
| // directory for the associated profile. |
| const base::FilePath download_path("Download/"); |
| return download_path == download->relative_path || |
| (!download->relative_path.IsAbsolute() && |
| !download->relative_path.ReferencesParent() && |
| download_path.IsParent(download->relative_path)); |
| } |
| |
| // Returns FileSystemContext. |
| scoped_refptr<storage::FileSystemContext> GetFileSystemContext( |
| content::BrowserContext* context) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| content::StoragePartition* storage = context->GetDefaultStoragePartition(); |
| return storage->GetFileSystemContext(); |
| } |
| |
| // Converts the given URL to a FileSystemURL. |
| file_manager::util::FileSystemURLAndHandle GetFileSystemURL( |
| const storage::FileSystemContext& context, |
| const GURL& url) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return file_manager::util::CreateIsolatedURLFromVirtualPath( |
| context, url::Origin(), ash::ExternalFileURLToVirtualPath(url)); |
| } |
| |
| // Retrieves file's metadata on the IO thread, and runs the callback on the UI |
| // thread. |
| void GetMetadataOnIOThread( |
| scoped_refptr<storage::FileSystemContext> context, |
| const storage::FileSystemURL& url, |
| storage::FileSystemOperation::GetMetadataFieldSet flags, |
| storage::FileSystemOperation::GetMetadataCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| context->operation_runner()->GetMetadata( |
| url, flags, |
| base::BindPostTask(content::GetUIThreadTaskRunner({}), |
| std::move(callback))); |
| } |
| |
| // TODO(risan): Write test. |
| // Open a file from a VFS (vs Chrome-only) filesystem. |
| mojo::ScopedHandle OpenVFSFileToRead(const base::FilePath& fs_path) { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::WILL_BLOCK); |
| // Open the file and wrap the fd to be returned through mojo. |
| base::ScopedFD fd(HANDLE_EINTR( |
| open(fs_path.value().c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW))); |
| if (!fd.is_valid()) { |
| PLOG(WARNING) << "Invalid FD for fs_path: " << fs_path; |
| return mojo::ScopedHandle(); |
| } |
| return mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd))); |
| } |
| |
| // Factory of ArcFileSystemBridge. |
| class ArcFileSystemBridgeFactory |
| : public internal::ArcBrowserContextKeyedServiceFactoryBase< |
| ArcFileSystemBridge, |
| ArcFileSystemBridgeFactory> { |
| public: |
| static constexpr const char* kName = "ArcFileSystemBridgeFactory"; |
| |
| static ArcFileSystemBridgeFactory* GetInstance() { |
| return base::Singleton<ArcFileSystemBridgeFactory>::get(); |
| } |
| |
| private: |
| friend base::DefaultSingletonTraits<ArcFileSystemBridgeFactory>; |
| ArcFileSystemBridgeFactory() = default; |
| ~ArcFileSystemBridgeFactory() override = default; |
| }; |
| |
| } // namespace |
| |
| ArcFileSystemBridge::ArcFileSystemBridge(content::BrowserContext* context, |
| ArcBridgeService* bridge_service) |
| : profile_(Profile::FromBrowserContext(context)), |
| bridge_service_(bridge_service), |
| select_files_handlers_manager_( |
| std::make_unique<ArcSelectFilesHandlersManager>(context)) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| bridge_service_->file_system()->SetHost(this); |
| bridge_service_->file_system()->AddObserver(this); |
| } |
| |
| ArcFileSystemBridge::~ArcFileSystemBridge() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| bridge_service_->file_system()->RemoveObserver(this); |
| bridge_service_->file_system()->SetHost(nullptr); |
| } |
| |
| // static |
| BrowserContextKeyedServiceFactory* ArcFileSystemBridge::GetFactory() { |
| return ArcFileSystemBridgeFactory::GetInstance(); |
| } |
| |
| // static |
| ArcFileSystemBridge* ArcFileSystemBridge::GetForBrowserContext( |
| content::BrowserContext* context) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return ArcFileSystemBridgeFactory::GetForBrowserContext(context); |
| } |
| |
| // static |
| ArcFileSystemBridge* ArcFileSystemBridge::GetForBrowserContextForTesting( |
| content::BrowserContext* context) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return ArcFileSystemBridgeFactory::GetForBrowserContextForTesting(context); |
| } |
| |
| void ArcFileSystemBridge::AddObserver(Observer* observer) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| observer_list_.AddObserver(observer); |
| } |
| |
| void ArcFileSystemBridge::RemoveObserver(Observer* observer) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void ArcFileSystemBridge::GetFileName(const std::string& url, |
| GetFileNameCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| GURL url_decoded = DecodeFromChromeContentProviderUrl(GURL(url)); |
| std::string unescaped_file_name; |
| // It's generally not safe to unescape path separators in strings to be used |
| // in file paths. |
| if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded) || |
| !base::UnescapeBinaryURLComponentSafe(url_decoded.ExtractFileName(), |
| true /* fail_on_path_separators */, |
| &unescaped_file_name)) { |
| LOG(ERROR) << "Invalid URL: " << url << " " << url_decoded; |
| std::move(callback).Run(std::nullopt); |
| return; |
| } |
| std::move(callback).Run(unescaped_file_name); |
| } |
| |
| void ArcFileSystemBridge::GetFileSize(const std::string& url, |
| GetFileSizeCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| GURL url_decoded = DecodeFromChromeContentProviderUrl(GURL(url)); |
| if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded)) { |
| LOG(ERROR) << "Invalid URL: " << url << " " << url_decoded; |
| std::move(callback).Run(-1); |
| return; |
| } |
| |
| GetFileSizeInternal(url_decoded, std::move(callback)); |
| } |
| |
| void ArcFileSystemBridge::GetFileSizeInternal(const GURL& url_decoded, |
| GetFileSizeCallback callback) { |
| GetMetadata(url_decoded, |
| {storage::FileSystemOperation::GetMetadataField::kIsDirectory, |
| storage::FileSystemOperation::GetMetadataField::kSize}, |
| base::BindOnce([](base::File::Error result, |
| const base::File::Info& file_info) -> int64_t { |
| if (result == base::File::FILE_OK && !file_info.is_directory && |
| file_info.size >= 0) { |
| return file_info.size; |
| } |
| return -1; |
| }).Then(std::move(callback))); |
| } |
| |
| void ArcFileSystemBridge::GetLastModified(const GURL& url, |
| GetLastModifiedCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| GURL url_decoded = DecodeFromChromeContentProviderUrl(url); |
| if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded)) { |
| LOG(ERROR) << "Invalid URL: " << url << " " << url_decoded; |
| std::move(callback).Run(std::nullopt); |
| return; |
| } |
| |
| GetMetadata(url_decoded, |
| {storage::FileSystemOperation::GetMetadataField::kLastModified}, |
| base::BindOnce([](base::File::Error result, |
| const base::File::Info& file_info) |
| -> std::optional<base::Time> { |
| if (result != base::File::FILE_OK) { |
| return std::nullopt; |
| } |
| return std::make_optional(file_info.last_modified); |
| }).Then(std::move(callback))); |
| } |
| |
| void ArcFileSystemBridge::GetMetadata( |
| const GURL& url_decoded, |
| storage::FileSystemOperation::GetMetadataFieldSet flags, |
| storage::FileSystemOperation::GetMetadataCallback callback) { |
| scoped_refptr<storage::FileSystemContext> context = |
| GetFileSystemContext(profile_); |
| file_manager::util::FileSystemURLAndHandle file_system_url_and_handle = |
| GetFileSystemURL(*context, url_decoded); |
| content::GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &GetMetadataOnIOThread, std::move(context), |
| file_system_url_and_handle.url, flags, |
| base::BindOnce( |
| [](const std::string& file_system_id, |
| storage::FileSystemOperation::GetMetadataCallback callback, |
| base::File::Error result, const base::File::Info& file_info) { |
| storage::IsolatedContext::GetInstance()->RemoveReference( |
| file_system_id); |
| std::move(callback).Run(result, file_info); |
| }, |
| file_system_url_and_handle.handle.id(), std::move(callback)))); |
| |
| storage::IsolatedContext::GetInstance()->AddReference( |
| file_system_url_and_handle.handle.id()); |
| } |
| |
| void ArcFileSystemBridge::GetFileType(const std::string& url, |
| GetFileTypeCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| GURL url_decoded = DecodeFromChromeContentProviderUrl(GURL(url)); |
| if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded)) { |
| LOG(ERROR) << "Invalid URL: " << url << " " << url_decoded; |
| std::move(callback).Run(std::nullopt); |
| return; |
| } |
| scoped_refptr<storage::FileSystemContext> context = |
| GetFileSystemContext(profile_); |
| file_manager::util::FileSystemURLAndHandle file_system_url_and_handle = |
| GetFileSystemURL(*context, url_decoded); |
| extensions::app_file_handler_util::GetMimeTypeForLocalPath( |
| profile_, file_system_url_and_handle.url.path(), |
| base::BindOnce([](const std::string& mime_type) { |
| return mime_type.empty() ? std::nullopt : std::make_optional(mime_type); |
| }).Then(std::move(callback))); |
| } |
| |
| void ArcFileSystemBridge::OnDocumentChanged( |
| int64_t watcher_id, |
| storage::WatcherManager::ChangeType type) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| for (auto& observer : observer_list_) |
| observer.OnDocumentChanged(watcher_id, type); |
| } |
| |
| void ArcFileSystemBridge::OnRootsChanged() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| for (auto& observer : observer_list_) |
| observer.OnRootsChanged(); |
| } |
| |
| void ArcFileSystemBridge::GetVirtualFileId(const std::string& url, |
| GetVirtualFileIdCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| GURL url_decoded = DecodeFromChromeContentProviderUrl(GURL(url)); |
| if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded)) { |
| LOG(ERROR) << "Invalid URL: " << url << " " << url_decoded; |
| std::move(callback).Run(std::nullopt); |
| return; |
| } |
| |
| GetVirtualFileIdInternal(url_decoded, std::move(callback)); |
| } |
| |
| void ArcFileSystemBridge::HandleIdReleased(const std::string& id, |
| HandleIdReleasedCallback callback) { |
| std::move(callback).Run(HandleIdReleased(id)); |
| } |
| |
| void ArcFileSystemBridge::OpenFileToRead(const std::string& url, |
| OpenFileToReadCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| GURL url_decoded = DecodeFromChromeContentProviderUrl(GURL(url)); |
| if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded)) { |
| LOG(ERROR) << "Invalid URL: " << url << " " << url_decoded; |
| std::move(callback).Run(mojo::ScopedHandle()); |
| return; |
| } |
| |
| base::FilePath fs_path = |
| GetLinuxVFSPathFromExternalFileURL(profile_, url_decoded); |
| // If the URL represents a file on a virtual (Chrome-only, e.g., FSP, |
| // MTP) filesystem, use VirtualFileProvider instead. |
| if (fs_path.empty()) { |
| GetVirtualFileIdInternal( |
| url_decoded, base::BindOnce(&ArcFileSystemBridge::OpenFileById, |
| weak_ptr_factory_.GetWeakPtr(), url_decoded, |
| std::move(callback))); |
| return; |
| } |
| |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock()}, |
| base::BindOnce(&OpenVFSFileToRead, fs_path), std::move(callback)); |
| } |
| |
| void ArcFileSystemBridge::GetVirtualFileIdInternal( |
| const GURL& url_decoded, |
| GetVirtualFileIdCallback callback) { |
| GetFileSizeInternal( |
| url_decoded, base::BindOnce(&ArcFileSystemBridge::GenerateVirtualFileId, |
| weak_ptr_factory_.GetWeakPtr(), url_decoded, |
| std::move(callback))); |
| } |
| |
| void ArcFileSystemBridge::SelectFiles(mojom::SelectFilesRequestPtr request, |
| SelectFilesCallback callback) { |
| select_files_handlers_manager_->SelectFiles(std::move(request), |
| std::move(callback)); |
| } |
| |
| void ArcFileSystemBridge::OnFileSelectorEvent( |
| mojom::FileSelectorEventPtr event, |
| ArcFileSystemBridge::OnFileSelectorEventCallback callback) { |
| std::string track; |
| select_files_handlers_manager_->OnFileSelectorEvent(std::move(event), |
| std::move(callback)); |
| } |
| |
| void ArcFileSystemBridge::GetFileSelectorElements( |
| mojom::GetFileSelectorElementsRequestPtr request, |
| GetFileSelectorElementsCallback callback) { |
| if (!IsTestImageBuild()) { |
| LOG(ERROR) |
| << "GetFileSelectorElements is only allowed under test conditions"; |
| std::move(callback).Run(mojom::FileSelectorElements::New()); |
| return; |
| } |
| select_files_handlers_manager_->GetFileSelectorElements(std::move(request), |
| std::move(callback)); |
| } |
| |
| void ArcFileSystemBridge::OnMediaStoreUriAdded( |
| const GURL& uri, |
| mojom::MediaStoreMetadataPtr metadata) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // Validate `metadata`. |
| bool is_valid = false; |
| if (metadata->is_download()) |
| is_valid = IsMediaStoreDownloadMetadataValid(metadata->get_download()); |
| |
| if (!is_valid) { |
| LOG(ERROR) << "`OnMediaStoreUriAdded()` called with invalid payload."; |
| return; |
| } |
| |
| for (auto& observer : observer_list_) |
| observer.OnMediaStoreUriAdded(uri, *metadata); |
| } |
| |
| void ArcFileSystemBridge::GenerateVirtualFileId( |
| const GURL& url_decoded, |
| GenerateVirtualFileIdCallback callback, |
| int64_t size) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (size < 0) { |
| LOG(ERROR) << "Failed to get file size " << url_decoded; |
| std::move(callback).Run(std::nullopt); |
| return; |
| } |
| ash::VirtualFileProviderClient::Get()->GenerateVirtualFileId( |
| size, base::BindOnce(&ArcFileSystemBridge::OnGenerateVirtualFileId, |
| weak_ptr_factory_.GetWeakPtr(), url_decoded, |
| std::move(callback))); |
| } |
| |
| void ArcFileSystemBridge::OnGenerateVirtualFileId( |
| const GURL& url_decoded, |
| GenerateVirtualFileIdCallback callback, |
| const std::optional<std::string>& id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(id.has_value()); |
| DCHECK_EQ(id_to_url_.count(id.value()), 0u); |
| id_to_url_[id.value()] = url_decoded; |
| |
| std::move(callback).Run(std::move(id)); |
| } |
| |
| void ArcFileSystemBridge::OpenFileById(const GURL& url_decoded, |
| OpenFileToReadCallback callback, |
| const std::optional<std::string>& id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!id.has_value()) { |
| LOG(ERROR) << "Missing ID"; |
| std::move(callback).Run(mojo::ScopedHandle()); |
| return; |
| } |
| |
| ash::VirtualFileProviderClient::Get()->OpenFileById( |
| id.value(), base::BindOnce(&ArcFileSystemBridge::OnOpenFileById, |
| weak_ptr_factory_.GetWeakPtr(), url_decoded, |
| std::move(callback), id.value())); |
| } |
| |
| void ArcFileSystemBridge::OnOpenFileById(const GURL& url_decoded, |
| OpenFileToReadCallback callback, |
| const std::string& id, |
| base::ScopedFD fd) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!fd.is_valid()) { |
| LOG(ERROR) << "Invalid FD"; |
| if (!HandleIdReleased(id)) |
| LOG(ERROR) << "Cannot release ID: " << id; |
| std::move(callback).Run(mojo::ScopedHandle()); |
| return; |
| } |
| |
| mojo::ScopedHandle wrapped_handle = |
| mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd))); |
| if (!wrapped_handle.is_valid()) { |
| LOG(ERROR) << "Failed to wrap handle"; |
| if (!HandleIdReleased(id)) |
| LOG(ERROR) << "Cannot release ID: " << id; |
| std::move(callback).Run(mojo::ScopedHandle()); |
| return; |
| } |
| std::move(callback).Run(std::move(wrapped_handle)); |
| } |
| |
| bool ArcFileSystemBridge::HandleReadRequest(const std::string& id, |
| int64_t offset, |
| int64_t size, |
| base::ScopedFD pipe_write_end) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| auto it_url = id_to_url_.find(id); |
| if (it_url == id_to_url_.end()) { |
| LOG(ERROR) << "Invalid ID: " << id; |
| return false; |
| } |
| |
| // Add a new element to the list, and get an iterator. |
| // NOTE: std::list iterators never get invalidated as long as the pointed |
| // element is alive. |
| file_stream_forwarders_.emplace_front(); |
| auto it_forwarder = file_stream_forwarders_.begin(); |
| |
| const GURL& url = it_url->second; |
| scoped_refptr<storage::FileSystemContext> context = |
| GetFileSystemContext(profile_); |
| file_manager::util::FileSystemURLAndHandle file_system_url_and_handle = |
| GetFileSystemURL(*context, url); |
| *it_forwarder = FileStreamForwarderPtr(new FileStreamForwarder( |
| std::move(context), file_system_url_and_handle.url, offset, size, |
| std::move(pipe_write_end), |
| base::BindOnce(&ArcFileSystemBridge::OnReadRequestCompleted, |
| weak_ptr_factory_.GetWeakPtr(), id, it_forwarder, |
| file_system_url_and_handle.handle.id()))); |
| storage::IsolatedContext::GetInstance()->AddReference( |
| file_system_url_and_handle.handle.id()); |
| return true; |
| } |
| |
| bool ArcFileSystemBridge::HandleIdReleased(const std::string& id) { |
| return id_to_url_.erase(id) != 0; |
| } |
| |
| void ArcFileSystemBridge::OnReadRequestCompleted( |
| const std::string& id, |
| std::list<FileStreamForwarderPtr>::iterator it, |
| const std::string& file_system_id, |
| bool result) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| LOG_IF(ERROR, !result) << "Failed to read " << id; |
| storage::IsolatedContext::GetInstance()->RemoveReference(file_system_id); |
| file_stream_forwarders_.erase(it); |
| } |
| |
| void ArcFileSystemBridge::OnConnectionClosed() { |
| LOG(WARNING) << "FileSystem connection has been closed. " |
| << "Closing SelectFileDialogs owned by ARC apps, if any."; |
| select_files_handlers_manager_->DeleteAllHandlers(); |
| } |
| |
| base::FilePath ArcFileSystemBridge::GetLinuxVFSPathFromExternalFileURL( |
| Profile* const profile, |
| const GURL& url) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| base::FilePath virtual_path = ash::ExternalFileURLToVirtualPath(url); |
| |
| std::string mount_name, cracked_id; |
| storage::FileSystemType file_system_type; |
| base::FilePath absolute_path; |
| storage::FileSystemMountOption mount_option; |
| |
| if (!storage::ExternalMountPoints::GetSystemInstance()->CrackVirtualPath( |
| virtual_path, &mount_name, &file_system_type, &cracked_id, |
| &absolute_path, &mount_option)) { |
| LOG(WARNING) << "Couldn't find mount point for: " << url; |
| return base::FilePath(); |
| } |
| |
| return GetLinuxVFSPathForPathOnFileSystemType(profile, absolute_path, |
| file_system_type); |
| } |
| |
| base::FilePath ArcFileSystemBridge::GetLinuxVFSPathForPathOnFileSystemType( |
| Profile* const profile, |
| const base::FilePath& path, |
| storage::FileSystemType file_system_type) { |
| switch (file_system_type) { |
| case storage::FileSystemType::kFileSystemTypeDriveFs: |
| case storage::FileSystemType::kFileSystemTypeSmbFs: |
| return path; |
| case storage::FileSystemType::kFileSystemTypeLocal: { |
| base::FilePath crostini_mount_path = |
| file_manager::util::GetCrostiniMountDirectory(profile); |
| if (crostini_mount_path == path || crostini_mount_path.IsParent(path)) |
| return path; |
| |
| // fuse-zip, rar2fs. |
| base::FilePath archive_mount_path = |
| base::FilePath(file_manager::util::kArchiveMountPath); |
| if (archive_mount_path.IsParent(path)) |
| return path; |
| |
| break; |
| } |
| default: |
| break; |
| } |
| |
| // The path is not representable on the Linux VFS. |
| return base::FilePath(); |
| } |
| |
| } // namespace arc |