• 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 
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  */
count_scanf_conversions(const char * fmt)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 
file_scanf(const char * file,const int lineno,const char * path,const char * fmt,...)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 
safe_file_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)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  */
file_lines_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),int strict,const char * path,const char * fmt,...)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 
file_printf(const char * file,const int lineno,const char * path,const char * fmt,...)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 
safe_file_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)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?
safe_cp(const char * file,const int lineno,void (* cleanup_fn)(void),const char * src,const char * dst)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 
set_time(struct timeval * res,const struct timespec * src,long cur_tv_sec,long cur_tv_usec)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 
safe_touch(const char * file,const int lineno,void (* cleanup_fn)(void),const char * pathname,mode_t mode,const struct timespec times[2])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