blob: 01f64ed3251b69c9785f8301be440a90546d0758 [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 Hrubis42748952012-11-27 19:26:45 +0100145 }
Wanlong Gao354ebb42012-12-07 10:10:04 +0800146
Cyril Hrubis42748952012-11-27 19:26:45 +0100147 exp_convs = count_scanf_conversions(fmt);
148
149 va_start(va, fmt);
150 ret = vfscanf(f, fmt, va);
151 va_end(va);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800152
Cyril Hrubis42748952012-11-27 19:26:45 +0100153 if (ret == EOF) {
154 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800155 "The FILE '%s' ended prematurely at %s:%d",
156 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100157 }
158
159 if (ret != exp_convs) {
160 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800161 "Expected %i conversions got %i FILE '%s' at %s:%d",
162 exp_convs, ret, path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100163 }
164
Li Wang8bfd7c12014-12-03 08:11:36 -0500165 if (fclose(f)) {
166 tst_brkm(TBROK | TERRNO, cleanup_fn,
167 "Failed to close FILE '%s' at %s:%d",
168 path, file, lineno);
169 }
170}
171
Jan Stancek9ff68de2016-07-14 12:35:22 +0200172
173/*
174 * Try to parse each line from file specified by 'path' according
175 * to scanf format 'fmt'. If all fields could be parsed, stop and
176 * return 0, otherwise continue or return 1 if EOF is reached.
177 */
178int file_lines_scanf(const char *file, const int lineno,
179 void (*cleanup_fn)(void), int strict,
180 const char *path, const char *fmt, ...)
181{
182 FILE *fp;
183 int ret = 0;
184 int arg_count = 0;
185 char line[BUFSIZ];
186 va_list ap;
187
188 if (!fmt) {
189 tst_brkm(TBROK, cleanup_fn, "pattern is NULL, %s:%d",
190 file, lineno);
191 }
192
193 fp = fopen(path, "r");
194 if (fp == NULL) {
195 tst_brkm(TBROK | TERRNO, cleanup_fn,
196 "Failed to open FILE '%s' for reading at %s:%d",
197 path, file, lineno);
198 }
199
200 arg_count = count_scanf_conversions(fmt);
201
202 while (fgets(line, BUFSIZ, fp) != NULL) {
203 va_start(ap, fmt);
204 ret = vsscanf(line, fmt, ap);
205 va_end(ap);
206
207 if (ret == arg_count)
208 break;
209 }
210 fclose(fp);
211
212 if (strict && ret != arg_count)
213 tst_brkm(TBROK, cleanup_fn, "Expected %i conversions got %i"
214 " at %s:%d", arg_count, ret, file, lineno);
215
216 return !(ret == arg_count);
217}
218
Li Wang8bfd7c12014-12-03 08:11:36 -0500219int file_printf(const char *file, const int lineno,
220 const char *path, const char *fmt, ...)
221{
222 va_list va;
223 FILE *f;
224
225 f = fopen(path, "w");
226
227 if (f == NULL) {
228 tst_resm(TWARN,
229 "Failed to open FILE '%s' at %s:%d",
230 path, file, lineno);
231 return 1;
232 }
233
234 va_start(va, fmt);
235
236 if (vfprintf(f, fmt, va) < 0) {
237 tst_resm(TWARN,
238 "Failed to print to FILE '%s' at %s:%d",
239 path, file, lineno);
240 goto err;
241 }
242
243 va_end(va);
244
245 if (fclose(f)) {
246 tst_resm(TWARN,
247 "Failed to close FILE '%s' at %s:%d",
248 path, file, lineno);
249 return 1;
250 }
251
252 return 0;
253
254err:
255 if (fclose(f)) {
256 tst_resm(TWARN,
257 "Failed to close FILE '%s' at %s:%d",
258 path, file, lineno);
259 }
260 return 1;
Cyril Hrubis42748952012-11-27 19:26:45 +0100261}
262
Cyril Hrubis42748952012-11-27 19:26:45 +0100263void safe_file_printf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800264 void (*cleanup_fn) (void),
265 const char *path, const char *fmt, ...)
Cyril Hrubis42748952012-11-27 19:26:45 +0100266{
267 va_list va;
268 FILE *f;
269
270 f = fopen(path, "w");
271
272 if (f == NULL) {
273 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800274 "Failed to open FILE '%s' for writing at %s:%d",
275 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100276 }
277
278 va_start(va, fmt);
279
280 if (vfprintf(f, fmt, va) < 0) {
281 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800282 "Failed to print to FILE '%s' at %s:%d",
283 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100284 }
285
286 va_end(va);
287
288 if (fclose(f)) {
289 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800290 "Failed to close FILE '%s' at %s:%d",
291 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100292 }
293}
294
295//TODO: C implementation? better error condition reporting?
296void safe_cp(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800297 void (*cleanup_fn) (void), const char *src, const char *dst)
Cyril Hrubis42748952012-11-27 19:26:45 +0100298{
299 size_t len = strlen(src) + strlen(dst) + 16;
300 char buf[len];
301 int ret;
302
303 snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
304
305 ret = system(buf);
306
307 if (ret) {
308 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800309 "Failed to copy '%s' to '%s' at %s:%d",
310 src, dst, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100311 }
312}
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400313
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400314#ifndef HAVE_UTIMENSAT
315
Mike Frysinger4fd26382014-01-13 18:02:29 -0500316static void set_time(struct timeval *res, const struct timespec *src,
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400317 long cur_tv_sec, long cur_tv_usec)
318{
319 switch (src->tv_nsec) {
320 case UTIME_NOW:
321 break;
322 case UTIME_OMIT:
323 res->tv_sec = cur_tv_sec;
324 res->tv_usec = cur_tv_usec;
325 break;
326 default:
327 res->tv_sec = src->tv_sec;
328 res->tv_usec = src->tv_nsec / 1000;
329 }
330}
331
332#endif
333
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400334void safe_touch(const char *file, const int lineno,
335 void (*cleanup_fn)(void),
336 const char *pathname,
337 mode_t mode, const struct timespec times[2])
338{
339 int ret;
340 mode_t defmode;
341
342 defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
343
344 ret = open(pathname, O_CREAT | O_WRONLY, defmode);
345 if (ret == -1)
346 tst_brkm(TBROK | TERRNO, cleanup_fn,
347 "Failed to open file '%s' at %s:%d",
348 pathname, file, lineno);
349
350 ret = close(ret);
351 if (ret == -1)
352 tst_brkm(TBROK | TERRNO, cleanup_fn,
353 "Failed to close file '%s' at %s:%d",
354 pathname, file, lineno);
355
356 if (mode != 0) {
357 ret = chmod(pathname, mode);
358 if (ret == -1)
359 tst_brkm(TBROK | TERRNO, cleanup_fn,
360 "Failed to chmod file '%s' at %s:%d",
361 pathname, file, lineno);
362 }
363
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400364
Mike Frysinger4fd26382014-01-13 18:02:29 -0500365#ifdef HAVE_UTIMENSAT
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400366 ret = utimensat(AT_FDCWD, pathname, times, 0);
367#else
368 if (times == NULL) {
369 ret = utimes(pathname, NULL);
370 } else {
371 struct stat sb;
372 struct timeval cotimes[2];
373
374 ret = stat(pathname, &sb);
375 if (ret == -1)
376 tst_brkm(TBROK | TERRNO, cleanup_fn,
377 "Failed to stat file '%s' at %s:%d",
378 pathname, file, lineno);
379
380 ret = gettimeofday(cotimes, NULL);
381 if (ret == -1)
382 tst_brkm(TBROK | TERRNO, cleanup_fn,
383 "Failed to gettimeofday() at %s:%d",
384 file, lineno);
385 cotimes[1] = cotimes[0];
386
387 set_time(cotimes, times,
388 sb.st_atime, sb.st_atim.tv_nsec / 1000);
389 set_time(cotimes + 1, times + 1,
390 sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
391
392 ret = utimes(pathname, cotimes);
393 }
394#endif
395 if (ret == -1) {
396 tst_brkm(TBROK | TERRNO, cleanup_fn,
397 "Failed to update the access/modification time on file"
398 " '%s' at %s:%d", pathname, file, lineno);
399 }
400}