| /* |
| * 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" |
| " at %s:%d", arg_count, ret, 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); |
| } |
| } |