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
tst_count_scanf_conversions(const char * fmt)37 int 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
file_scanf(const char * file,const int lineno,const char * path,const char * fmt,...)69 int 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, TWARN, "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, TWARN,
92 "The FILE '%s' ended prematurely", path);
93 goto err;
94 }
95
96 if (ret != exp_convs) {
97 tst_resm_(file, lineno, TWARN,
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, TWARN, "Failed to close FILE '%s'",
105 path);
106 return 1;
107 }
108
109 return 0;
110
111 err:
112 if (fclose(f)) {
113 tst_resm_(file, lineno, TWARN, "Failed to close FILE '%s'",
114 path);
115 }
116
117 return 1;
118 }
119
safe_file_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)120 void 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 */
file_lines_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),int strict,const char * path,const char * fmt,...)168 int 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
file_printf(const char * file,const int lineno,const char * path,const char * fmt,...)212 int 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, TWARN, "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, TWARN, "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, TWARN, "Failed to close FILE '%s'",
238 path);
239 return 1;
240 }
241
242 return 0;
243
244 err:
245 if (fclose(f)) {
246 tst_resm_(file, lineno, TWARN, "Failed to close FILE '%s'",
247 path);
248 }
249
250 return 1;
251 }
252
safe_file_vprintf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,va_list va)253 static 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
safe_file_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)280 void 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
safe_try_file_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)290 void 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?
safe_cp(const char * file,const int lineno,void (* cleanup_fn)(void),const char * src,const char * dst)304 int 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
set_time(struct timeval * res,const struct timespec * src,long cur_tv_sec,long cur_tv_usec)326 static 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
safe_touch(const char * file,const int lineno,void (* cleanup_fn)(void),const char * pathname,mode_t mode,const struct timespec times[2])344 int 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