mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Link: https://lore.kernel.org/20250624-work-pidfs-fhandle-v2-11-d02a04858fe3@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org>
563 lines
14 KiB
C
563 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <linux/types.h>
|
|
#include <poll.h>
|
|
#include <sched.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syscall.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/kcmp.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "pidfd.h"
|
|
#include "../kselftest_harness.h"
|
|
|
|
FIXTURE(file_handle)
|
|
{
|
|
pid_t pid;
|
|
int pidfd;
|
|
|
|
pid_t child_pid1;
|
|
int child_pidfd1;
|
|
|
|
pid_t child_pid2;
|
|
int child_pidfd2;
|
|
|
|
pid_t child_pid3;
|
|
int child_pidfd3;
|
|
};
|
|
|
|
FIXTURE_SETUP(file_handle)
|
|
{
|
|
int ret;
|
|
int ipc_sockets[2];
|
|
char c;
|
|
|
|
self->pid = getpid();
|
|
self->pidfd = sys_pidfd_open(self->pid, 0);
|
|
ASSERT_GE(self->pidfd, 0);
|
|
|
|
ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWUSER);
|
|
EXPECT_GE(self->child_pid1, 0);
|
|
|
|
if (self->child_pid1 == 0) {
|
|
close(ipc_sockets[0]);
|
|
|
|
if (write_nointr(ipc_sockets[1], "1", 1) < 0)
|
|
_exit(EXIT_FAILURE);
|
|
|
|
close(ipc_sockets[1]);
|
|
|
|
pause();
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
close(ipc_sockets[1]);
|
|
ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
|
|
close(ipc_sockets[0]);
|
|
|
|
ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWUSER | CLONE_NEWPID);
|
|
EXPECT_GE(self->child_pid2, 0);
|
|
|
|
if (self->child_pid2 == 0) {
|
|
close(ipc_sockets[0]);
|
|
|
|
if (write_nointr(ipc_sockets[1], "1", 1) < 0)
|
|
_exit(EXIT_FAILURE);
|
|
|
|
close(ipc_sockets[1]);
|
|
|
|
pause();
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
close(ipc_sockets[1]);
|
|
ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
|
|
close(ipc_sockets[0]);
|
|
|
|
ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
|
|
EXPECT_EQ(ret, 0);
|
|
|
|
self->child_pid3 = create_child(&self->child_pidfd3, CLONE_NEWUSER | CLONE_NEWPID);
|
|
EXPECT_GE(self->child_pid3, 0);
|
|
|
|
if (self->child_pid3 == 0) {
|
|
close(ipc_sockets[0]);
|
|
|
|
if (write_nointr(ipc_sockets[1], "1", 1) < 0)
|
|
_exit(EXIT_FAILURE);
|
|
|
|
close(ipc_sockets[1]);
|
|
|
|
pause();
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
close(ipc_sockets[1]);
|
|
ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
|
|
close(ipc_sockets[0]);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(file_handle)
|
|
{
|
|
EXPECT_EQ(close(self->pidfd), 0);
|
|
|
|
EXPECT_EQ(sys_pidfd_send_signal(self->child_pidfd1, SIGKILL, NULL, 0), 0);
|
|
if (self->child_pidfd1 >= 0)
|
|
EXPECT_EQ(0, close(self->child_pidfd1));
|
|
|
|
EXPECT_EQ(sys_waitid(P_PID, self->child_pid1, NULL, WEXITED), 0);
|
|
|
|
EXPECT_EQ(sys_pidfd_send_signal(self->child_pidfd2, SIGKILL, NULL, 0), 0);
|
|
if (self->child_pidfd2 >= 0)
|
|
EXPECT_EQ(0, close(self->child_pidfd2));
|
|
|
|
EXPECT_EQ(sys_waitid(P_PID, self->child_pid2, NULL, WEXITED), 0);
|
|
|
|
if (self->child_pidfd3 >= 0) {
|
|
EXPECT_EQ(sys_pidfd_send_signal(self->child_pidfd3, SIGKILL, NULL, 0), 0);
|
|
EXPECT_EQ(0, close(self->child_pidfd3));
|
|
EXPECT_EQ(sys_waitid(P_PID, self->child_pid3, NULL, WEXITED), 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Test that we can decode a pidfs file handle in the same pid
|
|
* namespace.
|
|
*/
|
|
TEST_F(file_handle, file_handle_same_pidns)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
int pidfd = -EBADF;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd1, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd1, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, O_CLOEXEC);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, O_NONBLOCK);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
free(fh);
|
|
}
|
|
|
|
/*
|
|
* Test that we can decode a pidfs file handle from a child pid
|
|
* namespace.
|
|
*/
|
|
TEST_F(file_handle, file_handle_child_pidns)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
int pidfd = -EBADF;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd2, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd2, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, O_CLOEXEC);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, O_NONBLOCK);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
free(fh);
|
|
}
|
|
|
|
/*
|
|
* Test that we fail to decode a pidfs file handle from an ancestor
|
|
* child pid namespace.
|
|
*/
|
|
TEST_F(file_handle, file_handle_foreign_pidns)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
pid_t pid;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->pidfd, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(setns(self->child_pidfd2, CLONE_NEWUSER | CLONE_NEWPID), 0);
|
|
|
|
pid = fork();
|
|
ASSERT_GE(pid, 0);
|
|
|
|
if (pid == 0) {
|
|
int pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
if (pidfd >= 0) {
|
|
TH_LOG("Managed to open pidfd outside of the caller's pid namespace hierarchy");
|
|
_exit(1);
|
|
}
|
|
_exit(0);
|
|
}
|
|
|
|
ASSERT_EQ(wait_for_pid(pid), 0);
|
|
|
|
free(fh);
|
|
}
|
|
|
|
/*
|
|
* Test that we can decode a pidfs file handle of a process that has
|
|
* exited but not been reaped.
|
|
*/
|
|
TEST_F(file_handle, pid_has_exited)
|
|
{
|
|
int mnt_id, pidfd, child_pidfd3;
|
|
struct file_handle *fh;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd3, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd3, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
child_pidfd3 = self->child_pidfd3;
|
|
self->child_pidfd3 = -EBADF;
|
|
EXPECT_EQ(sys_pidfd_send_signal(child_pidfd3, SIGKILL, NULL, 0), 0);
|
|
EXPECT_EQ(close(child_pidfd3), 0);
|
|
EXPECT_EQ(sys_waitid(P_PID, self->child_pid3, NULL, WEXITED | WNOWAIT), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
EXPECT_EQ(sys_waitid(P_PID, self->child_pid3, NULL, WEXITED), 0);
|
|
}
|
|
|
|
/*
|
|
* Test that we fail to decode a pidfs file handle of a process that has
|
|
* already been reaped.
|
|
*/
|
|
TEST_F(file_handle, pid_has_been_reaped)
|
|
{
|
|
int mnt_id, pidfd, child_pidfd3;
|
|
struct file_handle *fh;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd3, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd3, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
child_pidfd3 = self->child_pidfd3;
|
|
self->child_pidfd3 = -EBADF;
|
|
EXPECT_EQ(sys_pidfd_send_signal(child_pidfd3, SIGKILL, NULL, 0), 0);
|
|
EXPECT_EQ(close(child_pidfd3), 0);
|
|
EXPECT_EQ(sys_waitid(P_PID, self->child_pid3, NULL, WEXITED), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_LT(pidfd, 0);
|
|
}
|
|
|
|
/*
|
|
* Test valid flags to open a pidfd file handle. Note, that
|
|
* PIDFD_NONBLOCK is defined as O_NONBLOCK and O_NONBLOCK is an alias to
|
|
* O_NDELAY. Also note that PIDFD_THREAD is an alias for O_EXCL.
|
|
*/
|
|
TEST_F(file_handle, open_by_handle_at_valid_flags)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
int pidfd = -EBADF;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd2, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd2, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh,
|
|
O_RDONLY |
|
|
O_WRONLY |
|
|
O_RDWR |
|
|
O_NONBLOCK |
|
|
O_NDELAY |
|
|
O_CLOEXEC |
|
|
O_EXCL);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
}
|
|
|
|
/*
|
|
* Test that invalid flags passed to open a pidfd file handle are
|
|
* rejected.
|
|
*/
|
|
TEST_F(file_handle, open_by_handle_at_invalid_flags)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
int pidfd = -EBADF;
|
|
static const struct invalid_pidfs_file_handle_flags {
|
|
int oflag;
|
|
const char *oflag_name;
|
|
} invalid_pidfs_file_handle_flags[] = {
|
|
{ FASYNC, "FASYNC" },
|
|
{ O_CREAT, "O_CREAT" },
|
|
{ O_NOCTTY, "O_NOCTTY" },
|
|
{ O_CREAT, "O_CREAT" },
|
|
{ O_TRUNC, "O_TRUNC" },
|
|
{ O_APPEND, "O_APPEND" },
|
|
{ O_SYNC, "O_SYNC" },
|
|
{ O_DSYNC, "O_DSYNC" },
|
|
{ O_DIRECT, "O_DIRECT" },
|
|
{ O_DIRECTORY, "O_DIRECTORY" },
|
|
{ O_NOFOLLOW, "O_NOFOLLOW" },
|
|
{ O_NOATIME, "O_NOATIME" },
|
|
{ O_PATH, "O_PATH" },
|
|
{ O_TMPFILE, "O_TMPFILE" },
|
|
/*
|
|
* O_LARGEFILE is added implicitly by
|
|
* open_by_handle_at() so pidfs simply masks it off.
|
|
*/
|
|
};
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd2, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(invalid_pidfs_file_handle_flags); i++) {
|
|
pidfd = open_by_handle_at(self->pidfd, fh, invalid_pidfs_file_handle_flags[i].oflag);
|
|
ASSERT_LT(pidfd, 0) {
|
|
TH_LOG("open_by_handle_at() succeeded with invalid flags: %s", invalid_pidfs_file_handle_flags[i].oflag_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Test that lookup fails. */
|
|
TEST_F(file_handle, lookup_must_fail)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_NE(name_to_handle_at(self->child_pidfd2, "lookup-is-not-possible-with-pidfs", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
ASSERT_EQ(errno, ENOTDIR);
|
|
ASSERT_NE(name_to_handle_at(self->child_pidfd2, "lookup-is-not-possible-with-pidfs", fh, &mnt_id, 0), 0);
|
|
ASSERT_EQ(errno, ENOTDIR);
|
|
}
|
|
|
|
#ifndef AT_HANDLE_CONNECTABLE
|
|
#define AT_HANDLE_CONNECTABLE 0x002
|
|
#endif
|
|
|
|
/*
|
|
* Test that AT_HANDLE_CONNECTABLE is rejected. Connectable file handles
|
|
* don't make sense for pidfs. Note that currently AT_HANDLE_CONNECTABLE
|
|
* is rejected because it is incompatible with AT_EMPTY_PATH which is
|
|
* required with pidfds as we don't support lookup.
|
|
*/
|
|
TEST_F(file_handle, invalid_name_to_handle_at_flags)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_NE(name_to_handle_at(self->child_pidfd2, "", fh, &mnt_id, AT_EMPTY_PATH | AT_HANDLE_CONNECTABLE), 0);
|
|
}
|
|
|
|
#ifndef AT_HANDLE_FID
|
|
#define AT_HANDLE_FID 0x200
|
|
#endif
|
|
|
|
/*
|
|
* Test that a request with AT_HANDLE_FID always leads to decodable file
|
|
* handle as pidfs always provides export operations.
|
|
*/
|
|
TEST_F(file_handle, valid_name_to_handle_at_flags)
|
|
{
|
|
int mnt_id, pidfd;
|
|
struct file_handle *fh;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd2, "", fh, &mnt_id, AT_EMPTY_PATH | AT_HANDLE_FID), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd2, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
}
|
|
|
|
/*
|
|
* That we decode a file handle without having to pass a pidfd.
|
|
*/
|
|
TEST_F(file_handle, decode_purely_based_on_file_handle)
|
|
{
|
|
int mnt_id;
|
|
struct file_handle *fh;
|
|
int pidfd = -EBADF;
|
|
struct stat st1, st2;
|
|
|
|
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
ASSERT_NE(fh, NULL);
|
|
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
|
|
fh->handle_bytes = MAX_HANDLE_SZ;
|
|
|
|
ASSERT_EQ(name_to_handle_at(self->child_pidfd1, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
|
|
|
|
ASSERT_EQ(fstat(self->child_pidfd1, &st1), 0);
|
|
|
|
pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, O_CLOEXEC);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, O_NONBLOCK);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(self->pidfd, fh, 0);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
ASSERT_EQ(fstat(pidfd, &st2), 0);
|
|
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
|
|
|
|
ASSERT_EQ(close(pidfd), 0);
|
|
|
|
pidfd = open_by_handle_at(-EBADF, fh, 0);
|
|
ASSERT_LT(pidfd, 0);
|
|
|
|
pidfd = open_by_handle_at(AT_FDCWD, fh, 0);
|
|
ASSERT_LT(pidfd, 0);
|
|
|
|
free(fh);
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|