📄 tests.c
字号:
/* * Copyright (C) 2007 Nokia Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Author: Adrian Hunter */#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdint.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include <sys/vfs.h>#include <sys/statvfs.h>#include <linux/jffs2.h>#include <libgen.h>#include <dirent.h>#include <ctype.h>#include <limits.h>#include <sys/mount.h>#include <mntent.h>#include <time.h>#include "tests.h"char *tests_file_system_mount_dir = TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR;char *tests_file_system_type = TESTS_DEFAULT_FILE_SYSTEM_TYPE;int tests_ok_to_sync = 0; /* Whether to use fsync *//* General purpose test parameter to specify some aspect of test size. May be used by different tests in different ways or not at all. Set by the -z or --size option. */int64_t tests_size_parameter = 0;/* General purpose test parameter to specify some aspect of test repetition. May be used by different tests in different ways or not at all. Set by the -n, --repeat options. */int64_t tests_repeat_parameter = 0;/* General purpose test parameter to specify some aspect of test sleeping. May be used by different tests in different ways or not at all. Set by the -p, --sleep options. */int64_t tests_sleep_parameter = 0;/* Program name from argv[0] */char *program_name = "unknown";/* General purpose test parameter to specify a file should be unlinked. May be used by different tests in different ways or not at all. */int tests_unlink_flag = 0;/* General purpose test parameter to specify a file should be closed. May be used by different tests in different ways or not at all. */int tests_close_flag = 0;/* General purpose test parameter to specify a file should be deleted. May be used by different tests in different ways or not at all. */int tests_delete_flag = 0;/* General purpose test parameter to specify a file have a hole. May be used by different tests in different ways or not at all. */int tests_hole_flag = 0;/* Whether it is ok to test on the root file system */static int rootok = 0;/* Maximum file name length of test file system (from statfs) */long tests_max_fname_len = 255;/* Function invoked by the CHECK macro */void tests_test(int test,const char *msg,const char *file,unsigned line){ int eno; time_t t; if (test) return; eno = errno; time(&t); fprintf(stderr, "Test failed: %s on %s" "Test failed: %s in %s at line %u\n", program_name, ctime(&t), msg, file, line); if (eno) { fprintf(stderr,"errno = %d\n",eno); fprintf(stderr,"strerror = %s\n",strerror(eno)); } exit(1);}static int is_zero(const char *p){ for (;*p;++p) if (*p != '0') return 0; return 1;}static void fold(const char *text, int width){ int pos, bpos = 0; const char *p; char line[1024]; if (width > 1023) { printf("%s\n", text); return; } p = text; pos = 0; while (p[pos]) { while (!isspace(p[pos])) { line[pos] = p[pos]; if (!p[pos]) break; ++pos; if (pos == width) { line[pos] = '\0'; printf("%s\n", line); p += pos; pos = 0; } } while (pos < width) { line[pos] = p[pos]; if (!p[pos]) { bpos = pos; break; } if (isspace(p[pos])) bpos = pos; ++pos; } line[bpos] = '\0'; printf("%s\n", line); p += bpos; pos = 0; while (p[pos] && isspace(p[pos])) ++p; }}/* Handle common program options */int tests_get_args(int argc, char *argv[], const char *title, const char *desc, const char *opts){ int run_test = 0; int display_help = 0; int display_title = 0; int display_description = 0; int i; char *s; program_name = argv[0]; s = getenv("TEST_FILE_SYSTEM_MOUNT_DIR"); if (s) tests_file_system_mount_dir = strdup(s); s = getenv("TEST_FILE_SYSTEM_TYPE"); if (s) tests_file_system_type = strdup(s); run_test = 1; rootok = 1; for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) display_help = 1; else if (strcmp(argv[i], "--title") == 0 || strcmp(argv[i], "-t") == 0) display_title = 1; else if (strcmp(argv[i], "--description") == 0 || strcmp(argv[i], "-d") == 0) display_description = 1; else if (strcmp(argv[i], "--sync") == 0 || strcmp(argv[i], "-s") == 0) tests_ok_to_sync = 1; else if (strncmp(argv[i], "--size", 6) == 0 || strncmp(argv[i], "-z", 2) == 0) { int64_t n; char *p; if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++i; p = argv[i]; while (*p && !isdigit(*p)) ++p; n = atoll(p); if (n) tests_size_parameter = n; else { int all_zero = 1; for (; all_zero && *p; ++p) if (*p != '0') all_zero = 0; if (all_zero) tests_size_parameter = 0; else display_help = 1; } } else if (strncmp(argv[i], "--repeat", 8) == 0 || strncmp(argv[i], "-n", 2) == 0) { int64_t n; char *p; if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++i; p = argv[i]; while (*p && !isdigit(*p)) ++p; n = atoll(p); if (n || is_zero(p)) tests_repeat_parameter = n; else display_help = 1; } else if (strncmp(argv[i], "--sleep", 7) == 0 || strncmp(argv[i], "-p", 2) == 0) { int64_t n; char *p; if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++i; p = argv[i]; while (*p && !isdigit(*p)) ++p; n = atoll(p); if (n || is_zero(p)) tests_sleep_parameter = n; else display_help = 1; } else if (strcmp(argv[i], "--unlink") == 0 || strcmp(argv[i], "-u") == 0) tests_unlink_flag = 1; else if (strcmp(argv[i], "--hole") == 0 || strcmp(argv[i], "-o") == 0) tests_hole_flag = 1; else if (strcmp(argv[i], "--close") == 0 || strcmp(argv[i], "-c") == 0) tests_close_flag = 1; else if (strcmp(argv[i], "--delete") == 0 || strcmp(argv[i], "-e") == 0) tests_delete_flag = 1; else display_help = 1; } if (display_help) { run_test = 0; display_title = 0; display_description = 0; if (!opts) opts = ""; printf("File System Test Program\n\n"); printf("Test Title: %s\n\n", title); printf("Usage is: %s [ options ]\n",argv[0]); printf(" Options are:\n"); printf(" -h, --help "); printf("Display this help\n"); printf(" -t, --title "); printf("Display the test title\n"); printf(" -d, --description "); printf("Display the test description\n"); if (strchr(opts, 's')) { printf(" -s, --sync "); printf("Make use of fsync\n"); } if (strchr(opts, 'z')) { printf(" -z, --size "); printf("Set size parameter\n"); } if (strchr(opts, 'n')) { printf(" -n, --repeat "); printf("Set repeat parameter\n"); } if (strchr(opts, 'p')) { printf(" -p, --sleep "); printf("Set sleep parameter\n"); } if (strchr(opts, 'u')) { printf(" -u, --unlink "); printf("Unlink file\n"); } if (strchr(opts, 'o')) { printf(" -o, --hole "); printf("Create a hole in a file\n"); } if (strchr(opts, 'c')) { printf(" -c, --close "); printf("Close file\n"); } if (strchr(opts, 'e')) { printf(" -e, --delete "); printf("Delete file\n"); } printf("\nBy default, testing is done in directory "); printf("/mnt/test_file_system. To change this\nuse "); printf("environmental variable "); printf("TEST_FILE_SYSTEM_MOUNT_DIR. By default, "); printf("the file\nsystem tested is jffs2. To change this "); printf("set TEST_FILE_SYSTEM_TYPE.\n\n"); printf("Test Description:\n"); fold(desc, 80); } else { if (display_title) printf("%s\n", title); if (display_description) printf("%s\n", desc); if (display_title || display_description) if (argc == 2 || (argc == 3 && display_title && display_description)) run_test = 0; } return run_test;}/* Return the number of files (or directories) in the given directory */unsigned tests_count_files_in_dir(const char *dir_name){ DIR *dir; struct dirent *entry; unsigned count = 0; dir = opendir(dir_name); CHECK(dir != NULL); for (;;) { errno = 0; entry = readdir(dir); if (entry) { if (strcmp(".",entry->d_name) != 0 && strcmp("..",entry->d_name) != 0) ++count; } else { CHECK(errno == 0); break; } } CHECK(closedir(dir) != -1); return count;}/* Change to the file system mount directory, check that it is empty, matches the file system type, and is not the root file system */void tests_check_test_file_system(void){ struct statfs fs_info; struct stat f_info; struct stat root_f_info; if (chdir(tests_file_system_mount_dir) == -1 || statfs(tests_file_system_mount_dir, &fs_info) == -1) { fprintf(stderr, "Invalid test file system mount directory:" " %s\n", tests_file_system_mount_dir); fprintf(stderr, "Use environment variable " "TEST_FILE_SYSTEM_MOUNT_DIR\n"); CHECK(0); } tests_max_fname_len = fs_info.f_namelen; if (strcmp(tests_file_system_type, "jffs2") == 0 && fs_info.f_type != JFFS2_SUPER_MAGIC) { fprintf(stderr, "File system type is not jffs2\n"); CHECK(0); } /* Check that the test file system is not the root file system */ if (!rootok) { CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); CHECK(stat("/", &root_f_info) != -1); CHECK(f_info.st_dev != root_f_info.st_dev); }}/* Get the free space for the file system of the current directory */uint64_t tests_get_free_space(void){ struct statvfs fs_info; CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); return (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize;}/* Get the total space for the file system of the current directory */uint64_t tests_get_total_space(void){ struct statvfs fs_info; CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); return (uint64_t) fs_info.f_blocks * (uint64_t) fs_info.f_frsize;}#define WRITE_BUFFER_SIZE 32768static char write_buffer[WRITE_BUFFER_SIZE];static void init_write_buffer(){ static int init = 0; if (!init) { int i, d; uint64_t u; u = RAND_MAX; u += 1; u /= 256; d = (int) u; srand(1); for (i = 0; i < WRITE_BUFFER_SIZE; ++i) write_buffer[i] = rand() / d; init = 1; }}/* Write size random bytes into file descriptor fd at the current position, returning the number of bytes actually written */uint64_t tests_fill_file(int fd, uint64_t size){ ssize_t written; size_t sz; unsigned start = 0, length; uint64_t remains; uint64_t actual_size = 0; init_write_buffer(); remains = size; while (remains > 0) { length = WRITE_BUFFER_SIZE - start; if (remains > length) sz = length; else sz = (size_t) remains; written = write(fd, write_buffer + start, sz); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; break; } remains -= written; actual_size += written; if (written == sz) start = 0; else start += written; } tests_maybe_sync(fd); return actual_size;}/* Write size random bytes into file descriptor fd at offset, returning the number of bytes actually written */uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size){ ssize_t written; size_t sz; unsigned start = 0, length; uint64_t remains; uint64_t actual_size = 0; CHECK(lseek(fd, offset, SEEK_SET) == offset); init_write_buffer(); remains = size; start = offset % WRITE_BUFFER_SIZE; while (remains > 0) { length = WRITE_BUFFER_SIZE - start; if (remains > length) sz = length; else sz = (size_t) remains; written = write(fd, write_buffer + start, sz); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; break; } remains -= written; actual_size += written; if (written == sz) start = 0; else start += written; } tests_maybe_sync(fd); return actual_size;}/* Check that a file written using tests_fill_file() and/or tests_write_filled_file() and/or tests_create_file() contains the expected random data */void tests_check_filled_file_fd(int fd){ ssize_t sz; char buf[WRITE_BUFFER_SIZE]; CHECK(lseek(fd, 0, SEEK_SET) == 0); do { sz = read(fd, buf, WRITE_BUFFER_SIZE); CHECK(sz >= 0); CHECK(memcmp(buf, write_buffer, sz) == 0); } while (sz);}/* Check that a file written using tests_fill_file() and/or tests_write_filled_file() and/or tests_create_file() contains the expected random data */void tests_check_filled_file(const char *file_name){ int fd; fd = open(file_name, O_RDONLY); CHECK(fd != -1); tests_check_filled_file_fd(fd); CHECK(close(fd) != -1);}void tests_sync_directory(const char *file_name){ char *path; char *dir; int fd; if (!tests_ok_to_sync) return; path = strdup(file_name); dir = dirname(path); fd = open(dir,O_RDONLY | tests_maybe_sync_flag()); CHECK(fd != -1); CHECK(fsync(fd) != -1); CHECK(close(fd) != -1); free(path);}/* Delete a file */void tests_delete_file(const char *file_name){ CHECK(unlink(file_name) != -1); tests_sync_directory(file_name);}/* Create a file of size file_size */uint64_t tests_create_file(const char *file_name, uint64_t file_size){ int fd; int flags; mode_t mode; uint64_t actual_size; /* Less than size if the file system is full */ flags = O_CREAT | O_TRUNC | O_WRONLY | tests_maybe_sync_flag(); mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; fd = open(file_name, flags, mode); if (fd == -1 && errno == ENOSPC) { errno = 0; return 0; /* File system full */ }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -