|
|
#include "../../unity/unity.h" |
|
|
|
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <unistd.h> |
|
|
#include <fcntl.h> |
|
|
#include <sys/stat.h> |
|
|
#include <sys/types.h> |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern int dd_copy(void); |
|
|
|
|
|
|
|
|
|
|
|
static long get_page_size_for_tests(void) |
|
|
{ |
|
|
#ifdef _SC_PAGESIZE |
|
|
long ps = sysconf(_SC_PAGESIZE); |
|
|
if (ps > 0) return ps; |
|
|
#endif |
|
|
#ifdef _SC_PAGE_SIZE |
|
|
long ps = sysconf(_SC_PAGE_SIZE); |
|
|
if (ps > 0) return ps; |
|
|
#endif |
|
|
return 4096; |
|
|
} |
|
|
|
|
|
|
|
|
extern char const *input_file; |
|
|
extern char const *output_file; |
|
|
extern idx_t page_size; |
|
|
extern idx_t input_blocksize; |
|
|
extern idx_t output_blocksize; |
|
|
extern idx_t conversion_blocksize; |
|
|
extern intmax_t skip_records; |
|
|
extern idx_t skip_bytes; |
|
|
extern intmax_t seek_records; |
|
|
extern intmax_t seek_bytes; |
|
|
extern bool final_op_was_seek; |
|
|
extern intmax_t max_records; |
|
|
extern idx_t max_bytes; |
|
|
extern int conversions_mask; |
|
|
extern int input_flags; |
|
|
extern int output_flags; |
|
|
extern int status_level; |
|
|
extern bool translation_needed; |
|
|
extern intmax_t w_partial; |
|
|
extern intmax_t w_full; |
|
|
extern intmax_t r_partial; |
|
|
extern intmax_t r_full; |
|
|
extern intmax_t w_bytes; |
|
|
extern intmax_t reported_w_bytes; |
|
|
extern bool input_seekable; |
|
|
extern int input_seek_errno; |
|
|
extern off_t input_offset; |
|
|
extern bool warn_partial_read; |
|
|
extern intmax_t r_truncate; |
|
|
extern char newline_character; |
|
|
extern char space_character; |
|
|
extern char *ibuf; |
|
|
extern char *obuf; |
|
|
extern idx_t oc; |
|
|
extern idx_t col; |
|
|
extern bool i_nocache, o_nocache; |
|
|
extern bool i_nocache_eof, o_nocache_eof; |
|
|
extern ssize_t (*iread_fnc) (int fd, char *buf, idx_t size); |
|
|
|
|
|
|
|
|
extern ssize_t iread (int fd, char *buf, idx_t size); |
|
|
|
|
|
|
|
|
#ifndef C_TWOBUFS |
|
|
#define C_TWOBUFS 04000 |
|
|
#endif |
|
|
#ifndef C_SWAB |
|
|
#define C_SWAB 0200 |
|
|
#endif |
|
|
#ifndef C_BLOCK |
|
|
#define C_BLOCK 010 |
|
|
#endif |
|
|
#ifndef C_UNBLOCK |
|
|
#define C_UNBLOCK 020 |
|
|
#endif |
|
|
#ifndef C_SYNC |
|
|
#define C_SYNC 02000 |
|
|
#endif |
|
|
#ifndef STATUS_DEFAULT |
|
|
#define STATUS_DEFAULT 3 |
|
|
#endif |
|
|
|
|
|
|
|
|
static void dd_reset_state(void) |
|
|
{ |
|
|
input_file = NULL; |
|
|
output_file = NULL; |
|
|
page_size = (idx_t)get_page_size_for_tests(); |
|
|
|
|
|
input_blocksize = 0; |
|
|
output_blocksize = 0; |
|
|
conversion_blocksize = 0; |
|
|
|
|
|
skip_records = 0; |
|
|
skip_bytes = 0; |
|
|
seek_records = 0; |
|
|
seek_bytes = 0; |
|
|
|
|
|
final_op_was_seek = false; |
|
|
|
|
|
max_records = INTMAX_MAX; |
|
|
max_bytes = 0; |
|
|
|
|
|
conversions_mask = 0; |
|
|
input_flags = 0; |
|
|
output_flags = 0; |
|
|
|
|
|
status_level = STATUS_DEFAULT; |
|
|
translation_needed = false; |
|
|
|
|
|
w_partial = 0; |
|
|
w_full = 0; |
|
|
r_partial = 0; |
|
|
r_full = 0; |
|
|
w_bytes = 0; |
|
|
reported_w_bytes = -1; |
|
|
|
|
|
input_seekable = false; |
|
|
input_seek_errno = 0; |
|
|
input_offset = 0; |
|
|
|
|
|
warn_partial_read = false; |
|
|
|
|
|
r_truncate = 0; |
|
|
|
|
|
newline_character = '\n'; |
|
|
space_character = ' '; |
|
|
|
|
|
|
|
|
ibuf = NULL; |
|
|
obuf = NULL; |
|
|
|
|
|
oc = 0; |
|
|
col = 0; |
|
|
|
|
|
i_nocache = false; |
|
|
o_nocache = false; |
|
|
i_nocache_eof = false; |
|
|
o_nocache_eof = false; |
|
|
|
|
|
|
|
|
iread_fnc = iread; |
|
|
} |
|
|
|
|
|
|
|
|
static int write_all(int fd, const void* buf, size_t len) |
|
|
{ |
|
|
const char* p = (const char*)buf; |
|
|
while (len) { |
|
|
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 const char* invoke_dd_and_capture(const unsigned char* in, size_t in_len, |
|
|
unsigned char** out_buf, size_t* out_len) |
|
|
{ |
|
|
int saved_stdin = -1, saved_stdout = -1; |
|
|
int in_fd = -1, out_fd = -1; |
|
|
char in_template[] = "/tmp/dd_test_in_XXXXXX"; |
|
|
char out_template[] = "/tmp/dd_test_out_XXXXXX"; |
|
|
|
|
|
*out_buf = NULL; |
|
|
*out_len = 0; |
|
|
|
|
|
in_fd = mkstemp(in_template); |
|
|
if (in_fd < 0) return "mkstemp input failed"; |
|
|
if (fchmod(in_fd, S_IRUSR | S_IWUSR) != 0) { } |
|
|
if (in_len) { |
|
|
if (write_all(in_fd, in, in_len) != 0) { |
|
|
close(in_fd); |
|
|
unlink(in_template); |
|
|
return "writing input failed"; |
|
|
} |
|
|
} |
|
|
if (lseek(in_fd, 0, SEEK_SET) < 0) { |
|
|
close(in_fd); |
|
|
unlink(in_template); |
|
|
return "lseek input failed"; |
|
|
} |
|
|
|
|
|
out_fd = mkstemp(out_template); |
|
|
if (out_fd < 0) { |
|
|
close(in_fd); |
|
|
unlink(in_template); |
|
|
return "mkstemp output failed"; |
|
|
} |
|
|
if (fchmod(out_fd, S_IRUSR | S_IWUSR) != 0) { } |
|
|
|
|
|
saved_stdin = dup(STDIN_FILENO); |
|
|
saved_stdout = dup(STDOUT_FILENO); |
|
|
if (saved_stdin < 0 || saved_stdout < 0) { |
|
|
if (saved_stdin >= 0) close(saved_stdin); |
|
|
if (saved_stdout >= 0) close(saved_stdout); |
|
|
close(in_fd); |
|
|
close(out_fd); |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "dup save stdio failed"; |
|
|
} |
|
|
|
|
|
if (dup2(in_fd, STDIN_FILENO) < 0) { |
|
|
close(saved_stdin); |
|
|
close(saved_stdout); |
|
|
close(in_fd); |
|
|
close(out_fd); |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "dup2 stdin failed"; |
|
|
} |
|
|
if (dup2(out_fd, STDOUT_FILENO) < 0) { |
|
|
|
|
|
dup2(saved_stdin, STDIN_FILENO); |
|
|
close(saved_stdin); |
|
|
close(saved_stdout); |
|
|
close(in_fd); |
|
|
close(out_fd); |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "dup2 stdout failed"; |
|
|
} |
|
|
|
|
|
|
|
|
close(in_fd); |
|
|
close(out_fd); |
|
|
|
|
|
|
|
|
int rc = dd_copy(); |
|
|
|
|
|
|
|
|
if (dup2(saved_stdout, STDOUT_FILENO) < 0) { |
|
|
close(saved_stdout); |
|
|
close(saved_stdin); |
|
|
|
|
|
return "restore stdout failed"; |
|
|
} |
|
|
if (dup2(saved_stdin, STDIN_FILENO) < 0) { |
|
|
close(saved_stdout); |
|
|
close(saved_stdin); |
|
|
return "restore stdin failed"; |
|
|
} |
|
|
close(saved_stdout); |
|
|
close(saved_stdin); |
|
|
|
|
|
|
|
|
int rd_fd = open(out_template, O_RDONLY); |
|
|
if (rd_fd < 0) { |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "open output for read failed"; |
|
|
} |
|
|
|
|
|
|
|
|
struct stat st; |
|
|
if (fstat(rd_fd, &st) != 0) { |
|
|
close(rd_fd); |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "fstat output failed"; |
|
|
} |
|
|
|
|
|
size_t sz = (size_t)st.st_size; |
|
|
unsigned char* buf = (unsigned char*)malloc(sz ? sz : 1); |
|
|
if (!buf && sz) { |
|
|
close(rd_fd); |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "malloc output buffer failed"; |
|
|
} |
|
|
|
|
|
size_t off = 0; |
|
|
while (off < sz) { |
|
|
ssize_t n = read(rd_fd, buf + off, sz - off); |
|
|
if (n < 0) { |
|
|
if (errno == EINTR) continue; |
|
|
free(buf); |
|
|
close(rd_fd); |
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
return "read output failed"; |
|
|
} |
|
|
if (n == 0) break; |
|
|
off += (size_t)n; |
|
|
} |
|
|
close(rd_fd); |
|
|
|
|
|
|
|
|
unlink(in_template); |
|
|
unlink(out_template); |
|
|
|
|
|
if (rc != 0) { |
|
|
free(buf); |
|
|
return "dd_copy returned non-zero"; |
|
|
} |
|
|
|
|
|
*out_buf = buf; |
|
|
*out_len = sz; |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void test_dd_copy_simple_no_twobuffers(void) |
|
|
{ |
|
|
dd_reset_state(); |
|
|
input_blocksize = 4; |
|
|
output_blocksize = 4; |
|
|
conversions_mask = 0; |
|
|
|
|
|
const unsigned char in[] = "Hello, world!"; |
|
|
|
|
|
unsigned char* out = NULL; size_t out_len = 0; |
|
|
const char* err = invoke_dd_and_capture(in, sizeof(in)-1, &out, &out_len); |
|
|
if (err) { |
|
|
TEST_FAIL_MESSAGE(err); |
|
|
} |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(sizeof(in)-1, out_len); |
|
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(in, out, out_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(13, w_bytes); |
|
|
TEST_ASSERT_EQUAL_INT64(2, w_full); |
|
|
TEST_ASSERT_EQUAL_INT64(1, w_partial); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(2, r_full); |
|
|
TEST_ASSERT_EQUAL_INT64(1, r_partial); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_dd_copy_twobuffers_writes(void) |
|
|
{ |
|
|
dd_reset_state(); |
|
|
input_blocksize = 4; |
|
|
output_blocksize = 8; |
|
|
conversions_mask = C_TWOBUFS; |
|
|
|
|
|
unsigned char in[20]; |
|
|
for (size_t i = 0; i < sizeof(in); i++) in[i] = (unsigned char)('A' + (int)(i % 26)); |
|
|
|
|
|
unsigned char* out = NULL; size_t out_len = 0; |
|
|
const char* err = invoke_dd_and_capture(in, sizeof(in), &out, &out_len); |
|
|
if (err) { |
|
|
TEST_FAIL_MESSAGE(err); |
|
|
} |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(sizeof(in), out_len); |
|
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(in, out, out_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(20, w_bytes); |
|
|
TEST_ASSERT_EQUAL_INT64(2, w_full); |
|
|
TEST_ASSERT_EQUAL_INT64(1, w_partial); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_dd_copy_swab_odd_length(void) |
|
|
{ |
|
|
dd_reset_state(); |
|
|
input_blocksize = 5; |
|
|
output_blocksize = 8; |
|
|
conversions_mask = C_TWOBUFS | C_SWAB; |
|
|
|
|
|
const unsigned char in[] = { 'a','b','c','d','e' }; |
|
|
const unsigned char expected[] = { 'b','a','d','c','e' }; |
|
|
|
|
|
unsigned char* out = NULL; size_t out_len = 0; |
|
|
const char* err = invoke_dd_and_capture(in, sizeof(in), &out, &out_len); |
|
|
if (err) { |
|
|
TEST_FAIL_MESSAGE(err); |
|
|
} |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(sizeof(expected), out_len); |
|
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, out_len); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_dd_copy_block_truncate(void) |
|
|
{ |
|
|
dd_reset_state(); |
|
|
input_blocksize = 16; |
|
|
output_blocksize = 16; |
|
|
conversion_blocksize = 4; |
|
|
conversions_mask = C_TWOBUFS | C_BLOCK; |
|
|
|
|
|
const unsigned char in[] = "ab\nabcdef\nxy"; |
|
|
const unsigned char expected[] = "ab " "abcd" "xy "; |
|
|
|
|
|
unsigned char* out = NULL; size_t out_len = 0; |
|
|
const char* err = invoke_dd_and_capture(in, sizeof(in)-1, &out, &out_len); |
|
|
if (err) { |
|
|
TEST_FAIL_MESSAGE(err); |
|
|
} |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(sizeof(expected)-1, out_len); |
|
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, out_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(2, r_truncate); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_dd_copy_unblock(void) |
|
|
{ |
|
|
dd_reset_state(); |
|
|
input_blocksize = 12; |
|
|
output_blocksize = 16; |
|
|
conversion_blocksize = 4; |
|
|
conversions_mask = C_TWOBUFS | C_UNBLOCK; |
|
|
|
|
|
const unsigned char in[] = "ab cd ef "; |
|
|
const unsigned char expected[] = "ab\ncd\nef\n"; |
|
|
|
|
|
unsigned char* out = NULL; size_t out_len = 0; |
|
|
const char* err = invoke_dd_and_capture(in, sizeof(in)-1, &out, &out_len); |
|
|
if (err) { |
|
|
TEST_FAIL_MESSAGE(err); |
|
|
} |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(sizeof(expected)-1, out_len); |
|
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, out_len); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_dd_copy_sync_padding(void) |
|
|
{ |
|
|
dd_reset_state(); |
|
|
input_blocksize = 5; |
|
|
output_blocksize = 16; |
|
|
conversions_mask = C_TWOBUFS | C_SYNC; |
|
|
|
|
|
const unsigned char in[] = { 'x','y','z' }; |
|
|
const unsigned char expected[] = { 'x','y','z','\0','\0' }; |
|
|
|
|
|
unsigned char* out = NULL; size_t out_len = 0; |
|
|
const char* err = invoke_dd_and_capture(in, sizeof(in), &out, &out_len); |
|
|
if (err) { |
|
|
TEST_FAIL_MESSAGE(err); |
|
|
} |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(sizeof(expected), out_len); |
|
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, out_len); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_dd_copy_simple_no_twobuffers); |
|
|
RUN_TEST(test_dd_copy_twobuffers_writes); |
|
|
RUN_TEST(test_dd_copy_swab_odd_length); |
|
|
RUN_TEST(test_dd_copy_block_truncate); |
|
|
RUN_TEST(test_dd_copy_unblock); |
|
|
RUN_TEST(test_dd_copy_sync_padding); |
|
|
return UNITY_END(); |
|
|
} |