summaryrefslogtreecommitdiff
path: root/tests/clar/fs.h
diff options
context:
space:
mode:
Diffstat (limited to 'tests/clar/fs.h')
-rw-r--r--tests/clar/fs.h325
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