1 /* Tests for truncate(2) call family - by D.C. van Moolenbroek */ 2 #include <sys/stat.h> 3 #include <sys/param.h> 4 #include <unistd.h> 5 #include <fcntl.h> 6 #include <assert.h> 7 8 #define ITERATIONS 1 9 int max_error = 4; 10 #include "common.h" 11 12 13 #define TESTFILE "testfile" 14 #define TESTSIZE 4096 15 #define THRESHOLD 1048576 16 17 18 int main(int argc, char *argv[]); 19 void prepare(void); 20 int make_file(off_t size); 21 void check_file(int fd, off_t size, off_t hole_start, off_t hole_end); 22 void all_sizes(void (*call) (off_t osize, off_t nsize)); 23 void test50a(void); 24 void test50b(void); 25 void test50c(void); 26 void test50d(void); 27 void sub50e(off_t osize, off_t nsize); 28 void test50e(void); 29 void sub50f(off_t osize, off_t nsize); 30 void test50f(void); 31 void sub50g(off_t osize, off_t nsize); 32 void test50g(void); 33 void sub50h(off_t osize, off_t nsize); 34 void test50h(void); 35 void sub50i(off_t size, off_t off, size_t len, int type); 36 void test50i(void); 37 38 /* Some of the sizes have been chosen in such a way that they should be on the 39 * edge of direct/single indirect/double indirect switchovers for a MINIX 40 * file system with 4K block size. 41 */ 42 static off_t sizes[] = { 43 0L, 1L, 511L, 512L, 513L, 1023L, 1024L, 1025L, 2047L, 2048L, 2049L, 3071L, 44 3072L, 3073L, 4095L, 4096L, 4097L, 16383L, 16384L, 16385L, 28671L, 28672L, 45 28673L, 65535L, 65536L, 65537L, 4222975L, 4222976L, 4222977L 46 }; 47 48 static unsigned char *data; 49 50 int main(argc, argv) 51 int argc; 52 char *argv[]; 53 { 54 int j, m = 0xFFFF; 55 56 start(50); 57 prepare(); 58 if (argc == 2) m = atoi(argv[1]); 59 for (j = 0; j < ITERATIONS; j++) { 60 if (m & 00001) test50a(); 61 if (m & 00002) test50b(); 62 if (m & 00004) test50c(); 63 if (m & 00010) test50d(); 64 if (m & 00020) test50e(); 65 if (m & 00040) test50f(); 66 if (m & 00100) test50g(); 67 if (m & 00200) test50h(); 68 if (m & 00400) test50i(); 69 } 70 71 quit(); 72 return(-1); /* impossible */ 73 } 74 75 void prepare() 76 { 77 size_t largest; 78 int i; 79 80 largest = 0; 81 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) 82 if (largest < sizes[i]) largest = sizes[i]; 83 84 /* internal integrity check: this is needed for early tests */ 85 assert(largest >= TESTSIZE); 86 87 data = malloc(largest); 88 if (data == NULL) e(1000); 89 90 srand(1); 91 92 for (i = 0; i < largest; i++) 93 data[i] = (unsigned char) (rand() % 255 + 1); 94 } 95 96 void all_sizes(call) 97 void(*call) (off_t osize, off_t nsize); 98 { 99 int i, j; 100 101 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) 102 for (j = 0; j < sizeof(sizes) / sizeof(sizes[0]); j++) 103 call(sizes[i], sizes[j]); 104 } 105 106 int make_file(size) 107 off_t size; 108 { 109 off_t off; 110 int fd, r; 111 112 if ((fd = open(TESTFILE, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) e(1001); 113 114 off = 0; 115 while (off < size) { 116 r = write(fd, data + off, size - off); 117 118 if (r != size - off) e(1002); 119 120 off += r; 121 } 122 123 return fd; 124 } 125 126 void check_file(fd, hole_start, hole_end, size) 127 int fd; 128 off_t hole_start; 129 off_t hole_end; 130 off_t size; 131 { 132 static unsigned char buf[16384]; 133 struct stat statbuf; 134 off_t off; 135 int i, chunk; 136 137 /* The size must match. */ 138 if (fstat(fd, &statbuf) != 0) e(1003); 139 if (statbuf.st_size != size) e(1004); 140 141 if (lseek(fd, 0L, SEEK_SET) != 0L) e(1005); 142 143 /* All bytes in the file must be equal to what we wrote, except for the bytes 144 * in the hole, which must be zero. 145 */ 146 for (off = 0; off < size; off += chunk) { 147 chunk = MIN(sizeof(buf), size - off); 148 149 if (read(fd, buf, chunk) != chunk) e(1006); 150 151 for (i = 0; i < chunk; i++) { 152 if (off + i >= hole_start && off + i < hole_end) { 153 if (buf[i] != 0) e(1007); 154 } 155 else { 156 if (buf[i] != data[off+i]) e(1008); 157 } 158 } 159 } 160 161 /* We must get back EOF at the end. */ 162 if (read(fd, buf, sizeof(buf)) != 0) e(1009); 163 } 164 165 void test50a() 166 { 167 struct stat statbuf; 168 int fd; 169 170 subtest = 1; 171 172 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1); 173 174 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2); 175 176 /* Negative sizes should result in EINVAL. */ 177 if (truncate(TESTFILE, -1) != -1) e(3); 178 if (errno != EINVAL) e(4); 179 180 /* Make sure the file size did not change. */ 181 if (fstat(fd, &statbuf) != 0) e(5); 182 if (statbuf.st_size != TESTSIZE) e(6); 183 184 close(fd); 185 if (unlink(TESTFILE) != 0) e(7); 186 187 /* An empty path should result in ENOENT. */ 188 if (truncate("", 0) != -1) e(8); 189 if (errno != ENOENT) e(9); 190 191 /* A non-existing file name should result in ENOENT. */ 192 if (truncate(TESTFILE"2", 0) != -1) e(10); 193 if (errno != ENOENT) e(11); 194 } 195 196 void test50b() 197 { 198 struct stat statbuf; 199 int fd; 200 201 subtest = 2; 202 203 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1); 204 205 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2); 206 207 /* Negative sizes should result in EINVAL. */ 208 if (ftruncate(fd, -1) != -1) e(3); 209 if (errno != EINVAL) e(4); 210 211 /* Make sure the file size did not change. */ 212 if (fstat(fd, &statbuf) != 0) e(5); 213 if (statbuf.st_size != TESTSIZE) e(6); 214 215 close(fd); 216 217 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */ 218 if (ftruncate(fd, 0) != -1) e(7); 219 if (errno != EBADF && errno != EINVAL) e(8); 220 221 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(9); 222 223 /* Calls on a file opened read-only should return EBADF or EINVAL. */ 224 if (ftruncate(fd, 0) != -1) e(10); 225 if (errno != EBADF && errno != EINVAL) e(11); 226 227 close(fd); 228 229 if (unlink(TESTFILE) != 0) e(12); 230 } 231 232 void test50c() 233 { 234 struct stat statbuf; 235 struct flock flock; 236 off_t off; 237 int fd; 238 239 subtest = 3; 240 241 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1); 242 243 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2); 244 245 off = TESTSIZE / 2; 246 if (lseek(fd, off, SEEK_SET) != off) e(3); 247 248 flock.l_len = 0; 249 250 /* Negative sizes should result in EINVAL. */ 251 flock.l_whence = SEEK_SET; 252 flock.l_start = -1; 253 if (fcntl(fd, F_FREESP, &flock) != -1) e(4); 254 if (errno != EINVAL) e(5); 255 256 flock.l_whence = SEEK_CUR; 257 flock.l_start = -off - 1; 258 if (fcntl(fd, F_FREESP, &flock) != -1) e(6); 259 if (errno != EINVAL) e(7); 260 261 flock.l_whence = SEEK_END; 262 flock.l_start = -TESTSIZE - 1; 263 if (fcntl(fd, F_FREESP, &flock) != -1) e(8); 264 if (errno != EINVAL) e(9); 265 266 /* Make sure the file size did not change. */ 267 if (fstat(fd, &statbuf) != 0) e(10); 268 if (statbuf.st_size != TESTSIZE) e(11); 269 270 /* Proper negative values should work, however. */ 271 flock.l_whence = SEEK_CUR; 272 flock.l_start = -1; 273 if (fcntl(fd, F_FREESP, &flock) != 0) e(12); 274 275 if (fstat(fd, &statbuf) != 0) e(13); 276 if (statbuf.st_size != off - 1) e(14); 277 278 flock.l_whence = SEEK_END; 279 flock.l_start = -off + 1; 280 if (fcntl(fd, F_FREESP, &flock) != 0) e(15); 281 282 if (fstat(fd, &statbuf) != 0) e(16); 283 if (statbuf.st_size != 0L) e(17); 284 285 close(fd); 286 287 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */ 288 flock.l_whence = SEEK_SET; 289 flock.l_start = 0; 290 if (fcntl(fd, F_FREESP, &flock) != -1) e(18); 291 if (errno != EBADF && errno != EINVAL) e(19); 292 293 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(20); 294 295 /* Calls on a file opened read-only should return EBADF or EINVAL. */ 296 if (fcntl(fd, F_FREESP, &flock) != -1) e(21); 297 if (errno != EBADF && errno != EINVAL) e(22); 298 299 close(fd); 300 301 if (unlink(TESTFILE) != 0) e(23); 302 } 303 304 void test50d() 305 { 306 struct stat statbuf; 307 struct flock flock; 308 off_t off; 309 int fd; 310 311 subtest = 4; 312 313 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1); 314 315 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2); 316 317 off = TESTSIZE / 2; 318 if (lseek(fd, off, SEEK_SET) != off) e(3); 319 320 /* The given length must be positive. */ 321 flock.l_whence = SEEK_CUR; 322 flock.l_start = 0; 323 flock.l_len = -1; 324 if (fcntl(fd, F_FREESP, &flock) != -1) e(4); 325 if (errno != EINVAL) e(5); 326 327 /* Negative start positions are not allowed. */ 328 flock.l_whence = SEEK_SET; 329 flock.l_start = -1; 330 flock.l_len = 1; 331 if (fcntl(fd, F_FREESP, &flock) != -1) e(6); 332 if (errno != EINVAL) e(7); 333 334 flock.l_whence = SEEK_CUR; 335 flock.l_start = -off - 1; 336 if (fcntl(fd, F_FREESP, &flock) != -1) e(8); 337 if (errno != EINVAL) e(9); 338 339 flock.l_whence = SEEK_END; 340 flock.l_start = -TESTSIZE - 1; 341 if (fcntl(fd, F_FREESP, &flock) != -1) e(10); 342 if (errno != EINVAL) e(11); 343 344 /* Start positions at or beyond the end of the file are no good, either. */ 345 flock.l_whence = SEEK_SET; 346 flock.l_start = TESTSIZE; 347 if (fcntl(fd, F_FREESP, &flock) != -1) e(12); 348 if (errno != EINVAL) e(13); 349 350 flock.l_start = TESTSIZE + 1; 351 if (fcntl(fd, F_FREESP, &flock) != -1) e(13); 352 if (errno != EINVAL) e(14); 353 354 flock.l_whence = SEEK_CUR; 355 flock.l_start = TESTSIZE - off; 356 if (fcntl(fd, F_FREESP, &flock) != -1) e(15); 357 if (errno != EINVAL) e(16); 358 359 flock.l_whence = SEEK_END; 360 flock.l_start = 1; 361 if (fcntl(fd, F_FREESP, &flock) != -1) e(17); 362 if (errno != EINVAL) e(18); 363 364 /* End positions beyond the end of the file may be silently bounded. */ 365 flock.l_whence = SEEK_SET; 366 flock.l_start = 0; 367 flock.l_len = TESTSIZE + 1; 368 if (fcntl(fd, F_FREESP, &flock) != 0) e(19); 369 370 flock.l_whence = SEEK_CUR; 371 flock.l_len = TESTSIZE - off + 1; 372 if (fcntl(fd, F_FREESP, &flock) != 0) e(20); 373 374 flock.l_whence = SEEK_END; 375 flock.l_start = -1; 376 flock.l_len = 2; 377 if (fcntl(fd, F_FREESP, &flock) != 0) e(21); 378 379 /* However, this must never cause the file size to change. */ 380 if (fstat(fd, &statbuf) != 0) e(22); 381 if (statbuf.st_size != TESTSIZE) e(23); 382 383 close(fd); 384 385 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */ 386 flock.l_whence = SEEK_SET; 387 flock.l_start = 0; 388 if (fcntl(fd, F_FREESP, &flock) != -1) e(24); 389 if (errno != EBADF && errno != EINVAL) e(25); 390 391 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(26); 392 393 /* Calls on a file opened read-only should return EBADF or EINVAL. */ 394 if (fcntl(fd, F_FREESP, &flock) != -1) e(27); 395 if (errno != EBADF && errno != EINVAL) e(28); 396 397 close(fd); 398 399 if (unlink(TESTFILE) != 0) e(29); 400 } 401 402 void sub50e(osize, nsize) 403 off_t osize; 404 off_t nsize; 405 { 406 int fd; 407 408 fd = make_file(osize); 409 410 if (truncate(TESTFILE, nsize) != 0) e(1); 411 412 check_file(fd, osize, nsize, nsize); 413 414 if (nsize < osize) { 415 if (truncate(TESTFILE, osize) != 0) e(2); 416 417 check_file(fd, nsize, osize, osize); 418 } 419 420 close(fd); 421 422 if (unlink(TESTFILE) != 0) e(3); 423 424 } 425 426 void test50e() 427 { 428 subtest = 5; 429 430 /* truncate(2) on a file that is open. */ 431 all_sizes(sub50e); 432 } 433 434 void sub50f(osize, nsize) 435 off_t osize; 436 off_t nsize; 437 { 438 int fd; 439 440 fd = make_file(osize); 441 442 close(fd); 443 444 if (truncate(TESTFILE, nsize) != 0) e(1); 445 446 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(2); 447 448 check_file(fd, osize, nsize, nsize); 449 450 if (nsize < osize) { 451 close(fd); 452 453 if (truncate(TESTFILE, osize) != 0) e(3); 454 455 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(4); 456 457 check_file(fd, nsize, osize, osize); 458 } 459 460 close(fd); 461 462 if (unlink(TESTFILE) != 0) e(5); 463 } 464 465 void test50f() 466 { 467 subtest = 6; 468 469 /* truncate(2) on a file that is not open. */ 470 all_sizes(sub50f); 471 } 472 473 void sub50g(osize, nsize) 474 off_t osize; 475 off_t nsize; 476 { 477 int fd; 478 479 fd = make_file(osize); 480 481 if (ftruncate(fd, nsize) != 0) e(1); 482 483 check_file(fd, osize, nsize, nsize); 484 485 if (nsize < osize) { 486 if (ftruncate(fd, osize) != 0) e(2); 487 488 check_file(fd, nsize, osize, osize); 489 } 490 491 close(fd); 492 493 if (unlink(TESTFILE) != 0) e(3); 494 } 495 496 void test50g() 497 { 498 subtest = 7; 499 500 /* ftruncate(2) on an open file. */ 501 all_sizes(sub50g); 502 } 503 504 void sub50h(osize, nsize) 505 off_t osize; 506 off_t nsize; 507 { 508 struct flock flock; 509 int fd; 510 511 fd = make_file(osize); 512 513 flock.l_whence = SEEK_SET; 514 flock.l_start = nsize; 515 flock.l_len = 0; 516 if (fcntl(fd, F_FREESP, &flock) != 0) e(1); 517 518 check_file(fd, osize, nsize, nsize); 519 520 if (nsize < osize) { 521 flock.l_whence = SEEK_SET; 522 flock.l_start = osize; 523 flock.l_len = 0; 524 if (fcntl(fd, F_FREESP, &flock) != 0) e(2); 525 526 check_file(fd, nsize, osize, osize); 527 } 528 529 close(fd); 530 531 if (unlink(TESTFILE) != 0) e(3); 532 } 533 534 void test50h() 535 { 536 subtest = 8; 537 538 /* fcntl(2) with F_FREESP and l_len=0. */ 539 all_sizes(sub50h); 540 } 541 542 void sub50i(size, off, len, type) 543 off_t size; 544 off_t off; 545 size_t len; 546 int type; 547 { 548 struct flock flock; 549 int fd; 550 551 fd = make_file(size); 552 553 switch (type) { 554 case 0: 555 flock.l_whence = SEEK_SET; 556 flock.l_start = off; 557 break; 558 case 1: 559 if (lseek(fd, off, SEEK_SET) != off) e(1); 560 flock.l_whence = SEEK_CUR; 561 flock.l_start = 0; 562 break; 563 case 2: 564 flock.l_whence = SEEK_END; 565 flock.l_start = off - size; 566 break; 567 default: 568 e(1); 569 } 570 571 flock.l_len = len; 572 if (fcntl(fd, F_FREESP, &flock) != 0) e(2); 573 574 check_file(fd, off, off + len, size); 575 576 /* Repeat the call in order to see whether the file system can handle holes 577 * while freeing up. If not, the server would typically crash; we need not 578 * check the results again. 579 */ 580 flock.l_whence = SEEK_SET; 581 flock.l_start = off; 582 if (fcntl(fd, F_FREESP, &flock) != 0) e(3); 583 584 close(fd); 585 586 if (unlink(TESTFILE) != 0) e(4); 587 } 588 589 void test50i() 590 { 591 off_t off; 592 int i, j, k, l; 593 594 subtest = 9; 595 596 /* fcntl(2) with F_FREESP and l_len>0. */ 597 598 /* This loop determines the size of the test file. */ 599 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) { 600 /* Big files simply take too long. We have to compromise here. */ 601 if (sizes[i] >= THRESHOLD) continue; 602 603 /* This loop determines one of the two values for the offset. */ 604 for (j = 0; j < sizeof(sizes) / sizeof(sizes[0]); j++) { 605 if (sizes[j] >= sizes[i]) continue; 606 607 /* This loop determines the other. */ 608 for (k = 0; k < sizeof(sizes) / sizeof(sizes[0]); k++) { 609 if (sizes[k] > sizes[j]) continue; 610 611 /* Construct an offset by adding the two sizes. */ 612 off = sizes[j] + sizes[k]; 613 614 if (j + 1 < sizeof(sizes) / sizeof(sizes[0]) && 615 off >= sizes[j + 1]) continue; 616 617 /* This loop determines the length of the hole. */ 618 for (l = 0; l < sizeof(sizes) / sizeof(sizes[0]); l++) { 619 if (sizes[l] == 0 || off + sizes[l] > sizes[i]) 620 continue; 621 622 /* This could have been a loop, too! */ 623 sub50i(sizes[i], off, sizes[l], 0); 624 sub50i(sizes[i], off, sizes[l], 1); 625 sub50i(sizes[i], off, sizes[l], 2); 626 } 627 } 628 } 629 } 630 } 631