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