#define _GNU_SOURCE #include #include #include #include #include #include #include "test_fs.h" // ============================================================================ // Helper variable and function // ============================================================================ // Contains the name that was used to invoke the calling program extern char *program_invocation_short_name; static int test_readlink_from_procfs(const char *proc_inode, char *buf, int buf_size, const char *expected_target) { int n = readlink(proc_inode, buf, buf_size); if (n < 0) { THROW_ERROR("failed to readlink from %s", proc_inode); } else if (n != strlen(expected_target)) { THROW_ERROR("readlink from %s length is wrong", proc_inode); } if (strncmp(buf, expected_target, n) != 0) { THROW_ERROR("check the result from %s failed", proc_inode); } return 0; } static int test_read_from_procfs(const char *proc_inode) { char buf[1024] = { 0 }; int len; int fd = open(proc_inode, O_RDONLY); if (fd < 0) { THROW_ERROR("failed to open file: %s", proc_inode); } do { len = read(fd, buf, sizeof(buf)); if (len < 0) { THROW_ERROR("failed to read: %s", proc_inode); } } while (len == sizeof(buf)); close(fd); printf("test read from %s:\n%s\n", proc_inode, buf); return 0; } // ============================================================================ // Test cases for procfs // ============================================================================ static int test_readlink_from_proc_self_exe() { char exe_buf[PATH_MAX] = { 0 }; char absolute_path[PATH_MAX] = { 0 }; const char *proc_exe = "/proc/self/exe"; int n = snprintf(absolute_path, sizeof(absolute_path), "/bin/%s", program_invocation_short_name); if (n < 0) { THROW_ERROR("failed to call snprintf"); } if (test_readlink_from_procfs(proc_exe, exe_buf, PATH_MAX, absolute_path) < 0) { THROW_ERROR("failed to call test_readlink_from_procfs"); } return 0; } static int test_readlink_from_proc_self_cwd() { char cwd_buf[PATH_MAX] = { 0 }; const char *proc_cwd = "/proc/self/cwd"; if (test_readlink_from_procfs(proc_cwd, cwd_buf, PATH_MAX, "/") < 0) { THROW_ERROR("failed to call test_readlink_from_procfs"); } if (chdir("/bin") < 0) { THROW_ERROR("failed to chdir"); } if (test_readlink_from_procfs(proc_cwd, cwd_buf, PATH_MAX, "/bin") < 0) { THROW_ERROR("failed to call test_readlink_from_procfs after chdir"); } if (chdir("/") < 0) { THROW_ERROR("failed to chdir"); } return 0; } static int test_read_from_proc_self_maps() { const char *proc_maps = "/proc/self/maps"; if (test_read_from_procfs(proc_maps) < 0) { THROW_ERROR("failed to read the cpuinfo"); } return 0; } static int test_readlink_from_proc_self_root() { char root_buf[PATH_MAX] = { 0 }; const char *proc_root = "/proc/self/root"; if (test_readlink_from_procfs(proc_root, root_buf, PATH_MAX, "/") < 0) { THROW_ERROR("failed to call test_readlink_from_procfs"); } return 0; } static int test_create_and_unlink_file_from_proc_self_root() { const char *proc_root_file = "/proc/self/root/test_file"; int fd = open(proc_root_file, O_RDONLY | O_CREAT | O_TRUNC, 00666); if (fd < 0) { THROW_ERROR("failed to create a file"); } close(fd); if (unlink(proc_root_file) < 0) { THROW_ERROR("failed to unlink the created file"); } return 0; } static int test_read_from_proc_self_cmdline() { char absolute_path[PATH_MAX] = { 0 }; const char *proc_cmdline = "/proc/self/cmdline"; int n = snprintf(absolute_path, sizeof(absolute_path), "/bin/%s", program_invocation_short_name); if (n < 0) { THROW_ERROR("failed to call snprintf"); } char read_buf[PATH_MAX] = { 0 }; int fd = open(proc_cmdline, O_RDONLY); size_t len = read(fd, read_buf, sizeof(read_buf)); if (len != strlen(absolute_path) + 1) { THROW_ERROR("failed check the return value of reading from %s", proc_cmdline); } if (read_buf[strlen(absolute_path)] != '\0') { THROW_ERROR("failed check the buffer of reading from %s", proc_cmdline); } if (strcmp(absolute_path, read_buf) != 0) { THROW_ERROR("failed to check result in %s", proc_cmdline); } close(fd); return 0; } static int test_read_from_proc_self_comm() { // The name can be up to 16 bytes long, including the terminating null byte. char comm_name[16] = { 0 }; const char *proc_comm = "/proc/self/comm"; if (snprintf(comm_name, sizeof(comm_name), "%s", program_invocation_short_name) < 0) { THROW_ERROR("failed to call snprintf"); } // The last byte shoud be '\n' int end_idx = strlen(comm_name); comm_name[end_idx] = '\n'; if (fs_check_file_content(proc_comm, comm_name) < 0) { THROW_ERROR("failed to check result in %s", proc_comm); } return 0; } static int test_read_from_proc_self_stat() { const char *proc_self_stat = "/proc/self/stat"; FILE *fp = fopen(proc_self_stat, "r"); if (fp == NULL) { THROW_ERROR("failed to fopen: %s", proc_self_stat); } int pid, ppid, pgrp; char comm[32] = { 0 }; char state[32] = { 0 }; int ret = fscanf(fp, "%d %s %s %d %d", &pid, comm, state, &ppid, &pgrp); if (ret != 5) { THROW_ERROR("failed to parse the first 5 items"); } if (pid != getpid()) { THROW_ERROR("failed to check the result in %s", proc_self_stat); } printf("cat %s with the first 5 items:\n%d %s %s %d %d\n", proc_self_stat, pid, comm, state, ppid, pgrp); fclose(fp); return 0; } static int test_read_from_proc_meminfo() { const char *proc_meminfo = "/proc/meminfo"; if (test_read_from_procfs(proc_meminfo) < 0) { THROW_ERROR("failed to read the meminfo"); } return 0; } static int test_read_from_proc_cpuinfo() { const char *proc_cpuinfo = "/proc/cpuinfo"; if (test_read_from_procfs(proc_cpuinfo) < 0) { THROW_ERROR("failed to read the cpuinfo"); } return 0; } static int test_read_from_proc_stat() { const char *proc_stat = "/proc/stat"; if (test_read_from_procfs(proc_stat) < 0) { THROW_ERROR("failed to read the stat"); } return 0; } #define PROC_SUPER_MAGIC 0x9fa0 static int test_statfs() { const char *file_path = "/proc/cpuinfo"; struct statfs statfs_buf; int ret; ret = statfs(file_path, &statfs_buf); if (ret < 0) { THROW_ERROR("failed to statfs the file"); } if (statfs_buf.f_type != PROC_SUPER_MAGIC) { THROW_ERROR("failed to check the f_type"); } return 0; } static int test_readdir_root() { const char *root = "/proc"; char pid[NAME_MAX] = { 0 }; snprintf(pid, sizeof(pid), "%d", getpid()); char expected_entries[4][NAME_MAX] = { "self", "meminfo", "cpuinfo", { *pid }, }; if (check_readdir_with_expected_entries(root, expected_entries, 4) < 0) { THROW_ERROR("failed to test readdir %s", root); } return 0; } static int test_readdir_self() { const char *self = "/proc/self"; char expected_entries[6][NAME_MAX] = { "exe", "cwd", "root", "fd", "comm", "cmdline", }; if (check_readdir_with_expected_entries(self, expected_entries, 6) < 0) { THROW_ERROR("failed to test readdir %s", self); } return 0; } static int test_readdir_self_fd() { const char *self_fd = "/proc/self/fd"; char expected_entries[3][NAME_MAX] = { "0", "1", "2", }; if (check_readdir_with_expected_entries(self_fd, expected_entries, 3) < 0) { THROW_ERROR("failed to test readdir %s", self_fd); } return 0; } // ============================================================================ // Test suite main // ============================================================================ static test_case_t test_cases[] = { TEST_CASE(test_readlink_from_proc_self_exe), TEST_CASE(test_readlink_from_proc_self_cwd), TEST_CASE(test_readlink_from_proc_self_root), TEST_CASE(test_create_and_unlink_file_from_proc_self_root), TEST_CASE(test_read_from_proc_self_cmdline), TEST_CASE(test_read_from_proc_self_comm), TEST_CASE(test_read_from_proc_self_stat), TEST_CASE(test_read_from_proc_meminfo), TEST_CASE(test_read_from_proc_cpuinfo), TEST_CASE(test_read_from_proc_stat), TEST_CASE(test_statfs), TEST_CASE(test_readdir_root), TEST_CASE(test_readdir_self), TEST_CASE(test_readdir_self_fd), TEST_CASE(test_read_from_proc_self_maps), }; int main(int argc, const char *argv[]) { return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); }