blob: 2296cf6c7c8db3652dccbb7bcb387745e1e88d17 [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cros-disks/mount_point.h"
#include <utility>
#include <base/check.h>
#include <base/containers/contains.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include "cros-disks/platform.h"
#include "cros-disks/quote.h"
namespace cros_disks {
std::unique_ptr<MountPoint> MountPoint::CreateUnmounted(
MountPointData data, const Platform* const platform) {
std::unique_ptr<MountPoint> mount_point =
std::make_unique<MountPoint>(std::move(data), platform);
mount_point->is_mounted_ = false;
return mount_point;
}
std::unique_ptr<MountPoint> MountPoint::Mount(MountPointData data,
const Platform* const platform,
MountError* const error) {
DCHECK(error);
*error = platform->Mount(data.source, data.mount_path.value(),
data.filesystem_type, data.flags, data.data);
if (*error != MountError::kSuccess) {
return nullptr;
}
return std::make_unique<MountPoint>(std::move(data), platform);
}
MountPoint::MountPoint(MountPointData data, const Platform* platform)
: data_(std::move(data)), platform_(platform) {
DCHECK(!path().empty());
}
MountError MountPoint::Unmount() {
MountError error = MountError::kPathNotMounted;
if (is_mounted_) {
// To prevent the umount() syscall from blocking for too long (b/258344222),
// request the FUSE process termination if this process is "safe" to kill.
if (process_ && is_read_only())
process_->KillPidNamespace();
error = platform_->Unmount(data_.mount_path, data_.filesystem_type);
if (error == MountError::kSuccess || error == MountError::kPathNotMounted) {
is_mounted_ = false;
if (eject_)
std::move(eject_).Run();
}
}
process_.reset();
if (launcher_exit_callback_) {
DCHECK_EQ(MountError::kInProgress, data_.error);
data_.error = MountError::kCancelled;
std::move(launcher_exit_callback_).Run(MountError::kCancelled);
}
if (!is_mounted_ && must_remove_dir_ &&
platform_->RemoveEmptyDirectory(data_.mount_path.value())) {
must_remove_dir_ = false;
}
return error;
}
MountError MountPoint::Remount(bool read_only) {
if (!is_mounted_)
return MountError::kPathNotMounted;
uint64_t flags = data_.flags;
if (read_only) {
flags |= MS_RDONLY;
} else {
flags &= ~MS_RDONLY;
}
const MountError error =
platform_->Mount(data_.source, data_.mount_path.value(),
data_.filesystem_type, flags | MS_REMOUNT, data_.data);
if (error == MountError::kSuccess)
data_.flags = flags;
return error;
}
MountError MountPoint::ConvertLauncherExitCodeToMountError(
const int exit_code) const {
if (exit_code == 0)
return MountError::kSuccess;
if (base::Contains(password_needed_exit_codes_, exit_code))
return MountError::kNeedPassword;
return MountError::kMountProgramFailed;
}
void MountPoint::OnLauncherExit(const int exit_code) {
// Record the FUSE launcher's exit code in Metrics.
if (metrics_ && !metrics_name_.empty())
metrics_->RecordFuseMounterErrorCode(metrics_name_, exit_code);
DCHECK_EQ(MountError::kInProgress, data_.error);
data_.error = ConvertLauncherExitCodeToMountError(exit_code);
DCHECK_NE(MountError::kInProgress, data_.error);
if (exit_code != 0 && process_ && !LOG_IS_ON(INFO)) {
for (const auto& s : process_->GetCapturedOutput()) {
LOG(ERROR) << process_->GetProgramName() << "[" << process_->pid()
<< "]: " << s;
}
}
if (launcher_exit_callback_)
std::move(launcher_exit_callback_).Run(data_.error);
}
bool MountPoint::ParseProgressMessage(std::string_view message,
int* const percent) {
if (message.empty() || message.back() != '%')
return false;
// |message| ends with a percent sign '%'
message.remove_suffix(1);
// Extract the number before the percent sign.
std::string_view::size_type i = message.size();
while (i > 0 && base::IsAsciiDigit(message[i - 1]))
i--;
message.remove_prefix(i);
DCHECK(percent);
return base::StringToInt(message, percent) && *percent >= 0 &&
*percent <= 100;
}
void MountPoint::OnProgress(const std::string_view message) {
int percent;
if (!ParseProgressMessage(message, &percent))
return;
progress_percent_ = percent;
if (progress_callback_)
progress_callback_.Run(this);
}
void MountPoint::SetProcess(std::unique_ptr<Process> process,
Metrics* const metrics,
std::string metrics_name,
std::vector<int> password_needed_exit_codes) {
DCHECK(!process_);
process_ = std::move(process);
DCHECK(process_);
DCHECK(!metrics_);
metrics_ = metrics;
DCHECK(metrics_name_.empty());
metrics_name_ = std::move(metrics_name);
password_needed_exit_codes_ = std::move(password_needed_exit_codes);
DCHECK_EQ(MountError::kSuccess, data_.error);
data_.error = MountError::kInProgress;
process_->SetLauncherExitCallback(
base::BindOnce(&MountPoint::OnLauncherExit, GetWeakPtr()));
process_->SetOutputCallback(
base::BindRepeating(&MountPoint::OnProgress, GetWeakPtr()));
}
} // namespace cros_disks