Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/abi/linux/ioctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ const (
KCOV_MODE_TRACE_CMP = 3
)

// File clone/dedup ioctls from include/uapi/linux/fs.h.
var (
FICLONE = IOW(0x94, 9, 4)
FICLONERANGE = IOW(0x94, 13, 32)
FIDEDUPERANGE = IOWR(0x94, 54, 24)
)

// FUSE_DEV_IOC_CLONE from include/uapi/linux/fuse.h.
var (
FUSE_DEV_IOC_CLONE = IOR(229, 0, 4)
Expand Down
6 changes: 6 additions & 0 deletions pkg/sentry/vfs/file_description_impl_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ func (FileDescriptionDefaultImpl) ConfigureMMap(ctx context.Context, opts *memma
// Ioctl implements FileDescriptionImpl.Ioctl analogously to
// file_operations::unlocked_ioctl == NULL in Linux.
func (FileDescriptionDefaultImpl) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
// Return EOPNOTSUPP for reflink ioctls when the filesystem
// does not support remap_file_range, matching Linux behavior.
switch args[1].Uint() {
case linux.FICLONE, linux.FICLONERANGE, linux.FIDEDUPERANGE:
return 0, linuxerr.EOPNOTSUPP
}
return 0, linuxerr.ENOTTY
}

Expand Down
1 change: 1 addition & 0 deletions test/syscalls/linux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,7 @@ cc_binary(
"//test/util:file_descriptor",
"//test/util:signal_util",
"//test/util:socket_util",
"//test/util:temp_path",
"//test/util:test_main",
"//test/util:test_util",
],
Expand Down
49 changes: 49 additions & 0 deletions test/syscalls/linux/ioctl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <net/if.h>
#include <netdb.h>
#include <signal.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
Expand All @@ -29,6 +30,7 @@
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/signal_util.h"
#include "test/util/temp_path.h"
#include "test/util/socket_util.h"
#include "test/util/test_util.h"

Expand Down Expand Up @@ -422,6 +424,53 @@ std::vector<SocketKind> IoctlSocketTypes() {
INSTANTIATE_TEST_SUITE_P(IoctlTest, IoctlTestSIOCGIFCONF,
::testing::ValuesIn(IoctlSocketTypes()));

// FICLONE/FICLONERANGE/FIDEDUPERANGE should return EOPNOTSUPP on
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests will fail when run on Linux. IMO syscall test is not required for this change. We primarliy use them to compare gVisor <-> Linux compatibility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — I realized that on filesystems that support reflink, these ioctls would succeed instead of returning EOPNOTSUPP, so the tests could indeed fail on native Linux depending on the filesystem. I've send a follow-up pr #12863 to remove these test cases. Thanks for catching this!

// filesystems that do not support reflink (e.g. tmpfs).
TEST_F(IoctlTest, FicloneReturnsEopnotsupp) {
auto src = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
auto dst = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const FileDescriptor src_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(src.path(), O_RDONLY));
const FileDescriptor dst_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(dst.path(), O_WRONLY));

EXPECT_THAT(ioctl(dst_fd.get(), FICLONE, src_fd.get()),
SyscallFailsWithErrno(EOPNOTSUPP));
}

TEST_F(IoctlTest, FiclonerangeReturnsEopnotsupp) {
auto src = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
auto dst = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const FileDescriptor src_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(src.path(), O_RDONLY));
const FileDescriptor dst_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(dst.path(), O_WRONLY));

struct file_clone_range range = {};
range.src_fd = src_fd.get();
EXPECT_THAT(ioctl(dst_fd.get(), FICLONERANGE, &range),
SyscallFailsWithErrno(EOPNOTSUPP));
}

TEST_F(IoctlTest, FideduperangeReturnsEopnotsupp) {
auto src = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
auto dst = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const FileDescriptor src_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(src.path(), O_RDONLY));
const FileDescriptor dst_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(dst.path(), O_RDWR));

struct file_dedupe_range *range = static_cast<struct file_dedupe_range *>(
calloc(1, sizeof(*range) + sizeof(struct file_dedupe_range_info)));
ASSERT_NE(range, nullptr);
range->src_length = 0;
range->dest_count = 1;
range->info[0].dest_fd = dst_fd.get();
EXPECT_THAT(ioctl(src_fd.get(), FIDEDUPERANGE, range),
SyscallFailsWithErrno(EOPNOTSUPP));
free(range);
}

} // namespace

TEST_F(IoctlTest, FIOGETOWNSucceeds) {
Expand Down
Loading