1 /* 2 * 3 * Copyright (c) International Business Machines Corp., 2002 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 /* 21 * NAME 22 * diotest4.c 23 * 24 * DESCRIPTION 25 * The program generates error conditions and verifies the error 26 * code generated with the expected error value. The program also 27 * tests some of the boundary condtions. The size of test file created 28 * is filesize_in_blocks * 4k. 29 * Test blocks: 30 * [1] Negative Offset 31 * [2] Negative count - removed 08/01/2003 - robbiew 32 * [3] Odd count of read and write 33 * [4] Read beyond the file size 34 * [5] Invalid file descriptor 35 * [6] Out of range file descriptor 36 * [7] Closed file descriptor 37 * [8] Directory read, write - removed 10/7/2002 - plars 38 * [9] Character device (/dev/null) read, write 39 * [10] read, write to a mmaped file 40 * [11] read, write to an unmaped file with munmap 41 * [12] read from file not open for reading 42 * [13] write to file not open for writing 43 * [14] read, write with non-aligned buffer 44 * [15] read, write buffer in read-only space 45 * [16] read, write in non-existant space 46 * [17] read, write for file with O_SYNC 47 * 48 * USAGE 49 * diotest4 [-b filesize_in_blocks] 50 * 51 * History 52 * 04/22/2002 Narasimha Sharoff nsharoff (at) us.ibm.com 53 * 54 * RESTRICTIONS 55 * None 56 */ 57 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <unistd.h> 61 #include <signal.h> 62 #include <sys/file.h> 63 #include <fcntl.h> 64 #include <sys/types.h> 65 #include <sys/mman.h> 66 #include <sys/syscall.h> 67 #include <errno.h> 68 69 #include "diotest_routines.h" 70 71 #include "test.h" 72 #include "safe_macros.h" 73 #include "lapi/mmap.h" 74 75 char *TCID = "diotest4"; /* Test program identifier. */ 76 int TST_TOTAL = 17; /* Total number of test conditions */ 77 78 static long fs_type; 79 80 #ifdef O_DIRECT 81 82 #define BUFSIZE 4096 83 #define TRUE 1 84 #define LEN 30 85 86 #ifdef __GNUC__ 87 #define ADDRESS_OF_MAIN __builtin_extract_return_addr(__builtin_return_address(0)) 88 #else 89 #define ADDRESS_OF_MAIN main 90 #endif 91 92 /* 93 * runtest_f: Do read, writes. Verify the error value obtained by 94 * running read or write with the expected error value (errnum). 95 */ 96 int 97 runtest_f(int fd, char *buf, int offset, int count, int errnum, int testnum, 98 char *msg) 99 { 100 int ret; 101 int l_fail = 0; 102 103 if (lseek(fd, offset, SEEK_SET) < 0) { 104 if (errno != errnum) { 105 tst_resm(TFAIL, "lseek before read failed: %s", 106 strerror(errno)); 107 l_fail = TRUE; 108 } 109 } else { 110 errno = 0; 111 ret = read(fd, buf, count); 112 if (ret >= 0 || errno != errnum) { 113 tst_resm(TFAIL, "read allows %s. returns %d: %s", 114 msg, ret, strerror(errno)); 115 l_fail = TRUE; 116 } 117 } 118 if (lseek(fd, offset, SEEK_SET) < 0) { 119 if (errno != errnum) { 120 tst_resm(TFAIL, "lseek before write failed: %s", 121 strerror(errno)); 122 l_fail = TRUE; 123 } 124 } else { 125 errno = 0; 126 ret = write(fd, buf, count); 127 if (ret >= 0 || errno != errnum) { 128 tst_resm(TFAIL, "write allows %s.returns %d: %s", 129 msg, ret, strerror(errno)); 130 l_fail = TRUE; 131 } 132 } 133 return (l_fail); 134 } 135 136 /* 137 * runtest_s: Do read, writes. Verify the they run successfully. 138 */ 139 int runtest_s(int fd, char *buf, int offset, int count, int testnum, char *msg) 140 { 141 int ret; 142 int l_fail = 0; 143 144 if (lseek(fd, offset, SEEK_SET) < 0) { 145 tst_resm(TFAIL, "lseek before read failed: %s", 146 strerror(errno)); 147 l_fail = TRUE; 148 } else { 149 if ((ret = read(fd, buf, count)) < 0) { 150 tst_resm(TFAIL, "read failed for %s. returns %d: %s", 151 msg, ret, strerror(errno)); 152 l_fail = TRUE; 153 } 154 } 155 if (lseek(fd, offset, SEEK_SET) < 0) { 156 tst_resm(TFAIL, "lseek before write failed: %s", 157 strerror(errno)); 158 l_fail = TRUE; 159 } else { 160 if ((ret = write(fd, buf, count)) < 0) { 161 tst_resm(TFAIL, "write failed for %s. returns %d: %s", 162 msg, ret, strerror(errno)); 163 l_fail = TRUE; 164 } 165 } 166 return (l_fail); 167 } 168 169 static void prg_usage(void) 170 { 171 fprintf(stderr, "Usage: diotest4 [-b filesize_in_blocks]\n"); 172 exit(1); 173 } 174 175 static void testcheck_end(int ret, int *failed, int *fail_count, char *msg) 176 { 177 if (ret != 0) { 178 *failed = TRUE; 179 (*fail_count)++; 180 tst_resm(TFAIL, "%s", msg); 181 } else 182 tst_resm(TPASS, "%s", msg); 183 } 184 185 static void setup(void); 186 static void cleanup(void); 187 static int fd1 = -1; 188 static char filename[LEN]; 189 190 int main(int argc, char *argv[]) 191 { 192 int fblocks = 1; /* Iterations. Default 1 */ 193 int bufsize = BUFSIZE; 194 int count, ret; 195 int offset; 196 int fd, newfd; 197 int i, l_fail = 0, fail_count = 0, total = 0; 198 int failed = 0; 199 int shmsz = MMAP_GRANULARITY; 200 int pagemask = ~(sysconf(_SC_PAGE_SIZE) - 1); 201 char *buf0, *buf1, *buf2; 202 caddr_t shm_base; 203 204 /* Options */ 205 while ((i = getopt(argc, argv, "b:")) != -1) { 206 switch (i) { 207 case 'b': 208 if ((fblocks = atoi(optarg)) <= 0) { 209 fprintf(stderr, "fblocks must be > 0\n"); 210 prg_usage(); 211 } 212 break; 213 default: 214 prg_usage(); 215 } 216 } 217 218 setup(); 219 220 /* Open file and fill, allocate for buffer */ 221 if ((fd = open(filename, O_DIRECT | O_RDWR | O_CREAT, 0666)) < 0) { 222 tst_brkm(TBROK, cleanup, "open failed for %s: %s", 223 filename, strerror(errno)); 224 } 225 if ((buf0 = valloc(bufsize)) == NULL) { 226 tst_brkm(TBROK, cleanup, "valloc() buf0 failed: %s", 227 strerror(errno)); 228 } 229 for (i = 1; i < fblocks; i++) { 230 fillbuf(buf0, bufsize, (char)i); 231 if (write(fd, buf0, bufsize) < 0) { 232 tst_brkm(TBROK, cleanup, "write failed for %s: %s", 233 filename, strerror(errno)); 234 } 235 } 236 close(fd); 237 if ((buf2 = valloc(bufsize)) == NULL) { 238 tst_brkm(TBROK, cleanup, "valloc() buf2 failed: %s", 239 strerror(errno)); 240 } 241 if ((fd = open(filename, O_DIRECT | O_RDWR)) < 0) { 242 tst_brkm(TBROK, cleanup, "open failed for %s: %s", 243 filename, strerror(errno)); 244 } 245 246 /* Test-1: Negative Offset */ 247 offset = -1; 248 count = bufsize; 249 errno = 0; 250 ret = lseek(fd, offset, SEEK_SET); 251 if ((ret >= 0) || (errno != EINVAL)) { 252 tst_resm(TFAIL, "lseek allows negative offset. returns %d:%s", 253 ret, strerror(errno)); 254 failed = TRUE; 255 fail_count++; 256 } else 257 tst_resm(TPASS, "Negative Offset"); 258 total++; 259 260 /* Test-2: Removed */ 261 tst_resm(TPASS, "removed"); 262 263 /* Test-3: Odd count of read and write */ 264 offset = 0; 265 count = 1; 266 lseek(fd, 0, SEEK_SET); 267 if (write(fd, buf2, 4096) == -1) { 268 tst_resm(TFAIL, "can't write to file %d", ret); 269 } 270 switch (fs_type) { 271 case TST_NFS_MAGIC: 272 case TST_BTRFS_MAGIC: 273 tst_resm(TCONF, "%s supports odd count IO", 274 tst_fs_type_name(fs_type)); 275 break; 276 default: 277 ret = runtest_f(fd, buf2, offset, count, EINVAL, 3, "odd count"); 278 testcheck_end(ret, &failed, &fail_count, 279 "Odd count of read and write"); 280 } 281 282 total++; 283 284 /* Test-4: Read beyond the file size */ 285 offset = bufsize * (fblocks + 10); 286 count = bufsize; 287 if (lseek(fd, offset, SEEK_SET) < 0) { 288 tst_resm(TFAIL, "lseek failed: %s", strerror(errno)); 289 failed = TRUE; 290 fail_count++; 291 tst_resm(TFAIL, "Read beyond the file size"); 292 } else { 293 errno = 0; 294 ret = read(fd, buf2, count); 295 if (ret > 0 || (ret < 0 && errno != EINVAL)) { 296 tst_resm(TFAIL, 297 "allows read beyond file size. returns %d: %s", 298 ret, strerror(errno)); 299 failed = TRUE; 300 fail_count++; 301 } else 302 tst_resm(TPASS, "Read beyond the file size"); 303 } 304 total++; 305 306 /* Test-5: Invalid file descriptor */ 307 offset = 4096; 308 count = bufsize; 309 newfd = -1; 310 ret = runtest_f(newfd, buf2, offset, count, EBADF, 5, "negative fd"); 311 testcheck_end(ret, &failed, &fail_count, "Invalid file descriptor"); 312 total++; 313 314 /* Test-6: Out of range file descriptor */ 315 count = bufsize; 316 offset = 4096; 317 if ((newfd = getdtablesize()) < 0) { 318 tst_resm(TFAIL, "getdtablesize() failed: %s", strerror(errno)); 319 failed = TRUE; 320 tst_resm(TFAIL, "Out of range file descriptor"); 321 } else { 322 ret = runtest_f(newfd, buf2, offset, count, EBADF, 6, 323 "out of range fd"); 324 testcheck_end(ret, &failed, &fail_count, 325 "Out of range file descriptor"); 326 } 327 close(newfd); 328 total++; 329 330 /* Test-7: Closed file descriptor */ 331 offset = 4096; 332 count = bufsize; 333 SAFE_CLOSE(cleanup, fd); 334 ret = runtest_f(fd, buf2, offset, count, EBADF, 7, "closed fd"); 335 testcheck_end(ret, &failed, &fail_count, "Closed file descriptor"); 336 total++; 337 338 /* Test-9: removed */ 339 tst_resm(TPASS, "removed"); 340 341 /* Test-9: Character device (/dev/null) read, write */ 342 offset = 0; 343 count = bufsize; 344 if ((newfd = open("/dev/null", O_DIRECT | O_RDWR)) < 0) { 345 tst_resm(TCONF, "Direct I/O on /dev/null is not supported"); 346 } else { 347 ret = runtest_s(newfd, buf2, offset, count, 9, "/dev/null"); 348 testcheck_end(ret, &failed, &fail_count, 349 "character device read, write"); 350 } 351 close(newfd); 352 total++; 353 354 /* Test-10: read, write to a mmaped file */ 355 shm_base = (char *)(((long)sbrk(0) + (shmsz - 1)) & ~(shmsz - 1)); 356 if (shm_base == NULL) { 357 tst_brkm(TBROK, cleanup, "sbrk failed: %s", strerror(errno)); 358 } 359 offset = 4096; 360 count = bufsize; 361 if ((fd = open(filename, O_DIRECT | O_RDWR)) < 0) { 362 tst_brkm(TBROK, cleanup, "can't open %s: %s", 363 filename, strerror(errno)); 364 } 365 shm_base = mmap(shm_base, 0x100000, PROT_READ | PROT_WRITE, 366 MAP_SHARED | MAP_FIXED, fd, 0); 367 if (shm_base == (caddr_t) - 1) { 368 tst_brkm(TBROK, cleanup, "can't mmap file: %s", 369 strerror(errno)); 370 } 371 ret = runtest_s(fd, buf2, offset, count, 10, "mmapped file"); 372 testcheck_end(ret, &failed, &fail_count, 373 "read, write to a mmaped file"); 374 total++; 375 376 /* Test-11: read, write to an unmaped file with munmap */ 377 if ((ret = munmap(shm_base, 0x100000)) < 0) { 378 tst_brkm(TBROK, cleanup, "can't unmap file: %s", 379 strerror(errno)); 380 } 381 ret = runtest_s(fd, buf2, offset, count, 11, "unmapped file"); 382 testcheck_end(ret, &failed, &fail_count, 383 "read, write to an unmapped file"); 384 close(fd); 385 total++; 386 387 /* Test-12: read from file not open for reading */ 388 offset = 4096; 389 count = bufsize; 390 if ((fd = open(filename, O_DIRECT | O_WRONLY)) < 0) { 391 tst_brkm(TBROK, cleanup, "can't open %s: %s", 392 filename, strerror(errno)); 393 } 394 if (lseek(fd, offset, SEEK_SET) < 0) { 395 tst_resm(TFAIL, "lseek failed: %s", strerror(errno)); 396 failed = TRUE; 397 fail_count++; 398 } else { 399 errno = 0; 400 ret = read(fd, buf2, count); 401 if (ret >= 0 || errno != EBADF) { 402 tst_resm(TFAIL, 403 "allows read on file not open for reading. returns %d: %s", 404 ret, strerror(errno)); 405 failed = TRUE; 406 fail_count++; 407 } else 408 tst_resm(TPASS, "read from file not open for reading"); 409 } 410 close(fd); 411 total++; 412 413 /* Test-13: write to file not open for writing */ 414 offset = 4096; 415 count = bufsize; 416 if ((fd = open(filename, O_DIRECT | O_RDONLY)) < 0) { 417 tst_brkm(TBROK, cleanup, "can't open %s: %s", 418 filename, strerror(errno)); 419 } 420 if (lseek(fd, offset, SEEK_SET) < 0) { 421 tst_resm(TFAIL, "lseek failed: %s", strerror(errno)); 422 failed = TRUE; 423 fail_count++; 424 } else { 425 errno = 0; 426 ret = write(fd, buf2, count); 427 if (ret >= 0 || errno != EBADF) { 428 tst_resm(TFAIL, 429 "allows write on file not open for writing. returns %d: %s", 430 ret, strerror(errno)); 431 failed = TRUE; 432 fail_count++; 433 } else 434 tst_resm(TPASS, "write to file not open for writing"); 435 } 436 close(fd); 437 total++; 438 439 /* Test-14: read, write with non-aligned buffer */ 440 offset = 4096; 441 count = bufsize; 442 if ((fd = open(filename, O_DIRECT | O_RDWR)) < 0) { 443 tst_brkm(TBROK, cleanup, "can't open %s: %s", 444 filename, strerror(errno)); 445 } 446 switch (fs_type) { 447 case TST_NFS_MAGIC: 448 case TST_BTRFS_MAGIC: 449 tst_resm(TCONF, "%s supports non-aligned buffer", 450 tst_fs_type_name(fs_type)); 451 break; 452 default: 453 ret = runtest_f(fd, buf2 + 1, offset, count, EINVAL, 14, 454 " nonaligned buf"); 455 testcheck_end(ret, &failed, &fail_count, 456 "read, write with non-aligned buffer"); 457 } 458 close(fd); 459 total++; 460 461 /* Test-15: read, write buffer in read-only space */ 462 offset = 4096; 463 count = bufsize; 464 l_fail = 0; 465 if ((fd = open(filename, O_DIRECT | O_RDWR)) < 0) { 466 tst_brkm(TBROK, cleanup, "can't open %s: %s", 467 filename, strerror(errno)); 468 } 469 if (lseek(fd, offset, SEEK_SET) < 0) { 470 tst_resm(TFAIL, "lseek before read failed: %s", 471 strerror(errno)); 472 l_fail = TRUE; 473 } else { 474 errno = 0; 475 ret = read(fd, (char *)((ulong) ADDRESS_OF_MAIN & pagemask), 476 count); 477 if (ret >= 0 || errno != EFAULT) { 478 tst_resm(TFAIL, 479 "read to read-only space. returns %d: %s", ret, 480 strerror(errno)); 481 l_fail = TRUE; 482 } 483 } 484 if (lseek(fd, offset, SEEK_SET) < 0) { 485 tst_resm(TFAIL, "lseek before write failed: %s", 486 strerror(errno)); 487 l_fail = TRUE; 488 } else { 489 ret = write(fd, (char *)((ulong) ADDRESS_OF_MAIN & pagemask), 490 count); 491 if (ret < 0) { 492 tst_resm(TFAIL, 493 "write to read-only space. returns %d: %s", 494 ret, strerror(errno)); 495 l_fail = TRUE; 496 } 497 } 498 testcheck_end(l_fail, &failed, &fail_count, 499 "read, write buffer in read-only space"); 500 close(fd); 501 total++; 502 503 /* Test-16: read, write in non-existant space */ 504 offset = 4096; 505 count = bufsize; 506 if ((buf1 = 507 (char *)(((long)sbrk(0) + (shmsz - 1)) & ~(shmsz - 1))) == NULL) { 508 tst_brkm(TBROK | TERRNO, cleanup, "sbrk failed"); 509 } 510 if ((fd = open(filename, O_DIRECT | O_RDWR)) < 0) { 511 tst_brkm(TBROK | TERRNO, cleanup, 512 "open(%s, O_DIRECT|O_RDWR) failed", filename); 513 } 514 ret = runtest_f(fd, buf1, offset, count, EFAULT, 16, 515 " nonexistant space"); 516 testcheck_end(ret, &failed, &fail_count, 517 "read, write in non-existant space"); 518 total++; 519 close(fd); 520 521 /* Test-17: read, write for file with O_SYNC */ 522 offset = 4096; 523 count = bufsize; 524 if ((fd = open(filename, O_DIRECT | O_RDWR | O_SYNC)) < 0) { 525 tst_brkm(TBROK, cleanup, 526 "open(%s, O_DIRECT|O_RDWR|O_SYNC failed)", filename); 527 } 528 ret = runtest_s(fd, buf2, offset, count, 17, "opened with O_SYNC"); 529 testcheck_end(ret, &failed, &fail_count, 530 "read, write for file with O_SYNC"); 531 total++; 532 close(fd); 533 534 unlink(filename); 535 if (failed) 536 tst_resm(TINFO, "%d/%d test blocks failed", fail_count, total); 537 else 538 tst_resm(TINFO, "%d testblocks completed", total); 539 cleanup(); 540 541 tst_exit(); 542 } 543 544 static void setup(void) 545 { 546 struct sigaction act; 547 548 tst_tmpdir(); 549 550 act.sa_handler = SIG_IGN; 551 act.sa_flags = 0; 552 sigemptyset(&act.sa_mask); 553 (void)sigaction(SIGXFSZ, &act, NULL); 554 sprintf(filename, "testdata-4.%ld", syscall(__NR_gettid)); 555 556 if ((fd1 = open(filename, O_CREAT | O_EXCL, 0600)) < 0) { 557 tst_brkm(TBROK, cleanup, "Couldn't create test file %s: %s", 558 filename, strerror(errno)); 559 } 560 close(fd1); 561 562 /* Test for filesystem support of O_DIRECT */ 563 if ((fd1 = open(filename, O_DIRECT, 0600)) < 0) { 564 tst_brkm(TCONF, cleanup, 565 "O_DIRECT is not supported by this filesystem. %s", 566 strerror(errno)); 567 } 568 close(fd1); 569 570 fs_type = tst_fs_type(cleanup, "."); 571 } 572 573 static void cleanup(void) 574 { 575 if (fd1 != -1) 576 unlink(filename); 577 578 tst_rmdir(); 579 580 } 581 582 #else /* O_DIRECT */ 583 584 int main() 585 { 586 tst_brkm(TCONF, NULL, "O_DIRECT is not defined."); 587 } 588 #endif /* O_DIRECT */ 589