/* * 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 #include #include #include #include #include #include #include #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); } }