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