| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/storage/arc_disk_quota.h" |
| |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <brillo/cryptohome.h> |
| |
| #include "cryptohome/projectid_config.h" |
| |
| namespace cryptohome { |
| |
| ArcDiskQuota::ArcDiskQuota(HomeDirs* homedirs, |
| Platform* platform, |
| const base::FilePath& home) |
| : homedirs_(homedirs), platform_(platform), home_(home) {} |
| |
| ArcDiskQuota::~ArcDiskQuota() = default; |
| |
| void ArcDiskQuota::Initialize() { |
| device_ = GetDevice(); |
| } |
| |
| bool ArcDiskQuota::IsQuotaSupported() const { |
| if (device_.empty()) { |
| LOG(ERROR) << "No quota mount is found."; |
| return false; |
| } |
| |
| int cnt = homedirs_->GetUnmountedAndroidDataCount(); |
| if (cnt != 0) { |
| // Quota is not supported if there are one or more unmounted Android users. |
| // (b/181159107) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int64_t ArcDiskQuota::GetCurrentSpaceForUid(uid_t android_uid) const { |
| if (android_uid < kAndroidUidStart || android_uid > kAndroidUidEnd) { |
| LOG(ERROR) << "Android uid " << android_uid |
| << " is outside the allowed query range"; |
| return -1; |
| } |
| if (device_.empty()) { |
| LOG(ERROR) << "No quota mount is found"; |
| return -1; |
| } |
| uid_t real_uid = android_uid + kArcContainerShiftUid; |
| int64_t current_space = |
| platform_->GetQuotaCurrentSpaceForUid(device_, real_uid); |
| if (current_space == -1) { |
| PLOG(ERROR) << "Failed to get disk stats for uid: " << real_uid; |
| return -1; |
| } |
| return current_space; |
| } |
| |
| int64_t ArcDiskQuota::GetCurrentSpaceForGid(gid_t android_gid) const { |
| if (android_gid < kAndroidGidStart || android_gid > kAndroidGidEnd) { |
| LOG(ERROR) << "Android gid " << android_gid |
| << " is outside the allowed query range"; |
| return -1; |
| } |
| if (device_.empty()) { |
| LOG(ERROR) << "No quota mount is found"; |
| return -1; |
| } |
| gid_t real_gid = android_gid + kArcContainerShiftGid; |
| int64_t current_space = |
| platform_->GetQuotaCurrentSpaceForGid(device_, real_gid); |
| if (current_space == -1) { |
| PLOG(ERROR) << "Failed to get disk stats for gid: " << real_gid; |
| return -1; |
| } |
| return current_space; |
| } |
| |
| int64_t ArcDiskQuota::GetCurrentSpaceForProjectId(int project_id) const { |
| if ((project_id < kProjectIdForAndroidFilesStart || |
| project_id > kProjectIdForAndroidFilesEnd) && |
| (project_id < kProjectIdForAndroidAppsStart || |
| project_id > kProjectIdForAndroidAppsEnd)) { |
| LOG(ERROR) << "Project id " << project_id |
| << " is outside the allowed query range"; |
| return -1; |
| } |
| if (device_.empty()) { |
| LOG(ERROR) << "No quota mount is found"; |
| return -1; |
| } |
| int64_t current_space = |
| platform_->GetQuotaCurrentSpaceForProjectId(device_, project_id); |
| if (current_space == -1) { |
| PLOG(ERROR) << "Failed to get disk stats for project id: " << project_id; |
| return -1; |
| } |
| return current_space; |
| } |
| |
| bool ArcDiskQuota::SetProjectId(int project_id, |
| SetProjectIdAllowedPathType parent_path, |
| const base::FilePath& child_path, |
| const std::string& obfuscated_username) const { |
| if ((project_id < kProjectIdForAndroidFilesStart || |
| project_id > kProjectIdForAndroidFilesEnd) && |
| (project_id < kProjectIdForAndroidAppsStart || |
| project_id > kProjectIdForAndroidAppsEnd)) { |
| LOG(ERROR) << "Project id " << project_id |
| << " is outside the allowed query range"; |
| return false; |
| } |
| |
| if (child_path.ReferencesParent()) { |
| LOG(ERROR) << "child_path contains \"..\" : " << child_path; |
| return false; |
| } |
| |
| if (child_path.IsAbsolute()) { |
| LOG(ERROR) << "child_path is an absolute path : " << child_path; |
| return false; |
| } |
| |
| MountError mount_error; |
| if (!homedirs_->CryptohomeExists(obfuscated_username, &mount_error)) { |
| if (mount_error != MOUNT_ERROR_NONE) { |
| LOG(ERROR) << "Failed to check cryptohome existence for : " |
| << obfuscated_username << " error = " << mount_error; |
| } else { |
| LOG(ERROR) << "A cryptohome vault doesn't exist for : " |
| << obfuscated_username; |
| } |
| return false; |
| } |
| |
| base::FilePath path; |
| switch (parent_path) { |
| case PATH_DOWNLOADS: |
| // /home/user/<obfuscated_username>/Downloads/<child_path> |
| path = brillo::cryptohome::home::GetUserPathPrefix() |
| .Append(obfuscated_username) |
| .Append(kUserDownloadsDir) |
| .Append(child_path); |
| break; |
| case PATH_ANDROID_DATA: |
| // /home/root/<obfuscated_username>/android-data/<child_path> |
| path = brillo::cryptohome::home::GetRootPathPrefix() |
| .Append(obfuscated_username) |
| .Append(kAndroidDataDir) |
| .Append(child_path); |
| break; |
| } |
| |
| if (path.empty()) { |
| LOG(ERROR) << "Invalid parent path type : " << parent_path; |
| return false; |
| } |
| |
| return platform_->SetQuotaProjectId(project_id, path); |
| } |
| |
| base::FilePath ArcDiskQuota::GetDevice() { |
| std::string device; |
| if (!platform_->FindFilesystemDevice(home_, &device)) { |
| LOG(ERROR) << "Home device is not found."; |
| return base::FilePath(); |
| } |
| |
| // Check if the device is mounted with quota option. |
| if (platform_->GetQuotaCurrentSpaceForUid(base::FilePath(device), 0) < 0) { |
| LOG(ERROR) << "Device is not mounted with quota feature enabled."; |
| return base::FilePath(); |
| } |
| |
| return base::FilePath(device); |
| } |
| |
| } // namespace cryptohome |