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