blob: b2d7956d5deee5eb80704b75fb85d36c31cdd33e [file] [log] [blame]
// Copyright (c) 2012 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/system_mounter.h"
#include <sys/mount.h>
#include <base/files/scoped_temp_dir.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cros-disks/mount_options.h"
#include "cros-disks/mount_point.h"
#include "cros-disks/platform.h"
namespace cros_disks {
using testing::_;
using testing::Return;
namespace {
constexpr uint64_t kDefaultMountFlags =
MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC | MS_NOSYMFOLLOW;
class PlatformForTest : public Platform {
public:
// Tests are being run on devices that don't support nosymfollow. Strip it.
MountErrorType Mount(const std::string& source,
const std::string& target,
const std::string& filesystem_type,
uint64_t flags,
const std::string& options) const override {
EXPECT_TRUE((flags & MS_NOSYMFOLLOW) == MS_NOSYMFOLLOW);
return Platform::Mount(source, target, filesystem_type,
flags & ~MS_NOSYMFOLLOW, options);
}
};
class MockPlatform : public Platform {
public:
MOCK_METHOD(MountErrorType,
Mount,
(const std::string& source,
const std::string& target,
const std::string& filesystem_type,
uint64_t flags,
const std::string& options),
(const, override));
MOCK_METHOD(MountErrorType,
Unmount,
(const std::string& target, int flags),
(const, override));
};
} // namespace
TEST(SystemMounterTest, RunAsRootMount) {
PlatformForTest platform;
SystemMounter mounter(&platform, "tmpfs", /* read_only= */ false, {});
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
MountErrorType error = MOUNT_ERROR_NONE;
auto mountpoint = mounter.Mount("/dev/null", temp_dir.GetPath(), {}, &error);
EXPECT_TRUE(mountpoint);
EXPECT_EQ(MOUNT_ERROR_NONE, error);
error = mountpoint->Unmount();
EXPECT_EQ(MOUNT_ERROR_NONE, error);
}
TEST(SystemMounterTest, RunAsRootMountWithNonexistentSourcePath) {
PlatformForTest platform;
SystemMounter mounter(&platform, "ext2", /* read_only= */ false, {});
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// To test mounting a nonexistent source path, use ext2 as the
// filesystem type instead of tmpfs since tmpfs does not care
// about source path.
MountErrorType error = MOUNT_ERROR_NONE;
auto mountpoint =
mounter.Mount("/nonexistent", temp_dir.GetPath(), {}, &error);
EXPECT_FALSE(mountpoint);
EXPECT_EQ(MOUNT_ERROR_INVALID_PATH, error);
}
TEST(SystemMounterTest, RunAsRootMountWithNonexistentTargetPath) {
PlatformForTest platform;
SystemMounter mounter(&platform, "tmpfs", /* read_only= */ false, {});
MountErrorType error = MOUNT_ERROR_NONE;
auto mountpoint =
mounter.Mount("/dev/null", base::FilePath("/nonexistent"), {}, &error);
EXPECT_FALSE(mountpoint);
EXPECT_EQ(MOUNT_ERROR_INVALID_PATH, error);
}
TEST(SystemMounterTest, RunAsRootMountWithNonexistentFilesystemType) {
PlatformForTest platform;
SystemMounter mounter(&platform, "nonexistentfs", /* read_only= */ false, {});
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
MountErrorType error = MOUNT_ERROR_NONE;
auto mountpoint = mounter.Mount("/dev/null", temp_dir.GetPath(), {}, &error);
EXPECT_FALSE(mountpoint);
EXPECT_EQ(MOUNT_ERROR_UNSUPPORTED_FILESYSTEM, error);
}
TEST(SystemMounterTest, MountFilesystem) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {});
EXPECT_CALL(platform, Mount("/dev/block", "/mnt/dir", "fstype", _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error);
ASSERT_TRUE(mountpoint);
EXPECT_EQ(MOUNT_ERROR_NONE, error);
EXPECT_CALL(platform, Unmount("/mnt/dir", 0))
.WillOnce(Return(MOUNT_ERROR_NONE));
mountpoint.reset();
}
TEST(SystemMounterTest, MountFailed) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {});
EXPECT_CALL(platform, Mount("/dev/block", "/mnt/dir", "fstype", _, _))
.WillOnce(Return(MOUNT_ERROR_PATH_NOT_MOUNTED));
EXPECT_CALL(platform, Unmount).Times(0);
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error);
ASSERT_FALSE(mountpoint);
EXPECT_EQ(MOUNT_ERROR_PATH_NOT_MOUNTED, error);
}
TEST(SystemMounterTest, UnmountFailedNoRetry) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {});
EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error);
EXPECT_CALL(platform, Unmount("/mnt/dir", 0))
.WillOnce(Return(MOUNT_ERROR_INVALID_ARGUMENT))
.WillOnce(Return(MOUNT_ERROR_NONE));
EXPECT_EQ(MOUNT_ERROR_INVALID_ARGUMENT, mountpoint->Unmount());
mountpoint.reset();
}
TEST(SystemMounterTest, UnmountBusyRetry) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {});
EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error);
EXPECT_CALL(platform, Unmount("/mnt/dir", 0))
.WillOnce(Return(MOUNT_ERROR_PATH_ALREADY_MOUNTED));
EXPECT_CALL(platform, Unmount("/mnt/dir", MNT_DETACH | MNT_FORCE))
.WillOnce(Return(MOUNT_ERROR_NONE));
EXPECT_EQ(MOUNT_ERROR_NONE, mountpoint->Unmount());
mountpoint.reset();
}
TEST(SystemMounterTest, MountFlags) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {});
EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", kDefaultMountFlags, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error);
}
TEST(SystemMounterTest, ReadOnlyForced) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ true, {});
EXPECT_CALL(platform,
Mount(_, "/mnt/dir", "fstype", kDefaultMountFlags | MS_RDONLY, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error);
}
TEST(SystemMounterTest, ReadOnlyRequested) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {});
EXPECT_CALL(platform,
Mount(_, "/mnt/dir", "fstype", kDefaultMountFlags | MS_RDONLY, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint =
mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {"ro"}, &error);
}
TEST(SystemMounterTest, MountOptionsPassedButParamsIgnored) {
MockPlatform platform;
SystemMounter mounter(&platform, "fstype", /* read_only= */ false,
{"foo", "bar=baz"});
EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", _, "foo,bar=baz"))
.WillOnce(Return(MOUNT_ERROR_NONE));
MountErrorType error = MOUNT_ERROR_UNKNOWN;
auto mountpoint = mounter.Mount("/dev/block", base::FilePath("/mnt/dir"),
{"abc=def", "xyz"}, &error);
}
} // namespace cros_disks