xref: /third_party/ltp/lib/safe_file_ops.c (revision f08c3bdf)
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
37int 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
69int 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
111err:
112	if (fclose(f)) {
113		tst_resm_(file, lineno, TINFO, "Failed to close FILE '%s'",
114			path);
115	}
116
117	return 1;
118}
119
120void 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 */
168int 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
212int 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
244err:
245	if (fclose(f)) {
246		tst_resm_(file, lineno, TINFO, "Failed to close FILE '%s'",
247			path);
248	}
249
250	return 1;
251}
252
253static 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
280void 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
290void 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?
304int 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
326static 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
344int 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