|
|
#include "../../unity/unity.h" |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <stdint.h> |
|
|
#include <string.h> |
|
|
#include <fcntl.h> |
|
|
#include <unistd.h> |
|
|
#include <errno.h> |
|
|
#include <sys/stat.h> |
|
|
#include <sys/types.h> |
|
|
|
|
|
|
|
|
static int create_temp_file_with_data(const void *data, size_t len, char *path_buf, size_t path_buf_sz) |
|
|
{ |
|
|
|
|
|
const char *tmpl = "./test_copyfd_src_XXXXXX"; |
|
|
if (path_buf_sz < strlen(tmpl) + 1) |
|
|
return -1; |
|
|
strcpy(path_buf, tmpl); |
|
|
int fd = mkstemp(path_buf); |
|
|
if (fd < 0) |
|
|
return -1; |
|
|
|
|
|
if (len > 0) { |
|
|
ssize_t wr = write(fd, data, len); |
|
|
if (wr < 0 || (size_t)wr != len) { |
|
|
int saved = errno; |
|
|
close(fd); |
|
|
unlink(path_buf); |
|
|
errno = saved; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
if (lseek(fd, 0, SEEK_SET) < 0) { |
|
|
int saved = errno; |
|
|
close(fd); |
|
|
unlink(path_buf); |
|
|
errno = saved; |
|
|
return -1; |
|
|
} |
|
|
return fd; |
|
|
} |
|
|
|
|
|
|
|
|
static char *run_and_check_copy_fd(int src_fd, |
|
|
uintmax_t n_bytes, |
|
|
enum Copy_fd_status expected_status, |
|
|
const void *expected_output, |
|
|
size_t expected_output_len) |
|
|
{ |
|
|
|
|
|
fflush(stdout); |
|
|
|
|
|
|
|
|
int saved_stdout = dup(STDOUT_FILENO); |
|
|
if (saved_stdout < 0) { |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "dup(STDOUT) failed: errno=%d", errno); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
|
|
|
char out_tmpl[] = "./test_copyfd_out_XXXXXX"; |
|
|
int out_fd = mkstemp(out_tmpl); |
|
|
if (out_fd < 0) { |
|
|
close(saved_stdout); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "mkstemp for stdout capture failed: errno=%d", errno); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
|
|
|
if (dup2(out_fd, STDOUT_FILENO) < 0) { |
|
|
int saved = errno; |
|
|
close(out_fd); |
|
|
close(saved_stdout); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "dup2 to redirect stdout failed: errno=%d", saved); |
|
|
return msg; |
|
|
} |
|
|
close(out_fd); |
|
|
|
|
|
|
|
|
enum Copy_fd_status st = copy_fd(src_fd, n_bytes); |
|
|
|
|
|
|
|
|
fflush(stdout); |
|
|
|
|
|
|
|
|
if (dup2(saved_stdout, STDOUT_FILENO) < 0) { |
|
|
int saved = errno; |
|
|
close(saved_stdout); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "dup2 to restore stdout failed: errno=%d", saved); |
|
|
return msg; |
|
|
} |
|
|
close(saved_stdout); |
|
|
|
|
|
|
|
|
int cap_fd = open(out_tmpl, O_RDONLY); |
|
|
if (cap_fd < 0) { |
|
|
int saved = errno; |
|
|
unlink(out_tmpl); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "open captured file failed: errno=%d", saved); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
struct stat stbuf; |
|
|
if (fstat(cap_fd, &stbuf) < 0) { |
|
|
int saved = errno; |
|
|
close(cap_fd); |
|
|
unlink(out_tmpl); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "fstat captured file failed: errno=%d", saved); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
size_t cap_sz = (size_t)stbuf.st_size; |
|
|
char *cap = NULL; |
|
|
if (cap_sz > 0) { |
|
|
cap = (char*)malloc(cap_sz); |
|
|
if (!cap) { |
|
|
close(cap_fd); |
|
|
unlink(out_tmpl); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "malloc(%zu) failed for capture buffer", cap_sz); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
size_t off = 0; |
|
|
while (off < cap_sz) { |
|
|
ssize_t r = read(cap_fd, cap + off, cap_sz - off); |
|
|
if (r < 0) { |
|
|
int saved = errno; |
|
|
free(cap); |
|
|
close(cap_fd); |
|
|
unlink(out_tmpl); |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "read captured file failed: errno=%d", saved); |
|
|
return msg; |
|
|
} |
|
|
if (r == 0) break; |
|
|
off += (size_t)r; |
|
|
} |
|
|
|
|
|
} |
|
|
close(cap_fd); |
|
|
unlink(out_tmpl); |
|
|
|
|
|
|
|
|
if (st != expected_status) { |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "Status mismatch: got %d, expected %d", (int)st, (int)expected_status); |
|
|
free(cap); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
|
|
|
if (cap_sz != expected_output_len) { |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "Output length mismatch: got %zu, expected %zu", cap_sz, expected_output_len); |
|
|
free(cap); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
|
|
|
if (cap_sz > 0 && memcmp(cap, expected_output, cap_sz) != 0) { |
|
|
|
|
|
size_t idx = 0; |
|
|
while (idx < cap_sz && ((unsigned char*)cap)[idx] == ((const unsigned char*)expected_output)[idx]) |
|
|
idx++; |
|
|
char *msg = (char*)malloc(256); |
|
|
snprintf(msg, 256, "Output content mismatch at byte %zu", idx); |
|
|
free(cap); |
|
|
return msg; |
|
|
} |
|
|
|
|
|
free(cap); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
static void fill_pattern(char *buf, size_t len) |
|
|
{ |
|
|
for (size_t i = 0; i < len; i++) |
|
|
buf[i] = (char)('A' + (i % 26)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_copy_fd_zero_bytes_no_output(void) |
|
|
{ |
|
|
const char data[] = "ignored"; |
|
|
char path[64]; |
|
|
int fd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path)); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); |
|
|
|
|
|
|
|
|
char *err = run_and_check_copy_fd(fd, 0, COPY_FD_OK, "", 0); |
|
|
close(fd); |
|
|
unlink(path); |
|
|
|
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); |
|
|
free(err); |
|
|
} |
|
|
|
|
|
void test_copy_fd_partial_copy(void) |
|
|
{ |
|
|
const char data[] = "abcdef"; |
|
|
const size_t req = 3; |
|
|
char path[64]; |
|
|
int fd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path)); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); |
|
|
|
|
|
char *err = run_and_check_copy_fd(fd, req, COPY_FD_OK, data, req); |
|
|
close(fd); |
|
|
unlink(path); |
|
|
|
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); |
|
|
free(err); |
|
|
} |
|
|
|
|
|
void test_copy_fd_exact_copy(void) |
|
|
{ |
|
|
const char data[] = "Hello, world!"; |
|
|
const size_t len = sizeof(data) - 1; |
|
|
char path[64]; |
|
|
int fd = create_temp_file_with_data(data, len, path, sizeof(path)); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); |
|
|
|
|
|
char *err = run_and_check_copy_fd(fd, len, COPY_FD_OK, data, len); |
|
|
close(fd); |
|
|
unlink(path); |
|
|
|
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); |
|
|
free(err); |
|
|
} |
|
|
|
|
|
void test_copy_fd_oversized_request_unexpected_eof(void) |
|
|
{ |
|
|
const char data[] = "abcd"; |
|
|
const size_t len = sizeof(data) - 1; |
|
|
const uintmax_t req = 10; |
|
|
char path[64]; |
|
|
int fd = create_temp_file_with_data(data, len, path, sizeof(path)); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); |
|
|
|
|
|
|
|
|
char *err = run_and_check_copy_fd(fd, req, COPY_FD_UNEXPECTED_EOF, data, len); |
|
|
close(fd); |
|
|
unlink(path); |
|
|
|
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); |
|
|
free(err); |
|
|
} |
|
|
|
|
|
void test_copy_fd_read_error_returns_status(void) |
|
|
{ |
|
|
|
|
|
const char data[] = "some data"; |
|
|
char path[64]; |
|
|
int tmpfd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path)); |
|
|
TEST_ASSERT_MESSAGE(tmpfd >= 0, "Failed to create temp source file"); |
|
|
close(tmpfd); |
|
|
|
|
|
int wfd = open(path, O_WRONLY); |
|
|
TEST_ASSERT_MESSAGE(wfd >= 0, "Failed to open file write-only to induce read error"); |
|
|
|
|
|
|
|
|
char *err = run_and_check_copy_fd(wfd, 5, COPY_FD_READ_ERROR, "", 0); |
|
|
close(wfd); |
|
|
unlink(path); |
|
|
|
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); |
|
|
free(err); |
|
|
} |
|
|
|
|
|
void test_copy_fd_large_transfer_multiple_buffers(void) |
|
|
{ |
|
|
size_t len = (size_t)BUFSIZ * 3 + 123; |
|
|
char *data = (char*)malloc(len); |
|
|
TEST_ASSERT_NOT_NULL(data); |
|
|
fill_pattern(data, len); |
|
|
|
|
|
char path[64]; |
|
|
int fd = create_temp_file_with_data(data, len, path, sizeof(path)); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); |
|
|
|
|
|
char *err = run_and_check_copy_fd(fd, len, COPY_FD_OK, data, len); |
|
|
close(fd); |
|
|
unlink(path); |
|
|
free(data); |
|
|
|
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); |
|
|
free(err); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_copy_fd_zero_bytes_no_output); |
|
|
RUN_TEST(test_copy_fd_partial_copy); |
|
|
RUN_TEST(test_copy_fd_exact_copy); |
|
|
RUN_TEST(test_copy_fd_oversized_request_unexpected_eof); |
|
|
RUN_TEST(test_copy_fd_read_error_returns_status); |
|
|
RUN_TEST(test_copy_fd_large_transfer_multiple_buffers); |
|
|
return UNITY_END(); |
|
|
} |