| #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(); |
| } |