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