Home | History | Annotate | Download | only in lib
      1 /*
      2  * Copyright (C) 2012 Cyril Hrubis chrubis (at) suse.cz
      3  *
      4  * This program is free software; you can redistribute it and/or modify it
      5  * under the terms of version 2 of the GNU General Public License as
      6  * published by the Free Software Foundation.
      7  *
      8  * This program is distributed in the hope that it would be useful, but
      9  * WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     11  *
     12  * Further, this software is distributed without any warranty that it is
     13  * free of the rightful claim of any third person regarding infringement
     14  * or the like.  Any license provided herein, whether implied or
     15  * otherwise, applies only to this software file.  Patent licenses, if
     16  * any, provided herein do not apply to combinations of this program with
     17  * other software, or any other product whatsoever.
     18  *
     19  * You should have received a copy of the GNU General Public License along
     20  * with this program; if not, write the Free Software Foundation, Inc.,
     21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     22  */
     23 
     24 #include "config.h"
     25 #include <stdarg.h>
     26 #include <stdio.h>
     27 #include <sys/time.h>
     28 #include <sys/types.h>
     29 #include <sys/stat.h>
     30 #include <fcntl.h>
     31 #include <unistd.h>
     32 #include <utime.h>
     33 
     34 #include "test.h"
     35 #include "safe_file_ops_fn.h"
     36 
     37 /*
     38  * Count number of expected assigned conversions. Any conversion starts with '%'.
     39  * The '%%' matches % and no assignment is done. The %*x matches as x would do but
     40  * the assignment is suppressed.
     41  *
     42  * NOTE: This is not 100% correct for complex scanf strings, but will do for
     43  *       all of our intended usage.
     44  */
     45 static int count_scanf_conversions(const char *fmt)
     46 {
     47 	unsigned int cnt = 0;
     48 	int flag = 0;
     49 
     50 	while (*fmt) {
     51 		switch (*fmt) {
     52 		case '%':
     53 			if (flag) {
     54 				cnt--;
     55 				flag = 0;
     56 			} else {
     57 				flag = 1;
     58 				cnt++;
     59 			}
     60 			break;
     61 		case '*':
     62 			if (flag) {
     63 				cnt--;
     64 				flag = 0;
     65 			}
     66 			break;
     67 		default:
     68 			flag = 0;
     69 		}
     70 
     71 		fmt++;
     72 	}
     73 
     74 	return cnt;
     75 }
     76 
     77 int file_scanf(const char *file, const int lineno,
     78 		     const char *path, const char *fmt, ...)
     79 {
     80 	va_list va;
     81 	FILE *f;
     82 	int exp_convs, ret;
     83 
     84 	f = fopen(path, "r");
     85 
     86 	if (f == NULL) {
     87 		tst_resm(TWARN,
     88 			"Failed to open FILE '%s' at %s:%d",
     89 			 path, file, lineno);
     90 		return 1;
     91 	}
     92 
     93 	exp_convs = count_scanf_conversions(fmt);
     94 
     95 	va_start(va, fmt);
     96 	ret = vfscanf(f, fmt, va);
     97 	va_end(va);
     98 
     99 	if (ret == EOF) {
    100 		tst_resm(TWARN,
    101 			 "The FILE '%s' ended prematurely at %s:%d",
    102 			 path, file, lineno);
    103 		goto err;
    104 	}
    105 
    106 	if (ret != exp_convs) {
    107 		tst_resm(TWARN,
    108 			"Expected %i conversions got %i FILE '%s' at %s:%d",
    109 			 exp_convs, ret, path, file, lineno);
    110 		goto err;
    111 	}
    112 
    113 	if (fclose(f)) {
    114 		tst_resm(TWARN,
    115 			 "Failed to close FILE '%s' at %s:%d",
    116 			 path, file, lineno);
    117 		return 1;
    118 	}
    119 
    120 	return 0;
    121 
    122 err:
    123 	if (fclose(f)) {
    124 		tst_resm(TWARN,
    125 			 "Failed to close FILE '%s' at %s:%d",
    126 			 path, file, lineno);
    127 	}
    128 	return 1;
    129 }
    130 
    131 void safe_file_scanf(const char *file, const int lineno,
    132 		     void (*cleanup_fn) (void),
    133 		     const char *path, const char *fmt, ...)
    134 {
    135 	va_list va;
    136 	FILE *f;
    137 	int exp_convs, ret;
    138 
    139 	f = fopen(path, "r");
    140 
    141 	if (f == NULL) {
    142 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    143 			 "Failed to open FILE '%s' for reading at %s:%d",
    144 			 path, file, lineno);
    145 		return;
    146 	}
    147 
    148 	exp_convs = count_scanf_conversions(fmt);
    149 
    150 	va_start(va, fmt);
    151 	ret = vfscanf(f, fmt, va);
    152 	va_end(va);
    153 
    154 	if (ret == EOF) {
    155 		tst_brkm(TBROK, cleanup_fn,
    156 			 "The FILE '%s' ended prematurely at %s:%d",
    157 			 path, file, lineno);
    158 		return;
    159 	}
    160 
    161 	if (ret != exp_convs) {
    162 		tst_brkm(TBROK, cleanup_fn,
    163 			 "Expected %i conversions got %i FILE '%s' at %s:%d",
    164 			 exp_convs, ret, path, file, lineno);
    165 		return;
    166 	}
    167 
    168 	if (fclose(f)) {
    169 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    170 			 "Failed to close FILE '%s' at %s:%d",
    171 			 path, file, lineno);
    172 		return;
    173 	}
    174 }
    175 
    176 
    177 /*
    178  * Try to parse each line from file specified by 'path' according
    179  * to scanf format 'fmt'. If all fields could be parsed, stop and
    180  * return 0, otherwise continue or return 1 if EOF is reached.
    181  */
    182 int file_lines_scanf(const char *file, const int lineno,
    183 		     void (*cleanup_fn)(void), int strict,
    184 		     const char *path, const char *fmt, ...)
    185 {
    186 	FILE *fp;
    187 	int ret = 0;
    188 	int arg_count = 0;
    189 	char line[BUFSIZ];
    190 	va_list ap;
    191 
    192 	if (!fmt) {
    193 		tst_brkm(TBROK, cleanup_fn, "pattern is NULL, %s:%d",
    194 			file, lineno);
    195 		return 1;
    196 	}
    197 
    198 	fp = fopen(path, "r");
    199 	if (fp == NULL) {
    200 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    201 			"Failed to open FILE '%s' for reading at %s:%d",
    202 			path, file, lineno);
    203 		return 1;
    204 	}
    205 
    206 	arg_count = count_scanf_conversions(fmt);
    207 
    208 	while (fgets(line, BUFSIZ, fp) != NULL) {
    209 		va_start(ap, fmt);
    210 		ret = vsscanf(line, fmt, ap);
    211 		va_end(ap);
    212 
    213 		if (ret == arg_count)
    214 			break;
    215 	}
    216 	fclose(fp);
    217 
    218 	if (strict && ret != arg_count) {
    219 		tst_brkm(TBROK, cleanup_fn, "Expected %i conversions got %i"
    220 			" at %s:%d", arg_count, ret, file, lineno);
    221 		return 1;
    222 	}
    223 
    224 	return !(ret == arg_count);
    225 }
    226 
    227 int file_printf(const char *file, const int lineno,
    228 		      const char *path, const char *fmt, ...)
    229 {
    230 	va_list va;
    231 	FILE *f;
    232 
    233 	f = fopen(path, "w");
    234 
    235 	if (f == NULL) {
    236 		tst_resm(TWARN,
    237 			 "Failed to open FILE '%s' at %s:%d",
    238 			 path, file, lineno);
    239 		return 1;
    240 	}
    241 
    242 	va_start(va, fmt);
    243 
    244 	if (vfprintf(f, fmt, va) < 0) {
    245 		tst_resm(TWARN,
    246 			"Failed to print to FILE '%s' at %s:%d",
    247 			 path, file, lineno);
    248 		goto err;
    249 	}
    250 
    251 	va_end(va);
    252 
    253 	if (fclose(f)) {
    254 		tst_resm(TWARN,
    255 			 "Failed to close FILE '%s' at %s:%d",
    256 			 path, file, lineno);
    257 		return 1;
    258 	}
    259 
    260 	return 0;
    261 
    262 err:
    263 	if (fclose(f)) {
    264 		tst_resm(TWARN,
    265 			 "Failed to close FILE '%s' at %s:%d",
    266 			 path, file, lineno);
    267 	}
    268 	return 1;
    269 }
    270 
    271 void safe_file_printf(const char *file, const int lineno,
    272 		      void (*cleanup_fn) (void),
    273 		      const char *path, const char *fmt, ...)
    274 {
    275 	va_list va;
    276 	FILE *f;
    277 
    278 	f = fopen(path, "w");
    279 
    280 	if (f == NULL) {
    281 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    282 			 "Failed to open FILE '%s' for writing at %s:%d",
    283 			 path, file, lineno);
    284 		return;
    285 	}
    286 
    287 	va_start(va, fmt);
    288 
    289 	if (vfprintf(f, fmt, va) < 0) {
    290 		tst_brkm(TBROK, cleanup_fn,
    291 			 "Failed to print to FILE '%s' at %s:%d",
    292 			 path, file, lineno);
    293 		return;
    294 	}
    295 
    296 	va_end(va);
    297 
    298 	if (fclose(f)) {
    299 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    300 			 "Failed to close FILE '%s' at %s:%d",
    301 			 path, file, lineno);
    302 		return;
    303 	}
    304 }
    305 
    306 //TODO: C implementation? better error condition reporting?
    307 void safe_cp(const char *file, const int lineno,
    308 	     void (*cleanup_fn) (void), const char *src, const char *dst)
    309 {
    310 	size_t len = strlen(src) + strlen(dst) + 16;
    311 	char buf[len];
    312 	int ret;
    313 
    314 	snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
    315 
    316 	ret = system(buf);
    317 
    318 	if (ret) {
    319 		tst_brkm(TBROK, cleanup_fn,
    320 			 "Failed to copy '%s' to '%s' at %s:%d",
    321 			 src, dst, file, lineno);
    322 	}
    323 }
    324 
    325 #ifndef HAVE_UTIMENSAT
    326 
    327 static void set_time(struct timeval *res, const struct timespec *src,
    328 			long cur_tv_sec, long cur_tv_usec)
    329 {
    330 	switch (src->tv_nsec) {
    331 	case UTIME_NOW:
    332 	break;
    333 	case UTIME_OMIT:
    334 		res->tv_sec = cur_tv_sec;
    335 		res->tv_usec = cur_tv_usec;
    336 	break;
    337 	default:
    338 		res->tv_sec = src->tv_sec;
    339 		res->tv_usec = src->tv_nsec / 1000;
    340 	}
    341 }
    342 
    343 #endif
    344 
    345 void safe_touch(const char *file, const int lineno,
    346 		void (*cleanup_fn)(void),
    347 		const char *pathname,
    348 		mode_t mode, const struct timespec times[2])
    349 {
    350 	int ret;
    351 	mode_t defmode;
    352 
    353 	defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
    354 
    355 	ret = open(pathname, O_CREAT | O_WRONLY, defmode);
    356 	if (ret == -1) {
    357 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    358 			"Failed to open file '%s' at %s:%d",
    359 			pathname, file, lineno);
    360 		return;
    361 	}
    362 
    363 	ret = close(ret);
    364 	if (ret == -1) {
    365 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    366 			"Failed to close file '%s' at %s:%d",
    367 			pathname, file, lineno);
    368 		return;
    369 	}
    370 
    371 	if (mode != 0) {
    372 		ret = chmod(pathname, mode);
    373 		if (ret == -1) {
    374 			tst_brkm(TBROK | TERRNO, cleanup_fn,
    375 				"Failed to chmod file '%s' at %s:%d",
    376 				pathname, file, lineno);
    377 			return;
    378 		}
    379 	}
    380 
    381 
    382 #ifdef HAVE_UTIMENSAT
    383 	ret = utimensat(AT_FDCWD, pathname, times, 0);
    384 #else
    385 	if (times == NULL) {
    386 		ret = utimes(pathname, NULL);
    387 	} else {
    388 		struct stat sb;
    389 		struct timeval cotimes[2];
    390 
    391 		ret = stat(pathname, &sb);
    392 		if (ret == -1) {
    393 			tst_brkm(TBROK | TERRNO, cleanup_fn,
    394 				"Failed to stat file '%s' at %s:%d",
    395 				pathname, file, lineno);
    396 			return;
    397 		}
    398 
    399 		ret = gettimeofday(cotimes, NULL);
    400 		if (ret == -1) {
    401 			tst_brkm(TBROK | TERRNO, cleanup_fn,
    402 				"Failed to gettimeofday() at %s:%d",
    403 				file, lineno);
    404 			return;
    405 		}
    406 
    407 		cotimes[1] = cotimes[0];
    408 
    409 		set_time(cotimes, times,
    410 			sb.st_atime, sb.st_atim.tv_nsec / 1000);
    411 		set_time(cotimes + 1, times + 1,
    412 			sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
    413 
    414 		ret = utimes(pathname, cotimes);
    415 	}
    416 #endif
    417 	if (ret == -1) {
    418 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    419 			"Failed to update the access/modification time on file"
    420 			" '%s' at %s:%d", pathname, file, lineno);
    421 	}
    422 }
    423