You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
8.6 KiB
423 lines
8.6 KiB
/*
|
|
* 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);
|
|
}
|
|
}
|