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_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)253 void safe_file_printf(const char *file, const int lineno,
254 void (*cleanup_fn) (void),
255 const char *path, const char *fmt, ...)
256 {
257 va_list va;
258 FILE *f;
259
260 f = fopen(path, "w");
261
262 if (f == NULL) {
263 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
264 "Failed to open FILE '%s' for writing", path);
265 return;
266 }
267
268 va_start(va, fmt);
269
270 if (vfprintf(f, fmt, va) < 0) {
271 tst_brkm_(file, lineno, TBROK, cleanup_fn,
272 "Failed to print to FILE '%s'", path);
273 return;
274 }
275
276 va_end(va);
277
278 if (fclose(f)) {
279 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
280 "Failed to close FILE '%s'", path);
281 return;
282 }
283 }
284
285 //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)286 int safe_cp(const char *file, const int lineno,
287 void (*cleanup_fn) (void), const char *src, const char *dst)
288 {
289 size_t len = strlen(src) + strlen(dst) + 16;
290 char buf[len];
291 int ret;
292
293 snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
294
295 ret = system(buf);
296
297 if (ret) {
298 tst_brkm_(file, lineno, TBROK, cleanup_fn,
299 "Failed to copy '%s' to '%s'", src, dst);
300 return ret;
301 }
302
303 return 0;
304 }
305
306 #ifndef HAVE_UTIMENSAT
307
set_time(struct timeval * res,const struct timespec * src,long cur_tv_sec,long cur_tv_usec)308 static void set_time(struct timeval *res, const struct timespec *src,
309 long cur_tv_sec, long cur_tv_usec)
310 {
311 switch (src->tv_nsec) {
312 case UTIME_NOW:
313 break;
314 case UTIME_OMIT:
315 res->tv_sec = cur_tv_sec;
316 res->tv_usec = cur_tv_usec;
317 break;
318 default:
319 res->tv_sec = src->tv_sec;
320 res->tv_usec = src->tv_nsec / 1000;
321 }
322 }
323
324 #endif
325
safe_touch(const char * file,const int lineno,void (* cleanup_fn)(void),const char * pathname,mode_t mode,const struct timespec times[2])326 int safe_touch(const char *file, const int lineno,
327 void (*cleanup_fn)(void),
328 const char *pathname,
329 mode_t mode, const struct timespec times[2])
330 {
331 int ret;
332 mode_t defmode;
333
334 defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
335
336 ret = open(pathname, O_CREAT | O_WRONLY, defmode);
337
338 if (ret == -1) {
339 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
340 "Failed to open file '%s'", pathname);
341 return ret;
342 } else if (ret < 0) {
343 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
344 "Invalid open(%s) return value %d", pathname, ret);
345 return ret;
346 }
347
348 ret = close(ret);
349
350 if (ret == -1) {
351 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
352 "Failed to close file '%s'", pathname);
353 return ret;
354 } else if (ret) {
355 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
356 "Invalid close('%s') return value %d", pathname, ret);
357 return ret;
358 }
359
360 if (mode != 0) {
361 ret = chmod(pathname, mode);
362
363 if (ret == -1) {
364 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
365 "Failed to chmod file '%s'", pathname);
366 return ret;
367 } else if (ret) {
368 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
369 "Invalid chmod('%s') return value %d",
370 pathname, ret);
371 return ret;
372 }
373 }
374
375
376 #ifdef HAVE_UTIMENSAT
377 ret = utimensat(AT_FDCWD, pathname, times, 0);
378 #else
379 if (times == NULL) {
380 ret = utimes(pathname, NULL);
381 } else {
382 struct stat sb;
383 struct timeval cotimes[2];
384
385 ret = stat(pathname, &sb);
386
387 if (ret == -1) {
388 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
389 "Failed to stat file '%s'", pathname);
390 return ret;
391 } else if (ret) {
392 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
393 "Invalid stat('%s') return value %d",
394 pathname, ret);
395 return ret;
396 }
397
398 ret = gettimeofday(cotimes, NULL);
399
400 if (ret == -1) {
401 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
402 "Failed to gettimeofday()");
403 return ret;
404 } else if (ret) {
405 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
406 "Invalid gettimeofday() return value %d", ret);
407 return ret;
408 }
409
410 cotimes[1] = cotimes[0];
411
412 set_time(cotimes, times,
413 sb.st_atime, sb.st_atim.tv_nsec / 1000);
414 set_time(cotimes + 1, times + 1,
415 sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
416
417 ret = utimes(pathname, cotimes);
418 }
419 #endif
420 if (ret == -1) {
421 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
422 "Failed to update the access/modification time on file '%s'",
423 pathname);
424 } else if (ret) {
425 tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn,
426 #ifdef HAVE_UTIMENSAT
427 "Invalid utimensat('%s') return value %d",
428 #else
429 "Invalid utimes('%s') return value %d",
430 #endif
431 pathname, ret);
432 }
433
434 return ret;
435 }
436