diff options
Diffstat (limited to 'tests/clar/fs.h')
-rw-r--r-- | tests/clar/fs.h | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/tests/clar/fs.h b/tests/clar/fs.h new file mode 100644 index 000000000..b7a1ff9d2 --- /dev/null +++ b/tests/clar/fs.h @@ -0,0 +1,325 @@ +#ifdef _WIN32 + +#define RM_RETRY_COUNT 5 +#define RM_RETRY_DELAY 10 + +#ifdef __MINGW32__ + +/* These security-enhanced functions are not available + * in MinGW, so just use the vanilla ones */ +#define wcscpy_s(a, b, c) wcscpy((a), (c)) +#define wcscat_s(a, b, c) wcscat((a), (c)) + +#endif /* __MINGW32__ */ + +static int +fs__dotordotdot(WCHAR *_tocheck) +{ + return _tocheck[0] == '.' && + (_tocheck[1] == '\0' || + (_tocheck[1] == '.' && _tocheck[2] == '\0')); +} + +static int +fs_rmdir_rmdir(WCHAR *_wpath) +{ + unsigned retries = 1; + + while (!RemoveDirectoryW(_wpath)) { + /* Only retry when we have retries remaining, and the + * error was ERROR_DIR_NOT_EMPTY. */ + if (retries++ > RM_RETRY_COUNT || + ERROR_DIR_NOT_EMPTY != GetLastError()) + return -1; + + /* Give whatever has a handle to a child item some time + * to release it before trying again */ + Sleep(RM_RETRY_DELAY * retries * retries); + } + + return 0; +} + +static void +fs_rmdir_helper(WCHAR *_wsource) +{ + WCHAR buffer[MAX_PATH]; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + size_t buffer_prefix_len; + + /* Set up the buffer and capture the length */ + wcscpy_s(buffer, MAX_PATH, _wsource); + wcscat_s(buffer, MAX_PATH, L"\\"); + buffer_prefix_len = wcslen(buffer); + + /* FindFirstFile needs a wildcard to match multiple items */ + wcscat_s(buffer, MAX_PATH, L"*"); + find_handle = FindFirstFileW(buffer, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + + do { + /* FindFirstFile/FindNextFile gives back . and .. + * entries at the beginning */ + if (fs__dotordotdot(find_data.cFileName)) + continue; + + wcscpy_s(buffer + buffer_prefix_len, MAX_PATH - buffer_prefix_len, find_data.cFileName); + + if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) + fs_rmdir_helper(buffer); + else { + /* If set, the +R bit must be cleared before deleting */ + if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes) + cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)); + + cl_assert(DeleteFileW(buffer)); + } + } + while (FindNextFileW(find_handle, &find_data)); + + /* Ensure that we successfully completed the enumeration */ + cl_assert(ERROR_NO_MORE_FILES == GetLastError()); + + /* Close the find handle */ + FindClose(find_handle); + + /* Now that the directory is empty, remove it */ + cl_assert(0 == fs_rmdir_rmdir(_wsource)); +} + +static int +fs_rm_wait(WCHAR *_wpath) +{ + unsigned retries = 1; + DWORD last_error; + + do { + if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath)) + last_error = GetLastError(); + else + last_error = ERROR_SUCCESS; + + /* Is the item gone? */ + if (ERROR_FILE_NOT_FOUND == last_error || + ERROR_PATH_NOT_FOUND == last_error) + return 0; + + Sleep(RM_RETRY_DELAY * retries * retries); + } + while (retries++ <= RM_RETRY_COUNT); + + return -1; +} + +static void +fs_rm(const char *_source) +{ + WCHAR wsource[MAX_PATH]; + DWORD attrs; + + /* The input path is UTF-8. Convert it to wide characters + * for use with the Windows API */ + cl_assert(MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + _source, + -1, /* Indicates NULL termination */ + wsource, + MAX_PATH)); + + /* Does the item exist? If not, we have no work to do */ + attrs = GetFileAttributesW(wsource); + + if (INVALID_FILE_ATTRIBUTES == attrs) + return; + + if (FILE_ATTRIBUTE_DIRECTORY & attrs) + fs_rmdir_helper(wsource); + else { + /* The item is a file. Strip the +R bit */ + if (FILE_ATTRIBUTE_READONLY & attrs) + cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY)); + + cl_assert(DeleteFileW(wsource)); + } + + /* Wait for the DeleteFile or RemoveDirectory call to complete */ + cl_assert(0 == fs_rm_wait(wsource)); +} + +static void +fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest) +{ + WCHAR buf_source[MAX_PATH], buf_dest[MAX_PATH]; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + size_t buf_source_prefix_len, buf_dest_prefix_len; + + wcscpy_s(buf_source, MAX_PATH, _wsource); + wcscat_s(buf_source, MAX_PATH, L"\\"); + buf_source_prefix_len = wcslen(buf_source); + + wcscpy_s(buf_dest, MAX_PATH, _wdest); + wcscat_s(buf_dest, MAX_PATH, L"\\"); + buf_dest_prefix_len = wcslen(buf_dest); + + /* Get an enumerator for the items in the source. */ + wcscat_s(buf_source, MAX_PATH, L"*"); + find_handle = FindFirstFileW(buf_source, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + + /* Create the target directory. */ + cl_assert(CreateDirectoryW(_wdest, NULL)); + + do { + /* FindFirstFile/FindNextFile gives back . and .. + * entries at the beginning */ + if (fs__dotordotdot(find_data.cFileName)) + continue; + + wcscpy_s(buf_source + buf_source_prefix_len, MAX_PATH - buf_source_prefix_len, find_data.cFileName); + wcscpy_s(buf_dest + buf_dest_prefix_len, MAX_PATH - buf_dest_prefix_len, find_data.cFileName); + + if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) + fs_copydir_helper(buf_source, buf_dest); + else + cl_assert(CopyFileW(buf_source, buf_dest, TRUE)); + } + while (FindNextFileW(find_handle, &find_data)); + + /* Ensure that we successfully completed the enumeration */ + cl_assert(ERROR_NO_MORE_FILES == GetLastError()); + + /* Close the find handle */ + FindClose(find_handle); +} + +static void +fs_copy(const char *_source, const char *_dest) +{ + WCHAR wsource[MAX_PATH], wdest[MAX_PATH]; + DWORD source_attrs, dest_attrs; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + + /* The input paths are UTF-8. Convert them to wide characters + * for use with the Windows API. */ + cl_assert(MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + _source, + -1, + wsource, + MAX_PATH)); + + cl_assert(MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + _dest, + -1, + wdest, + MAX_PATH)); + + /* Check the source for existence */ + source_attrs = GetFileAttributesW(wsource); + cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs); + + /* Check the target for existence */ + dest_attrs = GetFileAttributesW(wdest); + + if (INVALID_FILE_ATTRIBUTES != dest_attrs) { + /* Target exists; append last path part of source to target. + * Use FindFirstFile to parse the path */ + find_handle = FindFirstFileW(wsource, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + wcscat_s(wdest, MAX_PATH, L"\\"); + wcscat_s(wdest, MAX_PATH, find_data.cFileName); + FindClose(find_handle); + + /* Check the new target for existence */ + cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest)); + } + + if (FILE_ATTRIBUTE_DIRECTORY & source_attrs) + fs_copydir_helper(wsource, wdest); + else + cl_assert(CopyFileW(wsource, wdest, TRUE)); +} + +void +cl_fs_cleanup(void) +{ + fs_rm(fixture_path(_clar_path, "*")); +} + +#else +static int +shell_out(char * const argv[]) +{ + int status; + pid_t pid; + + pid = fork(); + + if (pid < 0) { + fprintf(stderr, + "System error: `fork()` call failed.\n"); + exit(-1); + } + + if (pid == 0) { + execv(argv[0], argv); + } + + waitpid(pid, &status, 0); + return WEXITSTATUS(status); +} + +static void +fs_copy(const char *_source, const char *dest) +{ + char *argv[5]; + char *source; + size_t source_len; + + source = strdup(_source); + source_len = strlen(source); + + if (source[source_len - 1] == '/') + source[source_len - 1] = 0; + + argv[0] = "/bin/cp"; + argv[1] = "-R"; + argv[2] = source; + argv[3] = (char *)dest; + argv[4] = NULL; + + cl_must_pass_( + shell_out(argv), + "Failed to copy test fixtures to sandbox" + ); + + free(source); +} + +static void +fs_rm(const char *source) +{ + char *argv[4]; + + argv[0] = "/bin/rm"; + argv[1] = "-Rf"; + argv[2] = (char *)source; + argv[3] = NULL; + + cl_must_pass_( + shell_out(argv), + "Failed to cleanup the sandbox" + ); +} + +void +cl_fs_cleanup(void) +{ + clar_unsandbox(); + clar_sandbox(); +} +#endif |