blob: b576cb97e20355c715ae0df5eeea393b99051271 [file] [log] [blame]
Cyril Hrubis42748952012-11-27 19:26:45 +01001/*
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
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +040024#include "config.h"
Cyril Hrubis42748952012-11-27 19:26:45 +010025#include <stdarg.h>
26#include <stdio.h>
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +040027#include <sys/time.h>
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +040028#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +040031#include <unistd.h>
32#include <utime.h>
Cyril Hrubis42748952012-11-27 19:26:45 +010033
Cyril Hrubisbbdb9f72016-03-16 15:53:57 +010034#include "test.h"
35#include "safe_file_ops_fn.h"
Cyril Hrubis42748952012-11-27 19:26:45 +010036
37/*
38 * Count number of expected assigned conversions. Any conversion starts with '%'.
Wei,Jiangang62d6e8b2015-05-13 19:19:42 +080039 * The '%%' matches % and no assignment is done. The %*x matches as x would do but
40 * the assignment is suppressed.
Cyril Hrubis42748952012-11-27 19:26:45 +010041 *
42 * NOTE: This is not 100% correct for complex scanf strings, but will do for
43 * all of our intended usage.
44 */
45static 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 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080060 break;
Cyril Hrubis42748952012-11-27 19:26:45 +010061 case '*':
62 if (flag) {
63 cnt--;
64 flag = 0;
65 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080066 break;
Cyril Hrubis42748952012-11-27 19:26:45 +010067 default:
68 flag = 0;
69 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080070
Cyril Hrubis42748952012-11-27 19:26:45 +010071 fmt++;
72 }
73
74 return cnt;
75}
76
Li Wang8bfd7c12014-12-03 08:11:36 -050077int 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
122err:
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
Cyril Hrubis42748952012-11-27 19:26:45 +0100131void safe_file_scanf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800132 void (*cleanup_fn) (void),
Cyril Hrubis42748952012-11-27 19:26:45 +0100133 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,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800143 "Failed to open FILE '%s' for reading at %s:%d",
144 path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100145 return;
Cyril Hrubis42748952012-11-27 19:26:45 +0100146 }
Wanlong Gao354ebb42012-12-07 10:10:04 +0800147
Cyril Hrubis42748952012-11-27 19:26:45 +0100148 exp_convs = count_scanf_conversions(fmt);
149
150 va_start(va, fmt);
151 ret = vfscanf(f, fmt, va);
152 va_end(va);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800153
Cyril Hrubis42748952012-11-27 19:26:45 +0100154 if (ret == EOF) {
155 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800156 "The FILE '%s' ended prematurely at %s:%d",
157 path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100158 return;
Cyril Hrubis42748952012-11-27 19:26:45 +0100159 }
160
161 if (ret != exp_convs) {
162 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800163 "Expected %i conversions got %i FILE '%s' at %s:%d",
164 exp_convs, ret, path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100165 return;
Cyril Hrubis42748952012-11-27 19:26:45 +0100166 }
167
Li Wang8bfd7c12014-12-03 08:11:36 -0500168 if (fclose(f)) {
169 tst_brkm(TBROK | TERRNO, cleanup_fn,
170 "Failed to close FILE '%s' at %s:%d",
171 path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100172 return;
Li Wang8bfd7c12014-12-03 08:11:36 -0500173 }
174}
175
Jan Stancek9ff68de2016-07-14 12:35:22 +0200176
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 */
182int 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);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100195 return 1;
Jan Stancek9ff68de2016-07-14 12:35:22 +0200196 }
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);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100203 return 1;
Jan Stancek9ff68de2016-07-14 12:35:22 +0200204 }
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
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100218 if (strict && ret != arg_count) {
Jan Stancek9ff68de2016-07-14 12:35:22 +0200219 tst_brkm(TBROK, cleanup_fn, "Expected %i conversions got %i"
220 " at %s:%d", arg_count, ret, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100221 return 1;
222 }
Jan Stancek9ff68de2016-07-14 12:35:22 +0200223
224 return !(ret == arg_count);
225}
226
Li Wang8bfd7c12014-12-03 08:11:36 -0500227int 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
262err:
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;
Cyril Hrubis42748952012-11-27 19:26:45 +0100269}
270
Cyril Hrubis42748952012-11-27 19:26:45 +0100271void safe_file_printf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800272 void (*cleanup_fn) (void),
273 const char *path, const char *fmt, ...)
Cyril Hrubis42748952012-11-27 19:26:45 +0100274{
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,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800282 "Failed to open FILE '%s' for writing at %s:%d",
283 path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100284 return;
Cyril Hrubis42748952012-11-27 19:26:45 +0100285 }
286
287 va_start(va, fmt);
288
289 if (vfprintf(f, fmt, va) < 0) {
290 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800291 "Failed to print to FILE '%s' at %s:%d",
292 path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100293 return;
Cyril Hrubis42748952012-11-27 19:26:45 +0100294 }
295
296 va_end(va);
297
298 if (fclose(f)) {
299 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800300 "Failed to close FILE '%s' at %s:%d",
301 path, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100302 return;
Cyril Hrubis42748952012-11-27 19:26:45 +0100303 }
304}
305
306//TODO: C implementation? better error condition reporting?
307void safe_cp(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800308 void (*cleanup_fn) (void), const char *src, const char *dst)
Cyril Hrubis42748952012-11-27 19:26:45 +0100309{
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,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800320 "Failed to copy '%s' to '%s' at %s:%d",
321 src, dst, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100322 }
323}
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400324
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400325#ifndef HAVE_UTIMENSAT
326
Mike Frysinger4fd26382014-01-13 18:02:29 -0500327static void set_time(struct timeval *res, const struct timespec *src,
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400328 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
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400345void 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);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100356 if (ret == -1) {
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400357 tst_brkm(TBROK | TERRNO, cleanup_fn,
358 "Failed to open file '%s' at %s:%d",
359 pathname, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100360 return;
361 }
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400362
363 ret = close(ret);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100364 if (ret == -1) {
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400365 tst_brkm(TBROK | TERRNO, cleanup_fn,
366 "Failed to close file '%s' at %s:%d",
367 pathname, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100368 return;
369 }
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400370
371 if (mode != 0) {
372 ret = chmod(pathname, mode);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100373 if (ret == -1) {
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400374 tst_brkm(TBROK | TERRNO, cleanup_fn,
375 "Failed to chmod file '%s' at %s:%d",
376 pathname, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100377 return;
378 }
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400379 }
380
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400381
Mike Frysinger4fd26382014-01-13 18:02:29 -0500382#ifdef HAVE_UTIMENSAT
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400383 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);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100392 if (ret == -1) {
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400393 tst_brkm(TBROK | TERRNO, cleanup_fn,
394 "Failed to stat file '%s' at %s:%d",
395 pathname, file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100396 return;
397 }
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400398
399 ret = gettimeofday(cotimes, NULL);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100400 if (ret == -1) {
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400401 tst_brkm(TBROK | TERRNO, cleanup_fn,
402 "Failed to gettimeofday() at %s:%d",
403 file, lineno);
Cyril Hrubisd101cab2017-02-14 11:48:46 +0100404 return;
405 }
406
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400407 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}