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