blob: c732cb6ce90927062e6338ebce48ad97515cc400 [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/logging.h>
#include "cros-disks/error_logger.h"
#include "cros-disks/mounter.h"
#include "cros-disks/quote.h"
namespace cros_disks {
// static
std::unique_ptr<MountPoint> MountPoint::CreateLeaking(
const base::FilePath& path) {
auto mount_point =
std::make_unique<MountPoint>(MountPointData{path}, nullptr);
mount_point->Release();
return mount_point;
}
// static
std::unique_ptr<MountPoint> MountPoint::Mount(MountPointData data,
const Platform* platform,
MountErrorType* 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() {
if (!released_) {
MountErrorType error = Unmount();
if (error != MOUNT_ERROR_NONE && error != MOUNT_ERROR_PATH_NOT_MOUNTED) {
LOG(WARNING) << "Unmount error while destroying MountPoint("
<< quote(path()) << "): " << error;
}
}
}
void MountPoint::Release() {
released_ = true;
}
MountErrorType MountPoint::Unmount() {
if (released_) {
// TODO(amistry): "not mounted" and "already unmounted" are logically
// different and should be treated differently. Introduce a new error code
// to represent this distinction.
return MOUNT_ERROR_PATH_NOT_MOUNTED;
}
MountErrorType error = UnmountImpl();
if (error == MOUNT_ERROR_NONE || error == MOUNT_ERROR_PATH_NOT_MOUNTED) {
released_ = true;
}
return error;
}
MountErrorType MountPoint::Remount(bool read_only) {
if (released_) {
return MOUNT_ERROR_PATH_NOT_MOUNTED;
}
int new_flags = (data_.flags & ~MS_RDONLY) | (read_only ? MS_RDONLY : 0);
MountErrorType error = RemountImpl(new_flags);
if (error == MOUNT_ERROR_NONE) {
data_.flags = new_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.
MountErrorType error = platform_->Unmount(path().value(), 0 /* flags */);
if (error != MOUNT_ERROR_PATH_ALREADY_MOUNTED) {
// MOUNT_ERROR_PATH_ALREADY_MOUNTED is returned on EBUSY.
return error;
}
// 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.
LOG(WARNING) << "Mount point " << quote(path())
<< " is busy, using force unmount";
return platform_->Unmount(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);
}
} // namespace cros_disks