1 /*
2 * Copyright (C) 2013-2021 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * This code is a complete clean re-write of the stress tool by
19 * Colin Ian King <colin.king@canonical.com> and attempts to be
20 * backwardly compatible with the stress tool by Amos Waterland
21 * <apw@rossby.metr.ou.edu> but has more stress tests and more
22 * functionality.
23 *
24 */
25 #include "stress-ng.h"
26
27 static const stress_help_t help[] = {
28 { NULL, "file-ioctl N", "start N workers exercising file specific ioctls" },
29 { NULL, "file-ioctl-ops N", "stop after N file ioctl bogo operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #if (defined(FIONBIO) && defined(O_NONBLOCK)) || \
34 (defined(FIOASYNC) && defined(O_ASYNC))
check_flag(const stress_args_t * args,const char * ioctl_name,const int fd,const int flag,const int ret,const bool set)35 static void check_flag(
36 const stress_args_t *args,
37 const char *ioctl_name,
38 const int fd,
39 const int flag,
40 const int ret,
41 const bool set)
42 {
43 #if defined(F_GETFL)
44 if (ret == 0) {
45 int flags;
46
47 flags = fcntl(fd, F_GETFL, 0);
48 /*
49 * The fcntl failed, so checking is not a valid
50 * thing to sanity check with.
51 */
52 if (errno != 0)
53 return;
54 if ((set && !(flags & flag)) ||
55 (!set && (flags & flag)))
56 pr_fail("%s: ioctl %s failed, unexpected flags when checked with F_GETFL\n",
57 args->name, ioctl_name);
58 }
59 #else
60 (void)args;
61 (void)ioctl_name;
62 (void)fd;
63 (void)flag;
64 (void)ret;
65 #endif
66 }
67 #endif
68
69 /*
70 * stress_file_ioctl
71 * stress file ioctls
72 */
stress_file_ioctl(const stress_args_t * args)73 static int stress_file_ioctl(const stress_args_t *args)
74 {
75 char filename[PATH_MAX];
76 int ret, fd;
77 const int bad_fd = stress_get_bad_fd();
78 #if defined(FICLONE) || defined(FICLONERANGE)
79 int dfd;
80 #endif
81 const off_t file_sz = 8192;
82 uint32_t rnd = stress_mwc32();
83
84 ret = stress_temp_dir_mk_args(args);
85 if (ret < 0)
86 return exit_status(-ret);
87
88 (void)stress_temp_filename_args(args, filename, sizeof(filename), rnd);
89 fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
90 if (fd < 0) {
91 ret = exit_status(errno);
92 pr_err("%s: cannot create %s\n", args->name, filename);
93 (void)stress_temp_dir_rm_args(args);
94 return ret;
95 }
96 (void)unlink(filename);
97
98 #if defined(FICLONE) || defined(FICLONERANGE)
99 (void)stress_temp_filename_args(args, filename, sizeof(filename), rnd + 1);
100 dfd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
101 if (dfd < 0) {
102 ret = exit_status(errno);
103 (void)close(fd);
104 (void)stress_temp_dir_rm_args(args);
105 pr_err("%s: cannot create %s\n", args->name, filename);
106 return ret;
107 }
108 (void)unlink(filename);
109 #endif
110
111 (void)shim_fallocate(fd, 0, 0, file_sz);
112 #if defined(FICLONE) || defined(FICLONERANGE)
113 (void)shim_fallocate(dfd, 0, 0, file_sz);
114 #endif
115 (void)shim_fsync(fd);
116
117 (void)bad_fd;
118
119 stress_set_proc_state(args->name, STRESS_STATE_RUN);
120
121 do {
122 int exercised = 0;
123
124 #if defined(FIOCLEX)
125 {
126 ret = ioctl(fd, FIOCLEX);
127 (void)ret;
128
129 exercised++;
130 }
131 #endif
132 #if defined(FIONCLEX)
133 {
134 ret = ioctl(fd, FIONCLEX);
135 (void)ret;
136
137 exercised++;
138 }
139 #endif
140 #if defined(FIONBIO)
141 {
142 int opt;
143
144 opt = 1;
145 ret = ioctl(fd, FIONBIO, &opt);
146 #if defined(O_NONBLOCK)
147 check_flag(args, "FIONBIO", fd, O_NONBLOCK, ret, true);
148 #else
149 (void)ret;
150 #endif
151
152 opt = 0;
153 ret = ioctl(fd, FIONBIO, &opt);
154 #if defined(O_NONBLOCK)
155 check_flag(args, "FIONBIO", fd, O_NONBLOCK, ret, false);
156 #else
157 (void)ret;
158 #endif
159 exercised++;
160 }
161 #endif
162
163 #if defined(FIOASYNC)
164 {
165 int opt;
166
167 opt = 1;
168 ret = ioctl(fd, FIOASYNC, &opt);
169 #if defined(O_ASYNC)
170 check_flag(args, "FIONASYNC", fd, O_ASYNC, ret, true);
171 #else
172 (void)ret;
173 #endif
174
175 opt = 0;
176 ret = ioctl(fd, FIOASYNC, &opt);
177 #if defined(O_ASYNC)
178 check_flag(args, "FIONASYNC", fd, O_ASYNC, ret, false);
179 #else
180 (void)ret;
181 #endif
182 exercised++;
183 }
184 #endif
185
186 #if defined(FIOQSIZE)
187 {
188 shim_loff_t sz;
189 struct stat buf;
190
191 ret = fstat(fd, &buf);
192 if (ret == 0) {
193 ret = ioctl(fd, FIOQSIZE, &sz);
194 if ((ret == 0) && (file_sz != buf.st_size))
195 pr_fail("%s: ioctl FIOQSIZE failed, size %jd (filesize) vs %jd (reported)\n",
196 args->name,
197 (intmax_t)file_sz, (intmax_t)sz);
198 }
199 exercised++;
200 }
201 #endif
202
203 /* Disable this at the moment, it is fragile */
204 #if 0
205 #if defined(FIFREEZE) && \
206 defined(FITHAW)
207 {
208 ret = ioctl(fd, FIFREEZE);
209 (void)ret;
210 ret = ioctl(fd, FITHAW);
211 (void)ret;
212
213 exercised++;
214 }
215 #endif
216 #endif
217
218 #if defined(FIGETBSZ)
219 {
220 int isz;
221
222 ret = ioctl(fd, FIGETBSZ, &isz);
223 if ((ret == 0) && (isz < 1))
224 pr_fail("%s: ioctl FIGETBSZ returned unusual block size %d\n",
225 args->name, isz);
226
227 exercised++;
228 }
229 #endif
230
231 #if defined(FICLONE)
232 {
233 ret = ioctl(dfd, FICLONE, fd);
234 (void)ret;
235
236 exercised++;
237 }
238 #endif
239
240 #if defined(FICLONERANGE)
241 {
242 struct file_clone_range fcr;
243
244 (void)memset(&fcr, 0, sizeof(fcr));
245 fcr.src_fd = fd;
246 fcr.src_offset = 0;
247 fcr.src_length = file_sz;
248 fcr.dest_offset = 0;
249
250 ret = ioctl(dfd, FICLONERANGE, &fcr);
251 (void)ret;
252
253 exercised++;
254 }
255 #endif
256
257 #if defined(FIDEDUPERANGE)
258 {
259 const size_t sz = sizeof(struct file_dedupe_range) +
260 sizeof(struct file_dedupe_range_info);
261 char buf[sz] ALIGNED(64);
262
263 struct file_dedupe_range *d = (struct file_dedupe_range *)buf;
264
265 d->src_offset = 0;
266 d->src_length = file_sz;
267 d->dest_count = 1;
268 d->reserved1 = 0;
269 d->reserved2 = 0;
270 d->info[0].dest_fd = dfd;
271 d->info[0].dest_offset = 0;
272 /* Zero the return values */
273 d->info[0].bytes_deduped = 0;
274 d->info[0].status = 0;
275 d->info[0].reserved = 0;
276 ret = ioctl(fd, FIDEDUPERANGE, d);
277 (void)ret;
278
279 /*
280 * and exercise illegal dest_count to force an
281 * ENOMEM error
282 */
283 d->dest_count = (uint16_t)~0U;
284 ret = ioctl(fd, FIDEDUPERANGE, d);
285 (void)ret;
286
287 exercised++;
288 }
289 #endif
290
291 #if defined(FIONREAD)
292 {
293 int isz = 0;
294
295 ret = ioctl(fd, FIONREAD, &isz);
296 (void)ret;
297
298 exercised++;
299
300 /*
301 * exercise invalid fd
302 */
303 ret = ioctl(bad_fd, FIONREAD, &isz);
304 (void)ret;
305
306 exercised++;
307 }
308 #endif
309
310
311 #if defined(FIONWRITE)
312 {
313 int isz = 0;
314
315 ret = ioctl(fd, FIONWRITE, &isz);
316 (void)ret;
317
318 exercised++;
319 }
320 #endif
321
322 #if defined(FS_IOC_GETVERSION)
323 {
324 int ver;
325
326 ret = ioctl(fd, FS_IOC_GETVERSION, &ver);
327 (void)ret;
328
329 exercised++;
330 }
331 #endif
332
333 #if defined(_IOW) && \
334 defined(__linux__)
335
336 /*
337 * These will eventually be in linux/falloc.h for libc, but
338 * define a shim version for now.
339 */
340 struct shim_space_resv {
341 int16_t l_type;
342 int16_t l_whence;
343 int64_t l_start;
344 int64_t l_len;
345 int32_t l_sysid;
346 uint32_t l_pid;
347 int32_t l_pad[4];
348 };
349
350 #if !defined(FS_IOC_RESVSP) && \
351 defined(_IOW)
352 #define FS_IOC_RESVSP _IOW('X', 40, struct shim_space_resv)
353 #endif
354 #if !defined(FS_IOC_UNRESVSP) && \
355 defined(_IOW)
356 #define FS_IOC_UNRESVSP _IOW('X', 41, struct shim_space_resv)
357 #endif
358 #if !defined(FS_IOC_RESVSP64) && \
359 defined(_IOW)
360 #define FS_IOC_RESVSP64 _IOW('X', 42, struct shim_space_resv)
361 #endif
362 #if !defined(FS_IOC_UNRESVSP64) && \
363 defined(_IOW)
364 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct shim_space_resv)
365 #endif
366 #if !defined(FS_IOC_ZERO_RANGE) && \
367 defined(_IOW)
368 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct shim_space_resv)
369 #endif
370
371 #if defined(FS_IOC_RESVSP)
372 {
373 struct shim_space_resv r;
374
375 (void)memset(&r, 0, sizeof(r));
376 r.l_whence = SEEK_SET;
377 r.l_start = (int64_t)0;
378 r.l_len = (int64_t)file_sz * 2;
379 ret = ioctl(fd, FS_IOC_RESVSP, &r);
380 (void)ret;
381
382 if (lseek(fd, (off_t)0, SEEK_SET) != (off_t)-1) {
383 (void)memset(&r, 0, sizeof(r));
384 r.l_whence = SEEK_CUR;
385 r.l_start = (int64_t)0;
386 r.l_len = (int64_t)file_sz;
387 ret = ioctl(fd, FS_IOC_RESVSP, &r);
388 (void)ret;
389
390 (void)memset(&r, 0, sizeof(r));
391 r.l_whence = SEEK_END;
392 r.l_start = (int64_t)0;
393 r.l_len = (int64_t)1;
394 ret = ioctl(fd, FS_IOC_RESVSP, &r);
395 (void)ret;
396 }
397
398 exercised++;
399 }
400 #endif
401
402 #if defined(FS_IOC_RESVSP64)
403 {
404 struct shim_space_resv r;
405
406 (void)memset(&r, 0, sizeof(r));
407 r.l_whence = SEEK_SET;
408 r.l_start = (int64_t)0;
409 r.l_len = (int64_t)file_sz * 2;
410
411 ret = ioctl(fd, FS_IOC_RESVSP64, &r);
412 (void)ret;
413
414 if (lseek(fd, (off_t)0, SEEK_SET) != (off_t)-1) {
415 (void)memset(&r, 0, sizeof(r));
416 r.l_whence = SEEK_CUR;
417 r.l_start = (int64_t)0;
418 r.l_len = (int64_t)file_sz;
419 ret = ioctl(fd, FS_IOC_RESVSP64, &r);
420 (void)ret;
421
422 (void)memset(&r, 0, sizeof(r));
423 r.l_whence = SEEK_END;
424 r.l_start = (int64_t)0;
425 r.l_len = (int64_t)1;
426 ret = ioctl(fd, FS_IOC_RESVSP64, &r);
427 (void)ret;
428 }
429
430 exercised++;
431 }
432 #endif
433
434 #if defined(FS_IOC_UNRESVSP)
435 {
436 struct shim_space_resv r;
437
438 (void)memset(&r, 0, sizeof(r));
439 r.l_whence = SEEK_SET;
440 r.l_start = (int64_t)file_sz;
441 r.l_len = (int64_t)file_sz * 2;
442
443 ret = ioctl(fd, FS_IOC_UNRESVSP, &r);
444 (void)ret;
445
446 exercised++;
447 }
448 #endif
449
450 #if defined(FS_IOC_UNRESVSP64)
451 {
452 struct shim_space_resv r;
453
454 (void)memset(&r, 0, sizeof(r));
455 r.l_whence = SEEK_SET;
456 r.l_start = (int64_t)file_sz;
457 r.l_len = (int64_t)file_sz * 2;
458
459 ret = ioctl(fd, FS_IOC_UNRESVSP64, &r);
460 (void)ret;
461
462 exercised++;
463 }
464 #endif
465
466 #if defined(FS_IOC_ZERO_RANGE)
467 {
468 struct shim_space_resv r;
469
470 (void)memset(&r, 0, sizeof(r));
471 r.l_whence = SEEK_SET;
472 r.l_start = (int64_t)0;
473 r.l_len = (int64_t)file_sz / 2;
474
475 ret = ioctl(fd, FS_IOC_ZERO_RANGE, &r);
476 (void)ret;
477
478 exercised++;
479 }
480 #endif
481
482 #endif
483
484 #if defined(FIBMAP)
485 {
486 int block;
487
488 block = 0;
489 ret = ioctl(fd, FIBMAP, &block);
490 (void)ret;
491
492 /*
493 * and exercise huge block request
494 * that should return -ERANGE or -EINVAL;
495 */
496 block = -1;
497 ret = ioctl(fd, FIBMAP, &block);
498 (void)ret;
499
500 exercised++;
501 }
502 #endif
503 if (!exercised) { /* cppcheck-suppress knownConditionTrueFalse */
504 pr_inf("%s: no available file ioctls to exercise\n",
505 args->name);
506 ret = EXIT_NOT_IMPLEMENTED;
507 goto tidy;
508 }
509
510 inc_counter(args);
511 } while (keep_stressing(args));
512
513 ret = EXIT_SUCCESS;
514
515 tidy:
516 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
517 #if defined(FICLONE) || defined(FICLONERANGE)
518 (void)close(dfd);
519 #endif
520 (void)close(fd);
521 (void)stress_temp_dir_rm_args(args);
522
523 return ret;
524 }
525
526 stressor_info_t stress_file_ioctl_info = {
527 .stressor = stress_file_ioctl,
528 .class = CLASS_FILESYSTEM | CLASS_OS,
529 .help = help
530 };
531