blob: 586e0ac7bec4d72d39a61f28218fa3977c81ed1c [file] [log] [blame]
// Copyright 2019 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 "cros-disks/mount_point.h"
#include <utility>
#include <base/check.h>
#include <base/containers/contains.h>
#include <base/logging.h>
#include "cros-disks/error_logger.h"
#include "cros-disks/mounter.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,
MountErrorType* const error) {
DCHECK(error);
*error = platform->Mount(data.source, data.mount_path.value(),
data.filesystem_type, data.flags, data.data);
if (*error != MOUNT_ERROR_NONE) {
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());
}
MountPoint::~MountPoint() {
Unmount();
}
MountErrorType MountPoint::Unmount() {
MountErrorType error = MOUNT_ERROR_PATH_NOT_MOUNTED;
if (is_mounted_) {
error = UnmountImpl();
if (error == MOUNT_ERROR_NONE || error == MOUNT_ERROR_PATH_NOT_MOUNTED) {
is_mounted_ = false;
if (eject_)
std::move(eject_).Run();
}
}
process_.reset();
if (launcher_exit_callback_) {
DCHECK_EQ(MOUNT_ERROR_IN_PROGRESS, data_.error);
data_.error = MOUNT_ERROR_CANCELLED;
std::move(launcher_exit_callback_).Run(MOUNT_ERROR_CANCELLED);
}
if (!is_mounted_ && must_remove_dir_ &&
platform_->RemoveEmptyDirectory(data_.mount_path.value())) {
LOG(INFO) << "Removed " << quote(data_.mount_path);
must_remove_dir_ = false;
}
return error;
}
MountErrorType MountPoint::Remount(bool read_only) {
if (!is_mounted_)
return MOUNT_ERROR_PATH_NOT_MOUNTED;
int flags = data_.flags;
if (read_only) {
flags |= MS_RDONLY;
} else {
flags &= ~MS_RDONLY;
}
const MountErrorType error = RemountImpl(flags);
if (!error)
data_.flags = flags;
return error;
}
MountErrorType MountPoint::UnmountImpl() {
// We take a 2-step approach to unmounting FUSE filesystems. First, try a
// normal unmount. This lets the VFS flush any pending data and lets the
// filesystem shut down cleanly. If the filesystem is busy, force unmount
// the filesystem. This is done because there is no good recovery path the
// user can take, and these filesystem are sometimes unmounted implicitly on
// login/logout/suspend.
const base::FilePath& mount_path = data_.mount_path;
if (const MountErrorType error = platform_->Unmount(mount_path.value(), 0);
error != MOUNT_ERROR_PATH_ALREADY_MOUNTED)
return error;
// The mount point couldn't be unmounted because it is BUSY.
LOG(INFO) << "Forcefully unmounting " << quote(mount_path) << "...";
// For FUSE filesystems, MNT_FORCE will cause the kernel driver to immediately
// close the channel to the user-space driver program and cancel all
// outstanding requests. However, if any program is still accessing the
// filesystem, the umount2() will fail with EBUSY and the mountpoint will
// still be attached. Since the mountpoint is no longer valid, use MNT_DETACH
// to also force the mountpoint to be disconnected. On a non-FUSE filesystem
// MNT_FORCE doesn't have effect, so it only handles MNT_DETACH, but it's OK
// to pass MNT_FORCE too.
return platform_->Unmount(mount_path.value(), MNT_FORCE | MNT_DETACH);
}
MountErrorType MountPoint::RemountImpl(int flags) {
return platform_->Mount(data_.source, data_.mount_path.value(),
data_.filesystem_type, flags | MS_REMOUNT,
data_.data);
}
MountErrorType MountPoint::ConvertLauncherExitCodeToMountError(
const int exit_code) const {
if (exit_code == 0)
return MOUNT_ERROR_NONE;
if (base::Contains(password_needed_exit_codes_, exit_code))
return MOUNT_ERROR_NEED_PASSWORD;
return MOUNT_ERROR_MOUNT_PROGRAM_FAILED;
}
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(MOUNT_ERROR_IN_PROGRESS, data_.error);
data_.error = ConvertLauncherExitCodeToMountError(exit_code);
DCHECK_NE(MOUNT_ERROR_IN_PROGRESS, data_.error);
if (launcher_exit_callback_)
std::move(launcher_exit_callback_).Run(data_.error);
}
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(MOUNT_ERROR_NONE, data_.error);
data_.error = MOUNT_ERROR_IN_PROGRESS;
process_->SetLauncherExitCallback(
base::BindOnce(&MountPoint::OnLauncherExit, GetWeakPtr()));
}
} // namespace cros_disks