blob: e828c1d44df5b221d684e642b2f2b902498becc8 [file] [log] [blame]
// 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/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>
namespace cryptohome {
ArcDiskQuota::ArcDiskQuota(Platform* platform,
const base::FilePath& home,
const base::FilePath& shadow_root_relative_path,
const base::FilePath& android_data_relative_path)
: platform_(platform),
home_(home),
shadow_root_relative_path_(shadow_root_relative_path),
android_data_relative_path_(android_data_relative_path) {}
ArcDiskQuota::~ArcDiskQuota() = default;
void ArcDiskQuota::Initialize() {
device_ = GetDevice();
}
bool ArcDiskQuota::IsQuotaSupported() const {
if (device_.empty()) {
LOG(ERROR) << "No quota mount is found.";
return false;
}
// TODO(risan): Support quota for more than 1 Android user,
// after that, the following check could be removed.
int cnt = GetAndroidUserCount();
if (cnt != 1) {
LOG(ERROR) << "Quota is supported only for 1 Android user, found " << cnt
<< " Android users.";
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 + kContainerShiftUid;
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 + kContainerShiftGid;
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;
}
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);
}
int32_t ArcDiskQuota::GetAndroidUserCount() const {
// Get the number of users that contains android-data under:
// /home/.shadow/<HASH>/mount/root/android-data
FileEnumerator* dir_enum =
platform_->GetFileEnumerator(home_.Append(shadow_root_relative_path_),
false, base::FileEnumerator::DIRECTORIES);
// The number of user directories that contains android-data dir.
int number_of_android_users = 0;
for (base::FilePath user_dir = dir_enum->Next(); !user_dir.empty();
user_dir = dir_enum->Next()) {
base::FilePath data_dir = user_dir.Append(android_data_relative_path_);
if (platform_->FileExists(data_dir)) {
++number_of_android_users;
}
}
return number_of_android_users;
}
} // namespace cryptohome