blob: 683c4eb704785fd3603c7f90bc037783e63cf7c7 [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
34#include "safe_file_ops.h"
35
36/*
37 * Count number of expected assigned conversions. Any conversion starts with '%'.
38 * The '%%' matches % and no assigment is done. The %*x matches as x would do but
39 * the assigment is supressed.
40 *
41 * NOTE: This is not 100% correct for complex scanf strings, but will do for
42 * all of our intended usage.
43 */
44static int count_scanf_conversions(const char *fmt)
45{
46 unsigned int cnt = 0;
47 int flag = 0;
48
49 while (*fmt) {
50 switch (*fmt) {
51 case '%':
52 if (flag) {
53 cnt--;
54 flag = 0;
55 } else {
56 flag = 1;
57 cnt++;
58 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080059 break;
Cyril Hrubis42748952012-11-27 19:26:45 +010060 case '*':
61 if (flag) {
62 cnt--;
63 flag = 0;
64 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080065 break;
Cyril Hrubis42748952012-11-27 19:26:45 +010066 default:
67 flag = 0;
68 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080069
Cyril Hrubis42748952012-11-27 19:26:45 +010070 fmt++;
71 }
72
73 return cnt;
74}
75
Li Wang8bfd7c12014-12-03 08:11:36 -050076int file_scanf(const char *file, const int lineno,
77 const char *path, const char *fmt, ...)
78{
79 va_list va;
80 FILE *f;
81 int exp_convs, ret;
82
83 f = fopen(path, "r");
84
85 if (f == NULL) {
86 tst_resm(TWARN,
87 "Failed to open FILE '%s' at %s:%d",
88 path, file, lineno);
89 return 1;
90 }
91
92 exp_convs = count_scanf_conversions(fmt);
93
94 va_start(va, fmt);
95 ret = vfscanf(f, fmt, va);
96 va_end(va);
97
98 if (ret == EOF) {
99 tst_resm(TWARN,
100 "The FILE '%s' ended prematurely at %s:%d",
101 path, file, lineno);
102 goto err;
103 }
104
105 if (ret != exp_convs) {
106 tst_resm(TWARN,
107 "Expected %i conversions got %i FILE '%s' at %s:%d",
108 exp_convs, ret, path, file, lineno);
109 goto err;
110 }
111
112 if (fclose(f)) {
113 tst_resm(TWARN,
114 "Failed to close FILE '%s' at %s:%d",
115 path, file, lineno);
116 return 1;
117 }
118
119 return 0;
120
121err:
122 if (fclose(f)) {
123 tst_resm(TWARN,
124 "Failed to close FILE '%s' at %s:%d",
125 path, file, lineno);
126 }
127 return 1;
128}
129
Cyril Hrubis42748952012-11-27 19:26:45 +0100130void safe_file_scanf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800131 void (*cleanup_fn) (void),
Cyril Hrubis42748952012-11-27 19:26:45 +0100132 const char *path, const char *fmt, ...)
133{
134 va_list va;
135 FILE *f;
136 int exp_convs, ret;
137
138 f = fopen(path, "r");
139
140 if (f == NULL) {
141 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800142 "Failed to open FILE '%s' for reading at %s:%d",
143 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100144 }
Wanlong Gao354ebb42012-12-07 10:10:04 +0800145
Cyril Hrubis42748952012-11-27 19:26:45 +0100146 exp_convs = count_scanf_conversions(fmt);
147
148 va_start(va, fmt);
149 ret = vfscanf(f, fmt, va);
150 va_end(va);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800151
Cyril Hrubis42748952012-11-27 19:26:45 +0100152 if (ret == EOF) {
153 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800154 "The FILE '%s' ended prematurely at %s:%d",
155 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100156 }
157
158 if (ret != exp_convs) {
159 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800160 "Expected %i conversions got %i FILE '%s' at %s:%d",
161 exp_convs, ret, path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100162 }
163
Li Wang8bfd7c12014-12-03 08:11:36 -0500164 if (fclose(f)) {
165 tst_brkm(TBROK | TERRNO, cleanup_fn,
166 "Failed to close FILE '%s' at %s:%d",
167 path, file, lineno);
168 }
169}
170
171int file_printf(const char *file, const int lineno,
172 const char *path, const char *fmt, ...)
173{
174 va_list va;
175 FILE *f;
176
177 f = fopen(path, "w");
178
179 if (f == NULL) {
180 tst_resm(TWARN,
181 "Failed to open FILE '%s' at %s:%d",
182 path, file, lineno);
183 return 1;
184 }
185
186 va_start(va, fmt);
187
188 if (vfprintf(f, fmt, va) < 0) {
189 tst_resm(TWARN,
190 "Failed to print to FILE '%s' at %s:%d",
191 path, file, lineno);
192 goto err;
193 }
194
195 va_end(va);
196
197 if (fclose(f)) {
198 tst_resm(TWARN,
199 "Failed to close FILE '%s' at %s:%d",
200 path, file, lineno);
201 return 1;
202 }
203
204 return 0;
205
206err:
207 if (fclose(f)) {
208 tst_resm(TWARN,
209 "Failed to close FILE '%s' at %s:%d",
210 path, file, lineno);
211 }
212 return 1;
Cyril Hrubis42748952012-11-27 19:26:45 +0100213}
214
Cyril Hrubis42748952012-11-27 19:26:45 +0100215void safe_file_printf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800216 void (*cleanup_fn) (void),
217 const char *path, const char *fmt, ...)
Cyril Hrubis42748952012-11-27 19:26:45 +0100218{
219 va_list va;
220 FILE *f;
221
222 f = fopen(path, "w");
223
224 if (f == NULL) {
225 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800226 "Failed to open FILE '%s' for writing at %s:%d",
227 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100228 }
229
230 va_start(va, fmt);
231
232 if (vfprintf(f, fmt, va) < 0) {
233 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800234 "Failed to print to FILE '%s' at %s:%d",
235 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100236 }
237
238 va_end(va);
239
240 if (fclose(f)) {
241 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800242 "Failed to close FILE '%s' at %s:%d",
243 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100244 }
245}
246
247//TODO: C implementation? better error condition reporting?
248void safe_cp(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800249 void (*cleanup_fn) (void), const char *src, const char *dst)
Cyril Hrubis42748952012-11-27 19:26:45 +0100250{
251 size_t len = strlen(src) + strlen(dst) + 16;
252 char buf[len];
253 int ret;
254
255 snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
256
257 ret = system(buf);
258
259 if (ret) {
260 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800261 "Failed to copy '%s' to '%s' at %s:%d",
262 src, dst, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100263 }
264}
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400265
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400266#ifndef HAVE_UTIMENSAT
267
Mike Frysinger4fd26382014-01-13 18:02:29 -0500268static void set_time(struct timeval *res, const struct timespec *src,
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400269 long cur_tv_sec, long cur_tv_usec)
270{
271 switch (src->tv_nsec) {
272 case UTIME_NOW:
273 break;
274 case UTIME_OMIT:
275 res->tv_sec = cur_tv_sec;
276 res->tv_usec = cur_tv_usec;
277 break;
278 default:
279 res->tv_sec = src->tv_sec;
280 res->tv_usec = src->tv_nsec / 1000;
281 }
282}
283
284#endif
285
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400286void safe_touch(const char *file, const int lineno,
287 void (*cleanup_fn)(void),
288 const char *pathname,
289 mode_t mode, const struct timespec times[2])
290{
291 int ret;
292 mode_t defmode;
293
294 defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
295
296 ret = open(pathname, O_CREAT | O_WRONLY, defmode);
297 if (ret == -1)
298 tst_brkm(TBROK | TERRNO, cleanup_fn,
299 "Failed to open file '%s' at %s:%d",
300 pathname, file, lineno);
301
302 ret = close(ret);
303 if (ret == -1)
304 tst_brkm(TBROK | TERRNO, cleanup_fn,
305 "Failed to close file '%s' at %s:%d",
306 pathname, file, lineno);
307
308 if (mode != 0) {
309 ret = chmod(pathname, mode);
310 if (ret == -1)
311 tst_brkm(TBROK | TERRNO, cleanup_fn,
312 "Failed to chmod file '%s' at %s:%d",
313 pathname, file, lineno);
314 }
315
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400316
Mike Frysinger4fd26382014-01-13 18:02:29 -0500317#ifdef HAVE_UTIMENSAT
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400318 ret = utimensat(AT_FDCWD, pathname, times, 0);
319#else
320 if (times == NULL) {
321 ret = utimes(pathname, NULL);
322 } else {
323 struct stat sb;
324 struct timeval cotimes[2];
325
326 ret = stat(pathname, &sb);
327 if (ret == -1)
328 tst_brkm(TBROK | TERRNO, cleanup_fn,
329 "Failed to stat file '%s' at %s:%d",
330 pathname, file, lineno);
331
332 ret = gettimeofday(cotimes, NULL);
333 if (ret == -1)
334 tst_brkm(TBROK | TERRNO, cleanup_fn,
335 "Failed to gettimeofday() at %s:%d",
336 file, lineno);
337 cotimes[1] = cotimes[0];
338
339 set_time(cotimes, times,
340 sb.st_atime, sb.st_atim.tv_nsec / 1000);
341 set_time(cotimes + 1, times + 1,
342 sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
343
344 ret = utimes(pathname, cotimes);
345 }
346#endif
347 if (ret == -1) {
348 tst_brkm(TBROK | TERRNO, cleanup_fn,
349 "Failed to update the access/modification time on file"
350 " '%s' at %s:%d", pathname, file, lineno);
351 }
352}