coreutils / tests /dd /tests_for_ifd_reopen.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
/* The test file is included into dd.c after the function definitions,
so internal static functions and variables (like ifd_reopen and
info_signal_count) are visible here. */
static void remove_file_if_exists(const char *path)
{
if (path && *path)
unlink(path);
}
static void rmdir_if_exists(const char *path)
{
if (path && *path)
rmdir(path);
}
static int write_all(int fd, const void *buf, size_t len)
{
const char *p = (const char *)buf;
while (len > 0)
{
ssize_t n = write(fd, p, len);
if (n < 0)
{
if (errno == EINTR)
continue;
return -1;
}
if (n == 0)
return -1;
p += n;
len -= (size_t)n;
}
return 0;
}
static int read_all(int fd, void *buf, size_t len)
{
char *p = (char *)buf;
size_t got = 0;
while (got < len)
{
ssize_t n = read(fd, p + got, len - got);
if (n < 0)
{
if (errno == EINTR)
continue;
return -1;
}
if (n == 0)
break;
got += (size_t)n;
}
return (int)got;
}
/* Declarations of internal globals we will touch, which are defined above in dd.c. */
extern int status_level; /* from dd.c; default STATUS_DEFAULT */
/* These are file-scope static in dd.c, but since this test file is included
into the same translation unit, we can reference them directly. */
extern sig_atomic_t volatile info_signal_count; /* from dd.c */
extern sig_atomic_t volatile interrupt_signal; /* from dd.c */
void setUp(void) {
/* Ensure a quiet environment for tests that may trigger internal printing. */
status_level = 1; /* STATUS_NONE == 1 in dd.c */
info_signal_count = 0;
interrupt_signal = 0;
}
void tearDown(void) {
/* Restore baseline state between tests. */
status_level = 3; /* STATUS_DEFAULT == 3 in dd.c */
info_signal_count = 0;
interrupt_signal = 0;
}
/* Helper to create a temporary file path; returns malloc'd path string. */
static char *make_temp_file(char *out_template)
{
int fd = mkstemp(out_template);
if (fd < 0)
return NULL;
close(fd);
return out_template;
}
/* Helper to create a temporary directory path; returns malloc'd path string. */
static char *make_temp_dir(char *out_template)
{
char *p = mkdtemp(out_template);
return p; /* returns NULL on failure */
}
/* Test 1: Successful reopen of a duplicate FD to a regular file and verify content written via that FD. */
void test_ifd_reopen_success_reopens_fd(void)
{
char tmpl[] = "/tmp/dd_ifd_reopen_file_XXXXXX";
char *path = make_temp_file(tmpl);
TEST_ASSERT_MESSAGE(path != NULL, "mkstemp failed to create temp file");
int dupfd = dup(STDERR_FILENO);
TEST_ASSERT_MESSAGE(dupfd >= 0, "dup failed");
errno = 0;
int ret = ifd_reopen(dupfd, path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
TEST_ASSERT_EQUAL_MESSAGE(0, ret, "ifd_reopen should succeed");
const char payload[] = "hello world";
TEST_ASSERT_EQUAL_INT_MESSAGE(0, write_all(dupfd, payload, sizeof(payload)), "write_all failed");
close(dupfd);
int rfd = open(path, O_RDONLY);
TEST_ASSERT_MESSAGE(rfd >= 0, "open for readback failed");
char buf[sizeof(payload)] = {0};
int n = read_all(rfd, buf, sizeof(buf));
close(rfd);
TEST_ASSERT_EQUAL_INT((int)sizeof(payload), n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, buf, sizeof(payload));
remove_file_if_exists(path);
}
/* Test 2: Failure case - attempt to reopen FD to a directory using O_WRONLY should fail. */
void test_ifd_reopen_failure_returns_error(void)
{
char dtmpl[] = "/tmp/dd_ifd_reopen_dir_XXXXXX";
char *dirpath = make_temp_dir(dtmpl);
TEST_ASSERT_MESSAGE(dirpath != NULL, "mkdtemp failed to create temp dir");
int dupfd = dup(STDERR_FILENO);
TEST_ASSERT_MESSAGE(dupfd >= 0, "dup failed");
errno = 0;
int ret = ifd_reopen(dupfd, dirpath, O_WRONLY, 0);
TEST_ASSERT_TRUE_MESSAGE(ret < 0, "ifd_reopen should fail for directory with O_WRONLY");
/* We won't be strict about errno value, different platforms may set EISDIR or EACCES. */
close(dupfd);
rmdir_if_exists(dirpath);
}
/* Test 3: Verify that pending info signals are processed during ifd_reopen (process_signals is called). */
void test_ifd_reopen_processes_pending_infosignal(void)
{
/* Set a pending info signal. */
info_signal_count = 1;
status_level = 1; /* STATUS_NONE to avoid output during stats printing. */
char tmpl[] = "/tmp/dd_ifd_reopen_file_XXXXXX";
char *path = make_temp_file(tmpl);
TEST_ASSERT_MESSAGE(path != NULL, "mkstemp failed to create temp file");
int dupfd = dup(STDERR_FILENO);
TEST_ASSERT_MESSAGE(dupfd >= 0, "dup failed");
errno = 0;
int ret = ifd_reopen(dupfd, path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
TEST_ASSERT_EQUAL_MESSAGE(0, ret, "ifd_reopen should succeed");
/* The pending info signal count should have been consumed by process_signals(). */
TEST_ASSERT_EQUAL_INT_MESSAGE(0, (int)info_signal_count, "info_signal_count should be 0 after ifd_reopen");
close(dupfd);
remove_file_if_exists(path);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_ifd_reopen_success_reopens_fd);
RUN_TEST(test_ifd_reopen_failure_returns_error);
RUN_TEST(test_ifd_reopen_processes_pending_infosignal);
return UNITY_END();
}