117 lines
3.6 KiB
C
117 lines
3.6 KiB
C
|
|
/*
|
||
|
|
* 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 <errno.h>
|
||
|
|
#include <limits.h>
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|