# -*- Autotest -*-
AT_BANNER([dump_dir])
## --------- ##
## dd_sanity ##
## --------- ##
AT_TESTFUN([dd_sanity],
[[
#include "testsuite.h"
void validate_dump_dir_contents(struct dump_dir *dd)
{
int items = 0;
assert(dd_exist(dd, FILENAME_TIME));
++items;
assert(dd_exist(dd, FILENAME_KERNEL));
++items;
assert(dd_exist(dd, FILENAME_HOSTNAME));
++items;
assert(dd_exist(dd, FILENAME_ARCHITECTURE));
++items;
assert(dd_exist(dd, FILENAME_OS_INFO));
++items;
assert(dd_exist(dd, FILENAME_OS_RELEASE));
++items;
assert(dd_exist(dd, FILENAME_OS_RELEASE));
++items;
assert(dd_exist(dd, FILENAME_TYPE));
++items;
assert(dd_exist(dd, FILENAME_LAST_OCCURRENCE));
++items;
assert(dd_exist(dd, "at_test_text"));
assert(dd_get_item_size(dd, "at_test_text") == 3);
++items;
assert(dd_exist(dd, "at_test_binary"));
assert(dd_get_item_size(dd, "at_test_binary") == 4);
++items;
struct stat srv_buf;
stat("/etc/services", &srv_buf);
assert(dd_exist(dd, "at_test_services"));
assert(dd_get_item_size(dd, "at_test_services") == srv_buf.st_size);
++items;
struct stat grp_buf;
stat("/etc/group", &grp_buf);
assert(dd_exist(dd, "at_test_group"));
assert(dd_get_item_size(dd, "at_test_group") == grp_buf.st_size);
++items;
struct stat pwd_buf;
stat("/etc/passwd", &pwd_buf);
assert(dd_exist(dd, "at_test_passwd"));
assert(dd_get_item_size(dd, "at_test_passwd") == pwd_buf.st_size);
++items;
dd_save_text(dd, "at_test_to_delete", "deleted");
assert(dd_exist(dd, "at_test_to_delete"));
dd_delete_item(dd, "at_test_to_delete");
assert(!dd_exist(dd, "at_test_to_delete"));
DIR *d1 = dd_init_next_file(dd);
assert(d1 != NULL);
int items_counter = 0;
char *short_name, *full_name;
while (dd_get_next_file(dd, &short_name, &full_name))
{
++items_counter;
TS_ASSERT_PTR_IS_NOT_NULL(short_name);
TS_ASSERT_PTR_IS_NOT_NULL(full_name);
TS_ASSERT_STRING_EQ(short_name, (strrchr(full_name, '/') + 1), NULL);
TS_ASSERT_STRING_BEGINS_WITH(full_name, dd->dd_dirname, NULL);
TS_ASSERT_CHAR_EQ_MESSAGE(full_name[strlen(dd->dd_dirname)], '/', full_name);
}
TS_ASSERT_SIGNED_EQ(items, items_counter);
TS_ASSERT_SIGNED_EQ(dd_get_items_count(dd), items);
TS_ASSERT_PTR_IS_NULL(dd->next_dir);
TS_ASSERT_SIGNED_EQ(dd_get_next_file(dd, NULL, NULL), 0);
DIR *iterator_second_run = dd_init_next_file(dd);
TS_ASSERT_PTR_IS_NOT_NULL(iterator_second_run);
while (dd_get_next_file(dd, &short_name, &full_name))
--items_counter;
TS_ASSERT_SIGNED_OP_MESSAGE(items_counter, ==, 0, "Second run iterator goes through all items");
DIR *iterator_third_run = dd_init_next_file(dd);
TS_ASSERT_PTR_IS_NOT_NULL(iterator_third_run);
TS_ASSERT_PTR_IS_NOT_NULL(dd->next_dir);
dd_clear_next_file(dd);
TS_ASSERT_PTR_IS_NULL(dd->next_dir);
TS_ASSERT_SIGNED_OP_MESSAGE(dd_get_next_file(dd, NULL, NULL), ==, 0, "dd_clear_next_file(dd) stops iteration");
}
TS_MAIN
{
char template[] = "/tmp/XXXXXX/dump_dir";
char *last_slash = strrchr(template, '/');
*last_slash = '\0';
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
*last_slash = '/';
printf("Dump dir path: %s\n", template);
fprintf(stderr, "Create new dump directory\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
dd_save_text(dd, "at_test_text", "foo");
assert(dd_exist(dd, "at_test_text"));
dd_save_binary(dd, "at_test_binary", "blah", 4);
assert(dd_exist(dd, "at_test_binary"));
dd_copy_file(dd, "at_test_services", "/etc/services");
const int etc_dir_fd = open("/etc", O_DIRECTORY | O_PATH | O_CLOEXEC | O_EXCL);
assert(etc_dir_fd >= 0);
dd_copy_file_at(dd, "at_test_group", etc_dir_fd, "group");
close(etc_dir_fd);
int passwd_fd = open("/etc/passwd", O_RDONLY);
assert(passwd_fd >= 0);
dd_copy_fd(dd, "at_test_passwd", passwd_fd, 0, 0);
close(passwd_fd);
fprintf(stderr, "Test newly created dump directory\n");
validate_dump_dir_contents(dd);
dd_close(dd);
fprintf(stderr, "Test opened dump directory\n");
dd = dd_opendir(template, /*for writing*/0);
assert(dd != NULL || !"Cannot open the dump directory");
validate_dump_dir_contents(dd);
dd_close(dd);
fprintf(stderr, "Test renamed dump directory\n");
dd = dd_opendir(template, /*for writing*/0);
assert(dd != NULL || !"Cannot open the dump directory second time");
*(last_slash+1) = 'X';
assert(dd_rename(dd, template) == 0 || !"Cannot rename the dump directory");
validate_dump_dir_contents(dd);
dd_close(dd);
fprintf(stderr, "Test opened renamed dump directory\n");
assert(dd != NULL || !"Cannot open the renamed dump directory");
dd = dd_opendir(template, /*for writing*/0);
validate_dump_dir_contents(dd);
assert(dd_delete(dd) == 0);
*last_slash = '\0';
assert(rmdir(template) == 0);
}
TS_RETURN_MAIN
]])
## --------------------- ##
## dd_create_open_delete ##
## --------------------- ##
AT_TESTFUN([dd_create_open_delete],
[[
#include "internal_libreport.h"
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX/dump_dir";
char *last_slash = strrchr(template, '/');
*last_slash = '\0';
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
*last_slash = '/';
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(strcmp(dd->dd_dirname, template) == 0);
assert(dd->dd_fd >= 0);
assert(dd->dd_md_fd >= 0);
struct stat dd_st;
assert(fstat(dd->dd_fd, &dd_st) == 0);
struct stat md_st;
assert(fstat(dd->dd_md_fd, &md_st) == 0);
assert(dd_st.st_uid == md_st.st_uid);
assert(dd_st.st_gid == md_st.st_gid);
assert((dd_st.st_mode & 0666) == (md_st.st_mode & 0666));
struct stat path_md_st;
assert(fstatat(dd->dd_fd, ".libreport", &path_md_st, 0) == 0);
assert(md_st.st_ino = path_md_st.st_ino);
struct stat owner_md_st;
assert(fstatat(dd->dd_md_fd, "owner", &owner_md_st, 0) == 0);
assert((dd_st.st_mode & 0666) == (owner_md_st.st_mode & 0666));
assert(geteuid() == dd_get_owner(dd));
dd_create_basic_files(dd, (uid_t)-1, NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
dd_close(dd);
dd = NULL;
dd = dd_opendir(template, 0);
assert(dd != NULL);
assert(strcmp(dd->dd_dirname, template) == 0);
assert(dd->dd_fd >= 0);
assert(dd->dd_md_fd < 0);
dd_delete(dd);
assert(stat(template, &dd_st) != 0);
*last_slash = '\0';
assert(rmdir(template) == 0);
return EXIT_SUCCESS;
}
]])
## -------------------------- ##
## dd_sanitize_mode_and_owner ##
## -------------------------- ##
AT_TESTFUN([dd_sanitize_mode_and_owner],
[[
#include "internal_libreport.h"
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX/dump_dir";
char *last_slash = strrchr(template, '/');
*last_slash = '\0';
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
*last_slash = '/';
/* Prepare a directory for chmod test, use mode 0600 and chmod it to 0640 */
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0600);
assert(dd != NULL);
{
struct stat path_md_st;
assert((fstatat(dd->dd_fd, ".libreport", &path_md_st, 0) == 0) || !"Failed initialize meta-data");
assert((path_md_st.st_mode & 0077) == 0);
}
dd_create_basic_files(dd, (uid_t)-1, NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
dd_close(dd);
/* initialize meta-data */
dd = dd_opendir(template, DD_OPEN_FD_ONLY);
/* reopen for writing */
dd = dd_fdopendir(dd, /*for writing*/0);
assert(dd != NULL);
assert(fchmod(dd->dd_fd, 0750) == 0);
dd->mode = 0640;
fprintf(stderr, "Going to sanitize\n");
dd_sanitize_mode_and_owner(dd);
fprintf(stderr, "Sanitized\n");
{
DIR *d = opendir(template);
struct dirent *dent;
while ((dent = readdir(d)) != NULL)
{
if ( strcmp(".", dent->d_name) == 0
|| strcmp("..", dent->d_name) == 0
|| strcmp(".lock", dent->d_name) == 0
|| strcmp(".libreport", dent->d_name) == 0
)
continue;
struct stat sb;
printf("Testing element: %s\n", dent->d_name);
assert(fstatat(dd->dd_fd, dent->d_name, &sb, 0) == 0 || !"Cannot stat a regular element");
assert((sb.st_mode & 0777) == 0640 || !"Failed to chmod a regular element");
}
closedir(d);
}
{
struct stat path_md_st;
assert(fstatat(dd->dd_fd, ".libreport", &path_md_st, 0) == 0 || !"Cannot stat meta-data directory");
assert((path_md_st.st_mode & 0777) == 0750 || !"Failed chmod meta-data");
}
int md_dir_fd = openat(dd->dd_fd, ".libreport", O_DIRECTORY);
assert(md_dir_fd >= 0 || !"Cannot open meta-data directory");
DIR *d = fdopendir(md_dir_fd);
struct dirent *dent;
while ((dent = readdir(d)) != NULL)
{
if (strcmp(".", dent->d_name) == 0 || strcmp("..", dent->d_name) == 0)
continue;
struct stat sb;
printf("Testing meta-data: %s\n", dent->d_name);
assert(fstatat(md_dir_fd, dent->d_name, &sb, 0) == 0 || !"Cannot stat meta-data file");
assert((sb.st_mode & 0777) == 0640 || !"Failed to chmod a meta-data file");
}
closedir(d);
dd_delete(dd);
*last_slash = '\0';
assert(rmdir(template) == 0);
return EXIT_SUCCESS;
}
]])
## -------- ##
## dd_owner ##
## -------- ##
AT_TESTFUN([dd_owner],
[[
#include "internal_libreport.h"
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX/dump_dir";
char *last_slash = strrchr(template, '/');
*last_slash = '\0';
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
*last_slash = '/';
printf("Dump dir path: %s\n", template);
{
fprintf(stderr, "TEST === default owner\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL);
assert(geteuid() == dd_get_owner(dd));
assert(dd_delete(dd) == 0);
}
{
fprintf(stderr, "TEST === create basic files w/o UID\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL);
dd_create_basic_files(dd, (uid_t)-1, NULL);
assert(geteuid() == dd_get_owner(dd));
assert(dd_delete(dd) == 0);
}
{
fprintf(stderr, "TEST === create basic files with UID\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL);
dd_create_basic_files(dd, (geteuid() + 1), NULL);
assert((geteuid() + 1) == dd_get_owner(dd));
assert(dd_delete(dd) == 0);
}
{
fprintf(stderr, "TEST === set no owner\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL);
dd_set_no_owner(dd);
struct passwd *nobody_pw= getpwnam("nobody");
assert(nobody_pw != NULL);
assert(nobody_pw->pw_uid == dd_get_owner(dd));
assert(dd_delete(dd) == 0);
}
{
fprintf(stderr, "TEST === set artibrary owner\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL);
dd_set_owner(dd, (geteuid() + 2));
assert((geteuid() + 2) == dd_get_owner(dd));
assert(dd_delete(dd) == 0);
}
{
fprintf(stderr, "TEST === chown no owner\n");
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL);
dd_set_no_owner(dd);
assert(geteuid() != dd_get_owner(dd));
dd_chown(dd, geteuid());
assert(geteuid() == dd_get_owner(dd));
assert(dd_delete(dd) == 0);
}
*last_slash = '\0';
assert(rmdir(template) == 0);
return EXIT_SUCCESS;
}
]])
## ------- ##
## dd_stat ##
## ------- ##
AT_TESTFUN([dd_stat],
[[
#include "internal_libreport.h"
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX/dump_dir";
char *last_slash = strrchr(template, '/');
*last_slash = '\0';
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
*last_slash = '/';
printf("Dump dir path: %s\n", template);
dd_g_super_user_uid = geteuid();
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"create new");
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, "type", "custom");
dd_close(dd);
dd = NULL;
dd = dd_opendir(template, DD_OPEN_FD_ONLY);
assert(dd != NULL || !"open fds");
{
const int fst_stat = dd_stat_for_uid(dd, geteuid());
assert(fst_stat & DD_STAT_OWNED_BY_UID);
assert(fst_stat & DD_STAT_ACCESSIBLE_BY_UID);
assert(!(fst_stat & DD_STAT_NO_OWNER));
}
dd = dd_fdopendir(dd, /*for writing*/0);
assert(dd != NULL || !"reopen for writing");
assert(dd_set_owner(dd, geteuid() + 1) == 0);
assert(dd_get_owner(dd) == geteuid() + 1);
{
const int scn_stat = dd_stat_for_uid(dd, geteuid() + 2);
assert(scn_stat == 0);
}
{
const int thr_stat = dd_stat_for_uid(dd, dd_g_super_user_uid);
assert(!(thr_stat & DD_STAT_OWNED_BY_UID));
assert(thr_stat & DD_STAT_ACCESSIBLE_BY_UID);
assert(!(thr_stat & DD_STAT_NO_OWNER));
}
assert(dd_set_no_owner(dd) == 0);
assert(dd_get_owner(dd) != geteuid() + 3);
{
const int frt_stat = dd_stat_for_uid(dd, geteuid() + 3);
assert(!(frt_stat & DD_STAT_OWNED_BY_UID));
assert(frt_stat & DD_STAT_ACCESSIBLE_BY_UID);
assert(frt_stat & DD_STAT_NO_OWNER);
}
{
const int fft_stat = dd_stat_for_uid(dd, dd_g_super_user_uid);
assert(!(fft_stat & DD_STAT_OWNED_BY_UID));
assert(fft_stat & DD_STAT_ACCESSIBLE_BY_UID);
assert(fft_stat & DD_STAT_NO_OWNER);
}
assert(dd_delete(dd) == 0);
*last_slash = '\0';
assert(rmdir(template) == 0);
return EXIT_SUCCESS;
}
]])
## -------------- ##
## recursive_lock ##
## -------------- ##
AT_TESTFUN([recursive_lock],
[[
#include "internal_libreport.h"
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv)
{
g_verbose = 3;
char *path = tmpnam(NULL);
struct dump_dir *dd = dd_create(path, -1L, DEFAULT_DUMP_DIR_MODE);
char *lock_path = concat_path_file(path, ".lock");
struct stat buf;
assert(dd);
assert(lstat(lock_path, &buf) == 0 && S_ISLNK(buf.st_mode));
dd_create_basic_files(dd, -1L, "/");
dd_save_text(dd, "type", "custom");
struct dump_dir *dd2 = dd_opendir(path, DD_OPEN_READONLY);
assert(dd2->owns_lock == 0);
struct dump_dir *dd3 = dd_opendir(path, 0);
assert(dd3->owns_lock == 0);
dd_close(dd2);
assert(lstat(lock_path, &buf) == 0 && S_ISLNK(buf.st_mode));
dd_close(dd3);
assert(lstat(lock_path, &buf) == 0 && S_ISLNK(buf.st_mode));
dd_close(dd);
assert(stat(lock_path, &buf) != 0 && errno == ENOENT);
free(lock_path);
return 0;
}
]])
## ----------------------- ##
## str_is_correct_filename ##
## ----------------------- ##
AT_TESTFUN([str_is_correct_filename],
[[
#include "internal_libreport.h"
#include <assert.h>
#
int main(void)
{
g_verbose = 3;
assert(str_is_correct_filename("") == false);
assert(str_is_correct_filename("/") == false);
assert(str_is_correct_filename("//") == false);
assert(str_is_correct_filename("/a") == false);
assert(str_is_correct_filename("a/") == false);
assert(str_is_correct_filename(".") == false);
assert(str_is_correct_filename("..") == false);
assert(str_is_correct_filename("/.") == false);
assert(str_is_correct_filename("//.") == false);
assert(str_is_correct_filename("./") == false);
assert(str_is_correct_filename(".//") == false);
assert(str_is_correct_filename("/./") == false);
assert(str_is_correct_filename("/..") == false);
assert(str_is_correct_filename("//..") == false);
assert(str_is_correct_filename("../") == false);
assert(str_is_correct_filename("..//") == false);
assert(str_is_correct_filename("/../") == false);
assert(str_is_correct_filename("/.././") == false);
assert(str_is_correct_filename("looks-good-but-evil/") == false);
assert(str_is_correct_filename("looks-good-but-evil/../../") == false);
assert(str_is_correct_filename(".meta-data") == true);
assert(str_is_correct_filename("..meta-meta-data") == true);
assert(str_is_correct_filename("meta-..-data") == true);
assert(str_is_correct_filename("a") == true);
assert(str_is_correct_filename(".a") == true);
assert(str_is_correct_filename("a.") == true);
assert(str_is_correct_filename("ab") == true);
assert(str_is_correct_filename("abc") == true);
assert(str_is_correct_filename("abcd") == true);
assert(str_is_correct_filename("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-") == true);
assert(str_is_correct_filename("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-=") == false);
return 0;
}
]])
## ----------------##
## create_dump_dir ##
## ----------------##
AT_TESTFUN([create_dump_dir],
[[
#include "testsuite.h"
static int kill_canary(struct dump_dir *dd, void *args)
{
int *cb_canary = (int *)args;
*cb_canary = 0;
return 0;
}
static int revive_canary(struct dump_dir *dd, void *args)
{
int *cb_canary = (int *)args;
*cb_canary = 1;
return 1;
}
static int lay_an_egg(struct dump_dir *dd, void *args)
{
const char *bird_kind = (const char *)args;
dd_save_text(dd, "egg", bird_kind);
return 0;
}
static int create_type(struct dump_dir *dd, void *args)
{
const char *bird_kind = (const char *)args;
dd_save_text(dd, FILENAME_TYPE, bird_kind);
return 0;
}
TS_MAIN
{
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Base dump dir path: %s\n", template);
{
int cb_canary = 1;
struct dump_dir *dd = create_dump_dir(template, "../attest", (uid_t)-1, &kill_canary, &cb_canary);
TS_ASSERT_PTR_IS_NULL_MESSAGE(dd, "TYPE cannot be relative path");
TS_ASSERT_SIGNED_EQ(1, cb_canary);
}
{
int cb_canary = 1;
struct dump_dir *dd = create_dump_dir(template, "/tmp/attest", (uid_t)-1, &kill_canary, &cb_canary);
TS_ASSERT_PTR_IS_NULL_MESSAGE(dd, "TYPE cannot be absolute path");
TS_ASSERT_SIGNED_EQ(1, cb_canary);
}
{
int cb_canary = 0;
struct dump_dir *dd = create_dump_dir(template, "attest", (uid_t)-1, &revive_canary, &cb_canary);
TS_ASSERT_PTR_IS_NULL_MESSAGE(dd, "Non-zero callback return value stops dump dir creation");
TS_ASSERT_SIGNED_EQ(1, cb_canary);
}
{
struct dump_dir *dd = create_dump_dir(template, "birds", (uid_t)-1, lay_an_egg, (void *)"canary");
TS_ASSERT_PTR_IS_NOT_NULL_MESSAGE(dd, "Created dump directory with valid TYPE");
TS_ASSERT_STRING_BEGINS_WITH(strrchr(dd->dd_dirname, '/') + 1, "birds", "Type was honored");
TS_ASSERT_STRING_EQ(dd_load_text(dd, FILENAME_TYPE), "birds", "'type' is created from the passed string");
TS_ASSERT_STRING_EQ(dd_load_text(dd, "egg"), "canary", "Call back saves durable data");
TS_ASSERT_SIGNED_EQ(dd_delete(dd), 0);
}
{
struct dump_dir *dd = create_dump_dir(template, "birds", (uid_t)-1, create_type, (void *)"canary");
TS_ASSERT_PTR_IS_NOT_NULL_MESSAGE(dd, "Created dump directory with valid TYPE");
TS_ASSERT_STRING_BEGINS_WITH(strrchr(dd->dd_dirname, '/') + 1, "birds", "Type was honored");
TS_ASSERT_STRING_EQ(dd_load_text(dd, FILENAME_TYPE), "canary", "'type' was not overwritten");
TS_ASSERT_SIGNED_EQ(dd_delete(dd), 0);
}
}
TS_RETURN_MAIN
]])
## --------------------------------- ##
## create_dump_dir_from_problem_data ##
## --------------------------------- ##
AT_TESTFUN([create_dump_dir_from_problem_data],
[[
#include "testsuite.h"
TS_MAIN
{
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Base dump dir path: %s\n", template);
dd_g_fs_group_gid = getegid();
problem_data_t *pd = problem_data_new();
{
fprintf(stderr, "=== No FILENAME_TYPE in Problem Data ===\n");
struct dump_dir *no_type = create_dump_dir_from_problem_data(pd, template);
TS_ASSERT_PTR_IS_NULL_MESSAGE(no_type, "It's not possible to create dump directory without TYPE");
}
{
fprintf(stderr, "=== Prohibited name in FILENAME_TYPE in Problem Data ===\n");
problem_data_add_text_editable(pd, FILENAME_TYPE, "../attest");
struct dump_dir *prohibited_type = create_dump_dir_from_problem_data(pd, template);
TS_ASSERT_PTR_IS_NULL_MESSAGE(prohibited_type, "It's not possible to create dump directory with invalid TYPE");
}
problem_data_add_text_editable(pd, FILENAME_TYPE, "attest");
{
fprintf(stderr, "=== NO UID - geteuid() ===\n");
struct dump_dir *no_uid_dd_geteuid = create_dump_dir_from_problem_data_ext(pd, template, geteuid());
assert(no_uid_dd_geteuid != NULL);
assert(dd_get_owner(no_uid_dd_geteuid) == geteuid());
assert(dd_delete(no_uid_dd_geteuid) == 0);
fprintf(stderr, "=== NO UID - geteuid() ===\n");
}
{
fprintf(stderr, "=== NO UID - NO UID ===\n");
struct dump_dir *no_uid_dd_nouid = create_dump_dir_from_problem_data_ext(pd, template, (uid_t)-1L);
assert(no_uid_dd_nouid != NULL);
assert(dd_get_owner(no_uid_dd_nouid) == geteuid());
assert(dd_delete(no_uid_dd_nouid) == 0);
fprintf(stderr, "=== NO UID - NO UID ===\n");
}
char buf[sizeof(long)*3 + 2];
snprintf(buf, sizeof(buf), "%ld", (long)(geteuid() + 1));
problem_data_add_text_editable(pd, FILENAME_UID, buf);
{
fprintf(stderr, "=== UID - geteuid() ===\n");
struct dump_dir *uid_dd_geteuid = create_dump_dir_from_problem_data_ext(pd, template, geteuid());
assert(uid_dd_geteuid != NULL);
assert(dd_get_owner(uid_dd_geteuid) == (geteuid() + 1));
assert(dd_delete(uid_dd_geteuid) == 0);
fprintf(stderr, "=== UID - geteuid() ===\n");
}
{
fprintf(stderr, "=== UID - NO UID ===\n");
struct dump_dir *uid_dd_nouid = create_dump_dir_from_problem_data_ext(pd, template, (uid_t)-1L);
assert(uid_dd_nouid != NULL);
assert(dd_get_owner(uid_dd_nouid) == (geteuid() + 1));
assert(dd_delete(uid_dd_nouid) == 0);
fprintf(stderr, "=== UID - NO UID ===\n");
}
assert(rmdir(template) == 0);
}
TS_RETURN_MAIN
]])
## ---------- ##
## dd_copy_fd ##
## ---------- ##
AT_TESTFUN([dd_copy_fd],
[[
#include "testsuite.h"
void test(const char buffer[], const size_t buffer_size)
{
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
abort();
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
char tmpfile[] = "/tmp/libreport-attestsuite-dd_copy_fd.XXXXXX";
int tmpfd = mkstemp(tmpfile);
full_write(tmpfd, buffer, buffer_size);
{
assert((-1) != lseek(tmpfd, 0, SEEK_SET));
const off_t read_truncated = dd_copy_fd(dd, "truncated", tmpfd, 0, buffer_size/2);
TS_ASSERT_SIGNED_GE(read_truncated, buffer_size/2);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "truncated"), buffer_size/2);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "truncated"), 0);
}
{
assert((-1) != lseek(tmpfd, 0, SEEK_SET));
const off_t read_exact = dd_copy_fd(dd, "exact", tmpfd, 0, buffer_size);
TS_ASSERT_SIGNED_EQ(read_exact, buffer_size);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "exact"), buffer_size);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "exact"), 0);
}
{
assert((-1) != lseek(tmpfd, 0, SEEK_SET));
const off_t read_bigger = dd_copy_fd(dd, "bigger", tmpfd, 0, buffer_size * 2);
TS_ASSERT_SIGNED_EQ(read_bigger, buffer_size);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "bigger"), buffer_size);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "bigger"), 0);
}
{
assert((-1) != lseek(tmpfd, 0, SEEK_SET));
const off_t read_no_limit = dd_copy_fd(dd, "no_limit", tmpfd, 0, 0);
TS_ASSERT_SIGNED_EQ(read_no_limit, buffer_size);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "no_limit"), buffer_size);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "no_limit"), 0);
}
close(tmpfd);
unlink(tmpfile);
assert(dd_delete(dd) == 0);
}
TS_MAIN
{
{
char buffer[1024*2];
memset(buffer, 'x', sizeof(buffer));
test(buffer, sizeof(buffer));
}
{
char buffer[1024*4];
memset(buffer, 'y', sizeof(buffer));
test(buffer, sizeof(buffer));
}
{
char buffer[1024*6];
memset(buffer, 'z', sizeof(buffer));
test(buffer, sizeof(buffer));
}
{
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
{
int opath_fd = open("/etc/services", O_PATH);
char buf[16] = {0};
if (read(opath_fd, buf, sizeof(buf)/sizeof(buf[0])) == -1)
{
assert(errno == EBADF || !"O_PATH fd can be read");
close(opath_fd);
opath_fd = open("/etc/services", O_PATH);
assert(opath_fd >= 0);
const off_t opath_read = dd_copy_fd(dd, "opath", opath_fd, 0, 0);
TS_ASSERT_SIGNED_EQ(opath_read, -1);
TS_ASSERT_SIGNED_EQ(dd_exist(dd, "opath"), 0);
}
close(opath_fd);
}
{
int wronly_fd = open("/tmp/libreport.testsuite", O_WRONLY | O_CREAT | O_TRUNC, 0600);
assert(wronly_fd >= 0 || !"Cannot create temporary file");
char buf[] = "Hello, world!";
assert(write(wronly_fd, buf, sizeof(buf)/sizeof(buf[0])) == sizeof(buf)/sizeof(buf[0]));
close(wronly_fd);
wronly_fd = open("/tmp/libreport.testsuite", O_WRONLY);
assert(wronly_fd >= 0 || !"Cannot re-open temporary file");
const off_t wronly_read = dd_copy_fd(dd, "wronly", wronly_fd, 0, 0);
TS_ASSERT_SIGNED_EQ(wronly_read, -1);
TS_ASSERT_SIGNED_EQ(dd_exist(dd, "wronly"), 0);
close(wronly_fd);
}
dd_delete(dd);
}
}
TS_RETURN_MAIN
]])
## ------------- ##
## dd_load_int32 ##
## ------------- ##
AT_TESTFUN([dd_load_int32],
[[
#include "internal_libreport.h"
#include <assert.h>
#
int main(void)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
int32_t value;
dd_save_text(dd, "empty", "");
assert(dd_load_int32(dd, "empty", &value) == -EINVAL);
char *buf = NULL;
asprintf(&buf, "%lld", -1LL + INT32_MIN);
assert(buf != NULL);
dd_save_text(dd, "below_min", buf);
free(buf);
assert(dd_load_int32(dd, "below_min", &value) == -ERANGE);
asprintf(&buf, "%"PRId32, INT32_MIN);
dd_save_text(dd, "min", buf);
free(buf);
assert(dd_load_int32(dd, "min", &value) == 0);
assert(value == INT32_MIN || !"min");
asprintf(&buf, "%"PRId32, INT32_MAX);
assert(buf != NULL);
dd_save_text(dd, "max", buf);
free(buf);
assert(dd_load_int32(dd, "max", &value) == 0);
assert(value == INT32_MAX);
asprintf(&buf, "%lld", 1LL + INT32_MAX);
assert(buf != NULL);
dd_save_text(dd, "above_max", buf);
free(buf);
assert(dd_load_int32(dd, "above_max", &value) == -ERANGE);
dd_save_text(dd, "string_suffix", "123abc");
assert(dd_load_int32(dd, "string_suffix", &value) == -EINVAL);
dd_save_text(dd, "string", "abc");
assert(dd_load_int32(dd, "string", &value) == -EINVAL);
assert(dd_delete(dd) == 0);
return 0;
}
]])
## -------------- ##
## dd_load_uint32 ##
## -------------- ##
AT_TESTFUN([dd_load_uint32],
[[
#include "internal_libreport.h"
#include <assert.h>
#
int main(void)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
uint32_t value;
dd_save_text(dd, "empty", "");
assert(dd_load_uint32(dd, "empty", &value) == -EINVAL);
dd_save_text(dd, "below_min", "-1");
assert(dd_load_uint32(dd, "below_min", &value) == -ERANGE);
dd_save_text(dd, "min", "0");
assert(dd_load_uint32(dd, "min", &value) == 0);
assert(value == 0 || !"min");
char *buf = NULL;
asprintf(&buf, "%"PRIu32, UINT32_MAX);
assert(buf != NULL);
dd_save_text(dd, "max", buf);
assert(dd_load_uint32(dd, "max", &value) == 0);
assert(value == UINT32_MAX);
free(buf);
asprintf(&buf, "%lld", 1LL + UINT32_MAX);
assert(buf != NULL);
dd_save_text(dd, "above_max", buf);
assert(dd_load_uint32(dd, "above_max", &value) == -ERANGE);
dd_save_text(dd, "string_suffix", "123abc");
assert(dd_load_uint32(dd, "string_suffix", &value) == -EINVAL);
dd_save_text(dd, "string", "abc");
assert(dd_load_uint32(dd, "string", &value) == -EINVAL);
assert(dd_delete(dd) == 0);
return 0;
}
]])
## ------------- ##
## dd_load_int64 ##
## ------------- ##
AT_TESTFUN([dd_load_int64],
[[
#include "internal_libreport.h"
#include <assert.h>
int main(void)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
int64_t value;
dd_save_text(dd, "empty", "");
assert(dd_load_int64(dd, "empty", &value) == -EINVAL);
dd_save_text(dd, "below_min", "-9223372036854775809");
assert(dd_load_int64(dd, "below_min", &value) == -ERANGE);
char *buf = NULL;
asprintf(&buf, "%"PRId64, INT64_MIN);
assert(buf != NULL);
dd_save_text(dd, "min", buf);
assert(dd_load_int64(dd, "min", &value) == 0);
assert(value == INT64_MIN || !"min");
free(buf);
asprintf(&buf, "%"PRId64, INT64_MAX);
assert(buf != NULL);
dd_save_text(dd, "max", buf);
assert(dd_load_int64(dd, "max", &value) == 0);
assert(value == INT64_MAX);
free(buf);
dd_save_text(dd, "above_max", "9223372036854775808");
assert(dd_load_int64(dd, "above_max", &value) == -ERANGE);
dd_save_text(dd, "string_suffix", "123abc");
assert(dd_load_int64(dd, "string_suffix", &value) == -EINVAL);
dd_save_text(dd, "string", "abc");
assert(dd_load_int64(dd, "string", &value) == -EINVAL);
assert(dd_delete(dd) == 0);
return 0;
}
]])
## --------------- ##
## dd_load_uint64 ##
## --------------- ##
AT_TESTFUN([dd_load_uint64],
[[
#include "internal_libreport.h"
#include <assert.h>
int main(void)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
uint64_t value;
dd_save_text(dd, "empty", "");
assert(dd_load_uint64(dd, "empty", &value) == -EINVAL);
dd_save_text(dd, "below_min", "-1");
assert(dd_load_uint64(dd, "below_min", &value) == -ERANGE);
dd_save_text(dd, "min", "0");
assert(dd_load_uint64(dd, "min", &value) == 0);
assert(value == 0 || !"min");
char *buf = NULL;
asprintf(&buf, "%"PRIu64, UINT64_MAX);
assert(buf != NULL);
dd_save_text(dd, "max", buf);
assert(dd_load_uint64(dd, "max", &value) == 0);
assert(value == UINT64_MAX);
free(buf);
dd_save_text(dd, "above_max", "18446744073709551616");
assert(dd_load_uint64(dd, "above_max", &value) == -ERANGE);
dd_save_text(dd, "string_suffix", "123abc");
assert(dd_load_uint64(dd, "string_suffix", &value) == -EINVAL);
dd_save_text(dd, "string", "abc");
assert(dd_load_uint64(dd, "string", &value) == -EINVAL);
assert(dd_delete(dd) == 0);
return 0;
}
]])
## ----------------- ##
## dd_create_archive ##
## ----------------- ##
AT_TESTFUN([dd_create_archive],
[[
#include "internal_libreport.h"
#include <libtar.h>
#include <assert.h>
void verify_archive(struct dump_dir *dd, const char *file_name,
const_string_vector_const_ptr_t included_files,
const_string_vector_const_ptr_t excluded_files)
{
unsigned c = 0;
for (const_string_vector_const_ptr_t i = included_files; i && *i; ++i)
++c;
int *check_array = xzalloc(c * sizeof(int));
int pipe_from_parent_to_child[2];
xpipe(pipe_from_parent_to_child);
pid_t child = fork();
if (child < 0)
perror_msg_and_die("vfork");
if (child == 0)
{
/* child */
close(pipe_from_parent_to_child[0]);
xmove_fd(xopen(file_name, O_RDONLY), 0);
xmove_fd(pipe_from_parent_to_child[1], 1);
execlp("gzip", "gzip", "-d", NULL);
perror_msg_and_die("Can't execute '%s'", "gzip");
}
close(pipe_from_parent_to_child[1]);
/* If child died (say, in xopen), then parent might get SIGPIPE.
* We want to properly unlock dd, therefore we must not die on SIGPIPE:
*/
signal(SIGPIPE, SIG_IGN);
TAR* tar = NULL;
/* Create tar writer object */
if (tar_fdopen(&tar, pipe_from_parent_to_child[0], file_name,
/*fileops:(standard)*/ NULL, O_RDONLY, 0644, TAR_GNU) != 0)
{
fprintf(stderr, "Failed to open the pipe to gzip for archive: '%s'\n", file_name);
abort();
}
int r = 0;
const char *real_file = "/tmp/libreport-attest-extracted";
while ((r = th_read(tar)) == 0)
{
char *path = th_get_pathname(tar);
if (!TH_ISREG(tar))
{
fprintf(stderr, "Not regular file: '%s', found in archive: '%s'\n", path, file_name);
continue;
}
const_string_vector_const_ptr_t i = included_files;
for (c = 0; i && *i; ++i, ++c)
{
if (strcmp(*i, path) == 0)
break;
}
if (i && *i != NULL)
{
printf("Included file: '%s', found in archive '%s'\n", path, file_name);
check_array[c] += 1;
unlink(real_file);
if(tar_extract_regfile(tar, xstrdup(real_file)) != 0)
{
fprintf(stderr, "TAR failed to extract '%s' to '%s': %s\n", path, real_file, strerror(errno));
abort();
}
char *original = dd_load_text(dd, path);
assert(original != NULL);
assert(original[0] != '\0');
char *extracted = xmalloc_xopen_read_close("/tmp/libreport-attest-extracted", NULL);
assert(extracted != NULL);
if (strcmp(extracted, original) != 0)
{
fprintf(stderr, "Invalid file contents: '%s'\nExp: '%s'\nGot: '%s'\n", path, original, extracted);
abort();
}
free(original);
free(extracted);
continue;
}
i = excluded_files;
for (; i && *i; ++i)
{
if (strcmp(*i, path) == 0)
break;
}
if (i && *i != NULL)
{
fprintf(stderr, "Excluded file: '%s', found in archive '%s'\n", path, file_name);
abort();
}
fprintf(stderr, "Uncategorized file: '%s', found in archive '%s'\n", path, file_name);
}
if (r != 1)
{
fprintf(stderr, "th_read() failed: %s\n", strerror(errno));
abort();
}
tar_close(tar);
int status;
safe_waitpid(child, &status, 0);
if (status != 0)
{
fprintf(stderr, "gzip status code '%d'\n", status);
abort();
}
int err = 0;
const_string_vector_const_ptr_t i = included_files;
for (c = 0; i && *i; ++i, ++c)
{
switch (check_array[c])
{
case 0:
fprintf(stderr, "Not found included file: '%s', in archive: %s\n", *i, file_name);
++err;
break;
case 1:
fprintf(stdout, "Found included file: '%s', in archive: %s\n", *i, file_name);
break;
default:
fprintf(stderr, "%d occurrences of included file: '%s', in archive: %s\n", check_array[c], *i, file_name);
++err;
break;
}
}
if (err)
abort();
return;
}
int main(void)
{
g_verbose = 3;
char template[] = "/tmp/XXXXXX";
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
printf("Dump dir path: %s\n", template);
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
assert(dd != NULL || !"Cannot create new dump directory");
#define COMMON_FILES "time", "last_occurrence", "uid", "kernel", \
"architecture", "hostname", "os_info", "os_release", \
"type", "count", "component", "program_log"
#define SENSITIVE_FILES "environ", "backtrace", "secret_file", "private_file", \
"useless_file"
dd_create_basic_files(dd, geteuid(), NULL);
dd_save_text(dd, FILENAME_TYPE, "attest");
dd_save_text(dd, FILENAME_COUNT, "1");
dd_save_text(dd, FILENAME_COMPONENT, "libreport-attest");
dd_save_text(dd, "program_log", "Something very important!");
const gchar *excluded_files[] = {
SENSITIVE_FILES,
NULL,
};
for (const gchar **iter = excluded_files; *iter; ++iter)
dd_save_text(dd, *iter, *iter);
/* Un-supported archive type */
{
fprintf(stderr, "TEST-CASE: Un-supported type\n");
fprintf(stdout, "TEST-CASE: Un-supported type\n");
const int r = dd_create_archive(dd, "/tmp/libreport-attest.omg", NULL, 0);
printf("dd_create_archive() == %d\n", r);
assert(r == -ENOSYS || !"Not supported");
}
/* File already exists. */
{
fprintf(stderr, "TEST-CASE: File exists\n");
fprintf(stdout, "TEST-CASE: File exists\n");
char file_contents[] = "Non emtpy file";
const char *file_name = "/tmp/libreport-attest.tar.gz";
FILE *test_file = fopen(file_name, "w");
assert(test_file != NULL);
assert(fprintf(test_file, "%s", file_contents) == strlen(file_contents));
fclose(test_file);
assert(dd_create_archive(dd, file_name, NULL, 0) == -EEXIST || !"Exists");
char *canary = xmalloc_xopen_read_close(file_name, NULL);
assert(canary != NULL);
assert(strcmp(canary, file_contents) == 0);
}
/* All elements */
{
fprintf(stderr, "TEST-CASE: Compress all elements\n");
fprintf(stdout, "TEST-CASE: Compress all elements\n");
const gchar *included_files[] = {
COMMON_FILES,
SENSITIVE_FILES,
NULL,
};
const char *file_name = "/tmp/libreport-attest-all.tar.gz";
unlink(file_name);
assert(dd_create_archive(dd, file_name, NULL, 0) == 0 || !"All elements");
verify_archive(dd, file_name, included_files, NULL);
unlink(file_name);
}
/* Excluded elements */
{
fprintf(stderr, "TEST-CASE: Exclude elements\n");
fprintf(stdout, "TEST-CASE: Exclude elements\n");
const char *included_files[] = {
COMMON_FILES,
NULL,
};
const char *file_name = "/tmp/libreport-attest-excluded.tar.gz";
unlink(file_name);
assert(dd_create_archive(dd, file_name, excluded_files, 0) == 0 || !"Excluded elements");
verify_archive(dd, file_name, included_files, excluded_files);
unlink(file_name);
}
assert(dd_delete(dd) == 0);
return 0;
}
]])
## --------------- ##
## dd_compute_size ##
## --------------- ##
AT_TESTFUN([dd_compute_size],
[[
#include "testsuite.h"
TS_MAIN
{
char template[] = "/tmp/XXXXXX/dump_dir";
char *last_slash = strrchr(template, '/');
*last_slash = '\0';
if (mkdtemp(template) == NULL) {
perror("mkdtemp()");
return EXIT_FAILURE;
}
struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
off_t expected = 0;
struct string_pair {
const char *first;
const char *second;
} pairs [] = {
{ .first = "type", .second = "attestsuite" },
{ .first = "analyzer", .second = "dd_compute_size" },
{ .first = "time", .second = "1" },
{ .first = "first", .second = "second" },
{ .first = "foo", .second = "blah" },
{ .first = "moo", .second = "cow" },
{ .first = "rooster", .second = "cock-a-doodle-doo" },
{ .first = "frog", .second = "ribbit" },
{ .first = "pig", .second = "oink" },
};
for (size_t i = 0; i < ARRAY_SIZE(pairs); ++i)
{
dd_save_text(dd, pairs[i].first, pairs[i].second);
expected += strlen(pairs[i].second);
assert(expected >= 0);
}
TS_ASSERT_SIGNED_EQ(dd_compute_size(dd, 0), expected);
}
TS_RETURN_MAIN
]])
## ----------------- ##
## dd_items_handling ##
## ----------------- ##
AT_TESTFUN([dd_items_handling],
[[
#include "testsuite.h"
#include "testsuite_tools.h"
TS_MAIN
{
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
dd_create_basic_files(dd, geteuid(), NULL);
char *base_dir = xstrndup(dd->dd_dirname, strrchr(dd->dd_dirname, '/') - dd->dd_dirname);
assert(base_dir != NULL);
char *test_file_full_path = concat_path_file(base_dir, "libreport-test");
FILE *test_file = fopen(test_file_full_path, "w");
assert(test_file != NULL);
fprintf(test_file, "dd_items_handling");
fclose(test_file);
assert(symlinkat(test_file_full_path, dd->dd_fd, "link-item") == 0);
assert(mkdirat(dd->dd_fd, "dir-item", 0777) == 0);
{
struct stat buf;
TS_ASSERT_SIGNED_EQ(dd_item_stat(dd, test_file_full_path, &buf), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_item_stat(dd, "../libreport-test", &buf), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_item_stat(dd, "foo-blah-does-not-exist", &buf), -ENOENT);
TS_ASSERT_SIGNED_EQ(dd_item_stat(dd, "link-item", &buf), -EMEDIUMTYPE);
TS_ASSERT_SIGNED_EQ(dd_item_stat(dd, "dir-item", &buf), -EMEDIUMTYPE);
}
{
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, test_file_full_path), -1);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "../libreport-test"), -1);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "foo-blah-does-not-exist"), 0);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "link-item"), -1);
TS_ASSERT_SIGNED_EQ(dd_get_item_size(dd, "dir-item"), -1);
}
{
struct stat buf, real_buf;
assert(fstatat(dd->dd_fd, FILENAME_TIME, &real_buf, 0) == 0);
TS_ASSERT_SIGNED_EQ(dd_item_stat(dd, FILENAME_TIME, &buf), 0);
TS_ASSERT_SIGNED_EQ(real_buf.st_ino, buf.st_ino);
TS_ASSERT_SIGNED_EQ(real_buf.st_size, dd_get_item_size(dd, FILENAME_TIME));
}
{
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, test_file_full_path), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "../libreport-test"), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "foo-blah-does-not-exist"), 0);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "link-item"), 0);
TS_ASSERT_SIGNED_EQ(dd_delete_item(dd, "dir-item"), -1);
}
/* A T _REMOVEDIR (0x200) interferes with autconf */
assert(unlinkat(dd->dd_fd, "dir-item", 0x200) == 0);
assert(unlink(test_file_full_path) == 0);
testsuite_dump_dir_delete(dd);
}
TS_RETURN_MAIN
]])
## ------------------- ##
## dd_get_env_variable ##
## ------------------- ##
AT_TESTFUN([dd_get_env_variable],
[[
#include "testsuite.h"
#include "testsuite_tools.h"
TS_MAIN
{
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
dd_create_basic_files(dd, geteuid(), NULL);
char *value = NULL;
TS_ASSERT_SIGNED_EQ(dd_get_env_variable(dd, "FROG", &value), -ENOENT);
TS_ASSERT_PTR_IS_NULL_MESSAGE(value, "Untouched return value on -ENOENT");
dd_save_text(dd, FILENAME_ENVIRON, "FROG=ribbit\nROOSTER=cockle-doodle-doo");
TS_ASSERT_FUNCTION(dd_get_env_variable(dd, "HORSE", &value));
TS_ASSERT_PTR_IS_NULL_MESSAGE(value, "Untouched return value on missing variable");
TS_ASSERT_FUNCTION(dd_get_env_variable(dd, "ROOSTER", &value));
TS_ASSERT_STRING_EQ(value, "cockle-doodle-doo", "Variable ROOSTER loaded from the dump dir");
testsuite_dump_dir_delete(dd);
}
TS_RETURN_MAIN
]])
## ----------------------- ##
## dd_get_first_occurrence ##
## ----------------------- ##
AT_TESTFUN([dd_get_first_occurrence],
[[
#include "testsuite.h"
#include "testsuite_tools.h"
TS_MAIN
{
{ /* No time file */
const time_t time_before_create = time(NULL);
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
TS_ASSERT_SIGNED_GE(dd_get_first_occurrence(dd), time_before_create);
const time_t time_backup = dd_get_first_occurrence(dd);
sleep(2);
TS_ASSERT_SIGNED_EQ(dd_get_first_occurrence(dd), time_backup);
sleep(2);
dd_create_basic_files(dd, geteuid(), NULL);
TS_ASSERT_SIGNED_EQ(dd_get_first_occurrence(dd), time_backup);
uint64_t saved;
TS_ASSERT_FUNCTION(dd_load_uint64(dd, FILENAME_TIME, &saved));
TS_ASSERT_SIGNED_EQ(saved, dd_get_first_occurrence(dd));
testsuite_dump_dir_delete(dd);
}
{ /* Invalid time file */
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
const time_t time_backup = dd_get_first_occurrence(dd);
dd_save_text(dd, FILENAME_TIME, "foo");
sleep(2);
dd_create_basic_files(dd, geteuid(), NULL);
TS_ASSERT_SIGNED_EQ(dd_get_first_occurrence(dd), time_backup);
testsuite_dump_dir_delete(dd);
}
{ /* Valid time file */
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
dd_save_text(dd, FILENAME_TIME, "1234567");
sleep(2);
dd_create_basic_files(dd, geteuid(), NULL);
TS_ASSERT_SIGNED_EQ(dd_get_first_occurrence(dd), 1234567);
testsuite_dump_dir_delete(dd);
}
{ /* Impossible case = dd->dd_time == -1 */
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
dd->dd_time = -1;
errno = 0;
TS_ASSERT_SIGNED_EQ(dd_get_first_occurrence(dd), -1);
TS_ASSERT_SIGNED_EQ(errno, ENODATA);
testsuite_dump_dir_delete(dd);
}
}
TS_RETURN_MAIN
]])
## ----------------------- ##
## dd_get_last_occurrence ##
## ----------------------- ##
AT_TESTFUN([dd_get_last_occurrence],
[[
#include "testsuite.h"
#include "testsuite_tools.h"
TS_MAIN
{
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), dd_get_first_occurrence(dd));
dd->dd_time = (time_t)1234567;
dd_create_basic_files(dd, geteuid(), NULL);
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), dd_get_first_occurrence(dd));
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), 1234567);
dd_save_text(dd, FILENAME_LAST_OCCURRENCE, "123");
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), dd_get_first_occurrence(dd));
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), 1234567);
dd_save_text(dd, FILENAME_LAST_OCCURRENCE, "7654321");
TS_ASSERT_SIGNED_NEQ(dd_get_last_occurrence(dd), dd_get_first_occurrence(dd));
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), 7654321);
dd->dd_time = -1;
errno = 0;
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), -1);
TS_ASSERT_SIGNED_EQ(errno, ENODATA);
dd_delete_item(dd, FILENAME_LAST_OCCURRENCE);
errno = 0;
TS_ASSERT_SIGNED_EQ(dd_get_last_occurrence(dd), -1);
TS_ASSERT_SIGNED_EQ(errno, ENODATA);
testsuite_dump_dir_delete(dd);
}
TS_RETURN_MAIN
]])
## ------------ ##
## dd_open_item ##
## ------------ ##
AT_TESTFUN([dd_open_item], [[
#include "testsuite.h"
#include "testsuite_tools.h"
TS_MAIN
{
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
dd->dd_time = (time_t)1234567;
dd_create_basic_files(dd, geteuid(), NULL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "//", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/a", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "a/", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, ".", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "..", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/.", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "//.", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "./", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, ".//", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/./", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/..", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "//..", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "../", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "..//", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/../", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "/.././", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "looks-good-but-evil/", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "looks-good-but-evil/../../", O_RDWR), -EINVAL);
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-=", O_RDWR), -EINVAL);
const int fd_rdonly_noent = dd_open_item(dd, "nofile", O_RDONLY);
TS_ASSERT_SIGNED_LT(fd_rdonly_noent, 0);
const int fd_wronly_noent = dd_open_item(dd, "nofile", O_RDWR);
TS_ASSERT_SIGNED_GE(fd_wronly_noent, 0);
if (g_testsuite_last_ok) {
full_write_str(fd_wronly_noent, "fd_wronly_noent");
close(fd_wronly_noent);
char *const noent_contents = dd_load_text(dd, "nofile");
TS_ASSERT_STRING_EQ(noent_contents, "fd_wronly_noent", "Successfully wrote data");
free(noent_contents);
}
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "time", O_RDONLY | O_EXCL), -ENOTSUP);
const int fd_rdonly_time = dd_open_item(dd, "time", O_RDONLY);
TS_ASSERT_SIGNED_GE(fd_rdonly_time, 0);
if (g_testsuite_last_ok) {
char *time = dd_load_text(dd, "time");
TS_ASSERT_PTR_IS_NOT_NULL(time);
char rdonly_time_contents[16];
int bytes_rdonly_time = full_read(fd_rdonly_time, rdonly_time_contents, sizeof(rdonly_time_contents));
TS_ASSERT_SIGNED_GT(bytes_rdonly_time, 0);
if (bytes_rdonly_time > 0) {
rdonly_time_contents[bytes_rdonly_time] = '\0';
TS_ASSERT_STRING_EQ(rdonly_time_contents, time, "Read only time");
}
else {
TS_PRINTF("FD %d read error: %s\n", fd_rdonly_time, strerror(errno));
}
free(time);
close(fd_rdonly_time);
}
TS_ASSERT_SIGNED_EQ(dd_open_item(dd, "time", O_RDWR | O_EXCL), -ENOTSUP);
const int fd_rdwr_time = dd_open_item(dd, "time", O_RDWR);
TS_ASSERT_SIGNED_GE(fd_rdwr_time, 0);
if (g_testsuite_last_ok) {
full_write_str(fd_rdwr_time, "7654321");
TS_ASSERT_FUNCTION(lseek(fd_rdwr_time, 0, SEEK_SET));
char rdwr_time_contents[16];
int bytes_rdwr_time = full_read(fd_rdwr_time, rdwr_time_contents, sizeof(rdwr_time_contents));
close(fd_rdwr_time);
TS_ASSERT_SIGNED_GT(bytes_rdwr_time, 0);
if (g_testsuite_last_ok) {
rdwr_time_contents[bytes_rdwr_time] = '\0';
char *const time_contents = dd_load_text(dd, "time");
TS_ASSERT_STRING_EQ(rdwr_time_contents, "7654321", "Successfully wrote time data");
TS_ASSERT_STRING_EQ(time_contents, "7654321", "Successfully wrote time data");
TS_ASSERT_STRING_EQ(rdwr_time_contents, time_contents, "Read only time");
free(time_contents);
}
else {
TS_PRINTF("FD %d read error: %s\n", fd_rdwr_time, strerror(errno));
}
}
testsuite_dump_dir_delete(dd);
}
TS_RETURN_MAIN
]])
## ----------------- ##
## dd_open_item_file ##
## ----------------- ##
AT_TESTFUN([dd_open_item_file], [[
#include "testsuite.h"
#include "testsuite_tools.h"
TS_MAIN
{
struct dump_dir *dd = testsuite_dump_dir_create(-1, -1, 0);
dd->dd_time = (time_t)1234567;
dd_create_basic_files(dd, geteuid(), NULL);
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "//", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/a", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "a/", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, ".", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "..", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/.", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "//.", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "./", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, ".//", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/./", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/..", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "//..", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "../", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "..//", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/../", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "/.././", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "looks-good-but-evil/", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "looks-good-but-evil/../../", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-=", O_RDWR));
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "nofile", O_RDONLY));
{
FILE *const f_rdwr_noent = dd_open_item_file(dd, "nofile", O_RDWR);
TS_ASSERT_PTR_IS_NOT_NULL(f_rdwr_noent);
if (g_testsuite_last_ok) {
fprintf(f_rdwr_noent, "%s", "f_rdwr_noent");
rewind(f_rdwr_noent);
char rdwr_contents[256];
TS_ASSERT_PTR_IS_NOT_NULL(fgets(rdwr_contents, sizeof(rdwr_contents), f_rdwr_noent));
TS_ASSERT_STRING_EQ(rdwr_contents, "f_rdwr_noent", "Successfully read data");
fclose(f_rdwr_noent);
char *const noent_contents = dd_load_text(dd, "nofile");
TS_ASSERT_STRING_EQ(noent_contents, "f_rdwr_noent", "Successfully wrote data");
free(noent_contents);
}
}
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "time", O_RDONLY | O_EXCL));
{
FILE *const f_rdonly_time = dd_open_item_file(dd, "time", O_RDONLY);
TS_ASSERT_PTR_IS_NOT_NULL(f_rdonly_time);
if (g_testsuite_last_ok) {
char *time = dd_load_text(dd, "time");
TS_ASSERT_PTR_IS_NOT_NULL(time);
char rdonly_time_contents[16];
char *const res = fgets(rdonly_time_contents, sizeof(rdonly_time_contents), f_rdonly_time);
TS_ASSERT_PTR_EQ(rdonly_time_contents, res);
if (g_testsuite_last_ok) {
TS_ASSERT_STRING_EQ(rdonly_time_contents, time, "Read only time");
}
else {
TS_PRINTF("File 'time' read error: %s\n", strerror(errno));
}
fclose(f_rdonly_time);
}
}
TS_ASSERT_PTR_IS_NULL(dd_open_item_file(dd, "time", O_RDWR | O_EXCL));
{
FILE *const f_rdwr_time = dd_open_item_file(dd, "time", O_RDWR);
TS_ASSERT_PTR_IS_NOT_NULL(f_rdwr_time);
if (g_testsuite_last_ok) {
fprintf(f_rdwr_time, "7654321");
rewind(f_rdwr_time);
char rdwr_contents[256];
TS_ASSERT_PTR_IS_NOT_NULL(fgets(rdwr_contents, sizeof(rdwr_contents), f_rdwr_time));
TS_ASSERT_STRING_EQ(rdwr_contents, "7654321", "Successfully read time data");
fclose(f_rdwr_time);
char *const time_contents = dd_load_text(dd, "time");
TS_ASSERT_STRING_EQ(time_contents, "7654321", "Successfully wrote time data");
free(time_contents);
}
}
testsuite_dump_dir_delete(dd);
}
TS_RETURN_MAIN
]])