• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 Cyril Hrubis chrubis@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 
tst_count_scanf_conversions(const char * fmt)37 int tst_count_scanf_conversions(const char *fmt)
38 {
39 	unsigned int cnt = 0;
40 	int flag = 0;
41 
42 	while (*fmt) {
43 		switch (*fmt) {
44 		case '%':
45 			if (flag) {
46 				cnt--;
47 				flag = 0;
48 			} else {
49 				flag = 1;
50 				cnt++;
51 			}
52 			break;
53 		case '*':
54 			if (flag) {
55 				cnt--;
56 				flag = 0;
57 			}
58 			break;
59 		default:
60 			flag = 0;
61 		}
62 
63 		fmt++;
64 	}
65 
66 	return cnt;
67 }
68 
file_scanf(const char * file,const int lineno,const char * path,const char * fmt,...)69 int file_scanf(const char *file, const int lineno,
70 		     const char *path, const char *fmt, ...)
71 {
72 	va_list va;
73 	FILE *f;
74 	int exp_convs, ret;
75 
76 	f = fopen(path, "r");
77 
78 	if (f == NULL) {
79 		tst_resm_(file, lineno, TINFO, "Failed to open FILE '%s'",
80 			path);
81 		return 1;
82 	}
83 
84 	exp_convs = tst_count_scanf_conversions(fmt);
85 
86 	va_start(va, fmt);
87 	ret = vfscanf(f, fmt, va);
88 	va_end(va);
89 
90 	if (ret == EOF) {
91 		tst_resm_(file, lineno, TINFO,
92 			"The FILE '%s' ended prematurely", path);
93 		goto err;
94 	}
95 
96 	if (ret != exp_convs) {
97 		tst_resm_(file, lineno, TINFO,
98 			"Expected %i conversions got %i FILE '%s'",
99 			exp_convs, ret, path);
100 		goto err;
101 	}
102 
103 	if (fclose(f)) {
104 		tst_resm_(file, lineno, TINFO, "Failed to close FILE '%s'",
105 			path);
106 		return 1;
107 	}
108 
109 	return 0;
110 
111 err:
112 	if (fclose(f)) {
113 		tst_resm_(file, lineno, TINFO, "Failed to close FILE '%s'",
114 			path);
115 	}
116 
117 	return 1;
118 }
119 
safe_file_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)120 void safe_file_scanf(const char *file, const int lineno,
121 		     void (*cleanup_fn) (void),
122 		     const char *path, const char *fmt, ...)
123 {
124 	va_list va;
125 	FILE *f;
126 	int exp_convs, ret;
127 
128 	f = fopen(path, "r");
129 
130 	if (f == NULL) {
131 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
132 			"Failed to open FILE '%s' for reading", path);
133 		return;
134 	}
135 
136 	exp_convs = tst_count_scanf_conversions(fmt);
137 
138 	va_start(va, fmt);
139 	ret = vfscanf(f, fmt, va);
140 	va_end(va);
141 
142 	if (ret == EOF) {
143 		tst_brkm_(file, lineno, TBROK, cleanup_fn,
144 			"The FILE '%s' ended prematurely", path);
145 		return;
146 	}
147 
148 	if (ret != exp_convs) {
149 		tst_brkm_(file, lineno, TBROK, cleanup_fn,
150 			"Expected %i conversions got %i FILE '%s'",
151 			exp_convs, ret, path);
152 		return;
153 	}
154 
155 	if (fclose(f)) {
156 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
157 			"Failed to close FILE '%s'", path);
158 		return;
159 	}
160 }
161 
162 
163 /*
164  * Try to parse each line from file specified by 'path' according
165  * to scanf format 'fmt'. If all fields could be parsed, stop and
166  * return 0, otherwise continue or return 1 if EOF is reached.
167  */
file_lines_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),int strict,const char * path,const char * fmt,...)168 int file_lines_scanf(const char *file, const int lineno,
169 		     void (*cleanup_fn)(void), int strict,
170 		     const char *path, const char *fmt, ...)
171 {
172 	FILE *fp;
173 	int ret = 0;
174 	int arg_count = 0;
175 	char line[BUFSIZ];
176 	va_list ap;
177 
178 	if (!fmt) {
179 		tst_brkm_(file, lineno, TBROK, cleanup_fn, "pattern is NULL");
180 		return 1;
181 	}
182 
183 	fp = fopen(path, "r");
184 	if (fp == NULL) {
185 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
186 			"Failed to open FILE '%s' for reading", path);
187 		return 1;
188 	}
189 
190 	arg_count = tst_count_scanf_conversions(fmt);
191 
192 	while (fgets(line, BUFSIZ, fp) != NULL) {
193 		va_start(ap, fmt);
194 		ret = vsscanf(line, fmt, ap);
195 		va_end(ap);
196 
197 		if (ret == arg_count)
198 			break;
199 	}
200 	fclose(fp);
201 
202 	if (strict && ret != arg_count) {
203 		tst_brkm_(file, lineno, TBROK, cleanup_fn,
204 			"Expected %i conversions got %i FILE '%s'",
205 			arg_count, ret, path);
206 		return 1;
207 	}
208 
209 	return !(ret == arg_count);
210 }
211 
file_printf(const char * file,const int lineno,const char * path,const char * fmt,...)212 int file_printf(const char *file, const int lineno,
213 		      const char *path, const char *fmt, ...)
214 {
215 	va_list va;
216 	FILE *f;
217 
218 	f = fopen(path, "w");
219 
220 	if (f == NULL) {
221 		tst_resm_(file, lineno, TINFO, "Failed to open FILE '%s'",
222 			path);
223 		return 1;
224 	}
225 
226 	va_start(va, fmt);
227 
228 	if (vfprintf(f, fmt, va) < 0) {
229 		tst_resm_(file, lineno, TINFO, "Failed to print to FILE '%s'",
230 			path);
231 		goto err;
232 	}
233 
234 	va_end(va);
235 
236 	if (fclose(f)) {
237 		tst_resm_(file, lineno, TINFO, "Failed to close FILE '%s'",
238 			path);
239 		return 1;
240 	}
241 
242 	return 0;
243 
244 err:
245 	if (fclose(f)) {
246 		tst_resm_(file, lineno, TINFO, "Failed to close FILE '%s'",
247 			path);
248 	}
249 
250 	return 1;
251 }
252 
safe_file_vprintf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,va_list va)253 static void safe_file_vprintf(const char *file, const int lineno,
254 	void (*cleanup_fn)(void), const char *path, const char *fmt,
255 	va_list va)
256 {
257 	FILE *f;
258 
259 	f = fopen(path, "w");
260 
261 	if (f == NULL) {
262 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
263 			"Failed to open FILE '%s' for writing", path);
264 		return;
265 	}
266 
267 	if (vfprintf(f, fmt, va) < 0) {
268 		tst_brkm_(file, lineno, TBROK, cleanup_fn,
269 			"Failed to print to FILE '%s'", path);
270 		return;
271 	}
272 
273 	if (fclose(f)) {
274 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
275 			"Failed to close FILE '%s'", path);
276 		return;
277 	}
278 }
279 
safe_file_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)280 void safe_file_printf(const char *file, const int lineno,
281 	void (*cleanup_fn)(void), const char *path, const char *fmt, ...)
282 {
283 	va_list va;
284 
285 	va_start(va, fmt);
286 	safe_file_vprintf(file, lineno, cleanup_fn, path, fmt, va);
287 	va_end(va);
288 }
289 
safe_try_file_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)290 void safe_try_file_printf(const char *file, const int lineno,
291 	void (*cleanup_fn)(void), const char *path, const char *fmt, ...)
292 {
293 	va_list va;
294 
295 	if (access(path, F_OK))
296 		return;
297 
298 	va_start(va, fmt);
299 	safe_file_vprintf(file, lineno, cleanup_fn, path, fmt, va);
300 	va_end(va);
301 }
302 
303 //TODO: C implementation? better error condition reporting?
safe_cp(const char * file,const int lineno,void (* cleanup_fn)(void),const char * src,const char * dst)304 int safe_cp(const char *file, const int lineno,
305 	     void (*cleanup_fn) (void), const char *src, const char *dst)
306 {
307 	size_t len = strlen(src) + strlen(dst) + 16;
308 	char buf[len];
309 	int ret;
310 
311 	snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
312 
313 	ret = system(buf);
314 
315 	if (ret) {
316 		tst_brkm_(file, lineno, TBROK, cleanup_fn,
317 			"Failed to copy '%s' to '%s'", src, dst);
318 		return ret;
319 	}
320 
321 	return 0;
322 }
323 
324 #ifndef HAVE_UTIMENSAT
325 
set_time(struct timeval * res,const struct timespec * src,long cur_tv_sec,long cur_tv_usec)326 static void set_time(struct timeval *res, const struct timespec *src,
327 			long cur_tv_sec, long cur_tv_usec)
328 {
329 	switch (src->tv_nsec) {
330 	case UTIME_NOW:
331 	break;
332 	case UTIME_OMIT:
333 		res->tv_sec = cur_tv_sec;
334 		res->tv_usec = cur_tv_usec;
335 	break;
336 	default:
337 		res->tv_sec = src->tv_sec;
338 		res->tv_usec = src->tv_nsec / 1000;
339 	}
340 }
341 
342 #endif
343 
safe_touch(const char * file,const int lineno,void (* cleanup_fn)(void),const char * pathname,mode_t mode,const struct timespec times[2])344 int safe_touch(const char *file, const int lineno,
345 		void (*cleanup_fn)(void),
346 		const char *pathname,
347 		mode_t mode, const struct timespec times[2])
348 {
349 	int ret;
350 	mode_t defmode;
351 
352 	defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
353 
354 	ret = open(pathname, O_CREAT | O_WRONLY, defmode);
355 
356 	if (ret == -1) {
357 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
358 			"Failed to open file '%s'", pathname);
359 		return ret;
360 	} else if (ret < 0) {
361 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
362 			"Invalid open(%s) return value %d", pathname, ret);
363 		return ret;
364 	}
365 
366 	ret = close(ret);
367 
368 	if (ret == -1) {
369 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
370 			"Failed to close file '%s'", pathname);
371 		return ret;
372 	} else if (ret) {
373 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
374 			"Invalid close('%s') return value %d", pathname, ret);
375 		return ret;
376 	}
377 
378 	if (mode != 0) {
379 		ret = chmod(pathname, mode);
380 
381 		if (ret == -1) {
382 			tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
383 				"Failed to chmod file '%s'", pathname);
384 			return ret;
385 		} else if (ret) {
386 			tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
387 				"Invalid chmod('%s') return value %d",
388 				pathname, ret);
389 			return ret;
390 		}
391 	}
392 
393 
394 #ifdef HAVE_UTIMENSAT
395 	ret = utimensat(AT_FDCWD, pathname, times, 0);
396 #else
397 	if (times == NULL) {
398 		ret = utimes(pathname, NULL);
399 	} else {
400 		struct stat sb;
401 		struct timeval cotimes[2];
402 
403 		ret = stat(pathname, &sb);
404 
405 		if (ret == -1) {
406 			tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
407 				"Failed to stat file '%s'", pathname);
408 			return ret;
409 		} else if (ret) {
410 			tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
411 				"Invalid stat('%s') return value %d",
412 				pathname, ret);
413 			return ret;
414 		}
415 
416 		ret = gettimeofday(cotimes, NULL);
417 
418 		if (ret == -1) {
419 			tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
420 				"Failed to gettimeofday()");
421 			return ret;
422 		} else if (ret) {
423 			tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
424 				"Invalid gettimeofday() return value %d", ret);
425 			return ret;
426 		}
427 
428 		cotimes[1] = cotimes[0];
429 
430 		set_time(cotimes, times,
431 			sb.st_atime, sb.st_atim.tv_nsec / 1000);
432 		set_time(cotimes + 1, times + 1,
433 			sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
434 
435 		ret = utimes(pathname, cotimes);
436 	}
437 #endif
438 	if (ret == -1) {
439 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
440 			"Failed to update the access/modification time on file '%s'",
441 			pathname);
442 	} else if (ret) {
443 		tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
444 #ifdef HAVE_UTIMENSAT
445 			"Invalid utimensat('%s') return value %d",
446 #else
447 			"Invalid utimes('%s') return value %d",
448 #endif
449 			pathname, ret);
450 	}
451 
452 	return ret;
453 }
454