blob: 0325ce29d275c418534bef6c19885286bc8931c0 [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
76void safe_file_scanf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +080077 void (*cleanup_fn) (void),
Cyril Hrubis42748952012-11-27 19:26:45 +010078 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_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +080088 "Failed to open FILE '%s' for reading at %s:%d",
89 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +010090 }
Wanlong Gao354ebb42012-12-07 10:10:04 +080091
Cyril Hrubis42748952012-11-27 19:26:45 +010092 exp_convs = count_scanf_conversions(fmt);
93
94 va_start(va, fmt);
95 ret = vfscanf(f, fmt, va);
96 va_end(va);
Wanlong Gao354ebb42012-12-07 10:10:04 +080097
Cyril Hrubis42748952012-11-27 19:26:45 +010098 if (ret == EOF) {
99 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800100 "The FILE '%s' ended prematurely at %s:%d",
101 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100102 }
103
104 if (ret != exp_convs) {
105 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800106 "Expected %i conversions got %i FILE '%s' at %s:%d",
107 exp_convs, ret, path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100108 }
109
110}
111
Cyril Hrubis42748952012-11-27 19:26:45 +0100112void safe_file_printf(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800113 void (*cleanup_fn) (void),
114 const char *path, const char *fmt, ...)
Cyril Hrubis42748952012-11-27 19:26:45 +0100115{
116 va_list va;
117 FILE *f;
118
119 f = fopen(path, "w");
120
121 if (f == NULL) {
122 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800123 "Failed to open FILE '%s' for writing at %s:%d",
124 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100125 }
126
127 va_start(va, fmt);
128
129 if (vfprintf(f, fmt, va) < 0) {
130 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800131 "Failed to print to FILE '%s' at %s:%d",
132 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100133 }
134
135 va_end(va);
136
137 if (fclose(f)) {
138 tst_brkm(TBROK | TERRNO, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800139 "Failed to close FILE '%s' at %s:%d",
140 path, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100141 }
142}
143
144//TODO: C implementation? better error condition reporting?
145void safe_cp(const char *file, const int lineno,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800146 void (*cleanup_fn) (void), const char *src, const char *dst)
Cyril Hrubis42748952012-11-27 19:26:45 +0100147{
148 size_t len = strlen(src) + strlen(dst) + 16;
149 char buf[len];
150 int ret;
151
152 snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
153
154 ret = system(buf);
155
156 if (ret) {
157 tst_brkm(TBROK, cleanup_fn,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800158 "Failed to copy '%s' to '%s' at %s:%d",
159 src, dst, file, lineno);
Cyril Hrubis42748952012-11-27 19:26:45 +0100160 }
161}
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400162
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400163#ifndef HAVE_UTIMENSAT
164
Mike Frysinger4fd26382014-01-13 18:02:29 -0500165static void set_time(struct timeval *res, const struct timespec *src,
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400166 long cur_tv_sec, long cur_tv_usec)
167{
168 switch (src->tv_nsec) {
169 case UTIME_NOW:
170 break;
171 case UTIME_OMIT:
172 res->tv_sec = cur_tv_sec;
173 res->tv_usec = cur_tv_usec;
174 break;
175 default:
176 res->tv_sec = src->tv_sec;
177 res->tv_usec = src->tv_nsec / 1000;
178 }
179}
180
181#endif
182
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400183void safe_touch(const char *file, const int lineno,
184 void (*cleanup_fn)(void),
185 const char *pathname,
186 mode_t mode, const struct timespec times[2])
187{
188 int ret;
189 mode_t defmode;
190
191 defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
192
193 ret = open(pathname, O_CREAT | O_WRONLY, defmode);
194 if (ret == -1)
195 tst_brkm(TBROK | TERRNO, cleanup_fn,
196 "Failed to open file '%s' at %s:%d",
197 pathname, file, lineno);
198
199 ret = close(ret);
200 if (ret == -1)
201 tst_brkm(TBROK | TERRNO, cleanup_fn,
202 "Failed to close file '%s' at %s:%d",
203 pathname, file, lineno);
204
205 if (mode != 0) {
206 ret = chmod(pathname, mode);
207 if (ret == -1)
208 tst_brkm(TBROK | TERRNO, cleanup_fn,
209 "Failed to chmod file '%s' at %s:%d",
210 pathname, file, lineno);
211 }
212
Stanislav Kholmanskikhabf28b22013-11-06 12:23:06 +0400213
Mike Frysinger4fd26382014-01-13 18:02:29 -0500214#ifdef HAVE_UTIMENSAT
Stanislav Kholmanskikh19b69d52013-12-04 16:16:28 +0400215 ret = utimensat(AT_FDCWD, pathname, times, 0);
216#else
217 if (times == NULL) {
218 ret = utimes(pathname, NULL);
219 } else {
220 struct stat sb;
221 struct timeval cotimes[2];
222
223 ret = stat(pathname, &sb);
224 if (ret == -1)
225 tst_brkm(TBROK | TERRNO, cleanup_fn,
226 "Failed to stat file '%s' at %s:%d",
227 pathname, file, lineno);
228
229 ret = gettimeofday(cotimes, NULL);
230 if (ret == -1)
231 tst_brkm(TBROK | TERRNO, cleanup_fn,
232 "Failed to gettimeofday() at %s:%d",
233 file, lineno);
234 cotimes[1] = cotimes[0];
235
236 set_time(cotimes, times,
237 sb.st_atime, sb.st_atim.tv_nsec / 1000);
238 set_time(cotimes + 1, times + 1,
239 sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
240
241 ret = utimes(pathname, cotimes);
242 }
243#endif
244 if (ret == -1) {
245 tst_brkm(TBROK | TERRNO, cleanup_fn,
246 "Failed to update the access/modification time on file"
247 " '%s' at %s:%d", pathname, file, lineno);
248 }
249}