/* * Author: Manoj Ampalam * * Author: Bryan Berns * Added tests for symlink(), readlink(), lstat() */ #include "includes.h" #include #include #include #include #include #include #include "../test_helper/test_helper.h" #include "tests.h" #define SMALL_RECV_BUF_SIZE 128 #pragma warning(disable:4267) fd_set read_set, write_set, except_set; struct timeval time_val; char *send_buf, *recv_buf; int retValue, r_pipe, w_pipe; char *tmp_filename = "tmp.txt"; int unset_nonblock(int fd); int set_nonblock(int fd); void prep_input_buffer(char* buf, int size, int seed); void file_blocking_io_tests() { char* small_send_buf = "sample payload"; char small_recv_buf[SMALL_RECV_BUF_SIZE]; int pipeio[2]; { TEST_START("Basic pipe()"); retValue = pipe(pipeio); ASSERT_INT_EQ(retValue, 0); TEST_DONE(); } { TEST_START("pipe read and write"); r_pipe = pipeio[0]; w_pipe = pipeio[1]; retValue = write(r_pipe, small_send_buf, strlen(small_send_buf)); ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, EACCES); retValue = read(w_pipe, small_recv_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, EACCES); retValue = write(w_pipe, small_send_buf, strlen(small_send_buf)); ASSERT_INT_EQ(retValue, strlen(small_send_buf)); retValue = read(r_pipe, small_recv_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, strlen(small_send_buf)); small_recv_buf[retValue] = '\0'; ASSERT_STRING_EQ(small_send_buf, small_recv_buf); memset(small_recv_buf, 0, sizeof(small_recv_buf)); TEST_DONE(); } { TEST_START("close pipe fds"); retValue = close(w_pipe); ASSERT_INT_EQ(retValue, 0); retValue = read(r_pipe, small_recv_buf, SMALL_RECV_BUF_SIZE); /* send on other side is closed*/ ASSERT_INT_EQ(retValue, 0); retValue = close(r_pipe); ASSERT_INT_EQ(retValue, 0); TEST_DONE(); } } void file_simple_fileio() { TEST_START("file io and fstat"); char* small_write_buf = "sample payload"; char small_read_buf[SMALL_RECV_BUF_SIZE]; int f; struct stat st; { //f = open(tmp_filename, O_WRONLY | O_CREAT | O_TRUNC); //ASSERT_INT_EQ(f, -1); } { f = open(tmp_filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); ASSERT_INT_NE(f, -1); close(f); } { f = open(tmp_filename, O_RDONLY); ASSERT_INT_NE(f, -1); retValue = fstat(f, &st); ASSERT_INT_EQ(retValue, 0); ASSERT_INT_EQ(st.st_size, 0); ASSERT_INT_EQ(st.st_mode & 0777, 0666); retValue = read(f, small_read_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, 0); close(f); } { f = open(tmp_filename, O_WRONLY | O_TRUNC); ASSERT_INT_NE(f, -1); retValue = write(f, small_write_buf, strlen(small_write_buf)); ASSERT_INT_EQ(retValue, strlen(small_write_buf)); close(f); } { f = open(tmp_filename, O_RDONLY); ASSERT_INT_NE(f, -1); retValue = stat(tmp_filename, &st); ASSERT_INT_EQ(retValue, 0); ASSERT_INT_EQ(st.st_size, strlen(small_write_buf)); ASSERT_INT_EQ(st.st_mode & 0777, 0600); char mode[12]; strmode(st.st_mode, mode); ASSERT_CHAR_EQ(mode[0], '-'); struct timeval tv[2]; tv[0].tv_sec = st.st_atime + 1000; tv[1].tv_sec = st.st_mtime + 1000; tv[0].tv_usec = tv[1].tv_usec = 0; retValue = utimes(tmp_filename, tv); ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, ERROR_SHARING_VIOLATION); retValue = read(f, small_read_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, strlen(small_write_buf)); small_read_buf[retValue] = '\0'; ASSERT_STRING_EQ(small_write_buf, small_read_buf); retValue = read(f, small_read_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, 0); close(f); retValue = utimes(tmp_filename, tv); ASSERT_INT_EQ(retValue, 0); } { /* test fopen, fgets, fclose*/ FILE *fp = fopen(tmp_filename, "r"); ASSERT_PTR_NE(fp, NULL); char line[1024]; char *retp = fgets(line, sizeof(line), fp); ASSERT_PTR_NE(retp, NULL); retValue = strncmp(line, small_read_buf, strlen(line)); ASSERT_INT_EQ(retValue, 0); fclose(fp); } { /* test writev, ftruncate, isatty, lseek, fdopen */ f = open(tmp_filename, O_RDWR | O_TRUNC); ASSERT_INT_NE(f, -1); struct iovec iov; iov.iov_base = small_write_buf; iov.iov_len = strlen(small_write_buf); retValue = writev(f, &iov, 1); ASSERT_INT_EQ(retValue, strlen(small_write_buf)); int truncate_len = 10; int ret1 = ftruncate(f, truncate_len); ASSERT_INT_EQ(ret1, 0); explicit_bzero(small_read_buf, SMALL_RECV_BUF_SIZE); retValue = read(f, small_read_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, truncate_len); retValue = isatty(f); ASSERT_INT_EQ(retValue, 0); ASSERT_INT_EQ(errno, EINVAL); int offset = 3; retValue = lseek(f, offset, SEEK_SET); ASSERT_INT_EQ(retValue, 0); char *tmp = dup_str(small_read_buf); retValue = read(f, small_read_buf, SMALL_RECV_BUF_SIZE); small_read_buf[retValue] = '\0'; retValue = strcmp(tmp+offset, small_read_buf); ASSERT_INT_EQ(retValue, 0); FILE *f2 = fdopen(f, "r"); ASSERT_PTR_NE(f2, NULL); fclose(f2); retValue = unlink(tmp_filename); ASSERT_INT_EQ(retValue, 0); } { // test null device FILE *fp = fopen("/dev/null", "r"); ASSERT_PTR_NE(fp, NULL); f = open("/dev/null", O_RDONLY); ASSERT_INT_NE(f, -1); } TEST_DONE(); } void file_simple_fileio_mode() { TEST_START("file io and mode"); char * small_write_buf = "sample payload", *c, small_read_buf[SMALL_RECV_BUF_SIZE]; int ret; FILE* f; struct stat st; f = fopen(NULL, "w"); ASSERT_PTR_EQ(f, NULL); c = fgets(NULL, 0, f); ASSERT_PTR_EQ(c, NULL); f = fopen("tmp.txt", "w"); ASSERT_PTR_NE(f, NULL); fclose(f); f = fopen("tmp.txt", "r"); ASSERT_PTR_NE(f, NULL); c = fgets(small_read_buf, sizeof(small_read_buf), f); ASSERT_PTR_EQ(c, NULL); fclose(f); ret = stat("tmp.txt", &st); ASSERT_INT_EQ(ret, 0); ASSERT_INT_EQ(st.st_size, 0); f = fopen("tmp.txt", "w"); ASSERT_PTR_NE(f, NULL); ret = fputs(small_write_buf, f); ASSERT_INT_EQ(ret, 0); fclose(f); ret = stat("tmp.txt", &st); ASSERT_INT_EQ(ret, 0); ASSERT_INT_EQ(st.st_size, strlen(small_write_buf)); f = fopen("tmp.txt", "r"); ASSERT_PTR_NE(f, NULL); c = fgets(small_read_buf, sizeof(small_read_buf), f); ASSERT_PTR_NE(c, NULL); ASSERT_STRING_EQ(small_write_buf, small_read_buf); c = fgets(small_read_buf, sizeof(small_read_buf), f); ASSERT_PTR_EQ(c, NULL); fclose(f); TEST_DONE(); } void file_nonblocking_io_tests() { TEST_START("non blocking file io"); char* small_send_buf = "sample payload"; char small_recv_buf[SMALL_RECV_BUF_SIZE]; int pipeio[2]; retValue = pipe(pipeio); ASSERT_INT_EQ(retValue, 0); r_pipe = pipeio[0]; w_pipe = pipeio[1]; retValue = set_nonblock(r_pipe); ASSERT_INT_EQ(retValue, 0); retValue = read(r_pipe, small_recv_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, EAGAIN); retValue = unset_nonblock(w_pipe); ASSERT_INT_EQ(retValue, 0); retValue = write(w_pipe, small_send_buf, strlen(small_send_buf)); ASSERT_INT_EQ(retValue, strlen(small_send_buf)); retValue = unset_nonblock(r_pipe); ASSERT_INT_EQ(retValue, 0); retValue = read(r_pipe, small_recv_buf, SMALL_RECV_BUF_SIZE); ASSERT_INT_EQ(retValue, strlen(small_send_buf)); small_recv_buf[retValue] = '\0'; ASSERT_STRING_EQ(small_send_buf, small_recv_buf); memset(small_recv_buf, 0, sizeof(small_recv_buf)); send_buf = malloc(10 * 1024); ASSERT_PTR_NE(send_buf, NULL); retValue = set_nonblock(w_pipe); ASSERT_INT_EQ(retValue, 0); retValue = 1; while (retValue > 0) { retValue = write(w_pipe, send_buf, 10 * 1024); } ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, EAGAIN); retValue = close(r_pipe); ASSERT_INT_EQ(retValue, 0); retValue = close(w_pipe); ASSERT_INT_EQ(retValue, 0); free(send_buf); TEST_DONE(); } void file_select_tests() { TEST_START("select on file fds"); int num_bytes = 1024 * 700; //700KB int bytes_sent = 0; int bytes_received = 0; int seed = 326; int eagain_results = 0; int pipeio[2]; retValue = pipe(pipeio); ASSERT_INT_EQ(retValue, 0); r_pipe = pipeio[0]; w_pipe = pipeio[1]; retValue = set_nonblock(w_pipe); ASSERT_INT_EQ(retValue, 0); retValue = set_nonblock(r_pipe); ASSERT_INT_EQ(retValue, 0); send_buf = malloc(num_bytes); recv_buf = malloc(num_bytes + 1); ASSERT_PTR_NE(send_buf, NULL); ASSERT_PTR_NE(recv_buf, NULL); prep_input_buffer(send_buf, num_bytes, 17); FD_ZERO(&read_set); FD_ZERO(&write_set); FD_SET(w_pipe, &write_set); FD_SET(r_pipe, &read_set); while (-1 != select(max(r_pipe, w_pipe) + 1, &read_set, &write_set, NULL, &time_val)) { if (FD_ISSET(w_pipe, &write_set)) { while ((bytes_sent < num_bytes) && ((retValue = write(w_pipe, send_buf + bytes_sent, num_bytes - bytes_sent)) > 0)) bytes_sent += retValue; if (bytes_sent < num_bytes) { ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, EAGAIN); eagain_results++; } } if (FD_ISSET(r_pipe, &read_set)) { while ((retValue = read(r_pipe, recv_buf + bytes_received, num_bytes - bytes_received + 1)) > 0) bytes_received += retValue; if (retValue == 0) break; ASSERT_INT_EQ(retValue, -1); ASSERT_INT_EQ(errno, EAGAIN); eagain_results++; } if (bytes_sent < num_bytes) FD_SET(w_pipe, &write_set); else { FD_CLR(w_pipe, &write_set); retValue = close(w_pipe); ASSERT_INT_EQ(retValue, 0); } FD_SET(r_pipe, &read_set); } /*ensure that we hit send and recv paths that returned EAGAIN. Else it would not have touched the async paths*/ /*if this assert is being hit, then num_bytes is too small. up it*/ ASSERT_INT_GT(eagain_results, 0); ASSERT_INT_EQ(bytes_sent, bytes_received); ASSERT_INT_EQ(memcmp(send_buf, recv_buf, num_bytes), 0); retValue = close(r_pipe); ASSERT_INT_EQ(retValue, 0); free(send_buf); free(recv_buf); TEST_DONE(); } void file_miscellaneous_tests() { TEST_START("file miscellaneous"); char cwd[PATH_MAX]; char *pcwd = getcwd(cwd, PATH_MAX); ASSERT_PTR_NE(pcwd, NULL); char thishost[NI_MAXHOST]; retValue = gethostname(thishost, sizeof(thishost)); ASSERT_INT_NE(retValue, -1); char *tmp = dup_str(thishost); int len = strlen(tmp); int f = dup(STDOUT_FILENO); ASSERT_INT_NE(f, -1); retValue = write(f, tmp, len); ASSERT_INT_EQ(errno, 0); ASSERT_INT_EQ(retValue, len); close(f); f = dup(STDIN_FILENO); ASSERT_INT_NE(f, -1); close(f); f = dup(STDERR_FILENO); ASSERT_INT_NE(f, -1); close(f); f = open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0600); ASSERT_INT_NE(f, -1); int f1 = dup(f); ASSERT_INT_NE(f1, -1); HANDLE h = w32_fd_to_handle(f); ASSERT_HANDLE(h); close(f); close(f1); char *tmp_filename_1 = "tmp_1.txt"; retValue = rename(tmp_filename, tmp_filename_1); ASSERT_INT_EQ(retValue, 0); retValue = unlink(tmp_filename_1); ASSERT_INT_EQ(retValue, 0); if(tmp) free(tmp); h = w32_fd_to_handle(STDIN_FILENO); ASSERT_HANDLE(h); h = w32_fd_to_handle(STDOUT_FILENO); ASSERT_HANDLE(h); h = w32_fd_to_handle(STDERR_FILENO); ASSERT_HANDLE(h); f = open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); ASSERT_INT_NE(f, -1); wchar_t *t = utf8_to_utf16(tmp_filename); ASSERT_PTR_NE(t, NULL); int perm = get_others_file_permissions(t, 0); ASSERT_INT_EQ(perm, 7); free(t); close(f); retValue = unlink(tmp_filename); ASSERT_INT_EQ(retValue, 0); f = open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); ASSERT_INT_NE(f, -1); t = utf8_to_utf16(tmp_filename); ASSERT_PTR_NE(t, NULL); perm = get_others_file_permissions(t, 1); ASSERT_INT_EQ(perm, 5); free(t); close(f); retValue = unlink(tmp_filename); ASSERT_INT_EQ(retValue, 0); TEST_DONE(); } void file_symlink_tests() { /* skip these unit tests if we cannot create symbolic links at all * note: 0x2 = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE */ DeleteFileW(L"admin_check"); if (CreateSymbolicLinkW(L"admin_check", L"admin_check", 0) == 0 && CreateSymbolicLinkW(L"admin_check", L"admin_check", 0x2 == 0)) { return; } DeleteFileW(L"admin_check"); wchar_t curdir[PATH_MAX]; GetCurrentDirectoryW(PATH_MAX, curdir); /* perform a variety of symlink tests using unicode, directory targets, * file targets, absolute/relative links, absolute/relative targets */ for (int do_unicode = 0; do_unicode <= 1; do_unicode++) for (int do_dir = 0; do_dir <= 1; do_dir++) for (int do_absolute_lnk = 0; do_absolute_lnk <= 1; do_absolute_lnk++) for (int do_absolute_tgt = 0; do_absolute_tgt <= 1; do_absolute_tgt++) { char test_name[128]; sprintf(test_name, "Symlink: %s link, %s %s target, %s", (do_absolute_lnk) ? "relative" : "absolute", (do_absolute_tgt) ? "relative" : "absolute", (do_dir) ? "directory" : "file", (do_unicode) ? "unicode" : "ansi"); TEST_START(test_name); /* cleanup / setup basic test structure */ _wsystem(L"RD /S /Q win32compat-tmp >NUL 2>&1"); _wsystem(L"MKDIR win32compat-tmp >NUL 2>&1"); wchar_t tgt_path[PATH_MAX] = L""; wchar_t lnk_path[PATH_MAX] = L""; /* prepend absolute path if doing absolute test */ if (do_absolute_tgt) { wcscat(tgt_path, L"/"); wcscat(tgt_path, curdir); wcscat(tgt_path, L"/"); } if (do_absolute_lnk) { wcscat(lnk_path, L"/"); wcscat(lnk_path, curdir); wcscat(lnk_path, L"/"); } /* append the test paths */ wcscat(tgt_path, L"win32compat-tmp/tgt"); wcscat(lnk_path, L"win32compat-tmp/lnk"); /* append unicode char if doing unicode test */ if (do_unicode) { wcscat(tgt_path, L"Δ"); wcscat(lnk_path, L"Δ"); } /* ensure target is in forward slash format since this is * required for the readlink test output later * */ for (wchar_t * t = tgt_path; *t; t++) if (*t == '\\') *t = L'/'; /* create directory or file as target --- we have to offset * the first forward slash so the windows functions operate */ if (do_dir) CreateDirectoryW(&tgt_path[do_absolute_tgt], NULL); else CloseHandle(CreateFileW(&tgt_path[do_absolute_tgt], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)); /* convert to utf8 for test */ char * tgt_utf8 = utf16_to_utf8(tgt_path); char * lnk_utf8 = utf16_to_utf8(lnk_path); /* for relative link, the target is relative to the link */ char * tgt_name_utf8 = tgt_utf8; if (!do_absolute_tgt) tgt_name_utf8 = strrchr(tgt_utf8, '/') + 1; /* create symlink */ int symlink_ret = symlink(tgt_name_utf8, lnk_utf8); ASSERT_INT_EQ(symlink_ret, 0); /* verify readlink() output against symlink() input */ char readlink_buf[PATH_MAX] = ""; /* readlink returns the absolute path */ int readlink_ret = readlink(lnk_utf8, readlink_buf, PATH_MAX); char tgt_name_uft8_realpath[PATH_MAX] = { 0 }; realpath(tgt_utf8, tgt_name_uft8_realpath); ASSERT_INT_EQ(readlink_ret, strlen(tgt_name_uft8_realpath)); ASSERT_INT_EQ(0, memcmp(readlink_buf, tgt_name_uft8_realpath, readlink_ret)); /* verify lstat() gets the reference to the link */ struct w32_stat statbuf; int lstat_ret = lstat(lnk_utf8, &statbuf); ASSERT_INT_EQ(lstat_ret, 0); ASSERT_INT_EQ(1, S_ISLNK(statbuf.st_mode)); /* verify stat() gets a reference to the dir or file */ int stat_ret = stat(lnk_utf8, &statbuf); ASSERT_INT_EQ(stat_ret, 0); ASSERT_INT_EQ(0, S_ISLNK(statbuf.st_mode)); ASSERT_INT_EQ(do_dir, S_ISDIR(statbuf.st_mode)); TEST_DONE(); } _wsystem(L"RD /S /Q win32compat-tmp >NUL 2>&1"); } void file_link_tests() { /* skip these unit tests if we cannot create hard links at all */ CloseHandle(CreateFileW(L"admin_check_tgt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)); int perm_test = CreateHardLinkW(L"admin_check", L"admin_check_tgt", 0); DeleteFileW(L"admin_check"); DeleteFileW(L"admin_check_tgt"); if (perm_test == 0) return; wchar_t curdir[PATH_MAX]; GetCurrentDirectoryW(PATH_MAX, curdir); /* perform a variety of link tests using unicode, absolute/relative links, * absolute/relative targets */ for (int do_unicode = 0; do_unicode <= 1; do_unicode++) for (int do_absolute_lnk = 0; do_absolute_lnk <= 1; do_absolute_lnk++) for (int do_absolute_tgt = 0; do_absolute_tgt <= 1; do_absolute_tgt++) { char test_name[128]; sprintf(test_name, "link: %s link, %s file target, %s", (do_absolute_lnk) ? "relative" : "absolute", (do_absolute_tgt) ? "relative" : "absolute", (do_unicode) ? "unicode" : "ansi"); TEST_START(test_name); /* cleanup / setup basic test structure */ _wsystem(L"RD /S /Q win32compat-tmp >NUL 2>&1"); _wsystem(L"MKDIR win32compat-tmp >NUL 2>&1"); wchar_t tgt_path[PATH_MAX] = L""; wchar_t lnk_path[PATH_MAX] = L""; /* prepend absolute path if doing absolute test */ if (do_absolute_tgt) { wcscat(tgt_path, L"/"); wcscat(tgt_path, curdir); wcscat(tgt_path, L"/"); } if (do_absolute_lnk) { wcscat(lnk_path, L"/"); wcscat(lnk_path, curdir); wcscat(lnk_path, L"/"); } /* append the test paths */ wcscat(tgt_path, L"win32compat-tmp/tgt"); wcscat(lnk_path, L"win32compat-tmp/lnk"); /* append unicode char if doing unicode test */ if (do_unicode) { wcscat(tgt_path, L"Δ"); wcscat(lnk_path, L"Δ"); } /* create file as target */ CloseHandle(CreateFileW(&tgt_path[do_absolute_tgt], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)); /* convert to utf8 for test */ char * tgt_utf8 = utf16_to_utf8(tgt_path); char * lnk_utf8 = utf16_to_utf8(lnk_path); /* create link */ int link_ret = link(tgt_utf8, lnk_utf8); ASSERT_INT_EQ(link_ret, 0); /* verify stat() gets a reference to the dir or file */ struct w32_stat statbuf; int stat_ret = stat(lnk_utf8, &statbuf); ASSERT_INT_EQ(stat_ret, 0); ASSERT_INT_EQ(1, S_ISREG(statbuf.st_mode)); TEST_DONE(); } _wsystem(L"RD /S /Q win32compat-tmp >NUL 2>&1"); } void file_tests() { file_simple_fileio(); file_simple_fileio_mode(); file_blocking_io_tests(); file_nonblocking_io_tests(); file_select_tests(); file_miscellaneous_tests(); file_symlink_tests(); file_link_tests(); }