/* * aksl_realpath -- TODO.md section 1.5. * * The happy paths compare against realpath(3) itself rather than against a * hard-coded string, because $TMPDIR may itself be a symlink (/tmp -> /private/tmp * and friends) and the resolved answer is what the platform says it is. * * Every failure case here passes a *zeroed* resolved_path buffer. That is * deliberate: on failure the wrapper formats resolved_path with %s while * realpath(3) leaves the buffer unspecified (TODO.md 2.1.6), so a test that * passed an uninitialised buffer would be reading uninitialised memory in the * library's own error path. The uninitialised-buffer crash is the defect's own * test to write, not something these should trip over incidentally. * * Also not covered: resolved_path == NULL, which is unchecked today and leaks * the buffer realpath(3) allocates (2.1.6). */ #include "aksl_capture.h" #include #include static int test_resolves_an_existing_file(void) { char path[AKSL_TMP_MAX]; char resolved[PATH_MAX]; char expected[PATH_MAX]; AKSL_CHECK(aksl_temp_file(path, sizeof(path)) == 0); memset(resolved, 0x00, sizeof(resolved)); AKSL_CHECK(realpath(path, expected) != NULL); AKSL_CHECK_OK(aksl_realpath(path, resolved)); AKSL_CHECK(strcmp(resolved, expected) == 0); AKSL_CHECK(resolved[0] == '/'); AKSL_CHECK(unlink(path) == 0); return 0; } /* A symlink resolves to its target, not to itself. */ static int test_resolves_a_symlink_to_its_target(void) { char target[AKSL_TMP_MAX]; char link[AKSL_TMP_MAX]; char resolved[PATH_MAX]; char expected[PATH_MAX]; AKSL_CHECK(aksl_temp_file(target, sizeof(target)) == 0); AKSL_CHECK(aksl_temp_file(link, sizeof(link)) == 0); /* mkstemp created the link path as a regular file; symlink needs it gone. */ AKSL_CHECK(unlink(link) == 0); AKSL_CHECK(symlink(target, link) == 0); memset(resolved, 0x00, sizeof(resolved)); AKSL_CHECK(realpath(target, expected) != NULL); AKSL_CHECK_OK(aksl_realpath(link, resolved)); AKSL_CHECK(strcmp(resolved, expected) == 0); AKSL_CHECK(unlink(link) == 0); AKSL_CHECK(unlink(target) == 0); return 0; } static int test_missing_path_reports_enoent(void) { char resolved[PATH_MAX]; memset(resolved, 0x00, sizeof(resolved)); AKSL_CHECK_STATUS_MSG_CONTAINS( aksl_realpath("/nonexistent/aksl/path", resolved), ENOENT, "/nonexistent/aksl/path"); return 0; } /* A regular file used as a directory component is ENOTDIR, not ENOENT. */ static int test_non_directory_component_reports_enotdir(void) { char path[AKSL_TMP_MAX]; char child[AKSL_TMP_MAX + 8]; char resolved[PATH_MAX]; AKSL_CHECK(aksl_temp_file(path, sizeof(path)) == 0); AKSL_CHECK((size_t)snprintf(child, sizeof(child), "%s/child", path) < sizeof(child)); memset(resolved, 0x00, sizeof(resolved)); AKSL_CHECK_STATUS(aksl_realpath(child, resolved), ENOTDIR); AKSL_CHECK(unlink(path) == 0); return 0; } static int test_rejects_null_path(void) { char resolved[PATH_MAX]; memset(resolved, 0x00, sizeof(resolved)); AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_realpath(NULL, resolved), AKERR_NULLPOINTER, "path="); return 0; } int main(void) { int failures = 0; akerr_init(); AKSL_RUN(failures, test_resolves_an_existing_file); AKSL_RUN(failures, test_resolves_a_symlink_to_its_target); AKSL_RUN(failures, test_missing_path_reports_enoent); AKSL_RUN(failures, test_non_directory_component_reports_enotdir); AKSL_RUN(failures, test_rejects_null_path); AKSL_REPORT(failures); }