blob: 8352857e88e71e3b6fa9fd33e16c7fe45416f20e [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <base/command_line.h>
#include <brillo/flag_helper.h>
#include <gtest/gtest.h>
#include "arc/mount-passthrough/mount-passthrough-util.h"
namespace arc {
// Unit testing with brillo::FlagHelper and base::CommandLine requires
// some setup as those things touch global states. This is based on
// brillo/flag_helper_test.cc.
class MountPassthroughUtilTest : public ::testing::Test {
public:
MountPassthroughUtilTest() {}
~MountPassthroughUtilTest() override {
brillo::FlagHelper::ResetForTesting();
}
static void SetUpTestCase() { base::CommandLine::Init(0, nullptr); }
// Returns the front value, and erases it.
std::string PopFront(std::vector<std::string>& list) {
if (list.empty()) {
return "<not enough args>";
}
std::string value = list.front();
list.erase(list.begin());
return value;
}
};
// On VM, MyFiles and /media/removable are very similar (only --source and
// --dest are different), thus only MyFiles is tested.
TEST_F(MountPassthroughUtilTest, VmMyFiles) {
// From arc/vm/media-sharing-services/init/arcvm-mount-myfiles.conf.
const char* argv[] = {
"mount-passthrough-jailed",
"--source=/home/user/2d6fef33bb331bb08f3ca9d4db7078d776a251a6/MyFiles",
"--dest=/run/arcvm/media/MyFiles",
"--fuse_umask=007",
"--fuse_uid=10058",
"--fuse_gid=1077",
"--enter_concierge_namespace",
"--max_number_of_open_fds=262144",
};
base::CommandLine command_line(std::size(argv), argv);
brillo::FlagHelper::GetInstance()->set_command_line_for_testing(
&command_line);
CommandLineFlags flags;
ParseCommandLine(std::size(argv), argv, &flags);
auto args = CreateMinijailCommandLineArgs(flags);
EXPECT_EQ("/sbin/minijail0", PopFront(args));
// Enter the concierge namespace.
EXPECT_EQ("-V", PopFront(args));
EXPECT_EQ("/run/namespaces/mnt_concierge", PopFront(args));
// Enter a new cgroup namespace.
EXPECT_EQ("-N", PopFront(args));
// Enter a new UTS namespace.
EXPECT_EQ("--uts", PopFront(args));
// Enter a new VFS namespace and remount /proc read-only.
EXPECT_EQ("-v", PopFront(args));
EXPECT_EQ("-r", PopFront(args));
// Enter a new network namespace.
EXPECT_EQ("-e", PopFront(args));
// Enter a new IPC namespace.
EXPECT_EQ("-l", PopFront(args));
// Grant CAP_SYS_ADMIN needed to mount FUSE filesystem.
EXPECT_EQ("-c", PopFront(args));
EXPECT_EQ("cap_sys_admin+eip", PopFront(args));
// Set uid and gid of the daemon as chronos.
EXPECT_EQ("-u", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-g", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
// Inherit supplementary groups.
EXPECT_EQ("-G", PopFront(args));
// Allow sharing mounts between CrOS and Android.
EXPECT_EQ("-K", PopFront(args));
// Specify the maximum number of file descriptors the process can open.
EXPECT_EQ("-R", PopFront(args));
EXPECT_EQ("RLIMIT_NOFILE,262144,262144", PopFront(args));
// Finally, specify mount-passthrough command line arguments.
EXPECT_EQ("--", PopFront(args));
EXPECT_EQ("/usr/bin/mount-passthrough", PopFront(args));
EXPECT_EQ(
"--source=/home/user/2d6fef33bb331bb08f3ca9d4db7078d776a251a6/MyFiles",
PopFront(args));
EXPECT_EQ("--dest=/run/arcvm/media/MyFiles", PopFront(args));
EXPECT_EQ("--fuse_umask=007", PopFront(args));
EXPECT_EQ("--fuse_uid=10058", PopFront(args));
EXPECT_EQ("--fuse_gid=1077", PopFront(args));
EXPECT_EQ("--android_app_access_type=full", PopFront(args));
EXPECT_TRUE(args.empty());
}
// Container behaves very differently from VM (ex. not using the concierge
// namespace).
TEST_F(MountPassthroughUtilTest, ContainerMyFiles) {
// From arc/container/myfiles/arc-myfiles.conf.
const char* argv[] = {
"mount-passthrough-jailed",
"--source=/home/chronos/user/MyFiles",
"--dest=/run/arc/media/MyFiles",
"--fuse_umask=007",
"--fuse_uid=1023",
"--fuse_gid=1023",
};
base::CommandLine command_line(std::size(argv), argv);
brillo::FlagHelper::GetInstance()->set_command_line_for_testing(
&command_line);
CommandLineFlags flags;
ParseCommandLine(std::size(argv), argv, &flags);
auto args = CreateMinijailCommandLineArgs(flags);
EXPECT_EQ("/sbin/minijail0", PopFront(args));
// Use minimalistic-mountns profile.
EXPECT_EQ("--profile=minimalistic-mountns", PopFront(args));
EXPECT_EQ("--no-fs-restrictions", PopFront(args));
// Same with VM.
EXPECT_EQ("-N", PopFront(args));
EXPECT_EQ("--uts", PopFront(args));
EXPECT_EQ("-v", PopFront(args));
EXPECT_EQ("-r", PopFront(args));
EXPECT_EQ("-e", PopFront(args));
EXPECT_EQ("-l", PopFront(args));
EXPECT_EQ("-c", PopFront(args));
EXPECT_EQ("cap_sys_admin+eip", PopFront(args));
EXPECT_EQ("-u", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-g", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-G", PopFront(args));
EXPECT_EQ("-K", PopFront(args));
EXPECT_EQ("-R", PopFront(args));
EXPECT_EQ("RLIMIT_NOFILE,8192,8192", PopFront(args));
// Mount tmpfs on /mnt.
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("tmpfs,/mnt,tmpfs,MS_NOSUID|MS_NODEV|MS_NOEXEC", PopFront(args));
// Bind /dev/fuse to mount FUSE file systems.
EXPECT_EQ("-b", PopFront(args));
EXPECT_EQ("/dev/fuse", PopFront(args));
// Mark PRIVATE recursively under (pivot) root, in order not to
// expose shared mount points accidentally.
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("none,/,none,0x44000", PopFront(args));
// Mount source/dest directories.
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/home/chronos/user/MyFiles,/mnt/source,none,0x5000",
PopFront(args));
// 0x84000 = slave,rec
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/home/chronos/user/MyFiles,/mnt/source,none,0x84000",
PopFront(args));
// 0x102e = bind,remount,noexec,nodev,nosuid
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/home/chronos/user/MyFiles,/mnt/source,none,0x102e",
PopFront(args));
// 0x1000 = bind
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/run/arc/media/MyFiles,/mnt/dest,none,0x1000", PopFront(args));
// 0x102e = bind,remount,noexec,nodev,nosuid
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/run/arc/media/MyFiles,/mnt/dest,none,0x102e", PopFront(args));
// Mostly same with VM (different source/dest/uid/gid).
EXPECT_EQ("--", PopFront(args));
EXPECT_EQ("/usr/bin/mount-passthrough", PopFront(args));
EXPECT_EQ("--source=/mnt/source", PopFront(args));
EXPECT_EQ("--dest=/mnt/dest", PopFront(args));
EXPECT_EQ("--fuse_umask=007", PopFront(args));
EXPECT_EQ("--fuse_uid=1023", PopFront(args));
EXPECT_EQ("--fuse_gid=1023", PopFront(args));
EXPECT_EQ("--android_app_access_type=full", PopFront(args));
EXPECT_TRUE(args.empty());
}
// This is very similar to ContainerMyFiles but --dest and
// --android_app_access_type are different. Make sure non-default
// --android_app_access_type value is handled correctly.
//
// MyFiles-write and MyFiles-default aren't tested as those are similar
// enough to ContainerMyFiles and ContainerMyFilesRead.
TEST_F(MountPassthroughUtilTest, ContainerMyFilesRead) {
// From arc/container/myfiles/arc-myfiles-read.conf.
const char* argv[] = {
"mount-passthrough-jailed",
"--source=/home/chronos/user/MyFiles",
"--dest=/run/arc/media/MyFiles-read",
"--fuse_umask=027",
"--fuse_uid=0",
"--fuse_gid=9997",
"--android_app_access_type=read",
};
base::CommandLine command_line(std::size(argv), argv);
brillo::FlagHelper::GetInstance()->set_command_line_for_testing(
&command_line);
CommandLineFlags flags;
ParseCommandLine(std::size(argv), argv, &flags);
auto args = CreateMinijailCommandLineArgs(flags);
// Same with ContainerMyFiles.
EXPECT_EQ("/sbin/minijail0", PopFront(args));
EXPECT_EQ("--profile=minimalistic-mountns", PopFront(args));
EXPECT_EQ("--no-fs-restrictions", PopFront(args));
EXPECT_EQ("-N", PopFront(args));
EXPECT_EQ("--uts", PopFront(args));
EXPECT_EQ("-v", PopFront(args));
EXPECT_EQ("-r", PopFront(args));
EXPECT_EQ("-e", PopFront(args));
EXPECT_EQ("-l", PopFront(args));
EXPECT_EQ("-c", PopFront(args));
EXPECT_EQ("cap_sys_admin+eip", PopFront(args));
EXPECT_EQ("-u", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-g", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-G", PopFront(args));
EXPECT_EQ("-K", PopFront(args));
EXPECT_EQ("-R", PopFront(args));
EXPECT_EQ("RLIMIT_NOFILE,8192,8192", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("tmpfs,/mnt,tmpfs,MS_NOSUID|MS_NODEV|MS_NOEXEC", PopFront(args));
EXPECT_EQ("-b", PopFront(args));
EXPECT_EQ("/dev/fuse", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("none,/,none,0x44000", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/home/chronos/user/MyFiles,/mnt/source,none,0x5000",
PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/home/chronos/user/MyFiles,/mnt/source,none,0x84000",
PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/home/chronos/user/MyFiles,/mnt/source,none,0x102e",
PopFront(args));
// MyFiles-read should be used as the destination.
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/run/arc/media/MyFiles-read,/mnt/dest,none,0x1000",
PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/run/arc/media/MyFiles-read,/mnt/dest,none,0x102e",
PopFront(args));
// Same with ContainerMyFiles.
EXPECT_EQ("--", PopFront(args));
EXPECT_EQ("/usr/bin/mount-passthrough", PopFront(args));
EXPECT_EQ("--source=/mnt/source", PopFront(args));
EXPECT_EQ("--dest=/mnt/dest", PopFront(args));
EXPECT_EQ("--fuse_umask=027", PopFront(args));
EXPECT_EQ("--fuse_uid=0", PopFront(args));
EXPECT_EQ("--fuse_gid=9997", PopFront(args));
// The access type should be "read" instead of "full".
EXPECT_EQ("--android_app_access_type=read", PopFront(args));
EXPECT_TRUE(args.empty());
}
// On Container, /media/removable uses --use_default_selinux_context. Make
// sure that the flag is handled correctly.
// /media/removable-{write,default} aren't tested as those are similar
// enough to /media/removable-read.
TEST_F(MountPassthroughUtilTest, ContainerRemovableRead) {
// From arc/container/removable-media/arc-removable-media-read.conf.
const char* argv[] = {"mount-passthrough-jailed",
"--source=/media/removable",
"--dest=/run/arc/media/removable-read",
"--fuse_umask=027",
"--fuse_uid=0",
"--fuse_gid=9997",
"--android_app_access_type=read",
"--use_default_selinux_context"};
base::CommandLine command_line(std::size(argv), argv);
brillo::FlagHelper::GetInstance()->set_command_line_for_testing(
&command_line);
CommandLineFlags flags;
ParseCommandLine(std::size(argv), argv, &flags);
auto args = CreateMinijailCommandLineArgs(flags);
// Same with ContainerMyFiles.
EXPECT_EQ("/sbin/minijail0", PopFront(args));
EXPECT_EQ("--profile=minimalistic-mountns", PopFront(args));
EXPECT_EQ("--no-fs-restrictions", PopFront(args));
EXPECT_EQ("-N", PopFront(args));
EXPECT_EQ("--uts", PopFront(args));
EXPECT_EQ("-v", PopFront(args));
EXPECT_EQ("-r", PopFront(args));
EXPECT_EQ("-e", PopFront(args));
EXPECT_EQ("-l", PopFront(args));
EXPECT_EQ("-c", PopFront(args));
EXPECT_EQ("cap_sys_admin+eip", PopFront(args));
EXPECT_EQ("-u", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-g", PopFront(args));
EXPECT_EQ("chronos", PopFront(args));
EXPECT_EQ("-G", PopFront(args));
EXPECT_EQ("-K", PopFront(args));
EXPECT_EQ("-R", PopFront(args));
EXPECT_EQ("RLIMIT_NOFILE,8192,8192", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("tmpfs,/mnt,tmpfs,MS_NOSUID|MS_NODEV|MS_NOEXEC", PopFront(args));
EXPECT_EQ("-b", PopFront(args));
EXPECT_EQ("/dev/fuse", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("none,/,none,0x44000", PopFront(args));
// /media/removable should be used as the source.
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/media/removable,/mnt/source,none,0x5000", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/media/removable,/mnt/source,none,0x84000", PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/media/removable,/mnt/source,none,0x102e", PopFront(args));
// /media/removable-read should be used as the destination.
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/run/arc/media/removable-read,/mnt/dest,none,0x1000",
PopFront(args));
EXPECT_EQ("-k", PopFront(args));
EXPECT_EQ("/run/arc/media/removable-read,/mnt/dest,none,0x102e",
PopFront(args));
// Same with ContainerMyFilesRead.
EXPECT_EQ("--", PopFront(args));
EXPECT_EQ("/usr/bin/mount-passthrough", PopFront(args));
EXPECT_EQ("--source=/mnt/source", PopFront(args));
EXPECT_EQ("--dest=/mnt/dest", PopFront(args));
EXPECT_EQ("--fuse_umask=027", PopFront(args));
EXPECT_EQ("--fuse_uid=0", PopFront(args));
EXPECT_EQ("--fuse_gid=9997", PopFront(args));
EXPECT_EQ("--android_app_access_type=read", PopFront(args));
// The default SELinux context should be used.
EXPECT_EQ("--use_default_selinux_context", PopFront(args));
EXPECT_TRUE(args.empty());
}
} // namespace arc