| /* |
| * 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/time.h> |
| #include <time.h> |
| #include <sys/vfs.h> |
| #include <sys/statvfs.h> |
| #include <dirent.h> |
| #include <ctype.h> |
| #include <limits.h> |
| |
| #include "tests.h" |
| |
| uint32_t files_created = 0; |
| uint32_t files_removed = 0; |
| uint32_t dirs_created = 0; |
| uint32_t dirs_removed = 0; |
| int64_t *size_ptr = 0; |
| |
| void display_stats(void) |
| { |
| printf( "\nrndrm99 stats:\n" |
| "\tNumber of files created = %u\n" |
| "\tNumber of files deleted = %u\n" |
| "\tNumber of directories created = %u\n" |
| "\tNumber of directories deleted = %u\n" |
| "\tCurrent net size of creates and deletes = %lld\n", |
| (unsigned) files_created, |
| (unsigned) files_removed, |
| (unsigned) dirs_created, |
| (unsigned) dirs_removed, |
| (long long) (size_ptr ? *size_ptr : 0)); |
| fflush(stdout); |
| } |
| |
| struct timeval tv_before; |
| struct timeval tv_after; |
| |
| void before(void) |
| { |
| CHECK(gettimeofday(&tv_before, NULL) != -1); |
| } |
| |
| void after(const char *msg) |
| { |
| time_t diff; |
| CHECK(gettimeofday(&tv_after, NULL) != -1); |
| diff = tv_after.tv_sec - tv_before.tv_sec; |
| if (diff >= 8) { |
| printf("\nrndrm99: the following fn took more than 8 seconds: %s (took %u secs)\n",msg,(unsigned) diff); |
| fflush(stdout); |
| display_stats(); |
| } |
| } |
| |
| #define WRITE_BUFFER_SIZE 32768 |
| |
| static 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 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; |
| before(); |
| written = write(fd, write_buffer + start, sz); |
| if (written <= 0) { |
| CHECK(errno == ENOSPC); /* File system full */ |
| errno = 0; |
| after("write"); |
| fprintf(stderr,"\nrndrm99: write failed with ENOSPC\n");fflush(stderr); |
| display_stats(); |
| break; |
| } |
| after("write"); |
| remains -= written; |
| actual_size += written; |
| if ((size_t) written == sz) |
| start = 0; |
| else |
| start += written; |
| } |
| return actual_size; |
| } |
| |
| /* Create a file of size file_size */ |
| uint64_t 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; |
| mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; |
| before(); |
| fd = open(file_name, flags, mode); |
| if (fd == -1 && errno == ENOSPC) { |
| errno = 0; |
| after("open"); |
| fprintf(stderr,"\nrndrm99: open failed with ENOSPC\n");fflush(stderr); |
| display_stats(); |
| return 0; /* File system full */ |
| } |
| CHECK(fd != -1); |
| after("open"); |
| actual_size = fill_file(fd, file_size); |
| before(); |
| CHECK(close(fd) != -1); |
| after("close"); |
| if (file_size != 0 && actual_size == 0) { |
| printf("\nrndrm99: unlinking zero size file\n");fflush(stdout); |
| before(); |
| CHECK(unlink(file_name) != -1); |
| after("unlink (create_file)"); |
| } |
| return actual_size; |
| } |
| |
| /* Create an empty sub-directory or small file in the current directory */ |
| int64_t create_entry(char *return_name) |
| { |
| int fd; |
| char name[256]; |
| int64_t res; |
| |
| for (;;) { |
| sprintf(name, "%u", (unsigned) tests_random_no(10000000)); |
| before(); |
| fd = open(name, O_RDONLY); |
| after("open (create_entry)"); |
| if (fd == -1) |
| break; |
| before(); |
| close(fd); |
| after("close (create_entry)"); |
| } |
| if (return_name) |
| strcpy(return_name, name); |
| if (tests_random_no(2)) { |
| res = create_file(name, tests_random_no(4096)); |
| if (res > 0) |
| files_created += 1; |
| return res; |
| } else { |
| before(); |
| if (mkdir(name, 0777) == -1) { |
| CHECK(errno == ENOSPC); |
| after("mkdir"); |
| errno = 0; |
| fprintf(stderr,"\nrndrm99: mkdir failed with ENOSPC\n");fflush(stderr); |
| display_stats(); |
| return 0; |
| } |
| after("mkdir"); |
| dirs_created += 1; |
| return TESTS_EMPTY_DIR_SIZE; |
| } |
| } |
| |
| /* Remove a random file of empty sub-directory from the current directory */ |
| int64_t remove_entry(void) |
| { |
| DIR *dir; |
| struct dirent *entry; |
| unsigned count = 0, pos; |
| int64_t result = 0; |
| |
| before(); |
| dir = opendir("."); |
| CHECK(dir != NULL); |
| after("opendir"); |
| for (;;) { |
| errno = 0; |
| before(); |
| entry = readdir(dir); |
| if (entry) { |
| after("readdir 1"); |
| if (strcmp(".",entry->d_name) != 0 && |
| strcmp("..",entry->d_name) != 0) |
| ++count; |
| } else { |
| CHECK(errno == 0); |
| after("readdir 1"); |
| break; |
| } |
| } |
| pos = tests_random_no(count); |
| count = 0; |
| before(); |
| rewinddir(dir); |
| after("rewinddir"); |
| for (;;) { |
| errno = 0; |
| before(); |
| entry = readdir(dir); |
| if (!entry) { |
| CHECK(errno == 0); |
| after("readdir 2"); |
| break; |
| } |
| after("readdir 2"); |
| if (strcmp(".",entry->d_name) != 0 && |
| strcmp("..",entry->d_name) != 0) { |
| if (count == pos) { |
| if (entry->d_type == DT_DIR) { |
| before(); |
| tests_clear_dir(entry->d_name); |
| after("tests_clear_dir"); |
| before(); |
| CHECK(rmdir(entry->d_name) != -1); |
| after("rmdir"); |
| result = TESTS_EMPTY_DIR_SIZE; |
| dirs_removed += 1; |
| } else { |
| struct stat st; |
| before(); |
| CHECK(stat(entry->d_name, &st) != -1); |
| after("stat"); |
| result = st.st_size; |
| before(); |
| CHECK(unlink(entry->d_name) != -1); |
| after("unlink"); |
| files_removed += 1; |
| } |
| } |
| ++count; |
| } |
| } |
| before(); |
| CHECK(closedir(dir) != -1); |
| after("closedir"); |
| return result; |
| } |
| |
| void rndrm99(void) |
| { |
| int64_t repeat, loop_cnt; |
| int64_t size, this_size; |
| pid_t pid; |
| char dir_name[256]; |
| |
| size_ptr = &size; |
| /* Create a directory to test in */ |
| pid = getpid(); |
| tests_cat_pid(dir_name, "rndrm99_test_dir_", pid); |
| if (chdir(dir_name) == -1) |
| CHECK(mkdir(dir_name, 0777) != -1); |
| CHECK(chdir(dir_name) != -1); |
| /* Repeat loop */ |
| repeat = tests_repeat_parameter; |
| size = 0; |
| for (;;) { |
| /* Create and remove sub-dirs and small files, */ |
| /* but tending to grow */ |
| printf("\nrndrm99: growing\n");fflush(stdout); |
| loop_cnt = 0; |
| do { |
| if (loop_cnt++ % 2000 == 0) |
| display_stats(); |
| if (tests_random_no(3)) { |
| this_size = create_entry(NULL); |
| if (!this_size) |
| break; |
| size += this_size; |
| } else { |
| this_size = remove_entry(); |
| size -= this_size; |
| if (size < 0) |
| size = 0; |
| if (!this_size) |
| this_size = 1; |
| } |
| } while (this_size && |
| (tests_size_parameter == 0 || |
| size < tests_size_parameter)); |
| /* Create and remove sub-dirs and small files, but */ |
| /* but tending to shrink */ |
| printf("\nrndrm99: shrinking\n");fflush(stdout); |
| loop_cnt = 0; |
| do { |
| if (loop_cnt++ % 2000 == 0) |
| display_stats(); |
| if (!tests_random_no(3)) { |
| this_size = create_entry(NULL); |
| size += this_size; |
| } else { |
| this_size = remove_entry(); |
| size -= this_size; |
| if (size < 0) |
| size = 0; |
| } |
| } while ((tests_size_parameter != 0 && |
| size > tests_size_parameter / 10) || |
| (tests_size_parameter == 0 && size > 100000)); |
| /* Break if repeat count exceeded */ |
| if (tests_repeat_parameter > 0 && --repeat <= 0) |
| break; |
| /* Sleep */ |
| if (tests_sleep_parameter > 0) { |
| unsigned us = tests_sleep_parameter * 1000; |
| unsigned rand_divisor = RAND_MAX / us; |
| unsigned s = (us / 2) + (rand() / rand_divisor); |
| printf("\nrndrm99: sleeping\n");fflush(stdout); |
| usleep(s); |
| } |
| } |
| printf("\nrndrm99: tidying\n");fflush(stdout); |
| display_stats(); |
| /* Tidy up by removing everything */ |
| tests_clear_dir("."); |
| CHECK(chdir("..") != -1); |
| CHECK(rmdir(dir_name) != -1); |
| size_ptr = 0; |
| } |
| |
| /* Title of this test */ |
| |
| const char *rndrm99_get_title(void) |
| { |
| return "Randomly create and remove directories and files"; |
| } |
| |
| /* Description of this test */ |
| |
| const char *rndrm99_get_description(void) |
| { |
| return |
| "Create a directory named rndrm99_test_dir_pid, where " \ |
| "pid is the process id. Within that directory, " \ |
| "randomly create and remove " \ |
| "a number of sub-directories and small files, " \ |
| "but do more creates than removes. " \ |
| "When the total size of all sub-directories and files " \ |
| "is greater than the size specified by the size parameter, " \ |
| "start to do more removes than creates. " \ |
| "The size parameter is given by the -z or --size option, " \ |
| "otherwise it defaults to 1000000. " \ |
| "A size of zero fills the file system until there is no " |
| "space left. " \ |
| "The task repeats, sleeping in between each iteration. " \ |
| "The repeat count is set by the -n or --repeat option, " \ |
| "otherwise it defaults to 1. " \ |
| "A repeat count of zero repeats forever. " \ |
| "The sleep value is given by the -p or --sleep option, " \ |
| "otherwise it defaults to 0. " |
| "Sleep is specified in milliseconds."; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int run_test; |
| |
| /* Set default test size */ |
| tests_size_parameter = 1000000; |
| |
| /* Set default test repetition */ |
| tests_repeat_parameter = 1; |
| |
| /* Set default test sleep */ |
| tests_sleep_parameter = 0; |
| |
| /* Handle common arguments */ |
| run_test = tests_get_args(argc, argv, rndrm99_get_title(), |
| rndrm99_get_description(), "znp"); |
| if (!run_test) |
| return 1; |
| /* Change directory to the file system and check it is ok for testing */ |
| tests_check_test_file_system(); |
| /* Do the actual test */ |
| rndrm99(); |
| return 0; |
| } |