diff options
Diffstat (limited to 'kernel/tests/lib/safe_file_ops.c')
-rw-r--r-- | kernel/tests/lib/safe_file_ops.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/kernel/tests/lib/safe_file_ops.c b/kernel/tests/lib/safe_file_ops.c new file mode 100644 index 0000000..e06d399 --- /dev/null +++ b/kernel/tests/lib/safe_file_ops.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2012 Cyril Hrubis chrubis@suse.cz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include <stdarg.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <utime.h> + +#include "test.h" +#include "safe_file_ops_fn.h" + +/* + * Count number of expected assigned conversions. Any conversion starts with '%'. + * The '%%' matches % and no assignment is done. The %*x matches as x would do but + * the assignment is suppressed. + * + * NOTE: This is not 100% correct for complex scanf strings, but will do for + * all of our intended usage. + */ +static int count_scanf_conversions(const char *fmt) +{ + unsigned int cnt = 0; + int flag = 0; + + while (*fmt) { + switch (*fmt) { + case '%': + if (flag) { + cnt--; + flag = 0; + } else { + flag = 1; + cnt++; + } + break; + case '*': + if (flag) { + cnt--; + flag = 0; + } + break; + default: + flag = 0; + } + + fmt++; + } + + return cnt; +} + +int file_scanf(const char *file, const int lineno, + const char *path, const char *fmt, ...) +{ + va_list va; + FILE *f; + int exp_convs, ret; + + f = fopen(path, "r"); + + if (f == NULL) { + tst_resm(TWARN, + "Failed to open FILE '%s' at %s:%d", + path, file, lineno); + return 1; + } + + exp_convs = count_scanf_conversions(fmt); + + va_start(va, fmt); + ret = vfscanf(f, fmt, va); + va_end(va); + + if (ret == EOF) { + tst_resm(TWARN, + "The FILE '%s' ended prematurely at %s:%d", + path, file, lineno); + goto err; + } + + if (ret != exp_convs) { + tst_resm(TWARN, + "Expected %i conversions got %i FILE '%s' at %s:%d", + exp_convs, ret, path, file, lineno); + goto err; + } + + if (fclose(f)) { + tst_resm(TWARN, + "Failed to close FILE '%s' at %s:%d", + path, file, lineno); + return 1; + } + + return 0; + +err: + if (fclose(f)) { + tst_resm(TWARN, + "Failed to close FILE '%s' at %s:%d", + path, file, lineno); + } + return 1; +} + +void safe_file_scanf(const char *file, const int lineno, + void (*cleanup_fn) (void), + const char *path, const char *fmt, ...) +{ + va_list va; + FILE *f; + int exp_convs, ret; + + f = fopen(path, "r"); + + if (f == NULL) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to open FILE '%s' for reading at %s:%d", + path, file, lineno); + return; + } + + exp_convs = count_scanf_conversions(fmt); + + va_start(va, fmt); + ret = vfscanf(f, fmt, va); + va_end(va); + + if (ret == EOF) { + tst_brkm(TBROK, cleanup_fn, + "The FILE '%s' ended prematurely at %s:%d", + path, file, lineno); + return; + } + + if (ret != exp_convs) { + tst_brkm(TBROK, cleanup_fn, + "Expected %i conversions got %i FILE '%s' at %s:%d", + exp_convs, ret, path, file, lineno); + return; + } + + if (fclose(f)) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to close FILE '%s' at %s:%d", + path, file, lineno); + return; + } +} + + +/* + * Try to parse each line from file specified by 'path' according + * to scanf format 'fmt'. If all fields could be parsed, stop and + * return 0, otherwise continue or return 1 if EOF is reached. + */ +int file_lines_scanf(const char *file, const int lineno, + void (*cleanup_fn)(void), int strict, + const char *path, const char *fmt, ...) +{ + FILE *fp; + int ret = 0; + int arg_count = 0; + char line[BUFSIZ]; + va_list ap; + + if (!fmt) { + tst_brkm(TBROK, cleanup_fn, "pattern is NULL, %s:%d", + file, lineno); + return 1; + } + + fp = fopen(path, "r"); + if (fp == NULL) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to open FILE '%s' for reading at %s:%d", + path, file, lineno); + return 1; + } + + arg_count = count_scanf_conversions(fmt); + + while (fgets(line, BUFSIZ, fp) != NULL) { + va_start(ap, fmt); + ret = vsscanf(line, fmt, ap); + va_end(ap); + + if (ret == arg_count) + break; + } + fclose(fp); + + if (strict && ret != arg_count) { + tst_brkm(TBROK, cleanup_fn, "Expected %i conversions got %i" + " FILE '%s' at %s:%d", arg_count, ret, path, file, lineno); + return 1; + } + + return !(ret == arg_count); +} + +int file_printf(const char *file, const int lineno, + const char *path, const char *fmt, ...) +{ + va_list va; + FILE *f; + + f = fopen(path, "w"); + + if (f == NULL) { + tst_resm(TWARN, + "Failed to open FILE '%s' at %s:%d", + path, file, lineno); + return 1; + } + + va_start(va, fmt); + + if (vfprintf(f, fmt, va) < 0) { + tst_resm(TWARN, + "Failed to print to FILE '%s' at %s:%d", + path, file, lineno); + goto err; + } + + va_end(va); + + if (fclose(f)) { + tst_resm(TWARN, + "Failed to close FILE '%s' at %s:%d", + path, file, lineno); + return 1; + } + + return 0; + +err: + if (fclose(f)) { + tst_resm(TWARN, + "Failed to close FILE '%s' at %s:%d", + path, file, lineno); + } + return 1; +} + +void safe_file_printf(const char *file, const int lineno, + void (*cleanup_fn) (void), + const char *path, const char *fmt, ...) +{ + va_list va; + FILE *f; + + f = fopen(path, "w"); + + if (f == NULL) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to open FILE '%s' for writing at %s:%d", + path, file, lineno); + return; + } + + va_start(va, fmt); + + if (vfprintf(f, fmt, va) < 0) { + tst_brkm(TBROK, cleanup_fn, + "Failed to print to FILE '%s' at %s:%d", + path, file, lineno); + return; + } + + va_end(va); + + if (fclose(f)) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to close FILE '%s' at %s:%d", + path, file, lineno); + return; + } +} + +//TODO: C implementation? better error condition reporting? +void safe_cp(const char *file, const int lineno, + void (*cleanup_fn) (void), const char *src, const char *dst) +{ + size_t len = strlen(src) + strlen(dst) + 16; + char buf[len]; + int ret; + + snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst); + + ret = system(buf); + + if (ret) { + tst_brkm(TBROK, cleanup_fn, + "Failed to copy '%s' to '%s' at %s:%d", + src, dst, file, lineno); + } +} + +#ifndef HAVE_UTIMENSAT + +static void set_time(struct timeval *res, const struct timespec *src, + long cur_tv_sec, long cur_tv_usec) +{ + switch (src->tv_nsec) { + case UTIME_NOW: + break; + case UTIME_OMIT: + res->tv_sec = cur_tv_sec; + res->tv_usec = cur_tv_usec; + break; + default: + res->tv_sec = src->tv_sec; + res->tv_usec = src->tv_nsec / 1000; + } +} + +#endif + +void safe_touch(const char *file, const int lineno, + void (*cleanup_fn)(void), + const char *pathname, + mode_t mode, const struct timespec times[2]) +{ + int ret; + mode_t defmode; + + defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + ret = open(pathname, O_CREAT | O_WRONLY, defmode); + if (ret == -1) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to open file '%s' at %s:%d", + pathname, file, lineno); + return; + } + + ret = close(ret); + if (ret == -1) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to close file '%s' at %s:%d", + pathname, file, lineno); + return; + } + + if (mode != 0) { + ret = chmod(pathname, mode); + if (ret == -1) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to chmod file '%s' at %s:%d", + pathname, file, lineno); + return; + } + } + + +#ifdef HAVE_UTIMENSAT + ret = utimensat(AT_FDCWD, pathname, times, 0); +#else + if (times == NULL) { + ret = utimes(pathname, NULL); + } else { + struct stat sb; + struct timeval cotimes[2]; + + ret = stat(pathname, &sb); + if (ret == -1) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to stat file '%s' at %s:%d", + pathname, file, lineno); + return; + } + + ret = gettimeofday(cotimes, NULL); + if (ret == -1) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to gettimeofday() at %s:%d", + file, lineno); + return; + } + + cotimes[1] = cotimes[0]; + + set_time(cotimes, times, + sb.st_atime, sb.st_atim.tv_nsec / 1000); + set_time(cotimes + 1, times + 1, + sb.st_mtime, sb.st_mtim.tv_nsec / 1000); + + ret = utimes(pathname, cotimes); + } +#endif + if (ret == -1) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "Failed to update the access/modification time on file" + " '%s' at %s:%d", pathname, file, lineno); + } +} |