blob: eec2908ce140eff17cb0399d1eb9bdb4ae70bf2f [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 "cros-disks/drivefs_helper.h"
#include <sys/mount.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <brillo/process/process_reaper.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cros-disks/fuse_mounter.h"
#include "cros-disks/mount_options.h"
#include "cros-disks/platform.h"
#include "cros-disks/uri.h"
#include "cros-disks/user.h"
namespace cros_disks {
namespace {
using testing::_;
using testing::DoAll;
using testing::EndsWith;
using testing::HasSubstr;
using testing::Invoke;
using testing::IsSupersetOf;
using testing::Return;
using testing::SaveArg;
using testing::SetArgPointee;
using testing::StrEq;
using testing::UnorderedElementsAre;
constexpr char kSource[] = "drivefs://id";
constexpr char kDataDir[] = "/home/chronos/user/GCache/foo";
constexpr char kMountPath[] = "/media/fuse/drivefs/id";
constexpr char kPrefixParam[] = "prefix=/media/fuse/drivefs/id";
constexpr char kDataDirParam[] = "datadir=/home/chronos/user/GCache/foo";
constexpr char kMyFilesParam[] = "myfiles=/home/chronos/user/MyFiles";
std::vector<std::string> ParseOptions(const SandboxedProcess& sandbox) {
CHECK_EQ(2, sandbox.arguments().size());
CHECK_EQ("-o", sandbox.arguments()[0]);
return base::SplitString(sandbox.arguments()[1], ",",
base::WhitespaceHandling::KEEP_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);
}
// Mock Platform implementation for testing.
class MockPlatform : public Platform {
public:
MockPlatform() {
ON_CALL(*this, GetRealPath(_, _))
.WillByDefault(Invoke(this, &MockPlatform::GetRealPathImpl));
ON_CALL(*this, DirectoryExists(_)).WillByDefault(Return(true));
ON_CALL(*this, GetOwnership(_, _, _))
.WillByDefault(DoAll(SetArgPointee<1>(kChronosUID), Return(true)));
}
MOCK_METHOD(bool,
GetRealPath,
(const std::string&, std::string*),
(const, override));
MOCK_METHOD(bool, PathExists, (const std::string&), (const, override));
MOCK_METHOD(bool, DirectoryExists, (const std::string&), (const, override));
MOCK_METHOD(bool, CreateDirectory, (const std::string&), (const, override));
MOCK_METHOD(bool,
RemoveEmptyDirectory,
(const std::string&),
(const, override));
MOCK_METHOD(bool,
GetOwnership,
(const std::string&, uid_t*, gid_t*),
(const, override));
MOCK_METHOD(bool,
SetPermissions,
(const std::string&, mode_t),
(const, override));
bool GetRealPathImpl(const std::string& path, std::string* real_path) const {
std::vector<std::string> components;
base::FilePath(path).GetComponents(&components);
base::FilePath result(components[0]);
components.erase(components.begin());
for (const auto& part : components) {
if (part != ".")
result = result.Append(part);
}
*real_path = result.value();
return true;
}
bool GetUserAndGroupId(const std::string& user,
uid_t* user_id,
gid_t* group_id) const override {
NOTREACHED();
return false;
}
bool GetGroupId(const std::string& group, gid_t* group_id) const override {
NOTREACHED();
return false;
}
bool SetOwnership(const std::string&, uid_t, gid_t) const override {
NOTREACHED();
return false;
}
};
class TestDrivefsHelper : public DrivefsHelper {
public:
TestDrivefsHelper(const Platform* platform,
brillo::ProcessReaper* process_reaper)
: DrivefsHelper(platform, process_reaper) {
}
using DrivefsHelper::ConfigureSandbox;
};
class DrivefsHelperTest : public ::testing::Test {
public:
DrivefsHelperTest() : helper_(&platform_, &process_reaper_) {}
protected:
MockPlatform platform_;
brillo::ProcessReaper process_reaper_;
TestDrivefsHelper helper_;
};
TEST_F(DrivefsHelperTest, ConfigureSandbox) {
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(
kSource, base::FilePath(kMountPath),
{"datadir=/home/chronos//user/GCache//foo/./"}, &sandbox);
EXPECT_EQ(MOUNT_ERROR_NONE, error);
auto options = ParseOptions(sandbox);
EXPECT_THAT(options,
UnorderedElementsAre(kDataDirParam, "identity=id", "uid=1000",
"gid=1001", kPrefixParam));
}
TEST_F(DrivefsHelperTest, ConfigureSandboxWithMyFiles) {
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(
kSource, base::FilePath(kMountPath),
{kDataDirParam, "myfiles=/home/chronos//user/.//MyFiles"}, &sandbox);
EXPECT_EQ(MOUNT_ERROR_NONE, error);
auto options = ParseOptions(sandbox);
EXPECT_THAT(options, IsSupersetOf({StrEq(kMyFilesParam)}));
}
TEST_F(DrivefsHelperTest, ConfigureSandboxFailsIfInvalidSource) {
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(
"drive://id", base::FilePath(kMountPath), {kDataDirParam}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
error = helper_.ConfigureSandbox("/dev/block", base::FilePath(kMountPath),
{kDataDirParam}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
error = helper_.ConfigureSandbox("drivefs:/foo", base::FilePath(kMountPath),
{kDataDirParam}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
}
TEST_F(DrivefsHelperTest, ConfigureSandboxFailsIfDataDirInvalid) {
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), {},
&sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath),
{"datadir=dodgy/path"}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath),
{"datadir=/nonhome/dir"}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath),
{"datadir=/home/chronos/../../etc/passwd"},
&sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
}
TEST_F(DrivefsHelperTest, ConfigureSandboxFailsIfDataDirDoesntExist) {
EXPECT_CALL(platform_, DirectoryExists(kDataDir)).WillOnce(Return(false));
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath),
{kDataDirParam}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
}
TEST_F(DrivefsHelperTest, ConfigureSandboxFailsWhenCantStat) {
EXPECT_CALL(platform_, GetOwnership(kDataDir, _, _)).WillOnce(Return(false));
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath),
{kDataDirParam}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
}
TEST_F(DrivefsHelperTest, ConfigureSandboxFailsWhenWrongOwner) {
EXPECT_CALL(platform_, GetOwnership(kDataDir, _, _))
.WillOnce(DoAll(SetArgPointee<1>(kChronosUID + 100), Return(false)));
FakeSandboxedProcess sandbox;
auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath),
{kDataDirParam}, &sandbox);
EXPECT_NE(MOUNT_ERROR_NONE, error);
}
} // namespace
} // namespace cros_disks