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
37 /*
38 * Count number of expected assigned conversions. Any conversion starts with '%'.
39 * The '%%' matches % and no assignment is done. The %*x matches as x would do but
40 * the assignment is suppressed.
41 *
42 * NOTE: This is not 100% correct for complex scanf strings, but will do for
43 * all of our intended usage.
44 */
count_scanf_conversions(const char * fmt)45 static 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 }
60 break;
61 case '*':
62 if (flag) {
63 cnt--;
64 flag = 0;
65 }
66 break;
67 default:
68 flag = 0;
69 }
70
71 fmt++;
72 }
73
74 return cnt;
75 }
76
file_scanf(const char * file,const int lineno,const char * path,const char * fmt,...)77 int 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
122 err:
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
safe_file_scanf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)131 void safe_file_scanf(const char *file, const int lineno,
132 void (*cleanup_fn) (void),
133 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,
143 "Failed to open FILE '%s' for reading at %s:%d",
144 path, file, lineno);
145 }
146
147 exp_convs = count_scanf_conversions(fmt);
148
149 va_start(va, fmt);
150 ret = vfscanf(f, fmt, va);
151 va_end(va);
152
153 if (ret == EOF) {
154 tst_brkm(TBROK, cleanup_fn,
155 "The FILE '%s' ended prematurely at %s:%d",
156 path, file, lineno);
157 }
158
159 if (ret != exp_convs) {
160 tst_brkm(TBROK, cleanup_fn,
161 "Expected %i conversions got %i FILE '%s' at %s:%d",
162 exp_convs, ret, path, file, lineno);
163 }
164
165 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
file_printf(const char * file,const int lineno,const char * path,const char * fmt,...)172 int file_printf(const char *file, const int lineno,
173 const char *path, const char *fmt, ...)
174 {
175 va_list va;
176 FILE *f;
177
178 f = fopen(path, "w");
179
180 if (f == NULL) {
181 tst_resm(TWARN,
182 "Failed to open FILE '%s' at %s:%d",
183 path, file, lineno);
184 return 1;
185 }
186
187 va_start(va, fmt);
188
189 if (vfprintf(f, fmt, va) < 0) {
190 tst_resm(TWARN,
191 "Failed to print to FILE '%s' at %s:%d",
192 path, file, lineno);
193 goto err;
194 }
195
196 va_end(va);
197
198 if (fclose(f)) {
199 tst_resm(TWARN,
200 "Failed to close FILE '%s' at %s:%d",
201 path, file, lineno);
202 return 1;
203 }
204
205 return 0;
206
207 err:
208 if (fclose(f)) {
209 tst_resm(TWARN,
210 "Failed to close FILE '%s' at %s:%d",
211 path, file, lineno);
212 }
213 return 1;
214 }
215
safe_file_printf(const char * file,const int lineno,void (* cleanup_fn)(void),const char * path,const char * fmt,...)216 void safe_file_printf(const char *file, const int lineno,
217 void (*cleanup_fn) (void),
218 const char *path, const char *fmt, ...)
219 {
220 va_list va;
221 FILE *f;
222
223 f = fopen(path, "w");
224
225 if (f == NULL) {
226 tst_brkm(TBROK | TERRNO, cleanup_fn,
227 "Failed to open FILE '%s' for writing at %s:%d",
228 path, file, lineno);
229 }
230
231 va_start(va, fmt);
232
233 if (vfprintf(f, fmt, va) < 0) {
234 tst_brkm(TBROK, cleanup_fn,
235 "Failed to print to FILE '%s' at %s:%d",
236 path, file, lineno);
237 }
238
239 va_end(va);
240
241 if (fclose(f)) {
242 tst_brkm(TBROK | TERRNO, cleanup_fn,
243 "Failed to close FILE '%s' at %s:%d",
244 path, file, lineno);
245 }
246 }
247
248 //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)249 void safe_cp(const char *file, const int lineno,
250 void (*cleanup_fn) (void), const char *src, const char *dst)
251 {
252 size_t len = strlen(src) + strlen(dst) + 16;
253 char buf[len];
254 int ret;
255
256 snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
257
258 ret = system(buf);
259
260 if (ret) {
261 tst_brkm(TBROK, cleanup_fn,
262 "Failed to copy '%s' to '%s' at %s:%d",
263 src, dst, file, lineno);
264 }
265 }
266
267 #ifndef HAVE_UTIMENSAT
268
set_time(struct timeval * res,const struct timespec * src,long cur_tv_sec,long cur_tv_usec)269 static void set_time(struct timeval *res, const struct timespec *src,
270 long cur_tv_sec, long cur_tv_usec)
271 {
272 switch (src->tv_nsec) {
273 case UTIME_NOW:
274 break;
275 case UTIME_OMIT:
276 res->tv_sec = cur_tv_sec;
277 res->tv_usec = cur_tv_usec;
278 break;
279 default:
280 res->tv_sec = src->tv_sec;
281 res->tv_usec = src->tv_nsec / 1000;
282 }
283 }
284
285 #endif
286
safe_touch(const char * file,const int lineno,void (* cleanup_fn)(void),const char * pathname,mode_t mode,const struct timespec times[2])287 void safe_touch(const char *file, const int lineno,
288 void (*cleanup_fn)(void),
289 const char *pathname,
290 mode_t mode, const struct timespec times[2])
291 {
292 int ret;
293 mode_t defmode;
294
295 defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
296
297 ret = open(pathname, O_CREAT | O_WRONLY, defmode);
298 if (ret == -1)
299 tst_brkm(TBROK | TERRNO, cleanup_fn,
300 "Failed to open file '%s' at %s:%d",
301 pathname, file, lineno);
302
303 ret = close(ret);
304 if (ret == -1)
305 tst_brkm(TBROK | TERRNO, cleanup_fn,
306 "Failed to close file '%s' at %s:%d",
307 pathname, file, lineno);
308
309 if (mode != 0) {
310 ret = chmod(pathname, mode);
311 if (ret == -1)
312 tst_brkm(TBROK | TERRNO, cleanup_fn,
313 "Failed to chmod file '%s' at %s:%d",
314 pathname, file, lineno);
315 }
316
317
318 #ifdef HAVE_UTIMENSAT
319 ret = utimensat(AT_FDCWD, pathname, times, 0);
320 #else
321 if (times == NULL) {
322 ret = utimes(pathname, NULL);
323 } else {
324 struct stat sb;
325 struct timeval cotimes[2];
326
327 ret = stat(pathname, &sb);
328 if (ret == -1)
329 tst_brkm(TBROK | TERRNO, cleanup_fn,
330 "Failed to stat file '%s' at %s:%d",
331 pathname, file, lineno);
332
333 ret = gettimeofday(cotimes, NULL);
334 if (ret == -1)
335 tst_brkm(TBROK | TERRNO, cleanup_fn,
336 "Failed to gettimeofday() at %s:%d",
337 file, lineno);
338 cotimes[1] = cotimes[0];
339
340 set_time(cotimes, times,
341 sb.st_atime, sb.st_atim.tv_nsec / 1000);
342 set_time(cotimes + 1, times + 1,
343 sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
344
345 ret = utimes(pathname, cotimes);
346 }
347 #endif
348 if (ret == -1) {
349 tst_brkm(TBROK | TERRNO, cleanup_fn,
350 "Failed to update the access/modification time on file"
351 " '%s' at %s:%d", pathname, file, lineno);
352 }
353 }
354