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 	}
    146 
    147 	exp_convs = count_scanf_conversions(fmt);
    148 
    149 	va_start(va, fmt);
    150 	ret = vfscanf(f, fmt, va);
    151 	va_end(va);
    152 
    153 	if (ret == EOF) {
    154 		tst_brkm(TBROK, cleanup_fn,
    155 			 "The FILE '%s' ended prematurely at %s:%d",
    156 			 path, file, lineno);
    157 	}
    158 
    159 	if (ret != exp_convs) {
    160 		tst_brkm(TBROK, cleanup_fn,
    161 			 "Expected %i conversions got %i FILE '%s' at %s:%d",
    162 			 exp_convs, ret, path, file, lineno);
    163 	}
    164 
    165 	if (fclose(f)) {
    166 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    167 			 "Failed to close FILE '%s' at %s:%d",
    168 			 path, file, lineno);
    169 	}
    170 }
    171 
    172 int file_printf(const char *file, const int lineno,
    173 		      const char *path, const char *fmt, ...)
    174 {
    175 	va_list va;
    176 	FILE *f;
    177 
    178 	f = fopen(path, "w");
    179 
    180 	if (f == NULL) {
    181 		tst_resm(TWARN,
    182 			 "Failed to open FILE '%s' at %s:%d",
    183 			 path, file, lineno);
    184 		return 1;
    185 	}
    186 
    187 	va_start(va, fmt);
    188 
    189 	if (vfprintf(f, fmt, va) < 0) {
    190 		tst_resm(TWARN,
    191 			"Failed to print to FILE '%s' at %s:%d",
    192 			 path, file, lineno);
    193 		goto err;
    194 	}
    195 
    196 	va_end(va);
    197 
    198 	if (fclose(f)) {
    199 		tst_resm(TWARN,
    200 			 "Failed to close FILE '%s' at %s:%d",
    201 			 path, file, lineno);
    202 		return 1;
    203 	}
    204 
    205 	return 0;
    206 
    207 err:
    208 	if (fclose(f)) {
    209 		tst_resm(TWARN,
    210 			 "Failed to close FILE '%s' at %s:%d",
    211 			 path, file, lineno);
    212 	}
    213 	return 1;
    214 }
    215 
    216 void safe_file_printf(const char *file, const int lineno,
    217 		      void (*cleanup_fn) (void),
    218 		      const char *path, const char *fmt, ...)
    219 {
    220 	va_list va;
    221 	FILE *f;
    222 
    223 	f = fopen(path, "w");
    224 
    225 	if (f == NULL) {
    226 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    227 			 "Failed to open FILE '%s' for writing at %s:%d",
    228 			 path, file, lineno);
    229 	}
    230 
    231 	va_start(va, fmt);
    232 
    233 	if (vfprintf(f, fmt, va) < 0) {
    234 		tst_brkm(TBROK, cleanup_fn,
    235 			 "Failed to print to FILE '%s' at %s:%d",
    236 			 path, file, lineno);
    237 	}
    238 
    239 	va_end(va);
    240 
    241 	if (fclose(f)) {
    242 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    243 			 "Failed to close FILE '%s' at %s:%d",
    244 			 path, file, lineno);
    245 	}
    246 }
    247 
    248 //TODO: C implementation? better error condition reporting?
    249 void safe_cp(const char *file, const int lineno,
    250 	     void (*cleanup_fn) (void), const char *src, const char *dst)
    251 {
    252 	size_t len = strlen(src) + strlen(dst) + 16;
    253 	char buf[len];
    254 	int ret;
    255 
    256 	snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
    257 
    258 	ret = system(buf);
    259 
    260 	if (ret) {
    261 		tst_brkm(TBROK, cleanup_fn,
    262 			 "Failed to copy '%s' to '%s' at %s:%d",
    263 			 src, dst, file, lineno);
    264 	}
    265 }
    266 
    267 #ifndef HAVE_UTIMENSAT
    268 
    269 static void set_time(struct timeval *res, const struct timespec *src,
    270 			long cur_tv_sec, long cur_tv_usec)
    271 {
    272 	switch (src->tv_nsec) {
    273 	case UTIME_NOW:
    274 	break;
    275 	case UTIME_OMIT:
    276 		res->tv_sec = cur_tv_sec;
    277 		res->tv_usec = cur_tv_usec;
    278 	break;
    279 	default:
    280 		res->tv_sec = src->tv_sec;
    281 		res->tv_usec = src->tv_nsec / 1000;
    282 	}
    283 }
    284 
    285 #endif
    286 
    287 void safe_touch(const char *file, const int lineno,
    288 		void (*cleanup_fn)(void),
    289 		const char *pathname,
    290 		mode_t mode, const struct timespec times[2])
    291 {
    292 	int ret;
    293 	mode_t defmode;
    294 
    295 	defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
    296 
    297 	ret = open(pathname, O_CREAT | O_WRONLY, defmode);
    298 	if (ret == -1)
    299 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    300 			"Failed to open file '%s' at %s:%d",
    301 			pathname, file, lineno);
    302 
    303 	ret = close(ret);
    304 	if (ret == -1)
    305 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    306 			"Failed to close file '%s' at %s:%d",
    307 			pathname, file, lineno);
    308 
    309 	if (mode != 0) {
    310 		ret = chmod(pathname, mode);
    311 		if (ret == -1)
    312 			tst_brkm(TBROK | TERRNO, cleanup_fn,
    313 				"Failed to chmod file '%s' at %s:%d",
    314 				pathname, file, lineno);
    315 	}
    316 
    317 
    318 #ifdef HAVE_UTIMENSAT
    319 	ret = utimensat(AT_FDCWD, pathname, times, 0);
    320 #else
    321 	if (times == NULL) {
    322 		ret = utimes(pathname, NULL);
    323 	} else {
    324 		struct stat sb;
    325 		struct timeval cotimes[2];
    326 
    327 		ret = stat(pathname, &sb);
    328 		if (ret == -1)
    329 			tst_brkm(TBROK | TERRNO, cleanup_fn,
    330 				"Failed to stat file '%s' at %s:%d",
    331 				pathname, file, lineno);
    332 
    333 		ret = gettimeofday(cotimes, NULL);
    334 		if (ret == -1)
    335 			tst_brkm(TBROK | TERRNO, cleanup_fn,
    336 				"Failed to gettimeofday() at %s:%d",
    337 				file, lineno);
    338 		cotimes[1] = cotimes[0];
    339 
    340 		set_time(cotimes, times,
    341 			sb.st_atime, sb.st_atim.tv_nsec / 1000);
    342 		set_time(cotimes + 1, times + 1,
    343 			sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
    344 
    345 		ret = utimes(pathname, cotimes);
    346 	}
    347 #endif
    348 	if (ret == -1) {
    349 		tst_brkm(TBROK | TERRNO, cleanup_fn,
    350 			"Failed to update the access/modification time on file"
    351 			" '%s' at %s:%d", pathname, file, lineno);
    352 	}
    353 }
    354