#include "chrome/browser/ash/file_manager/path_util.h"
#include <memory>
#include <string_view>
#include <utility>
#include "ash/constants/ash_switches.h"
#include "base/barrier_closure.h"
#include "base/base64.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "base/pickle.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root_map.h"
#include "chrome/browser/ash/arc/fileapi/arc_media_view_util.h"
#include "chrome/browser/ash/arc/fileapi/chrome_content_provider_url_util.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/ash/drive/drive_integration_service_factory.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/volume_manager.h"
#include "chrome/browser/ash/fileapi/external_file_url_util.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/ash/fusebox/fusebox_server.h"
#include "chrome/browser/ash/guest_os/guest_os_session_tracker.h"
#include "chrome/browser/ash/guest_os/guest_os_session_tracker_factory.h"
#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h"
#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
#include "chrome/browser/ash/guest_os/public/guest_os_service_factory.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/smb_client/smb_service.h"
#include "chrome/browser/ash/smb_client/smb_service_factory.h"
#include "chrome/browser/ash/smb_client/smbfs_share.h"
#include "chrome/browser/download/download_dir_util.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "chromeos/ash/components/disks/disk.h"
#include "chromeos/ash/components/disks/disk_mount_manager.h"
#include "chromeos/ash/experiences/arc/arc_features.h"
#include "chromeos/ash/experiences/arc/arc_util.h"
#include "components/drive/file_system_core_util.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/extension.h"
#include "net/base/filename_util.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/common/file_system/file_system_types.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/cros_system_api/dbus/fusebox/dbus-constants.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace file_manager::util {
namespace {
using base::FilePath;
using base::StrCat;
using drive::DriveIntegrationService;
using drive::DriveIntegrationServiceFactory;
using l10n_util::GetStringUTF8;
constexpr char kAndroidFilesMountPointName[] = "android_files";
constexpr char kCrostiniMapGoogleDrive[] = "GoogleDrive";
constexpr char kCrostiniMapLinuxFiles[] = "LinuxFiles";
constexpr char kCrostiniMapMyDrive[] = "MyDrive";
constexpr char kCrostiniMapPlayFiles[] = "PlayFiles";
constexpr char kCrostiniMapSmbFs[] = "SMB";
constexpr char kCrostiniMapFusebox[] = "Fusebox";
constexpr char kCrostiniMapTeamDrives[] = "SharedDrives";
constexpr char kCrostiniMapSharedWithMe[] = "SharedWithMe";
constexpr char kCrostiniMapShortcutsSharedWithMe[] = "ShortcutsSharedWithMe";
constexpr char kFolderNameDownloads[] = "Downloads";
constexpr char kFolderNameMyFiles[] = "MyFiles";
constexpr char kFolderNamePvmDefault[] = "PvmDefault";
constexpr char kFolderNameCamera[] = "Camera";
constexpr char kFolderNameShareCache[] = "ShareCache";
constexpr char kDisplayNameGoogleDrive[] = "Google Drive";
constexpr char kDisplayNameMicrosoftOneDrive[] = "Microsoft OneDrive";
constexpr char kDriveFsDirComputers[] = "Computers";
constexpr char kDriveFsDirSharedWithMe[] = ".files-by-id";
constexpr char kDriveFsDirShortcutsSharedWithMe[] = ".shortcut-targets-by-id";
constexpr char kDriveFsDirRoot[] = "root";
constexpr char kDriveFsDirTeamDrives[] = "team_drives";
constexpr char16_t kFilesAppMimeSources[] = u"fs/sources";
constexpr char16_t kFilesAppSeparator16[] = u"\n";
constexpr FilePath::CharType kArcDownloadRoot[] =
FILE_PATH_LITERAL("/download");
constexpr FilePath::CharType kArcExternalFilesRoot[] =
FILE_PATH_LITERAL("/external_files");
constexpr char kArcStorageContentUrlPrefix[] =
"content://org.chromium.arc.volumeprovider/";
constexpr char kArcRemovableMediaUuidForTesting[] =
"00000000000000000000000000000000DEADBEEF";
constexpr char kArcMyFilesContentUrlPrefix[] =
"content://org.chromium.arc.volumeprovider/"
"0000000000000000000000000000CAFEF00D2019/";
void OnSingleContentUrlResolved(const base::RepeatingClosure& barrier_closure,
std::vector<GURL>* out_urls,
size_t index,
const GURL& url) {
(*out_urls)[index] = url;
barrier_closure.Run();
}
void OnAllContentUrlsResolved(
ConvertToContentUrlsCallback callback,
std::unique_ptr<std::vector<GURL>> urls,
std::unique_ptr<std::vector<FilePath>> paths_to_share) {
std::move(callback).Run(*urls, *paths_to_share);
}
bool ShouldMountPrimaryUserDownloads(Profile* profile) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kUseMyFilesInUserDataDirForTesting)) {
return false;
}
if (!base::SysInfo::IsRunningOnChromeOS() &&
user_manager::UserManager::IsInitialized()) {
const user_manager::User* const user =
ash::ProfileHelper::Get()->GetUserByProfile(
profile->GetOriginalProfile());
const user_manager::User* const primary_user =
user_manager::UserManager::Get()->GetPrimaryUser();
return user == primary_user;
}
return false;
}
FilePath ExtractLegacyDrivePath(const FilePath& path) {
std::vector<FilePath::StringType> components = path.GetComponents();
if (components.size() < 3) {
return FilePath();
}
if (components[0] != FILE_PATH_LITERAL("/")) {
return FilePath();
}
if (components[1] != FILE_PATH_LITERAL("special")) {
return FilePath();
}
static const FilePath::CharType kPrefix[] = FILE_PATH_LITERAL("drive");
if (components[2].compare(0, std::size(kPrefix) - 1, kPrefix) != 0) {
return FilePath();
}
FilePath drive_path = drive::util::GetDriveGrandRootPath();
for (size_t i = 3; i < components.size(); ++i) {
drive_path = drive_path.Append(components[i]);
}
return drive_path;
}
std::string ExtractVolumeNameFromRelativePathForRemovableMedia(
const FilePath& relative_path) {
std::vector<FilePath::StringType> components = relative_path.GetComponents();
if (components.empty()) {
LOG(WARNING) << "Failed to extract volume name from relative path: "
<< relative_path;
return std::string();
}
return components[0];
}
std::string GetSourcePathForRemovableMedia(const std::string& volume_name) {
const std::string mount_path(
base::StringPrintf("%s/%s", kRemovableMediaPath, volume_name.c_str()));
const auto& mount_points =
ash::disks::DiskMountManager::GetInstance()->mount_points();
const auto found = mount_points.find(mount_path);
return found == mount_points.end() ? std::string() : found->source_path;
}
std::string GetFsUuidForRemovableMedia(const std::string& volume_name) {
const std::string source_path = GetSourcePathForRemovableMedia(volume_name);
if (source_path.empty()) {
LOG(WARNING) << "No source path is found for volume name: " << volume_name;
return std::string();
}
const ash::disks::Disk* disk =
ash::disks::DiskMountManager::GetInstance()->FindDiskBySourcePath(
source_path);
std::string fs_uuid = disk == nullptr ? std::string() : disk->fs_uuid();
if (fs_uuid.empty()) {
LOG(WARNING) << "No UUID is found for volume name: " << volume_name;
}
return fs_uuid;
}
bool AppendRelativePath(const FilePath& parent,
const FilePath& child,
FilePath* path) {
return child == parent || parent.AppendRelativePath(child, path);
}
bool SetRelativePath(const FilePath& parent,
const FilePath& child,
FilePath* path) {
path->clear();
return parent.AppendRelativePath(child, path);
}
std::optional<int> DriveFsFolderToMessageId(std::string folder) {
if (folder == kDriveFsDirRoot) {
return IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL;
} else if (folder == kDriveFsDirTeamDrives) {
return IDS_FILE_BROWSER_DRIVE_SHARED_DRIVES_LABEL;
} else if (folder == kDriveFsDirComputers) {
return IDS_FILE_BROWSER_DRIVE_COMPUTERS_LABEL;
} else if (folder == kDriveFsDirSharedWithMe) {
return IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL;
} else if (folder == kDriveFsDirShortcutsSharedWithMe) {
return IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL;
}
return std::nullopt;
}
std::optional<int> MyFilesFolderToMessageId(std::string folder) {
if (folder == kFolderNameDownloads) {
return IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL;
} else if (folder == kFolderNamePvmDefault) {
return IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL;
} else if (folder == kFolderNameCamera) {
return IDS_FILE_BROWSER_CAMERA_DIRECTORY_LABEL;
}
return std::nullopt;
}
std::string GetMountPointNameForProfile(Profile* profile,
const std::string& folder_name) {
const user_manager::User* const user =
user_manager::UserManager::IsInitialized()
? ash::ProfileHelper::Get()->GetUserByProfile(
profile->GetOriginalProfile())
: nullptr;
const std::string id = user ? "-" + user->username_hash() : "";
return base::EscapeQueryParamValue(folder_name + id, false);
}
}
const FilePath::CharType kFuseBoxMediaPath[] =
FILE_PATH_LITERAL("/media/fuse/fusebox");
const FilePath::CharType kFuseBoxMediaSlashPath[] =
FILE_PATH_LITERAL("/media/fuse/fusebox/");
const FilePath::CharType kRemovableMediaPath[] =
FILE_PATH_LITERAL("/media/removable");
const FilePath::CharType kAndroidFilesPath[] =
FILE_PATH_LITERAL("/run/arc/sdcard/write/emulated/0");
const FilePath::CharType kGuestOsAndroidFilesPath[] =
FILE_PATH_LITERAL("/media/fuse/android_files");
const FilePath::CharType kSystemFontsPath[] =
FILE_PATH_LITERAL("/usr/share/fonts");
const FilePath::CharType kArchiveMountPath[] =
FILE_PATH_LITERAL("/media/archive");
const char kFuseBox[] = "fusebox";
const char kFuseBoxMountNamePrefix[] = "fubomona:";
const char kFuseBoxSubdirPrefixADP[] = "adp.";
const char kFuseBoxSubdirPrefixFSP[] = "fsp.";
const char kFuseBoxSubdirPrefixLOC[] = "loc.";
const char kFuseBoxSubdirPrefixMTP[] = "mtp.";
const char kFuseBoxSubdirPrefixTMP[] = "tmp.";
const char kShareCacheMountPointName[] = "ShareCache";
const url::Origin& GetFilesAppOrigin() {
static const base::NoDestructor<url::Origin> origin(
[] { return url::Origin::Create(GetFileManagerURL()); }());
return *origin;
}
FilePath GetDownloadsFolderForProfile(Profile* profile) {
const std::string mount_point_name =
util::GetDownloadsMountPointName(profile);
storage::ExternalMountPoints* const mount_points =
storage::ExternalMountPoints::GetSystemInstance();
FilePath path;
if (mount_points->GetRegisteredPath(mount_point_name, &path)) {
return path.AppendASCII(kFolderNameDownloads);
}
if (ShouldMountPrimaryUserDownloads(profile)) {
return DownloadPrefs::GetDefaultDownloadDirectory();
}
return profile->GetPath()
.AppendASCII(kFolderNameMyFiles)
.AppendASCII(kFolderNameDownloads);
}
FilePath GetMyFilesFolderForProfile(Profile* profile) {
const std::string mount_point_name =
util::GetDownloadsMountPointName(profile);
storage::ExternalMountPoints* const mount_points =
storage::ExternalMountPoints::GetSystemInstance();
FilePath path;
if (mount_points->GetRegisteredPath(mount_point_name, &path)) {
return path;
}
if (ShouldMountPrimaryUserDownloads(profile)) {
return DownloadPrefs::GetDefaultDownloadDirectory();
}
return profile->GetPath().AppendASCII(kFolderNameMyFiles);
}
FilePath GetShareCacheFilePath(Profile* profile) {
return profile->GetPath().AppendASCII(kFolderNameShareCache);
}
FilePath GetAndroidFilesPath() {
const std::string mount_point_name = util::GetAndroidFilesMountPointName();
storage::ExternalMountPoints* const mount_points =
storage::ExternalMountPoints::GetSystemInstance();
FilePath path;
if (mount_points->GetRegisteredPath(mount_point_name, &path)) {
return path;
}
if (arc::IsArcVmEnabled()) {
return FilePath(file_manager::util::kGuestOsAndroidFilesPath);
}
return FilePath(file_manager::util::kAndroidFilesPath);
}
bool MigratePathFromOldFormat(Profile* profile,
const FilePath& old_base,
const FilePath& old_path,
FilePath* new_path) {
if (old_path == old_base && old_path == FilePath("/home/chronos/user")) {
*new_path = GetDownloadsFolderForProfile(profile);
return true;
}
const FilePath new_base = GetMyFilesFolderForProfile(profile);
if (new_base.IsParent(old_path)) {
return false;
}
FilePath relative;
if (old_base.AppendRelativePath(old_path, &relative)) {
*new_path = new_base.Append(relative);
return old_path != *new_path;
}
return false;
}
bool MigrateToDriveFs(Profile* profile,
const FilePath& old_path,
FilePath* new_path) {
const auto* user = ash::ProfileHelper::Get()->GetUserByProfile(profile);
DriveIntegrationService* const service =
DriveIntegrationServiceFactory::FindForProfile(profile);
if (!service || !service->is_enabled() || !user ||
!user->GetAccountId().HasAccountIdKey()) {
return false;
}
*new_path = service->GetMountPointPath();
return drive::util::GetDriveGrandRootPath().AppendRelativePath(
ExtractLegacyDrivePath(old_path), new_path);
}
std::string GetDownloadsMountPointName(Profile* profile) {
return GetMountPointNameForProfile(profile, kFolderNameDownloads);
}
std::string GetShareCacheMountPointName(Profile* profile) {
return GetMountPointNameForProfile(profile, kFolderNameShareCache);
}
std::string GetAndroidFilesMountPointName() {
return kAndroidFilesMountPointName;
}
bool IsBruschettaMountPointName(const std::string& name,
Profile* profile,
guest_os::GuestId* guest_id) {
auto* service = guest_os::GuestOsServiceFactory::GetForProfile(profile);
if (!service) {
return false;
}
auto* registry = service->MountProviderRegistry();
for (const auto id : registry->List()) {
auto* provider = registry->Get(id);
if (provider->vm_type() != vm_tools::apps::VmType::BRUSCHETTA) {
continue;
}
if (name == util::GetGuestOsMountPointName(profile, provider->GuestId())) {
*guest_id = provider->GuestId();
return true;
}
}
return false;
}
std::string GetCrostiniMountPointName(Profile* profile) {
return base::JoinString(
{"crostini", crostini::CryptohomeIdForProfile(profile),
crostini::kCrostiniDefaultVmName,
crostini::kCrostiniDefaultContainerName},
"_");
}
std::string GetGuestOsMountPointName(Profile* profile,
const guest_os::GuestId& id) {
if (id.vm_type == guest_os::VmType::ARCVM) {
return kAndroidFilesMountPointName;
}
if (id == crostini::DefaultContainerId()) {
return GetCrostiniMountPointName(profile);
}
return base::JoinString(
{"guestos", ash::ProfileHelper::GetUserIdHashFromProfile(profile),
base::EscapeAllExceptUnreserved(id.vm_name),
base::EscapeAllExceptUnreserved(id.container_name)},
"+");
}
FilePath GetCrostiniMountDirectory(Profile* profile) {
return FilePath("/media/fuse/" + GetCrostiniMountPointName(profile));
}
FilePath GetGuestOsMountDirectory(std::string mountPointName) {
return FilePath("/media/fuse/" + mountPointName);
}
bool ConvertFileSystemURLToPathInsideVM(
Profile* profile,
const storage::FileSystemURL& file_system_url,
const FilePath& vm_mount,
bool map_crostini_home,
FilePath* inside) {
const std::string& id(file_system_url.mount_filesystem_id());
FilePath path =
FilePath(file_system_url.virtual_path()).StripTrailingSeparators();
std::string mount_point_name_drive;
if (DriveIntegrationService* const service =
DriveIntegrationServiceFactory::FindForProfile(profile)) {
mount_point_name_drive = service->GetMountPointPath().BaseName().value();
}
FilePath base_to_exclude(id);
guest_os::GuestId guest_id("", "");
if (id == GetDownloadsMountPointName(profile)) {
*inside = vm_mount.Append(kFolderNameMyFiles);
} else if (!mount_point_name_drive.empty() && id == mount_point_name_drive) {
std::vector<FilePath::StringType> components = path.GetComponents();
*inside = vm_mount.Append(kCrostiniMapGoogleDrive);
if (components.size() >= 2 && components[1] == kDriveFsDirRoot) {
base_to_exclude = base_to_exclude.Append(kDriveFsDirRoot);
*inside = inside->Append(kCrostiniMapMyDrive);
} else if (components.size() >= 2 &&
components[1] == kDriveFsDirTeamDrives) {
base_to_exclude = base_to_exclude.Append(kDriveFsDirTeamDrives);
*inside = inside->Append(kCrostiniMapTeamDrives);
} else if (components.size() >= 2 &&
components[1] == kDriveFsDirSharedWithMe) {
base_to_exclude = base_to_exclude.Append(kDriveFsDirSharedWithMe);
*inside = inside->Append(kCrostiniMapSharedWithMe);
} else if (components.size() >= 2 &&
components[1] == kDriveFsDirShortcutsSharedWithMe) {
base_to_exclude =
base_to_exclude.Append(kDriveFsDirShortcutsSharedWithMe);
*inside = inside->Append(kCrostiniMapShortcutsSharedWithMe);
}
} else if (id == ash::kSystemMountNameRemovable) {
*inside = vm_mount.Append(ash::kSystemMountNameRemovable);
} else if (id == GetAndroidFilesMountPointName()) {
*inside = vm_mount.Append(kCrostiniMapPlayFiles);
} else if (id == ash::kSystemMountNameArchive) {
*inside = vm_mount.Append(ash::kSystemMountNameArchive);
} else if (id == GetCrostiniMountPointName(profile)) {
if (map_crostini_home) {
auto container_info =
guest_os::GuestOsSessionTrackerFactory::GetForProfile(profile)
->GetInfo(crostini::DefaultContainerId());
if (!container_info) {
return false;
}
*inside = container_info->homedir;
} else {
*inside = vm_mount.Append(kCrostiniMapLinuxFiles);
}
} else if (IsBruschettaMountPointName(id, profile, &guest_id)) {
auto container_info =
guest_os::GuestOsSessionTrackerFactory::GetForProfile(profile)->GetInfo(
guest_id);
if (!container_info) {
return false;
}
*inside = container_info->homedir;
} else if (file_system_url.type() == storage::kFileSystemTypeSmbFs) {
*inside = vm_mount.Append(kCrostiniMapSmbFs);
*inside = inside->Append(id);
} else if (file_system_url.type() == storage::kFileSystemTypeFuseBox) {
*inside = vm_mount.Append(kCrostiniMapFusebox);
return base::FilePath(kFuseBoxMediaPath)
.AppendRelativePath(file_system_url.path(), inside);
} else {
return false;
}
return AppendRelativePath(base_to_exclude, path, inside);
}
bool ConvertFileSystemURLToPathInsideCrostini(
Profile* profile,
const storage::FileSystemURL& file_system_url,
FilePath* inside) {
return ConvertFileSystemURLToPathInsideVM(
profile, file_system_url, crostini::ContainerChromeOSBaseDirectory(),
true, inside);
}
bool ConvertFuseboxMonikerPathToPathInsideVM(const base::FilePath& path,
const base::FilePath& vm_mount,
base::FilePath* inside) {
base::FilePath relative_path;
if (!base::FilePath(fusebox::kMonikerFilenamePrefixWithTrailingSlash)
.AppendRelativePath(path, &relative_path)) {
return false;
}
*inside = vm_mount.Append(kCrostiniMapFusebox)
.Append(fusebox::kMonikerSubdir)
.Append(relative_path);
return true;
}
bool ConvertPathInsideVMToFileSystemURL(
Profile* profile,
const FilePath& inside,
const FilePath& vm_mount,
bool map_crostini_home,
storage::FileSystemURL* file_system_url) {
storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();
std::string mount_point_name_drive;
if (DriveIntegrationService* const service =
DriveIntegrationServiceFactory::FindForProfile(profile)) {
mount_point_name_drive = service->GetMountPointPath().BaseName().value();
}
std::string mount_name;
FilePath path;
FilePath relative_path;
if (map_crostini_home) {
auto container_info =
guest_os::GuestOsSessionTrackerFactory::GetForProfile(profile)->GetInfo(
crostini::DefaultContainerId());
if (container_info &&
AppendRelativePath(container_info->homedir, inside, &relative_path)) {
*file_system_url = mount_points->CreateExternalFileSystemURL(
blink::StorageKey::CreateFirstParty(GetFilesAppOrigin()),
GetCrostiniMountPointName(profile), relative_path);
return file_system_url->is_valid();
}
}
if (!vm_mount.AppendRelativePath(inside, &path)) {
return false;
}
if (AppendRelativePath(FilePath(kFolderNameMyFiles), path, &relative_path)) {
mount_name = GetDownloadsMountPointName(profile);
path = relative_path;
} else if (AppendRelativePath(FilePath(kCrostiniMapLinuxFiles), path,
&relative_path)) {
mount_name = GetCrostiniMountPointName(profile);
path = relative_path;
} else if (FilePath(kCrostiniMapGoogleDrive)
.AppendRelativePath(path, &relative_path)) {
mount_name = mount_point_name_drive;
path = relative_path;
relative_path.clear();
if (AppendRelativePath(FilePath(kCrostiniMapMyDrive), path,
&relative_path)) {
path = FilePath(kDriveFsDirRoot).Append(relative_path);
} else if (AppendRelativePath(FilePath(kCrostiniMapTeamDrives), path,
&relative_path)) {
path = FilePath(kDriveFsDirTeamDrives).Append(relative_path);
} else if (AppendRelativePath(FilePath(kCrostiniMapSharedWithMe), path,
&relative_path)) {
path = FilePath(kDriveFsDirSharedWithMe).Append(relative_path);
} else if (AppendRelativePath(FilePath(kCrostiniMapShortcutsSharedWithMe),
path, &relative_path)) {
path = FilePath(kDriveFsDirShortcutsSharedWithMe).Append(relative_path);
}
} else if (FilePath(ash::kSystemMountNameRemovable)
.AppendRelativePath(path, &relative_path)) {
mount_name = ash::kSystemMountNameRemovable;
path = relative_path;
} else if (AppendRelativePath(FilePath(kCrostiniMapPlayFiles), path,
&relative_path)) {
mount_name = GetAndroidFilesMountPointName();
path = relative_path;
} else if (FilePath(ash::kSystemMountNameArchive)
.AppendRelativePath(path, &relative_path)) {
mount_name = ash::kSystemMountNameArchive;
path = relative_path;
} else if (FilePath(kCrostiniMapSmbFs)
.AppendRelativePath(path, &relative_path)) {
std::vector<FilePath::StringType> components =
relative_path.GetComponents();
if (components.size() < 1) {
return false;
}
mount_name = components[0];
path.clear();
FilePath(mount_name).AppendRelativePath(relative_path, &path);
} else if (FilePath(kCrostiniMapFusebox)
.AppendRelativePath(path, &relative_path)) {
const FilePath absolute_path =
FilePath(kFuseBoxMediaPath).Append(relative_path);
FilePath virtual_path;
if (!mount_points->GetVirtualPath(absolute_path, &virtual_path)) {
return false;
}
const std::vector<FilePath::StringType> components =
virtual_path.GetComponents();
if (components.size() < 1) {
return false;
}
mount_name = components[0];
SetRelativePath(FilePath(mount_name), virtual_path, &path);
} else {
return false;
}
*file_system_url = mount_points->CreateExternalFileSystemURL(
blink::StorageKey::CreateFirstParty(GetFilesAppOrigin()), mount_name,
path);
return file_system_url->is_valid();
}
bool ConvertPathToArcUrl(const FilePath& path,
GURL* const arc_url_out,
bool* const requires_sharing_out) {
DCHECK(arc_url_out);
DCHECK(requires_sharing_out);
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
*requires_sharing_out = false;
Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
if (!primary_profile) {
return false;
}
FilePath primary_downloads = GetDownloadsFolderForProfile(primary_profile);
FilePath result_path(kArcDownloadRoot);
if (primary_downloads.AppendRelativePath(path, &result_path)) {
*arc_url_out = GURL(kArcStorageContentUrlPrefix)
.Resolve(base::EscapePath(result_path.AsUTF8Unsafe()));
return true;
}
result_path = FilePath(kArcExternalFilesRoot);
if (GetAndroidFilesPath().AppendRelativePath(path, &result_path)) {
*arc_url_out = GURL(kArcStorageContentUrlPrefix)
.Resolve(base::EscapePath(result_path.AsUTF8Unsafe()));
return true;
}
FilePath relative_path;
if (SetRelativePath(FilePath(kRemovableMediaPath), path, &relative_path)) {
const std::string volume_name =
ExtractVolumeNameFromRelativePathForRemovableMedia(relative_path);
if (volume_name.empty()) {
return false;
}
const std::string fs_uuid = GetFsUuidForRemovableMedia(volume_name);
FilePath relative_path_with_uuid =
FilePath(fs_uuid.empty() ? kArcRemovableMediaUuidForTesting : fs_uuid);
if (!FilePath(volume_name)
.AppendRelativePath(relative_path, &relative_path_with_uuid)) {
LOG(WARNING) << "Failed to replace volume name \"" << volume_name
<< "\" in relative path \"" << relative_path
<< "\" with UUID \"" << fs_uuid << "\"";
return false;
}
*arc_url_out =
GURL(kArcStorageContentUrlPrefix)
.Resolve(base::EscapePath(relative_path_with_uuid.AsUTF8Unsafe()));
return true;
}
if (SetRelativePath(GetMyFilesFolderForProfile(primary_profile), path,
&relative_path)) {
*arc_url_out = GURL(kArcMyFilesContentUrlPrefix)
.Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
return true;
}
bool force_external = false;
const DriveIntegrationService* integration_service =
drive::util::GetIntegrationServiceByProfile(primary_profile);
if (integration_service &&
SetRelativePath(integration_service->GetMountPointPath(), path,
&relative_path)) {
if (arc::IsArcVmEnabled()) {
*arc_url_out =
GURL("content://org.chromium.arc.volumeprovider/MyDrive/")
.Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
*requires_sharing_out = true;
return true;
}
force_external = true;
}
if (SetRelativePath(GetCrostiniMountDirectory(primary_profile), path,
&relative_path)) {
if (arc::IsArcVmEnabled()) {
*arc_url_out =
GURL("content://org.chromium.arc.volumeprovider/crostini/")
.Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
*requires_sharing_out = true;
return true;
}
force_external = true;
}
if (SetRelativePath(FilePath(kArchiveMountPath), path, &relative_path)) {
if (arc::IsArcVmEnabled()) {
*arc_url_out =
GURL("content://org.chromium.arc.volumeprovider/archive/")
.Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
*requires_sharing_out = true;
return true;
}
force_external = true;
}
if (ash::smb_client::SmbService* const service =
ash::smb_client::SmbServiceFactory::Get(primary_profile)) {
if (const ash::smb_client::SmbFsShare* const share =
service->GetSmbFsShareForPath(path)) {
if (SetRelativePath(share->mount_path(), path, &relative_path)) {
if (arc::IsArcVmEnabled()) {
*arc_url_out =
GURL(StrCat({"content://org.chromium.arc.volumeprovider/smb/",
share->mount_id(), "/"}))
.Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
*requires_sharing_out = true;
return true;
}
force_external = true;
}
}
}
if (SetRelativePath(FilePath(kFuseBoxMediaPath), path, &relative_path)) {
if (arc::IsArcVmEnabled()) {
*arc_url_out =
GURL("content://org.chromium.arc.volumeprovider/fusebox/")
.Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
*requires_sharing_out = true;
return true;
}
force_external = true;
}
if (GetShareCacheFilePath(primary_profile).IsParent(path)) {
force_external = true;
}
GURL external_file_url =
ash::CreateExternalFileURLFromPath(primary_profile, path, force_external);
if (!external_file_url.is_empty()) {
*arc_url_out = arc::EncodeToChromeContentProviderUrl(external_file_url);
return true;
}
return false;
}
base::FilePath ConvertFileSystemURLToPathForSharingWithArc(
const storage::FileSystemURL& file_system_url) {
switch (file_system_url.type()) {
case storage::kFileSystemTypeProvided:
case storage::kFileSystemTypeDeviceMediaAsFileStorage:
return fusebox::Server::SubstituteFuseboxFilePath(file_system_url);
default:
return file_system_url.path();
}
}
void ConvertToContentUrls(
Profile* profile,
const std::vector<storage::FileSystemURL>& file_system_urls,
ConvertToContentUrlsCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (file_system_urls.empty()) {
std::move(callback).Run(std::vector<GURL>(), std::vector<FilePath>());
return;
}
auto* documents_provider_root_map =
profile ? arc::ArcDocumentsProviderRootMap::GetForBrowserContext(profile)
: nullptr;
auto out_urls = std::make_unique<std::vector<GURL>>(file_system_urls.size());
auto* out_urls_ptr = out_urls.get();
auto paths_to_share = std::make_unique<std::vector<FilePath>>();
auto* paths_to_share_ptr = paths_to_share.get();
auto barrier = base::BarrierClosure(
file_system_urls.size(),
base::BindOnce(&OnAllContentUrlsResolved, std::move(callback),
std::move(out_urls), std::move(paths_to_share)));
auto single_content_url_callback =
base::BindRepeating(&OnSingleContentUrlResolved, barrier, out_urls_ptr);
for (size_t index = 0; index < file_system_urls.size(); ++index) {
const auto& file_system_url = file_system_urls[index];
if (documents_provider_root_map) {
FilePath file_path;
auto* documents_provider_root =
documents_provider_root_map->ParseAndLookup(file_system_url,
&file_path);
if (documents_provider_root) {
documents_provider_root->ResolveToContentUrl(
file_path, base::BindOnce(single_content_url_callback, index));
continue;
}
}
if (file_system_url.mount_type() == storage::kFileSystemTypeExternal) {
const base::FilePath path =
ConvertFileSystemURLToPathForSharingWithArc(file_system_url);
GURL arc_url;
bool requires_sharing = false;
if (ConvertPathToArcUrl(path, &arc_url, &requires_sharing)) {
if (requires_sharing) {
paths_to_share_ptr->push_back(path);
}
single_content_url_callback.Run(index, arc_url);
continue;
}
}
single_content_url_callback.Run(index, GURL());
}
}
bool ReplacePrefix(std::string* const s,
std::string_view prefix,
std::string_view replacement) {
DCHECK(s);
if (s->starts_with(prefix) &&
(prefix.ends_with('/') || s->size() <= prefix.size() ||
(*s)[prefix.size()] == '/')) {
s->replace(0, prefix.size(), replacement);
return true;
}
return false;
}
std::string GetPathDisplayTextForSettings(Profile* const profile,
std::string_view path) {
std::string result(path);
DriveIntegrationService* service =
DriveIntegrationServiceFactory::FindForProfile(profile);
if (service && !service->is_enabled()) {
service = nullptr;
}
bool is_odfs_mounted = ash::cloud_upload::IsODFSMounted(profile);
const std::string_view sep = " › ";
const std::string downloads_label =
StrCat({GetStringUTF8(IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL), sep,
GetStringUTF8(IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL)});
if (ReplacePrefix(&result, "/home/chronos/user/MyFiles/Downloads",
downloads_label)) {
} else if (ReplacePrefix(
&result,
profile->GetPath().Append("MyFiles/Downloads").value(),
downloads_label)) {
} else if (ReplacePrefix(
&result, "/home/chronos/user/MyFiles",
GetStringUTF8(IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL))) {
} else if (ReplacePrefix(
&result, profile->GetPath().Append(kFolderNameMyFiles).value(),
GetStringUTF8(IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL))) {
} else if (service &&
ReplacePrefix(
&result,
service->GetMountPointPath().Append(kDriveFsDirRoot).value(),
base::FilePath(kDisplayNameGoogleDrive)
.Append(l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL))
.value())) {
} else if (ReplacePrefix(&result,
download_dir_util::kDriveNamePolicyVariableName,
base::FilePath(kDisplayNameGoogleDrive)
.Append(l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL))
.value())) {
} else if (service &&
ReplacePrefix(&result,
service->GetMountPointPath()
.Append(kDriveFsDirTeamDrives)
.value(),
base::FilePath(kDisplayNameGoogleDrive)
.Append(l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_DRIVE_SHARED_DRIVES_LABEL))
.value())) {
} else if (service &&
ReplacePrefix(&result,
service->GetMountPointPath()
.Append(kDriveFsDirComputers)
.value(),
base::FilePath(kDisplayNameGoogleDrive)
.Append(l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_DRIVE_COMPUTERS_LABEL))
.value())) {
} else if (
service &&
ReplacePrefix(
&result,
service->GetMountPointPath().Append(kDriveFsDirSharedWithMe).value(),
base::FilePath(kDisplayNameGoogleDrive)
.Append(l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL))
.value())) {
} else if (
service &&
ReplacePrefix(
&result,
service->GetMountPointPath()
.Append(kDriveFsDirShortcutsSharedWithMe)
.value(),
base::FilePath(kDisplayNameGoogleDrive)
.Append(l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL))
.value())) {
} else if (ReplacePrefix(
&result, download_dir_util::kOneDriveNamePolicyVariableName,
base::FilePath(kDisplayNameMicrosoftOneDrive).value())) {
} else if (is_odfs_mounted &&
ReplacePrefix(
&result,
ash::cloud_upload::GetODFSFuseboxMount(profile).value(),
base::FilePath(kDisplayNameMicrosoftOneDrive).value())) {
} else if (ReplacePrefix(&result, GetAndroidFilesPath().value(),
l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_ANDROID_FILES_ROOT_LABEL))) {
} else if (ReplacePrefix(&result, GetCrostiniMountDirectory(profile).value(),
l10n_util::GetStringUTF8(
IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL))) {
} else if (ReplacePrefix(&result,
base::FilePath(kRemovableMediaPath)
.AsEndingWithSeparator()
.value(),
"")) {
} else if (ReplacePrefix(&result,
base::FilePath(kArchiveMountPath)
.AsEndingWithSeparator()
.value(),
"")) {
}
base::ReplaceChars(result, "/", sep, &result);
return result;
}
bool ExtractMountNameFileSystemNameFullPath(const FilePath& absolute_path,
std::string* mount_name,
std::string* file_system_name,
std::string* full_path) {
DCHECK(absolute_path.IsAbsolute());
DCHECK(mount_name);
DCHECK(full_path);
storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();
FilePath virtual_path;
if (!mount_points->GetVirtualPath(absolute_path, &virtual_path)) {
return false;
}
const std::string& value = virtual_path.value();
size_t fs_start = 0;
size_t slash_pos = value.find(FilePath::kSeparators[0]);
*mount_name = *file_system_name = value.substr(0, slash_pos);
if (*mount_name == ash::kSystemMountNameRemovable ||
*mount_name == ash::kSystemMountNameArchive) {
if (slash_pos == std::string::npos) {
return false;
}
fs_start = slash_pos + 1;
slash_pos = value.find(FilePath::kSeparators[0], fs_start);
*mount_name = value.substr(0, slash_pos);
}
if (slash_pos == std::string::npos) {
*file_system_name = value.substr(fs_start);
*full_path = "/";
} else {
*file_system_name = value.substr(fs_start, slash_pos - fs_start);
*full_path = value.substr(slash_pos);
}
return true;
}
std::string GetDisplayableFileName(GURL file_url) {
std::string file_name;
if (!base::UnescapeBinaryURLComponentSafe(file_url.ExtractFileName(),
true,
&file_name)) {
file_name = file_url.ExtractFileName();
}
return file_name;
}
std::string GetDisplayableFileName(storage::FileSystemURL file_url) {
return GetDisplayableFileName(file_url.ToGURL());
}
std::u16string GetDisplayableFileName16(GURL file_url) {
return base::UTF8ToUTF16(GetDisplayableFileName(file_url));
}
std::u16string GetDisplayableFileName16(storage::FileSystemURL file_url) {
return base::UTF8ToUTF16(GetDisplayableFileName(file_url.ToGURL()));
}
std::optional<FilePath> GetDisplayablePath(Profile* profile, FilePath path) {
base::WeakPtr<Volume> volume =
file_manager::VolumeManager::Get(profile)->FindVolumeFromPath(path);
if (!volume) {
return std::nullopt;
}
FilePath mount_relative_path;
volume->mount_path().AppendRelativePath(path, &mount_relative_path);
auto path_components = mount_relative_path.GetComponents();
auto cur_component = path_components.begin();
FilePath result;
switch (volume->type()) {
case VOLUME_TYPE_GOOGLE_DRIVE: {
result = FilePath(volume->volume_label());
if (cur_component == path_components.end()) {
return std::nullopt;
}
auto maybe_id = DriveFsFolderToMessageId(*cur_component);
if (!maybe_id.has_value()) {
return std::nullopt;
}
result = result.Append(GetStringUTF8(*maybe_id));
cur_component++;
if (cur_component != path_components.end() &&
(path_components[0] == kDriveFsDirSharedWithMe ||
path_components[0] == kDriveFsDirShortcutsSharedWithMe)) {
++cur_component;
}
break;
}
case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
result = FilePath(volume->volume_label());
if (cur_component != path_components.end()) {
auto maybe_id = MyFilesFolderToMessageId(*cur_component);
if (maybe_id.has_value()) {
result = result.Append(GetStringUTF8(*maybe_id));
++cur_component;
}
}
break;
case VOLUME_TYPE_ANDROID_FILES:
case VOLUME_TYPE_CROSTINI:
case VOLUME_TYPE_GUEST_OS:
result = FilePath(GetStringUTF8(IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL))
.Append(volume->volume_label());
break;
case VOLUME_TYPE_MEDIA_VIEW:
case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
case VOLUME_TYPE_PROVIDED:
case VOLUME_TYPE_DOCUMENTS_PROVIDER:
case VOLUME_TYPE_MTP:
case VOLUME_TYPE_SMB:
result = FilePath(volume->volume_label());
break;
case VOLUME_TYPE_TESTING:
case VOLUME_TYPE_SYSTEM_INTERNAL:
return std::nullopt;
case NUM_VOLUME_TYPE:
NOTREACHED();
}
while (cur_component != path_components.end()) {
result = result.Append(*cur_component);
cur_component++;
}
return result;
}
std::optional<FilePath> GetDisplayablePath(Profile* profile,
storage::FileSystemURL file_url) {
return GetDisplayablePath(profile, file_url.path());
}
std::vector<ui::FileInfo> ParseFileSystemSources(
const ui::DataTransferEndpoint* source,
const base::Pickle& pickle) {
std::vector<ui::FileInfo> file_info;
if (!source || !source->GetURL() || !IsFileManagerURL(*source->GetURL())) {
return file_info;
}
std::optional<std::u16string> maybe_file_system_url_list =
ui::ReadCustomDataForType(pickle, kFilesAppMimeSources);
if (!maybe_file_system_url_list || maybe_file_system_url_list->empty()) {
return file_info;
}
storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();
for (std::u16string_view line : base::SplitStringPiece(
*maybe_file_system_url_list, kFilesAppSeparator16,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
if (line.empty() || line[0] == '#') {
continue;
}
const GURL gurl(line);
storage::FileSystemURL url = mount_points->CrackURL(
gurl, blink::StorageKey::CreateFirstParty(url::Origin::Create(gurl)));
if (!url.is_valid()) {
LOG(WARNING) << "Invalid clipboard FileSystemURL: " << line;
continue;
} else if (url.TypeImpliesPathIsReal()) {
file_info.emplace_back(std::move(url.path()), FilePath());
} else if (FilePath path = fusebox::Server::SubstituteFuseboxFilePath(url);
!path.empty()) {
file_info.emplace_back(std::move(path), FilePath());
}
}
return file_info;
}
}