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